section:“.bss” contains the uninitialized global data, while .data contains all the initialized
data.
In general, the data segment of the executable contains
initialized global/static variables and the BSS segment contains uninitialized
global/static variables.
The GOT is private to each process, and the process must
have write permissions to it. Conversely the library code is shared and the
process should have only read and execute permissions on the code; it would be
a serious security breach if the process could modify code.
------------------------------------------------------------------------------------------------
jtony@genoa:~/scrum/s7$ cat got.cextern int i;
void test(void)
{
i = 100;
}
------------------------------------------------------------------------------------------------
Above we create a simple shared library which refers to an external symbol. We do not know the address of this symbol at compile time, so we leave it for the dynamic linker to fix up at runtime. But we want our code to remain sharable, in case other processes want to use our code as well.
------------------------------------------------------------------------------------------------
$ clang -nostdlib -shared -o got.so ./got.c
$ readelf --sections ./got.so
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags
Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0
0 0
[ 1] .gnu.hash GNU_HASH 0000000000000158 00000158
0000000000000034 0000000000000000 A
2 0 8
…
[ 9] .got
PROGBITS
0000000000020000 00010000
0000000000000010
0000000000000008 WA 0
0 8
[10] .comment PROGBITS 0000000000000000 00010010
00000000000000a5 0000000000000001 MS
0 0 1
------------------------------------------------------------------------------------------------
If we have a look at the readelf output above
we can see that the .got section starts 0x20000
bytes past where library was loaded into memory. Thus if the library were to be loaded into
memory at address 0x6000000000000000 the .got would be at
0x6000000000020000, The disassembly reveals just how we
do this with the .got.
-----------------------------------
objdump --disassemble ./got.so
Disassembly
of section .text:
0000000000000290
<test>:
290:
02 00 4c 3c addis r2,r12,2
294:
70 7d 42 38 addi r2,r2,32112
298:
64 00 60 38 li r3,100
29c:
00 00 00 60 nop
2a4:
00 00 64 90 stw r3,0(r4)
2a8:
20 00 80 4e blr
...
On ppc64le, the
register r2 is known as the Toc pointer and always points to TOC base address.
The symbol .TOC. may be used to
access the GOT or in TOC-relative addressing to other data constructs. The
symbol may be offset by 0x8000 bytes, or another offset, from the start of the
.got section. I think after line 290 – 294 (addis, addi) [need to confirm] , r2 will point to .GOT + 0x8000 (i.e
32768). Then the ld on line 2a0, will
set r4 point to .GOT + (32768 – 32760), i.e. r4 point to .GOT + 8
Table 4: Relocation
-----------------------------------
jtony@genoa:~/scrum/s7$
readelf --relocs ./got.so
Relocation
section '.rela.dyn' at offset 0x270 contains 1 entries:
000000020008 000300000026
R_PPC64_ADDR64 0000000000000000 i + 0
-----------------------------------
The relocation says "replace the value at offset 0x20008
with the memory location that symbol i is
stored at".
We know that the .got starts at
offset 0x20000 from the previous output. We have also seen how the code loads
an address 0x8 past this ( using ld r4,-32760(r2)) giving us an address of 0x20000 + 0x8 =
0x20008 ... the address which the relocation is for!
So before the program begins, the dynamic linker
will have fixed up the relocation to ensure that the value of the memory at
offset 0x20008 is the address of the global variable i!
No comments:
Post a Comment