Связывание со старой версией libc для обеспечения большего охвата приложений


двоичные файлы Linux обычно динамически связаны с основной системной библиотекой (libc). Это сохраняет объем памяти двоичного файла довольно небольшим, но двоичные файлы, которые зависят от последних библиотек, не будут работать на старых системах. И наоборот, двоичные файлы, связанные со старыми библиотеками, будут успешно работать на последних системах.

поэтому, чтобы обеспечить хорошее покрытие нашего приложения во время распространения, нам нужно выяснить, какой самый старый libc мы можем поддерживать и связывать наши двоичный против этого.

Как мы должны определить самую старую версию libc, на которую мы можем ссылаться?

4 59

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 framework (это промежуточное ПО RPC). Я предпочитаю статическую ссылку динамической ссылке, поэтому моя программа связана с libthrift.а статически вместо libthrift.so. однако,libthrift.а динамически связан с glibc, и так как мой libthrift.а строится на моей системе с glibc 2.15, my libthrift.а использует memcpy по версии 2.14(memcpy@GLIBC_2.14) библиотеку glibc 2.15.

но проблема в том, что наши серверные машины имеют только версию 2.5 glibc, которая имеет только memcpy@GLIBC_2.2.5. Это намного ниже, чем memcpy@GLIBC_2.14. Поэтому, конечно, моя серверная программа не может работать на этих машинах.

И Я нашел это решение:

  1. использование .symver для получения ссылки на memcpy@GLIBC_2.2.5.

  2. напиши мой собственный __оберните_функции memcpy функция, которая просто называет memcpy@GLIBC_2.2.5 напрямую.

  3. при связывании моей программы, добавить - 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-довольно распространенная минимальная версия. Однако поиск платформы сборки для этой версии может быть нетривиальным.

вероятно, лучшее направление-подумать о самой старой ОС, которую вы хотите поддерживать и строить на этом.