【问题标题】:ld linker script producing huge binaryld 链接器脚本产生巨大的二进制文件
【发布时间】:2013-03-02 07:24:09
【问题描述】:

我正在使用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

无论我的链接描述文件的内容如何,​​这种情况都会发生。有什么想法吗?

【问题讨论】:

  • 是否也生成了符号表?
  • 可以看到有一个.symtab部分,长度与这个问题无关。

标签: ld elf linker-scripts


【解决方案1】:

我今天在学习链接器脚本时遇到了同样的问题。 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”来查看)

【讨论】:

  • 我可能非常密集,但是当我使用. = 0x10000 + SIZEOF_HEADERS 行执行此操作时,我仍然会得到一个“巨大”的二进制文件。我把它改成. = 0x400000 + SIZEOF_HEADERS,它又变小了。你知道为什么会这样吗?
  • 嗯,我从this answer 尝试了-n,它又变小了。我想我需要更多地了解“页面对齐”及其含义......
【解决方案2】:

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

-z max-page-size=0x1000

【讨论】:

    【解决方案3】:

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

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

    编辑:考虑一下,我们都希望访问虚拟地址 0x00000000 会产生异常(segfault)。为此,我看到了两种可能性:内核映射一个没有访问权限的页面(r/w/x)或者(更有可能)它根本不映射任何东西(没有映射页面=> segfault)并且链接器必须以某种方式知道...这可以解释为什么 ld 跳过地址为零的第一页。这是待定。

    【讨论】:

      猜你喜欢
      • 2017-03-22
      • 2010-09-24
      • 1970-01-01
      • 1970-01-01
      • 2011-07-11
      • 2011-08-24
      • 1970-01-01
      • 1970-01-01
      • 2019-12-24
      相关资源
      最近更新 更多