적어도 정적 라이브러리 의 경우 매우 편리하게 해결할 수 있습니다.
라이브러리 foo 및 bar의 헤더를 고려하십시오 . 이 튜토리얼을 위해 소스 파일도 제공합니다.
예 /ex01/foo.h
int spam(void);
double eggs(void);
examples / 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);
examples / 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는 과부하를 알지 못합니다. 따라서 이름은 같지만 서명이 다른 두 개의 함수가 두 번 있습니다. 그래서 우리는 그것들을 구별 할 방법이 필요합니다. 어쨌든 컴파일러가 이것에 대해 무엇을 말해야하는지 봅시다 :
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
라이브러리에서 내 보낸 모든 기호의 접두사를 지정 하는 여러 전 처리기 지시문이 포함됩니다 . 나중에 우리는 멋지고 아늑한 wrapper-header로 이것을 수행하지만, 무슨 일이 일어나고 있는지 보여주기 위해 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
objcopy는 임시 파일에서 작동하므로 제자리에서 작동하는 것처럼 사용할 수 있습니다. --prefix-symbols 라는 하나의 옵션 / 작업이 있으며 그 작업 을 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를 사용하여 라이브러리의 기호를 자동으로 추출 하고 구조의 래퍼 헤더 파일을 작성하는 도구 / 스크립트를 구현하는 독자에게 연습으로 남겨 둡니다.
#define spam foo_spam
#define eggs foo_eggs
#include <foo.h>
#undef spam
#undef eggs
objcopy를 사용하여 심볼 접두사를 정적 라이브러리의 개체 파일에 적용합니다 .
공유 라이브러리는 어떻습니까?
원칙적으로 공유 라이브러리에서도 동일한 작업을 수행 할 수 있습니다. 그러나 이름에서 알 수 있듯이 공유 라이브러리는 여러 프로그램간에 공유되므로 이러한 방식으로 공유 라이브러리를 엉망으로 만드는 것은 좋은 생각이 아닙니다.
트램폴린 래퍼를 작성하지 않을 것입니다. 더 나쁜 경우에는 개체 파일 수준에서 공유 라이브러리에 연결할 수 없지만 동적로드를 수행해야합니다. 그러나 이것은 자체 기사가 필요합니다.
계속 지켜봐 주시고 즐거운 코딩을하세요.