몇해전에 boost의 ABI와 관련된 글을 적은 일이 있다. http://www.jiniya.net/wp/archives/7543
그때 우리는 문제를 -Dboost=boost2로 해결을 했는데 최근에 동일한 문제가 발생했다. 이번엔 boost 같은 외부 라이브러리가 아닌 libstdc++.so라는 G++ 공유 라이브러리가 문제였다. 대다수 업체는 libstdc++.so.6을 사용하는데 한 업체에서 아주 오래된 libstdc++.so.5를 사용하면서 문제가 생긴 것이다.
libstdc++.so.5와 libstdc++.so.6은 ABI가 호환되지 않는다. http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html
그래서 이번에 이 문제에 대한 근본적인 원인에 대해서 파헤쳐 보게 되었다. 근본적인 원인은 boost 글에도 있는 것처럼 심벌을 다른 so 파일에서 찾는 것이 문제다. 간단한 예제를 만들어서 테스트 해 보자.
a.cpp 코드
#include <stdio.h>
extern void Foo()
{
printf("Foo in liba.so\n");
}
extern void BarA()
{
Foo();
printf("BarA in liba.so\n");
}
b.cpp 코드
#include <stdio.h>
extern void Foo()
{
printf("Foo in libb.so\n");
}
extern void BarB()
{
Foo();
printf("BarB in libb.so\n");
}
main.cpp 코드
extern void BarA();
extern void BarB();
int main()
{
BarA();
BarB();
return 0;
}
이제 아래와 같이 컴파일을 해보자
g++ -shared -o liba.so a.cpp
g++ -shared -o libb.so b.cpp
g++ -L. -o main main.cpp -la -lb
결과를 보면 libb.so의 BarB에서 호출하는 Foo가 libb.so에 있는 Foo가 아닌 liba.so에 Foo로 링크된 것을 알 수 있다. 티끌만한 것도 찾아서 공유하는 정신이 GNU 스럽다고 할 수 있겠지만 심벌 이름이 충돌 나는 경우에는 여간 성가신 문제가 아닐 수 없다. 특히나 boost 같이 우리가 컴파일 하는 경우에는 -Dboost=boost2같은 꼼수라도 쓰겠지만 컴파일 할 수 없는 심벌인 경우에는 문제가 많아진다.
물론 GNU 애들도 바보가 아닌 이상 이런 문제가 굉장히 성가시다는 걸 몰랐을 리는 없다. 그래서 만들어진 -Wl,-Bsymbolic 이라는 마법같은 옵션이 있다. so 파일을 링크할 때 저 옵션을 넣어주고 링크하면 so 파일 내의 심벌을 먼저 참조하도록 설정된다.
참고할만한 글