如何创建可执行文件以在某个处理器体系结构(而不​​是某些操作系统)上运行?

时间:2009-08-28 01:47:17

标签: c++ c compilation executable processor

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

我的见解:

  1. 我猜我可能不是。我知道有一个英特尔C ++编译器,所以它会制作特定于处理器的汇编代码吗? EXE在Windows上运行,因此它们已经建立了很多东西,从图形包到大规模的.NET框架。特定于处理器的可执行文件将从头开始,仅使用处理器的指令集。

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

10 个答案:

答案 0 :(得分:17)

让我们考虑“跑”的意思......

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

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

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

<强>忆起

在过去的日子里(是的,我已经老了)我在没有操作系统的机器上工作过。我们也没有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. 了解如何编写设备驱动程序。如果您不使用操作系统,则需要编写设备驱动程序。

答案 1 :(得分:6)

问题在于操作系统确实可以帮助您启动程序。 EXE文件本身具有Windows识别的标题信息,将自身标识为EXE文件。您的应用程序可以完成所有操作,从文件系统访问到内存分配,再到操作系统。

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

答案 2 :(得分:5)

答案 3 :(得分:3)

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

除非您使用专用硬件构建嵌入式系统,否则没有必要这样做。此外,运行程序意味着用户必须放弃运行其他程序。虽然这对于今天的自动取款机或1984年的WordStar来说可能是可以接受的,但是现在人们都不愿意在听音乐的同时检查电子邮件。

答案 4 :(得分:1)

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

相关类型的编译器是为虚拟平台编译的编译器。 That's how Java works

答案 5 :(得分:1)

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

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

答案 6 :(得分:0)

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

你也失去了一些其他的奢侈品,例如main(),它们不能成为程序的起点,因为main()是由OS和C运行时环境调用的东西。

答案 7 :(得分:0)

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

欢迎您提供每行代码和每条指令,而不是使用开发工具包,但仍然使用像gcc这样的编译器。一些二进制格式对于像elf这样的操作系统上运行的那些是常见的。您可以在Linux上执行elf文件,但也可以将嵌入式程序生成为elf二进制文件。处理器不能以该格式执行elf,但是在某些情况下启动prom或ram的任何程序都将从elf文件中提取二进制程序,这与提取程序从elf文件运行的操作系统不同。 EXE不是这些文件格式之一。您最喜欢的Windows应用程序编译器可能不是嵌入式编译器,尽管您有时可以使用一个来执行高级语言,然后使用备用汇编程序和链接器。比平时更多的工作。例如,您在C中编写一个函数(不进行任何库或系统调用),将其编译为对象。编写自己的或找到一个实用程序从该对象中提取已编译的二进制文件,将其转换为另一种对象格式或汇编程序(反汇编)。添加您的启动代码和其他程序集。将所有内容组装并链接在一起作为嵌入式程序。我用Microsofts嵌入式视觉C做了一次,看看它是如何测量到其他编译器的,它并不可怕,但肯定不值得黑客攻击输出。

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

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

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