Build(빌드)/CMake
[CMake] 플러그인 빌드 구조 만들기 (--export-dynamic 활용)
2026. 5. 14. 02:11반응형
CMake 환경에서 플러그인 빌드 구조를 만들기 위한 방법을 소개한다.
예시로 설명한 플러그인 구조 및 요구사항은 아래와 같다.
- main.cpp -> plugin.cpp의 .so파일을 load한다.
- plugin.cpp -> 공유라이브러리로 구현되어 산출물은 .so 파일이다.
- 플러그인이 main.cpp 측에 구현된 C++ 클래스를 사용이 가능하도록 구성한다.
1. 프로젝트 구조
.
├── CMakeLists.txt
├── Log.h # main.cpp와 plugin.cpp가 공유하는 인터페이스
├── main.cpp # 실행 파일 (구현체 포함)
└── plugin.cpp # 공유 라이브러리
2. 소스코드
Log.h
#pragma once
#include <iostream>
class Log {
public:
// 구현은 main.cpp에만 둘 예정
void print();
};
main.cpp
실행 파일 (main)이 Log::print의 실체를 가지고 있으며, 플러그인 (plugin.cpp)을 로드한다.
#include <iostream>
#include <dlfcn.h>
#include "Log.h"
// Log 클래스의 구현부 - 실행 파일 A에 위치함
void Log::print() {
std::cout << "Hello World from A's Log class!" << std::endl;
}
int main() {
// 플러그인 B(libpluginB.so)를 로드
void* handle = dlopen("./libpluginB.so", RTLD_NOW);
if (!handle) {
std::cerr << "Cannot open library: " << dlerror() << std::endl;
return 1;
}
// 플러그인 내부의 'run_plugin' 함수 호출
typedef void (*run_func)();
run_func run = (run_func)dlsym(handle, "run_plugin");
if (run) {
run();
} else {
std::cerr << "Cannot load symbol 'run_plugin': " << dlerror() << std::endl;
}
dlclose(handle);
return 0;
}
plugin.cpp (공유 라이브러리)
이 plugin은 Log.h만 알고 있고, 구현체가 없지만 Log 객체를 사용한다.
#include "Log.h"
extern "C" void run_plugin() {
Log logger;
// 플러그인에는 print()의 구현이 없지만, 실행 시 main쪽의 심볼을 찾아서 호출한다.
logger.print();
}
CMakeLists.txt
여기서 --export-dynamic 설정이 핵심이다.
cmake_minimum_required(VERSION 3.10)
project(ExportDynamicTest)
# 1. 실행 파일 A 생성
add_executable(AppA main.cpp)
# --export-dynamic 옵션 부여 (A의 심볼을 외부에 노출)
target_link_options(AppA PRIVATE "-Wl,--export-dynamic")
# dlopen 기능을 위해 dl 라이브러리 링크 (리눅스)
target_link_libraries(AppA PRIVATE ${CMAKE_DL_LIBS})
# 2. 공유 라이브러리 B 생성
# 주의: 여기서는 Log.cpp(구현부)를 절대 추가하지 않습니다.
add_library(pluginB SHARED plugin.cpp)
# B는 링크 시점에 Log::print 심볼이 없어도 무시하도록 설정 (Undefined symbols)
# 하지만 보통 Linux 링커는 공유 라이브러리 빌드 시
# 정의되지 않은 심볼이 있어도 기본적으로 허용합니다.
3. 테스트 및 결과
빌드
rm -rf build
mkdir build && cd build
cmake ..
make
실행
./AppA
결과
Hello World from A's Log class!
4. 고찰
만약 AppA의 target_link_options에서 -Wl,--export-dynamic을 제거하고 다시 빌드하면 어떻게 될까?
- AppA는 문제없이 실행되지만, dlopen으로 pluginB를 로드하는 순간 다음과 같은 에러가 발생하며 중단된다.
- undefined symbol: _ZN3Log5printEv (Log::print()의 망글링된 이름)
이는 pluginB가 자신의 메모리 안에서 Log::print를 찾지 못했고, 메인 프로그램(AppA)이 그 심볼을 공개하지 않았기 때문에 발생하는 현상이다. 따라서 --export-dynamic은 "내 안에 구현된 클래스 기능을 너(플러그인)에게 공유해줄게"라는 선언과 같다.
반응형
'Build(빌드) > CMake' 카테고리의 다른 글
| [CMake] 플러그인 빌드 구조 만들기 - 예제 2. 정적라이브러리 심볼 활용 (0) | 2026.05.18 |
|---|---|
| [CMake] target_include_directories와 target_link_libraries 개념 (0) | 2026.05.11 |
| [CMake] CMake 명령어 순서에 대한 고찰 (0) | 2026.05.11 |
| [CMake] find_package와 pkg_check_modules(PkgConfig)의 차이에 대한 고찰 (0) | 2026.04.16 |
| [CMake] Module(모듈) (0) | 2025.12.03 |