nasm 引导扇区中可能存在语法问题

Posted

技术标签:

【中文标题】nasm 引导扇区中可能存在语法问题【英文标题】:Possible syntax problem in boot sector in nasm 【发布时间】:2021-12-16 11:26:26 【问题描述】:

我正在为我的操作系统编写一个 64 位引导扇区,我发现了这段代码(使用此链接作为参考 https://github.com/gmarino2048/64bit-os-tutorial/blob/master/Chapter%203/3.1%20-%20The%20IDT/bootloader/boot.asm)。这段代码对我不起作用。这是有效的 nasm 语法吗?据我了解,有两件事是错误的。

    我们在没有 ret 指令的情况下调用标签 标签语法错误,因为equ 用于定义常量但不是这样
; BEGIN SECOND SECTOR. THIS ONE CONTAINS 32-BIT CODE ONLY

bootsector_extended:
begin_protected:

[bits 32]

在这里,我清除了一些不相关的功能,例如使用 vga 缓冲区进行操作并从其他文件中包含在内。以下代码是文件的延续。

; Define necessary constants
vga_start:                  equ 0x000B8000
vga_extent:                 equ 80 * 25 * 2             ; VGA Memory is 80 chars wide by 25 chars tall (one char is 2 bytes)
style_wb:                   equ 0x0F

; Define messages
protected_alert:                 db `64-bit long mode supported`, 0

; Fill with zeros to the end of the sector
times 512 - ($ - bootsector_extended) db 0x00
begin_long_mode:

[bits 64]

mov rdi, style_blue
call clear_long

mov rdi, style_blue
mov rsi, long_mode_note
call print_long

call kernel_start

jmp $

%include "long_mode/clear.asm"
%include "long_mode/print.asm"

kernel_start:                   equ 0x8200              ; Kernel is at 1MB
long_mode_note:                 db `Now running in fully-enabled, 64-bit long mode!`, 0
style_blue:                     equ 0x1F

times 512 - ($ - begin_long_mode) db 0x00

代码在调用 kernel_start 时特别失败

【问题讨论】:

怎么不行?你有错误吗? 1)你可以在没有ret的情况下使用call,这不是语法错误,但我什至看不出你认为ret在哪里丢失2)通常不使用冒号,但至少我的nasm版本接受它. @Jester 我的鼻涕虫也吃它。据我了解,该代码应该以 1 mb 的速度加载内核,但它在 call print_long 之后“崩溃”并且一切都重新开始(整个程序是一个繁忙循环,正如您在链接中看到的那样。我想要用 gdb 调试它,但我缺乏在 qemu 中进行调试的经验 在哪里切换到64位模式 我终于得到了在 Bochs 下运行的教程操作系统。我对 nasm 语法不太熟悉,但显然 call kernel_start 是有效的,kernel_start 设置为 0x8200。反汇编看起来像:call .+477 (0x0000000000008200)。不知道为什么它说内核为 1MB,但内核映像跳转到 0x8200 并且 ELF 内核映像的反汇编显示地址是 kernel/src/cpu/isr_defs.asm:isr_common @sj95126:在 NASM 语法中,call <numeric literal or constant> 将数字视为绝对地址,并计算正确的 rel32 以从此处到达它。在平面二进制文件中,它基于org。 (在-felf64 或其他什么中,当前的nasm 填充rel32 假设当前节的开头是地址0,看起来。但是yasm 使用R_X86_64_PC32 重定位来让链接器填写正确的rel32。)令人困惑的是, nasm -l/dev/stdout 列出不显示实际的机器代码字节,它显示计算的目标地址,所以我不得不使用ndisasm -b64 【参考方案1】:

答案因较新的代码而无效。除了您可以挑选的评论链中仍然有用的信息之外,这将是一个删除答案。

【讨论】:

重要的不是 jmp 与 call 对吧?相当远与近。如果你愿意,你可以做一个很远的call。 (如果你的 main 返回 retf,它可以返回到这个无限循环。) @PeterCordes:我做了一些调整,但我很确定 ret 只会在偶然的情况下起作用。 这里所说的有点令人困惑。启用长模式后,您需要做的第一件事就是在cs 中加载新的代码段描述符。如果你在实模式下,你会做一个 16 位的远跳。如果您处于保护模式,则为 32 位远跳。无论哪种方式,在您重新加载 cs 之前,您都不会运行 64 位指令。 @Joshua:但这是我的意思,不是那种状态。仅仅加载一个新的 GDT 并不意味着这些描述符正在使用中。运行 16 位还是 64 位指令由代码段描述符中的D/BL 位决定。在您强制重新加载之前,旧值仍位于 cs 的隐藏/缓存部分中,并且您尚未解码 64 位指令。 @Joshua:我们不知道发生了什么,因为我们还没有看到所有的代码。但是,如果它完全按照该教程中的内容进行操作,它会启用保护模式并立即进行一次远跳,然后它会启用长模式并进行另一次远跳。所以我认为问题出在其他地方。

以上是关于nasm 引导扇区中可能存在语法问题的主要内容,如果未能解决你的问题,请参考以下文章

如何使用NASM调用位于后两个扇区的代码?

MBR扇区故障修复

MBR扇区故障及修复

Linux常见故障-------grub菜单故障

MBR引导扇区故障恢复

有关MBR的问题