Создайте статический ELF без libc с помощью unistd.h из заголовков Linux
Я заинтересован в создании статической программы ELF без (g)libc, используя unistd.h предоставляется заголовками Linux.
Я прочитал эти статьи/Вопрос, которые дают примерное представление о том, что я пытаюсь сделать, но не совсем: http://www.muppetlabs.com / ~breadbox/software/tiny/teensy.html
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