【问题标题】:Bootloader crash引导加载程序崩溃
【发布时间】:2012-10-12 12:47:08
【问题描述】:

我目前正在开发我的引导加载程序,但我遇到了问题。

我使用 Bochs 测试引导加载程序,我编译引导加载程序并制作磁盘映像:

rm disk.bin
rm boot.bin
rm post.bin
nasm bootloader.asm -o boot.bin
nasm postmbr.asm -o post.bin
cat boot.bin post.bin > disk.bin

这是 bootloader.asm:

[BITS 16]       ;Tells the assembler that its a 16 bit code
[ORG 0x7C00]    ;Origin, tell the assembler that where the code will

MOV SI, HelloString ;Store string pointer to SI
MOV AH, 02h          ; read sector command
MOV AL, 01h
MOV CX, 0001h
MOV DH, 00h
MOV DL, 80h ;disk
MOV AX, 7E00h
MOV ES, AX ;buffer
MOV BX, 00h ;offset
CALL PrintString
JMP 0x7E00              ;Infinite loop, hang it here.


PrintCharacter: ;Procedure to print character on screen
        ;Assume that ASCII value is in register AL
MOV AH, 0Eh     ;Tell BIOS that we need to print one charater on screen.
MOV BH, 00h     ;Page no.
MOV BL, 07h     ;Text attribute 0x07 is lightgrey font on black background

INT 10h ;Call video interrupt
RET             ;Return to calling procedure



PrintString:    ;Procedure to print string on screen
        ;Assume that string starting pointer is in register SI

next_character: ;Lable to fetch next character from string
MOV AL, [SI]    ;Get a byte from string and store in AL register
INC SI          ;Increment SI pointer
OR AL, AL       ;Check if value in AL is zero (end of string)
JZ exit_function ;If end then return
CALL PrintCharacter ;Else print the character which is in AL register
JMP next_character      ;Fetch next character from string
exit_function:  ;End label
RET             ;Return from procedure


;Data
HelloString db 'Loading OS demo...', 0  ;HelloWorld string ending with 0

TIMES 510 - ($ - $$) db 0       ;Fill the rest of sector with 0
DW 0xAA55                       ;Add boot signature at the end of bootloader

还有 postmbr.asm:

[BITS 16]       ;Tells the assembler that its a 16 bit code

MOV SI, HelloString ;Store string pointer to SI
CALL PrintString        ;Call print string procedure
JMP $           ;Infinite loop, hang it here.


PrintCharacter: ;Procedure to print character on screen
        ;Assume that ASCII value is in register AL
MOV AH, 0x0E    ;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00    ;Page no.
MOV BL, 0x07    ;Text attribute 0x07 is lightgrey font on black background

INT 0x10        ;Call video interrupt
RET             ;Return to calling procedure



PrintString:    ;Procedure to print string on screen
        ;Assume that string starting pointer is in register SI

next_character: ;Lable to fetch next character from string
MOV AL, [SI]    ;Get a byte from string and store in AL register
INC SI          ;Increment SI pointer
OR AL, AL       ;Check if value in AL is zero (end of string)
JZ exit_function ;If end then return
CALL PrintCharacter ;Else print the character which is in AL register
JMP next_character      ;Fetch next character from string
exit_function:  ;End label
RET             ;Return from procedure


;Data
HelloString db 'Hello World', 0 ;HelloWorld string ending with 0

TIMES 512 - ($ - $$) db 0       ;Fill the rest of sector with 0

这是来自 Bochs 的崩溃日志:

00000004661i[BIOS ] $Revision: 11318 $ $Date: 2012-08-06 19:59:54 +0200 (Mo, 06. Aug 2012) $
00000319074i[KBD  ] reset-disable command received
00000321808i[BIOS ] Starting rombios32
00000322242i[BIOS ] Shutdown flag 0
00000322837i[BIOS ] ram_size=0x02000000
00000323258i[BIOS ] ram_end=32MB
00000363787i[BIOS ] Found 1 cpu(s)
00000377969i[BIOS ] bios_table_addr: 0x000fa438 end=0x000fcc00
00000396429i[BIOS ] bios_table_cur_addr: 0x000fa438
00000524046i[VBIOS] VGABios $Id: vgabios.c,v 1.75 2011/10/15 14:07:21 vruppert Exp $
00000524117i[BXVGA] VBE known Display Interface b0c0
00000524149i[BXVGA] VBE known Display Interface b0c5
00000527074i[VBIOS] VBE Bios $Id: vbe.c,v 1.64 2011/07/19 18:25:05 vruppert Exp $
00000800003i[XGUI ] charmap update. Font Height is 16
00000866078i[BIOS ] ata0-0: PCHS=1/1/2 translation=none LCHS=1/1/2
00004743252i[BIOS ] IDE time out
00016726470i[BIOS ] Booting from 0000:7c00
00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553i[CPU0 ] CPU is in real mode (active)
00016755553i[CPU0 ] CS.mode = 16 bit
00016755553i[CPU0 ] SS.mode = 16 bit
00016755553i[CPU0 ] EFER   = 0x00000000
00016755553i[CPU0 ] | EAX=000000aa  EBX=00000007  ECX=00090001  EDX=00000080
00016755553i[CPU0 ] | ESP=00000001  EBP=00000000  ESI=000e7c45  EDI=0000ffac
00016755553i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of DF if tf SF zf af PF cf
00016755553i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00016755553i[CPU0 ] |  CS:0000( 0004| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  DS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  SS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  ES:7e00( 0005| 0|  0) 0007e000 0000ffff 0 0
00016755553i[CPU0 ] |  FS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  GS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] | EIP=0000fd34 (0000fd34)
00016755553i[CPU0 ] | CR0=0x60000010 CR2=0x00000000
00016755553i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00016755553i[CPU0 ] 0x0000fd34>> pusha  : 60
00016755553e[CPU0 ] exception(): 3rd (12) exception with no resolution, shutdown status is 00h, resetting
00016755553i[SYS  ] bx_pc_system_c::Reset(HARDWARE) called
00016755553i[CPU0 ] cpu hardware reset
00016755553i[APIC0] allocate APIC id=0 (MMIO enabled) to 0x00000000fee00000
00016755553i[CPU0 ] CPUID[0x00000000]: 00000002 68747541 444d4163 69746e65
00016755553i[CPU0 ] CPUID[0x00000001]: 00000633 00010800 00000008 17cbfbff
00016755553i[CPU0 ] CPUID[0x00000002]: 00000000 00000000 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000000]: 80000008 68747541 444d4163 69746e65
00016755553i[CPU0 ] CPUID[0x80000001]: 00000633 00000000 00000000 c1c3f3ff
00016755553i[CPU0 ] CPUID[0x80000002]: 20444d41 6c687441 74286e6f 7020296d
00016755553i[CPU0 ] CPUID[0x80000003]: 65636f72 726f7373 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000004]: 00000000 00000000 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000005]: 01ff01ff 01ff01ff 40020140 40020140
00016755553i[CPU0 ] CPUID[0x80000006]: 00000000 42004200 02008140 00000000
00016755553i[CPU0 ] CPUID[0x80000007]: 00000000 00000000 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000008]: 00002028 00000000 00000000 00000000
00016755553i[     ] reset of 'cmos' plugin device by virtual method
00016755553i[     ] reset of 'dma' plugin device by virtual method
00016755553i[     ] reset of 'pic' plugin device by virtual method
00016755553i[     ] reset of 'pit' plugin device by virtual method
00016755553i[     ] reset of 'floppy' plugin device by virtual method
00016755553i[     ] reset of 'vga' plugin device by virtual method
00016755553i[     ] reset of 'ioapic' plugin device by virtual method
00016755553i[     ] reset of 'keyboard' plugin device by virtual method
00016755553i[     ] reset of 'harddrv' plugin device by virtual method
00016755553i[     ] reset of 'unmapped' plugin device by virtual method
00016755553i[     ] reset of 'biosdev' plugin device by virtual method
00016755553i[     ] reset of 'speaker' plugin device by virtual method
00016755553i[     ] reset of 'extfpuirq' plugin device by virtual method
00016755553i[     ] reset of 'parallel' plugin device by virtual method
00016755553i[     ] reset of 'serial' plugin device by virtual method

这是 Bochs 的配置文件:

# configuration file generated by Bochs
plugin_ctrl: unmapped=1, biosdev=1, speaker=1, extfpuirq=1, parallel=1, serial=1
config_interface: textconfig
display_library: x
memory: host=32, guest=32
romimage: file="/usr/share/bochs/BIOS-bochs-latest"
vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest"
boot: disk
floppy_bootsig_check: disabled=0
# no floppya
# no floppyb
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, mode=flat, translation=auto, path="disk.bin", cylinders=1, heads=1, spt=2, biosdetect=auto, model="Generic 1234"
ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata2: enabled=0
ata3: enabled=0
pci: enabled=0
vga: extension=vbe, update_freq=5
cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
cpuid: family=6, model=0x03, stepping=3, mmx=1, apic=xapic, sse=sse2, sse4a=0, sep=1, aes=0, xsave=0, xsaveopt=0, movbe=0, adx=0, smep=0, mwait=1
cpuid: vendor_string="AuthenticAMD"
cpuid: brand_string="AMD Athlon(tm) processor"

print_timestamps: enabled=0
port_e9_hack: enabled=0
private_colormap: enabled=0
clock: sync=none, time0=local, rtc_sync=0
# no cmosimage
# no loader
log: -
logprefix: %t%e%d
panic: action=ask
error: action=report
info: action=report
debug: action=ignore
keyboard: type=mf, serial_delay=250, paste_delay=100000, keymap=
user_shortcut: keys=none
mouse: enabled=0, type=ps2, toggle=ctrl+mbutton
parport1: enabled=1, file=""
parport2: enabled=0
com1: enabled=1, mode=null, dev=""
com2: enabled=0
com3: enabled=0
com4: enabled=0

我不知道是什么问题,我没有在其他平台上测试过我的引导加载程序。

【问题讨论】:

  • 奇怪的是没有任何寄存器内容的转储。还。除非我遗漏了什么,否则似乎没有任何迹象表明发生了引导扇区加载和执行。
  • @AlexeyFrunze 我已经用正确的崩溃日志更新了崩溃日志,来自文件(这个来自控制台输出)

标签: assembly x86 nasm osdev bochs


【解决方案1】:

嗯,故障转储明确指出 pusha 指令试图将寄存器压入已破坏的堆栈。

相关位:

00016755553e[CPU0] write_virtual_word_32():段限制违规
00016755553e[CPU0] write_virtual_word_32():段限制违规
00016755553e[CPU0] write_virtual_word_32():段限制违规
00016755553i[CPU0] | EAX=000000aa EBX=00000007 ECX=00090001 EDX=00000080
00016755553i[CPU0] | ESP=00000001 EBP=00000000 ESI=000e7c45 EDI=0000ffac
00016755553i[CPU0] | SEG sltr(index|ti|rpl) 基本限制 G D
00016755553i[CPU0] | CS:0000(0004| 0| 0) 00000000 0000ffff 0 0
00016755553i[CPU0] | SS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00016755553i[CPU0] | EIP=0000fd34 (0000fd34)
00016755553i[CPU0] 0x0000fd34>> pusha : 60
00016755553e[CPU0] exception(): 第三 (12) 个异常,无解决,关机状态为 00h,正在重置

当将数据写入堆栈时,该指令试图在偏移量 0 处越过下段边界。这会导致不可恢复的异常和以下重置。

最可能的原因是跳转到错误的位置并在那里执行随机的东西,可能是数据。

看看这个指令:

JMP 0x7E00              ;Infinite loop, hang it here.

CPU 执行后,CS:IP 将是 0:0x7E00。

现在看看你的 postmbr.asm:

[BITS 16]       ;Tells the assembler that its a 16 bit code

MOV SI, HelloString ;Store string pointer to SI
...

那里没有org,在这种情况下org 0是隐含的。

所以,这部分代码是在假设IP=0 开始时编译的,并且所有偏移量都是从这部分代码的开头开始计算的,从逻辑上讲,这意味着CS= DS=0 也是错误的。但是您在寄存器中使用错误的、意外的值跳转到它。

所以这段代码不会按预期执行。 IOW,它几乎可以做任何事情,我敢打赌,它确实会导致堆栈破坏和崩溃。

这里的关键教训:x86 代码不是位置独立的。

您应该插入适当的org (org 0x7E00) 或相应地设置寄存器并跳到这部分代码(例如使用jmp 0x7e0:0)。

更新:现在是我上一课的时候了……上述问题确实存在。然而,正如 @ughoavgfhw 所指出的,还有一个几乎在同一个地方,但发生的时间稍早一些。永远不会加载第二个 512 字节。是的。 BIOS 本身只加载第一个扇区。第一个扇区必须通过显式调用 BIOS int 13h 来加载第二个扇区。

【讨论】:

  • 我已将[ORG 0x7E00] 添加到 postmbr.asm,但仍然是同样的错误。
  • 您没有阅读更新和其他答案/cmets吗?这不是唯一的问题。第二部分未加载到内存中。 Jmp 0x7e00 跳进垃圾场。
  • 听起来可能是个愚蠢的问题,但是:int 13h 部分不会将代码加载到 RAM 中?
【解决方案2】:
JMP 0x7E00

此时跳转到 0x7E00 会导致意外行为。看起来您想跳转到 post.bin,您希望在 0x7E00 加载该文件。它不是。 BIOS 仅在 0x7C00 处加载了 1 个扇区 boot.bin。您必须手动加载任何其他扇区。

【讨论】:

    【解决方案3】:

    当您的引导加载程序开始运行时,大多数寄存器的值可以是任何值。这包括堆栈指针。您的崩溃报告显示 esp 为 1,并且由于 pusha 指令上的三重错误而发生崩溃,这基本上意味着您正在尝试将数据推送到不存在的堆栈上。这是因为你从来没有设置堆栈指针,所以你不知道它在哪里。您需要将堆栈指针设置为您首先要做的事情之一。我建议将其设置为 0x7c00,以便将其放置在您的代码下方。将其放在引导加载程序的开头附近:

    mov  sp, 0x7C00
    

    【讨论】:

    • -1。这不是问题。如果是这样,ISR 就没有机会工作了。看我的回答。
    • @AlexeyFrunze ISR 将有机会,因为堆栈没有在 1 处开始。虽然有可能(可能)您发现了实际问题,但我的回答仍然指出引导加载程序中的一个严重缺陷也可能导致类似的结果。
    • sp=1 不是凭空出现的。这是错误执行位置相关代码的结果。我同意这个缺陷,但它没有解决问题中的原始问题。
    • 正如我所说,sp 的值可以是任何值。它从某个值开始,并通过推送变为 1。另请注意,这些跳转和调用是相对的,因此唯一依赖于位置的代码是加载字符串的地址,这只会打印垃圾。而且他从来没有真正加载过那个扇区,所以他的代码可能甚至没有被执行。但这并不是讨论这个问题的最佳场所,所以如果你想继续,请随时与我聊天。 (所有会议室都已冻结/删除,因此请随时创建/选择其他会议室并告诉我)。
    • 哦,开枪。愚蠢的我。事实上,BIOS 不会加载 2 个扇区。您应该在答案中添加它。我将撤消 -1。
    猜你喜欢
    • 2013-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-06
    • 2017-07-06
    • 2015-04-26
    • 2015-08-17
    相关资源
    最近更新 更多