C/C++中的引导程序?

22

是否可能使用C或C ++创建引导程序,而不使用某种类型的汇编语言(最好不使用__asm)?我正在编写一个操作系统,并希望完全使用C和C ++进行编写。


如果它将成为一个开源项目,我很愿意像这样跟随它。 - McKayla
3
@Mehrdad - 在C语言中,没有办法在函数上下文之外编写代码,这一点非常重要,即使您可以访问内置函数。您如何设置原始堆栈指针?几乎每个函数调用都将以“push”指令开头,因此存在一个很大的进退两难。内置函数和内联汇编对于可移植性来说非常麻烦 - 我更希望看到引导加载程序的扁平汇编入口点。 - Carl Norum
1
@Mehrdad - 如果你使用魔法编译器功能编写类似于C但实际上不是C的代码,那并不算数。通过向编译器添加特殊行为来实现这一点是完全可能的。但这并不能使你的代码成为C语言。Jason关于设置堆栈指针的观点仍然是正确的 - 你如何在没有汇编的情况下解决这个问题?使用编译器内置函数?这仍然是作弊... - Carl Norum
2
由于您没有指定目标处理器,因此无法回答。 - bmargulies
由于您没有指定目标处理器,答案是“是”。 - Robᵩ
显示剩余8条评论
4个回答

24

这很依赖于系统。在大多数情况下,答案是否定的——在运行C代码之前,您需要编写一些自定义汇编语言来设置C运行时环境。不过,也有一些例外情况。例如,ARM Cortex-M0可以直接从复位运行C代码。

但是,假设你没有使用M0,那么你需要编写一些汇编语言。同样地,这取决于系统/芯片,但是您可能可以尝试使用如下简单的内容:

reset_vector:
    mov  sp, SOME_KNOWN_GOOD_STACK_ADDRESS
    call c_entry_point

该操作仅初始化堆栈指针并调用C程序入口点。当然,这种简单的设置依赖于芯片具有支持它的复位向量/向量表,RAM(或类似RAM)在调用复位向量之前被初始化等因素。在系统早期初始化中往往存在许多"陷阱"。

准备好熟悉你的编译器、汇编器和链接器文档——生成一个可以作为第一阶段引导加载程序的平面二进制文件通常是一个非常麻烦的过程。

祝你好运!


1
好的回答。我愿意支付(声望)来看一个完整的代码示例,该示例调用以裸启动方式启动的C函数(例如在qemu中)。 - Kerrek SB
@Carl:不,你不需要汇编来设置堆栈指针。你可以创建一个没有序言和尾声的裸函数,并手动完成操作。-1因为那不是正确的(如果修复了,我会更正)。 - user541686
@Jason:啊,糟糕。我不知道__setReg在x86上不起作用。如果有人编辑过后,我会点赞的。抱歉。 - user541686
2
@Mehrdad,但那不是标准的C语言。如果你有特殊的编译器功能可以欺骗它,那就是另一回事了。我坚持我的答案。 - Carl Norum
@Jason:非常感谢,非常有趣!我也通过Mat的帖子找到了这个关于OSDev的教程(http://wiki.osdev.org/Real_mode_assembly_bare_bones)。 - Kerrek SB
显示剩余6条评论

6
假设这是针对x86的,如果你有正确的编译器选项并成功获取引导扇区的正确布局和正确的链接器魔法,你可能可以在16位模式下运行某些内容。但是,使用普通的C(或C ++)无法完成太多事情:您需要快速屏蔽中断,并且没有C函数可用于此。需要使用汇编语言。这对于大多数其他体系结构也可能是相同的:C和C ++根本没有这些内置功能(一些编译器扩展可能会有所帮助)。关于你正在尝试做的事情,一个很好的资源是:OSDev

3

假设是x86保护模式:

我认为答案是不可以,因为你需要像这样做才能切换到保护模式:

    lgdt[GDTR]
    jmp CODESEL:FLUSH
FLUSH:
    ...

我认为在纯C/C++中没有办法执行jmp指令,但我可能不正确。(我绝不是专家;我只是参考了一段时间前制作的一个引导加载程序。)


1
谁说他想要一个现代的、受保护模式的操作系统?(或者说他是在x86上?) - Mat
1
我只需要一些简单的东西。这将是一个类似DOS的系统,用于我在高中新生课程中学习。 - Joshua Vega
3
@Mat: 我不知道...因为当我听到马蹄声时,我想到的是马,而不是斑马。... 新高一印象深刻 - user541686
1
@Mehrdad,是的,我是在家上学的,我决定学习计算机科学和操作系统理论或者叫什么名字。 - Joshua Vega
@Josh:+1,我现在上大学,而且好像连修这门课都不是必须的! - user541686

3
不,在“纯” C 中不可能,至少在 x86...除了一个 x86 机器将引导到 16 位实模式(需要编译器生成 16 位而不是 32 位代码)的事实之外,您还需要能够屏蔽中断、设置段寄存器、从硬件设备(即磁盘)加载代码到内存、设置堆栈、访问 I/O 端口等,所有这些都需要访问 CPU 寄存器和/或特定的汇编命令,在 x86 和其他平台上都需要。
其次,对于 C++,如果您决定定义任何类,则需要某种手动配置和运行的“构造函数”,以便设置内存,以便您的初始类实际上可以存在于内存中......您也无法抛出任何异常。 基本上,您将尝试使用的任何 C++ 特定功能都将无用,因为这些更高级别的数据抽象需要操作系统运行时本身的适当支持,这必须使用汇编和 C 代码的组合进行设置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接