Assembly 如何将JMP映射到MBR中重新定位的代码?
我正在尝试编写一个非常简单的MBR来开始学习如何编写MBR/内核。这就是我到目前为止所做的(从其他MBR的片段创建)。使用nasm和ld-to-link得到的二进制文件与仅使用nasm两者有点不同,但这似乎不是问题所在 我最初是从Assembly 如何将JMP映射到MBR中重新定位的代码?,assembly,x86,nasm,bootloader,osdev,Assembly,X86,Nasm,Bootloader,Osdev,我正在尝试编写一个非常简单的MBR来开始学习如何编写MBR/内核。这就是我到目前为止所做的(从其他MBR的片段创建)。使用nasm和ld-to-link得到的二进制文件与仅使用nasm两者有点不同,但这似乎不是问题所在 我最初是从jmp 0:continue开始的,但它似乎跳转到0000:7c22(或001d仅用nasm…我相信我没有指定它从7c00)开始,但我希望跳转到:7a22或:7a1d,重新定位代码的地址。我尝试只使用jmp continue,然后如下面未注释所示,将堆栈指针添加到con
jmp 0:continue
开始的,但它似乎跳转到0000:7c22
(或001d
仅用nasm…我相信我没有指定它从7c00
)开始,但我希望跳转到:7a22
或:7a1d
,重新定位代码的地址。我尝试只使用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]
使用以下命令编译和链接:
nasm-f bin-o mbr.bin mbr.asm
[BITS 16]
ORG 0x00007a00
; opcodes comment
global _start
_start:
xor cx, cx ; 31c9 Set segment registers to zero
mov es, cx ; 8ec1
mov ds, cx ; 8ed9
mov ss, cx ; 8ed1
mov sp, 0x7A00 ; bc007a Stack
mov di, sp ; 89e7 Bottom of relocation point
mov esi, 0x00007C00 ; 66be007c0000 Original location
cld ; fc
mov ch, 1 ; b501 CX = 256
rep movsw ; f3a5 Copy self to 0:7A00
jmp 0:continue ; ea1d7a0000 near JMP to copy of self
continue:
sti ; fb
ERROR:
mov esi, errormsg ; 66be357a0000 Error Message location
mov ah, 0x0E ; b40e 0E TTY Output
mov bx, 7 ; bb0700 Page number
disp:
lodsb ; ac Load next char
cmp al, 0x00 ; 3c00 Compare to zero
je end ; 7404 If so, end
int 10h ; cd10 Display char
jmp disp ; ebf6 Loop
end:
nop ; 90 Do Nothing
jmp end ; ebfd infinte loop
errormsg db 10,'YOU MESSED UP!',13,0
times (0x1b8 - ($-$$)) nop ; 90909090... 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
PT1more db 0x00,0x08,0x00,0x00,0xb0,0x43,0xF9,0x0D
PT2 times 16 db 0
PT3 times 16 db 0
PT4 times 16 db 0
BOOTSIG dw 0xAA55 ;Boot Signature
hextump-C mbr.bin的输出
:
00000000 31 c9 8e c1 8e d9 8e d1 bc 00 7a 89 e7 66 be 00 |1.........z..f..|
00000010 7c 00 00 fc b5 01 f3 a5 ea 1d 7a 00 00 fb 66 be ||.........z...f.|
00000020 35 7a 00 00 b4 0e bb 07 00 ac 3c 00 74 04 cd 10 |5z........<.t...|
00000030 eb f7 90 eb fd 0a 59 4f 55 20 4d 45 53 53 45 44 |......YOU MESSED|
00000040 20 55 50 21 0d 00 90 90 90 90 90 90 90 90 90 90 | UP!............|
00000050 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 |................|
*
000001b0 90 90 90 90 90 90 90 90 f5 bf 0f 18 00 00 80 20 |............... |
000001c0 21 00 0c 50 7f 01 00 08 00 00 b0 43 f9 0d 00 00 |!..P.......C....|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200
00000000 31 c9 8e c1 8e d9 8e d1 bc 00 7a 89 e7 66 be 00 | 1………z..f|
00000010 7c 00 fc b5 01 f3 a5 ea 1d 7a 00 fb 66 be | | | | z…f|
000000 20 35 7a 00 00 b4 0e bb 07 00 ac 3c 00 74 04 cd 10 | 5z……。正如您所发现的,您可以为整个引导加载程序将原点设置为ORG 0x7A00
。这非常有效。将引导扇区复制到0x7A00的代码不依赖于任何绝对标签,只依赖于相关标签。这个答案更多的是一个思维实验,也是一种不同的方法
例如,如果我们想在副本之前显示一个字符串,会发生什么?有哪些可能的选择
NASM允许BIN格式(-f BIN
)包含具有一个(原点)和一个物理地址(起点)的部分。对于引导加载程序的布局,此方法限制太多
使用定义引导加载程序的布局李>
重新组织代码以使用0x0000的组织(原点),并相应地设置段寄存器。请听我回答这个问题
这一回答侧重于备选方案2。对于Stackoverflow来说,解释LD链接器脚本如何工作太广泛了。这是最好的信息来源,它确实有例子。我们的想法是允许引导加载程序被放置在链接器脚本中。我们可以设置LMA(加载内存地址)来指定将节加载到内存中的内存地址。VMA是截面的原点。节中的所有标签和地址将相对于其VMA进行解析
我们可以方便地使用带有特定LMA的节将引导签名直接放入输出文件中,而不是在汇编代码中指定它。我们还可以使用NASMextern
指令从汇编代码中定义链接器脚本中的符号
所有这些的一个优点是,您可以在汇编代码中按任何顺序定义节,链接器脚本将重新排序。还可以将多个对象文件链接在一起。应该首先列出包含要首先显示的启动代码的目标文件
此链接器脚本的布局大致如下所示:
用于布局此引导加载程序的链接器脚本可能类似于:
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
中的数据,我在复制之前显示了一个字符串。然后,我对重新定位的代码执行远JMP。重新定位的代码将显示一个成功字符串
我修改了代码,不使用像ESI这样的32位寄存器,因为您将在实模式下执行此代码。我还修改了无限循环以使用指令
代码和链接器脚本可以修改为仅从重新定位的数据的开头复制到第512个字节,但超出了此答案的范围
一看拆卸
下面提供了原点为0x7c00的.bootinit
部分。这是该节的OBJDUMP片段(为了简洁起见,没有数据):
左列中的VMA相对于0x7A00的开头,这是正确的。指令mov si,0x7a54
是一个绝对的近内存地址,它被正确编码以引用successsg
地址(为了简洁起见,我删掉了数据,所以它不会出现)
参赛作品:
00007a3d <rel_entry-0x1>:
...
应该指出的是,我们在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
与第一种方法不同的是,我们不使用LD的--oformat=binary
选项。结果将生成一个ELF32映像,并将其放置在输出文件boot.elf
中。我们不能直接使用boot.elf
作为引导映像,因此我们使用OBJCOPY将ELF32文件转换为名为boot.bin
的二进制文件。如果我们使用这样的命令来转储ELF文件的内容和反汇编,就可以看出这样做的有用性:
-D
选项为全部反汇编
-x
输出标题
-mi8086
反汇编为16位8086代码
-Mintel
反汇编应为英特尔语法,而不是默认的ATT语法
重新定位MBR/引导加载程序的另一种选择,它不涉及链接器脚本,但允许引导加载程序中的所有代码和数据重新定位到段:偏移对=段:0x0000的任何位置。这并没有限制
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>
00007a3d <rel_entry-0x1>:
...
nasm -f elf32 -o boot.o boot.asm
ld -melf_i386 -Tlinker.ld -o boot.bin --oformat=binary boot.o
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
objdump boot.elf -Mintel -mi8086 -Dx
Physical address = (segment << 4) + offset
Physical address = (0x0000 << 4) + 0x7c00 = 0x07c00
Physical address = (0x07c0 << 4) + 0x0000 = 0x07c00
BITS 16
ORG 0x0000
global _start
_start:
xor bp, bp
mov ss, bp ; Set SS segment register to zero
mov sp, 0x7A00 ; Set stack to SS:SP=0x0000:0x7A00
mov dx, 0x07C0
mov ds, dx ; DS = 0x07C0 - Segment:Offset = 0x07c0:0x0000 = 0x07C00
sub dx, 0x20 ; DX = 0x07C0-0x20=0x07A0
mov es, dx ; ES = 0x07A0 - Segment:Offset = 0x07a0:0x0000 = 0x07A00
cld
; Print message before the copy
mov si, copymsg ; Copy message (DS:[copymsg])
call outputstr
; Copy 256 words of memory from 0x07c00 to 0x07a00
mov di, bp ; Destination is ES:DI = 0x07a0:0x0000 = 0x07a00
mov si, bp ; Source is DS:SI = 0x07c0:0x0000 = 0x07c00
mov cx, 256 ; CX = 256 words to copy
rep movsw ; Copy self to destination
jmp 0x07A0:rel_entry
; far JMP jumps to phys address 0x07a00, sets
; CS = 0x07A0 and IP = rel_entry.
rel_entry:
sti ; Enable interrupts
mov ds, dx ; DS=ES=0x07A0
; ES already 0x07A0 before the jump, no need to set again
.success:
; Print the "Success!" message
mov si, successmsg ; Success Message location (DS:[successmsg])
call outputstr
cli ; Disable interrupts
.end:
hlt ; CPU hlt
jmp .end ; infinite loop
; Function: outputstr
; Inputs:
; SI = address of string
; Outputs:
; None
; Registers destroyed:
; AX, BX, SI
;
outputstr:
mov ah, 0x0E ; 0E TTY Output
mov bx, 7 ; Page number
.disp:
lodsb ; Load next char from DS:[SI], SI+=2
test al, al ; Compare to zero
je .end ; If so, end
int 0x10 ; Display char
jmp .disp ; Loop
.end:
ret
successmsg db 10,'Success!',13,0
copymsg db 10,'Before copy!',13,0
times (0x1b8 - ($-$$)) db 0x00 ; 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
PT1more db 0x00,0x08,0x00,0x00,0xb0,0x43,0xF9,0x0D
PT2 times 16 db 0
PT3 times 16 db 0
PT4 times 16 db 0
BOOTSIG dw 0xAA55 ;Boot Signature
nasm -f bin boot.bin boot.asm