Ссылка на старую версию 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. Так что, конечно, моя серверная программа не может работать на этих машинах.

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

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

  2. Напишите мою собственную функцию __wrap_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 - довольно распространенная минимальная версия. Однако найти платформу сборки для этой версии может быть нетривиальным.

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

Другие вопросы по тегам