Во-первых, моей родной системой является amd64, Windows, использующая cygwin, а также набор инструментов GNU и binutils.
Я пишу загрузчик x86, но не могу получить 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
, чтобы сделать это.