🧠 Что такое libc и зачем от неё отказываться
libc — это стандартная библиотека языка C, реализующая функции вроде printf
, malloc
, memcpy
, exit
. Почти все программы на C её используют, независимо от платформы: glibc, musl, uClibc, newlib.
Но libc — это не язык. Это интерфейс между программой и OS, часто скрывающий детали вызовов ядра, аллокации и выхода. В системном программировании, bootloader, kernel, initrd, freestanding‑окружениях и embedded‑средах часто требуется отказаться от libc.
⚙️ Что нужно реализовать вручную
Точка входа
Libc определяет main()
, но реально выполнение начинается с _start
. Без libc программист должен определить _start
и завершать программу напрямую через exit syscall
или вызов hlt
.
__attribute__((naked, section(".text")))
void _start() {
asm volatile (
"mov $60, %rax\n" // syscall: exit
"xor %rdi, %rdi\n" // status: 0
"syscall\n"
);
}
Ввод/вывод
Функции printf
, puts
, read
, write
зависят от syscall и libc. Без неё — только системные вызовы напрямую:
long write(int fd, const void* buf, unsigned long len) {
long ret;
asm volatile (
"mov $1, %%rax\n" // syscall: write
"syscall"
: "=a"(ret)
: "D"(fd), "S"(buf), "d"(len)
: "rcx", "r11", "memory"
);
return ret;
}
Аллокация
Без malloc
/free
нет автоматического управления памятью. Возможные решения:
-
Предварительное выделение из
.bss
-
Ручная реализация bump allocator
✅ Преимущества
-
Полный контроль над кодом, памятью, входом и выходом
-
Снижение размера бинарника до килобайт
-
Возможность писать независимые ELF‑файлы, init‑утилиты, загрузчики
-
Упрощение статического анализа и аудита
❌ Недостатки
-
Потеря удобства: нет форматирования строк, динамической аллокации,
errno
-
Нужно самому учитывать ABI, соглашения о вызовах, выравнивание
-
Вся совместимость с C‑библиотеками теряется
-
Сложность портирования и отладки
⚙️ Где используется
-
OSDev, написание ядра и утилит начальной загрузки
-
Bare metal программирование и RTOS
-
Разработка эксплойтов, шеллкодов, минимальных загрузчиков
-
Написание
init
,stage1
,freestanding
программ
🔗 Вывод
Libc — мощный, но тяжёлый слой над ядром. Писать без него — значит взять ответственность за весь путь от точки входа до системных вызовов. Это трудно, но даёт редкий уровень контроля, особенно важный в низкоуровневой и безопасной разработке.