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

Я хочу скомпилировать свой C-код без (g)libc. Как его отключить и какие функции от него зависят?

Я попробовал -nostdlib, но это не помогает: код компилируется и работает, но я все еще могу найти имя libc в шестнадцатеричном дампе моего исполняемого файла.


person u149796    schedule 30.03.2010    source источник
comment
-nostdlib должен это сделать, какую версию платформы/компилятора вы используете?   -  person Carl Norum    schedule 31.03.2010
comment
не помогает, так как это не отключило библиотеку, или вы не могли ничего скомпилировать с этим флагом?   -  person Josh Lee    schedule 31.03.2010
comment
Вы, вероятно, также хотите -nostartupfiles.   -  person    schedule 31.03.2010
comment
blog.ksplice.com/2010/03/libc-free-world имеет очень хорошее описание точного управления программным выводом gcc. Редактировать: они (ksplice) только что выпустили часть 2 вышеупомянутого руководства/руководства. См. здесь: blog.ksplice.com/2010/04/libc -free-world-2 В основном это относится к настройкам компоновщика для удаления лишнего мусора из файлов.   -  person Adam Shiemke    schedule 31.03.2010
comment
Опция -nostartupfiles отсутствует. Вы, вероятно, имеете в виду -nostartfiles, что уже подразумевается -nostdlib.   -  person ataylor    schedule 31.03.2010
comment
Вот отличная статья об оптимизации двоичных файлов elf, в которой рассказывается об удалении исполняемых файлов из stdlib: [Учебник Whirlwind по созданию действительно крошечных исполняемых файлов ELF для Linux](muppetlabs.com/~breadbox/software/tiny/teensy.html)   -  person Michał Trybus    schedule 04.04.2010


Ответы (2)


Если вы скомпилируете свой код с помощью -nostdlib, вы не сможете вызывать какие-либо функции библиотеки C (конечно), но вы также не получите обычный загрузочный код C. В частности, реальной точкой входа программы в Linux является не main(), а функция с именем _start(). Стандартные библиотеки обычно предоставляют такую ​​версию, которая запускает некоторый код инициализации, а затем вызывает main().

Попробуйте скомпилировать это с помощью gcc -nostdlib -m32:

void _start() {

    /* main body of program: call main(), etc */

    /* exit system call */
    asm("movl $1,%eax;"
        "xorl %ebx,%ebx;"
        "int  $0x80"
    );
}

Функция _start() всегда должна заканчиваться вызовом exit (или другим системным вызовом без возврата, например exec). В приведенном выше примере системный вызов вызывается непосредственно со встроенной сборкой, поскольку обычный exit() недоступен.

person ataylor    schedule 30.03.2010
comment
Для 64-битной версии ассемблерный код должен выглядеть так: asm("mov rax,60; mov rdi,0; syscall"). - person sigalor; 03.05.2016
comment
Добавляя к комментарию @sigalor, для компиляции с gcc вам нужно будет использовать синтаксис AT&T, поэтому это должно выглядеть следующим образом: asm(mov $60,%rax; mov $0,%rdi; syscall) - person lanoxx; 11.05.2016
comment
@ataylor: почему функция _start() всегда должна заканчиваться вызовом exit()? Что, если я не напишу exit() в функции start()? - person Destructor; 25.05.2017
comment
@Destructor в моей системе зависает на несколько секунд, а затем segfaults. - person Moonchild; 16.08.2017
comment
По моему опыту, вам нужно комбинировать -nostdlib с -nostartfiles (сейчас?), чтобы получить волшебный урезанный опыт без основного. - person SilverbackNet; 01.08.2018
comment
@Destructor, из кода дизассемблирования от objdump -dS exe_file, 080480d8 <_start>: .globl _start _start:call main 80480d8: e8 00 00 00 00 call 80480dd <main> 080480dd <main>: int main() { 80480dd: 55 push %ebp ..... 80480ef: c9 leave 80480f0: c3 ret . После call следующая операция — 80480dd. Если вы не хотите exit(), придумайте другую идею, чтобы пропустить 80480dd~80480f0. _exit() делает - person gfan; 02.10.2018
comment
Чтобы вызвать эту функцию как угодно, кроме _start, используйте параметр компоновщика -e для установки точки входа: gcc -nostdlib -Wl,-efunction1 - person Ivan; 14.02.2020
comment
Обратите внимание, что стек выравнивается по-разному при входе в _start: в стеке нет адреса возврата, поэтому он по-прежнему выровнен по 16 байтам. Но GCC будет считать это нормальной функцией, поэтому будет нарушать ABI при вызове других функций. Используйте -mincoming-stack-boundary=2 для этого файла (docs), чтобы сообщить GCC, что стек может быть выровнено только по 4 байтам при входе в функцию или, может быть, __attribte__((target("something"))) только по _start - person Peter Cordes; 02.06.2020

Самый простой способ — скомпилировать код C в объектные файлы (gcc -c, чтобы получить несколько *.o файлов), а затем напрямую связать их с компоновщиком (ld). Вам придется связать ваши объектные файлы с несколькими дополнительными объектными файлами, такими как /usr/lib/crt1.o, чтобы получить работающий исполняемый файл (между точкой входа, видимой ядром, и функцией main() есть немного работы) . Чтобы узнать, с чем связываться, попробуйте связать glibc, используя gcc -v: это должно показать вам, что обычно входит в исполняемый файл.

Вы обнаружите, что gcc генерирует код, который может иметь некоторые зависимости от нескольких скрытых функций. Большинство из них находятся в libgcc.a. Также могут быть скрытые вызовы memcpy(), memmove(), memset() и memcmp(), которые находятся в libc, поэтому вам, возможно, придется предоставить свои собственные версии (что несложно, по крайней мере, если вы не слишком требовательны к производительности).

Временами все может проясняться, если вы посмотрите на созданную сборку (используйте флаг -S).

person Thomas Pornin    schedule 30.03.2010
comment
Мне приходится использовать _start вместо main, но когда я пытаюсь вызвать функцию libc, gcc не жалуется. Исчезает ли ссылка libc, если я удаляю все вызовы libc? - person u149796; 01.04.2010
comment
Не напрямую. Если вы попробуете gcc -v, вы увидите, что gcc передает компоновщику некоторые объектные файлы (*.o). Компоновщик включает все предоставленные ему объектные файлы. Исчезновение происходит только с библиотеками (*.a), потому что они являются репозиториями объектных файлов, которые компоновщик может использовать или не использовать. - person Thomas Pornin; 01.04.2010