首先,我的原生系统是amd64,Windows,使用cygwin,以及GNU工具链和binutils。
我正在写一个x86的bootloader,但是不能得到 ld
来生成正确的相对地址。我准备了这个最低限度的可重现的例子。
main.s
.code16
.global _start
.section .text
_start:
call other
hlt
other.s
.code16
.global other
.section .text
other:
mov $0xFFFF, %ax
ret
script.ld
ENTRY(_start);
SECTIONS {
.text : {
*(.text);
}
}
要重现,请执行
$ as main.s -o a.o
$ as other.s -o b.o
$ ld -T script.ld *.o -o c.o
然后当你检查 c.o
的使用。
$ objdump -sD -m i8086 c.o
c.o: file format pei-x86-64
Contents of section .text:
200000000 e80b00f4 90909090 90909090 90909090 ................
200000010 b8ffffc3 90909090 90909090 90909090 ................
Disassembly of section .text:
00000000 <_start>:
0: e8 0b 00 call e <__major_subsystem_version__+0x9>
3: f4 hlt
4: 90 nop
5: 90 nop
6: 90 nop
7: 90 nop
8: 90 nop
9: 90 nop
a: 90 nop
b: 90 nop
c: 90 nop
d: 90 nop
e: 90 nop
f: 90 nop
00000010 <other>:
10: b8 ff ff mov $0xffff,%ax
13: c3 ret
14: 90 nop
15: 90 nop
16: 90 nop
17: 90 nop
18: 90 nop
19: 90 nop
1a: 90 nop
1b: 90 nop
1c: 90 nop
1d: 90 nop
1e: 90 nop
1f: 90 nop
请注意,相对地址为 call
地址指令 0
指向 0xE
而不是 0x10
的地方。
当对象文件在 pe[i]-x86-64
格式的指令仍然是16位的(因此,在这里的 -m i8086
选项,以便正确拆卸)。)
我之所以认为地址错误,是因为 ld
认为代码是64位的信任文件格式,并解析出错误的地址。然而,这种理论如履薄冰,因为文件中的重定位信息在 a.o
说。
$ objdump -sDr -m i8086 a.o
a.o: file format pe-x86-64
Contents of section .text:
0000 e80000f4 90909090 90909090 90909090 ................
Disassembly of section .text:
00000000 <_start>:
0: e8 00 00 call 3 <_start+0x3>
1: R_X86_64_PC16 other
3: f4 hlt
[...]
其中搬迁类型是 R_X86_64_PC16
它将地址截断到16位。 据我所知,应该可以用。
在我的实际项目中,我使用 ld
组合对象文件,就像上面一样,然后使用 objcopy
将其转换为一个平面二进制映像,以便使用模拟器作为软盘启动。我这样做是因为 ld
根本无法将对象文件转换为平面二进制文件。
我曾试着改变了 a.o
和 b.o
但我的系统除了32位和64位的对象格式外,不支持任何其他格式,也就是说,我不能(或认为不能)使用 objcopy
来做。
正如 @NateEldredge 和 @MichaelPetch 指出的,这不是任何工具或代码的问题,而是我的工具链的问题。此后,我已经为操作系统无关的通用x86-32(i686)目标平台编译了GCC交叉编译器和binutils。
对于其他在网上搜索时发现这个答案的人。https:/wiki.osdev.orgGCC_Cross-Compiler