如何告诉/强制 GNU ld 将部分/符号放在输出 ELF 文件的特定部分?

Posted

技术标签:

【中文标题】如何告诉/强制 GNU ld 将部分/符号放在输出 ELF 文件的特定部分?【英文标题】:How to tell / force GNU ld to put a section/symbol in a specific part of the output ELF file? 【发布时间】:2013-11-23 15:21:41 【问题描述】:

那会很长,所以喝点咖啡/茶/yerba吧。

总结

如何告诉/强制 GNU ld 放置一个部分/符号 在输出 ELF 文件的特定部分?

具体来说,我不是在询问符号的物理/加载地址 但关于文件内的偏移量。

背景

我正在尝试采用真实世界的 BSD 内核并使其兼容 GRUB 可多重引导和引导。 为了使 ELF 映像与 Multiboot 兼容且可识别 通过 GRUB,需要在第一个中嵌入一个幻数 (0x1BADB002) 8KiB 的文件。

我指的是Multiboot 0.6.96。

由于原始内核是相当大的一段代码, 我将使用基于Bare Bones kernel from OSDev wiki 的示例。 由于该内核已经兼容 Multiboot,我将使用 extra_symbol0xCAFEBABE 作为我的问题的示例。

boot.s 包含:

.set CAFEBABE, 0xCAFEBABE
; ... snip ...
.section .extras
extra_symbol:
    .long CAFEBABE

.text 中的额外符号

最简单的选择是将具有该值的符号放入.text 其他部分之前的部分:

.text BLOCK(4K) : ALIGN(4K)

    *(.extras)
    *(.multiboot)
    *(.text)

这个例子很好,但对于真正的 BSD 内核.text 在文件中的 0x2000 (8KiB) 之后开始。这种方法不是一种选择。

.text之前的额外部分?

另一种选择是将整个.extras 部分放在.text 之前。 在我的天真中,我希望在.text之前放置这样一个部分 链接器脚本也会使其更早地出现在输出 ELF 中:

SECTIONS

    // ... some stuff ...

    .extras :  *(.extras) 

    .text BLOCK(4K) : ALIGN(4K)
    
        *(.multiboot)
        *(.text)
    

    // ... more stuff ...


但事实并非如此:

$ i586-elf-readelf -S myos.bin 
There are 10 section headers, starting at offset 0x6078:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .extras           PROGBITS        00100000 006010 000004 00      0   0  1
  [ 2] .comment          PROGBITS        00000000 006014 000011 01  MS  0   0  1
  [ 3] .text             PROGBITS        00001000 001000 0001e4 00  AX  0   0 4096
  [ 4] .rodata.str1.1    PROGBITS        000011e4 0011e4 000016 01 AMS  0   0  1
  [ 5] .eh_frame         PROGBITS        000011fc 0011fc 000104 00   A  0   0  4
  [ 6] .bss              PROGBITS        00002000 002000 004010 00  WA  0   0 4096
  [ 7] .shstrtab         STRTAB          00000000 006025 000050 00      0   0  1
  [ 8] .symtab           SYMTAB          00000000 006208 000210 10      9  19  4
  [ 9] .strtab           STRTAB          00000000 006418 000130 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

实际上,节在节头表中排在第一位,但它的 文件中的偏移量 (0x6010) 在.text 之后。事实上,它甚至 在.bss之后。

问题

ELF 规范明确指出:

虽然图中显示了紧随其后的程序头表 ELF 头,以及节后的节头表,实际 文件可能不同。此外,节和段没有指定的顺序。 只有 ELF 头在文件中有固定的位置。

我如何利用它并告诉 GNU ld 放置我的extra_symbol (可能是整个 .extras 部分)在文件中的任何其他部分之前, 最好在 ELF 标头之后?

【问题讨论】:

您是否尝试指定您的部分的加载地址?像: .extras : *(.extras) >ROM AT> ROM 其中 ROM 在内存块中定义,例如: MEMORY ROM : ORIGIN = 0x1000, LENGTH = 0x1000 您甚至可以制作自己的内存块 MEMORY SIGNATURE :原点 = 0x....,长度 = 4 .extras : *(.extras) >SIGNATURE AT> SIGNATURE 我尝试了多种设置负载和虚拟地址以及使用内存块的组合。 readelf 输出中的相关地址确实发生了变化。 xxd 打印的二进制文件中 0xCAFEBABE 的偏移量没有。我将用我尝试过的组合的详细信息更新问题(现在不能这样做)。 @erszcz 到目前为止有任何更新吗? @yeputons 不幸的是,我最终使用 hack 将图像识别为 Multiboot。 I placed the MB header in .interp which I observed to be (one of?) the first section(s) in the generated file。在这个特定的链接器脚本中,文件开头有很多部分(我假设)来自正在使用的工具链 - 这就是为什么真正的 .init.text 远远落后于 GRUB 检查的初始 8KiB . 也就是说,我仍然不知道如何编写链接器脚本以正确生成布局 - 这可能是不可能的。除了上述技巧之外,我能想到的唯一方法是在生成 ELF 二进制文件后对其进行编辑。 【参考方案1】:

为了将部分放在.text 之前,您是否已经尝试使其分配(A)和可执行(X)?

如果.interp hack 有效,那么当然也可以。

【讨论】:

感谢您的回复,但我目前无法检查:(这个问题现在已经快 4 岁了。【参考方案2】:

我遇到了同样的问题——多重引导签名是在 .text 部分之后加载的——并且使用 Florian 的指针,这就是我强制在 ELF 文件开头加载示例部分的方式。

.section .multiboot, "ax", @progbits
.align 4
.long MAGIC
.long FLAGS
...

即通过在节声明处标记可分配和可执行

【讨论】:

感谢您的评论和验证 Florian 的建议!我想是时候将他的答案标记为已接受了 :) 迟到总比没有好。 如此,如此:-)

以上是关于如何告诉/强制 GNU ld 将部分/符号放在输出 ELF 文件的特定部分?的主要内容,如果未能解决你的问题,请参考以下文章

GNU gcc/ld - 使用在同一个目标文件中定义的调用者和被调用者包装对符号的调用

我可以强制 ld 解析本地符号吗?

GNU-ld链接脚本浅析

GNU LD 脚本学习笔记

使用 GNU ld 链接器脚本包含二进制文件

强制 venv 将 python 的符号链接放在 macOS 和 Windows 上的相同位置