Подтвердить что ты не робот

Связывание со старой версией символа в файле .so

С помощью gcc и ld на linux x86_64 мне нужно связать с более новой версией библиотеки (glibc 2.14), но исполняемый файл должен запускаться в системе со старой версией (2.5). Поскольку единственным несовместимым символом является memcpy (требуется [email protected]_2.2.5, но библиотека, предоставляющая [email protected]_2.14), я хотел бы сказать компоновщику, что вместо того, чтобы брать версию по умолчанию для memcpy, она должна взять старую версию, которую я указываю.

Я нашел довольно непростой способ сделать это: просто укажите копию старого .so файла в командной строке компоновщика. Это отлично работает, но мне не нравится идея наличия нескольких файлов .so(я мог бы заставить его работать, указав все старые библиотеки, на которые я ссылаюсь, и ссылки на memcpy), которые были проверены в svn и необходимы моей системе сборки.

Итак, я ищу способ сообщить компоновщику взять старый символ с версией.

Альтернативы, которые не работают (ну) для меня:

  • Использование asm.symver(как видно на Веб-архив блога Trevor Pounds), так как это потребует от меня убедиться, что symver перед всем кодом, использующим memcpy, который будет очень сложным (сложная кодовая база с сторонним кодом).
  • Поддержка среды сборки со старыми библиотеками; просто потому, что я хочу развиваться на своей настольной системе, и это будет лаваш для синхронизации в нашей сети.

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

Любые другие идеи, которые находятся на том же уровне сложности, что и простая компоновка командной строки (например, создание простого компоновщика script и т.д.), также приветствуются, если они не странные хаки, как редактирование полученного двоичного файла..

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

4b9b3361

Ответ 1

Просто соедините memcpy статически - вытащите memcpy.o из libc.a ar x /path/to/libc.a memcpy.o (любая версия - memcpy - это почти отдельная функция) и включите ее в свою последнюю ссылку. Обратите внимание, что статическое связывание может затруднить проблемы с лицензированием, если ваш проект распространяется среди общественности, а не с открытым исходным кодом.

В качестве альтернативы вы можете просто реализовать memcpy самостоятельно, хотя настроенная вручную версия сборки в glibc, вероятно, будет более эффективной

Обратите внимание, что [email protected]_2.2.5 сопоставляется с memmove (старые версии memcpy, последовательно скопированные в предсказуемом направлении, что иногда приводило к неправильному использованию, когда memmove были использованы), и это единственная причина для версии bump - вы могли бы просто заменить memcpy memmove в вашем коде для этого конкретного случая.

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

Ответ 2

Я нашел следующее рабочее решение. Сначала создайте файл memcpy.c:

#include <string.h>

/* some systems do not have newest [email protected]@GLIBC_2.14 - stay with old good one */
asm (".symver memcpy, [email protected]_2.2.5");

void *__wrap_memcpy(void *dest, const void *src, size_t n)
{
    return memcpy(dest, src, n);
}

Никаких дополнительных CFLAGS, необходимых для компиляции этого файла. Затем свяжите свою программу с -Wl, - wrap = memcpy.

Ответ 3

У меня была аналогичная проблема. Для используемой сторонней библиотеки нужна старая [email protected]_2.2.5. Мое решение - расширенный подход @anight.

Я также деформирую команду memcpy, но мне пришлось использовать несколько иной подход, так как решение @anight не работает для меня.

memcpy_wrap.c:

#include <stddef.h>
#include <string.h>

asm (".symver wrap_memcpy, [email protected]_2.2.5");
void *wrap_memcpy(void *dest, const void *src, size_t n) {
  return memcpy(dest, src, n);
}

memcpy_wrap.map:

GLIBC_2.2.5 {
   memcpy;
};

Создайте обертку:

gcc -c memcpy_wrap.c -o memcpy_wrap.o

Теперь, наконец, при связывании программы добавьте

  • -Wl,--version-script memcpy_wrap.map
  • memcpy_wrap.o

чтобы вы получили что-то вроде:

g++ <some flags> -Wl,--version-script memcpy_wrap.map <some .o files> memcpy_wrap.o <some libs>

Ответ 4

У меня была аналогичная проблема. Попытка установить некоторые компоненты оракула на RHEL 7.1, я получил следующее:

$ gcc -o /some/oracle/bin/foo .... -L/some/oracle/lib ... 
/some/oracle/lib/libfoo.so: undefined reference to `[email protected]_2.14'

Кажется, что (мой) RHEL glibc определяет только [email protected]_2.2.5:

$ readelf -Ws /usr/lib/x86_64-redhat-linux6E/lib64/libc_real.so | fgrep [email protected]
   367: 000000000001bfe0    16 FUNC    GLOBAL DEFAULT    8 [email protected]@GLIBC_2.2.5
  1166: 0000000000019250    16 FUNC    WEAK   DEFAULT    8 [email protected]@GLIBC_2.2.5

Итак, мне удалось обойти это, сначала создав файл memcpy.c без обертывания, следующим образом:

#include <string.h>
asm (".symver old_memcpy, [email protected]_2.2.5");       // hook old_memcpy as [email protected]
void *old_memcpy(void *, const void *, size_t );
void *memcpy(void *dest, const void *src, size_t n)   // then export memcpy
{
    return old_memcpy(dest, src, n);
}

и файл memcpy.map, который экспортирует нашу memcpy как [email protected]_2.14:

GLIBC_2.14 {
   memcpy;
};

Затем я скомпилировал свой собственный memcpy.c в общий lib следующим образом:

$ gcc -shared -fPIC -c memcpy.c
$ gcc -shared -fPIC -Wl,--version-script memcpy.map -o libmemcpy-2.14.so memcpy.o -lc

переместил libmemcpy-2.14.so в /some/oracle/lib (указанный аргументами -L в моей ссылке) и снова связался с

$ gcc -o /some/oracle/bin/foo .... -L/some/oracle/lib ... /some/oracle/lib/libmemcpy-2.14.so -lfoo ...

(который скомпилирован без ошибок) и подтвердил его:

$ ldd /some/oracle/bin/foo
    linux-vdso.so.1 =>  (0x00007fff9f3fe000)
    /some/oracle/lib/libmemcpy-2.14.so (0x00007f963a63e000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007f963a428000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f963a20c000)
    librt.so.1 => /lib64/librt.so.1 (0x00007f963a003000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f9639c42000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f963aa5b000)

Это сработало для меня. Надеюсь, он тоже сделает это за вас.

Ответ 5

Я явно немного поздно отвечаю на это, но недавно обновил (больше причин никогда не обновлять) свою ОС Linux до XUbuntu 14.04, которая появилась с новым libc. Я собираю общую библиотеку на своей машине, которая используется клиентами, которые по каким-либо законным причинам не обновили свою среду с 10.04. Общая библиотека, которую я скомпилировал, больше не загружается в их среду, поскольку gcc устанавливает зависимость от memcpy glibc v. 2.14 (или выше). Оставьте в стороне безумие этого. Обходной путь по всему моему проекту был в три раза:

  • добавлен в мои gcc cflags: -ключить glibc_version_nightmare.h
  • создал glibc_version_nightmare.h
  • создал perl script для проверки символов в .so

glibc_version_nightmare.h:

#if defined(__GNUC__) && defined(__LP64__)  /* only under 64 bit gcc */
#include <features.h>       /* for glibc version */
#if defined(__GLIBC__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 14)
/* force mempcy to be from earlier compatible system */
__asm__(".symver memcpy,[email protected]_2.2.5");
#endif
#undef _FEATURES_H      /* so gets reloaded if necessary */
#endif

perl script фрагмент:

...
open SYMS, "nm $flags $libname |";

my $status = 0;

sub complain {
my ($symbol, $verstr) = @_;
print STDERR "ERROR: $libname $symbol requires $verstr\n";
$status = 1;
}

while (<SYMS>) {
next unless /\@\@GLIBC/;
chomp;
my ($symbol, $verstr) = (m/^\s+.\s(.*)\@\@GLIBC_(.*)/);
die "unable to parse version from $libname in $_\n"
    unless $verstr;
my @ver = split(/\./, $verstr);
complain $symbol, $verstr
    if ($ver[0] > 2 || $ver[1] > 10);
}
close SYMS;

exit $status;

Ответ 6

Я думаю, вы можете уйти от создания простого файла C, содержащего инструкцию symver и, возможно, фиктивную функцию, вызывающую memcpy. Тогда вам просто нужно убедиться, что полученный объектный файл - это первый файл, предоставленный компоновщику.

Ответ 7

Я предлагаю вам либо ссылку memcpy() статически; или найти источник memcpy() и скомпилировать его как свою собственную библиотеку.

Ответ 8

Это может быть вызвано старой версией ld (gnu link). Для следующей простой задачи:

#include <string.h>
#include <stdio.h>
int main(int argc,char **argv)
{
    char buf[5];
    memset(buf,0,sizeof(buf));
    printf("ok\n");
    return 0;
}

Когда я использую ld 2.19.1, memset перемещается в: memset @@GLIBC_2.0 и вызывает сбой. После обновления до 2.25 это будет: memset @plt, и авария решена.