【问题标题】:How to properly create a kernel in C and link to bootloader如何在 C 中正确创建内核并链接到引导加载程序
【发布时间】:2019-03-21 01:47:06
【问题描述】:

我在这里组装了一个简单的引导加载程序:

;initialization type stuff
[bits 16]
[org 0x7c00]

;make message and print it
mov si, MESSAGE
call print

;infinite loop to stall
jmp $

;print "method"
print:
    pusha
    print_loop:
        mov al, [si]
        cmp al, 0
        jne print_char
        popa
        ret
    print_char:
        mov ah, 0x0e
        int 0x10
        add si, 1
        jmp print_loop

;actual assigning of message
MESSAGE: db "Hello World!", 0

;padding & magic number (end)
times 510-($-$$) db 0
dw 0xaa55

我的目标是用 C 语言编写大部分操作系统,目前我正在尝试用 C 语言正确链接引导加载程序和内核:

void kernel_main()
{
    printf("Hello World!");
}

我如何将这些链接在一起并将它们编译成可以在 qemu/任何普通计算机中打开的东西?

【问题讨论】:

  • 你使用什么编译器、汇编器、链接、工具链?什么提供了printf 实现?
  • 链接器 是一个程序,它接收多个目标文件并将它们链接到一个大程序中。通常,您使用编译器生成汇编文件,使用汇编器将它们汇编成目标文件,并使用链接器将所有这些目标文件链接到一个程序中。在深入了解您的内核项目之前,我建议您熟悉链接的工作原理,因为它是这个难题中非常重要的一部分。
  • 请注意,GCC 并不能真正生成正确的 16 位代码。如果您要使用 GCC,我建议您的引导加载程序进入保护模式并编写 32 位内核(或从引导加载程序进入 64 位长模式以获取 64 位内核)。如果您想创建 16 位实模式内核,请考虑 Alexey 的 Small C Compiler、Openwatcom C 或 BCC(Bruce 的 C 编译器)。
  • 这是一个非常简单的引导加载程序,它与在保护模式下运行的 32 位 C 内核链接。引导加载程序在调用内核之前进入 32 位保护模式。内核在 0x7e00 的引导加载程序之后加载。大多数繁重的工作都是通过专门的链接器脚本完成的。 capp-sysware.com/misc/osdev/linkedboot_kreloc
  • @liaquore 没错。决定程序的哪些部分最终到哪里是链接器的工作,并且可以通过 链接器脚本来控制。 如果您生成目标文件,则使用 org 指令毫无意义.

标签: c assembly x86 bootloader osdev


【解决方案1】:

如何在 C 中正确创建内核并链接到引导加载程序

不要。

操作系统通常是许多单独的可执行文件(引导加载程序、内核、GUI、文本编辑器、Web 浏览器等),它们不会链接到一个可以执行所有操作的单个大型文件中(例如“Ubuntu.exe”) .

适用于 80x86 的引导加载程序;一般来说,您可能有:

  • 为未分区存储设备(例如古老的软盘)设计的 BIOS 引导加载程序
  • 为“MBR 分区”存储设备设计的 BIOS 引导加载程序
  • 为“GPT 分区”存储设备设计的 BIOS 引导加载程序
  • 为可引导 CD 设计的 BIOS 引导加载程序(“无仿真 El Torito”)
  • 为 PXE/网络引导设计的 BIOS 引导加载程序
  • 一个引导加载程序设计 UEFI(并编译为 32 位)
  • 一个引导加载程序设计 UEFI(并编译为 64 位)

每一件事都涉及一些非常不同的代码(引导加载程序文件格式、固件限制、固件 API、从何处获取内核等)。

您希望确保您的任何引导加载程序都可以与同一个内核一起使用(并确保一个可能是错误的特定引导加载程序没有内置/链接到内核中)。

不要忘记(最终,不是在早期开发期间)最终用户下载/获得某种“操作系统安装程序”;并且“操作系统安装程序”确定要与操作系统一起安装的正确引导加载程序(可能是在询问用户是否/在哪里/如何安装操作系统以及在检测到有关正在安装操作系统的计算机的各种信息之后- 什么类型的 CPU,什么类型的固件,什么类型的分区方案,...)。在“操作系统安装程序”完成大量工作之前,您无法知道哪个引导加载程序是正确的引导加载程序(并且在构建项目时无法知道哪个引导加载程序是正确的引导加载程序)。

【讨论】:

  • 所以在我的引导加载程序中,我可以引用外部文件,例如kernel.o?
  • @liaquore : 通常人们所做的是编译他们的C(和汇编)代码,然后链接它,并创建一个独立于引导加载程序的内核二进制映像.他们所做的是使用一种机制,强制他们的内核入口点成为二进制映像开头的指令。这样,如果您从物理地址 0x07e00(我使用 0x7e00,但它可以是任何地址)开始加载内核,您将跳转到地址 0x07e00 以启动内核运行。
  • @liaquore:引导加载程序的工作之一是查找和加载(或下载)其他文件(配置文件、内核、初始 RAM 磁盘……)。引导加载程序必须做一些其他事情(例如,从固件获取内存映射)并且可能会做很多其他事情(例如,设置一些默认视频/输出、解压缩文件、设置分页、查找各种固件表、自动检测哪个kernel 是最好的启动内核等)
猜你喜欢
  • 2015-02-21
  • 2021-10-12
  • 2014-04-23
  • 1970-01-01
  • 1970-01-01
  • 2012-03-02
  • 1970-01-01
  • 2015-01-19
  • 2020-09-10
相关资源
最近更新 更多