Создайте статический ELF без libc с помощью unistd.h из заголовков Linux

Я заинтересован в создании статической программы ELF без (g)libc, используя unistd.h предоставляется заголовками Linux.

Я прочитал эти статьи/Вопрос, которые дают примерное представление о том, что я пытаюсь сделать, но не совсем: http://www.muppetlabs.com / ~breadbox/software/tiny/teensy.html

компиляция без libc

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

I есть базовый код, который зависит только от запустите.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 в соответствии со значениями, найденными в заголовках ядра. Иначе было бы ошибкой говорить, что они не были определены, что имеет смысл.

Я полагаю, что мне нужно связать в unstripped vmlinux для предоставления символов для использования. Тем не менее, я прочитал символы, и хотя было много llseek, они не были llseek дословно.

Так что мой вопрос может идти в нескольких направлениях:

Как я могу указать файл ELF для использования символов? И я предполагаю, что если/как это возможно, символы не будут совпадать. Если это правильно, есть ли существующий файл заголовка, который переопределит llseek и default_llseek или что-то точно в ядро?

есть ли лучший способ написать код Posix в C без libc?

моя цель-написать или портировать довольно стандартный код C, используя (возможно, исключительно) unistd.h и вызовите его без libc. Я, вероятно, в порядке без нескольких функций unistd и не уверен, какие из них существуют "чисто" как вызовы ядра или нет. Я люблю собрания, но это не моя цель. Надеясь остаться как можно более строгим C (я в порядке с несколькими внешними файлами сборки, если мне нужно), чтобы разрешить файл libc-менее статическая система в какой-то момент.

Спасибо за прочтение!

2 ответов


это далеко не идеально, но немного (x86_64) ассемблер имеет меня чуть ниже 5KB (но большая часть этого - "другие вещи, чем код" - фактический код находится под 1KB [771 байт, чтобы быть точным], но размер файла намного больше, я думаю, потому что размер кода округлен до 4KB, а затем к этому добавляется некоторый верхний/нижний колонтитул/дополнительный материал]

вот что я сделал: gcc-g-static-nostdlib-o запуск glibc.s glibc.c-Os-lc

glibc.с содержит:

#include <unistd.h>

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

    _exit(0);
}

начать.s содержит:

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

    call    main

    hlt


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

этот главный момент заключается не в том, чтобы показать, что это не часть системного вызова glibc, которая занимает много места, а "подготовительные вещи" - и будьте осторожны, что если вы вызовете, например, printf, возможно, даже (v)sprintf, или exit (), или любую другую функцию "стандартной библиотеки", вы находитесь в стране "никто не знает, что произойдет".

Edit: Обновлено " 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 - у меня был немного неправильный порядок в предыдущем коде.


если вы хотите написать код POSIX в C, отказ от 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;
}

компиляция его с musl ниже yeilds двоичный менее 3K

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

dietlibc производит еще меньший двоичный файл, менее 1,5 к:

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