I wrote simple shared library:
extern void some_func(void); void function(void) { some_func(); }
Compiled/built:
gcc -fPIC -mcmodel=large -c test.c -o test.o gcc -fPIC -shared test.o -o libtest.so
Disassembled, to see how some_func is referenced:
$ objdump -d libtest.so 00000000000006a0 <function>: 6a0: 55 push %rbp 6a1: 48 89 e5 mov %rsp,%rbp 6a4: 41 57 push %r15 6a6: 48 83 ec 08 sub $0x8,%rsp 6aa: 48 8d 05 f9 ff ff ff lea -0x7(%rip),%rax # 6aa <function+0xa> 6b1: 49 bb 56 09 20 00 00 movabs $0x200956,%r11 6b8: 00 00 00 6bb: 4c 01 d8 add %r11,%rax 6be: 49 89 c7 mov %rax,%r15 6c1: 48 ba 80 f5 df ff ff movabs $0xffffffffffdff580,%rdx 6c8: ff ff ff 6cb: 48 01 c2 add %rax,%rdx 6ce: ff d2 callq *%rdx 6d0: 90 nop 6d1: 48 83 c4 08 add $0x8,%rsp 6d5: 41 5f pop %r15 6d7: 5d pop %rbp 6d8: c3 retq
Looked where .got.plt
is located:
$ readelf -S libtest.so ... [21] .got.plt PROGBITS 0000000000201000 00001000 0000000000000020 0000000000000008 WA 0 0 8 ...
What is the relocation:
$ readelf -r libtest.so Relocation section '.rela.plt' at offset 0x538 contains 1 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000201018 000400000007 R_X86_64_JUMP_SLO 0000000000000000 some_func + 0
In 6aa
–6bb
we get absolute location of GOT: 6aa + 0x200956 = 0x201000
That agrees with readelf -S libtest.so
‘s output.
We skip 3 reserved bytes in GOT(functions-related) and determine that some_func’s absolute address should be found at +0x18(forth byte from GOT) offset at runtime.
That agrees with readelf -r libtest.so
.
But 6c1 instruction in objdump’s disassembly shows:
movabs $0xfff...dff580, %rdx
I expect that source operand will hold +0x18
(offset from GOT, its address located at rax
), but instead it has some large negative number.
Could you explain what it shows that number but not 0x18
?
Advertisement
Answer
There are two kinds of relocations: static and dynamic (1); one for static linker ld
and other for loader (dynamic linker, rtld) – ld-linux.so.2
for linux’s glibc 2.* (check Dynamic Linking and Loading, 1999 or Static Linkers and Dyanmic Link Loaders).
When you use objdump
to dump relocations, it has -r
option for static relocations, and -R
for dynamic relocations.
Your case is not just GOT, it is GOT.PLT – GOT used for procedute linkage. This kind of access uses dynamic relocations. So, you should check output of objdump -dR libtest.so
, it will show you both disassembly and dynamic relocations in it.
Cited line from readelf -r libtest.so
is just for PLT table, not for the code.
http://www.airs.com/blog/archives/41
or function calls, the program linker will set up a PLT entry to look like this:
jmp *offset(%ebx) pushl #index jmp first_plt_entryThe program linker will allocate an entry in the GOT for each entry in the PLT. It will create a dynamic relocation for the GOT entry of type JMP_SLOT. It will initialize the GOT entry to the base address of the shared library plus the address of the second instruction in the code sequence above. When the dynamic linker does the initial lazy binding on a JMP_SLOT reloc, it will simply add the difference between the shared library load address and the shared library base address to the GOT entry. The effect is that the first jmp instruction will jump to the second instruction, which will push the index entry and branch to the first PLT entry. The first PLT entry is special, and looks like this:
pushl 4(%ebx) jmp *8(%ebx)This references the second and third entries in the GOT. The dynamic linker will initialize them to have appropriate values for a callback into the dynamic linker itself. The dynamic linker will use the index pushed by the first code sequence to find the JMP_SLOT relocation. When the dynamic linker determines the function to be called, it will store the address of the function into the GOT entry references by the first code sequence. Thus, the next time the function is called, the jmp instruction will branch directly to the right code.