Disassemble ELF - PC is set to 0?
Clash Royale CLAN TAG#URR8PPP
up vote
2
down vote
favorite
I tried to disassemble a ELF file which is a shared object file executed on armv7a (Android). I saw a strange block. It seems that the PC
, program counter register, is set to 0
. Did I miss something or do something wrong?
The process goes into 0x1708
in ARM mode. Below is the strange block of asm code I disassembled from the ELF file.
; section: .plt
; function: function_1708 at 0x1708 -- 0x1718
0x1708: 04 e0 2d e5 str lr, [sp, #-4]!
0x170c: 04 e0 9f e5 ldr lr, [pc, #4]
0x1710: 0e e0 8f e0 add lr, pc, lr
0x1714: 08 f0 be e5 ldr pc, [lr, #8]!
; data inside code section at 0x1718 -- 0x171c
0x1718: b4 77 00 00 |.w.. |
After executing line 0x170c
, the LR
register should be set as the value at the address 0x1718
. The value is 0x77b4
(this file is stored in little-endian). And go ahead.
0x1710: lr += 0x1710 + 8 // lr = 0x8ecc
0x1714: pc = *(lr + 8) // pc = *(0x8ed4)
lr += 8
And 0x8ed4 is in .got
section.
; section: .got
0x8eac: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x8ebc: 00 00 00 00 58 70 00 00 e0 6e 00 00 00 00 00 00 |....Xp...n......|
0x8ecc: 00 00 00 00 00 00 00 00 00 00 00 00 08 17 00 00 |................|
0x8edc: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8eec: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8efc: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f0c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f1c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f2c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f3c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
It seems that the value at 0x8ed4
is zero. I traced to this strange block from JNI_OnLoad()
, so no data should be modified before executing this block.
Did I do something wrong, or is this a specific behavior of ARM architecture?
android arm asm
New contributor
Iven CJ7 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |Â
up vote
2
down vote
favorite
I tried to disassemble a ELF file which is a shared object file executed on armv7a (Android). I saw a strange block. It seems that the PC
, program counter register, is set to 0
. Did I miss something or do something wrong?
The process goes into 0x1708
in ARM mode. Below is the strange block of asm code I disassembled from the ELF file.
; section: .plt
; function: function_1708 at 0x1708 -- 0x1718
0x1708: 04 e0 2d e5 str lr, [sp, #-4]!
0x170c: 04 e0 9f e5 ldr lr, [pc, #4]
0x1710: 0e e0 8f e0 add lr, pc, lr
0x1714: 08 f0 be e5 ldr pc, [lr, #8]!
; data inside code section at 0x1718 -- 0x171c
0x1718: b4 77 00 00 |.w.. |
After executing line 0x170c
, the LR
register should be set as the value at the address 0x1718
. The value is 0x77b4
(this file is stored in little-endian). And go ahead.
0x1710: lr += 0x1710 + 8 // lr = 0x8ecc
0x1714: pc = *(lr + 8) // pc = *(0x8ed4)
lr += 8
And 0x8ed4 is in .got
section.
; section: .got
0x8eac: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x8ebc: 00 00 00 00 58 70 00 00 e0 6e 00 00 00 00 00 00 |....Xp...n......|
0x8ecc: 00 00 00 00 00 00 00 00 00 00 00 00 08 17 00 00 |................|
0x8edc: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8eec: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8efc: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f0c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f1c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f2c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f3c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
It seems that the value at 0x8ed4
is zero. I traced to this strange block from JNI_OnLoad()
, so no data should be modified before executing this block.
Did I do something wrong, or is this a specific behavior of ARM architecture?
android arm asm
New contributor
Iven CJ7 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1
Are you familiar with GOT/PLT in other assembly languages? (usually, the x86 is better known). Because your question is, in fact, strongly related to the GOT/PLT behavior in general, and related to ARM architecture in particular.
– perror
5 hours ago
No. I am new to reverse engineering. I am googling relative information. Is there any good article for reading?
– Iven CJ7
5 hours ago
Ok, I'll write a complete answer, then.
– perror
5 hours ago
Note that this is not trivial! If you do not really understand at first read, this is perfectly normal. Try to fetch a debugger and to step through your program to better understand (and keep faith!).
– perror
4 hours ago
add a comment |Â
up vote
2
down vote
favorite
up vote
2
down vote
favorite
I tried to disassemble a ELF file which is a shared object file executed on armv7a (Android). I saw a strange block. It seems that the PC
, program counter register, is set to 0
. Did I miss something or do something wrong?
The process goes into 0x1708
in ARM mode. Below is the strange block of asm code I disassembled from the ELF file.
; section: .plt
; function: function_1708 at 0x1708 -- 0x1718
0x1708: 04 e0 2d e5 str lr, [sp, #-4]!
0x170c: 04 e0 9f e5 ldr lr, [pc, #4]
0x1710: 0e e0 8f e0 add lr, pc, lr
0x1714: 08 f0 be e5 ldr pc, [lr, #8]!
; data inside code section at 0x1718 -- 0x171c
0x1718: b4 77 00 00 |.w.. |
After executing line 0x170c
, the LR
register should be set as the value at the address 0x1718
. The value is 0x77b4
(this file is stored in little-endian). And go ahead.
0x1710: lr += 0x1710 + 8 // lr = 0x8ecc
0x1714: pc = *(lr + 8) // pc = *(0x8ed4)
lr += 8
And 0x8ed4 is in .got
section.
; section: .got
0x8eac: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x8ebc: 00 00 00 00 58 70 00 00 e0 6e 00 00 00 00 00 00 |....Xp...n......|
0x8ecc: 00 00 00 00 00 00 00 00 00 00 00 00 08 17 00 00 |................|
0x8edc: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8eec: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8efc: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f0c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f1c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f2c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f3c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
It seems that the value at 0x8ed4
is zero. I traced to this strange block from JNI_OnLoad()
, so no data should be modified before executing this block.
Did I do something wrong, or is this a specific behavior of ARM architecture?
android arm asm
New contributor
Iven CJ7 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
I tried to disassemble a ELF file which is a shared object file executed on armv7a (Android). I saw a strange block. It seems that the PC
, program counter register, is set to 0
. Did I miss something or do something wrong?
The process goes into 0x1708
in ARM mode. Below is the strange block of asm code I disassembled from the ELF file.
; section: .plt
; function: function_1708 at 0x1708 -- 0x1718
0x1708: 04 e0 2d e5 str lr, [sp, #-4]!
0x170c: 04 e0 9f e5 ldr lr, [pc, #4]
0x1710: 0e e0 8f e0 add lr, pc, lr
0x1714: 08 f0 be e5 ldr pc, [lr, #8]!
; data inside code section at 0x1718 -- 0x171c
0x1718: b4 77 00 00 |.w.. |
After executing line 0x170c
, the LR
register should be set as the value at the address 0x1718
. The value is 0x77b4
(this file is stored in little-endian). And go ahead.
0x1710: lr += 0x1710 + 8 // lr = 0x8ecc
0x1714: pc = *(lr + 8) // pc = *(0x8ed4)
lr += 8
And 0x8ed4 is in .got
section.
; section: .got
0x8eac: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x8ebc: 00 00 00 00 58 70 00 00 e0 6e 00 00 00 00 00 00 |....Xp...n......|
0x8ecc: 00 00 00 00 00 00 00 00 00 00 00 00 08 17 00 00 |................|
0x8edc: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8eec: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8efc: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f0c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f1c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f2c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
0x8f3c: 08 17 00 00 08 17 00 00 08 17 00 00 08 17 00 00 |................|
It seems that the value at 0x8ed4
is zero. I traced to this strange block from JNI_OnLoad()
, so no data should be modified before executing this block.
Did I do something wrong, or is this a specific behavior of ARM architecture?
android arm asm
android arm asm
New contributor
Iven CJ7 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Iven CJ7 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
edited 5 hours ago
perror
10.6k1763129
10.6k1763129
New contributor
Iven CJ7 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
asked 5 hours ago


Iven CJ7
133
133
New contributor
Iven CJ7 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Iven CJ7 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
Iven CJ7 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1
Are you familiar with GOT/PLT in other assembly languages? (usually, the x86 is better known). Because your question is, in fact, strongly related to the GOT/PLT behavior in general, and related to ARM architecture in particular.
– perror
5 hours ago
No. I am new to reverse engineering. I am googling relative information. Is there any good article for reading?
– Iven CJ7
5 hours ago
Ok, I'll write a complete answer, then.
– perror
5 hours ago
Note that this is not trivial! If you do not really understand at first read, this is perfectly normal. Try to fetch a debugger and to step through your program to better understand (and keep faith!).
– perror
4 hours ago
add a comment |Â
1
Are you familiar with GOT/PLT in other assembly languages? (usually, the x86 is better known). Because your question is, in fact, strongly related to the GOT/PLT behavior in general, and related to ARM architecture in particular.
– perror
5 hours ago
No. I am new to reverse engineering. I am googling relative information. Is there any good article for reading?
– Iven CJ7
5 hours ago
Ok, I'll write a complete answer, then.
– perror
5 hours ago
Note that this is not trivial! If you do not really understand at first read, this is perfectly normal. Try to fetch a debugger and to step through your program to better understand (and keep faith!).
– perror
4 hours ago
1
1
Are you familiar with GOT/PLT in other assembly languages? (usually, the x86 is better known). Because your question is, in fact, strongly related to the GOT/PLT behavior in general, and related to ARM architecture in particular.
– perror
5 hours ago
Are you familiar with GOT/PLT in other assembly languages? (usually, the x86 is better known). Because your question is, in fact, strongly related to the GOT/PLT behavior in general, and related to ARM architecture in particular.
– perror
5 hours ago
No. I am new to reverse engineering. I am googling relative information. Is there any good article for reading?
– Iven CJ7
5 hours ago
No. I am new to reverse engineering. I am googling relative information. Is there any good article for reading?
– Iven CJ7
5 hours ago
Ok, I'll write a complete answer, then.
– perror
5 hours ago
Ok, I'll write a complete answer, then.
– perror
5 hours ago
Note that this is not trivial! If you do not really understand at first read, this is perfectly normal. Try to fetch a debugger and to step through your program to better understand (and keep faith!).
– perror
4 hours ago
Note that this is not trivial! If you do not really understand at first read, this is perfectly normal. Try to fetch a debugger and to step through your program to better understand (and keep faith!).
– perror
4 hours ago
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
3
down vote
accepted
In fact, the behavior you are describing is coming from the usual behavior of the GOT/PLT sections. They are used to dynamically link the program calls to shared libraries functions at runtime.
In fact, a shared library can be loaded at any place in the process memory, there is no way to predict statically where it will pop up. So, the GOT/PLT load the address of each library's function dynamically at runtime and cache it for further use.
The way it works is pretty simple, the PLT (Procedure Linkage Table) is just a bunch of small code gadgets (one for each library's function called in the program, plus one generic code located at the beginning of the PLT section).
And, the GOT (Global Offset Table) is just a table used to store (and cache) the addresses of the library's functions.
At first, the GOT is all set to zero because no function address has been resolved yet. The GOT get filled all along the program is executed and call new functions (some functions may be called only rarely and their address may be only rarely filled in the GOT).
When a library function get called the program counter goes to the PLT (see the figure below) and executes the code specifically written for this function (each of the code gadget has its own offsets to get the proper address from the GOT). But, at first, the GOT contains only zeroes, so you need to initialize it.
To initialize it, you jump at the very beginning of the PLT and find the address of library's function the program want to call. You execute this code and, then, you write the address of the function in the GOT.
Once you cached the address of the function, you do not need anymore to recompute the address if you call your function and you jump directly to it (as in the following picture).
In the case of ARM, you have two types of GOT/PLT schemas:
Direct PLTs- Indirect PLTs
In your case, this is a direct PLT (but it seems to be quite old code, I would suspect an old compiler or so, because new ones better use ip
in place of lr
for that).
; section: .plt
; function: function_1708 at 0x1708 -- 0x1718
0x1708: 04 e0 2d e5 str lr, [sp, #-4]! <-- Save lr on the stack
0x170c: 04 e0 9f e5 ldr lr, [pc, #4] <-- Get a point of reference in memory
0x1710: 0e e0 8f e0 add lr, pc, lr <-- Load offset to the next step
0x1714: 08 f0 be e5 ldr pc, [lr, #8]! <-- Jump to the next step
Note that the PLT is a static section, therefore the 0x170c
will always stay the same (and, therefore, the pc
will always be loaded with the same value at 0x170c
).
References
Position Independent Code (PIC) in shared libraries, by Eli Bendersky.
ARM assembler in Raspberry Pi – Chapter 27 , by Roger Ferrer Ibáñez.
ARM FDPIC toolset, kernel & libraries for Cortex-M & Cortex-R.
So, in my situation, function_1708 is PLT[0], the code to call resolver, right? How does the linker know where to fill its memory address to the correct address of the shared library? In my case,0x8ed4
is not the first address in.got
. The first is0x8eac
. The strategy of filling the address of the resolver is depended on the format of the ELF file, the running operating system, or the architecture or CPU?
– Iven CJ7
3 hours ago
At the beginning of each run of your program, the linker set the address where the shared library is in the PLT and each function used by the program is given an offset in the current shared library. So that, if you know the address of the shared library in memory and the offset of a specific function you can get the address of the function in the virtual memory. You compute this only once, thanks to the GOT (which cache the result).
– perror
3 hours ago
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
accepted
In fact, the behavior you are describing is coming from the usual behavior of the GOT/PLT sections. They are used to dynamically link the program calls to shared libraries functions at runtime.
In fact, a shared library can be loaded at any place in the process memory, there is no way to predict statically where it will pop up. So, the GOT/PLT load the address of each library's function dynamically at runtime and cache it for further use.
The way it works is pretty simple, the PLT (Procedure Linkage Table) is just a bunch of small code gadgets (one for each library's function called in the program, plus one generic code located at the beginning of the PLT section).
And, the GOT (Global Offset Table) is just a table used to store (and cache) the addresses of the library's functions.
At first, the GOT is all set to zero because no function address has been resolved yet. The GOT get filled all along the program is executed and call new functions (some functions may be called only rarely and their address may be only rarely filled in the GOT).
When a library function get called the program counter goes to the PLT (see the figure below) and executes the code specifically written for this function (each of the code gadget has its own offsets to get the proper address from the GOT). But, at first, the GOT contains only zeroes, so you need to initialize it.
To initialize it, you jump at the very beginning of the PLT and find the address of library's function the program want to call. You execute this code and, then, you write the address of the function in the GOT.
Once you cached the address of the function, you do not need anymore to recompute the address if you call your function and you jump directly to it (as in the following picture).
In the case of ARM, you have two types of GOT/PLT schemas:
Direct PLTs- Indirect PLTs
In your case, this is a direct PLT (but it seems to be quite old code, I would suspect an old compiler or so, because new ones better use ip
in place of lr
for that).
; section: .plt
; function: function_1708 at 0x1708 -- 0x1718
0x1708: 04 e0 2d e5 str lr, [sp, #-4]! <-- Save lr on the stack
0x170c: 04 e0 9f e5 ldr lr, [pc, #4] <-- Get a point of reference in memory
0x1710: 0e e0 8f e0 add lr, pc, lr <-- Load offset to the next step
0x1714: 08 f0 be e5 ldr pc, [lr, #8]! <-- Jump to the next step
Note that the PLT is a static section, therefore the 0x170c
will always stay the same (and, therefore, the pc
will always be loaded with the same value at 0x170c
).
References
Position Independent Code (PIC) in shared libraries, by Eli Bendersky.
ARM assembler in Raspberry Pi – Chapter 27 , by Roger Ferrer Ibáñez.
ARM FDPIC toolset, kernel & libraries for Cortex-M & Cortex-R.
So, in my situation, function_1708 is PLT[0], the code to call resolver, right? How does the linker know where to fill its memory address to the correct address of the shared library? In my case,0x8ed4
is not the first address in.got
. The first is0x8eac
. The strategy of filling the address of the resolver is depended on the format of the ELF file, the running operating system, or the architecture or CPU?
– Iven CJ7
3 hours ago
At the beginning of each run of your program, the linker set the address where the shared library is in the PLT and each function used by the program is given an offset in the current shared library. So that, if you know the address of the shared library in memory and the offset of a specific function you can get the address of the function in the virtual memory. You compute this only once, thanks to the GOT (which cache the result).
– perror
3 hours ago
add a comment |Â
up vote
3
down vote
accepted
In fact, the behavior you are describing is coming from the usual behavior of the GOT/PLT sections. They are used to dynamically link the program calls to shared libraries functions at runtime.
In fact, a shared library can be loaded at any place in the process memory, there is no way to predict statically where it will pop up. So, the GOT/PLT load the address of each library's function dynamically at runtime and cache it for further use.
The way it works is pretty simple, the PLT (Procedure Linkage Table) is just a bunch of small code gadgets (one for each library's function called in the program, plus one generic code located at the beginning of the PLT section).
And, the GOT (Global Offset Table) is just a table used to store (and cache) the addresses of the library's functions.
At first, the GOT is all set to zero because no function address has been resolved yet. The GOT get filled all along the program is executed and call new functions (some functions may be called only rarely and their address may be only rarely filled in the GOT).
When a library function get called the program counter goes to the PLT (see the figure below) and executes the code specifically written for this function (each of the code gadget has its own offsets to get the proper address from the GOT). But, at first, the GOT contains only zeroes, so you need to initialize it.
To initialize it, you jump at the very beginning of the PLT and find the address of library's function the program want to call. You execute this code and, then, you write the address of the function in the GOT.
Once you cached the address of the function, you do not need anymore to recompute the address if you call your function and you jump directly to it (as in the following picture).
In the case of ARM, you have two types of GOT/PLT schemas:
Direct PLTs- Indirect PLTs
In your case, this is a direct PLT (but it seems to be quite old code, I would suspect an old compiler or so, because new ones better use ip
in place of lr
for that).
; section: .plt
; function: function_1708 at 0x1708 -- 0x1718
0x1708: 04 e0 2d e5 str lr, [sp, #-4]! <-- Save lr on the stack
0x170c: 04 e0 9f e5 ldr lr, [pc, #4] <-- Get a point of reference in memory
0x1710: 0e e0 8f e0 add lr, pc, lr <-- Load offset to the next step
0x1714: 08 f0 be e5 ldr pc, [lr, #8]! <-- Jump to the next step
Note that the PLT is a static section, therefore the 0x170c
will always stay the same (and, therefore, the pc
will always be loaded with the same value at 0x170c
).
References
Position Independent Code (PIC) in shared libraries, by Eli Bendersky.
ARM assembler in Raspberry Pi – Chapter 27 , by Roger Ferrer Ibáñez.
ARM FDPIC toolset, kernel & libraries for Cortex-M & Cortex-R.
So, in my situation, function_1708 is PLT[0], the code to call resolver, right? How does the linker know where to fill its memory address to the correct address of the shared library? In my case,0x8ed4
is not the first address in.got
. The first is0x8eac
. The strategy of filling the address of the resolver is depended on the format of the ELF file, the running operating system, or the architecture or CPU?
– Iven CJ7
3 hours ago
At the beginning of each run of your program, the linker set the address where the shared library is in the PLT and each function used by the program is given an offset in the current shared library. So that, if you know the address of the shared library in memory and the offset of a specific function you can get the address of the function in the virtual memory. You compute this only once, thanks to the GOT (which cache the result).
– perror
3 hours ago
add a comment |Â
up vote
3
down vote
accepted
up vote
3
down vote
accepted
In fact, the behavior you are describing is coming from the usual behavior of the GOT/PLT sections. They are used to dynamically link the program calls to shared libraries functions at runtime.
In fact, a shared library can be loaded at any place in the process memory, there is no way to predict statically where it will pop up. So, the GOT/PLT load the address of each library's function dynamically at runtime and cache it for further use.
The way it works is pretty simple, the PLT (Procedure Linkage Table) is just a bunch of small code gadgets (one for each library's function called in the program, plus one generic code located at the beginning of the PLT section).
And, the GOT (Global Offset Table) is just a table used to store (and cache) the addresses of the library's functions.
At first, the GOT is all set to zero because no function address has been resolved yet. The GOT get filled all along the program is executed and call new functions (some functions may be called only rarely and their address may be only rarely filled in the GOT).
When a library function get called the program counter goes to the PLT (see the figure below) and executes the code specifically written for this function (each of the code gadget has its own offsets to get the proper address from the GOT). But, at first, the GOT contains only zeroes, so you need to initialize it.
To initialize it, you jump at the very beginning of the PLT and find the address of library's function the program want to call. You execute this code and, then, you write the address of the function in the GOT.
Once you cached the address of the function, you do not need anymore to recompute the address if you call your function and you jump directly to it (as in the following picture).
In the case of ARM, you have two types of GOT/PLT schemas:
Direct PLTs- Indirect PLTs
In your case, this is a direct PLT (but it seems to be quite old code, I would suspect an old compiler or so, because new ones better use ip
in place of lr
for that).
; section: .plt
; function: function_1708 at 0x1708 -- 0x1718
0x1708: 04 e0 2d e5 str lr, [sp, #-4]! <-- Save lr on the stack
0x170c: 04 e0 9f e5 ldr lr, [pc, #4] <-- Get a point of reference in memory
0x1710: 0e e0 8f e0 add lr, pc, lr <-- Load offset to the next step
0x1714: 08 f0 be e5 ldr pc, [lr, #8]! <-- Jump to the next step
Note that the PLT is a static section, therefore the 0x170c
will always stay the same (and, therefore, the pc
will always be loaded with the same value at 0x170c
).
References
Position Independent Code (PIC) in shared libraries, by Eli Bendersky.
ARM assembler in Raspberry Pi – Chapter 27 , by Roger Ferrer Ibáñez.
ARM FDPIC toolset, kernel & libraries for Cortex-M & Cortex-R.
In fact, the behavior you are describing is coming from the usual behavior of the GOT/PLT sections. They are used to dynamically link the program calls to shared libraries functions at runtime.
In fact, a shared library can be loaded at any place in the process memory, there is no way to predict statically where it will pop up. So, the GOT/PLT load the address of each library's function dynamically at runtime and cache it for further use.
The way it works is pretty simple, the PLT (Procedure Linkage Table) is just a bunch of small code gadgets (one for each library's function called in the program, plus one generic code located at the beginning of the PLT section).
And, the GOT (Global Offset Table) is just a table used to store (and cache) the addresses of the library's functions.
At first, the GOT is all set to zero because no function address has been resolved yet. The GOT get filled all along the program is executed and call new functions (some functions may be called only rarely and their address may be only rarely filled in the GOT).
When a library function get called the program counter goes to the PLT (see the figure below) and executes the code specifically written for this function (each of the code gadget has its own offsets to get the proper address from the GOT). But, at first, the GOT contains only zeroes, so you need to initialize it.
To initialize it, you jump at the very beginning of the PLT and find the address of library's function the program want to call. You execute this code and, then, you write the address of the function in the GOT.
Once you cached the address of the function, you do not need anymore to recompute the address if you call your function and you jump directly to it (as in the following picture).
In the case of ARM, you have two types of GOT/PLT schemas:
Direct PLTs- Indirect PLTs
In your case, this is a direct PLT (but it seems to be quite old code, I would suspect an old compiler or so, because new ones better use ip
in place of lr
for that).
; section: .plt
; function: function_1708 at 0x1708 -- 0x1718
0x1708: 04 e0 2d e5 str lr, [sp, #-4]! <-- Save lr on the stack
0x170c: 04 e0 9f e5 ldr lr, [pc, #4] <-- Get a point of reference in memory
0x1710: 0e e0 8f e0 add lr, pc, lr <-- Load offset to the next step
0x1714: 08 f0 be e5 ldr pc, [lr, #8]! <-- Jump to the next step
Note that the PLT is a static section, therefore the 0x170c
will always stay the same (and, therefore, the pc
will always be loaded with the same value at 0x170c
).
References
Position Independent Code (PIC) in shared libraries, by Eli Bendersky.
ARM assembler in Raspberry Pi – Chapter 27 , by Roger Ferrer Ibáñez.
ARM FDPIC toolset, kernel & libraries for Cortex-M & Cortex-R.
edited 4 hours ago
answered 4 hours ago
perror
10.6k1763129
10.6k1763129
So, in my situation, function_1708 is PLT[0], the code to call resolver, right? How does the linker know where to fill its memory address to the correct address of the shared library? In my case,0x8ed4
is not the first address in.got
. The first is0x8eac
. The strategy of filling the address of the resolver is depended on the format of the ELF file, the running operating system, or the architecture or CPU?
– Iven CJ7
3 hours ago
At the beginning of each run of your program, the linker set the address where the shared library is in the PLT and each function used by the program is given an offset in the current shared library. So that, if you know the address of the shared library in memory and the offset of a specific function you can get the address of the function in the virtual memory. You compute this only once, thanks to the GOT (which cache the result).
– perror
3 hours ago
add a comment |Â
So, in my situation, function_1708 is PLT[0], the code to call resolver, right? How does the linker know where to fill its memory address to the correct address of the shared library? In my case,0x8ed4
is not the first address in.got
. The first is0x8eac
. The strategy of filling the address of the resolver is depended on the format of the ELF file, the running operating system, or the architecture or CPU?
– Iven CJ7
3 hours ago
At the beginning of each run of your program, the linker set the address where the shared library is in the PLT and each function used by the program is given an offset in the current shared library. So that, if you know the address of the shared library in memory and the offset of a specific function you can get the address of the function in the virtual memory. You compute this only once, thanks to the GOT (which cache the result).
– perror
3 hours ago
So, in my situation, function_1708 is PLT[0], the code to call resolver, right? How does the linker know where to fill its memory address to the correct address of the shared library? In my case,
0x8ed4
is not the first address in .got
. The first is 0x8eac
. The strategy of filling the address of the resolver is depended on the format of the ELF file, the running operating system, or the architecture or CPU?– Iven CJ7
3 hours ago
So, in my situation, function_1708 is PLT[0], the code to call resolver, right? How does the linker know where to fill its memory address to the correct address of the shared library? In my case,
0x8ed4
is not the first address in .got
. The first is 0x8eac
. The strategy of filling the address of the resolver is depended on the format of the ELF file, the running operating system, or the architecture or CPU?– Iven CJ7
3 hours ago
At the beginning of each run of your program, the linker set the address where the shared library is in the PLT and each function used by the program is given an offset in the current shared library. So that, if you know the address of the shared library in memory and the offset of a specific function you can get the address of the function in the virtual memory. You compute this only once, thanks to the GOT (which cache the result).
– perror
3 hours ago
At the beginning of each run of your program, the linker set the address where the shared library is in the PLT and each function used by the program is given an offset in the current shared library. So that, if you know the address of the shared library in memory and the offset of a specific function you can get the address of the function in the virtual memory. You compute this only once, thanks to the GOT (which cache the result).
– perror
3 hours ago
add a comment |Â
Iven CJ7 is a new contributor. Be nice, and check out our Code of Conduct.
Iven CJ7 is a new contributor. Be nice, and check out our Code of Conduct.
Iven CJ7 is a new contributor. Be nice, and check out our Code of Conduct.
Iven CJ7 is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2freverseengineering.stackexchange.com%2fquestions%2f19378%2fdisassemble-elf-pc-is-set-to-0%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
1
Are you familiar with GOT/PLT in other assembly languages? (usually, the x86 is better known). Because your question is, in fact, strongly related to the GOT/PLT behavior in general, and related to ARM architecture in particular.
– perror
5 hours ago
No. I am new to reverse engineering. I am googling relative information. Is there any good article for reading?
– Iven CJ7
5 hours ago
Ok, I'll write a complete answer, then.
– perror
5 hours ago
Note that this is not trivial! If you do not really understand at first read, this is perfectly normal. Try to fetch a debugger and to step through your program to better understand (and keep faith!).
– perror
4 hours ago