Disassemble ELF - PC is set to 0?

The name of the pictureThe name of the pictureThe name of the pictureClash 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?










share|improve this question









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














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?










share|improve this question









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












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?










share|improve this question









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






share|improve this question









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.











share|improve this question









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.









share|improve this question




share|improve this question








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












  • 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










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.



PLT/GOT Schema (before)



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).



PLT/GOT Schema (after)



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.






share|improve this answer






















  • 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











Your Answer







StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "489"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
noCode: true, onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);






Iven CJ7 is a new contributor. Be nice, and check out our Code of Conduct.









 

draft saved


draft discarded


















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






























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.



PLT/GOT Schema (before)



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).



PLT/GOT Schema (after)



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.






share|improve this answer






















  • 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















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.



PLT/GOT Schema (before)



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).



PLT/GOT Schema (after)



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.






share|improve this answer






















  • 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













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.



PLT/GOT Schema (before)



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).



PLT/GOT Schema (after)



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.






share|improve this answer














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.



PLT/GOT Schema (before)



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).



PLT/GOT Schema (after)



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.







share|improve this answer














share|improve this answer



share|improve this answer








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 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

















  • 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
















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











Iven CJ7 is a new contributor. Be nice, and check out our Code of Conduct.









 

draft saved


draft discarded


















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.













 


draft saved


draft discarded














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













































































Comments

Popular posts from this blog

What does second last employer means? [closed]

List of Gilmore Girls characters

Confectionery