waqur: (Евро)
[personal profile] waqur
Можно ли линковать libc статически в UNIX?



Это зависит от типа libc и от типа UNIX, а также от лицензии на ваш код
Сначала разберёмся с основами.

1. Разница между Windows и UNIX в части запуска процесса и загрузки динамических библиотек

В UNIX есть два типа исполняемых файлов: статически скомпонованные и динамически скомпонованные. Статически скомпонованные файлы включают в себя libc и все остальные необходимые библиотеки, при этом вызовы ядра они делают напрямую с помощью процессорных инструкций "int 0x80", "sysenter" или "syscall". Динамически скомпонованные файлы просто вызывают libc и другие библиотеки, если им нужно сделать системный вызов. Они начинают своё выполнение с загрузчика (/lib/ld-linux.so.N или /libexec/ld-elf.so.N), ссылка на который указана в их ELF-заголовке. Ядро передаёт загрузчику исполняемый файл уже в открытом виде, его файловый дескриптор передаётся в auxiliary vector по индексу AT_EXECFD. Работа загрузчика — замапить страницы, распарсить ELF-файл и загрузить данные с диска, задать флаги страниц, повторить всё то же самое для разделяемых библиотек и их зависимостей и передать управление точке входа основного ELF-файла. Загрузчик остаётся в памяти как ещё одна динамическая библиотека, так что его интерфейс доступен для основного исполняемого файла в виде функций dlopen(3), dlfunc(3) и т.д., а если загрузчика не было (статически скомпонованный исполняемый файл), то и функции эти не доступны.

В Windows NT нельзя создать статический исполняемый файл в UNIX-смысле. В памяти процесса всегда будет присутствовать NTDLL.DLL и далее, в зависимости от подсистемы, другие DLL (например для подсистемы Win32 — KERNEL32.DLL). NTDLL получает управление от ядра при создании процесса, она же создаёт первый SEH-фрейм и содержит загрузчик для PE-файлов (группа функций Ldr*).

Конечно, в винде бывают и более высокоуровневые "компиляторные" libc, такие как MSVCRT.DLL (Windows 95 OSR2+), CRTDLL.DLL (ещё древнее, Win32s); MSVCRnm[D].DLL (более новые версии Visual Studio n.m с опциональной отладкой). Однако никто не мешает использовать компилятор фирмы Borland со своими libc, или вообще избавиться от зависимости на них, скомпоновав свой exe-файл статически. Однако от зависимости от KERNEL32.DLL и NTDLL.DLL избавиться никак невозможно, и в общем эти два файла выполняют в винде ту же роль, что и libc в UNIX. В том числе, в винде невозможно избавиться от загрузчика (Ldr*-функции NTDLL.DLL), загрузчик всегда находится в памяти процесса. Клиентские функции загрузчика LoadLibrary и GetProcAddress доступны всегда, если ваше приложение относится к подсистеме Win32. В винде приложения никогда не делают прямых вызовов ядра с помощью процессорных инструкций "int 0x2E", "sysenter" или "syscall", а чтобы эта шальная мысль не пришла в чью-то дурную голову, номера системных вызовов периодически меняются (это последовательные целые числа в алфавитном списке системных вызов NT Native API, в очередной версии винды, сервиспаке или хотфиксе список раздвигается в середине — и номера всех последующих функций инкрементируются). В этом смысле все исполняемые файлы в Windows используют динамическую libc.


2. Разница между BSD-системами и Linux в части функциональности libc

В Linux'е glibc(eglibc) — это просто монстр. Помимо собственно libc, она включает libiconv (конверсию кодировок), libidn (поддержка доменных имён с неанглийскими буквами), librt (сигналы реального времени, точные часы, aio, POSIX mq, SysV shm), поддержку locale, полный набор файлов данных и утилит для работы с time zones, в т.ч. часто меняющиеся правила перевода часов на летнее/зимнее время для каждой страны, две реализации pthreads (1:1 nptl и N:M linuxthreads), libthread_db, библиотеку libresolv (DNS-клиент, преобразование доменных имён в IP-адреса), libnsl (SUN RPC, X/Open Transport Interface (XTI) и т.п.), а также криптографические функции MD5, SHA-256 и SHA-512, поддержу профилирования и т.д.

В операционной системе FreeBSD libc минималистична. Весь дополнительный функционал вынесен в отдельные библиотеки.

Почему так? Части "большой libc" очень тесно переплетены друг с другом. Разные части от разных версий "большой libc" нельзя смешивать. Поэтому shipping "большой libc" должен происходить одним пакетом, а вся она должна находиться в одном репозитарии. В случае с FreeBSD это возможно (вся base system, включая ядро, находится в одном репозитарии), в случае Linux приходится укрупнять саму libc.


3. Почему в Linux'е статическая компоновка glibc нормально не работает

Потому что линуксовая "большая libc" слишком долго бы инициализировалась для HelloWorld-приложения или например CGI-обработчика. Или создавала бы проблемы таким процессам, как init(8). По этой причине часть её функционала (например iconv/gconv) вынесена в .so-библиотеки, которые динамически догружаются с помощью dlopen(3) по мере надобности. Или не загружаются, если glibc статически связана и загрузчика нет.

Можно ли сделать так, чтобы dlopen(3) работала из статически скомпонованных бинарей? Можно! Но рассмотрим такой пример. Вы статически привязываетесь к glibc, в т.ч. к libstdc++, затем вызываете dlopen(3) для third-party so-библиотеки и она каким-то чудом срабатывает. Эта third-party so-библиотека тоже привязывается к libstdc++, только к динамическому её варианту (libstdc++.so). Получается две копии libstdc++ в адресном пространстве вашего процесса, с разным связыванием, и потенциально разных версий. С этого момента очень интересно будут себя вести глобальные переменные типа std::cout или, например, обработка исключений, пересекающих границы библиотеки и приложения. Также у вас будет две "чисто сишных" glibc в одном процессе: два объекта stdio, два списка atexit, два malloc-пула, война за TLS и так далее. Приедет ли автобус (SIGBUS) или же вы получите банальный SIGSEGV, я думаю, безразлично.

Кроме того, GNU libc находится под лицензией LGPL. Суть этой лицензии заключается в том, что пользователь имеет свободу компоновки (например, берёт ваш проприетарный бинарь, пробует на старой версии glibc с ошибками; обновляет свою версию glibc, чтобы исправить ошибки; и запускает ваш бинарь без перелинковки с помощью ld). Если же вы статически скомпоновались с GNU libc, тогда вы нарушаете права пользователя на свободу замены glibc.

Статическая компоновка с GNU libc вводит всё ваше приложение в сферу действия лицензии LGPL (или более сильной: GPL, AGPL на ваш выбор), хотите вы того, или нет. Таким образом, вы обязаны будете предоставить исходники, или же все ваши *.o-файлы для статической перелинковки в пользовательской среде с новой версией glibc.


4. Работает ли в BSD статическая компоновка libc

Да! Нет проблем ни с отложенной загрузкой зависимостей, ни с лицензией. Функция dlopen(3) при статической компоновке с libc в BSD просто честно не работает:

#pragma weak dlopen
void *
dlopen(const char *name, int mode)
{
_rtld_error(sorry);
return NULL;
}


5. Можно ли в Linux'е статически компоновать non-GNU libc (dietlibc, bionic libc, newlib и так далее)? Будет ли работать dlopen(3) в этом случае?

А хрен его знает.

Date: 2012-12-04 01:06 pm (UTC)
From: [identity profile] cd-riper.livejournal.com
не уверен, что тут у тебя корректно с терминологией. все-таки формально libc это всего лишь библиотека языка си. не более.

Date: 2012-12-04 01:24 pm (UTC)
From: [identity profile] waqur.livejournal.com
все языки так или иначе используют некий переходной слой между ядром и пользовательским миром

от libc зависит не только C и C++, но и питон, и жаба, и эрланг, и похапэ, и node.js

может быть её правильнее было бы назвать libkernelapi ? не знаю

в общем, как в 1970 году Керниган и Ритчи назвали, так и называется до сих пор

Date: 2012-12-04 01:36 pm (UTC)
From: [identity profile] cd-riper.livejournal.com
просто в случае винды у тебя есть win32 api и есть libc, который можно реализовать совершенно по разному его используя.
я к тому, что всякие ntdll это не libc. нельзя какие-то нюансы linux экстраполировать на все ОС.

Date: 2012-12-04 01:57 pm (UTC)
From: [identity profile] waqur.livejournal.com
да, в винде разделили загрузчик-и-интерфейс-с-ядром-а-также-хозяин-TLS от рантайм-поддержки-языка-си
так что другие языки (не си) используют только первую шнягу, но не используют вторую

к сожалению, термин для первой шняги в винде придумать забыли
Edited Date: 2012-12-04 08:59 pm (UTC)

March 2024

S M T W T F S
     12
3456789
10111213141516
17181920212223
24252627282930
31      

Автор стиля

Развернуть

No cut tags
Page generated 2026-03-01 01:12 pm
Powered by Dreamwidth Studios