我想知道我是否可以使用C
编程语言编写一个可执行的程序,尽管不使用单个库调用,例如甚至没有退出()?
如果是这样,显然它根本不依赖于库(libc,ld-linux)。
答案 0 :(得分:7)
我怀疑你可以编写这样的东西,但最后需要有一个无限循环,因为你不能要求操作系统退出你的进程。你无法做任何有用的事情。
首先编译ELF程序,查看ELF规范并将程序段,程序段和程序所需的其他部分组合在一起。内核会加载你的代码并跳转到一些初始地址。你可以在那里放置无限循环。但是,如果不知道一些汇编程序,那么从一开始就无望。
glibc使用的start.S
文件可能有用作起点。尝试更改它,以便您可以从中组装独立的可执行文件。那个start.S文件是所有ELF应用程序的入口点,并且是调用__libc_start_main
的文件,后者又调用main
。您只需更改它以满足您的需求。
好的,这是理论上的。但是现在,它有什么实际用途?
好。有一个名为libgloss
的库,它为要在嵌入式系统上运行的程序提供最小的接口。 newlib
C库使用该库作为其系统调用接口。一般的想法是libgloss是C库和操作系统之间的层。因此,它还包含操作系统跳转到的启动文件。这两个库都是GNU binutils项目的一部分。我已经用它们为另一个操作系统和另一个处理器做了接口,但似乎没有Linux的libgloss端口,所以如果你打电话给系统调用,你必须自己做,就像其他已经说过的那样
绝对可以用C编程语言编写程序。 linux内核就是这样一个程序的一个很好的例子。但也可以使用用户程序。但最低要求的是运行时库(如果你想做任何严肃的事情)。这样的包含真正的基本功能,如memcpy,基本宏等。 C标准具有一种称为独立的特殊一致性模式,它只需要一组非常有限的功能,也适用于内核。实际上,我对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;
}
我们很高兴,它实际上编译并运行:)
答案 1 :(得分:5)
是的,但是您可能需要进行系统调用并手动设置输入点。
带入口点的最小程序示例:
.globl _start
.text
_start:
xorl %eax,%eax
incl %eax
movb $42, %bl
int $0x80
或者在普通C(没有出口):
void __attribute__((noreturn)) _start() {
while(1);
}
编译:
gcc -nostdlib -o example example.s
gcc -nostdlib -o example example.c
答案 2 :(得分:2)
纯C?正如其他人所说,你仍然需要一种方法来进行系统调用,所以你可能需要下拉到内联asm。也就是说,如果使用gcc检查出来的话,那就更好了。
答案 3 :(得分:2)
您需要一种方法来阻止C编译器生成依赖于libc的代码,而gcc
可以使用-fno-hosted
来完成。并且您需要一个汇编语言例程来实现syscall(2)
。如果你能得到合适的OS doco,他们就不难写。之后,你将参加比赛。
答案 4 :(得分:0)
是的,你可以,但这很棘手。
基本上没有必要。
您可以静态链接程序,但随后C ++库的相应部分包含在其二进制文件中(因此它没有任何依赖项)。
您可以完全不使用C库,在这种情况下,您需要使用适当的低级接口进行系统调用,该接口取决于体系结构,而不一定是int 0x80。
如果您的目标是制作一个非常小的自包含二进制文件,那么对于像uclibc这样的东西,静态链接可能会更好。
答案 5 :(得分:-1)
好吧,你需要使用一些系统调用将所有信息加载到内存中,所以我对此表示怀疑。
你几乎必须使用exit(),因为Linux的工作方式。