GCC 공유 라이브러리 심벌 문제 (Draft)

@codemaru · April 04, 2014 · 3 min read

몇해전에 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

GCC                 Draft  md 0

결과를 보면 libb.so의 BarB에서 호출하는 Foo가 libb.so에 있는 Foo가 아닌 liba.so에 Foo로 링크된 것을 알 수 있다. 티끌만한 것도 찾아서 공유하는 정신이 GNU 스럽다고 할 수 있겠지만 심벌 이름이 충돌 나는 경우에는 여간 성가신 문제가 아닐 수 없다. 특히나 boost 같이 우리가 컴파일 하는 경우에는 -Dboost=boost2같은 꼼수라도 쓰겠지만 컴파일 할 수 없는 심벌인 경우에는 문제가 많아진다.

물론 GNU 애들도 바보가 아닌 이상 이런 문제가 굉장히 성가시다는 걸 몰랐을 리는 없다. 그래서 만들어진 -Wl,-Bsymbolic 이라는 마법같은 옵션이 있다. so 파일을 링크할 때 저 옵션을 넣어주고 링크하면 so 파일 내의 심벌을 먼저 참조하도록 설정된다.

참고할만한 글

http://bottomupcs.sourceforge.net/csbu/x4012.htm

@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중