🧠 Что такое 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

  • Использование sbrk/mmap

  • Ручная реализация bump allocator

✅ Преимущества

  • Полный контроль над кодом, памятью, входом и выходом

  • Снижение размера бинарника до килобайт

  • Возможность писать независимые ELF‑файлы, init‑утилиты, загрузчики

  • Упрощение статического анализа и аудита

❌ Недостатки

  • Потеря удобства: нет форматирования строк, динамической аллокации, errno

  • Нужно самому учитывать ABI, соглашения о вызовах, выравнивание

  • Вся совместимость с C‑библиотеками теряется

  • Сложность портирования и отладки

⚙️ Где используется

  • OSDev, написание ядра и утилит начальной загрузки

  • Bare metal программирование и RTOS

  • Разработка эксплойтов, шеллкодов, минимальных загрузчиков

  • Написание init, stage1, freestanding программ

🔗 Вывод

Libc — мощный, но тяжёлый слой над ядром. Писать без него — значит взять ответственность за весь путь от точки входа до системных вызовов. Это трудно, но даёт редкий уровень контроля, особенно важный в низкоуровневой и безопасной разработке.