Ссылка на старую версию libc, чтобы обеспечить больший охват приложения
Двоичные файлы Linux обычно динамически связаны с базовой системной библиотекой (libc). Это сохраняет объем памяти двоичного файла довольно маленьким, но двоичные файлы, которые зависят от последних библиотек, не будут работать на старых системах. И наоборот, двоичные файлы, связанные со старыми библиотеками, будут успешно работать на последних системах.
Поэтому, чтобы обеспечить хорошее покрытие нашего приложения во время распространения, нам нужно выяснить самый старый libc, который мы можем поддержать, и связать наш двоичный файл с этим.
Как определить самую старую версию libc, на которую мы можем ссылаться?
4 ответа
Определите, какие символы в вашем исполняемом файле создают зависимость от нежелательной версии glibc.
$ objdump -p myprog
...
Version References:
required from libc.so.6:
0x09691972 0x00 05 GLIBC_2.3
0x09691a75 0x00 03 GLIBC_2.2.5
$ objdump -T myprog | fgrep GLIBC_2.3
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3 realpath
Загляните в зависимую библиотеку, чтобы увидеть, есть ли в старых версиях какие-либо символы, на которые вы можете ссылаться:
$ objdump -T /lib/libc.so.6 | grep -w realpath
0000000000105d90 g DF .text 0000000000000021 (GLIBC_2.2.5) realpath
000000000003e7b0 g DF .text 00000000000004bf GLIBC_2.3 realpath
Нам повезло!
Запросить версию от GLIBC_2.2.5
в вашем коде:
#include <limits.h>
#include <stdlib.h>
__asm__(".symver realpath,realpath@GLIBC_2.2.5");
int main () {
realpath ("foo", "bar");
}
Обратите внимание, что GLIBC_2.3 больше не нужен:
$ objdump -p myprog
...
Version References:
required from libc.so.6:
0x09691a75 0x00 02 GLIBC_2.2.5
$ objdump -T myprog | grep realpath
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 realpath
Для получения дополнительной информации см. http://www.trevorpounds.com/blog/?p=103.
К сожалению, решение @Sam не работает в моей ситуации. Но, по его мнению, я нашел свой способ решить это.
Это моя ситуация:
Я пишу программу на C++ с использованием фреймворка Thrift (это промежуточное программное обеспечение RPC). Я предпочитаю статическую ссылку динамической ссылке, поэтому моя программа статически связана с libthrift.a вместо libthrift.so. Однако libthrift.a динамически связан с glibc, и, так как мой libthrift.a построен на моей системе с glibc 2.15, мой libthrift.a использует memcpy версии 2.14 (memcpy@GLIBC_2.14), предоставленной glibc 2.15.
Но проблема в том, что наши серверные машины имеют только версию 2.5 glibc, которая имеет только memcpy@GLIBC_2.2.5. Это намного ниже, чем memcpy@GLIBC_2.14. Так что, конечно, моя серверная программа не может работать на этих машинах.
И я нашел это решение:
Использование.symver для получения ссылки на memcpy@GLIBC_2.2.5.
Напишите мою собственную функцию __wrap_memcpy, которая просто вызывает memcpy@GLIBC_2.2.5 напрямую.
При связывании моей программы добавьте опцию -Wl,- wrap=memcpy в gcc/g++.
Код, включенный в шаги 1 и 2, находится здесь: https://gist.github.com/nicky-zs/7541169
Чтобы сделать это более автоматизированным способом, вы можете использовать следующий скрипт, чтобы создать список всех символов, которые более новые в вашей GLIBC, чем в данной версии (установлено в строке 2). Это создает glibc.h
файл (имя файла задается аргументом скрипта), который содержит все необходимое .symver
деклараций. Вы можете добавить -include glibc.h
чтобы ваши CFLAGS, чтобы убедиться, что он будет поднят везде в вашей компиляции.
Этого достаточно, если вы не используете статические библиотеки, которые были скомпилированы без включенного выше. Если вы делаете, и вы не хотите перекомпилировать, вы можете использовать objcopy
создать копию библиотеки с символами, переименованными в старые версии. Вторая строчка скрипта создает версию вашей системы. libstdc++.a
это будет ссылаться на старые символы glibc. Добавление -L.
(или же -Lpath/to/libstdc++.a/
) заставит вашу программу статически связывать libstdC++ без связывания в кучу новых символов. Если вам это не нужно, удалите две последние строки и printf ... redeff
линия.
#!/bin/bash
maxver=2.9
headerf=${1:-glibc.h}
set -e
for lib in libc.so.6 libm.so.6 libpthread.so.0 libdl.so.2 libresolv.so.2 librt.so.1; do
objdump -T /usr/lib/$lib
done | awk -v maxver=${maxver} -vheaderf=${headerf} -vredeff=${headerf}.redef -f <(cat <<'EOF'
BEGIN {
split(maxver, ver, /\./)
limit_ver = ver[1] * 10000 + ver[2]*100 + ver[3]
}
/GLIBC_/ {
gsub(/\(|\)/, "",$(NF-1))
split($(NF-1), ver, /GLIBC_|\./)
vers = ver[2] * 10000 + ver[3]*100 + ver[4]
if (vers > 0) {
if (symvertext[$(NF)] != $(NF-1))
count[$(NF)]++
if (vers <= limit_ver && vers > symvers[$(NF)]) {
symvers[$(NF)] = vers
symvertext[$(NF)] = $(NF-1)
}
}
}
END {
for (s in symvers) {
if (count[s] > 1) {
printf("__asm__(\".symver %s,%s@%s\");\n", s, s, symvertext[s]) > headerf
printf("%s %s@%s\n", s, s, symvertext[s]) > redeff
}
}
}
EOF
)
sort ${headerf} -o ${headerf}
objcopy --redefine-syms=${headerf}.redef /usr/lib/libstdc++.a libstdc++.a
rm ${headerf}.redef
glibc 2.2 - довольно распространенная минимальная версия. Однако найти платформу сборки для этой версии может быть нетривиальным.
Вероятно, лучшее направление - подумать о самой старой ОС, которую вы хотите поддерживать, и опираться на нее.