Как вы обнаружили, вы можете установить исходную точку ORG 0x7A00
для всего загрузчика. Это прекрасно работает. Код, копирующий загрузочный сектор в 0x7A00, не полагается ни на какие абсолютные метки, а только на родственные. Этот ответ - скорее мысленный эксперимент и другой подход к нему.
Что бы произошло, если бы мы хотели отобразить строку перед копией в качестве примера? Какие есть возможные варианты?
- NASM позволяет формату BIN (
-f bin
) иметь разделы, которые принимают виртуальная отправная точка (исходная точка) и физический адрес (начало). Этот метод слишком ограничивает расположение загрузчика.
- Используйте LD скрипт компоновщика. для определения макета загрузчика.
- Реорганизуйте код для использования ORG (исходной точки) 0x0000 и соответствующим образом установите регистры сегмента. См. Мой другой ответ на этот вопрос.
В этом ответе основное внимание уделяется варианту 2. Объяснение того, как работают сценарии LD компоновщика, слишком широко для Stackoverflow. LD manual - это лучший источник информации, и у него есть примеры. Идея в том, что мы позволяем размещать загрузчик внутри скрипта компоновщика. Мы можем настроить LMA (Load Memory Address), чтобы указать адрес памяти, по которому раздел будет загружен в память. VMA - это исходная точка для раздела. Все метки и адреса в разделе будут разрешены относительно его VMA.
Для удобства мы можем использовать раздел с определенным LMA, чтобы поместить загрузочную сигнатуру непосредственно в выходной файл, а не указывать ее в коде сборки. Мы также можем определить символы из сценария компоновщика, к которым можно получить доступ из кода сборки с помощью директивы NASM extern
.
Одним из преимуществ всего этого является то, что вы можете определять разделы в своем коде сборки в любом порядке, а сценарий компоновщика будет переупорядочивать вещи. Вы также можете связать вместе несколько объектных файлов. Первым должен быть указан объектный файл, содержащий загрузочный код, который должен отображаться первым.
Макет этого скрипта компоновщика примерно выглядит следующим образом:
Non-relocatable portion of boot code (boot.text) Relative to an origin of 0x7c00
Non-relocatable portion of boot data (boot.data)
--------------------------------------- Word aligned
Relocatable portion of boot code (rel.text) - Relative to an origin of 0x7a00
Relocatable portion of boot data (rel.data)
Relocatable portion of partition data at offset 0x1b8 (partition.data)
---------------------------------------
Boot signature at offset 0x1fe
Скрипт компоновщика, который будет компоновщик этого загрузчика, может выглядеть примерно так:
ENTRY(_start);
OUTPUT(elf_i386);
SECTIONS
{
/* Set the base of the main bootloader offsets */
_bootbase = 0x7c00; /* Where bootloader initially is loaded in memory */
_relbase = 0x7a00; /* Address entire bootsector will be copied to
This linker script expects it to be word aligned */
_partoffset = 0x1b8; /* Offset of UID and Partition data */
_sigoffset = 0x1fe; /* Offset of the boot signature word */
/* SUBALIGN(n) in an output section will override the alignment
* of any input section that is encontered */
/* This is the boot loader code and data that is expected to run from 0x7c00 */
.bootinit _bootbase : SUBALIGN(2)
{
*(boot.text);
*(boot.data);
}
/* Note that referencing any data in the partition table will
* only be usable from the code that is in the .bootrel section */
/* Partition data */
.partdata _relbase + _partoffset :
AT(_bootbase + _partoffset) SUBALIGN(0)
{
*(partition.data);
}
/* Boot signature */
.bootsig :
AT(_bootbase + _sigoffset) SUBALIGN(0)
{
SHORT(0xaa55);
}
/* Length of region to copy in 16-bit words */
_rel_length = 256;
/* Address to copy to */
_rel_start = _relbase; /* Word aligned start address */
/* Code and data that will expect to run once relocated
* is placed in this section. Aligned to word boundary.
* This relocateable code and data will be placed right
* after the .bootinit section in the output file */
.bootrel _relbase + SIZEOF(.bootinit) :
AT(_bootbase + SIZEOF(.bootinit)) SUBALIGN(2)
{
*(rel.text);
*(rel.data);
}
}
Отредактированная копия вашего кода с использованием этого сценария компоновщика и определенных в нем символов может выглядеть так:
BITS 16
extern _bootbase
extern _relbase
extern _rel_length
extern _rel_start
section boot.text
; comment
global _start
_start:
xor cx, cx ; Set segment registers to zero
mov es, cx
mov ds, cx
mov ss, cx
mov sp, 0x7A00 ; Stack
cld
.copymsg:
mov si, copymsg ; Copy message
mov ah, 0x0E ; 0E TTY Output
mov bx, 7 ; Page number
.dispcopy:
lodsb ; Load next char
test al, al ; Compare to zero
jz .end ; If so, end
int 10h ; Display char
jmp .dispcopy ; Loop
.end:
mov di, _rel_start ; Beginning of relocation point
mov si, _bootbase ; Original location to copy from
mov cx, _rel_length ; CX = words to copy
rep movsw ; Copy self to destination
jmp 0:rel_entry ; far JMP to copy of self
section rel.text
rel_entry:
sti ; Enable interrupts
mov si, successmsg ; Error Message location
mov ah, 0x0E ; 0E TTY Output
mov bx, 7 ; Page number
.disp:
lodsb ; Load next char
test al, al ; Compare to zero
je .end ; If so, end
int 10h ; Display char
jmp .disp ; Loop
cli ; Disable interrupts
.end:
hlt ; CPU hlt
jmp .end ; infinte loop
section rel.data
successmsg db 10,'Success!',13,0
section boot.data
copymsg db 10,'Before copy!',13,0
section partition.data
UID db 0xf5,0xbf,0x0f,0x18 ;Unique Disk ID
BLANK times 2 db 0
PT1 db 0x80,0x20,0x21,0x00,0x0C,0x50,0x7F,0x01
db 0x00,0x08,0x00,0x00,0xb0,0x43,0xF9,0x0D
PT2 times 16 db 0
PT3 times 16 db 0
PT4 times 16 db 0
В качестве эксперимента, чтобы убедиться, что код в разделе boot.text
может получить доступ к данным в boot.data
, я показываю строку перед копией. Затем я выполняю FAR JMP для перемещенного кода. Перемещенный код отображает строку успеха.
Я изменил код, чтобы не использовать 32-битные регистры, такие как ESI, поскольку вы будете выполнять этот код в реальном режиме. Я также изменил ваш бесконечный цикл, чтобы использовать инструкцию HLT .
Код и сценарий компоновщика можно изменить, чтобы копировать только с начала перемещенных данных до 512-го байта, но это выходит за рамки этого ответа.
Взгляд на разборку
Раздел .bootinit
, имеющий исходную точку 0x7c00, представлен ниже. Это фрагмент OBJDUMP этого раздела (без данных для краткости):
Disassembly of section .bootinit:
00007c00 <_start>:
7c00: 31 c9 xor cx,cx
7c02: 8e c1 mov es,cx
7c04: 8e d9 mov ds,cx
7c06: 8e d1 mov ss,cx
7c08: bc 00 7a mov sp,0x7a00
7c0b: fc cld
00007c0c <_start.copymsg>:
7c0c: be 2e 7c mov si,0x7c2e
7c0f: b4 0e mov ah,0xe
7c11: bb 07 00 mov bx,0x7
00007c14 <_start.dispcopy>:
7c14: ac lods al,BYTE PTR ds:[si]
7c15: 84 c0 test al,al
7c17: 74 04 je 7c1d <_start.end>
7c19: cd 10 int 0x10
7c1b: eb f7 jmp 7c14 <_start.dispcopy>
00007c1d <_start.end>:
7c1d: bf 00 7a mov di,0x7a00
7c20: be 00 7c mov si,0x7c00
7c23: b9 00 01 mov cx,0x100
7c26: f3 a5 rep movs WORD PTR es:[di],WORD PTR ds:[si]
7c28: ea 3e 7a 00 00 jmp 0x0:0x7a3e
Все адреса VMA в левом столбце выглядят правильно настроенными относительно исходной точки 0x7c00. FAR JUMP (jmp 0x0:0x7a3e
) также переместился в то место, где все было перемещено (скопировано). Аналогичный сокращенный дамп раздела .bootrel
выглядит так:
Disassembly of section .bootrel:
00007a3d <rel_entry-0x1>:
...
00007a3e <rel_entry>:
7a3e: fb sti
7a3f: be 54 7a mov si,0x7a54
7a42: b4 0e mov ah,0xe
7a44: bb 07 00 mov bx,0x7
00007a47 <rel_entry.disp>:
7a47: ac lods al,BYTE PTR ds:[si]
7a48: 3c 00 cmp al,0x0
7a4a: 74 05 je 7a51 <rel_entry.end>
7a4c: cd 10 int 0x10
7a4e: eb f7 jmp 7a47 <rel_entry.disp>
7a50: fa cli
00007a51 <rel_entry.end>:
7a51: f4 hlt
7a52: eb fd jmp 7a51 <rel_entry.end>
VMA в левом столбце соответствует началу 0x7A00, что правильно. Инструкция mov si,0x7a54
- это абсолютный адрес, близкий к памяти, и он правильно закодирован для ссылки на successmsg
адрес (я вырезал данные для краткости, чтобы они не отображались).
Записи:
00007a3d <rel_entry-0x1>:
...
Информация, связанная с выравниванием раздела .bootrel
по четной границе слова. С этим скриптом компоновщика rel_entry
всегда будет иметь четный адрес.
Компиляция и связывание этого загрузчика
Самый простой способ - использовать эти команды:
nasm -f elf32 -o boot.o boot.asm
ld -melf_i386 -Tlinker.ld -o boot.bin --oformat=binary boot.o
Следует отметить, что мы используем формат ELF32 с NASM, а не BIN. Затем LD используется для создания двоичного файла boot.bin
, который должен быть 512-байтовым образом загрузочного сектора. linker.ld
- это имя файла сценария компоновщика.
Если вы хотите получить дамп объекта, вы можете использовать эти команды для сборки и связывания:
nasm -f elf32 -o boot.o boot.asm
ld -melf_i386 -Tlinker.ld -o boot.elf boot.o
objcopy -O binary boot.elf boot.bin
Отличие от первого метода в том, что мы не используем параметр --oformat=binary
с LD. В результате будет сгенерировано изображение ELF32, которое будет помещено в выходной файл boot.elf
. Мы не можем использовать boot.elf
напрямую в качестве загрузочного образа, поэтому мы используем OBJCOPY для преобразования файла ELF32 в двоичный файл с именем boot.bin
. Полезность этого способа можно увидеть, если мы воспользуемся такой командой для сброса содержимого и дизассемблирования файла ELF:
objdump boot.elf -Mintel -mi8086 -Dx
-D
вариант разобрать все
-x
выводить заголовки
-mi8086
дизассемблировать как 16-битный код 8086
-Mintel
дизассемблирование должно выполняться с использованием синтаксиса INTEL, а не синтаксиса по умолчанию ATT.
person
Michael Petch
schedule
19.05.2016
ld
на самом деле вставлял префиксы размера перед инструкциями ... все это выглядит странно). Какие настоящие команды вы используете для сборки и компоновки? Как ты в обоих случаях сваливаешь результат? - person davmac   schedule 18.05.2016BITS 16
, чтобы сделать это явным. - person Ross Ridge   schedule 18.05.2016jmp 0:continue
может поступить правильно. Как ассемблер или компоновщик может узнать, куда вы скопировали код? - person Ross Ridge   schedule 18.05.2016nasm -f bin -o mbr.dd mbr.asm
илиnasm -f elf mbr.asm; ld mbr.o -o mbr.bin -Ttext 0x00007C00 --oformat binary
, и я просто скопировал файлы, чтобы получить коды операций - person Willdorf   schedule 18.05.2016elf
по умолчанию 32, сbin
по умолчанию 16. - person Ross Ridge   schedule 18.05.2016org
. Или измените-Ttext 0x00007C00
в своей команде компоновщика. Вы утверждаете, что код находится по адресу 0x7C00, и он делает именно то, что вы просите ... - person davmac   schedule 18.05.2016