【问题标题】:Assembly and C how to compile with GCC-TDM(win10x64) properly?汇编和 C 如何正确使用 GCC-TDM(win10x64) 编译?
【发布时间】: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


【解决方案1】:

在我搜索了很多文档之后,我发现制作引导加载程序非常难,同时也不难。是的,从头开始写所有东西对我来说是不可能的。评论是正确的,这可能在模拟器中工作,但会在真实硬件中引起很多问题。我无法处理这个。最后,我发现intel有EFI。 Whis 现在是 UEFI。不过,我并不声称自己知道任何事情。但至少我在搜索之后学到了很多东西。首先,从 Windows 迁移到 linux 很容易,但实际上很难做到。改变你的习惯是不可能的。首先我搜索了 linux 版本的 UEFI。有很多版本问题。然后我寻找WIN版本。希望找到vs2019兼容版本的uefi环境。项目在“gnu-efi”中没有源代码,并且在构建时抛出错误。我根据信息搜索了“gnu-efi-3.0.12”并下载了它。我将主文件夹中的文件放入“gnu-efi”。然后,回到 vs2019。构建它并且构建成功。我在调试模式 aa64 平台上运行它。我收到一条 Windows 消息,它会下载一次 qemu。它自己下载了,我得到了带有正确消息的 qemu 页面,例如:

Print(L"\n%H*** UEFI:HI I AM GOMI ***%N\n\n");
Print(L"%EPRESS ANY KEY AND EXIT.%N\n");

我还检查了调试文件夹并找到了“.efi”文件。我不确定我是否制作了一个真正的可启动 USB 设备并放入可以工作的编译文件。

但是,既然那是 efi,我认为应该启动。

我认为有用的源页面

uefi 的好网站:https://wiki.osdev.org/

vs2019 兼容项目:https://github.com/pbatard/uefi-simple

结果:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-10
    • 1970-01-01
    相关资源
    最近更新 更多