繁体   English   中英

使用Linux标头中的unistd.h构建不带libc的静态ELF

[英]Build static ELF without libc using unistd.h from Linux headers

我有兴趣使用Linux标头提供的unistd.h构建不带(g)libc的静态ELF程序。

我已经阅读了这些文章/问题,这些文章/问题给出了我要尝试做的一个大概的想法,但并不太清楚: http : //www.muppetlabs.com/~breadbox/software/tiny/teensy.html

不使用libc进行编译

https://blogs.oracle.com/ksplice/entry/hello_from_a_libc_free

我有只依赖于unistd.h的基本代码,我的理解是,每个功能都是由内核提供的,不需要libc。 这是我最有前途的选择:

    $ gcc -I /usr/include/asm/ -nostdlib grabbytes.c -o grabbytesstatic
    /usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400144
    /tmp/ccn1mSkn.o: In function `main':
    grabbytes.c:(.text+0x38): undefined reference to `open'
    grabbytes.c:(.text+0x64): undefined reference to `lseek'
    grabbytes.c:(.text+0x8f): undefined reference to `lseek'
    grabbytes.c:(.text+0xaa): undefined reference to `read'
    grabbytes.c:(.text+0xc5): undefined reference to `write'
    grabbytes.c:(.text+0xe0): undefined reference to `read'
    collect2: error: ld returned 1 exit status

在此之前,我必须根据内核头文件中的值手动定义SEEK_END和SEEK_SET。 否则,错误地指出未定义它们,这是有道理的。

我想我需要链接到未剥离的vmlinux中以提供要使用的符号。 但是,我通读了这些符号,尽管有很多llseeks,但它们并不是逐字记录的。

所以我的问题可以向几个方向发展:

如何指定ELF文件来利用符号? 我正在猜测是否/如何可能,这些符号将不匹配。 如果正确的话,是否存在一个现有的头文件,它将重新定义llseek和default_llseek或内核中完全相同的东西?

没有libc,有没有更好的方法用C编写Posix代码?

我的目标是使用unistd.h(也许仅)编写或移植相当标准的C代码,并在没有libc的情况下调用它。 如果没有一些unisted函数,我可能还可以,并且不确定哪些内核可以“纯粹地”存在。 我喜欢组装,但这不是我的目标。 希望尽可能严格地保留C(如果需要的话,我可以使用一些外部汇编文件),以便在某个时候允许使用无libc的静态系统。

感谢您的阅读!

如果您希望用C编写POSIX代码,那么放弃libc将无济于事。 尽管您可以在汇编器中实现syscall函数,并从内核头文件复制结构和定义,但实际上您将在编写自己的libc,几乎可以肯定它不符合POSIX。 有了所有出色的libc实现,几乎没有理由开始实现自己的实现。

Dietlibcmusl libc都是节俭的libc实现,它们产生的二进制文件非常小。 只要编写了一个库来避免意外引入大量依赖关系,实际上只有您使用的功能才会链接到您的程序中。

这是一个简单的hello world程序:

#include<unistd.h>

int main(){
    char str[] = "Hello, World!\n";
    write(1, str, sizeof str - 1);
    return 0;
}

使用下面的musl编译它会生成小于3K的二进制文件

$ musl-gcc -Os -static hello.c
$ strip a.out 
$ wc -c a.out
2800 a.out

Dietlibc生成的二进制文件甚至更小,小于1.5K:

$ diet -Os gcc hello.c
$ strip a.out 
$ wc -c a.out
1360 a.out

这远非理想,但是(x86_64)汇编程序的一小部分使我不足5KB(但其中大多数是“代码以外的其他东西”)-实际代码在1KB以下(精确到771个字节),但是文件大小要大得多,我认为是因为代码大小四舍五入为4KB,然后在其中添加了一些页眉/页脚/其他内容]

这是我所做的:gcc -g -static -nostdlib -o glibc start.s glibc.c -Os -lc

glibc.c包含:

#include <unistd.h>

int main()
{
    const char str[] = "Hello, World!\n";
    write(1, str, sizeof(str));

    _exit(0);
}

start.s包含:

    .globl _start
_start: 
    xor %ebp, %ebp
    mov %rdx, %r9
    mov %rsp, %rdx
    and $~16, %rsp
    push    $0
    push    %rsp

    call    main

    hlt


    .globl _exit
_exit:
    //  We known %RDI already has the exit code... 
    mov $0x3c, %eax
    syscall
    hlt

这样做的主要目的不是要表明不是glibc的系统调用部分占用了大量空间,而是“准备事情”-请注意,如果要调用例如printf,甚至可能是(v) sprintf或exit()或任何其他“标准库”函数,您将处于“没人知道会发生什么”的境地。

编辑:更新了“ start.s”以将argc / argv放在正确的位置:

_start: 
    xor %ebp, %ebp
    mov %rdx, %r9
    pop     %rdi
    mov %rsp, %rsi
    and $~16, %rsp
    push    %rax
    push    %rsp

    // %rdi = argc, %rsi=argv
    call    main

请注意,我已经更改了哪个寄存器包含什么东西,以便它与main匹配-在上一代码中,它们的顺序有些错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM