Abstract This article describes data sections of ELF64 file and the difference among data sections. This is a interesting part to begin to get to know about PIC (Position Independent code) data reference and function call.
Difference between .bss, .data, and .rodata sections
Section
Diff 1
Diff 2
.rodata
Read-only data, constant strings, global, variables with const modification and static variables.
Occupy space in object files
.data
Global, static variables with an initial value of non-zero (without const).
Occupy space in object files
.bss
Global, static variables are not initialized or have an initial value of 0 (without const).
No space in object files.
Local non-static variables - runtime stack.
gcc
marks the uninitialized global variables in C programs (file .c
) as COMMON
instead of putting them in .bss
section.
C++ programs, uninitialized global variables in the program will be placed in .bss
section.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 ethanol@ethanol:~/Desktop/sections$ readelf -S datasection There are 13 section headers, starting at offset 0x388: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 0000000000000000 00000040 0000000000000018 0000000000000000 AX 0 0 1 [ 2] .data PROGBITS 0000000000000000 00000058 0000000000000008 0000000000000000 WA 0 0 4 [ 3] .bss NOBITS 0000000000000000 00000060 000000000000000c 0000000000000000 WA 0 0 4 [ 4] .rodata PROGBITS 0000000000000000 00000060 0000000000000004 0000000000000000 A 0 0 4 [ 5] .comment PROGBITS 0000000000000000 00000064 000000000000002b 0000000000000001 MS 0 0 1 [ 6] .note.GNU-stack PROGBITS 0000000000000000 0000008f 0000000000000000 0000000000000000 0 0 1 [ 7] .note.gnu.propert NOTE 0000000000000000 00000090 0000000000000020 0000000000000000 A 0 0 8 [ 8] .eh_frame PROGBITS 0000000000000000 000000b0 0000000000000038 0000000000000000 A 0 0 8 [ 9] .rela.eh_frame RELA 0000000000000000 00000300 0000000000000018 0000000000000018 I 10 8 8 [10] .symtab SYMTAB 0000000000000000 000000e8 0000000000000198 0000000000000018 11 16 8 [11] .strtab STRTAB 0000000000000000 00000280 0000000000000080 0000000000000000 0 0 1 [12] .shstrtab STRTAB 0000000000000000 00000318 000000000000006f 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), l (large), p (processor specific) ethanol@ethanol:~/Desktop/sections$ cat datasection.cpp int sum(int a, int b) { static int val_1; static int val_2 = 0; static int val_3 = 1; static int val_4 = 0; static int val_5 = 2; const static int val_6 = 0; return a + b; }
.rodata
: size = 0x4
$\rightarrow$ val_6
.data
: size = 0x8
$\rightarrow$ val_3
and val_5
.bss
: size = 0xc
$\rightarrow$ val_1
, val_2
and val_4
Futher reading: view offset.
The difference between .plt and .plt.sec, .plt.got, .got, .got.plt sections
Section
Segment
Access
Use
.plt
code segment
RE
PLT
.plt.got
code segment
RE
PLT entry _cxa_finalize
function
.got
data segment
RW
- Address of global variable - Address of funtions that do not require delay biding.
.got.plt
data segment
RW
- Address of the function that requires delay binding.
View the program table.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 03 .init .plt .plt.sec .text .fini 04 .rodata .eh_frame_hdr .eh_frame 05 .init_array .fini_array .dynamic .got .got.plt .data .bss 06 .dynamic 07 .note.gnu.property 08 .note.gnu.build-id .note.ABI-tag 09 .note.gnu.property 10 .eh_frame_hdr 11 12 .init_array .fini_array .dynamic .got
.plt
, .plt.sec
, .text
are located in the code segment.
.got
, .got.plt
and .data
in the same segment.
.plt and .plt.sec .plt
1 objdump -d --section=.plt main_mix
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Disassembly of section .plt: 0000000000401020 <.plt>: 401020: ff 35 e2 2f 00 00 pushq 0x2fe2(%rip) 401026: f2 ff 25 e3 2f 00 00 bnd jmpq *0x2fe3(%rip) 40102d: 0f 1f 00 nopl (%rax) 401030: f3 0f 1e fa endbr64 401034: 68 00 00 00 00 pushq $0x0 401039: f2 e9 e1 ff ff ff bnd jmpq 401020 <.plt> 40103f: 90 nop 401040: f3 0f 1e fa endbr64 401044: 68 01 00 00 00 pushq $0x1 401049: f2 e9 d1 ff ff ff bnd jmpq 401020 <.plt> 40104f: 90 nop
.plt.sec
Call function.
If not $\rightarrow$ call dynamic link.
1 2 3 4 5 6 7 8 9 10 11 Disassembly of section .plt.sec: 0000000000401050 <_Z3addii@plt>: 401050: f3 0f 1e fa endbr64 401054: f2 ff 25 bd 2f 00 00 bnd jmpq *0x2fbd(%rip) 40105b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 0000000000401060 <_Z3subii@plt>: 401060: f3 0f 1e fa endbr64 401064: f2 ff 25 b5 2f 00 00 bnd jmpq *0x2fb5(%rip) 40106b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
Conclusion : Call dynamic link.
.plt.got 1 objdump -d libadd_debug.so
1 2 3 4 5 6 7 8 9 10 11 ethanol@ethanol:~/Desktop/pic$ objdump -d --section=.plt.got libadd_debug.so libadd_debug.so: file format elf64-x86-64 Disassembly of section .plt.got: 0000000000001030 <__cxa_finalize@plt>: 1030: f3 0f 1e fa endbr64 1034: f2 ff 25 9d 2f 00 00 bnd jmpq *0x2f9d(%rip) 103b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
Debug with GDB and xfiles
The second instruction in plt.got
is jump to the first address of .got
of libadd_debug.so
.
1 2 3 4 5 6 7 8 9 0x00007ffff7fc3020 0x00007ffff7fc3030 .plt ./libadd_debug.so 0x00007ffff7fc3030 0x00007ffff7fc3040 .plt.got ./libadd_debug.so 0x00007ffff7fc5fd8 0x00007ffff7fc6000 .got ./libadd_debug.so 0x00007ffff7fc6000 0x00007ffff7fc6018 .got.plt ./libadd_debug.so 0x00007ffff7fbe020 0x00007ffff7fbe030 .plt ./libsub_debug.so 0x00007ffff7fbe030 0x00007ffff7fbe040 .plt.got ./libsub_debug.so 0x00007ffff7fc0fd8 0x00007ffff7fc1000 .got ./libsub_debug.so 0x00007ffff7fc1000 0x00007ffff7fc1018 .got.plt ./libsub_debug.so
1 2 3 4 gef➤ x/3i 0x00007ffff7fc3030 0x7ffff7fc3030 <__cxa_finalize@plt>: endbr64 0x7ffff7fc3034 <__cxa_finalize@plt+4>: bnd jmp QWORD PTR [rip+0x2f9d] 0x7ffff7fc303b <__cxa_finalize@plt+11>: nop DWORD PTR [rax+rax*1+0x0]
Conclusion : plt.got
is used to hold the PLT entry for the <__cxa_finalize@plt>
.
.got 1 2 3 4 5 6 7 8 gef➤ x/10gx 0x7ffff7fc5fd8 0x7ffff7fc5fd8: 0x00007ffff7e03090 0x00007ffff7fc6024 0x7ffff7fc5fe8: 0x0000000000000000 0x0000000000000000 0x7ffff7fc5ff8: 0x0000000000000000 0x0000000000003e88 0x7ffff7fc6008: 0x0000000000000000 0x0000000000000000 0x7ffff7fc6018: 0x00007ffff7fc6018 0x0000000000000000 gef➤ p/x &g_sum $1 = 0x7ffff7fc6024
GOT[2] hold global variables g_sum
.
1 2 3 4 5 6 7 8 9 10 gef➤ disas 0x00007ffff7e03090, 0x00007ffff7e03090+0x10 Dump of assembler code from 0x7ffff7e03090 to 0x7ffff7e030a0: 0x00007ffff7e03090 <__cxa_finalize+0>: endbr64 0x00007ffff7e03094 <__cxa_finalize+4>: push r15 0x00007ffff7e03096 <__cxa_finalize+6>: push r14 0x00007ffff7e03098 <__cxa_finalize+8>: push r13 0x00007ffff7e0309a <__cxa_finalize+10>: push r12 0x00007ffff7e0309c <__cxa_finalize+12>: push rbp 0x00007ffff7e0309d <__cxa_finalize+13>: mov rbp,rdi End of assembler dump.
GOT[1] hold the address of function <__cxa_finalize>
.
.got.plt
1 0x0000000000404000 0x0000000000404028 .got.plt /home/ethanol/Desktop/pic/main_mix
1 2 3 4 5 6 7 8 9 10 11 12 gef➤ x/10gx 0x404000 0x404000: 0x0000000000403e00 0x00007ffff7ffe190 0x404010: 0x00007ffff7fe7ae0 0x00007ffff7fc30f9 0x404020 <_Z3subii@got.plt>: 0x00007ffff7fbe0f9 0x0000000000000000 0x404030: 0x0000000000000000 0x0000000000000000 0x404040: 0x0000000000000000 0x0000000000000000 gef➤ info symbol 0x00007ffff7fc30f9 add(int, int) in section .text of ./libadd_debug.so gef➤ info symbol 0x00007ffff7fbe0f9 sub(int, int) in section .text of ./libsub_debug.so gef➤ info symbol 0x0000000000403e00 _DYNAMIC in section .dynamic of /home/ethanol/Desktop/pic/main_mix
Code debugging with GDB Series