【发布时间】:2021-03-06 11:49:34
【问题描述】:
我正在尝试制作一个简单的程序。我使用 windows10x64、qemu(x86_64)、C 和 assembly-nasm。我没有使用 asm 的经验。我试图理解它。我使用了一个简单的 boot.asm、kernel_entry.asm 和 loader.c 文件。我基于一个totorial。
boot.asm
;; memory offset where our kernel is located
KERNEL_OFFSET equ 0x1000
;; save the boot drive number
mov [BOOT_DRIVE], dl
;; update base and stack pointers
mov bp, 0x9000
mov sp, bp
init:
mov si, msg ; loads the address of "msg" into SI register
mov ah, 0x0e ; sets AH to 0xe (function teletype)
print_char:
lodsb ; loads the current byte from SI into AL and increments the address in SI
cmp al, 0 ; compares AL to zero
je done ; if AL == 0, jump to "done"
int 0x10 ; print to screen using function 0xe of interrupt 0x10
jmp print_char ; repeat with next byte
;; call routine that loads kernel into memory
call load_kernel_into_memory
;; switch to Protected Mode
call switch_to_pm
jmp $
;; routine reads kernel from disk into memory
load_kernel_into_memory:
;; store all register values
pusha
;; set up parameters for disk_read routine
mov bx, KERNEL_OFFSET
mov dh, 15
mov dl, [BOOT_DRIVE]
call disk_read
;; restore register values and ret
popa
ret
;
[bits 32]
begin_pm:
;; Check if we can move from Protected Mode to Long Mode
;; If something went wrong (detect_lm shouldnt return at all)
;; we call execute_kernel in x32 Protected Mode
call detect_lm
call execute_kernel
jmp $
[bits 64]
begin_lm:
;; In case, if detect_lm and switch_to_lm works fine, call kernel in x64 mode
call execute_kernel
jmp $
;
execute_kernel:
call KERNEL_OFFSET
jmp $
;
%include "disk/disk_read.asm";
%include "lm/detect_lm.asm";
%include "lm/switch_to_lm.asm";
%include "pm/gdt.asm"
%include "pm/switch_to_pm.asm"
%include "print/print_string.asm";
%include "print/print_nl.asm";
BOOT_DRIVE: db 0
done:
hlt ; stop execution
msg: db "PROGRAM STARTED", 0x0a; we need to explicitely put the zero byte here
;
times 510-($-$$) db 0 ; fill the output file with zeroes until 510 bytes are full
dw 0xaa55 ; magic number that tells the BIOS this is bootable
kernel_entry.asm
global _start
global kernel_main
[bits 32]
[extern _kernel_main] ; I use "_" otherwise it will not compile. _kernel_main comes from "c" file.
;In there that is kernel_main
_start:
call _kernel_main
jmp $
loader.c
#include <stdio.h>
extern void kernel_main() {
//printf("test");
for (int i = 0; i < 26; i++) {
char c = 0x41 + i;
asm(
"mov %0, %%al;"
"mov $0x0E, %%ah;"
"int $0x10;"
:
: "r" (c)
);
}
}
我使用 nasm-WINx64version 和 GCC-TDM 在 WINx64 上编译。
我的步骤是:
nasm kernel_entry.asm -f elf32 -o loader_entry.o
gcc -O0 -g -ffreestanding -m32 -c C:/Users/_USR_/Desktop/nasm-2.15.05/loader.c -o C:/Users/_USR_/Desktop/nasm-2.15.05/loader.o
ld -o "C:/Users/_USR_/Desktop/nasm-2.15.05/loader.tmp" -m i386pe -Ttext 0x1000 -T NUL C:/Users/_USR_/Desktop/nasm-2.15.05/loader_entry.o C:/Users/_USR_/Desktop/nasm-2.15.05/loader.o
objcopy -O binary loader.tmp loader.bin
type boot.bin loader.bin > myboot.bin
最后我在 QEMU 中运行它并得到“程序启动”输出但没有“C”语言消息。此外,当我使用 printf("..."); 时出现未声明的错误。还有一件事,如果我在 LD 命令中添加 -lc 参数,我会收到“找不到”消息。看起来它不识别“C”。否则我运行程序 aster 与“C”和 asm 链接。我没有收到错误,但我也没有得到“C”函数。
我在这里做错了什么?
【问题讨论】:
-
C 库对您不可用,因此 printf 不可用,除非您编写它。您必须自己制作。我建议使用交叉编译器,而不是原生 GCC Windows 编译器,因为这可能会引发不必要的问题,从而使内核开发变得非常困难。
-
您的内联程序集有问题,并且它在不告诉编译器的情况下破坏了寄存器。您的引导加载程序假定段寄存器设置为 0,但这可能在模拟器中工作,但在真实硬件上您可能会遇到问题。
-
关于内联汇编中实际代码的一个非常严重的问题是它使用了 BIOS 中断。您不能在受保护模式下使用它们。如果达到该代码,处理器将出现故障,并可能导致机器以三重故障复位。
-
整件事都非常值得怀疑。您在哪里学习了本教程?你至少应该停止关注它。
-
首先,@Michael Petch 感谢您的建议。那很有帮助。评论 1.) 我不使用 linux,但主要基于 linux 的资源。所以我想我可以使用教程来做,但这是一个非常糟糕的主意。您认为我可以使用 linux 版本获得更好的结果吗?声明“您无法使用 C 库”您的意思是我没有 clib 吗? GCC 应该有它,但不知何故没有库。 2)你的意思是我没有禁用中断? 3)谢谢你的警告,我明白了。
标签: assembly gcc x86 nasm osdev