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