Łączenie ze starszą wersją symbolu w a.so plik

Używając gcc i ld na Linuksie x86_64 muszę połączyć się z nowszą wersją biblioteki (glibc 2.14), ale plik wykonywalny musi działać na systemie ze starszą wersją (2.5). Ponieważ jedynym niezgodnym symbolem jest memcpy (potrzebny memcpy@GLIBC_2.2.5 ale biblioteka zapewniająca memcpy@GLIBC_2.14), chciałbym powiedzieć linkerowi, że zamiast przyjmować domyślną wersję memcpy, powinien przyjąć starą wersję, którą podam.

Znalazłem na to dość prosty sposób: po prostu podaj kopię pliku old. so w wierszu poleceń linker. Działa to dobrze, ale nie podoba mi się pomysł posiadania wielu plików .so (mogłem to zrobić tylko przez podanie wszystkich starych bibliotek, do których linkuję, które również mają odniesienia do memcpy) sprawdzonych w svn i potrzebnych przez mój system budowania.

Więc szukam sposobu, aby powiedzieć linkerowi, aby wziął Stary wersjonowany symbol.

Alternatywy, które nie działają (dobrze) dla mnie to:

  • używając asm .symver (jak widać na Web Archive of Trevor Pounds' Blog) ponieważ wymagałoby to ode mnie upewnienia się, że symver jest przed całym kodem używającym memcpy, co byłoby bardzo trudne (złożona baza kodu z 3rd party code)
  • utrzymanie środowiska kompilacji ze starymi bibliotekami; po prostu dlatego, że chcę rozwijać się na moim komputerze i byłoby pita synchronizować rzeczy w naszej sieci.

Myśląc o wszystkich zadaniach, które wykonuje linker, nie wydaje się być trudnym do imlpement, przecież ma jakiś kod, aby dowiedzieć się domyślną wersję symbolu też.

Wszelkie inne pomysły, które są na tym samym poziomie złożoności, co prosta linia poleceń linkera (jak tworzenie prostego skryptu linkera itp.) są również mile widziane, o ile nie są to dziwne hacki, takie jak edycja pliku binarnego...

Edit: Aby zachować to dla przyszłych czytelników, dodatkowo do poniższych pomysłów znalazłem opcję --wrap do linkera, która może być przydatna czasami też.

Author: jefe2000, 2012-01-11

11 answers

Po prostu połącz memcpy statycznie-pull memcpy.o z libc.a ar x /path/to/libc.a memcpy.o (niezależnie od wersji-memcpy jest w zasadzie samodzielną funkcją) i dołącz ją do ostatecznego linku. Zauważ, że statyczne łączenie może komplikować problemy z licencjonowaniem, jeśli twój projekt jest rozpowszechniany publicznie, a nie open-source.

Alternatywnie, możesz po prostu zaimplementować memcpy samodzielnie, chociaż ręcznie dostrojona wersja asemblera w glibc prawdopodobnie będzie bardziej wydajna

Zauważ, że memcpy@GLIBC_2.2.5 jest mapowane do memmove (stare wersje memcpy konsekwentnie kopiowane w przewidywalnym kierunku, co doprowadziło do tego, że czasami jest nadużywane, gdy memmove powinno być używane), i to jest jedyny powód zmiany wersji-możesz po prostu zastąpić memcpy memmove w kodzie w tym konkretnym przypadku.

Albo możesz przejść do statycznego łączenia, albo możesz upewnić się, że wszystkie systemy w Twojej sieci mają tę samą lub lepszą wersję niż Twoja maszyna do budowania.

 19
Author: Random832,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-11 17:09:34

Znalazłem następujące rozwiązanie robocze. Najpierw Utwórz plik 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);
}

Do skompilowania tego pliku nie są potrzebne żadne dodatkowe CFLAGS. Następnie połącz swój program z -WL,--wrap=memcpy.

 48
Author: anight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-14 13:52:07

Miałem podobny problem. Biblioteka stron trzecich, z której korzystamy, potrzebuje starej memcpy@GLIBC_2.2.5. Moim rozwiązaniem jest podejście rozszerzone @ anight posted.

Wypaczam też komendę memcpy, ale musiałem zastosować nieco inne podejście, ponieważ opublikowane rozwiązanie @anight nie zadziałało u mnie.

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.Mapa:

GLIBC_2.2.5 {
   memcpy;
};

Zbuduj wrapper:

gcc -c memcpy_wrap.c -o memcpy_wrap.o

Teraz w końcu przy linkowaniu programu dodaj

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

Tak, że skończysz z czymś takim jak:

g++ <some flags> -Wl,--version-script memcpy_wrap.map <some .o files> memcpy_wrap.o <some libs>
 8
Author: Ortwin Angermeier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-26 10:47:36

Miałem podobny problem. Po zainstalowaniu niektórych komponentów oracle na RHEL 7.1, mam to:

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

Wygląda na to, że (mój) glibc RHELA definiuje tylko 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

Udało mi się to obejść, najpierw tworząc memcpy.plik c bez zawijania, jak następuje:

#include <string.h>
asm (".symver old_memcpy, memcpy@GLIBC_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);
}

I memcpy.plik map, który eksportuje naszą memcpy jako memcpy@GLIBC_2.14:

GLIBC_2.14 {
   memcpy;
};

Potem skompilowałem własną memcpy.c do wspólnej lib jak to:

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

, przeniesiony libmemcpy-2.14.so do /some / oracle / lib (wskazywane przez-L argumenty w moim linkowaniu), i linkowane ponownie przez

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

(który skompilował bez błędów) i zweryfikował przez:

$ 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)
To mi pomogło. Mam nadzieję,że dla ciebie też.
 6
Author: avarvit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-22 09:52:36

Jestem wyraźnie trochę spóźniony, ale niedawno uaktualniłem (więcej powodów, aby nigdy nie aktualizować) mój system operacyjny Linux do Xubuntu 14.04, który przyszedł z nowym libc. Kompiluję bibliotekę współdzieloną na moim komputerze, która jest używana przez klientów, którzy Z uzasadnionych powodów nie zaktualizowali swojego środowiska od wersji 10.04. Skompilowana biblioteka współdzielona nie jest już ładowana w ich środowisku, ponieważ gcc uzależnia się od glibc memcpy w wersji 2.14 (lub wyższej). Zapomnijmy o tym szaleństwie. Obejście całego mojego projektu było trzykrotne:

  1. dodano do mojego gcc cflags: - include glibc_version_nightmare.h
  2. utworzono glibc_version_nightmare.h
  3. utworzono skrypt Perla, aby zweryfikować symbole w .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

Fragment skryptu Perla:

...
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;
 3
Author: Oliver K.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-07 17:26:05

To obejście nie wydaje się zgodne z opcją kompilacji-flto.

Moim rozwiązaniem jest wywołanie memmove. memove wykonuje dokładnie te same zadania co memcpy. Jedyną różnicą jest to, że gdy strefy src i dest nakładają się, memmove jest bezpieczne, a memcpy nieprzewidywalne. Więc możemy bezpiecznie zawsze nazywać memmove zamiast 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
 3
Author: Gilles Olivier Vollant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-16 10:48:22

Minimal runnable self contained example

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;
}

A. c

#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;
}

A. h

#ifndef A_H
#define A_H

int a(void);

#endif

A. Mapa

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

Testowane na Ubuntu 16.04.

 3
Author: Ciro Santilli TRUMP BAN IS BAD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-15 10:12:12

Dla nim-lang, opracowałem rozwiązanie, które znalazłem przy użyciu flagi kompilatora C --include= następująco:

Utwórz plik .h z:

__asm__(".symver fcntl,fcntl@GLIBC_2.4");

Zbuduj swój program z nim c ---passC:--include=symver.h

Co do mnie to też się krzyżuję. Kompiluję z nim c --cpu:arm --os:linux --passC:--include=symver.h ... i mogę uzyskać wersje symboli używając arm-linux-gnueabihf-objdump -T ../arm-libc.so.6 | grep fcntl

Musiałem usunąć ~/.cache/nim w pewnym momencie. I wydaje się, że działa.

 3
Author: Mildred,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-20 16:36:38

Myślę, że uda ci się stworzyć prosty plik C zawierający instrukcję symver i być może fałszywą funkcję wywołującą memcpy. Następnie musisz tylko upewnić się, że wynikowy plik obiektowy jest pierwszym plikiem podanym linkerowi.

 2
Author: zvrba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-11 17:05:54

Proponuję albo połączyć statycznie memcpy (), albo znaleźć źródło memcpy () i skompilować je jako własną bibliotekę.

 1
Author: Pete Wilson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-11 17:09:58

Może być spowodowana starą wersją LD (gnu link). Dla następującego prostego problemu:

#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;
}

Kiedy używam ld 2.19.1, memset zostaje przeniesiony do: memset@@GLIBC_2.0 i spowodować crash. Po uaktualnieniu do 2.25 jest to: memset@plt i crash rozwiązany.

 0
Author: jeanerpp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-29 06:23:34