无法解释的填充到 ELF 部分
Unexplained Padding to ELF Section
我正在尝试更好地了解 ELF 格式。为此,我编写了一个名为 share.c 的小型 C 文件,并从中创建了一个名为 share.so 的共享 object。下面是share.c的内容:
static int count = 0;
void increment()
{
count++;
}
下面是我用来创建的命令share.so:
gcc -fPIC -shared -o share.so share.c
我使用 readelf 工具查看了程序 headers 和 share.so 中的部分 headers。以下是 share.so 中的程序 headers:
Elf file type is DYN (Shared object file)
Entry point 0x550
There are 7 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000006f4 0x00000000000006f4 R E 200000
LOAD 0x0000000000000e30 0x0000000000200e30 0x0000000000200e30
0x00000000000001f0 0x00000000000001f8 RW 200000
DYNAMIC 0x0000000000000e48 0x0000000000200e48 0x0000000000200e48
0x0000000000000190 0x0000000000000190 RW 8
NOTE 0x00000000000001c8 0x00000000000001c8 0x00000000000001c8
0x0000000000000024 0x0000000000000024 R 4
GNU_EH_FRAME 0x0000000000000674 0x0000000000000674 0x0000000000000674
0x000000000000001c 0x000000000000001c R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x0000000000000e30 0x0000000000200e30 0x0000000000200e30
0x00000000000001d0 0x00000000000001d0 R 1
Section to Segment mapping:
Segment Sections...
00 .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .init .plt .plt.got .text .fini .eh_frame_hdr .eh_frame
01 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
02 .dynamic
03 .note.gnu.build-id
04 .eh_frame_hdr
05
06 .init_array .fini_array .jcr .dynamic .got
以下是 share.so 中的 header 部分:
There are 27 section headers, starting at offset 0x1820:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.gnu.build-i NOTE 00000000000001c8 000001c8
0000000000000024 0000000000000000 A 0 0 4
[ 2] .gnu.hash GNU_HASH 00000000000001f0 000001f0
000000000000003c 0000000000000000 A 3 0 8
[ 3] .dynsym DYNSYM 0000000000000230 00000230
0000000000000138 0000000000000018 A 4 2 8
[ 4] .dynstr STRTAB 0000000000000368 00000368
00000000000000ad 0000000000000000 A 0 0 1
[ 5] .gnu.version VERSYM 0000000000000416 00000416
000000000000001a 0000000000000002 A 3 0 2
[ 6] .gnu.version_r VERNEED 0000000000000430 00000430
0000000000000020 0000000000000000 A 4 1 8
[ 7] .rela.dyn RELA 0000000000000450 00000450
00000000000000c0 0000000000000018 A 3 0 8
[ 8] .init PROGBITS 0000000000000510 00000510
000000000000001a 0000000000000000 AX 0 0 4
[ 9] .plt PROGBITS 0000000000000530 00000530
0000000000000010 0000000000000010 AX 0 0 16
[10] .plt.got PROGBITS 0000000000000540 00000540
0000000000000010 0000000000000000 AX 0 0 8
[11] .text PROGBITS 0000000000000550 00000550
0000000000000116 0000000000000000 AX 0 0 16
[12] .fini PROGBITS 0000000000000668 00000668
0000000000000009 0000000000000000 AX 0 0 4
[13] .eh_frame_hdr PROGBITS 0000000000000674 00000674
000000000000001c 0000000000000000 A 0 0 4
[14] .eh_frame PROGBITS 0000000000000690 00000690
0000000000000064 0000000000000000 A 0 0 8
[15] .init_array INIT_ARRAY 0000000000200e30 00000e30
0000000000000008 0000000000000000 WA 0 0 8
[16] .fini_array FINI_ARRAY 0000000000200e38 00000e38
0000000000000008 0000000000000000 WA 0 0 8
[17] .jcr PROGBITS 0000000000200e40 00000e40
0000000000000008 0000000000000000 WA 0 0 8
[18] .dynamic DYNAMIC 0000000000200e48 00000e48
0000000000000190 0000000000000010 WA 4 0 8
[19] .got PROGBITS 0000000000200fd8 00000fd8
0000000000000028 0000000000000008 WA 0 0 8
[20] .got.plt PROGBITS 0000000000201000 00001000
0000000000000018 0000000000000008 WA 0 0 8
[21] .data PROGBITS 0000000000201018 00001018
0000000000000008 0000000000000000 WA 0 0 8
[22] .bss NOBITS 0000000000201020 00001020
0000000000000008 0000000000000000 WA 0 0 4
[23] .comment PROGBITS 0000000000000000 00001020
0000000000000034 0000000000000001 MS 0 0 1
[24] .shstrtab STRTAB 0000000000000000 0000173b
00000000000000e4 0000000000000000 0 0 1
[25] .symtab SYMTAB 0000000000000000 00001058
0000000000000528 0000000000000018 26 44 8
[26] .strtab STRTAB 0000000000000000 00001580
00000000000001bb 0000000000000000 0 0 1
根据这些信息,可以看出ELFheader和程序header构成了文件的前0x1C8字节,这就是为什么第一节(.note.gnu.build-i) 从偏移量 0x1C8 开始。当您考虑对齐要求时,报告的所有部分直到但不包括 .init_array 的偏移量才有意义。
对我来说没有意义的是 .init_array 部分的偏移量。此部分的对齐要求为 8 个字节,前一部分 (.eh_frame) 的结尾位于偏移量 0x6F4 处。这似乎暗示下一部分应该位于 0x6F8(4 个字节的填充)。但是,readelf 报告 .init_array 部分从偏移量 0xE30.
开始
我想也许在这个意外的空白中插入了一些其他有用的信息,但是 hexdump 只显示空字节。这让我相信这是某种填充。包含 .init_array 段的 LOAD 段的对齐要求似乎无法解释此填充。链接器为此共享 object 创建的部分映射文件如下:
.eh_frame 0x0000000000000690 0x64
*(.eh_frame)
.eh_frame 0x0000000000000690 0x40 /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
.eh_frame 0x00000000000006d0 0x20 /tmp/ccDzwTL8.o
0x38 (size before relaxing)
.eh_frame 0x00000000000006f0 0x4 /usr/lib/gcc/x86_64-linux-gnu/5/crtendS.o
*(.eh_frame.*)
.gcc_except_table
*(.gcc_except_table .gcc_except_table.*)
.gnu_extab
*(.gnu_extab*)
.exception_ranges
*(.exception_ranges .exception_ranges*)
0x0000000000200e30 . = DATA_SEGMENT_ALIGN (0x200000, 0x1000)
.eh_frame
*(.eh_frame)
*(.eh_frame.*)
.gnu_extab
*(.gnu_extab)
.gcc_except_table
*(.gcc_except_table .gcc_except_table.*)
.exception_ranges
*(.exception_ranges .exception_ranges*)
.tdata
*(.tdata .tdata.* .gnu.linkonce.td.*)
.tbss
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
.preinit_array
*(.preinit_array)
.init_array 0x0000000000200e30 0x8
*(SORT(.init_array.*) SORT(.ctors.*))
*(.init_array EXCLUDE_FILE(*crtend?.o *crtend.o *crtbegin?.o *crtbegin.o) .ctors)
.init_array 0x0000000000200e30 0x8 /usr/lib/gcc/x86_64-linux-gnu/5/crtbeginS.o
带有注释“.= DATA_SEGMENT_ALIGN (0x200000, 0x1000)”的行似乎也没有解释这个填充。我希望位置计数器的值为 0x00000000002006F8。
有没有对 ELF 格式的细节有更多经验的人对这种意外的填充有解释?
我发现了填充的来源。根据这个page, the AMD64 toolchain provided with Ubuntu is likely to use the -z relro option as a default. This explains why there is a GNU_RELRO entry in the program headers table. The built in default linker script contains a DATA_SEGMENT_RELRO_END(offset, exp) directive before the .got.plt section. According to this page:
When ‘-z relro’ option is not present, DATA_SEGMENT_RELRO_END does
nothing, otherwise DATA_SEGMENT_ALIGN is padded so that exp + offset
is aligned to the most commonly used page boundary for particular
target
这可以解释为什么 .got.plt 部分的偏移量与最近的页面 (0x1000) 对齐。因此,从 .init_array 到 .got 的部分位于上一页的末尾,该页面在 .eh_frame 部分之后引入了神秘的填充。
程序头 table 中的 GNU_RELRO 条目和填充在使用以下命令构建 share.so 时消失:
gcc -fPIC -shared -Wl,-z,norelro -o share.so share.c
我正在尝试更好地了解 ELF 格式。为此,我编写了一个名为 share.c 的小型 C 文件,并从中创建了一个名为 share.so 的共享 object。下面是share.c的内容:
static int count = 0;
void increment()
{
count++;
}
下面是我用来创建的命令share.so:
gcc -fPIC -shared -o share.so share.c
我使用 readelf 工具查看了程序 headers 和 share.so 中的部分 headers。以下是 share.so 中的程序 headers:
Elf file type is DYN (Shared object file)
Entry point 0x550
There are 7 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000006f4 0x00000000000006f4 R E 200000
LOAD 0x0000000000000e30 0x0000000000200e30 0x0000000000200e30
0x00000000000001f0 0x00000000000001f8 RW 200000
DYNAMIC 0x0000000000000e48 0x0000000000200e48 0x0000000000200e48
0x0000000000000190 0x0000000000000190 RW 8
NOTE 0x00000000000001c8 0x00000000000001c8 0x00000000000001c8
0x0000000000000024 0x0000000000000024 R 4
GNU_EH_FRAME 0x0000000000000674 0x0000000000000674 0x0000000000000674
0x000000000000001c 0x000000000000001c R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x0000000000000e30 0x0000000000200e30 0x0000000000200e30
0x00000000000001d0 0x00000000000001d0 R 1
Section to Segment mapping:
Segment Sections...
00 .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .init .plt .plt.got .text .fini .eh_frame_hdr .eh_frame
01 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
02 .dynamic
03 .note.gnu.build-id
04 .eh_frame_hdr
05
06 .init_array .fini_array .jcr .dynamic .got
以下是 share.so 中的 header 部分:
There are 27 section headers, starting at offset 0x1820:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.gnu.build-i NOTE 00000000000001c8 000001c8
0000000000000024 0000000000000000 A 0 0 4
[ 2] .gnu.hash GNU_HASH 00000000000001f0 000001f0
000000000000003c 0000000000000000 A 3 0 8
[ 3] .dynsym DYNSYM 0000000000000230 00000230
0000000000000138 0000000000000018 A 4 2 8
[ 4] .dynstr STRTAB 0000000000000368 00000368
00000000000000ad 0000000000000000 A 0 0 1
[ 5] .gnu.version VERSYM 0000000000000416 00000416
000000000000001a 0000000000000002 A 3 0 2
[ 6] .gnu.version_r VERNEED 0000000000000430 00000430
0000000000000020 0000000000000000 A 4 1 8
[ 7] .rela.dyn RELA 0000000000000450 00000450
00000000000000c0 0000000000000018 A 3 0 8
[ 8] .init PROGBITS 0000000000000510 00000510
000000000000001a 0000000000000000 AX 0 0 4
[ 9] .plt PROGBITS 0000000000000530 00000530
0000000000000010 0000000000000010 AX 0 0 16
[10] .plt.got PROGBITS 0000000000000540 00000540
0000000000000010 0000000000000000 AX 0 0 8
[11] .text PROGBITS 0000000000000550 00000550
0000000000000116 0000000000000000 AX 0 0 16
[12] .fini PROGBITS 0000000000000668 00000668
0000000000000009 0000000000000000 AX 0 0 4
[13] .eh_frame_hdr PROGBITS 0000000000000674 00000674
000000000000001c 0000000000000000 A 0 0 4
[14] .eh_frame PROGBITS 0000000000000690 00000690
0000000000000064 0000000000000000 A 0 0 8
[15] .init_array INIT_ARRAY 0000000000200e30 00000e30
0000000000000008 0000000000000000 WA 0 0 8
[16] .fini_array FINI_ARRAY 0000000000200e38 00000e38
0000000000000008 0000000000000000 WA 0 0 8
[17] .jcr PROGBITS 0000000000200e40 00000e40
0000000000000008 0000000000000000 WA 0 0 8
[18] .dynamic DYNAMIC 0000000000200e48 00000e48
0000000000000190 0000000000000010 WA 4 0 8
[19] .got PROGBITS 0000000000200fd8 00000fd8
0000000000000028 0000000000000008 WA 0 0 8
[20] .got.plt PROGBITS 0000000000201000 00001000
0000000000000018 0000000000000008 WA 0 0 8
[21] .data PROGBITS 0000000000201018 00001018
0000000000000008 0000000000000000 WA 0 0 8
[22] .bss NOBITS 0000000000201020 00001020
0000000000000008 0000000000000000 WA 0 0 4
[23] .comment PROGBITS 0000000000000000 00001020
0000000000000034 0000000000000001 MS 0 0 1
[24] .shstrtab STRTAB 0000000000000000 0000173b
00000000000000e4 0000000000000000 0 0 1
[25] .symtab SYMTAB 0000000000000000 00001058
0000000000000528 0000000000000018 26 44 8
[26] .strtab STRTAB 0000000000000000 00001580
00000000000001bb 0000000000000000 0 0 1
根据这些信息,可以看出ELFheader和程序header构成了文件的前0x1C8字节,这就是为什么第一节(.note.gnu.build-i) 从偏移量 0x1C8 开始。当您考虑对齐要求时,报告的所有部分直到但不包括 .init_array 的偏移量才有意义。
对我来说没有意义的是 .init_array 部分的偏移量。此部分的对齐要求为 8 个字节,前一部分 (.eh_frame) 的结尾位于偏移量 0x6F4 处。这似乎暗示下一部分应该位于 0x6F8(4 个字节的填充)。但是,readelf 报告 .init_array 部分从偏移量 0xE30.
开始我想也许在这个意外的空白中插入了一些其他有用的信息,但是 hexdump 只显示空字节。这让我相信这是某种填充。包含 .init_array 段的 LOAD 段的对齐要求似乎无法解释此填充。链接器为此共享 object 创建的部分映射文件如下:
.eh_frame 0x0000000000000690 0x64
*(.eh_frame)
.eh_frame 0x0000000000000690 0x40 /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
.eh_frame 0x00000000000006d0 0x20 /tmp/ccDzwTL8.o
0x38 (size before relaxing)
.eh_frame 0x00000000000006f0 0x4 /usr/lib/gcc/x86_64-linux-gnu/5/crtendS.o
*(.eh_frame.*)
.gcc_except_table
*(.gcc_except_table .gcc_except_table.*)
.gnu_extab
*(.gnu_extab*)
.exception_ranges
*(.exception_ranges .exception_ranges*)
0x0000000000200e30 . = DATA_SEGMENT_ALIGN (0x200000, 0x1000)
.eh_frame
*(.eh_frame)
*(.eh_frame.*)
.gnu_extab
*(.gnu_extab)
.gcc_except_table
*(.gcc_except_table .gcc_except_table.*)
.exception_ranges
*(.exception_ranges .exception_ranges*)
.tdata
*(.tdata .tdata.* .gnu.linkonce.td.*)
.tbss
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
.preinit_array
*(.preinit_array)
.init_array 0x0000000000200e30 0x8
*(SORT(.init_array.*) SORT(.ctors.*))
*(.init_array EXCLUDE_FILE(*crtend?.o *crtend.o *crtbegin?.o *crtbegin.o) .ctors)
.init_array 0x0000000000200e30 0x8 /usr/lib/gcc/x86_64-linux-gnu/5/crtbeginS.o
带有注释“.= DATA_SEGMENT_ALIGN (0x200000, 0x1000)”的行似乎也没有解释这个填充。我希望位置计数器的值为 0x00000000002006F8。
有没有对 ELF 格式的细节有更多经验的人对这种意外的填充有解释?
我发现了填充的来源。根据这个page, the AMD64 toolchain provided with Ubuntu is likely to use the -z relro option as a default. This explains why there is a GNU_RELRO entry in the program headers table. The built in default linker script contains a DATA_SEGMENT_RELRO_END(offset, exp) directive before the .got.plt section. According to this page:
When ‘-z relro’ option is not present, DATA_SEGMENT_RELRO_END does nothing, otherwise DATA_SEGMENT_ALIGN is padded so that exp + offset is aligned to the most commonly used page boundary for particular target
这可以解释为什么 .got.plt 部分的偏移量与最近的页面 (0x1000) 对齐。因此,从 .init_array 到 .got 的部分位于上一页的末尾,该页面在 .eh_frame 部分之后引入了神秘的填充。
程序头 table 中的 GNU_RELRO 条目和填充在使用以下命令构建 share.so 时消失:
gcc -fPIC -shared -Wl,-z,norelro -o share.so share.c