簡體   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