问题描述
首先,我的本机系统是 amd64,Windows,使用 cygwin,以及 GNU 工具链和 binutils.
Firstly, my native system is amd64, Windows, using cygwin, and the GNU toolchain and binutils.
我正在编写一个 x86 引导加载程序,但无法让 ld
生成正确的相对地址.我已经准备了这个最小的可重复示例:
I am writing an x86 bootloader but can't get ld
to generate correct relative addresses. I've prepared this minimum reproducible example:
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
时使用:
Then when you examine c.o
using:
$ 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
注意地址 0
处的 call
指令的相对地址如何指向 0xE
而不是 0x10
,其中应该.
Notice how the relative address for the call
instruction at address 0
points to 0xE
instead of 0x10
where it should.
虽然目标文件是 pe[i]-x86-64
格式,但指令仍然是 16 位的(因此 -m i8086
选项可以正确反汇编).
While the object files are in pe[i]-x86-64
format the instructions are still 16-bit (hence the -m i8086
option for proper disassembly).
我认为地址错误的原因是因为 ld
认为代码是 64 位信任文件格式并解析错误地址.然而,这个理论如履薄冰,因为 a.o
中的重定位信息说:
The reason why I think the address is wrong is because ld
believes the code is 64-bit trusting the file format and resolves wrong addresses. This theory is on thin ice however because the relocation information in a.o
says:
$ 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 位,并且应该可以工作.
where the relocation type is R_X86_64_PC16
which truncates the address down to 16-bits as far as I can tell, and should work.
在我的实际项目中,我使用 ld
像上面一样组合目标文件,然后使用 objcopy
将其转换为平面二进制映像,以便作为软盘启动使用模拟器.我这样做是因为 ld
根本无法将目标文件转换为平面二进制文件.
In my actual project I use ld
to combine object files just like above then use objcopy
to convert it into a flat binary image in order to boot as a floppy disk using an emulator. I do it this way because ld
simply cannot convert object files into flat binaries.
我尝试在链接之前更改 ao
和 bo
的对象格式,但我的系统不支持 32 和 64 位对象格式以外的任何其他格式,即我可以't(或认为我不能)使用 objcopy
来做到这一点.
I've tried to change the object formats of a.o
and b.o
before linking but my system does not support anything other than 32 and 64 bit object formats i.e. I can't (or don't think I can) use objcopy
to do it.
推荐答案
正如@NateEldredge 和@MichaelPetch 指出的那样,这不是任何工具或代码的问题,而是我的工具链的问题.此后,我为与操作系统无关的通用 x86-32 (i686) 目标平台编译了 GCC 交叉编译器和 binutils.
As @NateEldredge and @MichaelPetch have pointed out this is not a problem with any of the tools or the code but with my toolchain. I have since compiled a GCC cross-compiler and binutils for an OS-agnostic generic x86-32 (i686) target platform.
对于在网上搜索时找到此答案的其他人:https://wiki.osdev.org/GCC_Cross-Compiler
For others who find this answer while searching the net:https://wiki.osdev.org/GCC_Cross-Compiler
这篇关于使用带有 16 位 x86 的 GNU LD 的相对地址不正确的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!