Неверный относительный адрес при использовании GNU LD с 16-битным x86 - PullRequest
0 голосов
/ 26 апреля 2020

Во-первых, моей родной системой является 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, чтобы сделать это.

1 Ответ

2 голосов
/ 27 апреля 2020

Как отметили @NateEldredge и @MichaelPetch, это не проблема ни с одним из инструментов или кода, а с моей цепочкой инструментов. С тех пор я скомпилировал кросс-компилятор G CC и binutils для целевой платформы OS-agnosti c generi c x86-32 (i686).

Для тех, кто находит этот ответ при поиске net: https://wiki.osdev.org/GCC_Cross-Compiler

...