카테고리:

4 분 소요

문제 상황

RHEL 7.9에서 사용하던 C++ 프로젝트를 RHEL 8.10로 이전하는 과정에서 기존에는 발생하지 않던 컴파일 오류와 링커 오류가 발생했다.

원인은 GCC 버전 업그레이드에 따른 C++ 표준 변경으로 인한 이름 충돌과 ABI 변경으로 인한 링커 오류이었다.

문제 해결

요약하자면, 기존 소스코드의 수정 범위를 최소화하기 위해 다음 두 가지 컴파일 옵션을 적용했다.

CXXFLAGS += -std=gnu++98
CXXFLAGS += -D_GLIBCXX_USE_CXX11_ABI=0

C++ 표준 변경으로 인한 이름 충돌

GCC 4.8.5에서는 C++98이 기본이지만, GCC 8.5에서는 C++14가 기본으로 적용된다.

이 때문에 기존 프로젝트에서 정의한 is_empty와 C++11부터 제공되는 std::is_empty가 충돌하여 다음과 같은 오류가 발생한다.

error: reference to 'is_empty' is ambiguous

해결하기 위해서는 Makefile에 아래와 같은 옵션을 추가하면 된다.

CXXFLAGS += -std=gnu++98

일반적인 표준 라이브러리 헤더에서는 C++11 이후의 기능이 노출되지 않으므로 기존 사용자 정의 is_empty와의 충돌을 피할 수 있다.

다만 <type_traits>처럼 C++11 이상을 요구하는 헤더를 직접 포함하고 있다면 해당 헤더 자체가 C++98 환경에서 컴파일되지 않을 수 있다.

또한 실제로 C++11 이상의 언어 기능을 사용하는 프로젝트라면 C++98로 되돌리는 대신 사용자 정의 템플릿의 네임스페이스를 명시하거나 이름을 변경해야 한다.

C++ ABI 변경으로 인한 링커 오류

GCC 5.1부터는 libstdc++에 새로운 C++11 ABI가 도입되었다. 기존 ABI와의 호환성을 유지하기 위해 libstdc++는 두 가지 ABI를 동시에 지원하는 Dual ABI 구조를 제공한다. 대표적으로 std::string, std::list 등의 구현이 변경되었다.

기존 GCC 4.8.5로 컴파일한 라이브러리와 GCC 8.5로 컴파일한 소스를 연결하면 ABI 불일치로 링커 오류가 발생할 수 있다.

undefined reference to GetName\[abi:cxx11\]()

해결하기 위해서는 Makefile에 아래와 같은 옵션을 추가하면 된다.

CXXFLAGS += -D_GLIBCXX_USE_CXX11_ABI=0

주의할 점은 C++ 언어 표준과 libstdc++ ABI는 서로 독립적으로 적용된다는 것이다. 따라서 -std=gnu++98로 컴파일하더라도 ABI가 자동으로 Old ABI로 변경되는 것은 아니다.

또한 Old ABI를 선택한다고 GCC 4.8.5와 모든 바이너리 호환성이 보장되는 것도 아니다. 서로 다른 컴파일러로 빌드된 라이브러리 사이에서 C++ 객체를 주고받는 경우 추가적인 ABI 검증이 필요하다

마무리

GCC 4.8.5에서 GCC 8.5로 마이그레이션할 때 발생하는 오류가 반드시 기존 소스코드의 문제는 아니다. 컴파일러 기본 설정과 표준 라이브러리 ABI의 변경이 원인일 수 있다.

이런 경우에는 컴파일러에 적절한 옵션을 적용하면 기존 소스코드를 대규모로 수정하지 않고도 신규 컴파일러 환경으로 이전하는 데 도움이 된다.

다만 이러한 설정은 레거시 호환성을 위한 조치이며, 모든 소스코드와 라이브러리의 호환성을 보장하지는 않는다. 특히 정의되지 않은 동작이나 메모리 관리 오류는 컴파일 옵션과 무관하게 별도로 수정해야 한다.

태그: ABI, C++, G++, GCC, GCC 4.8.5, GCC 8.5, libstdc++, Linux, Makefile, RHEL, RHEL8, 마이그레이션

업데이트:

정보의 바다를 헤엄치는 수달의 마음으로

syudal 이미지 프로필