【问题标题】:LINUX: Is it possible to write a working program that does not rely on the libc library?LINUX:是否可以编写不依赖 libc 库的工作程序?
【发布时间】:2010-10-06 04:57:00
【问题描述】:

我想知道我是否可以用C-programming 语言编写一个可执行的程序,尽管不使用单个库调用,例如甚至没有 exit()?

如果是这样,它显然根本不依赖库(libc、ld-linux)。

【问题讨论】:

  • 您能否将标题从功能更改为工作,以避免与不可变编程范式混淆?

标签: c linux binary dependencies executable


【解决方案1】:

嗯,你需要使用一些系统调用来将所有信息加载到内存中,所以我对此表示怀疑。

而且您几乎必须使用 exit(),只是因为 Linux 的工作方式。

【讨论】:

  • 您不需要使用_exit。您可以使用一些信号终止程序(例如,取消引用 NULL 指针;这将给出一个 SIGSEGV,默认情况下使用 core 转储终止程序)
【解决方案2】:

是的,这是可能的,但是您必须进行系统调用并手动设置您的入口点。

带有入口点的最小程序示例:

.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

【讨论】:

  • 非常好的答案。严格意义上来说,它是正确的答案,因为它是没有内联汇编的纯 C。我选择了 litb 的答案作为正确答案,因为它还解释了如何执行系统调用。谢谢你,跳棋。
【解决方案3】:

我怀疑你可以写这样的东西,但它需要在最后有一个无限循环,因为你不能要求操作系统退出你的进程。你不能做任何有用的事情。

从编译 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 标准有一种特殊的一致性模式,称为 freestanding,它只需要非常有限的功能集,也适用于内核。实际上,我对 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;
}

我们很高兴,它实际上可以编译并运行 :)

【讨论】:

  • 我修改了我的问题,因为它具有误导性。我不想编写一个没有系统调用的程序,而是一个不需要链接到 c 库的程序。
【解决方案4】:

在纯 C 中?正如其他人所说,您仍然需要一种进行系统调用的方法,因此您可能需要为此下拉到内联 asm。也就是说,如果使用 gcc,请查看 -ffreestanding。

【讨论】:

    【解决方案5】:

    您需要一种方法来防止 C 编译器生成依赖于 libc 的代码,使用 gcc 可以使用 -fno-hosted 完成。你需要一个汇编语言例程来实现syscall(2)。如果你能得到合适的 OS doco,它们并不难写。之后你就可以参加比赛了。

    【讨论】:

      【解决方案6】:

      是的,你可以,但这很棘手。

      基本上没有任何意义。

      您可以静态链接一个程序,但相应的 C 库部分会包含在其二进制文件中(因此它没有任何依赖项)。

      您完全可以不使用 C 库,在这种情况下,您需要使用适当的低级接口进行系统调用,这取决于架构,不一定是 int 0x80。

      如果您的目标是制作一个非常小的自包含二进制文件,那么您最好对 uclibc 之类的东西进行静态链接。

      【讨论】:

        猜你喜欢
        • 2017-11-07
        • 1970-01-01
        • 1970-01-01
        • 2011-10-27
        • 1970-01-01
        • 2016-10-25
        • 2017-12-10
        • 2019-10-10
        • 2021-08-11
        相关资源
        最近更新 更多