robot-piglet 53dcfae09d Intermediate changes | 2 months ago | |
---|---|---|
.. | ||
README.md | 1 year ago | |
ya.make | 2 months ago |
В библиотеку добавлены реализации символов, появившихся в libc.so.6
Ubuntu
14.04 с момента Ubuntu 12.04.
Если какой-либо объектный файл ссылается на один из таких символов, то при
компоновке этот символ будет взят из нашей библиотеки. В противном случае,
компоновщик сослался бы на такой символ из libc.so.6
, добавив к исполняемому
файлу зависимость от новой версии динамической библиотеки (GLIBC_2.16
,
GLIBC_2.17
или GLIBC_2.18
). Такой исполняемый файл не может быть запущен на
Ubuntu 12.04, даже если ни один из новых символов не используется в runtime.
На Ubuntu 14.04 или более новых, в случае если процесс загружает libc.so.6
, мы
будем в runtime иметь две реализации символов, нашу и libc. Но все добавленные
функции не имеют какого-либо состояния и никакими деталями внутренней реализации
не связаны с другими. Нет разницы, какую из реализаций использовать, и даже
попеременное использование различных реализаций в одном и том же контексте не
должно приводить к некорректной работе.
В какой-то момент этот слой совместимости будет отключен: https://st.yandex-team.ru/DEVTOOLS-7436
Была идея оформить новые реализации так, чтобы в случае их наличия и в загруженной libc динамический компоновщик выбирал для всех ссылок одну реализацию.
По всей видимости, для этого требуется собирать исполняемый файл как PIE. Только в этом случае для наших реализаций будут сгенерированы дополнительные PLT прослойки и вызовы наших реализаций будут проходить через них.
Но в этом случае вообще все вызовы будут происходить таким образом и это повлияет на производительность. Ухудшения производительности, наверное, можно избежать, явно указав, какие символы в исполняемом файле должны быть публичными, но сейчас нет способа сделать это в одном месте, учитывая, что в некоторых случаях этот список должен дополняться.
getauxval
и secure_getenv
Функция getauxval
требует загрузки и хранения «Auxiliary Vector» (см.
здесь).
Эти данные доступны в момент запуска процесса. libc из новых Ubuntu сохраняет
эти данные и предоставляет реализацию getauxval
. В этом случае наша реализация
перенаправляет вызовы getauxval
в libc, получив при старте исполняемого файла
соответствующий указатель.
Если реализация libc недоступна (на старых Ubuntu или если libc не загружена),
то эти данные можно получить, прочитав /proc/self/auxv
. Это также делается
один раз при старте.
В обоих случаях, статически инициализируется синглтон NUbuntuCompat::TGlibc
,
который производит эти действия в конструкторе.
secure_getenv
использует одно из значений getauxval
, поэтому к нему всё это
также относится.
Каждый метод новой libc реализован в отдельном объектом файле. TGlibc
также
находится в отдельном файле, и ссылки на неё стоят только в местах использования.
Если при компоновке не понадобились ни getauxval
, ни secure_getenv
, то
объектный файл с TGlibc
тоже не будет выбран компоновщиком, и в этом случае
никакой лишней статической инициализации выполняться не будет.
Чтобы иметь возможность использовать getauxval
(точнее его реализацию
__getauxval
) из новой libc, если таковая уже используется процессом,
библиотека совместимости объявляет этот символ у себя как внешний и слабый. При
загрузке динамический компоновщик устанавливает значение этого символа из libc
или nullptr
, если его там нет. Наша реализация getauxval
проверяет
доступность реализации libc, просто сравнивая указатель с nullptr
.
В Аркадии также есть код, который подобным образом работает с символом
__cxa_thread_atexit_impl
.
Однако, если компоновать такую программу с использованием новой libc, то к таким
символам и самой программе будет приписано требование соответствующей (новой)
версии libc. Чтобы этого не произошло, при сборке с этой библиотекой
совместимости используется патченный вариант libc.so.6
, где у таких символов
удалена версия.
Также, файлы, проверяющие, что доступна реализация из libc, должны быть собраны
как PIC. В противном случае вместо значения, заполненного динамическим
компоновщиком, компилятор ещё на стадии компиляции использует nullptr
и
проверка никогда не срабатывает.
Идея о возможности добавить слой совместимости была взята из ClickHouse.