Wil*_*orf 4 x86 assembly nasm osdev bootloader
我正在尝试编写一个非常简单的MBR来开始学习如何编写MBR /内核.这是我到目前为止(从其他MBR的部分创建).我从使用nasm然后使用ld到链接的二进制文件与仅使用nasm两者有点不同,但这似乎不是问题.
我刚开始,jmp 0:continue但似乎跳转到0000:7c22(或001d单独使用nasm ...我相信我没有指定它开始于7c00)但我希望跳转到:7a22或:7a1d重新定位代码的地址.我尝试使用just jmp continue,然后在下面看到未注释,将堆栈指针添加到continue指针,推送它并返回.当我读到第一个扇区时,我得到的只是一个闪烁的光标.任何帮助表示赞赏.
; nasm+ld nasm comment
global _start
_start:
xor cx, cx ; 6631c9 31c9 Set segment registers to zero
mov es, cx ; 8ec1 8ec1
mov ds, cx ; 8ed9 8ed9
mov ss, cx ; 8ed1 8ed1
mov sp, 0x7A00 ; 66bc007a bc007a Stack
mov di, sp ; 6689e7 89e7 Bottom of relocation point
mov esi, _start ; be007c0000 66be00000000
cld ; fc fc
mov ch, 1 ; b501 b501 cx = 256
rep movsw ; f366a5 f3a5 Copy self to 0:7A00
;----------------------------------------------------------------------------------------------------------------------
xor eax,eax
mov ax, sp
add ax, continue
;jmp 0:continue ; ea227c00000000 ea1d000000 near JMP to copy of self
; or
;jmp continue ; (eb00)
push eax
ret
;----------------------------------------------------------------------------------------------------------------------
continue:
sti ; fb fb
ERROR:
mov esi, errormsg ; be3b7c0000 (be36) 66be36000000 Error Message loc
mov ah, 0x0E ; b40e b40e
mov bx, 7 ; 66bb bb0700
disp:
lodsb ; ac ac
cmp ah, 0x00 ; 80fc00 80fc00
je end ; 7404 7404
int 10h ; cd10 cd10
jmp disp ; ebf6 ebf6
end:
nop ; 90 90
jmp end ; ebfd ebfd infinte loop
errormsg db 10,'YOU MESSED UP.',13,0
times (0x1b8 - ($-$$)) nop ; 90 90 Padding
UID db 0xf5,0xbf,0x0f,0x18 ;Unique Disk ID
BLANK times 2 db 0
PT1 db 0x80,0x20,0x21,0x00,0x0C,0x50,0x7F,0x01,0x00,0x08,0x00,0x00,0xb0,0x43,0xF9,0x0D ;First Partition Entry
PT2 times 16 db 0 ;Second Partition Entry
PT3 times 16 db 0 ;Third Partition Entry
PT4 times 16 db 0 ;Fourth Partition Entry
BOOTSIG dw 0xAA55 ;Boot Signature[/code]
Run Code Online (Sandbox Code Playgroud)
谢谢!
正如您所发现的,您可以ORG 0x7A00为整个引导加载程序设置原点.这非常有效.将引导扇区复制到0x7A00的代码不依赖于任何绝对标签,只依赖于亲属标签.这个答案更多的是一个思想实验和一种接近它的不同方式.
如果我们想在复制之前显示一个字符串作为例子,会发生什么?有哪些可能的选择?
-f bin)具有呈现虚拟起始点(原点)和物理地址(开始)的部分.此方法对引导加载程序的布局方式限制太多.这个答案侧重于选项2.解释LD链接器脚本如何工作对于Stackoverflow而言过于宽泛.该LD手册是最好的信息源,并且它也有实施例.我们的想法是允许引导加载程序在链接描述文件中布局.我们可以设置LMA(加载内存地址)来指定将该部分加载到内存中的内存地址.VMA是一个部分的原点.部分中的所有标签和地址将相对于其VMA进行解析.
方便地,我们可以使用具有特定LMA的部分将引导签名直接放入输出文件中,而不是在汇编代码中指定它.我们还可以从链接器脚本定义符号,这些符号可以使用NASM extern指令从汇编代码中访问.
所有这一切的一个优点是,您可以按照您想要的任何顺序在汇编代码中定义节,并且链接器脚本将重新排序.您还可以将多个目标文件链接在一起.首先应列出包含要首先显示的引导代码的目标文件.
此链接描述文件的布局大致如下所示:
Run Code Online (Sandbox Code Playgroud)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);
}
}
Run Code Online (Sandbox Code Playgroud)
使用此链接描述文件及其中定义的符号修改代码副本可能如下所示:
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
Run Code Online (Sandbox Code Playgroud)
作为一个实验来确保该boot.text部分中的代码可以访问boot.data我在复制之前显示字符串的数据.然后我对重定位的代码执行FAR JMP.重定位的代码显示成功字符串.
我将代码修改为不使用ESI之类的32位寄存器,因为您将在实模式下执行此代码.我还修改了你的无限循环以使用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
Run Code Online (Sandbox Code Playgroud)
左列上的所有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>
Run Code Online (Sandbox Code Playgroud)
左列中的VMA相对于0x7A00的开头是正确的.该指令mov si,0x7a54是一个绝对的近存储器地址,并且它被正确编码以引用该successmsg地址(为了简洁,我将数据剪切掉,因此它不会出现).
参赛作品:
00007a3d <rel_entry-0x1>:
...
Run Code Online (Sandbox Code Playgroud)
是否将该.bootrel部分与偶数字边界对齐有关的信息.使用此链接器脚本rel_entry将始终具有偶数地址.
最简单的方法是使用以下命令:
nasm -f elf32 -o boot.o boot.asm
ld -melf_i386 -Tlinker.ld -o boot.bin --oformat=binary boot.o
Run Code Online (Sandbox Code Playgroud)
应该指出的是,我们使用的是带有NASM的ELF32格式,而不是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
Run Code Online (Sandbox Code Playgroud)
与第一种方法的不同之处在于我们不使用LD--oformat=binary选项.结果将生成ELF32图像并将其放置在输出文件中.我们不能直接用作引导映像,因此我们使用OBJCOPY将ELF32文件转换为名为的二进制文件.如果我们使用这样的命令来转储ELF文件的内容和反汇编,可以看到以这种方式执行它的有用性:boot.elfboot.elfboot.bin
objdump boot.elf -Mintel -mi8086 -Dx
Run Code Online (Sandbox Code Playgroud)
-D 选项是全部拆解-x 输出标题-mi8086 反汇编为16位8086代码-Mintel反汇编应该是INTEL语法而不是默认的ATT语法| 归档时间: |
|
| 查看次数: |
671 次 |
| 最近记录: |