7

所以我在 Visual Studio 中使用我的 C++ 程序进行编译,它会输出一个漂亮的小 EXE 文件。但是 EXE 只能在 Windows 上运行,而且我听到很多关于 C/C++ 如何编译成直接在处理器上运行的汇编语言的信息。EXE 在 windows 的帮助下运行,或者我可以有一个程序来生成在 mac 上运行的可执行文件。但是我不是将 C++ 代码编译成特定于处理器的汇编语言吗?

我的见解:

  1. 我猜我可能不是。我知道有一个英特尔 C++ 编译器,那么它会生成特定于处理器的汇编代码吗?EXE 在 Windows 上运行,因此它们可以利用大量已经设置好的东西,从图形包到庞大的 .NET 框架。特定于处理器的可执行文件实际上是从头开始,只有处理器的指令集。

  2. 这个可执行文件会是文件类型吗?我们可以运行 Windows 并打开它,但是控制只能切换到处理器?我假设这个可执行文件类似于操作系统,因为它必须在启动其他任何东西之前运行,并且只有处理器指令集“使用”。

4

10 回答 10

18

让我们想想“跑步”是什么意思……

有些东西必须将二进制代码加载到内存中。这是一个操作系统功能。.EXE 或二进制可执行文件或包或其他文件以非常特定于操作系统的方式格式化,以便操作系统可以将其加载到内存中。

必须将控制权交给那些二进制代码。又是操作系统。

I/O 例程(在 C++ 中,但在大多数地方都是如此)只是一个封装 OS API 的库。Drat那个操作系统,它无处不在。

回忆

在过去(是的,我这么老了)我在没有操作系统的机器上工作。我们也没有 C.

我们使用“汇编器”和“链接器”等工具编写机器代码,以创建可以加载到机器中的大二进制图像。我们不得不通过一个痛苦的引导过程来加载这些二进制图像。

我们会使用前面板按键将足够的代码加载到内存中,以读取诸如打孔纸带阅读器之类的方便设备。这将加载一小块相当标准的引导链接加载程序软件。(我们使用聚酯薄膜胶带,所以它不会磨损。)

然后,当我们在内存中拥有这个链接加载器时,我们可以将我们之前准备好的磁带传送给汇编器。

我们编写了自己的设备驱动程序。或者我们使用源代码形式的库例程,在纸带上打孔。

“补丁”实际上是修补的纸带。另外,由于还有一些小错误,我们必须根据手写指令调整内存映像——没有放入磁带的补丁。

后来,我们有了简单的操作系统,它有简单的 API、简单的设备驱动程序和一些实用程序,如“文件系统”、“编辑器”和“编译器”。这是针对一种叫做 Jovial 的语言,但我们有时也使用 Fortran。

我们必须焊接串行接口板才能插入设备。我们必须编写设备驱动程序。

底线

您可以轻松编写不需要操作系统的 C++ 程序。

  1. 了解作为处理器芯片组一部分的硬件 BIOS(或类似 BIOS)设施。大多数现代硬件都有一个连接到 ROM 的简单操作系统,它执行开机自检 (POST)、加载一些简单的驱动程序并定位引导块。

  2. 了解如何编写自己的引导块。这是 POST 之后加载的第一个正确的“软件”内容。这并不是那么难。您可以使用各种分区工具将引导块程序强制到磁盘上,这样您就可以完全控制硬件。没有操作系统。

  3. 了解 GRUB、LILO 或 BootCamp 如何启动操作系统。这并不复杂。一旦它们启动,它们就可以加载您的程序,然后您就可以开始运行了。这稍微简单一些,因为您创建了引导加载程序想要加载的分区类型。将你的基于 Linux 内核,你会更快乐。不要试图弄清楚 Windows 是如何启动的——这太复杂了。

  4. 阅读 ELF。 http://en.wikipedia.org/wiki/Executable_and_Linkable_Format

  5. 了解如何编写设备驱动程序。如果您不使用操作系统,则需要编写设备驱动程序。

于 2009-08-28T02:07:19.907 回答
6

问题是操作系统确实在启动您的程序方面做了很多工作。EXE 文件本身具有 Windows 识别的标题信息,将其标识为 EXE 文件。您的应用程序通过操作系统完成所有工作,从文件系统访问到内存分配。

但是,是的,您可以在其他平台上运行为 Windows/intel 编译的应用程序而无需仿真。如果您想在 Mac 或 UNIX 上运行您的 EXE,您将需要安装更多的软件来完成 Windows 运行程序所需的工作——看看“Wine”项目。

于 2009-08-28T01:53:40.420 回答
6

您所说的是嵌入式世界中所谓的“裸机”应用程序。它们对于诸如 ARM Cortex-M3 之类的东西非常常见,它放入(比如)借记卡验证器盒或交互式玩具中,并且没有足够的内存或能力来运行完整的操作系统。因此,您无需使用“ARM/Linux”编译器来编译应用程序以在 ARM 处理器上运行在 Linux 上,而是获得“ARM 裸机”编译器,该编译器可以在没有操作系统的情况下编译运行在 ARM 处理器上的东西。(我使用 ARM 而不是 x86 作为示例,因为 x86 裸机应用程序现在非常罕见。)

正如您的问题和其他答案中所述,您的应用程序将需要执行一些原本由操作系统处理的事情。

首先,它需要初始化内存系统、中断向量和其他各种 board goo 位。通常这是裸机编译器会为您做的事情,但如果您有一块奇怪的板,您可能需要告诉它如何做到这一点。这会从板子打开的位置到您的 main() 函数开始的位置。

然后,您需要与 CPU 和 RAM 之外的事物进行交互。操作系统包括执行此操作的各种功能——磁盘 I/O、屏幕输出、键盘和鼠标输入、网络等,等等。如果没有操作系统,您必须从其他地方获取它。您可以从硬件制造商的库中获得其中的一些;例如,我最近玩的一块板子有一个 40x200 像素的 LED 屏幕,它带有一个库,其中包含打开它并在其上设置单个像素值的代码。并且有几家公司出售库来实现 TCP/IP 堆栈之类的东西,用于网络或诸如此类的东西。

例如,考虑一下这使得即使是基本的 printf 也变得困难。当你有一个操作系统时, printf 只是向操作系统发送一条消息,说“把这个字符串放在控制台上”,操作系统会在控制台上找到当前光标位置,并做所有的事情来找出什么像素在屏幕上进行更改,以及使用什么 CPU 指令来更改这些像素,以便做到这一点。

哦,我们有没有提到您首先必须弄清楚如何将程序放入 CPU 中?典型的计算机具有一些可编程 ROM,它将在启动时加载指令。在 x86 上,这是 BIOS,它通常已经包含一个方便的程序,可以启动 CPU、设置显示、查找磁盘以及从找到的磁盘加载程序。在嵌入式系统上,这通常是您的程序所在的位置——这意味着您需要某种方式将您的程序放在那里。通常,这意味着您有一个称为“调试器”的设备,它物理地连接到您的嵌入式板以加载程序 - 并且还可以执行允许您暂停处理器并确定其状态的事情,这样您就可以单步执行您的程序,就像您在计算机上的软件调试器中运行它一样。但我离题了。

无论如何,要回答你的第二个问题,你要创建的这个可执行文件是存储在嵌入式板上的那个 ROM 中的东西——或者你可能只是将它的一部分存储在 ROM 中(毕竟,这很漂亮小)并将其余部分存储在闪存驱动器上,ROM中的位将包含将其余部分从闪存驱动器中取出的指令。它可能会作为文件存储在您的主计算机(即您正在创建它的 Linux 或 Windows 计算机)上,但这只是为了存储,它不会在那里运行。

您会注意到,当您将大量此类库放在一起时,它们的功能与操作系统的功能相当,并且在一堆库和真正的操作系统之间存在一定的空间。在那个空间里有所谓的 RTOS——“实时操作系统”。其中较小的实际上只是库的集合,它们一起工作以完成所有操作系统的事情,有时还包括一些东西,这样你就可以一次运行多个线程(然后你可以让不同的线程像不同的程序一样工作) - - 尽管所有这些都被编译到同一个编译的“程序”中,而 RTOS 实际上只不过是您包含的一个库。较大的开始将部分代码存储在不同的地方,而且我认为其中一些甚至可以从磁盘加载代码片段——就像 Windows 和 Linux 在运行程序时所做的那样。这是一个连续统一体,而不是非此即彼的。

FreeRTOS 系统是一个开源的 RTOS,它接近 RTOS 空间的较小端;如果您更感兴趣,它们可能是查看其中一些内容的好地方。他们确实有一些 x86 应用程序的示例,可以让您了解什么样的 x86 系统可以运行裸机或基于 RTOS 的程序,以及如何编译一些东西以在其中运行;链接在这里:http ://www.freertos.org/a00090.html#186 。

于 2009-08-28T02:59:44.003 回答
3

计算机不是 CPU。为了做任何有用的事情,CPU 必须连接到内存和 IO 控制器和其他设备。操作系统负责从正在运行的程序中抽象出所有这些。所以,如果你想编写一个在没有操作系统的情况下运行的程序,你的程序必须至少复制操作系统的一些特性:在引导过程中从 BIOS 接管,初始化设备,与磁盘控制器通信以加载代码和数据,与显示控制器通信以向用户显示信息,与键盘控制器和鼠标控制器通信以读取用户输入等等等。

除非您正在使用专门的硬件构建嵌入式系统,否则这样做是没有意义的。此外,运行您的程序意味着用户将不得不放弃运行其他程序。虽然这对于今天的 ATM 或 1984 年的 WordStar 来说可能是可以接受的,但如今人们因无法在听音乐时查看电子邮件而皱眉。

于 2009-08-28T02:06:41.107 回答
1

当然,它们存在。它们被称为交叉编译器。例如,这就是我可以使用 Xcode 为 iPhone 平台编程的方式。

一种相关类型的编译器是为虚拟平台编译的编译器。这就是 Java 的工作原理

于 2009-08-28T01:56:13.130 回答
1

任何给定的编译器/工具集都会为特定的处理器/操作系统组合生成代码。因此,您的 Visual Studio 编译示例会为 x86/Windows 生成代码。该 .EXE 只能在 x86/Windows 上运行,而不能在(例如)ARM/Windows(某些手机使用)上运行。

要为处理器/操作系统组合生成代码,而不是您正在运行编译器的代码,需要通常称为交叉编译器的代码。如果您拥有完整的专业 Visual Studio 订阅,您可以获得 ARM 交叉编译器,这将允许您生成 ARM/Windows .EXE 文件,这些文件不会在您的台式机上运行,​​但将在基于 ARM/Windows 的手机上运行或掌上电脑。

于 2009-08-28T02:06:14.323 回答
0

是的,您可以制作在处理器的“裸机”上运行的可执行文件。显然,这就是操作系统内核的工作方式。您需要做的主要事情是创建一个不使用任何库的可执行文件。但是,“无库”限制包括 C 标准库!所以这意味着没有 malloc、没有 printf 等。你必须基本上是你自己的操作系统并自己管理内存和 I/O。这将不可避免地需要在某个阶段直接在组装中进行大量工作。

您还失去了其他一些奢侈品,例如 main(),它不能作为程序的起点,因为 main() 是由操作系统和 C 运行时环境调用的东西。

于 2009-08-28T01:58:21.757 回答
0

绝对地!这就是嵌入式编程。正如许多人可能已经说过的那样,操作系统已经为您做了很多事情。即使在没有操作系统的嵌入式世界中,许多开发工具也会提供启动代码以使处理器运行得足以跳转到您的程序。一些/许多提供完整的 C/C++ 库,以便您可以调用 memcpy() 之类的函数,有时甚至可以调用 malloc() 和 printf()。

欢迎您提供每一行代码和每条指令,而不是使用开发工具包,但仍然使用像 gcc 这样的编译器。例如,某些二进制格式对于在诸如 elf 之类的操作系统上运行的格式是通用的。您可以在 Linux 上执行 elf 文件,但也可以让您的嵌入式程序生成 elf 二进制文件。处理器无法以这种格式执行 elf,但在某些情况下,无论启动 prom 或 ram 的任何程序都会从​​ elf 文件中提取二进制程序,这与操作系统从 elf 文件中提取要运行的程序不同。EXE 不是这些文件格式之一。您最喜欢的 Windows 应用程序编译器可能也不是嵌入式编译器,尽管您有时可以使用一个来完成高级语言的工作,然后使用替代的汇编器和链接器。工作量超出通常的价值。例如,您在 C 中编写一个函数(不进行任何库或系统调用),将其编译为一个对象。编写您自己的或查找实用程序以从该对象中提取已编译的二进制文件,将其转换为另一种对象格式或汇编程序(反汇编)。将您的启动代码和其他程序集添加到其中。将所有内容作为嵌入式程序组装和链接在一起。我用微软的嵌入式可视化 C 做过一次,只是为了看看它如何与其他编译器相媲美,这并不可怕,但肯定不值得为获得输出而付出努力。将其转换为另一种对象格式或汇编器(反汇编)。将您的启动代码和其他程序集添加到其中。将所有内容作为嵌入式程序组装和链接在一起。我用微软的嵌入式可视化 C 做过一次,只是为了看看它如何与其他编译器相媲美,这并不可怕,但肯定不值得为获得输出而付出努力。将其转换为另一种对象格式或汇编器(反汇编)。将您的启动代码和其他程序集添加到其中。将所有内容作为嵌入式程序组装和链接在一起。我用微软的嵌入式可视化 C 做过一次,只是为了看看它如何与其他编译器相媲美,这并不可怕,但肯定不值得为获得输出而付出努力。

从计算机中的处理器到手机或微波炉中的处理器,每个处理器都有一些启动代码。该代码未在操作系统上运行。该代码使用与操作系统应用程序使用的相同或相似的编译器。对于某些编码的设备,将处理器和内存以及片上和片外外围设备置于可以启动操作系统的状态。从那里操作系统接管。在您的计算机上,这将是 BIOS,然后是引导加载程序,然后是操作系统、dos、windows、linux 等。

于 2009-08-28T03:39:51.433 回答
0

主要问题是文件格式。PE 与 ELF 非常不同(用于类 unix 系统)。一个有效的 PE 程序不能是一个有效的 ELF。因此,您要么使用不同的启动器动态加载二进制文件,要么必须放弃。

除此之外,通过了解操作系统服务、启动时寄存器的值等,您的代码可能可以轻松可靠地检测您在哪个操作系统下运行并采取相应措施(某些恶意软件就是这样做的)。另一个挑战是重用代码,而不是在同一个二进制文件中拥有两个或多个不同的程序。基本上你必须编写一个模拟器,至少对于你需要的服务。

于 2009-08-28T06:49:18.170 回答
0

不要忘记 Windows 库。查看 QT 和 GTK+

于 2010-12-27T20:49:31.957 回答