21.2.2.1.1. MODULE 속성과 바이너리 심볼 테이블(Symbol Table) 네이밍 룰의 상관관계
px4_add_module() 매크로의 첫 번째 줄을 장식하는 파라미터는 바로 모듈의 호적상 이름을 결정짓는 MODULE 속성이다.
px4_add_module(
MODULE modules__custom_app
MAIN custom_app
# ...
)
초보 개발자들은 자신의 폴더 이름이 custom_app이니까 으레 MODULE custom_app이라고 적어 넣는 실수를 범하곤 한다. 이렇게 적어도 빌드가 당장 터지지는 않지만, 나중에 도저히 원인을 알 수 없는 링커(Linker) 에러나 C++ 전역 변수 충돌(Global Variable Clash)을 마주하게 된다. 왜 그럴까?
이 매크로 속성 뒤에 숨겨진 PX4 빌드 시스템의 **‘심볼 테이블 네이밍 규칙(Symbol Table Naming Convention)’**을 이해해야만 그 해답이 풀린다.
1. 플랫 메모리와 다중 정의(Multiple Definition)의 공포
이전 단원(21.1.1.1.1)에서 살펴본 바와 같이, PX4가 구동되는 NuttX RTOS 환경은 거대한 하나의 ’플랫 펌웨어(Flat Firmware)’이다. 리눅스 환경의 독립 실행 파일(.exe, .elf) 수백 개가 굴러다니는 것이 아니라, 수백 개의 모듈이 단일한 전역 심볼 테이블(Global Symbol Table) 통 하나에 모든 함수 포인터와 전역 변수 이름표를 때려 넣고 링크된다.
만약 드라이버 계층의 src/drivers/camera 모듈과 응용 계층의 src/modules/camera 모듈이 의사소통 없이 각자의 CMakeLists.txt 파일에 똑같이 MODULE camera라고 선언했다고 가정해 보자.
컴파일러가 목적 파일(.obj)들을 모아 펌웨어(.px4)로 빚어내는 링킹 단계에서, 링커(ld)는 camera라는 이름의 초기화 함수(예: camera_main)를 두 개나 발견하게 된다. 결국 multiple definition of 'camera_main'이라는 치명적인 충돌 에러가 발생하며 릴레이 빌드 라인이 올스톱(All-stop)된다.
2. 경로명(Path)을 강제하는 언더스코어(__) 네이밍 룰
이 거대한 하나의 도가니 속에서 이름 충돌(Naming Clash)을 피하기 위해, PX4 빌드 시스템은 MODULE 이름에 **‘모듈의 물리적 디렉터리 경로’**를 접두어(Prefix)로 강제하는 독특한 네이밍 룰을 도입했다.
- 디렉터리 구분자 매핑: 소스 트리의 폴더 경로(
/)를 두 개의 언더스코어(__)로 치환하여 모듈의 고유 이름(Unique Identifier)을 만들어낸다. - 예시 1:
src/modules/custom_app폴더에 위치한 모듈이라면, 무조건MODULE modules__custom_app이라고 지어야 한다. - 예시 2:
src/drivers/distance_sensor/tfmini폴더에 위치한 드라이버라면,MODULE drivers__distance_sensor__tfmini가 올바른 이름이다.
2.1 네이밍 룰이 링킹 최적화(LTO)에 미치는 영향
이 고유한 MODULE 이름은 단순히 인간이 읽기 편하라고 강제하는 것이 아니다. CMake 스크립트 내부에서 이 MODULE 속성 문자열은 곧바로 정적 라이브러리의 파일명(.a 파일명)으로 둔갑한다. 즉 modules__custom_app은 컴파일 후 libmodules__custom_app.a라는 라이브러리로 구워져 build/ 디렉터리에 곱게 쌓이게 된다.
최종적으로 빌드 시스템이 흩어진 100여 개의 라이브러리 파일들을 타겟 보드 설정 파일(예: boards/px4/fmu-v6x/default.px4board)의 CONFIG_MODULES_CUSTOM_APP=y 리스트와 대조하여 체크할 때, 바로 이 MODULE 이름과 Kconfig 변수명이 1:1로 매핑되는지 검증을 거친다.
따라서 이 네이밍 룰을 어긋나게 작성하면, 최악의 경우 Kconfig 메뉴에서는 체크박스를 켰음에도 불구하고 링커가 해당 라이브러리를 최종 펌웨어에 포함시키는 것을 누락해 버리는 ‘조용한 죽음(Silent Drop)’ 버그를 겪게 된다.
펌웨어가 여러분의 모듈을 부르는 이름표(MODULE)를 정확히 새겼다면, 이제 다음 21.2.2.1.2 단원에서는 그 모듈이 펌웨어 운영체제 위에서 얼마나 거대한 방 크기(STACK_MAIN)를 차지할지 할당받는 런타임 결정 로직을 다루어보자.