【发布时间】: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。
标签: assembly nasm bootloader boot