Runtime Symbol Resolution


Published on

This presentation material is about runtime symbol resolution on Linux x86-64. We will not discuss Windows/Mac implementation.

  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Runtime Symbol Resolution

  1. 1. Runtime Symbol Resolution
  2. 2. Outline <ul><li>Overview of Dynamic Library
  3. 3. Relocatable code / Relocation section
  4. 4. Executable code / PLT & GOT
  5. 5. Runtime process analysis with GDB
  6. 6. Summary </li></ul>
  7. 7. Overview <ul><li>This is about runtime symbol resolution on Linux x86-64 </li><ul><li>Windows/Mac have different mechanisms </li></ul><li>Assume source code is written in C
  8. 8. “Linker” means “static linker” (not dynamic) in this material </li></ul>
  9. 9. Build (& Load) Process Usually you just type “gcc foo.c” and it invokes the four sub-processes for you. (text) (text) (text) (ELF) (ELF) (ELF) C code Preprocessed C code Assembly code Executable code Relocatable code Preprocessor Compiler Process Image Assembler Linker Loader DL
  10. 10. What's Dynamic Library? <ul><li>Dynamic library is linked at runtime </li><ul><li>While static library is linked at “compile-time” </li></ul><li>Shared by more than one application </li><ul><li>Often called “Shared library” </li></ul></ul>Process 1 Process 2 Dynamic Lib Static Lib Static Lib Process 1 Process 2 Memory (or storage) image
  11. 11. How is DL different? <ul><li>Address of symbols (functions, variables) are only known at run-time </li><ul><li>Linker cannot tell where the DL will be loaded
  12. 12. Executables cannot contain exact addresses </li></ul><li>Loader must play a role so that the code can call functions (or refer to variables) defined in DLs </li></ul>
  13. 13. Sample Code: Hello, World! #include <stdio.h> int main() { puts(&quot;Hello, World!&quot;); return 0; } hello.c
  14. 14. Build <ul><li>Preprocess & Compile & Assemble </li><ul><li>“gcc -c hello.c” generates a relocatable file “hello.o” </li></ul><li>Preprocess & Compile & Assemble & Link </li><ul><li>“gcc hello.c” generates an executable file “a.out” </li></ul></ul>% ls a.out hello.c hello.o
  15. 15. Relocatable code % objdump -d hello.o // disassemble text section [...] 0000000000000000 <main>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: bf 00 00 00 00 mov $0x0,%edi 9: e8 00 00 00 00 callq e <main+0xe> e: b8 00 00 00 00 mov $0x0,%eax 13: 5d pop %rbp 14: c3 retq This must be a call to “puts”. But ...
  16. 16. “e8” or “callq” <ul><li>Instruction “e8” is “Call … displacement relative to next instruction...” </li><ul><li>See “Intel Software Developer's Manual” </li></ul><li>“e8 00 00 00 00” means “call next instruction”, which doesn't make sense
  17. 17. “00 00 00 00” must be replaced with “puts” </li><ul><li>“hello.o” must contain relocation info </li></ul></ul>
  18. 18. Relocation section % readelf -r hello.o # output edited for better readability Relocation section '.rela.text' at offset 0x598 contains 2 entries: Offset Info Type Sym. Val. Sym. Name + Addend 00000005 00050000000a R_X86_64_32 00000000 .rodata + 0 0000000a 000a 00000002 R_X86_64_PC32 00000000 puts - 4 […] The symbol has index 0x0a (= “puts”) Show relocation section Replace value at 0x0a addend Value = [value of symbol] + [addend] - [offset]
  19. 19. Summary: Relocatable code <ul><li>Code (or “.text” section) has zero as an address
  20. 20. Relocation table (or “.rela” section) tells what to replace the zero with
  21. 21. The later process must utilize the relocation info to actually link “puts” to the code </li></ul>
  22. 22. Executable code % objdump -d a.out // disassemble text section [...] 00000000004004c4 <main>: 4004c4: 55 push %rbp 4004c5: 48 89 e5 mov %rsp,%rbp 4004c8: bf e0 05 40 00 mov $0x4005e0,%edi 4004cd: e8 e6 fe ff ff callq 4003b8 <puts@plt> 4004d2: b8 00 00 00 00 mov $0x0,%eax 4004d7: 5d pop %rbp 4004d8: c3 retq Calling “puts@plt”, not “puts”
  23. 23. Executable code: PLT % objdump -d a.out [...] 00000000004003a8 <puts@plt-0x10>: 4003a8: pushq 0x2004b2(%rip) # 600860 4003ae: jmpq *0x2004b4(%rip) # 600868 4003b4: nopl 0x0(%rax) 00000000004003b8 < [email_address] >: 4003b8: jmpq *0x2004b2(%rip) # 600870 4003be: pushq $0x0 4003c3: jmpq 4003a8 <_init+0x18> _GLOBAL_OFFSET_TABLE_+0x8 _GLOBAL_OFFSET_TABLE_+0x10 _GLOBAL_OFFSET_TABLE_+0x18 Machine code is omitted as it's getting cryptic...
  24. 24. Executable code: GOT Symbol Address Value _GLOBAL_OFFSET_TABLE_ + 0x0 0x600858 0x006006c0 _GLOBAL_OFFSET_TABLE_ + 0x8 0x600860 0x00000000 _GLOBAL_OFFSET_TABLE_ + 0x10 0x600868 0x00000000 _GLOBAL_OFFSET_TABLE_ + 0x18 0x600870 0x004003be _GLOBAL_OFFSET_TABLE_ + 0x20 0x600878 0x004003ce <ul><li>GOT contents can be shown with objdump </li><ul><li>“objdump -s --start-address=0x600858 --stop-address=0x600880 a.out” </li></ul></ul>
  25. 25. Summary: Executable code <ul><li>When “puts@plt” is called … </li><ul><li>Jump to an address stored in GOT </li><ul><li>The address points to the next instruction </li></ul><li>Push 0 to stack
  26. 26. Jump to “puts@plt – 0x10”
  27. 27. Push an address of a GOT entry to stack
  28. 28. Jump to the address stored in GOT </li><ul><li>The address is set to 0 </li></ul></ul><li>What's going on??? </li></ul>??? Jumping to address 0 will crash the process What's this 0? What's this address used for?
  29. 29. A bit more analysis... <ul><li>Just before jumping to address 0, stack should look like...
  30. 30. Executable code has “.rela.plt” section </li></ul>Relocation section '.rela.plt' at offset 0x360 contains 2 entries: Offset Info Type Sym. Val. Sym. Name + Addend 00600870 […] R_X86_64_JUMP_SLO 00000000 puts + 0 00600878 […] R_X86_64_JUMP_SLO 00000000 __libc_start_main + 0 Stack Top ... 0x600860 0
  31. 31. Process Image <ul><li>GOT in the executable code looks weird. Gdb can show us the real GOT at runtime </li></ul>% gdb a.out [...] Reading symbols from /foo/a.out...[...] (gdb) start Temporary breakpoint 1 at 0x4004c8 Starting program: /foo/a.out Temporary breakpoint 1, 0x00000000004004c8 in main ()
  32. 32. Process Image: GOT <ul><li>Remember GOT is stored at 0x600858 </li></ul>(gdb) x/a 0x600858 0x600858: 0x6006c0 <_DYNAMIC> (gdb) x/a 0x600860 0x600860: 0x302cc20288 (gdb) x/a 0x600868 0x600868: 0x302ca13850 <_dl_runtime_resolve> (gdb) x/a 0x600870 0x600870: 0x4003be <puts@plt+6> (gdb) x/a 0x600878 0x600878: 0x302ce212b0 <__libc_start_main>
  33. 33. Changes in GOT Address Value in Executable Value in Process 0x600858 0x6006c0 0x6006c0 0x600860 0x0 0x302cc20288 0x600868 0x0 0x302ca13850 <_dl_runtime_resolve> 0x600870 0x4003be <_puts@plt+6> 0x4003be <_puts@plt+6> 0x600878 0x4003ce <__libc_start_main@plt+6> 0x302ce212b0 <__libc_start_main> Ignore this change for now
  34. 34. Revisiting “puts@plt” <ul><li>When “puts@plt” is called … </li><ul><li>Jump to an address stored in GOT </li><ul><li>The address points to the next instruction </li></ul><li>Push 0 to stack
  35. 35. Jump to “puts@plt – 0x10”
  36. 36. Push an address of a GOT entry to stack </li><ul><li>The address stores value 0x302cc20288 </li></ul><li>Jump to “_dl_runtime_resolve” </li><ul><li>The name suggests “runtime symbol resolution” </li></ul></ul></ul>??? What's this 0? What's this address used for?
  37. 37. _dl_runtime_resolve <ul><li>Don't have time to dive into the function :-)
  38. 38. Takes 2 arguments </li><ul><li>A pointer to a structure containing DL info </li><ul><li>0x302cc20288 in this case </li></ul><li>An index of relocation entry in “.rela.plt” </li><ul><li>0 for “puts”; 1 for “__libc_start_main” </li></ul></ul><li>Replaces a value in GOT
  39. 39. Jmp to the function </li><ul><li>“puts” in this case </li></ul></ul>
  40. 40. Revisiting “puts@plt” (3 rd time) <ul><li>When “puts@plt” is called … </li><ul><li>Jump to an address stored in GOT </li><ul><li>The address points to the next instruction </li></ul><li>Push 0 to stack </li><ul><li>2 nd argument to “_dl_runtime_resolve” </li></ul><li>Jump to “puts@plt – 0x10”
  41. 41. Push an address of a GOT entry to stack </li><ul><li>1 st argument to “_dl_runtime_resolve” </li></ul><li>Jump to “_dl_runtime_resolve” </li><ul><li>GOT entry is replaced with the address of “puts” </li></ul></ul></ul>???
  42. 42. GOT after calling “puts” (gdb) ni 0x00000000004004cd in main () (gdb) ni Hello, World! 0x00000000004004d2 in main () (gdb) x/a 0x600870 0x600870: 0x302ce692e0 <puts> Step over to next instruction “ puts” is called Now the GOT entry contains the actual address of “puts” !
  43. 43. Lazy link <ul><li>What happens if the code has another call to “puts” </li><ul><li>“puts@plt” is called
  44. 44. Jump to an address stored in GOT </li><ul><li>Which is now an exact address of “puts”
  45. 45. No need to jump to “_dl_runtime_resolve” </li></ul></ul><li>The complicated code in “.plt” section is for lazy link </li><ul><li>Every function call is resolved on the first call </li></ul></ul>
  46. 46. Revisiting “puts@plt” (4 th time) <ul><li>When “puts@plt” is called … </li><ul><li>Jump to an address stored in GOT </li><ul><li>The address initially points to the next instruction and is later replaced with “puts” address </li></ul><li>Push 0 to stack
  47. 47. Jump to “puts@plt – 0x10”
  48. 48. Push an address of a GOT entry to stack
  49. 49. Jump to “_dl_runtime_resolve” </li><ul><li>Which uses 0 and the GOT entry address </li></ul></ul></ul>
  50. 50. Summary <ul><li>How dynamic link is implemented </li><ul><li>“.rela” section in ELF
  51. 51. Indirect call via Procedure Linkage Table
  52. 52. Lazy linking by using Global Offset Table </li><ul><li>Help to speed up application launch </li></ul><li>“_dl_runtime_resolve” does the linking </li><ul><li>Defined in glibc. Might be interesting to look into it. </li></ul></ul></ul>
  53. 53. References <ul><li>AMD64 Application Binary Interface </li><ul><li> </li></ul><li>Intel386 Architecture Processor Supplement </li><ul><li> </li></ul><li>Intel Software Developer Manual </li><ul><li> </li></ul><li>Linkers and Loaders </li><ul><li> </li></ul></ul>