ld链接器脚本产生巨大的二进制

Jon*_*art 4 elf ld linker-scripts

我正在使用binutils-2.21.53.0.1-6.fc16.x86_64.

我有一个小的目标文件,hello.o只有足够的"东西"来包含所有部分的内容:

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
       000000000000005d  0000000000000000  AX       0     0     4
  [ 2] .rela.text        RELA             0000000000000000  00000808
       0000000000000060  0000000000000018          15     1     8
  [ 3] .data             PROGBITS         0000000000000000  000000a0
       0000000000000000  0000000000000000  WA       0     0     4
  [ 4] .bss              NOBITS           0000000000000000  000000a0
       0000000000000053  0000000000000000  WA       0     0     32
  [ 5] .rodata           PROGBITS         0000000000000000  000000a0
       000000000000000f  0000000000000000   A       0     0     1
  [ 6] .data.rel.local   PROGBITS         0000000000000000  000000b0
       0000000000000008  0000000000000000  WA       0     0     8
  [ 7] .rela.data.rel.lo RELA             0000000000000000  00000868
       0000000000000018  0000000000000018          15     6     8
  [ 8] .data.rel         PROGBITS         0000000000000000  000000b8
       0000000000000008  0000000000000000  WA       0     0     8
  [ 9] .rela.data.rel    RELA             0000000000000000  00000880
       0000000000000018  0000000000000018          15     8     8
  [10] .comment          PROGBITS         0000000000000000  000000c0
       000000000000002d  0000000000000001  MS       0     0     1
  [11] .note.GNU-stack   PROGBITS         0000000000000000  000000ed
       0000000000000000  0000000000000000           0     0     1
  [12] .eh_frame         PROGBITS         0000000000000000  000000f0
       0000000000000058  0000000000000000   A       0     0     8
  [13] .rela.eh_frame    RELA             0000000000000000  00000898
       0000000000000030  0000000000000018          15    12     8
  [14] .shstrtab         STRTAB           0000000000000000  00000148
       0000000000000085  0000000000000000           0     0     1
  [15] .symtab           SYMTAB           0000000000000000  00000610
       00000000000001b0  0000000000000018          16    11     8
  [16] .strtab           STRTAB           0000000000000000  000007c0
       0000000000000045  0000000000000000           0     0     1
Run Code Online (Sandbox Code Playgroud)

如果我使用-pie且没有链接器脚本,则结果如预期:

$ ld -pie -Map hello_pie.map -o hello_pie.elf hello.o 

$ ll hello_pie.elf 
-rwxrwx---. 1 jreinhart jreinhart 3453 Mar 13 23:44 hello_pie.elf
Run Code Online (Sandbox Code Playgroud)

但是,如果我包含任何类型的链接描述文件,则输出大小会爆炸:

$ cat 1.ld 
SECTIONS
{

}
$ ld -T 1.ld -pie -Map hello_pie.map -o hello_pie.elf hello.o 
$ ll hello_pie.elf 
-rwxrwx---. 1 jreinhart jreinhart 2100070 Mar 13 23:45 hello_pie.elf
Run Code Online (Sandbox Code Playgroud)

如你所见,这个文件变得庞大.

请注意,这似乎是因为该.text部分坚持从文件中的偏移量0x200000开始:

$ readelf -l -S hello_pie.elf 
There are 19 section headers, starting at offset 0x200400:

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  00200000  <--- Why?
       000000000000005d  0000000000000000  AX       0     0     4
  [ 2] .rodata           PROGBITS         000000000000005d  0020005d
       000000000000000f  0000000000000000   A       0     0     1
  [ 3] .eh_frame         PROGBITS         0000000000000070  00200070
       0000000000000058  0000000000000000   A       0     0     8
  [ 4] .interp           PROGBITS         00000000000000c8  002000c8
       000000000000000f  0000000000000000   A       0     0     1
  [ 5] .dynsym           DYNSYM           00000000000000d8  002000d8
       0000000000000078  0000000000000018   A       6     2     8
  [ 6] .dynstr           STRTAB           0000000000000150  00200150
       0000000000000014  0000000000000000   A       0     0     1
  [ 7] .hash             HASH             0000000000000168  00200168
       0000000000000028  0000000000000004   A       5     0     8
  [ 8] .rela.dyn         RELA             0000000000000190  00200190
       0000000000000078  0000000000000018   A       5     0     8
  [ 9] .data.rel.local   PROGBITS         0000000000000208  00200208
       0000000000000008  0000000000000000  WA       0     0     8
  [10] .data.rel         PROGBITS         0000000000000210  00200210
       0000000000000008  0000000000000000  WA       0     0     8
  [11] .dynamic          DYNAMIC          0000000000000218  00200218
       00000000000000f0  0000000000000010  WA       6     0     8
  [12] .got              PROGBITS         0000000000000308  00200308
       0000000000000018  0000000000000008  WA       0     0     8
  [13] .got.plt          PROGBITS         0000000000000320  00200320
       0000000000000018  0000000000000008  WA       0     0     8
  [14] .bss              NOBITS           0000000000000340  00200338
       0000000000000053  0000000000000000  WA       0     0     32
  [15] .comment          PROGBITS         0000000000000000  00200338
       000000000000002c  0000000000000001  MS       0     0     1
  [16] .shstrtab         STRTAB           0000000000000000  00200364
       000000000000009a  0000000000000000           0     0     1
  [17] .symtab           SYMTAB           0000000000000000  002008c0
       0000000000000258  0000000000000018          18    19     8
  [18] .strtab           STRTAB           0000000000000000  00200b18
       000000000000004e  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

Elf file type is DYN (Shared object file)
Entry point 0x0
There are 5 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000200040 0x0000000000000000
                 0x0000000000000118 0x0000000000000118  R E    8
  INTERP         0x00000000002000c8 0x00000000000000c8 0x00000000000000c8
                 0x000000000000000f 0x000000000000000f  R      1
      [Requesting program interpreter: /lib/ld64.so.1]
  LOAD       --> 0x0000000000200000 0x0000000000000000 0x0000000000000000
                 0x0000000000000338 0x0000000000000393  RWE    200000
  DYNAMIC        0x0000000000200218 0x0000000000000218 0x0000000000000218
                 0x00000000000000f0 0x00000000000000f0  RW     8
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     8
Run Code Online (Sandbox Code Playgroud)

无论我的链接器脚本的内容如何,​​都会发生这种情况.有什么想法发生了什么?

Chr*_*Vig 7

在学习链接器脚本时,我今天遇到了同样的问题.SIZEOF_HEADERS是解决它的灵丹妙药.这是我构建我正在链接的对象的简单源文件:

    .section .text
    .global _start
_start:
    mov $1, %eax
    mov $8, %ebx
    int $0x80
Run Code Online (Sandbox Code Playgroud)

使用以下链接描述文件,我得到一个2+ MB的可执行文件:

SECTIONS
{
    . = 0x400000;
    .text : { *(.text) }
}
Run Code Online (Sandbox Code Playgroud)

如果我添加+ SIZEOF_HEADERS,如下所示,我得到一个568字节的可执行文件:

SECTIONS
{
    . = 0x400000 + SIZEOF_HEADERS;
    .text : { *(.text) }
}
Run Code Online (Sandbox Code Playgroud)

根据LD文档,此函数返回输出文件头的大小.手动设置偏移量以包括标头大小也会产生568字节的可执行文件:

SECTIONS
{
    . = 0x400078;
    .text : { *(.text) }
}
Run Code Online (Sandbox Code Playgroud)

如果我进一步向下移动.text,可执行文件就会开始扩展.以下产生一个65984字节的可执行文件:

SECTIONS
{
    . = 0x410000;
    .text : { *(.text) }
}
Run Code Online (Sandbox Code Playgroud)

所以基本上,据我所知,看来:

  • 第一个输出部分似乎与输出文件头共享一个内存页面.如果第一部分与头部重叠,则LD在输出第一部分之前发出整页填充字节以避免冲突
  • 要解决此问题,请将第一个输出节的输出地址设置为X + SIZEOF_HEADERS.这是LD的内置链接描述文件(您可以通过运行"ld --verbose"来看一看)


Bee*_*vik 6

尝试对ld(或gcc)使用以下命令行选项:

-z max-page-size=0x1000
Run Code Online (Sandbox Code Playgroud)


Eri*_*ric 5

默认情况下,ld页面对齐输入节.由于您的内核强制执行超级页面(页面为2MB = 0x200000字节),因此您的.text部分将在偏移量0x200000处对齐.这似乎是一个错误,ld因为它应该使用偏移量0x0000000(请参阅下面的编辑以获得可能的解释)

为了防止这种创建更大文件的对齐,您可以使用--nmagic标志ld来防止它对.text部分进行页面对齐,尽管它有副作用(它还禁用链接到共享库).但要小心将其他部分(.data,.rodata...)与2M页面对齐,因为它们不能与.text位于同一页面中,因为所有这些部分都需要不同的访问位.

编辑:考虑一下,我们都期望访问虚拟地址0x00000000来生成异常(segfault).为此,我看到两种可能性:要么内核映射没有访问权限的页面(r/w/x),要么(更有可能)它根本不映射任何内容(没有页面映射=> segfault),链接器必须知道这个......这可以解释为什么ld会跳过地址为零的第一页.这是TBC.

  • 谢谢你们`--nmagic`,这是漫长旅途的结束. (2认同)