## Общие соображения В библиотеку добавлены реализации символов, появившихся в `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» (см. [здесь](https://refspecs.linuxfoundation.org/LSB_1.3.0/IA64/spec/auxiliaryvector.html)). Эти данные доступны в момент запуска процесса. libc из новых Ubuntu сохраняет эти данные и предоставляет реализацию `getauxval`. В этом случае наша реализация перенаправляет вызовы `getauxval` в libc, получив при старте исполняемого файла соответствующий указатель. Если реализация libc недоступна (на старых Ubuntu или если libc не загружена), то эти данные можно получить, прочитав `/proc/self/auxv`. Это также делается один раз при старте. В обоих случаях, статически инициализируется синглтон `NUbuntuCompat::TGlibc`, который производит эти действия в конструкторе. `secure_getenv` использует одно из значений `getauxval`, поэтому к нему всё это также относится. Каждый метод новой libc реализован в отдельном объектом файле. `TGlibc` также находится в отдельном файле, и ссылки на неё стоят только в местах использования. Если при компоновке не понадобились ни `getauxval`, ни `secure_getenv`, то объектный файл с `TGlibc` тоже не будет выбран компоновщиком, и в этом случае никакой лишней статической инициализации выполняться не будет. ## Патч libc.so Чтобы иметь возможность использовать `getauxval` (точнее его реализацию `__getauxval`) из новой libc, если таковая уже используется процессом, библиотека совместимости объявляет этот символ у себя как внешний и слабый. При загрузке динамический компоновщик устанавливает значение этого символа из libc или `nullptr`, если его там нет. Наша реализация `getauxval` проверяет доступность реализации libc, просто сравнивая указатель с `nullptr`. В Аркадии также есть код, который подобным образом работает с символом `__cxa_thread_atexit_impl`. Однако, если компоновать такую программу с использованием новой libc, то к таким символам и самой программе будет приписано требование соответствующей (новой) версии libc. Чтобы этого не произошло, при сборке с этой библиотекой совместимости используется патченный вариант `libc.so.6`, где у таких символов удалена версия. Также, файлы, проверяющие, что доступна реализация из libc, должны быть собраны как PIC. В противном случае вместо значения, заполненного динамическим компоновщиком, компилятор ещё на стадии компиляции использует `nullptr` и проверка никогда не срабатывает. ## Упоминания Идея о возможности добавить слой совместимости была взята из ClickHouse. * [https://clickhouse.tech/](https://clickhouse.tech/) * [https://wiki.yandex-team.ru/clickhouse/](https://wiki.yandex-team.ru/clickhouse/)