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/B
和L
位决定。在您强制重新加载之前,旧值仍位于 cs
的隐藏/缓存部分中,并且您尚未解码 64 位指令。
@Joshua:我们不知道发生了什么,因为我们还没有看到所有的代码。但是,如果它完全按照该教程中的内容进行操作,它会启用保护模式并立即进行一次远跳,然后它会启用长模式并进行另一次远跳。所以我认为问题出在其他地方。以上是关于nasm 引导扇区中可能存在语法问题的主要内容,如果未能解决你的问题,请参考以下文章