Понимание кода сборки загрузчика и областей памяти - PullRequest
0 голосов
/ 01 сентября 2018

Я хочу проверить мое понимание следующего кода загрузчика:

BITS 16

start:
    mov ax, 07C0h   ; Set up 4K stack space after this bootloader
    add ax, 288     ; (4096 + 512) / 16 bytes per paragraph
    mov ss, ax
    mov sp, 4096

    mov ax, 07C0h       ; Set data segment to where we're loaded
    mov ds, ax

    mov si, text_string ; Put string position into SI
    call print_string   ; Call our string-printing routine

    jmp $           ; Jump here - infinite loop!

    text_string db 'This is my cool new OS!', 0

print_string:           ; Routine: output string in SI to screen
    mov ah, 0Eh     ; int 10h 'print char' function

.repeat:
    lodsb           ; Get character from string
    cmp al, 0
    je .done        ; If char is zero, end of string
    int 10h         ; Otherwise, print it
    jmp .repeat

.done:
ret

    times 510-($-$$) db 0   ; Pad remainder of boot sector with 0s
    dw 0xAA55       ; The standard PC boot signature

Вот мое понимание кода:

Мовор, 07C0h:

  • Адрес памяти может быть получен из значения сегмента стека (хранится в регистрах ss) и смещения (которое сохраняется в регистре sp). Вы получите адрес, выполнив следующее: значение сегмента стека x 16 + значение смещения.
  • Мы используем сегмент 07C0h для установки пространства в нашем коде. Поэтому, когда 07C0h смещен на 0, он будет ссылаться на адрес 0x7C00. BIOS пытается загрузить код с 0x7C00. Примером кода, который копируется в 0x7C00, является MBR.
  • Каждый сегмент увеличивается в блоках по 16 байтов, поэтому 07C0h даст вам диапазон адресов 0x7C00-0x7C0F. Следующий сегмент 07C1h даст вам адрес 0x7C10-0x7C1F.

добавить топор, 288

  • Чтобы установить пространство стека 4K после загрузчика, нам нужно добавить 288, полученных из (4096 + 512) / 16 байт на абзац. Десятичное значение 288 равно 120h.
  • Значение, хранящееся в регистре топоров, теперь равно 08e0h, которое я получил: 07c0h + 120h = 08e0h (120h - десятичное 288).

мов сс, топор

  • Скопировать значение из регистра ax в регистр ss. Смещение теперь содержит сегмент: 08e0h.

mov sp, 4096

  • Значение смещения составляет 4096, что составляет 0x1000h. Пара ss: sp дает значение 08e0: 1000.
  • Нижняя часть стека начинается с адреса памяти 0x8e00, а верхняя часть стека - с 0x9e00 (от 0x8e00 + 0x1000 = 0x9e00).

Вот схема кода в памяти и место, выделенное, как показано ниже.

enter image description here

Примечание: загрузчик и ссылка на память на этой диаграмме, скорее всего, неверны, я предполагаю, что она не будет последовательной, и ассемблер скомпилирует машинный код по-разному. Однако начало кода будет начинаться с более низкого адреса памяти (0x7C00), а подпись загрузки будет начинаться с более высокого адреса (подпись диска 0xaa55 начинается с 0x7c0: 0x1fe (физический адрес 0x7c0 * 16 + 0x1fe = 0x7dfe), которые являются последние два байта первого 512-байтового сектора, который работает от 0x7c0: 0x0000 до 0x7c0: 0x200 (0x7C32, который является концом 512 байтов)).

Странно видеть выделенное пространство - это два блока по 4096 байт: один для стека, а другой содержит код и данные. Я подозреваю, что я что-то здесь упускаю.

1 Ответ

0 голосов
/ 01 сентября 2018

следующий код загрузчика:

Отсутствует информация о том, как будет установлен относительный адрес собранного кода, то есть как будут рассчитываться локальные смещения. Обычно загрузчики начинаются с org 0x7C00, чтобы сделать это явным, код ожидает запуска на cs:ip = 0000:7C00. Но если бы вы сделали это, ds=07C0 было бы неправильно, если предположить, что код ожидает, что смещения будут собраны, как если бы он начинался с 07C0:0000 вместо 0000:7C00. В то время как оба адреса ориентированы на один и тот же адрес физической памяти, пары сегмент: смещение отличаются.

Каждый сегмент увеличивается в блоках по 16 байтов, поэтому 07C0h даст вам диапазон адресов 0x7C00-0x7C0F. Следующий сегмент 07C1h даст вам адрес 0x7C10-0x7C1F.

Каждый сегмент дает диапазон 64 кБ, хотя начальный адрес увеличивается только на 16 байт, поэтому между сегментами существует много совпадений, и вы можете адресовать один и тот же физический адрес многими комбинациями. То есть ds=07C0 дает вам окно в диапазон физической памяти 07C00-17BFF.


Тогда ваши преобразования значений в шестнадцатеричные значения неправильны (см. Также комментарии Михаэля), 288 = 0x120 и 4096 = 0x1000, но вы правильно пришли к выводу, что есть 512B кода загрузчика (один сектор блочного устройства), 4096B свободного места, а затем 4096B стекового пространства. Если бы вы заполнили стек, вставив в него более 4096 байт, он не попадет в свободное место после кода, но обернется до 08E0:FFFE (намного выше первоначального начала стека).

Я предполагаю, что он не будет последовательным, и ассемблер скомпилирует машинный код по-другому.

Напротив, инструкции и определенные байты в исходном коде выводятся последовательно в результирующий машинный код. Используйте переключатель командной строки «list», чтобы увидеть, как ассемблер генерирует машинный код для определенных строк. Если бы вы, например, переместили строку text_string db 'This is my cool new OS!', 0 в начале после директивы BITS 16, этот текст был бы затем в начале машинного кода, загружался и выполнялся BIOS по адресу / из 0000:7C00, выполняя текстовые байты в качестве инструкций .

...