seg*_*a01 10 linux gcc posix libc elf
我有兴趣使用Linux头文件提供的unistd.h构建一个没有(g)libc的静态ELF程序.
我已经阅读了这些文章/问题,这些文章/问题粗略地概括了我正在尝试做什么,但并不完全:http: //www.muppetlabs.com/~breadbox/software/tiny/teensy.html
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
Run Code Online (Sandbox Code Playgroud)
在此之前,我必须根据内核头文件中的值手动定义SEEK_END和SEEK_SET.否则就会错误地说那些没有定义,这是有道理的.
我想我需要链接到未经剥离的vmlinux来提供符号来使用.然而,我读完了符号,虽然有很多llseeks,但他们并不是llseek逐字逐句.
所以我的问题可以在几个方面:
如何指定ELF文件以使用符号?而且我猜测是否/如何可能,符号将不匹配.如果这是正确的,是否有一个现有的头文件将重新定义llseek和default_llseek或内核中的任何内容?
有没有更好的方法在没有libc的情况下在C语言中编写Posix代码?
我的目标是使用(可能只是)unistd.h编写或移植相当标准的C代码,并在没有libc的情况下调用它.我可能没有一些unistd函数,并且不确定哪些"纯粹"存在于内核调用或不存在.我喜欢集会,但这不是我的目标.希望保持尽可能严格的C(如果必须的话,我可以使用一些外部汇编文件),以便在某些时候允许无libc的静态系统.
谢谢你的阅读!
如果您希望用C语言编写POSIX代码,那么放弃libc将没有用处.虽然您可以syscall在汇编程序中实现一个函数,并从内核头文件中复制结构和定义,但您实际上是在编写自己的libc,几乎可以肯定它不符合POSIX.有了所有优秀的libc实现,几乎没有理由开始实现自己的.
dietlibc和musl libc都是节俭的libc实现,可以产生令人印象深刻的小二进制文件链接器通常很聪明; 只要编写一个库以避免意外地引入大量依赖项,只有您使用的函数才会实际链接到您的程序中.
这是一个简单的hello world程序:
#include<unistd.h>
int main(){
char str[] = "Hello, World!\n";
write(1, str, sizeof str - 1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
用低于3K的二进制文件编译它
$ musl-gcc -Os -static hello.c
$ strip a.out
$ wc -c a.out
2800 a.out
Run Code Online (Sandbox Code Playgroud)
dietlibc生成一个甚至更小的二进制文件,小于1.5K:
$ diet -Os gcc hello.c
$ strip a.out
$ wc -c a.out
1360 a.out
Run Code Online (Sandbox Code Playgroud)
这远非理想,但一点点 (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);
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
这样做的要点并不是要表明占用大量空间的不是 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
Run Code Online (Sandbox Code Playgroud)
请注意,我已经更改了哪个寄存器包含什么内容,以便它与 main 匹配 - 我在之前的代码中将它们的顺序稍微错误了。
| 归档时间: |
|
| 查看次数: |
2428 次 |
| 最近记录: |