C++ 이름 맹글링과 extern "C"
C++ 컴파일러는 함수 오버로딩과 네임스페이스를 지원하기 위해 함수명에 타입 정보를 추가해 고유한 심볼 이름을 생성합니다. 이를 **이름 맹글링(name mangling)**이라 합니다. C 라이브러리 연동이나 동적 라이브러리 개발 시 반드시 이해해야 합니다.
1. 이름 맹글링 예시
섹션 제목: “1. 이름 맹글링 예시”// 소스 코드namespace Math { int add(int a, int b); double add(double a, double b); void process(const std::string& s);}
// 컴파일 후 심볼 (GCC, Itanium ABI 기준)// _ZN4Math3addEii → Math::add(int, int)// _ZN4Math3addEdd → Math::add(double, double)// _ZN4Math7processERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE// → Math::process(const std::string&)2. 심볼 확인 도구
섹션 제목: “2. 심볼 확인 도구”# nm으로 오브젝트 파일 심볼 확인nm -C libmylib.a # -C: 디맹글링(demangling)
# objdump로 심볼 덤프objdump -t -C myapp.o
# c++filt으로 맹글링된 이름 디맹글링echo "_ZN4Math3addEii" | c++filt# 출력: Math::add(int, int)
# readelf로 동적 심볼 확인readelf -Ws libmylib.so | grep " T "3. extern “C” — C 링키지 선언
섹션 제목: “3. extern “C” — C 링키지 선언”// C++ 함수를 C에서 호출 가능하게extern "C" { int add(int a, int b); // 맹글링 없음 → 심볼: "add" void process(const char* s);}
// 구현extern "C" int add(int a, int b) { return a + b;}4. C/C++ 헤더 공용 패턴
섹션 제목: “4. C/C++ 헤더 공용 패턴”// mylib.h — C와 C++ 모두에서 사용 가능#ifdef __cplusplusextern "C" {#endif
typedef struct MyHandle MyHandle;
MyHandle* mylib_create(void);void mylib_destroy(MyHandle* h);int mylib_process(MyHandle* h, const char* data, int len);
#ifdef __cplusplus}#endifC++에서는 extern "C" 블록이 적용되고, C 컴파일러에서는 무시됩니다.
5. 동적 라이브러리 심볼 가시성
섹션 제목: “5. 동적 라이브러리 심볼 가시성”// GCC/Clang — 심볼 가시성 제어#define EXPORT __attribute__((visibility("default")))#define HIDDEN __attribute__((visibility("hidden")))
EXPORT int public_api(); // .so 외부에서 접근 가능HIDDEN int internal_impl(); // .so 내부에서만 접근
// CMake 설정// target_compile_options(mylib PRIVATE -fvisibility=hidden)// → 기본 hidden, EXPORT만 노출Windows DLL:
#ifdef _WIN32 #ifdef BUILDING_MYLIB #define MYLIB_API __declspec(dllexport) #else #define MYLIB_API __declspec(dllimport) #endif#else #define MYLIB_API __attribute__((visibility("default")))#endif
MYLIB_API int my_function(int x);6. dlopen/dlsym으로 동적 로드
섹션 제목: “6. dlopen/dlsym으로 동적 로드”#include <dlfcn.h>
void load_plugin(const char* path){ void* handle = dlopen(path, RTLD_LAZY); if (!handle) { std::cerr << dlerror() << '\n'; return; }
// extern "C" 함수만 이름으로 로드 가능 using CreateFn = Plugin*(*)(); auto create = reinterpret_cast<CreateFn>(dlsym(handle, "create_plugin"));
if (auto* err = dlerror()) { std::cerr << "심볼 없음: " << err << '\n'; dlclose(handle); return; }
Plugin* plugin = create(); plugin->run();
dlclose(handle);}7. 컴파일러별 ABI 차이
섹션 제목: “7. 컴파일러별 ABI 차이”| 컴파일러 | ABI | 맹글링 규칙 |
|---|---|---|
| GCC/Clang (Linux) | Itanium ABI | _Z 접두사 |
| MSVC (Windows) | Microsoft ABI | ? 접두사 |
| GCC (MinGW) | Itanium ABI | _Z 접두사 |
MSVC와 GCC는 C++ 심볼 호환이 안 되므로 크로스 컴파일러 라이브러리는 extern "C" 인터페이스를 사용해야 합니다.
8. 이름 맹글링과 템플릿
섹션 제목: “8. 이름 맹글링과 템플릿”// 템플릿 인스턴스화는 더 복잡한 맹글링을 생성template<typename T>T add(T a, T b) { return a + b; }
// 인스턴스: add<int>(int, int)// 맹글링 (GCC): _Z3addIiET_S0_S0_// _Z = C++ 심볼 접두사// 3add = 함수명 (길이 3)// I = 템플릿 인수 시작// i = int// E = 템플릿 인수 끝// T_ = 반환타입 (첫 번째 템플릿 파라미터)// S0_ = 첫 번째 인수 (T의 별칭)// S0_ = 두 번째 인수 (T의 별칭)
// 외부에서 템플릿 인스턴스를 직접 참조하는 것은 이식성 없음// → extern "C" 래퍼를 제공하는 것이 올바른 방법extern "C" int add_int(int a, int b) { return add<int>(a, b); }9. 인라인 네임스페이스와 ABI 버전 관리
섹션 제목: “9. 인라인 네임스페이스와 ABI 버전 관리”// 인라인 네임스페이스로 ABI 버전 관리namespace mylib { inline namespace v2 { // v2가 기본값 struct Config { int version; int extra_field; // v2에서 추가된 필드 }; void process(const Config& cfg); }
namespace v1 { struct Config { int version; }; void process(const Config& cfg); }}
// 사용자는 mylib::Config로 접근 → v2 자동 선택// 구버전 코드는 mylib::v1::Config로 명시적 접근mylib::Config cfg{2, 42}; // v2mylib::v1::Config old_cfg{1}; // v1 명시
// 링커 심볼:// _ZN5mylib2v27processERKNS0_6ConfigE (v2)// _ZN5mylib2v17processERKNS0_6ConfigE (v1)// → 같은 바이너리에 두 버전 공존 가능이름 맹글링은 오버로딩과 네임스페이스를 링커 수준에서 구현하는 메커니즘입니다. C 상호운용이나 동적 라이브러리 공개 API는 extern "C"로 맹글링을 비활성화하고, __attribute__((visibility("hidden")))으로 내부 심볼을 숨겨 .so 파일 크기와 로딩 시간을 줄이세요.
| 상황 | 해결책 |
|---|---|
| C 라이브러리 연동 | extern "C" 래퍼 |
| 크로스 컴파일러 라이브러리 | extern "C" C 스타일 인터페이스 |
| .so 심볼 노출 최소화 | -fvisibility=hidden + EXPORT 매크로 |
| ABI 버전 관리 | inline namespace v2 패턴 |
| 맹글링 디버깅 | c++filt, nm -C, objdump -t -C |