aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/libc_compat/ubuntu_14/README.md
blob: 63dd8a22d9538ee5861b5bbf7e7fe8653679af97 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
## Общие соображения 
 
В библиотеку добавлены реализации символов, появившихся в `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/)