[C++] GCC 4.8.5에서 GCC 8.5로 마이그레이션 시 C++ 컴파일 호환성 문제 해결하기
카테고리: C++
문제 상황
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의 변경이 원인일 수 있다.
이런 경우에는 컴파일러에 적절한 옵션을 적용하면 기존 소스코드를 대규모로 수정하지 않고도 신규 컴파일러 환경으로 이전하는 데 도움이 된다.
다만 이러한 설정은 레거시 호환성을 위한 조치이며, 모든 소스코드와 라이브러리의 호환성을 보장하지는 않는다. 특히 정의되지 않은 동작이나 메모리 관리 오류는 컴파일 옵션과 무관하게 별도로 수정해야 한다.
정보의 바다를 헤엄치는 수달의 마음으로