21.3.1. 모듈 엔트리 포인트(Entry Point)의 C 링키지(Linkage) 설계
C++라는 세련된 객체지향 언어를 손에 쥐고 의기양양하게 PX4 커스텀 모듈 코딩을 시작하려는 초보 개발자들의 기를 가장 먼저 꺾어놓는 코드가 있다. 어떤 커스텀 모듈 자습서 소스 코드를 열어보아도, 파일의 가장 하단 부근에 십중팔구 다음과 같이 생긴 기괴하고 투박한 함수 껍데기가 똬리를 틀고 있기 때문이다.
extern "C" __EXPORT int custom_app_main(int argc, char *argv[]);
int custom_app_main(int argc, char *argv[])
{
// C++ 클래스를 생성하고 실행하는 로직...
return 0;
}
왜 1970년대에 발명된 C 언어의 잔재인 extern "C"와, 마치 윈도우(Windows) DLL 파일에서나 볼 법한 __EXPORT 지시자가 21세기 최첨단 드론 펌웨어의 C++ 소스 코드 한가운데에 버젓이 자리 잡고 있는 것일까?
이 불편한 동거는 픽스호크 하드웨어를 지배하고 있는 운영체제, NuttX RTOS의 태생적 한계이자 굳건한 철학 때문이다.
1. C 기반 RTOS 커널과의 대화 채널
PX4 펌웨어의 심장부인 NuttX 커널은 극한의 가벼움과 하드웨어 제어 능력을 위해 뼛속까지 순수 C 언어로 작성되어 있다. 이 커널은 스레드를 생성하고, 메모리를 할당하며, NSH(NuttShell)라는 텍스트 콘솔을 통해 사용자의 명령어 터치보드 입력을 받아들인다.
드론에 전원을 넣고 마이크로 5핀 USB 케이블을 꽂아 NSH 콘솔 창을 띄운 뒤, 우리가 무심코 custom_app start라는 텍스트를 입력하고 엔터를 치는 순간, 커널 내부에서는 다음과 같은 숨 가쁜 번역 과정이 일어난다.
- 명령어 파싱: 커널의 셸(Shell) 파서는 사용자가 입력한 문자열
custom_app을 읽어 들인다. - 심볼 테이블 검색: 커널은 이 문자열 텍스트 뒤에 관습적으로
_main을 붙여custom_app_main이라는 문자열을 만든다. 그리고 펌웨어 전체의 바이너리 심볼 테이블을 이 잡듯이 뒤져서 **정확히custom_app_main이라는 이름을 가진 C 지시어 함수 포인터(Function Pointer)**를 찾는다. - OS 레벨의 콜(Call): 함수 포인터를 찾아냈다면, 커널은 그 메모리 주소로
커맨드라인 인자(argc, argv)들을 던져주며 새로운 스레드(Task)를 스폰(Spawn)시킨다.
이것이 바로 위 코드 블록이 **‘모듈 엔트리 포인트(Entry Point)’**라고 불리는 이유다. 아무리 내부 로직을 화려한 C++ 템플릿과 다형성으로 도배해 두었더라도, 정작 바깥쪽 껍데기에 이 C 언어 규격의 낡은 문(Door)을 달아두지 않으면 커널은 여러분의 모듈을 영원히 찾아내지 못한다.
그렇다면 구체적으로 저 extern "C"는 C++ 컴파일러에게 어떤 극악무도한 짓을 막고 있는 것이며, 굳이 __EXPORT라는 배지를 달아주는 이유는 무엇일까? 컴파일러의 ’네임 맹글링(Name Mangling)’이라는 불쾌한 습성을 알면 그 이유를 명확히 이해할 수 있다. 다음 단원 21.3.1.1.에서 이 지시자들의 바이너리 레벨 역할을 현미경으로 들여다보자.