вызов в реальном режиме x86 не сохраняет обратный адрес

#assembly #x86 #x86-16 #bootloader #real-mode

#сборка #x86 #x86-16 #загрузчик #реальный режим

Вопрос:

Я пытаюсь написать загрузчик реального режима, и в настоящее время у меня возникают проблемы при попытке включить строку A20. Вот мой код до сих пор, я собираю с помощью NASM:

 [bits 16]

[global _start]

jmp _start

bios_print:
 lodsb
 test al, al
 jz bios_print_done
 mov ah, 0x0E
 mov bh, 0
 int 0x10
 jmp bios_print

bios_print_done:
 ret

a20_is_enabled:
 push ds
 push si
 push es
 push di

 xor ax, ax
 mov ds, ax
 mov si, BOOT_ID_OFFS

 mov ax, BOOT_ID_OFFS_PLUS_1MB_SEGM
 mov es, ax
 mov di, BOOT_ID_OFFS_PLUS_1MB_OFFS

 cmp word [es:di], BOOT_ID

 mov ax, 1
 jne a20_is_enabled_done

 mov ax, word [ds:si]
 xor ax, ax
 mov [ds:si], ax

 cmp word [es:di], BOOT_ID

 push ax
 xor ax, ax
 mov [ds:si], ax
 pop ax

 mov ax, 1
 jne a20_is_enabled_done

 mov ax, 0

a20_is_enabled_done:
 pop di
 pos es
 pop si
 pop ds

 ret

a20_enable_bios:
 mov ax, 0x2403
 int 0x15
 jc a20_enable_bios_failure
 test ah, ah
 jnz a20_enable_bios_failure

 mov ax, 0x2401
 int 0x15
 jc a20_enable_bios_failure
 test ah, ah
 jnz a20_enable_bios_failure

 mov ax, 1
 jmp a20_enable_bios_done

a20_enable_bios_failure:
 mov ax, 0

a20_enable_bios_done:
 ret

a20_enable:

 push si

 mov si, word MSG_A20_TRY_BIOS
 call bios_print

 pop si

 call a20_enable_bios

 test ax, ax
 jz a20_enable_failure

 call a20_is_enabled

 test ax, ax
 jnz a20_enable_success

a20_enable_failure:

 push si

 mov si, word MSG_A20_FAILURE
 call bios_print

 pop si

 mov ax, 0
 jmp a20_enable_done

a20_enable_success:

 push si

 mov si, word MSG_A20_SUCCESS
 call bios_print

 pop si

 mov ax, 1

a20_enable_done:
 ret

_start:
 xor ax, ax
 mov ds, ax

 cld

 cli

 push si

 mov si, word MSG_GREETING
 call bios_print

 pop si

 call a20_enable

 test ax, ax
 jz boot_error

 ; TODO

boot_error:
 jmp boot_error

BOOT_ID equ 0xAA55
BOOT_ID_OFFS equ 0x7DFE
BOOT_ID_OFFS_PLUS_1MB_SEGM equ 0xFFFF
BOOT_ID_OFFS_PLUS_1MB_OFFS equ BOOT_ID_OFFS   (0x1 << 20) - (BOOT_ID_OFFS_PLUS_1MB_SEGM << 4)

MSG_GREETING db 'Hello from the bootloader', 0xA, 0xD, 0
MSG_A20_TRY_BIOS db 'Trying to enable A20 line via BIOS interrupt', 0xA, 0xD, 0
MSG_A20_SUCCESS db 'Successfully enabled A20 line', 0xA, 0xD, 0
MSG_A20_FAILURE db 'Failed to enable A20 line', 0xA, 0xD, 0

times 510-($-$$) db 0
dw BOOT_ID
  

Проблема заключается в функции a20_is_enabled , которая должна проверять, включена ли линия A20 после a20_enable_bios того, как активировала ее через прерывание BIOS (я знаю, что это не надежно, здесь будет приведен дополнительный код). Когда я отлаживаю код , кажется , что все в порядке до тех пор, пока call a20_is_enabled . Затем процессор действительно выполняет ближний вызов для исправления адреса здесь, но обратный адрес не помещается в стек (что я проверил с помощью gdb). Таким образом, при ret выполнении in a20_is_enabled указатель инструкции устанавливается на некоторый мусорный адрес. Почему это так?

РЕДАКТИРОВАТЬ: обратите внимание, что ORG 0x7C00 в начале моего ассемблерного кода его нет. Это потому, что я сначала создаю файл elf, чтобы я мог отлаживать свой код с помощью gdb, и это плохо работает ORG , поэтому я на самом деле делаю это:

 nasm -f elf32 -g -F dwarf boot.asm -o boot.o
ld -Ttext=0x7c00 -melf_i386 boot.o -o boot.elf
objcopy -O binary boot.elf boot.bin
  

Комментарии:

1. Я настоятельно рекомендую BOCHS для отладки в реальном режиме таких вещей, как загрузчики и другой код реального режима. В отличие от GDB, BOCHs правильно понимает сегмент реального режима: адресация с удаленным набором. BOCHs имеет только ограниченную символическую отладку, но обычно это не имеет большого значения для перехода через такие маленькие вещи, как загрузчик.

2. @MichaelPetch: я раньше не использовал BOCHS, но это звучит разумно. Что касается org 0x7c00: это как-то связано с тем фактом, что я использую gdb, я отредактирую вопрос.

3. @MichaelPetch: я обновил вопрос, строка ORG отсутствует, потому что я создаю файл ELF с отладочной информацией, и это, похоже, не работает с ORG. Возможно, команда, которую я использую для ссылки, не подходит.

Ответ №1:

Обычно этот вопрос можно закрыть, поскольку он вызван типографской ошибкой, но ошибка не обязательно очевидна на первый взгляд. В отладчике нужно внимательно следить за выполняемыми инструкциями.

Это заставило меня почесать голову, так как, когда я посмотрел в отладчике последовательность:

  push ds
 push si
 push es
 push di

 ; Snip other code

 pop di
 pos es
 pop si
 pop ds
 ret
  

только показал, что процессор выполняет 3 POP s и a ret , когда явно есть 4 POP инструкции. Поскольку процессор не выполняет достаточно всплывающих окон, адрес возврата неверен и ret возвращается в неправильную часть памяти и вызывает неожиданное поведение.

Проблема довольно тривиальна, и из-за невезения инструкция создается без ошибок, но это не та инструкция, которую вы хотите. Если вы посмотрите внимательно, это виновник:

  pos es
  

Здесь опечатка. POS должно быть POP . Сначала мой мозг этого не уловил. pos обрабатывается как метка и es является переопределением сегмента, поэтому может отображаться в строке самостоятельно. Это привело к созданию инструкции es pop si .

Очевидно, что исправление состоит в том, чтобы изменить его на:

  pop es
  

Комментарии:

1. Боже мой, я смотрел на это несколько минут и не видел этого. Отличный улов.

2. Приятное обнаружение. Я годами хотел получить дополнительное предупреждение для этого в NASM. См. Предупреждение о добавлении метки без двоеточия .

3. @ecm: Я согласен. Предупреждение о метке без двоеточия в строке с инструкцией было бы здесь кстати, lol. Это был один из первых вопросов после пробуждения, и я не понял опечатку после повторного просмотра исходного кода. BOCHs не оказал мне никакой помощи, кроме как сказал, что было 3 pop-инструкции. Для меня это оставалось загадкой, пока я не выполнил objdump кода, и objdump был достаточно любезен, чтобы указать, что инструкция была es pop si . Для BOCHs было бы удобно сказать мне, что это было то, что было закодировано. Как только я увидел это и, наконец, внимательно посмотрел, проблема была очевидна

4. @ecm: что забавно, так это то, что, когда я понял это, я собирался оставить комментарий, указать на проблему и закрыть ее как простую опечатку, и подумал, черт возьми, я потратил достаточно времени на эту глупую проблему, что, возможно, она достойна ответа на будущее. Я не поклонник меток без двоеточий. MASM не помог, поскольку существуют правила, касающиеся двоеточия в данных и коде, которые мне никогда не нравились. В любом случае, я хотел бы, чтобы BOCHs показал нам лучшее декодирование и NASM с большим количеством предупреждений, и я думаю, что ваш патч — хорошая идея для этого и других случаев.

5. 100% согласен. Если NASM не предупреждает вас об этом по умолчанию, это плохо. Если он вообще не может предупредить вас об этом, это ужасно. label: prefix в строке само по себе кажется маловероятным, и если вы хотите, вы можете сделать это с помощью двоеточия, чтобы было понятно. label prefix должен ли IMO никогда не появляться в строке сам по себе в нормальном исходном коде, даже если метку нелегко принять за инструкцию. Также или вместо этого может быть полезна опция просто отключить прием не- : меток, @ecm, чтобы позволить ассемблеру применять стандартный стиль NASM.