콘텐츠로 이동

C++ 이름 맹글링과 extern "C"

C++ 컴파일러는 함수 오버로딩과 네임스페이스를 지원하기 위해 함수명에 타입 정보를 추가해 고유한 심볼 이름을 생성합니다. 이를 **이름 맹글링(name mangling)**이라 합니다. C 라이브러리 연동이나 동적 라이브러리 개발 시 반드시 이해해야 합니다.


// 소스 코드
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&)

Terminal window
# 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 "

// 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;
}

// mylib.h — C와 C++ 모두에서 사용 가능
#ifdef __cplusplus
extern "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
}
#endif

C++에서는 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);

#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);
}

컴파일러ABI맹글링 규칙
GCC/Clang (Linux)Itanium ABI_Z 접두사
MSVC (Windows)Microsoft ABI? 접두사
GCC (MinGW)Itanium ABI_Z 접두사

MSVC와 GCC는 C++ 심볼 호환이 안 되므로 크로스 컴파일러 라이브러리는 extern "C" 인터페이스를 사용해야 합니다.


// 템플릿 인스턴스화는 더 복잡한 맹글링을 생성
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}; // v2
mylib::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