使用链接描述文件制作 ELF 文件,在节之间没有零初始化块

Posted

技术标签:

【中文标题】使用链接描述文件制作 ELF 文件,在节之间没有零初始化块【英文标题】:Crafting an ELF file using linker scripts without zero-initialized blocks between sections 【发布时间】:2017-10-12 10:13:26 【问题描述】:

我正在尝试制作一个链接器命令脚本,以便由旧版 grub 引导(使用多重引导)。我很难在所需位置(前 8192 个字节内)获取多重引导标头。我的脚本看起来像:

SECTIONS

    .multiboot :
    
        __multiboot_header = .;
        *(.multiboot)
    

    .text 0x00100000 :
    
        *(.text*)
        *(.rodata)
    

    /* ... remainder of script ... */

总的来说,我的目标是让引导加载程序在前 1MiB 物理内存之后加载我的自定义可执行文件;作为 .text 部分声明的一部分的地址似乎已经按照我的预期完成了。读取 ELF 标头给出的入口点为:

$ readelf -h kernel.elf | grep Entry
  Entry point address:               0x100000

但是,这样做我似乎将文件增加了这​​么多。

$ ls -l file.elf
-rwxr-xr-x 1 user user 1049960 May 13 02:20 file.elf

ELF 标头和 .text 部分之间的区域被初始化为零。

$ hexdump -C file.elf
00000000  7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 03 00 01 00 00 00  00 00 10 00 34 00 00 00  |............4...|
00000020  00 04 10 00 00 00 00 00  34 00 20 00 02 00 28 00  |........4. ...(.|
00000030  09 00 08 00 01 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  00 00 00 00 f8 00 10 00  f8 f0 4e 00 07 00 00 00  |..........N.....|
00000050  00 00 20 00 51 e5 74 64  00 00 00 00 00 00 00 00  |.. .Q.td........|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 07 00 00 00  |................|
00000070  10 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00100000  8b 25 f8 f0 4e 00 50 53  e8 66 00 00 00 fa f4 eb  |.%..N.PS.f......|
00100010  fc 55 89 e5 53 83 ec 10  c7 45 f8 00 00 00 00 eb  |.U..S....E......|
00100020  38 a1 f4 00 10 00 8b 55  f8 01 d2 01 d0 8b 15 f4  |8......U........|

此外,尽管readelf -s 报告__multiboot_header 的值为 0x0(应该是结构的地址,因为它是在链接器文件中提到的同一点定义的对吧?):

$ readelf -s kernel.elf | grep multiboot
    22: 00000000     0 NOTYPE  GLOBAL DEFAULT    1 __multiboot_header

readelf -S 的输出好像有冲突:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 1] .multiboot        PROGBITS        00000000 1000f8 00000c 00      0   0  1
  [ 2] .text             PROGBITS        00100000 100000 000096 00  AX  0   0  1

这意味着 .multiboot 部分实际上在 .text 部分内。

如果我在文件中偏移 0x1000f8,那么我可以找到结构,但是,我不确定偏移量来自哪里。

tl;dr

1) 如何确保特定数据结构位于输出文件的前 8192 个字节内?

2) 如何指定加载地址,而不会使输出二进制文件因零初始化块的大间隙而膨胀?

【问题讨论】:

我不遵循您的结论“这意味着 .multiboot 部分实际上位于 .text 部分内。”。根据Addr值,.multiboot00000000开始,大小为00000c.text00100000开始,所以前者不在后者内部... @falstaff,我是根据Off 字段得出的结论; .multiboot0xf8 字节超过 .text。由于首先定义了多重引导,我希望它在文件中更早。我知道Offset 指的是 ELF 文件,但对为什么将它放在文本部分之后感到困惑。 【参考方案1】:

elf 文件格式是由它自己的规则生成的,它存储信息的方式不受链接器文件的直接影响(例如,存储节的偏移量不得与其在内存中的生存期位置相关)。它是由链接器文件中的SECTIONS 命令描述的内存布局,而 elf 文件格式 描述 这种布局......您需要一个支持 elf 的加载器来将各个部分加载到目标中地点。

要获得可以 1:1 加载到内存中的平面二进制文件,请使用 objcopy(例如 objcopy -O binary myfile.elf myfile.bin 之类的东西)。该文件的布局直接受链接描述文件的影响,.multiboot 部分的内容实际上应该位于偏移量0

【讨论】:

根据this grub 参考,支持加载ELF。实际上,如果加载的映像是正确的 ELF 文件,则无需指定多重引导标头中的其他字段(如加载地址)。 给定您的 cmets,如果使用合适的 ELF 加载器,物理内存将如何布局?多重引导标头是否会放置在物理地址 0x00,.text 部分的第一个字节位于 0x00100000 我正在考虑objcopy 路由,但我必须完成多重引导标头的所有字段。我可能仍然会这样做,但链接器/ELF 关系是我想首先解决的问题。我了解您上面的 cmets,但不了解额外零初始化内存的 1MiB 来自哪里(即,在使用 . = 0x00100000 移动位置计数器后,为什么生成的 ELF 文件的大小增加了这么多? )。这些字节不属于任何部分。我的目标是简单地指定.text 的第一个字节的加载地址。

以上是关于使用链接描述文件制作 ELF 文件,在节之间没有零初始化块的主要内容,如果未能解决你的问题,请参考以下文章

.bss 部分零初始化变量是不是占用 elf 文件中的空间?

程序运行之ELF 符号表

Linux零基础入门第五课

4月13日总结

嵌入式开发中的.bin文件和.elf文件的区别

ELF文件之二——使用链接脚本