programing

.so 파일의 이전 심볼 버전에 대한 링크

prostudy 2022. 9. 1. 21:22
반응형

.so 파일의 이전 심볼 버전에 대한 링크

x86_64 Linux에서 gcc 및 ld를 사용하여 새로운 버전의 라이브러리(glibc 2.14)에 링크해야 하는데 실행 파일은 오래된 버전(2.5)의 시스템에서 실행해야 합니다.호환되지 않는 기호는 memcpy뿐이므로(memcpy@ 필요)GLIBC_2.2.5 단, memcpy@를 제공하는 라이브러리GLIBC_2.14)는 링커에게 memcpy의 디폴트버전을 사용하는 것이 아니라 지정한 오래된 버전을 사용하는 것을 지시하고 싶습니다.

링커 명령줄에서 오래된 .so 파일의 복사본을 지정하기만 하면 됩니다.이것은 정상적으로 동작합니다만, 복수의 .so 파일(링크 하는 모든 오래된 라이브러리와 memcpy에 대한 참조를 지정하는 것만으로 동작시킬 수 있었습니다)을 svn에 체크인 해, 빌드 시스템에 필요한 것을 좋아하지 않습니다.

그래서 링커에게 구버전 심볼을 가져가라고 말하는 방법을 찾고 있습니다.

나에게 있어서 효과가 없는 대안은 다음과 같습니다.

  • asm .symver (Trevor Pounds의 Web Archive's Blog에서 볼 수 있듯이)를 사용하면 memcpy를 사용하는 모든 코드 앞에 symver가 있는지 확인해야 합니다.이것은 매우 어렵습니다(서드파티 코드와 복잡한 코드베이스).
  • 오래된 라이브러리로 빌드 환경을 유지합니다.단순히 데스크톱 시스템을 개발하고 싶기 때문에 네트워크 내의 데이터를 동기화할 수 있습니다.

링커가 하는 모든 일을 생각할 때, 기호의 디폴트 버전도 알아내는 코드를 가지고 있기 때문에, 그것을 수정하는 것은 어려운 일이 아닌 것 같습니다.

간단한 링커 명령줄과 같은 복잡도 수준(단순 링커 스크립트 작성 등)에 있는 다른 아이디어도 바이너리 편집과 같은 이상한 해킹이 아니라면 환영입니다.

편집: 향후 독자를 위해 이 정보를 보존하기 위해 아래 아이디어 외에 옵션도 찾았습니다.--wrap링커에 접속할 수도 있습니다.

저는 다음과 같은 유효한 해결책을 찾았습니다.먼저 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와 연결합니다.

대해서는 C 컴파일러 nim-lang을 에 대해 했습니다.--include=하다

다음 파일을 사용하여 symver.h 파일을 만듭니다.

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

★★★★★★★★★★★★★★로 프로그램을 구축해 .nim c ---passC:--include=symver.h

저도 교차 편집 중입니다.와 함께 컴파일합니다.nim c --cpu:arm --os:linux --passC:--include=symver.h ...은 '심볼버전'을 볼 수 요.arm-linux-gnueabihf-objdump -T ../arm-libc.so.6 | grep fcntl

는 그것을 제거해야만 했다.~/.cache/nim어느 순간엔가.★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

으로 링크하기만 하면 .-.a memcpy에서.를 꺼냅니다.o2 libc.a에서 꺼냅니다.ar x /path/to/libc.a memcpy.o(어느 버전이든 memcpy는 거의 스탠드아론 함수입니다)그리고 그것을 최종 링크에 포함합니다.프로젝트가 오픈소스가 아닌 일반에 배포될 경우 정적 링크는 라이센스 문제를 복잡하게 만들 수 있습니다.

또는 직접 memcpy를 구현할 수도 있습니다.단, glibc에서 수동 조정된 어셈블리 버전이 더 효율적일 수 있습니다.

memcpy@에 주의해 주세요.GLIBC_2.2.5는 memmove에 매핑되어 있습니다(예측 가능한 방향으로 이전 버전의 memcpy가 일관되게 복사되어 memmove가 사용되었을 때 오용될 수 있습니다).이것이 버전 범프의 유일한 이유입니다.이 특정 케이스에서는 memcpy를 코드로 memmove로 대체할 수 있습니다.

또는 정적 링크로 이동하거나 네트워크상의 모든 시스템에 빌드 머신과 같거나 더 나은 버전이 있는지 확인할 수 있습니다.

확실히 대응이 늦었습니다만, 최근 Linux OS를 새로운 libc에 부속된 XUbuntu 14.04로 업그레이드(업그레이드하지 않는 이유)했습니다.정당한 이유로 환경을 10.04에서 업그레이드하지 않은 클라이언트가 사용하는 공유 라이브러리를 머신으로 컴파일합니다.gcc가 memcpy glibc v.2.14(또는 그 이후)에 의존하기 때문에 컴파일한 공유 라이브러리는 이들 환경에서 로드되지 않습니다.이 광기는 접어두자.프로젝트 전체에 걸친 회피책은 세 가지였습니다.

  1. gcc cflags에 추가됨: -syslog glibc_version_niightmare.h
  2. glibc_version_niightmare.h 를 작성했습니다.
  3. perl 스크립트를 생성하여의 기호를 확인합니다.

glibc_version_n라이트마레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;

최소한의 실행 가능 자체 억제 예

GitHub 업스트림

메인

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

교류

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

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

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

파일 만들기

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로 테스트.

저도 비슷한 문제가 있었어요.RHEL 7.1에 몇 가지 Oracle 컴포넌트를 설치하려고 했을 때 다음과 같은 결과가 나왔습니다.

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

(나의) RHEL의 glibc는 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.map 파일GLIBC_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/somet/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)

이건 나한테 효과가 있었어.너도 그랬으면 좋겠다.

이 회피책은 -flto 컴파일 옵션과 호환되지 않는 것 같습니다.

제 해결책은 memmove를 호출하는 것입니다. memove는 memcpy와 정확히 같은 일을 합니다.유일한 차이점은 src와 수신인 존이 겹치면 memmove는 안전하며 memcpy는 예측할 수 없다는 것입니다.그래서 우리는 항상 memcpy 대신 memmove를 안전하게 호출할 수 있다.

#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

저도 비슷한 문제가 있었어요.우리가 사용하는 서드파티 라이브러리는 오래된 것을 필요로 한다.memcpy@GLIBC_2.2.5저의 솔루션은 @anight posted 확장 접근법입니다.

저도 휘어집니다.memcpy명령어를 사용했지만, @night에 게시된 솔루션이 나에게 효과가 없었기 때문에 조금 다른 방법을 사용해야만 했습니다.

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

symver 문이 포함된 간단한 C파일과 memcpy를 호출하는 더미 함수를 만드는 것은 피할 수 있을 것 같습니다.다음으로 결과 객체파일이 링커에게 주어진 첫 번째 파일인지 확인하기만 하면 됩니다.

오래된 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로 크래시가 해결됩니다.

memcpy()를 정적으로 링크하거나 memcpy()의 소스를 찾아 자신의 라이브러리로 컴파일할 것을 권장합니다.

언급URL : https://stackoverflow.com/questions/8823267/linking-against-older-symbol-version-in-a-so-file

반응형