Связывание со старой версией символа в файле .so - PullRequest
41 голосов
/ 11 января 2012

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

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

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

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

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

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

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

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

Ответы [ 10 ]

45 голосов
/ 14 января 2012

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

#include <string.h>

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

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

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

18 голосов
/ 11 января 2012

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

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

Обратите внимание, что memcpy@GLIBC_2.2.5 отображается на memmove (старые версии memcpy последовательно копируются в предсказуемом направлении, что иногда приводило к неправильному использованию, когда memmove должен был использоваться), и это единственная причина повышения версии - вы можете просто заменить memcpy на memmove в вашем коде для этого конкретного случая.

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

6 голосов
/ 26 ноября 2013

У меня была похожая проблема.Сторонней библиотеке, которую мы используем, нужна старая memcpy@GLIBC_2.2.5.Мое решение - расширенный подход @anight posts.

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

memcpy_wrap.c:

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

asm (".symver wrap_memcpy, memcpy@GLIBC_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>
5 голосов
/ 22 октября 2015

У меня была похожая проблема. Пытаясь установить некоторые компоненты Oracle на RHEL 7.1, я получил это:

$ gcc -o /some/oracle/bin/foo .... -L/some/oracle/lib ... 
/some/oracle/lib/libfoo.so: undefined reference to `memcpy@GLIBC_2.14'

Похоже, что (мой) glibc RHEL определяет только memcpy@GLIBC_2.2.5:

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

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

#include <string.h>
asm (".symver old_memcpy, memcpy@GLIBC_2.2.5");       // hook old_memcpy as memcpy@2.2.5
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 как memcpy@GLIBC_2.14:

GLIBC_2.14 {
   memcpy;
};

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

$ 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)

Это сработало для меня. Я надеюсь, что это делает это и для вас.

4 голосов
/ 07 октября 2014

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

  1. добавлено в мои флаги gcc:скрипт для проверки символов в .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,memcpy@GLIBC_2.2.5");
#endif
#undef _FEATURES_H      /* so gets reloaded if necessary */
#endif

фрагмент скрипта perl:

...
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;
2 голосов
/ 16 февраля 2018

Этот обходной путь кажется несовместимым с опцией -flto compile.

Мое решение - вызвать memmove. memove выполняет ту же работу, что и memcpy. Разница лишь в том, что когда зоны src и dest перекрываются, memmove безопасен, а memcpy непредсказуем. Таким образом, мы можем всегда безопасно вызывать memmove вместо memcpy

#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}
#endif
1 голос
/ 11 января 2012

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

1 голос
/ 11 января 2012

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

0 голосов

Пример минимального запуска автономного

GitHub upstream .

main.c

#include <assert.h>
#include <stdlib.h>

#include "a.h"

#if defined(V1)
__asm__(".symver a,a@LIBA_1");
#elif defined(V2)
__asm__(".symver a,a@LIBA_2");
#endif

int main(void) {
#if defined(V1)
    assert(a() == 1);
#else
    assert(a() == 2);
#endif
    return EXIT_SUCCESS;
}

ac

#include "a.h"

__asm__(".symver a1,a@LIBA_1");
int a1(void) {
    return 1;
}

/* @@ means "default version". */
__asm__(".symver a2,a@@LIBA_2");
int a2(void) {
    return 2;
}

ах

#ifndef A_H
#define A_H

int a(void);

#endif

a.map

LIBA_1{
    global:
    a;
    local:
    *;
};

LIBA_2{
    global:
    a;
    local:
    *;
};

Makefile

CC := gcc -pedantic-errors -std=c89 -Wall -Wextra

.PHONY: all clean run

all: main.out main1.out main2.out

run: all
    LD_LIBRARY_PATH=. ./main.out
    LD_LIBRARY_PATH=. ./main1.out
    LD_LIBRARY_PATH=. ./main2.out

main.out: main.c libcirosantilli_a.so
    $(CC) -L'.' main.c -o '$@' -lcirosantilli_a

main1.out: main.c libcirosantilli_a.so
    $(CC) -DV1 -L'.' main.c -o '$@' -lcirosantilli_a

main2.out: main.c libcirosantilli_a.so
    $(CC) -DV2 -L'.' main.c -o '$@' -lcirosantilli_a

a.o: a.c
    $(CC) -fPIC -c '$<' -o '$@'

libcirosantilli_a.so: a.o
    $(CC) -Wl,--version-script,a.map -L'.' -shared a.o -o '$@'

libcirosantilli_a.o: a.c
    $(CC) -fPIC -c '$<' -o '$@'

clean:
    rm -rf *.o *.a *.so *.out

Протестировано в Ubuntu 16.04.

0 голосов
/ 29 января 2016

Это может быть вызвано старой версией 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 и сбой решен.

...