Близкие таблицы вызовов / переходов не всегда работают в загрузчике
Я разрабатывал простой загрузчик и столкнулся с проблемой в некоторых средах, где подобные инструкции не работают:
mov si, call_tbl ; SI=Call table pointer
call [call_tbl] ; Call print_char using near indirect absolute call
; via memory operand
call [ds:call_tbl] ; Call print_char using near indirect absolute call
; via memory operand w/segment override
call near [si] ; Call print_char using near indirect absolute call
; via register
Каждый из них связан с косвеннымВЫЗОВ к абсолютным смещениям памяти. Я обнаружил, что у меня есть проблемы, если я использую аналогичныеJMP столы. Относительные вызовы и прыжки, похоже, не затрагиваются. Код как это работает:
call print_char
Я воспользовался советом, представленным на Stackoverflow, постерами, обсуждающими то, что нужно и чего не стоит писать загрузчик. В частности я видел этоПереполнение стека ответить сОбщие советы по загрузке, Первый совет был:
Когда BIOS переходит к вашему коду, вы не можете положитьсяCS,DS,ES,SS,SP регистры, имеющие действительные или ожидаемые значения. Они должны быть настроены соответствующим образом при запуске вашего загрузчика. Вы можете только гарантировать, что ваш загрузчик будет загружен и запущен с физического адреса 0x07c00 и что номер загрузочного диска загружен вDL регистр.Принимая все советы, я не полагался наCSЯ установил стек и установилDS быть подходящим дляORG (Исходное смещение) Я использовал. Я создал пример минимальной полной проверки, который демонстрирует проблему. Я построил это, используяNASM, но это, похоже, не является проблемой, специфичной дляNASM.
Минимальный примерКод для тестирования выглядит следующим образом:
[ORG 0x7c00]
[Bits 16]
section .text
main:
xor ax, ax
mov ds, ax ; DS=0x0000 since OFFSET=0x7c00
cli ; Turn off interrupts for potentially buggy 8088
mov ss, ax
mov sp, 0x7c00 ; SS:SP = Stack just below 0x7c00
sti ; Turn interrupts back on
mov si, call_tbl ; SI=Call table pointer
mov al, [char_arr] ; First char to print 'B' (beginning)
call print_char ; Call print_char directly (relative jump)
mov al, [char_arr+1] ; Character to print 'M' (middle)
call [call_tbl] ; Call print_char using near indirect absolute call
; via memory operand
call [ds:call_tbl] ; Call print_char using near indirect absolute call
; via memory operand w/segment override
call near [si] ; Call print_char using near indirect absolute call
; via register
mov al, [char_arr+2] ; Third char to print 'E' (end)
call print_char ; Call print_char directly (relative jump)
end:
cli
.endloop:
hlt ; Halt processor
jmp .endloop
print_char:
mov ah, 0x0e ; Write CHAR/Attrib as TTY
mov bx, 0x00 ; Page 0
int 0x10
retn
; Near call address table with one entry
call_tbl: dw print_char
; Simple array of characters
char_arr: db 'BME'
; Bootsector padding
times 510-($-$$) db 0
dw 0xAA55
Я строю какISO изображение и образ дискеты 1,44 МБ для тестовых целей. Я использую среду Debian Jessie, но большинство дистрибутивов Linux будут похожи:
nasm -f bin boot.asm -o boot.bin
dd if=/dev/zero of=floppy.img bs=1024 count=1440
dd if=boot.bin of=floppy.img conv=notrunc
mkdir iso
cp floppy.img iso/
genisoimage -quiet -V 'MYBOOT' -input-charset iso8859-1 -o myos.iso -b floppy.img -hide floppy.img iso
Я получаю образ дискеты под названиемfloppy.img
иISO изображение называетсяmyos.iso
.
В большинстве случаев этот код работает, но в ряде сред это не так. Когда это работает, он просто печатает это на дисплее:
BMMME
Я распечатываюB
используя типичныйВЫЗОВ с относительным смещением, кажется, работает нормально. В некоторых средах, когда я запускаю код, я просто получаю:
B
А потом, кажется, просто перестает что-либо делать. Кажется, распечататьB
правильно, но потом происходит нечто неожиданное.
Среды, которые, кажется, работают:
QEMU загрузился с дискеты и ISOVirtualBox загрузился с дискеты и ISOVMWare 9 загрузился с дискеты и ISODosBox загрузился с дискетыОфициально упакованBochs(2.6) в Debian Jessie с использованием образа дискетыBochs 2.6.6 (собран из системы контроля версий) в Debian Jessie с использованием образа дискеты иISO образСистема AST Premmia SMP P90 с середины 90-х годов с использованием дискет иISOСреды, которые не работают должным образом:
Официально упакованBochs(2.6) в Debian Jessie используяISO образСистема на базе 486DX с AMI BIOS начала 90-х, использующая образ дискеты. Компакт-диски не загружаются в этой системе, поэтому их невозможно протестировать.Что я нахожу интересным, так это то, чтоBochs (версия 2.6) не работает должным образом на Debian Jessie с использованиемISO, Когда я загружаюсь с дискеты с той же версией, она работает как положено.
Во всех случаяхISO и образ дискеты, казалось, загружается и начинает работать, так как вВСЕ случаи, по крайней мере, был в состоянии распечататьB
на дисплее.
B
и больше ничего?Почему некоторые среды работают, а другие - нет?Это ошибка в моем коде или аппаратном / BIOS?Как я могу исправить это так, чтобы я все еще мог использовать почти непрямые таблицы Jump и Call для абсолютных смещений памяти? Я знаю, что могу полностью избежать этих инструкций, и это, кажется, решает мою проблему, но я хотел бы иметь возможность понять, как и если я могу правильно использовать их в загрузчике.