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

时间:2013-03-14 03:55:59

标签: ld elf 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

如果我使用-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

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

$ 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

如您所见,此文件变得非常庞大。

请注意,这似乎是因为.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

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

3 个答案:

答案 0 :(得分:6)

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

    .section .text
    .global _start
_start:
    mov $1, %eax
    mov $8, %ebx
    int $0x80

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

SECTIONS
{
    . = 0x400000;
    .text : { *(.text) }
}

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

SECTIONS
{
    . = 0x400000 + SIZEOF_HEADERS;
    .text : { *(.text) }
}

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

SECTIONS
{
    . = 0x400078;
    .text : { *(.text) }
}

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

SECTIONS
{
    . = 0x410000;
    .text : { *(.text) }
}

所以基本上,据我所知,它似乎是:

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

答案 1 :(得分:5)

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

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

编辑:想一想,我们都希望访问虚拟地址0x00000000来生成异常(segfault)。为此,我看到两种可能性:要么内核映射没有访问权限的页面(r / w / x),要么(更有可能)它不会映射任何内容(没有页面映射=&gt;段错误)和链接器必须知道某种方式......这可以解释为什么ld会跳过地址为零的第一页。这是TBC。

答案 2 :(得分:4)

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

-z max-page-size=0x1000