Linking against older symbol version in a .so file

前端 未结 11 1802
长发绾君心
长发绾君心 2020-11-27 11:25

Using gcc and ld on x86_64 linux I need to link against a newer version of a library (glibc 2.14) but the executable needs to run on a system with an older version (2.5). Si

相关标签:
11条回答
  • 2020-11-27 12:00

    I'm clearly a little late responding to this but I recently upgraded (more reasons to never upgrade) my Linux OS to XUbuntu 14.04 which came with the new libc. I compile a shared library on my machine which is used by clients who, for whatever legitimate reasons, have not upgraded their environment from 10.04. The shared library I compiled no longer loaded in their environment because gcc put a dependency on memcpy glibc v. 2.14 (or higher). Let's leave aside the insanity of this. The workaround across my whole project was three fold:

    1. added to my gcc cflags: -include glibc_version_nightmare.h
    2. created the glibc_version_nightmare.h
    3. created a perl script to verify the symbols in the .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 script fragment:

    ...
    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;
    
    0 讨论(0)
  • 2020-11-27 12:00

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

    Tested on Ubuntu 16.04.

    0 讨论(0)
  • 2020-11-27 12:01

    I suggest you either link memcpy() statically; or find the source of memcpy( ) and compile it as your own library.

    0 讨论(0)
  • 2020-11-27 12:02

    This workaround seem not compatible with -flto compile option.

    My solution is calling memmove. memove does exactly the same jobs than memcpy. The only difference is when src and dest zone overlap, memmove is safe and memcpy is unpredictable. So we can safely always call memmove instead 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
    
    0 讨论(0)
  • 2020-11-27 12:03

    Just link memcpy statically - pull memcpy.o out of libc.a ar x /path/to/libc.a memcpy.o (whatever version - memcpy is pretty much a standalone function) and include it in your final link. Note that static linking may complicate licensing issues if your project is distributed to the public and not open-source.

    Alternatively, you could simply implement memcpy yourself, though the hand-tuned assembly version in glibc is likely to be more efficient

    Note that memcpy@GLIBC_2.2.5 is mapped to memmove (old versions of memcpy consistently copied in a predictable direction, which led to it sometimes being misused when memmove should have been used), and this is the only reason for the version bump - you could simply replace memcpy with memmove in your code for this specific case.

    Or you could go to static linking, or you could ensure that all systems on your network have the same or better version than your build machine.

    0 讨论(0)
提交回复
热议问题