정적으로 연결된 라이브러리 간에 심볼 충돌을 처리하는 방법은 무엇입니까?
라이브러리를 작성할 때 가장 중요한 규칙과 베스트 프랙티스 중 하나는 라이브러리의 모든 기호를 라이브러리 고유의 네임스페이스에 넣는 것입니다.는 C++가 있기 할 수 .namespace
키워드를 지정합니다.C에서는 식별자에 라이브러리 고유의 프레픽스를 붙이는 것이 통상의 어프로치입니다.
C 표준의 규칙은 (안전한 컴파일을 위해) 몇 가지 제약을 가한다.볼 수 C 컴파일러는 8자만 볼 수 있습니다.foobar2k_eggs
★★★★★★★★★★★★★★★★★」foobar2k_spam
는 같은 식별자로 올바르게 해석될 수 있습니다.다만, 현대의 모든 컴파일러는 임의의 긴 식별자를 사용할 수 있기 때문에, 우리 시대(21세기)에서는 이것에 대해 걱정할 필요가 없습니다.
그러나 기호 이름/아이덴피어를 변경할 수 없는 라이브러리와 마주하고 있다면?정적 바이너리와 헤더만 얻었거나 원하지 않거나 직접 조정 및 재컴파일할 수 없습니다.
적어도 정적 라이브러리의 경우 상당히 편리하게 작업할 수 있습니다.
라이브러리의 헤더를 foo와 bar로 생각해 주세요.이 튜토리얼을 위해 소스 파일도 드리겠습니다.
예/ex01/foo.h
int spam(void);
double eggs(void);
example/ex01/foo.c (불투명/사용 불가)
int the_spams;
double the_eggs;
int spam()
{
return the_spams++;
}
double eggs()
{
return the_eggs--;
}
예/ex01/bar.h
int spam(int new_spams);
double eggs(double new_eggs);
example/ex01/bar.c (불투명할 수 있음/사용할 수 없음)
int the_spams;
double the_eggs;
int spam(int new_spams)
{
int old_spams = the_spams;
the_spams = new_spams;
return old_spams;
}
double eggs(double new_eggs)
{
double old_eggs = the_eggs;
the_eggs = new_eggs;
return old_eggs;
}
우리는 그것들을 프로그램 foobar에 사용하고 싶다.
예/ex01/foobar.c
#include <stdio.h>
#include "foo.h"
#include "bar.h"
int main()
{
const int new_bar_spam = 3;
const double new_bar_eggs = 5.0f;
printf("foo: spam = %d, eggs = %f\n", spam(), eggs() );
printf("bar: old spam = %d, new spam = %d ; old eggs = %f, new eggs = %f\n",
spam(new_bar_spam), new_bar_spam,
eggs(new_bar_eggs), new_bar_eggs );
return 0;
}
한 가지 문제가 바로 나타납니다.C는 과부하를 인식하지 않습니다.이름은 같지만 시그니처는 다른 2 곱하기 2 함수가 있습니다.그래서 우리는 그것들을 구별할 수 있는 방법이 필요하다.어쨌든, 이것에 대해서 컴파일러의 의견을 들어보겠습니다.
example/ex01/ $ make
cc -c -o foobar.o foobar.c
In file included from foobar.c:4:
bar.h:1: error: conflicting types for ‘spam’
foo.h:1: note: previous declaration of ‘spam’ was here
bar.h:2: error: conflicting types for ‘eggs’
foo.h:2: note: previous declaration of ‘eggs’ was here
foobar.c: In function ‘main’:
foobar.c:11: error: too few arguments to function ‘spam’
foobar.c:11: error: too few arguments to function ‘eggs’
make: *** [foobar.o] Error 1
좋아, 이건 놀랄 일이 아니야. 우리가 이미 알고 있거나 최소한 의심하고 있는 걸 말해줬을 뿐이야
원래 라이브러리의 소스 코드나 헤더를 수정하지 않고 식별자 충돌을 해결할 수 있을까요?사실 우리는 할 수 있다.
먼저 컴파일 시간 문제를 해결합니다..#define
라이브러리에서 내보낸 모든 기호 앞에 접두사를 붙이는 지시문입니다.나중에 우리는 이 작업을 멋진 래퍼 헤더를 사용하여 수행하지만, 무슨 일이 일어나고 있는지 보여주기 위해 foobar.c 소스 파일에 있는 그대로 수행했습니다.
예/ex02/foobar.c
#include <stdio.h>
#define spam foo_spam
#define eggs foo_eggs
# include "foo.h"
#undef spam
#undef eggs
#define spam bar_spam
#define eggs bar_eggs
# include "bar.h"
#undef spam
#undef eggs
int main()
{
const int new_bar_spam = 3;
const double new_bar_eggs = 5.0f;
printf("foo: spam = %d, eggs = %f\n", foo_spam(), foo_eggs() );
printf("bar: old spam = %d, new spam = %d ; old eggs = %f, new eggs = %f\n",
bar_spam(new_bar_spam), new_bar_spam,
bar_eggs(new_bar_eggs), new_bar_eggs );
return 0;
}
이걸 정리하면...
example/ex02/ $ make
cc -c -o foobar.o foobar.c
cc foobar.o foo.o bar.o -o foobar
bar.o: In function `spam':
bar.c:(.text+0x0): multiple definition of `spam'
foo.o:foo.c:(.text+0x0): first defined here
bar.o: In function `eggs':
bar.c:(.text+0x1e): multiple definition of `eggs'
foo.o:foo.c:(.text+0x19): first defined here
foobar.o: In function `main':
foobar.c:(.text+0x1e): undefined reference to `foo_eggs'
foobar.c:(.text+0x28): undefined reference to `foo_spam'
foobar.c:(.text+0x4d): undefined reference to `bar_eggs'
foobar.c:(.text+0x5c): undefined reference to `bar_spam'
collect2: ld returned 1 exit status
make: *** [foobar] Error 1
...처음엔 상황이 더 나빠진 것 같아요.하지만 자세히 보세요.사실 편찬 단계는 잘 진행되었습니다.현재 심볼이 충돌하고 있다고 불평하는 링커뿐이며, 이러한 현상이 발생하는 위치(소스 파일 및 라인)를 알려줍니다.보시다시피 이 기호들은 고정되지 않았습니다.
nm 유틸리티를 사용한 심볼 테이블을 살펴보겠습니다.
example/ex02/ $ nm foo.o
0000000000000019 T eggs
0000000000000000 T spam
0000000000000008 C the_eggs
0000000000000004 C the_spams
example/ex02/ $ nm bar.o
0000000000000019 T eggs
0000000000000000 T spam
0000000000000008 C the_eggs
0000000000000004 C the_spams
그래서 우리는 이 기호들을 불투명한 이진법으로 앞에 붙이는 연습을 해야 합니다.예, 이 예에서는 소스를 사용하고 있으며, 이 경우 소스를 변경할 수 있습니다.다만, 현재로서는, 이러한 .o 파일 또는 .a(실제로 .o의 집합일 뿐) 밖에 없다고 가정해 주세요.
구조에 부치다
특히 흥미로운 툴이 하나 있습니다.objcopy
objcopy는 임시 파일로 동작하기 때문에 마치 사내에서 동작하는 것처럼 사용할 수 있습니다.--prefix-symbols 라고 하는 옵션/연산이 1개 있는데, 그 기능을 3개 추측할 수 있습니다.
그럼 이 친구를 우리의 완고한 도서관에 던져보자.
example/ex03/ $ objcopy --prefix-symbols=foo_ foo.o
example/ex03/ $ objcopy --prefix-symbols=bar_ bar.o
nm은 이것이 동작하는 것처럼 보였음을 나타냅니다.
example/ex03/ $ nm foo.o
0000000000000019 T foo_eggs
0000000000000000 T foo_spam
0000000000000008 C foo_the_eggs
0000000000000004 C foo_the_spams
example/ex03/ $ nm bar.o
000000000000001e T bar_eggs
0000000000000000 T bar_spam
0000000000000008 C bar_the_eggs
0000000000000004 C bar_the_spams
이 모든 것을 링크해 보겠습니다.
example/ex03/ $ make
cc foobar.o foo.o bar.o -o foobar
실제로 효과가 있었습니다.
example/ex03/ $ ./foobar
foo: spam = 0, eggs = 0.000000
bar: old spam = 0, new spam = 3 ; old eggs = 0.000000, new eggs = 5.000000
이제 nm을 사용하여 라이브러리의 기호를 자동으로 추출하고 구조의 래퍼 헤더 파일을 쓰는 도구/스크립트를 구현하는 것을 독자에게 연습으로 맡깁니다.
/* wrapper header wrapper_foo.h for foo.h */
#define spam foo_spam
#define eggs foo_eggs
/* ... */
#include <foo.h>
#undef spam
#undef eggs
/* ... */
objcopy를 사용하여 정적 라이브러리의 객체 파일에 기호 접두사를 적용합니다.
공유 라이브러리는 어떻습니까?
원칙적으로 공유 라이브러리에서도 동일한 작업을 수행할 수 있습니다.그러나 공유 라이브러리는 이름 그대로 여러 프로그램 간에 공유되기 때문에 이러한 방식으로 공유 라이브러리를 조작하는 것은 좋은 생각이 아닙니다.
당신은 트램펄린 포장지를 쓸 수 없을 것이다.게다가 오브젝트 파일레벨에서는 공유 라이브러리에 링크 할 수 없지만, 동적인 로드를 강제합니다.하지만 이것은 그 자신의 기사를 쓸 가치가 있다.
채널 고정하시고 해피 코딩.
C 표준의 규칙은 (안전한 컴파일을 위해) 몇 가지 제약을 가한다.C 컴파일러는 식별자의 첫 번째 8글자만 볼 수 있기 때문에 foobar2k_egs와 foobar2k_spam은 같은 식별자로 해석될 수 있습니다.다만, 현대의 컴파일러는 모두 임의의 긴 식별자를 사용할 수 있기 때문에, (21세기에는) 이것에 대해 신경 쓸 필요가 없습니다.
이는 최신 컴파일러의 확장뿐 아니라 현재의 C 표준에서는 컴파일러가 상당히 긴 외부 이름을 지원해야 합니다.정확한 길이는 잊어버렸는데, 제가 기억하기엔 31자 정도 되는 것 같아요.
그러나 기호 이름/아이덴피어를 변경할 수 없는 라이브러리와 마주하고 있다면?정적 바이너리와 헤더만 얻었거나 원하지 않거나 직접 조정 및 재컴파일할 수 없습니다.
하세요.이치노 to to to to to deb deb deb deb i i 、 deb 。libSDL
, 링크libsoundfile
에는) 를 (이러한 이유로) 시켰습니다.dsp
(!)데비안에게 항의했더니, 다시는 문제가 발생했다는 소식을 듣지 못했기 때문에, 그들은 패키지를 수리하고 수정 프로그램을 업스트림으로 보냈습니다.
저는 이것이 정말 최선의 방법이라고 생각합니다.왜냐하면 모든 사람이 이 문제를 해결해주기 때문입니다.로컬 해킹을 하면 다음 번 불행한 사용자가 다시 만나 싸울 수 있도록 문제를 라이브러리에 남겨둡니다.
수정이 출처가 , 할 수 .-Dfoo=crappylib_foo -Dbar=crappylib_bar
메이크 파일않은 경우는, 「」를 해 주세요.objcopy
이치노
GCC를 사용하는 경우 --allow-multiple-definition 링커 스위치는 편리한 디버깅툴입니다이것에 의해, 링커가 최초의 정의를 사용하도록(또, 이것에 대해 불평하지 않는다) 합니다.자세한 것은 이쪽.
이 기능은 개발 중에 벤더 제공 라이브러리의 소스를 사용할 수 있고 어떤 이유로 라이브러리 기능을 추적해야 할 때 도움이 되었습니다.이 스위치를 사용하면 소스 파일의 로컬복사본을 컴파일 및 링크하여 변경되지 않은 스태틱벤더 라이브러리에 링크할 수 있습니다.발견 항해가 완료되면 Make 심볼에서 스위치를 빼는 것을 잊지 마십시오.의도적인 네임스페이스 충돌이 있는 출하 릴리스 코드는 의도하지 않은 네임스페이스 충돌을 포함한 함정에 빠지기 쉽습니다.
언급URL : https://stackoverflow.com/questions/6940384/how-to-deal-with-symbol-collisions-between-statically-linked-libraries
'programing' 카테고리의 다른 글
Java 제네릭스에서 ?와 오브젝트의 차이점은 무엇입니까? (0) | 2022.08.16 |
---|---|
C은 무엇인가"정적"기능을 합니까? (0) | 2022.08.16 |
여러 연결을 할 때 C에서 소켓타임아웃을 설정하려면 어떻게 해야 합니까? (0) | 2022.08.16 |
GCC를 사용하여 읽을 수 있는 어셈블리를 만들고 있습니까? (0) | 2022.08.09 |
vuejs v2.0에서 컴포넌트로 데이터 전달 (0) | 2022.08.09 |