2015-08-27 3 views
2

Например, у меня есть следующий код (MikeOS).Создает ли сборку места памяти в момент написания ярлыка?

jmp short bootloader_start ; Jump past disk description section 
nop       ; Pad out before disk description 
... 
... 

OEMLabel   db "MIKEBOOT" ; Disk label 
BytesPerSector  dw 512   ; Bytes per sector 
SectorsPerCluster db 1   ; Sectors per cluster 
ReservedForBoot  dw 1   ; Reserved sectors for boot record 
NumberOfFats  db 2   ; Number of copies of the FAT 

bootloader_start: 
    mov ax, 07C0h   ; Set up 4K of stack space above buffer 
    add ax, 544    ; 8k buffer = 512 paragraphs + 32 paragraphs (loader) 
    ... 
    ... 
.... 

Теперь я знаю, что jmp short bootloader_start означает, что он прыгает мимо секции OEMLabel... и переходит на метку.

Поскольку я новичок в сборке, у меня есть несколько вопросов:

  • ли сборка выделения памяти в тот момент, вы пишете инструкции? Например, в последние пару строк, код выглядит:

    times 510-($-$$) db 0 ; Pad remainder of boot sector with zeros 
        dw 0AA55h    ; Boot signature (DO NOT CHANGE!) 
    
    buffer:     ; Disk buffer begins (8k after this, stack starts) 
    

    buffer: выделяет память?

  • В этом блоке коды:

    cli    ; Disable interrupts while changing stack 
    mov ss, ax 
    mov sp, 4096 
    sti    ; Restore interrupts 
    

    Почему мы очищаем Сбросьте Прерывание? Если я не ошибаюсь, этот бит кода выделяет 4096 байт стека.

    Наконец, после вышеуказанного блока, мы имеем следующее:

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

    Почему мы это делаем? На мой взгляд, это делается, чтобы сообщить начало сегмента данных, где его происхождение?

+0

'db' и т. Д. Помещают данные в сегмент данных исполняемого файла. http://stackoverflow.com/questions/31941830/how-are-arrays-initialised-to-zero-in-c-by-the-compiler/31942189#31942189. Для автономного кода, такого как загрузочное ядро ​​ОС, разделы просто действуют, чтобы группировать байты данных отдельно от байтов кода. Все заканчивается, просто загружается в память. Я не уверен, как зарезервированное пространство 'bss' обрабатывается для загрузочной ОС, в отличие от двоичного файла Linux ELF (где код загрузчика ОС ELF отображает как можно большую нулевую память, чем говорит BSS). –

ответ

6
times 510-($-$$) db 0 

Это (NASM) ассемблер конкретные инструкции, которые будут заполнять оставшееся свободное пространство до 510 байтов с нулями при токе смещения в двоичном (память). Этикетка сама будет не создать любой. Единственной инструкцией, которая будет создавать/выделять байты, является DB, DW, DD, DQ и т. Д. Это no инструкция процессора, но некий макрос, интерпретируемый программой ассемблера.

Edit (Какие ярлыки?):

Ярлык представляет собой смещение (адрес в памяти или в двоичном файле). Возьмите следующий как пример:

MyFirstLabel: 
    db 1, 2, 3, 4 
MySecondLabel: 
    db 5, 6, 7, 8 
Start: 

Если это ваш файл на ассемблере и она загружается в память по смещению 0, он будет выглядеть следующим образом:

 
OFS DATA 
0000h: 01 02 03 04 
0004h: 05 06 07 08 

Вы заметите, что MyFirstLabel только смещение, где хранятся данные, в этом случае смещение 0. MySecondLabel - это просто другое смещение, но оно начинается за предыдущими выделенными данными, в этом случае смещение 4. Мой ярлык Start, например, представляет смещение 8 в файле. Таким образом, «адрес» этой метки составляет 0008h (например, по отношению к сегменту данных/кода).

Так что в вашем случае, если вы заполните оставшуюся память до 510 байт нулей (это была ваша times 510-($-$$) db 0 инструкции делает), выделяющих одно дополнительного слово данных (DW 0AA55h), то смещение вашего buffer этикетки точно 512 (0200h) (обычно это размер главной загрузочной записи).

Инструкция cli сообщит процессору, что он не должен прерываться до тех пор, пока не вызывается sti. Это важно, так как регистр указателя стека (sp) и регистр сегмента сегмента (ss) изменяется и эти обе команды могут не гарантированно быть бесперебойными. Это означает, что при смене одного из этих регистров может произойти прерывание. В этой ситуации стек может быть неопределенным/недействительным. Как указано в комментариях к этой публикации, cli/sti для изменения сегмента стека и указателя стека на самом деле не требуется. См. Документацию Intel о инструкции «MOV»:

Загрузка регистра SS в инструкцию MOV запрещает все прерывания до тех пор, пока не будет выполнена следующая инструкция. Эта операция позволяет загружать указатель стека в регистр ESP следующей командой (MOV ESP, значение указателя стека) до того, как произойдет прерывание.

Так следующее будет правильным без кли/БППП

mov bx, 4096 
mov ss, ax 
mov sp, bx 

И следующий не является правильным

mov ss, ax 
mov ax, 4096 
mov sp, ax 

Вы правы, mov ds, ax изменит регистр сегмента данных. Значение этого регистра зависит от того, работаем ли мы в реальном режиме, в защищенном режиме и т. Д. Вы должны искать «сегментированную модель памяти x86» в своей любимой поисковой системе.

+0

Извините за то, что вы немного глупы, но «буфер - это всего лишь метка за этими выделенными байтами». Что именно вы имеете в виду? – weirdpanda

+0

'mov' to' ss' неявно отключает прерывания до следующей инструкции. Таким образом, 'cli/sti' на самом деле не требуется в этом случае. http://stackoverflow.com/a/32086036/224132. Должен любить сумасшедшие x86 специальные случаи. –

+0

@PeterCordes: Да, это, конечно, правильно, но тогда изменение стека должно быть строго в этом порядке «mov ss, ...» 'mov sp, ...', ничего между ними. 'cli/sti' было бы намного безопаснее здесь. Но интересно, я не знаю этого :) – bkausbk

4

Память есть все время. buffer: просто придает ему символическое имя.

Прерывания отключены для защиты от других процессов, которые мешают чему-то, что может потребовать нескольких инструкций. В частности, для настройки стека вам не нужно этого делать, поскольку установка регистра ss отключит прерывания для следующей команды. Предполагается, что у вас должно быть время установить sp.

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

Регистр ds должен быть установлен в сегмент, предполагаемый кодом, который вы хотите выполнить. В противном случае программа не найдет ваши переменные.