Tips2018. 4. 27. 09:21

보통 make 혹은 cmake 등으로 빌드를 수행할 때, undefined reference 에러가 발생하는 경우가 있을 것이다. 

필요한 라이브러리를 -l 옵션을 사용하여 링커에게 전달하지 않았거나, 라이브러리 간의 order가 맞지 않을 때 발생하는 문제이다. 예를 들어 pthread를 사용한다면, 다음과 같이 옵션을 주면 된다.

$ gcc a.c -o a -lpthread

코드가 pthread를 사용하여 구현되었을 경우, 해당 실행 파일(여기서는 a) 내에는 pthread관련 함수의 바이너리가 없기 때문에 실제 코드가 구현된 pthread 라이브러리를 링크시켜주어야 한다. 이 역할을 수행하는 것이 바로 링커(리눅스에서는 보통 ld)이며, 링커에게 링크 해야하는 라이브러리를 알려주는 지시자가 바로 -lpthread 가 되겠다.

물론, 이걸로 해결안되는 경우도 많다. 사실 이 경우 때문에 이 포스팅을 남기기도 하는 것이고.

내가 작성한 라이브러리를 사용하여 다른 애플리케이션을 수정할 일이 생겼다. 그래서 애플리케이션 코드를 수정하고, cmake 스크립트를 수정하여 라이브러리를 링크하도록 만들어 놓고 빌드를 수행하였는데, undefined reference 에러가 발생하는게 아닌가?

여러 삽질을 해봤는데, 결과는 영 꽝이었고, 혹시나 싶어 nm을 통해 코드에서 심볼들이 어떻게 정의되어 있는지 보았다. 애플리케이션도 라이브러리로 컴파일 된 뒤, 다른 실행 파일에서 이를 링크하여 사용하는 방식으로 되어 있기 때문에, 여기서는 애플리케이션이 작성한 라이브러리를 확인하였다.

$ nm app.a | c++filt | grep pthread

                 U pthread_cond_broadcast

                 U pthread_cond_init

                 U pthread_cond_signal

                 U pthread_cond_wait

                 ...

$ nm app.a | c++filt | grep my_lib_func

                 U my_lib_func1(my_struct*)

                 U my_lib_func2(unsigned long, my_struct*, int)

                 U my_lib_func3(unsigned long, my_struct*)


차이가 눈에 띄는가?

pthread 함수의 경우, argument에 대한 정의가 심볼에 포함되어 있지 않지만, 내가 작성한 라이브러리의 경우 심볼에 argument의 정의가 포함되어 있다.

이런 이유가 발생한 이유는 따지고 보면 간단한데(물론 찾는 것은 간단하지 않았다.), 나는 C언어를 사용하여 라이브러리를 작성하였고, 애플리케이션은 C++로 구현되어 있기 때문이었다. 

C++는 오버로딩과 같은 특성을 지원하기 때문에, 심볼을 다룰 때 간결한 C언어와 차이점을 보이게 되는 듯 하다. 

해결책은 간단하다. 

헤더파일의 선언문들을 extern "C" { } 로 감싸주면된다. 물론, C++의 경우에만 이를 적용하면 되므로 #ifdef __cplusplus와 같은 매크로를 이용해 구분을 해주면 더욱 좋겠다.,

이렇게 헤더파일을 수정할 경우, 실행파일의 심볼 테이블에서 argument에 대한 정의가 제거된다. 

$ nm app.a | c++filt | grep my_lib_func

                 U my_lib_func1

                 U my_lib_func2

                 U my_lib_func3

빌드해본 결과, 에러없이 잘 빌드되는 것을 확인할 수 있었다.

Posted by 곰푼