我有兴趣在没有(g)libc的情况下使用Linux头文件提供的unistd.h构build一个静态的ELF程序。
我已经阅读了这些文章/问题,这些文章/问题大致介绍了我正在尝试做的事情,但并不完全: http : //www.muppetlabs.com/~breadbox/software/tiny/teensy.html
编译没有libc
https://blogs.oracle.com/ksplice/entry/hello_from_a_libc_free
我有基本的代码只依赖于unistd.h,其中,我的理解是,每个这些function是由内核提供的,而不应该需要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。 否则会错误的说那些没有定义,这是有道理的。
我想我需要链接到一个untripped vmlinux提供符号来利用。 然而,我读了这些符号,虽然有很多希望,但他们并没有逐字。
所以我的问题可以走几个方向:
我怎样才能指定一个ELF文件来利用符号? 而且我猜如果这是可能的,这些符号将不会匹配。 如果这是正确的,是否有一个现有的头文件将重新定义llseek和default_llseek或任何内核中的东西?
有没有更好的方式来编写没有libc的C语言的Posix代码?
我的目标是使用(也许完全)unistd.h编写或移植相当标准的C代码,并在不使用libc的情况下调用它。 我可能没有几个unistd函数没有问题,我不确定哪些是纯粹作为内核调用存在或不存在。 我喜欢集会,但这不是我的目标。 希望尽可能保持C(如果必须的话,我可以使用几个外部程序集文件),以便在某些时候允许使用无libc的静态系统。
感谢您的阅读!
这是非常不理想的,但是有一点(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
请注意,我已经改变了哪个寄存器包含什么东西,所以它匹配主 – 我有他们在前面的代码中有一些错误的顺序。
如果你想用C编写POSIX代码,放弃libc不会有帮助。 尽管你可以在汇编程序中实现一个syscall
函数,并且从内核头文件中复制结构和定义,但实质上是编写自己的libc,这几乎可以肯定不符合POSIX标准。 所有伟大的libc实现在那里,几乎没有理由开始实现自己的。
dietlibc和musl libc都是节俭的libc实现,产生令人印象深刻的小二进制文件。 只要编写了一个库来避免意外地引入大量的依赖关系,那么只有你使用的函数才会被链接到你的程序中。
这是一个简单的hello世界计划:
#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