我怀疑你可以写这样的东西,但最后需要有一个无限循环,因为你不能要求操作系统退出你的进程。而你却无法做任何有用的事情。
我们从编译 ELF 程序开始,研究 ELF 规范,并将标头、程序段和程序所需的其他部分组合在一起。内核将加载您的代码并跳转到某个初始地址。你可以在那里放置一个无限循环。但如果不了解一些汇编程序,无论如何从一开始就没有希望。
The glibc 使用的文件作为起点可能很有用。尝试更改它,以便您可以从中组装出独立的可执行文件。 start.S 文件是所有 ELF 应用程序的入口点,并且是调用的文件__libc_start_main
这又调用main
。您只需更改它即可满足您的需求。
好吧,那是理论上的。但现在,这有什么实际用途呢?
回答更新的问题
出色地。有一个图书馆叫libgloss
它为要在嵌入式系统上运行的程序提供了最小的接口。这newlib
C 库使用该接口作为其系统调用接口。总体思路是libgloss是C库和操作系统之间的层。因此,它还包含操作系统跳转到的启动文件。这两个库都是 GNU binutils 项目的一部分。我用它们为另一个操作系统和另一个处理器做接口,但似乎没有适用于 Linux 的 libgloss 端口,所以如果你调用系统调用,你将不得不自己做,正如其他人已经说过的那样。
用C编程语言编写程序是完全可能的。这linux内核是此类程序的一个很好的例子。但用户程序也是可能的。但最低限度需要的是一个运行时库(如果你想做任何严肃的事情)。这样的函数将包含非常基本的函数,例如 memcpy、基本宏等等。 C 标准有一个特殊的一致性模式,称为独立式,它只需要一组非常有限的功能,也适用于内核。其实我有no clue关于 x86 汇编程序,但我尝试了一个非常简单的 C 程序:
/* gcc -nostdlib start.c */
int main(int, char**, char**);
void _start(int args)
{
/* we do not care about arguments for main. start.S in
* glibc documents how the kernel passes them though.
*/
int c = main(0,0,0);
/* do the system-call for exit. */
asm("movl %0,%%ebx\n" /* first argument */
"movl $1,%%eax\n" /* syscall 1 */
"int $0x80" /* fire interrupt */
: : "r"(c) :"%eax", "%ebx");
}
int main(int argc, char** argv, char** env) {
/* yeah here we can do some stuff */
return 42;
}
我们很高兴,它实际上可以编译并运行:)