임베디드 리눅스 빌드 시스템(Build System) (OpenWrt · Buildroot · Yocto)

임베디드 리눅스 개발의 핵심인 OpenWrt, Buildroot, Yocto/OpenEmbedded 3대 빌드 시스템을 종합 비교합니다. 각 시스템의 빌드 파이프라인(Pipeline), 패키지 인프라, 펌웨어(Firmware) 이미지 구조, 크로스 툴체인 전략, Device Tree 통합, BSP 개발 방법론, SDK 활용, 디버깅(Debugging) 워크플로, 실전 유스케이스까지 임베디드 빌드 시스템의 모든 것을 다룹니다.

전제 조건: LFS (크로스 컴파일)빌드 시스템, Device Tree 문서를 먼저 읽으세요. 임베디드 빌드 시스템은 크로스 컴파일러 구성, Makefile/Kconfig, 그리고 Device Tree 이해가 필수적입니다.
일상 비유: 임베디드 빌드 시스템은 가구 조립 키트와 비슷합니다. OpenWrt는 라우터 전문 조립 키트(부품이 미리 선별됨), Buildroot는 미니멀리즘 DIY 키트(필요한 것만 빠르게), Yocto는 산업용 자동화 생산 라인(복잡하지만 대량 생산에 최적)입니다.
핵심 용어 한눈에 보기
  • 임베디드 리눅스(Embedded Linux) — 라우터·카메라·산업용 단말 등 특수 목적 하드웨어에 올리는 리눅스. 데스크탑 대비 RAM·플래시가 수십~수백 배 제한되어 필요한 것만 포함한 최소 이미지를 만들어야 합니다.
  • 크로스 컴파일(Cross Compile) — x86 PC(호스트)에서 ARM·MIPS 등 다른 CPU(타겟)용 바이너리를 생성하는 것. 타겟 장치에서 직접 빌드하면 수십 배 느리거나 불가능하기 때문입니다.
  • 툴체인(Toolchain) — 크로스 컴파일러(GCC), 링커(Linker, binutils), C 라이브러리(musl/glibc)를 묶은 도구 세트. 호스트에서 타겟 바이너리를 만드는 핵심 도구입니다.
  • sysroot — 타겟 환경의 헤더·라이브러리를 호스트에 미리 복사해 둔 디렉토리. 크로스 컴파일 시 컴파일러가 이 경로를 참조합니다.
  • rootfs (루트 파일시스템) — 타겟에서 실행되는 /bin, /etc, /lib 등 전체 파일 트리. 빌드 시스템이 최종적으로 조합해 이미지로 패키징합니다.
  • 펌웨어 이미지(Firmware Image) — 부트로더(Bootloader)+커널+rootfs를 플래시 메모리 레이아웃에 맞게 하나로 묶은 바이너리. 장치에 직접 플래시(flash)합니다.
  • 피드(Feed) — OpenWrt에서 외부 패키지 저장소를 가리키는 용어. git 저장소 형태로 관리되며, feeds update/install로 로컬에 가져옵니다.
  • 레시피(Recipe, .bb) — Yocto에서 패키지 하나를 빌드하는 방법(소스 URL·컴파일 옵션·설치 규칙)을 기술한 파일. Buildroot의 .mk와 동일한 역할입니다.
  • 레이어(Layer) — Yocto에서 관련 레시피·설정을 묶는 단위 디렉토리. BSP 레이어, 기능 레이어, 배포판 레이어를 쌓아(stack) 맞춤 배포판을 구성합니다.
  • BSP (Board Support Package) — 특정 보드를 지원하는 부트로더·커널 설정·Device Tree·드라이버 묶음. 새 하드웨어를 지원할 때 가장 먼저 작성합니다.

핵심 요약

  • OpenWrt — 라우터/네트워크 장비 특화 배포판+빌드 시스템. opkg 패키지 매니저, LuCI, UCI를 기본 제공합니다.
  • Buildroot — Make+Kconfig 기반 간결한 빌드 프레임워크. 최소 수 MB 수준의 정적 이미지 생성에 강점입니다.
  • Yocto / OpenEmbedded — BitBake 엔진과 레이어 아키텍처로 엔터프라이즈·벤더 BSP 통합에 적합합니다.
  • 크로스 컴파일 — x86_64 호스트에서 ARM/MIPS 등 타겟 바이너리를 생성하는 공통 기반 기술입니다.
  • BSP(Board Support Package) — 부트로더, 커널, Device Tree, 드라이버 묶음으로 특정 보드를 지원합니다.

단계별 이해

  1. 왜 크로스 컴파일인가
    타겟은 리소스가 제한적이므로 호스트에서 크로스 툴체인으로 빌드해 시간을 크게 단축합니다.
  2. 빌드 시스템이 자동화하는 것
    툴체인 구축, 수백 개 패키지 크로스 컴파일, 루트 파일시스템(Filesystem)·커널·펌웨어 이미지 생성까지 이어집니다.
  3. 세 시스템의 선택 기준
    네트워크 장비는 OpenWrt, 최소 정적 이미지는 Buildroot, 대규모 벤더 BSP는 Yocto가 기본 출발점입니다.
  4. defconfig로 첫 빌드
    각 시스템의 보드별 defconfig/Target Profile을 선택해 menuconfig로 최소 변경 후 병렬 빌드를 돌립니다.
  5. 산출물과 배포
    커널 이미지, 루트 파일시스템, 부트로더, 패키지 피드를 묶어 플래시 가능한 펌웨어 이미지로 패키징하는 절차를 확인합니다.

3대 빌드 시스템 개요 및 비교

왜 임베디드 전용 빌드 시스템이 필요한가

데스크탑 리눅스에서는 apt install 한 줄로 소프트웨어를 설치할 수 있습니다. 그런데 임베디드 개발에서는 왜 OpenWrt·Buildroot·Yocto 같은 전용 빌드 시스템이 필요할까요? 세 가지 근본 이유가 있습니다.

문제데스크탑 방식의 한계임베디드 빌드 시스템의 해결
아키텍처 불일치 x86 PC에서 만든 바이너리는 ARM·MIPS 장치에서 실행 불가합니다. 크로스 툴체인을 자동으로 구축해 타겟 CPU용 바이너리를 생성합니다.
리소스 제약 데스크탑 패키지는 수십~수백 MB, 임베디드 플래시는 4~32 MB 수준입니다. 필요한 기능만 선택(menuconfig)해 수 MB짜리 최소 이미지를 만듭니다.
재현성·통합 수백 개 패키지 간 버전 조합을 수동으로 맞추기 어렵습니다. 전체 소스(툴체인~커널~애플리케이션)를 한 번의 make로 재현 가능하게 빌드합니다.

크로스 컴파일이란

임베디드 빌드 시스템이 공통으로 사용하는 핵심 기술입니다. 세 가지 환경을 구분해야 합니다.

환경예시역할
빌드(Build)x86_64 Ubuntu컴파일러 자체를 빌드하는 환경 (보통 호스트와 같음)
호스트(Host)x86_64 Ubuntu컴파일러가 실행되는 환경 — 개발자의 PC
타겟(Target)arm-linux-gnueabihf최종 바이너리가 실행될 환경 — 임베디드 장치
툴체인 트리플렛(Triple): 크로스 컴파일러 이름에는 arch-vendor-os-abi 형태의 트리플렛이 붙습니다. 예를 들어 arm-linux-gnueabihf-gcc는 "ARM CPU, Linux OS, GNU ABI, 하드웨어 부동소수점" 타겟용 GCC를 의미합니다. 빌드 시스템은 이 툴체인을 자동으로 구축하거나 외부 것을 가져다 씁니다.

역사와 기원

세 시스템은 서로 다른 배경에서 탄생하여 고유한 철학과 강점을 발전시켰습니다.

역사 타임라인 비교

연도OpenWrtBuildrootYocto/OE
2000uClibc 빌드 인프라 시작
2003독립 프로젝트화OpenEmbedded 프로젝트 시작
2004WRT54G GPL 소스 기반 탄생
2006Kamikaze (첫 공식 릴리스)BitBake 엔진 안정화
2009Peter Korsgaard 메인테이너
2010Backfire분기별 릴리스 시작Yocto Project 출범 (Linux Foundation)
2012Attitude Adjustment1.0 Bernard → 1.2 Danny
2016LEDE 프로젝트 분기Cargo/Go 인프라2.1 Krogoth
2018LEDE와 OpenWrt 재통합 (18.06)2.5 Sumo
202019.07 → DSA 스위치 지원per-package-directories3.1 Dunfell (첫 LTS)
202222.03 (Firewall4/nftables)Meson 인프라 강화4.0 Kirkstone (LTS, 2026-04-27 지원 종료)
202423.05 LTS (7월)2024.02/05/08/11 분기별 릴리스5.0 Scarthgap LTS (4월, 2028-04까지 지원)
2025-0224.10 첫 안정 릴리스 (커널 6.6, WiFi 7 초기 지원, MPTCP)2025.02/05/08/11 — SBOM(CycloneDX) 고도화, PEP 517 Python 지원5.1 Styhead (10월, 비-LTS)
2026-0224.10.x 유지2026.02 — Binutils 2.44 기본, 커널 헤더 6.19 기본, HPPA 아키텍처 추가5.2 Walnascar (4월)·5.3 Whinlatter (개발 중)
2026-0325.12 첫 안정 릴리스 (25.12.0: 2026-03-05, 25.12.2: 2026-03-26)
2026년 4월 기준 최신 릴리스:
  • OpenWrt 25.12 계열 — 2026년 4월 기준 최신 안정 시리즈는 25.12이며, 25.12.2가 최신 릴리스입니다(2026-03-26 공개). 이전 24.10 계열(24.10.0: 2025-02-06)은 유지보수 단계입니다.
  • Buildroot 2026.02 (2026-03-05) — 커널 헤더 6.19.x 기본(6.17.x 제거), Binutils 2.44 기본·2.45 시리즈 추가. HPPA 아키텍처 추가·ARC 빅엔디안 제거. utils/generate-cyclonedx 개선(CVE 패치 annotation), AMD Versal2 VEK385·HP-9000·QEMU HPPA B160L·STM32h747i-disco defconfig 신규. 97명 컨트리뷰터, 1,500+ 변경.
  • Yocto 5.2 Walnascar (2025-04) — 비-LTS. gcc 15, glibc 2.41, 커널 6.12 계열 LTS 기본. systemd 257, Python 3.13 전면 채택.
  • Yocto 5.0 Scarthgap LTS (2024-04) — 2028-04까지 지원(4년). gcc 13.3, glibc 2.39, 커널 6.6 LTS. 장기 상용 제품 타깃.
  • Yocto 4.0 Kirkstone LTS (2022-05) — 현재 공개된 일정 기준 지원 종료일은 2026-04-27입니다. 마이그레이션 경로는 Scarthgap LTS(5.0) 또는 이후 LTS 계열을 검토하는 편이 안전합니다.

철학 비교

관점OpenWrtBuildrootYocto
설계 목표네트워크 어플라이언스 OS최소한의 임베디드 시스템유연한 엔터프라이즈 빌드
핵심 원칙라우터에 완전한 리눅스를단순하게 유지하라재사용성과 확장성
빌드 도구Make + KconfigMake + KconfigBitBake + OE metadata
패키지 매니저opkg (런타임 설치 가능)없음 (정적 이미지)rpm/deb/ipk (선택)
라이선스GPL-2.0GPL-2.0+MIT (Poky/BitBake)
주요 사용처라우터, AP, 스위치, NASIoT, 산업용 제어기, STB자동차, 항공, 의료, 반도체 벤더 SDK
3대 임베디드 빌드 시스템 비교 개요 OpenWrt 2004~ | GPL-2.0 라우터 · 네트워크 어플라이언스 빌드 도구: Make + Kconfig 패키지: opkg (IPK) C 라이브러리: musl (기본) 설정: menuconfig 이미지: SquashFS + JFFS2 웹 UI: LuCI 네트워크: UCI 추상화 Init: procd (경량) IPC: ubus (JSON-RPC) 빌드 시간: ~1시간 이미지: 4~16 MB 학습 곡선: 중간 호스트 디스크: ~15 GB 릴리스: ~12개월 주기 커뮤니티: 활발 (GitHub) 패키지 5,000+ · 타겟 1,500+ Buildroot 2001~ | GPL-2.0+ 미니멀 임베디드 시스템 빌드 도구: Make + Kconfig 패키지: 없음 (정적 이미지) C 라이브러리: musl/glibc/uclibc 설정: defconfig + menuconfig 이미지: ext4/squashfs/cpio 외부 트리: BR2_EXTERNAL 시각화: graph-depends Init: BusyBox/systemd/sysv 법적: make legal-info 빌드 시간: ~30분 이미지: 2~8 MB 학습 곡선: 낮음 호스트 디스크: ~5 GB 릴리스: 분기별 (2/5/8/11) 커뮤니티: 안정적 (메일링 리스트) 패키지 2,800+ · defconfig 200+ Yocto 2010~ | MIT 엔터프라이즈 · 산업용 빌드 도구: BitBake + OE 패키지: rpm/deb/ipk (선택) C 라이브러리: glibc/musl 설정: .conf + .bb recipes 이미지: 다양 (wic/ext4/...) 레이어: meta-* 아키텍처 캐시: sstate-cache Init: systemd/sysvinit SDK: Standard/Extensible 빌드 시간: 2~4시간 이미지: 설정에 따라 다양 학습 곡선: 높음 호스트 디스크: ~50 GB+ 릴리스: 6개월 (LTS 2년) 커뮤니티: 기업 주도 + 커뮤니티 레이어 400+ · 벤더 공식 BSP
그림 1. 3대 임베디드 빌드 시스템 비교 개요 — OpenWrt(파랑), Buildroot(초록), Yocto(보라)

상세 비교표

항목OpenWrtBuildrootYocto
첫 빌드 시간~1시간~30분2~4시간
재빌드 시간수 분 (패키지 단위)수 분 (패키지 단위)수 초~분 (sstate-cache)
최소 이미지 크기~4 MB~2 MB~8 MB
런타임 패키지 관리opkg (필수)없음rpm/deb/ipk (선택)
기본 initprocdBusyBox init/systemd/sysvinitsystemd/sysvinit
기본 C 라이브러리musl선택 (musl/glibc/uclibc-ng)glibc (기본)
설정 방식Kconfig (menuconfig)Kconfig (menuconfig).conf 파일 + BitBake
패키지 수5,000+2,800+수만 (레이어 합산)
SDK 제공SDK + Image BuilderSDK (output/host)Standard/Extensible SDK
CI/CD 친화성중간높음 (단순)매우 높음 (sstate, hash equiv.)
상업적 지원커뮤니티 중심커뮤니티 중심Wind River, Mentor, Siemens 등
주요 벤더라우터/AP 제조사STMicro, MicrochipIntel, TI, NXP, AMD, Qualcomm
호스트 디스크~15 GB~5 GB~50 GB+
문서 품질Wiki 중심, 양호매뉴얼 우수방대 (Mega Manual)

라이선스 컴플라이언스 워크플로

임베디드 제품의 GPL/LGPL 소스 코드 공개 의무를 처리하는 방법은 시스템마다 다릅니다:

시스템라이선스 수집 명령출력물비고
OpenWrtmake package/xxx/compile 시 자동bin/packages/에 소스 포함 가능CONFIG_SRC_TREE_OVERRIDE로 소스 보존
Buildrootmake legal-infooutput/legal-info/ — 라이선스, 소스 아카이브, 매니페스트 CSV가장 체계적; 호스트/타겟 분리, 재배포 소스 자동 수집
YoctoINHERIT += "archiver" in local.conftmp/deploy/sources/, tmp/deploy/licenses/ARCHIVER_MODE 옵션으로 패치(Patch) 포함 원본/수정 소스 선택

커뮤니티 지표 비교

지표OpenWrtBuildrootYocto
GitHub Stars~20K~3K~3K (Poky)
연간 커밋 수~8,000~3,000~5,000 (OE-Core)
활성 기여자~300명/년~200명/년~400명/년
메일링 리스트중간 활성높은 활성매우 높은 활성
IRC/Discord#openwrt (Libera)#buildroot (OFTC)#yocto (Libera)
릴리스 주기~12개월3개월6개월
버전 기준: OpenWrt, Buildroot, Yocto의 최신 stable / LTS 상태는 자주 바뀌므로 실제 작업 전 공식 다운로드·지원 페이지(Page)를 먼저 확인하세요. 본문 일부 실습 예시는 호환성과 마이그레이션 설명을 위해 OpenWrt 23.05/24.10, Buildroot 2024.02, Yocto 5.0 계열 사례를 함께 유지합니다.

OpenWrt 빌드 시스템 아키텍처

OpenWrt란 무엇인가

OpenWrt는 임베디드 리눅스 배포판인 동시에 빌드 시스템입니다. 이 이중 성격이 Buildroot·Yocto와 구별되는 핵심입니다.

성격의미구체적 예시
배포판(Distribution) 완성된 라우터 OS. 설치하면 바로 동작합니다. ipq806x 라우터에 OpenWrt 이미지를 플래시하면 즉시 웹 관리 화면(LuCI)이 뜹니다.
빌드 시스템 커스텀 이미지를 처음부터 직접 빌드할 수 있습니다. 소스를 받아 make menuconfigmake로 자신만의 펌웨어를 만듭니다.
피드(Feed)가 왜 별도로 존재하는가: OpenWrt 핵심 저장소에는 네트워크 관련 필수 패키지만 포함됩니다. Samba, Python, Node.js 같은 선택적 소프트웨어는 별도 피드 저장소에서 관리합니다. feeds update -a && feeds install -a로 이 패키지들을 로컬에 가져온 뒤 menuconfig에서 선택할 수 있게 됩니다. 핵심 저장소를 작고 빠르게 유지하면서도 수천 개의 추가 패키지를 지원하기 위한 설계입니다.

OpenWrt의 소스 트리는 라우터/네트워크 장비에 특화된 계층 구조를 가집니다. 핵심은 target/(하드웨어 지원), package/(소프트웨어 패키지), feeds/(외부 패키지 저장소) 3개 디렉토리입니다.

OpenWrt 소스 트리 아키텍처 openwrt/ target/ package/ feeds/ toolchain/ tools/ include/ scripts/ linux/ ath79/ ramips/ mediatek/ x86/ target/linux/ath79/ generic/ — subtarget 공용 설정 config-6.6 — 커널 .config patches-6.6/ — 커널 패치 dts/ — Device Tree 소스 feeds.conf.default src-git packages ... src-git luci ... src-git routing ... src-git telephony ... 빌드 생성 디렉토리 build_dir/ — 소스 압축 해제 및 빌드 staging_dir/ — 크로스 툴체인+sysroot dl/ — 다운로드 캐시 bin/ — 최종 펌웨어 이미지 tmp/ — 임시 파일, .config toolchain/ binutils/ — 어셈블러, 링커 gcc/ — 크로스 컴파일러 musl/ — C 라이브러리 (기본) 하드웨어 타겟 패키지 피드 툴체인/빌드
그림 2. OpenWrt 소스 트리 아키텍처 — target/(하드웨어), package/(소프트웨어), feeds/(외부 저장소)가 핵심

주요 디렉토리 역할

디렉토리역할핵심 파일
target/linux/타겟 플랫폼별 커널 설정, 패치, DTSconfig-6.6, patches-6.6/, dts/
target/sdk/SDK 이미지 생성 규칙Makefile
package/핵심 패키지 Makefile (base-files, kernel, network 등)package/*/Makefile
feeds/외부 패키지 소스 (feeds install 후 심볼릭 링크)feeds.conf.default
toolchain/크로스 컴파일러 빌드 (GCC, Binutils, musl/glibc)toolchain/gcc/, toolchain/musl/
tools/호스트 빌드 도구 (m4, autoconf, cmake, sstrip 등)각 도구별 Makefile
include/빌드 시스템 공통 Makefile 인프라package.mk, image.mk, kernel.mk
scripts/빌드 유틸리티 스크립트feeds, config/, download.pl

target/subtarget/profile 계층 구조

OpenWrt는 3단계 계층으로 하드웨어를 분류합니다:

  1. Target — SoC 패밀리 (예: ath79 = Qualcomm Atheros AR7xxx/AR9xxx)
  2. Subtarget — SoC 변형 (예: generic, nand, tiny)
  3. Profile — 개별 보드/제품 (예: tplink_tl-wr1043nd-v1)
# target/linux/ath79/ 디렉토리 구조 예시
target/linux/ath79/
├── Makefile                  # 타겟 정의, BOARDNAME, FEATURES
├── config-6.6               # 커널 .config (subtarget 공용)
├── generic/                  # subtarget: generic
│   ├── config-default        # subtarget 기본 설정
│   └── target.mk             # DEVICE_PACKAGES 등
├── nand/                     # subtarget: nand (NAND 플래시 장비)
├── dts/                      # Device Tree 소스 (.dts/.dtsi)
│   ├── ar9344_tplink_tl-wr1043nd-v2.dts
│   └── qca9558_tplink_archer-c7-v2.dts
├── files/                    # 커널 소스에 추가할 파일
├── patches-6.6/              # 커널 패치 (순서번호_설명.patch)
│   ├── 0001-ath79-add-xxx.patch
│   └── 0002-ath79-fix-xxx.patch
└── image/                    # 이미지 생성 규칙
    └── Makefile              # DEVICE_xxx 매크로로 프로파일 정의

UCI/procd/netifd/ubus 서브시스템

OpenWrt가 일반 임베디드 리눅스와 구별되는 핵심은 자체 서브시스템 스택입니다. UCI(Unified Configuration Interface)는 /etc/config/ 디렉토리의 구조화된 설정 파일을 관리하며, procd는 systemd 대신 사용하는 경량 init/서비스 매니저, netifd는 네트워크 인터페이스를 추상화하는 데몬, ubus는 시스템 내 IPC(프로세스(Process) 간 통신)를 담당하는 JSON-RPC 버스(Bus)입니다.

OpenWrt UCI/procd/netifd 아키텍처 사용자 인터페이스 계층 LuCI Web UI CLI (uci 명령) rpcd (JSON-RPC) 커스텀 스크립트 UCI 설정 계층 (/etc/config/) network wireless firewall dhcp system ubus (마이크로 버스 — JSON-RPC IPC) procd (init/서비스 관리) PID 1 | 서비스 감시 | 핫플러그 프로세스 재시작 | jail(샌드박스) netifd (네트워크 데몬) 인터페이스/프로토콜 관리 bridge, VLAN, PPPoE, DHCP 기타 데몬 dnsmasq, fw4(nftables) hostapd, uhttpd, logd Linux Kernel 프로세스 관리 Netlink / Netfilter nl80211 (WiFi) DSA (Switch) procd vs systemd: procd는 ~200KB(systemd ~10MB), JSON 서비스 정의, 의존성 해결 단순화, 4MB 플래시에서도 동작
그림 13. OpenWrt UCI/procd/netifd 아키텍처 — LuCI/CLI → UCI 설정 → ubus IPC → procd/netifd → 커널

UCI 시스템 상세

# UCI 설정 파일 구조 (/etc/config/network 예시)
config interface 'loopback'
    option device 'lo'
    option proto 'static'
    option ipaddr '127.0.0.1'
    option netmask '255.0.0.0'

config interface 'lan'
    option device 'br-lan'
    option proto 'static'
    option ipaddr '192.168.1.1'
    option netmask '255.255.255.0'

config interface 'wan'
    option device 'eth0.2'
    option proto 'dhcp'

# UCI CLI 명령
uci show network                     # 전체 설정 보기
uci get network.lan.ipaddr           # 특정 값 읽기
uci set network.lan.ipaddr='10.0.0.1' # 값 변경
uci commit network                   # 변경사항 저장
/etc/init.d/network restart          # 서비스 재시작으로 적용

# procd 서비스 관리
/etc/init.d/dnsmasq start|stop|restart|enable|disable
service list                         # 실행 중인 서비스 목록 (JSON)

procd vs systemd 비교

항목procd (OpenWrt)systemd (일반 리눅스)
바이너리 크기~200 KB~10 MB
서비스 정의Shell 스크립트 + JSON.service unit 파일
의존성단순 (시작 순서 번호)복잡 (Wants, Requires, After)
프로세스 감시respawn 지원Restart=on-failure 등 다양
샌드박스(Sandbox)procd jail (seccomp, namespace)다양한 보안 옵션
핫플러그(Hotplug)내장 (hotplug.d/)udev + systemd
최소 RAM~1 MB~30 MB

staging_dir 구조

staging_dir/은 OpenWrt 빌드 시스템의 핵심 중간 산출물 디렉토리입니다:

staging_dir/
├── host/                     # 호스트 빌드 도구 (cmake, m4, pkg-config 등)
├── hostpkg/                  # 호스트 패키지
├── toolchain-mips_24kc_gcc-13.3.0_musl/   # 크로스 툴체인
│   ├── bin/                  # mips-openwrt-linux-musl-gcc 등
│   ├── include/              # 헤더 파일
│   └── lib/                  # 크로스 컴파일된 라이브러리
└── target-mips_24kc_musl/    # 타겟 sysroot
    ├── usr/include/          # 타겟 헤더
    ├── usr/lib/              # 타겟 라이브러리 (.so, .a)
    └── pkgconfig/            # pkg-config .pc 파일

OpenWrt 빌드 과정

OpenWrt 빌드 파이프라인 feeds update -a 외부 저장소 동기화 feeds install -a 심볼릭 링크 생성 make menuconfig 타겟/패키지 선택 make -j$(nproc) V=s 전체 빌드 실행 1. Host Tools 2. Toolchain 3. Kernel 4. Packages 5. Image m4, autoconf, cmake sstrip, mkimage → staging_dir/host/ binutils, gcc, musl kernel headers → staging_dir/toolchain-*/ config-* 기반 .config patches-* 적용 → build_dir/target-*/linux-*/ 의존성 순서대로 크로스 컴파일 → IPK 패키지 생성 rootfs 조합 SquashFS 생성 → bin/targets/ bin/targets/ath79/generic/ *-sysupgrade.bin | *-factory.bin | *-initramfs.bin packages/ (IPK 저장소) | sha256sums
그림 3. OpenWrt 빌드 파이프라인 — feeds → menuconfig → host tools → toolchain → kernel → packages → firmware image

빌드 단계 상세

Step 1: 피드 업데이트 및 설치

# feeds.conf.default 내용 확인
cat feeds.conf.default
# src-git packages https://git.openwrt.org/feed/packages.git;openwrt-23.05
# src-git luci https://git.openwrt.org/project/luci.git;openwrt-23.05
# src-git routing https://git.openwrt.org/feed/routing.git;openwrt-23.05
# src-git telephony https://git.openwrt.org/feed/telephony.git;openwrt-23.05

# 모든 피드 업데이트 (git clone/pull)
./scripts/feeds update -a

# 모든 피드 패키지를 빌드 트리에 심볼릭 링크
./scripts/feeds install -a

# 특정 패키지만 설치
./scripts/feeds install luci-app-openvpn

Step 2: 설정 (menuconfig)

# ncurses 기반 메뉴 설정
make menuconfig

# 설정 항목:
# Target System → (예: MediaTek Ralink MIPS)
# Subtarget → (예: MT7621 based boards)
# Target Profile → (예: Xiaomi Mi Router 4A Gigabit)
# [*] Base system → base-files, busybox, ...
# [*] LuCI → Collections → luci
# [M] Network → VPN → openvpn-openssl    # M=모듈(IPK 생성)

# 커널 설정 확인/수정
make kernel_menuconfig

Step 3: 빌드 실행

# 전체 빌드 (상세 로그, CPU 코어 수 병렬)
make -j$(nproc) V=s

# 다운로드만 먼저 실행 (빌드 서버에서 유용)
make download -j8

# 특정 패키지만 재빌드
make package/network/services/dnsmasq/compile V=s

# 특정 패키지 정리 후 재빌드
make package/network/services/dnsmasq/{clean,compile} V=s

# 펌웨어 이미지만 재생성 (패키지 변경 없이)
make target/install V=s

# ccache 활성화 (재빌드 가속)
echo "CONFIG_CCACHE=y" >> .config
make defconfig

커널 모듈(Kernel Module) 컴파일

OpenWrt에서 커널 모듈은 kmod-* 패키지로 관리됩니다. 커널 모듈 패키지는 커널 버전과 정확히 일치해야 합니다:

# 커널 모듈 패키지 빌드 (kmod-usb-net 예시)
make package/kernel/linux/compile V=s

# 커널 모듈 패키지 정의 위치
package/kernel/linux/modules/
├── block.mk         # kmod-loop, kmod-nbd 등
├── crypto.mk        # kmod-crypto-aes 등
├── netdevices.mk    # kmod-usb-net, kmod-tun 등
├── netsupport.mk    # kmod-ipsec, kmod-wireguard 등
└── usb.mk           # kmod-usb-core, kmod-usb-storage 등

# 모듈 이름 패턴: kmod-<기능명>
# 예: kmod-usb-storage, kmod-wireguard, kmod-nft-nat

다운로드 미러 및 재현 가능 빌드

# 다운로드 미러 설정 (.config)
CONFIG_DOWNLOAD_FOLDER="/shared/dl"           # 공유 다운로드 캐시
CONFIG_LOCALMIRROR="https://mirror.example.com/openwrt/dl"  # 로컬 미러

# 또는 환경변수로 지정
export DL_DIR=/shared/dl

# 재현 가능 빌드를 위한 체크리스트
# 1. .config 파일 버전 관리
# 2. feeds.conf.default에 커밋 해시 고정
#    src-git packages https://git.openwrt.org/feed/packages.git^abc1234
# 3. dl/ 디렉토리 보존 (다운로드 소스 아카이브)
# 4. 호스트 도구 버전 통일 (Docker 권장)

빌드 오류 디버깅

# 단일 스레드로 상세 빌드 (오류 위치 확인)
make -j1 V=s 2>&1 | tee build.log

# 특정 패키지만 빌드 (의존성 문제 격리)
make package/xxx/compile V=s

# 빌드 디렉토리에서 직접 확인
ls build_dir/target-mips_24kc_musl/xxx-*/

# .config 변경 반영
make defconfig    # 의존성 자동 해결
make oldconfig    # 대화형 질문
빌드 가속 팁:
  • CONFIG_CCACHE=y — 컴파일러 캐시로 재빌드 시간 대폭 단축
  • CONFIG_DOWNLOAD_FOLDER — 공유 다운로드 캐시 디렉토리 지정
  • make download -j8 — 소스 다운로드를 먼저 병렬 실행
  • tmpfs에 build_dir/ 배치 — I/O 병목(Bottleneck) 해소 (16GB+ RAM 필요)
  • CONFIG_AUTOREMOVE=y — 빌드 완료된 패키지의 빌드 디렉토리 자동 삭제로 디스크 절약

OpenWrt 패키지 시스템

OpenWrt의 패키지 시스템은 다른 임베디드 빌드 시스템과 차별화되는 핵심 기능입니다. 런타임 패키지 설치/제거가 가능한 opkg 패키지 매니저와 IPK 포맷을 사용합니다.

OpenWrt 패키지 Makefile 구조 package/xxx/Makefile PKG_NAME:=hello PKG_VERSION:=1.0 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_HASH:=sha256... define Package/hello SECTION:=utils CATEGORY:=Utilities DEPENDS:=+libc +libpthread define Build/Compile $(MAKE) -C $(PKG_BUILD_DIR) ... endef define Package/hello/install $(INSTALL_BIN) ... $(1)/usr/bin/hello endef $(eval $(call BuildPackage,hello)) 소스 다운로드 dl/ → PKG_SOURCE 크로스 컴파일 build_dir/target-*/ 스테이징 설치 staging_dir/target-*/ IPK 패키지 생성 bin/packages/*/ hello_1.0-1_mips.ipk control.tar.gz - control (메타데이터) - preinst/postinst data.tar.gz - usr/bin/hello
그림 4. OpenWrt 패키지 Makefile 구조 — PKG_ 변수 → Build/Compile → Package/install → IPK 출력

패키지 Makefile 예제: Hello World

# package/hello/Makefile
include $(TOPDIR)/rules.mk

PKG_NAME:=hello
PKG_VERSION:=1.0
PKG_RELEASE:=1

PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://example.com/
PKG_HASH:=skip

include $(INCLUDE_DIR)/package.mk

define Package/hello
  SECTION:=utils
  CATEGORY:=Utilities
  TITLE:=Hello World Example
  DEPENDS:=+libc
  MAINTAINER:=Developer <dev@example.com>
endef

define Package/hello/description
  A simple Hello World package for OpenWrt.
endef

define Build/Compile
  $(TARGET_CC) $(TARGET_CFLAGS) $(TARGET_LDFLAGS) \
    -o $(PKG_BUILD_DIR)/hello $(PKG_BUILD_DIR)/hello.c
endef

define Package/hello/install
  $(INSTALL_DIR) $(1)/usr/bin
  $(INSTALL_BIN) $(PKG_BUILD_DIR)/hello $(1)/usr/bin/hello
endef

$(eval $(call BuildPackage,hello))

커널 모듈 패키지 패턴 (kmod-*)

OpenWrt의 커널 모듈은 독립 패키지로 분리되어 런타임 설치가 가능합니다:

# package/kernel/linux/modules/netdevices.mk (발췌)
define KernelPackage/usb-net
  SUBMENU:=$(USB_MENU)
  TITLE:=Kernel modules for USB-to-Ethernet
  DEPENDS:=+kmod-mii +kmod-usb-core
  KCONFIG:=CONFIG_USB_USBNET
  FILES:=$(LINUX_DIR)/drivers/net/usb/usbnet.ko
  AUTOLOAD:=$(call AutoLoad,61,usbnet)
endef

# 사용자가 menuconfig에서 kmod-usb-net을 선택하면
# → 커널 CONFIG_USB_USBNET=m 자동 설정
# → usbnet.ko 빌드 → IPK 생성
# → 타겟에서 opkg install kmod-usb-net 가능

LuCI 앱 패키지 패턴

# package/luci-app-myvpn/Makefile
include $(TOPDIR)/rules.mk

LUCI_TITLE:=LuCI support for MyVPN
LUCI_DEPENDS:=+myvpn +luci-base

include $(TOPDIR)/feeds/luci/luci.mk

# 필요한 것은 이것뿐 — luci.mk가 나머지를 자동 처리
$(eval $(call BuildPackage,luci-app-myvpn))

UCI 설정 통합 패턴

패키지가 UCI 설정을 사용하는 경우, 설치 시 기본 설정 파일을 제공합니다:

# Package/hello/install에 UCI 설정 포함
define Package/hello/install
  $(INSTALL_DIR) $(1)/usr/bin
  $(INSTALL_BIN) $(PKG_BUILD_DIR)/hello $(1)/usr/bin/hello
  $(INSTALL_DIR) $(1)/etc/config
  $(INSTALL_CONF) ./files/hello.conf $(1)/etc/config/hello
  $(INSTALL_DIR) $(1)/etc/init.d
  $(INSTALL_BIN) ./files/hello.init $(1)/etc/init.d/hello
endef

# files/hello.conf (UCI 설정 파일)
# config hello 'main'
#     option enabled '1'
#     option port '8080'

# files/hello.init (procd 서비스 스크립트)
# #!/bin/sh /etc/rc.common
# START=99
# USE_PROCD=1
# start_service() {
#     procd_open_instance
#     procd_set_param command /usr/bin/hello
#     procd_set_param respawn
#     procd_close_instance
# }
OpenWrt 네트워크 구성 추상화 (UCI → netifd → kernel) /etc/config/network (UCI 설정) interface 'lan' device 'br-lan', proto 'static' interface 'wan' device 'eth0.2', proto 'dhcp' device 'br-lan' type 'bridge', ports 'eth0.1' 'wlan0' uci commit → /etc/init.d/network restart netifd (Network Interface Daemon) UCI 설정 파싱 → 프로토콜 핸들러 실행 → 커널 인터페이스 생성 Bridge 생성 ip link add br-lan type bridge ip link set eth0.1 master br-lan VLAN 생성 ip link add eth0.1 link eth0 type vlan id 1 ip link add eth0.2 link eth0 type vlan id 2 IP 설정 ip addr add 192.168.1.1/24 dev br-lan udhcpc -i eth0.2 (WAN DHCP) 최종 네트워크 토폴로지 WAN(eth0.2/DHCP) ↔ NAT/Firewall ↔ LAN(br-lan: eth0.1+wlan0, 192.168.1.0/24)
그림 15. OpenWrt 네트워크 구성 추상화 — UCI 설정 → netifd → bridge/VLAN/IP 커널 설정

opkg 패키지 매니저

# 패키지 목록 업데이트
opkg update

# 패키지 설치
opkg install luci-app-openvpn

# 패키지 제거
opkg remove luci-app-openvpn

# 설치된 패키지 목록
opkg list-installed

# 패키지 정보 조회
opkg info dnsmasq

# 패키지가 제공하는 파일 확인
opkg files dnsmasq

# 의존성 확인
opkg depends luci

가상 패키지와 패키지 변형

OpenWrt는 같은 기능의 대안 패키지를 가상 패키지(virtual package)로 관리합니다:

# 예: OpenSSL vs wolfSSL — 둘 다 "libssl" 가상 패키지를 제공
# package/libs/openssl/Makefile
define Package/libopenssl
  PROVIDES:=libssl
endef

# package/libs/wolfssl/Makefile
define Package/libwolfssl
  PROVIDES:=libssl
endef

# 의존하는 패키지는 +libssl로 선언 → menuconfig에서 구현체 선택

opkg 오버레이(Overlay)와 플래시 공간 관리

# 플래시 사용량 확인
df -h /overlay
# Filesystem    Size  Used  Avail  Use%  Mounted on
# /dev/mtdblock6  3.5M  1.2M  2.3M   34%  /overlay

# 패키지 설치 전 공간 확인
opkg install --size luci-app-statistics
# Installing luci-app-statistics (40-r23456-abc1234) to root...
# Installing collectd (5.12.0-r23456) to root... (120 KB 필요)

# USB/SD로 opkg overlay 확장 (extroot)
# 1. USB 포맷: mkfs.ext4 /dev/sda1
# 2. /etc/config/fstab에 마운트 설정:
#    config mount
#        option target '/overlay'
#        option device '/dev/sda1'
#        option enabled '1'
# 3. 기존 overlay 복사: tar -C /overlay -cvf - . | tar -C /mnt/sda1 -xf -
# 4. reboot

피드(Feed) 저장소 구조

# /etc/opkg/distfeeds.conf (타겟 장비에서)
src/gz openwrt_core https://downloads.openwrt.org/releases/23.05.4/targets/ath79/generic/packages
src/gz openwrt_base https://downloads.openwrt.org/releases/23.05.4/packages/mips_24kc/base
src/gz openwrt_luci https://downloads.openwrt.org/releases/23.05.4/packages/mips_24kc/luci
src/gz openwrt_packages https://downloads.openwrt.org/releases/23.05.4/packages/mips_24kc/packages

# 저장소 디렉토리 구조
# packages/
# ├── Packages.gz          # 패키지 인덱스 (압축)
# ├── Packages.sig         # 서명
# ├── hello_1.0-1_mips_24kc.ipk
# └── ...
IPK vs DEB vs RPM: OpenWrt의 IPK 형식은 Debian의 DEB와 유사한 ar 아카이브 구조입니다. 경량화에 초점을 맞추어 메타데이터가 최소화되어 있으며, opkg 패키지 매니저도 dpkg에 비해 메모리/디스크 사용량이 매우 적습니다. 4MB 플래시 장비에서도 동작합니다.

OpenWrt 펌웨어 이미지 구조

OpenWrt 펌웨어의 핵심은 SquashFS + JFFS2 오버레이 아키텍처입니다. 읽기 전용(Read-Only) SquashFS 위에 쓰기 가능한 JFFS2 오버레이를 결합하여, 최소 플래시 사용으로 설정 변경과 패키지 설치를 지원합니다.

OpenWrt 플래시 파티션 레이아웃 NOR Flash 레이아웃 (일반 라우터) U-Boot 128~256 KB Env 64K Kernel (uImage/FIT) 1.5~4 MB RootFS (SquashFS, 읽기 전용) 2~10 MB Overlay (JFFS2) 나머지 공간, 쓰기 가능 OverlayFS 동작 원리 Lower Layer: SquashFS (읽기 전용) /rom — 원본 루트 파일시스템 Upper Layer: JFFS2 (쓰기 가능) /overlay — 변경/추가된 파일 / (OverlayFS 마운트) NAND Flash 레이아웃 (UBI) U-Boot + Env raw 파티션 UBI Vol: kernel UBI 볼륨 (wear leveling) UBI Vol: rootfs (SquashFS) 읽기 전용, UBI 볼륨 UBI Vol: rootfs_data (UBIFS) 쓰기 가능, overlay
그림 5. OpenWrt 플래시 파티션 레이아웃 — NOR(SquashFS+JFFS2 overlay)와 NAND(UBI 볼륨) 비교

이미지 유형

이미지 유형파일명 패턴용도
sysupgrade.bin*-sysupgrade.bin기존 OpenWrt에서 업그레이드용. 설정 보존 가능
factory.bin*-factory.bin제조사 펌웨어에서 최초 설치용. 벤더 헤더 포함
initramfs*-initramfs-kernel.binRAM 디스크 부팅. 디버깅/복구용 (플래시 미사용)
rootfs.img*-squashfs-rootfs.img.gzSquashFS 루트 이미지만 (별도 커널 필요)
포맷매직 넘버용도구조
TRXHDR0Broadcom 라우터 (레거시)헤더(28B) + kernel + rootfs [+ 추가 파티션]
uImage27 05 19 56U-Boot 레거시 이미지64B 헤더 + 압축 커널 (로드 주소/엔트리 포인트 포함)
FIT (ITB)FDT 구조현대 장비 (권장)Device Tree 기반 — 다중 커널/DTB/initrd, 서명 지원
UBIUBI#NAND 장비UBI 볼륨 이미지 — wear leveling, bad block 관리 포함

FIT (Flattened Image Tree) 포맷 상세

최신 OpenWrt 타겟은 FIT 이미지를 사용합니다. FIT는 Device Tree 구조를 이용하여 커널, DTB, initrd를 하나의 서명 가능한 이미지로 결합합니다:

/* FIT 이미지 구조 (.its 파일) */
/dts-v1/;
/ {
    description = "OpenWrt FIT Image";
    images {
        kernel-1 {
            data = /incbin/("zImage");
            type = "kernel";
            arch = "arm";
            compression = "none";
            hash-1 { algo = "sha256"; };
        };
        fdt-1 {
            data = /incbin/("my-board.dtb");
            type = "flat_dt";
            arch = "arm";
            hash-1 { algo = "sha256"; };
        };
    };
    configurations {
        default = "config-1";
        config-1 {
            kernel = "kernel-1";
            fdt = "fdt-1";
            signature-1 {
                algo = "sha256,rsa2048";
                key-name-hint = "dev";
            };
        };
    };
};

eMMC 장비 레이아웃

최신 고성능 라우터(MediaTek MT7986, Qualcomm IPQ807x 등)는 eMMC를 사용합니다:

# eMMC 파티션 레이아웃 (GPT)
# /dev/mmcblk0p1  boot0     — BL2 (ARM Trusted Firmware stage 1)
# /dev/mmcblk0p2  boot1     — FIP (BL31 + U-Boot)
# /dev/mmcblk0p3  env       — U-Boot 환경변수
# /dev/mmcblk0p4  factory   — 칼리브레이션 데이터 (WiFi, MAC)
# /dev/mmcblk0p5  kernel    — FIT 이미지 (커널 + DTB)
# /dev/mmcblk0p6  rootfs    — SquashFS 루트 파일시스템
# /dev/mmcblk0p7  rootfs_data — ext4/f2fs overlay (쓰기 가능)

# 이미지 크기 최적화 팁
# 1. 불필요한 커널 모듈 제거 (kmod-* 최소화)
# 2. LuCI 대신 CLI만 사용 시 ~1MB 절약
# 3. SquashFS XZ 압축으로 30-50% 절약 (기본 활성화)
# 4. strip 레벨: CONFIG_USE_SSTRIP=y (초공격적 strip)

sysupgrade 메커니즘

# 펌웨어 업그레이드 (설정 보존)
sysupgrade -v /tmp/firmware-sysupgrade.bin

# 설정 초기화하며 업그레이드
sysupgrade -n /tmp/firmware-sysupgrade.bin

# 업그레이드 전 설정 백업
sysupgrade -b /tmp/backup.tar.gz

# 복구: firstboot (overlay 초기화)
firstboot -y && reboot

# Failsafe 모드: 부팅 시 버튼 누르기 → 192.168.1.1:22 접속
# overlay를 마운트하지 않아 /rom만으로 부팅
관련 문서: SquashFS 상세는 SquashFS 문서, JFFS2 상세는 JFFS2 문서를 참고하세요.

Buildroot 아키텍처

Buildroot가 실제로 무엇을 해주는가

Buildroot는 "처음부터 끝까지 한 번의 make로"라는 철학의 빌드 자동화 프레임워크입니다. 아래 다섯 단계를 자동으로 수행합니다.

단계Buildroot가 하는 일출력 위치
① 소스 다운로드패키지별 tarball·git을 dl/에 캐시합니다.dl/
② 툴체인 구축GCC·binutils·C 라이브러리를 타겟 아키텍처용으로 빌드합니다.output/host/
③ 패키지 크로스 컴파일선택한 패키지를 의존성 순서대로 크로스 컴파일합니다.output/target/
④ rootfs 조합컴파일된 파일들을 /bin, /etc, /lib 구조로 조립합니다.output/target/
⑤ 이미지 생성ext4·squashfs·cpio 등 원하는 포맷으로 패키징합니다.output/images/
Make+Kconfig만으로 왜 충분한가: Buildroot의 핵심 설계 원칙은 "단순함"입니다. BitBake처럼 별도 빌드 엔진을 배울 필요 없이, Linux 커널 설정과 동일한 방식의 menuconfig로 구성하고 표준 make로 빌드합니다. 2,800개 이상의 패키지가 모두 같은 패턴의 .mk 파일로 정의되어 있어 새 패키지 추가 진입 장벽이 낮습니다.

Buildroot는 "Make를 사용하여 임베디드 리눅스 시스템을 간단하게 생성합니다"는 철학을 따릅니다. 전체가 Makefile과 Kconfig로 구성되어 있으며, 추가 빌드 도구(BitBake 같은)가 필요 없습니다.

소스 트리 구조

buildroot/
├── Makefile                  # 최상위 Makefile
├── Config.in                 # 최상위 Kconfig
├── package/                  # 패키지 정의 (2800+ 패키지)
│   ├── busybox/              # BusyBox
│   │   ├── busybox.mk        # 빌드 레시피
│   │   ├── Config.in          # Kconfig 옵션
│   │   └── busybox.config     # BusyBox 설정
│   ├── openssl/
│   │   ├── openssl.mk
│   │   └── Config.in
│   └── pkg-generic.mk        # 공통 패키지 인프라
├── board/                    # 보드별 설정/스크립트
│   ├── qemu/                 # QEMU 보드
│   │   └── arm-vexpress/
│   │       ├── linux.config   # 커널 설정
│   │       ├── rootfs-overlay/ # 루트FS 오버레이
│   │       └── post-build.sh  # 빌드 후 스크립트
│   └── raspberrypi/          # Raspberry Pi
├── configs/                  # 사전 정의된 defconfig
│   ├── qemu_arm_vexpress_defconfig
│   ├── raspberrypi4_64_defconfig
│   └── ...                   # 200+ defconfig
├── dl/                       # 다운로드 캐시
├── output/                   # 빌드 출력 (아래에서 상세)
├── boot/                     # 부트로더 패키지 (U-Boot, GRUB, Barebox)
├── linux/                    # 커널 빌드 규칙
├── toolchain/                # 툴체인 빌드/외부 정의
├── system/                   # 시스템 설정 (hostname, init, overlay 등)
├── fs/                       # 파일시스템 이미지 생성 (ext4, squashfs, cpio 등)
├── support/                  # 빌드 지원 스크립트
└── docs/                     # 문서

시스템 스켈레톤 (system/)

Buildroot의 system/ 디렉토리는 루트 파일시스템의 기본 구조를 정의합니다:

# system/skeleton/ — 기본 루트FS 구조
system/skeleton/
├── etc/
│   ├── fstab              # 파일시스템 마운트 테이블
│   ├── group              # 기본 그룹 (root, daemon, ...)
│   ├── hostname           # BR2_TARGET_GENERIC_HOSTNAME
│   ├── hosts              # 로컬 호스트 매핑
│   ├── inittab            # BusyBox init 설정
│   ├── issue              # 로그인 배너
│   ├── passwd             # 기본 사용자 (root)
│   ├── profile            # 셸 프로필
│   ├── protocols          # 프로토콜 번호
│   ├── resolv.conf        # DNS 설정
│   ├── services           # 서비스 포트 매핑
│   └── shadow             # 패스워드 해시 (BR2_TARGET_GENERIC_ROOT_PASSWD)
└── usr/

# 시스템 설정 옵션 (menuconfig → System configuration)
BR2_TARGET_GENERIC_HOSTNAME="my-device"     # 호스트 이름
BR2_TARGET_GENERIC_ISSUE="Welcome to MyDevice"  # 로그인 배너
BR2_TARGET_GENERIC_ROOT_PASSWD="secret"     # root 패스워드
BR2_TARGET_GENERIC_GETTY_PORT="ttyS0"       # 시리얼 콘솔 포트
BR2_TARGET_GENERIC_GETTY_BAUDRATE="115200"  # 시리얼 속도

# init 시스템 선택
BR2_INIT_BUSYBOX=y           # BusyBox init (기본, 가장 작음)
# BR2_INIT_SYSV=y            # sysvinit
# BR2_INIT_SYSTEMD=y         # systemd (glibc 필요)
# BR2_INIT_NONE=y            # init 없음 (커스텀)

Kconfig 시스템

Buildroot의 모든 설정은 BR2_* 변수로 관리됩니다:

# 설정 방법들
make menuconfig             # ncurses 메뉴
make nconfig                # 대안 ncurses
make xconfig                # Qt GUI
make gconfig                # GTK GUI

# defconfig 워크플로
make qemu_arm_vexpress_defconfig   # 사전 설정 적용
make menuconfig                     # 추가 커스터마이징
make savedefconfig                  # 변경 저장 → configs/

# 주요 설정 카테고리 (BR2_*)
# BR2_TOOLCHAIN_*           — 툴체인 옵션
# BR2_LINUX_KERNEL_*        — 커널 설정
# BR2_PACKAGE_*             — 패키지 선택
# BR2_TARGET_ROOTFS_*       — 루트FS 이미지 형식
# BR2_ROOTFS_OVERLAY        — 루트FS 오버레이 경로
# BR2_ROOTFS_POST_BUILD_SCRIPT — 빌드 후 스크립트

툴체인 선택

옵션설명장점단점
InternalBuildroot가 직접 GCC+binutils+C lib 빌드완전한 제어, 최신 버전빌드 시간 증가 (~20분)
External prebuiltLinaro, ARM 등 사전 빌드 툴체인 사용빠른 빌드 시작버전 제한
External customcrosstool-NG 등으로 직접 빌드한 툴체인완전 맞춤화별도 관리 필요

Buildroot 빌드 과정

Buildroot 빌드 파이프라인 make xxx_defconfig .config 생성 make menuconfig 커스터마이징 (선택) make -j$(nproc) BR2_JLEVEL=$(nproc) — 패키지별 병렬 빌드 1. Toolchain GCC + binutils + musl/glibc 2. Packages 의존성 순서 크로스 컴파일 3. RootFS target/ → 파일시스템 조합 4. Image ext4/squashfs/cpio/wic BR2_TOOLCHAIN_* BR2_PACKAGE_* BR2_ROOTFS_* BR2_TARGET_ROOTFS_* output/images/ — 최종 펌웨어 이미지
그림 6. Buildroot 빌드 파이프라인 — defconfig → toolchain → packages → rootfs → image
Buildroot output/ 디렉토리 트리 output/ build/ busybox-1.37.0/ linux-6.6.x/ openssl-3.x/ uboot-2024.x/ 패키지별 빌드 디렉토리 host/ bin/ (cross-gcc 등) lib/ (호스트 라이브러리) share/ (호스트 데이터) 크로스 툴체인 + 호스트 도구 images/ zImage (커널) rootfs.ext4 sdcard.img 최종 결과물 이미지 staging/ usr/include/ usr/lib/ (.so, .a) → host/에 심볼릭 링크 개발용 sysroot target/ bin/ usr/ etc/ lib/ var/ tmp/ (strip된 바이너리) 루트FS 스켈레톤 mksquashfs / mkfs.ext4 / genimage 주요 명령어 make xxx-rebuild 패키지 재빌드 (configure 건너뛰기) make xxx-reconfigure 패키지 재설정 + 재빌드 make graph-depends 의존성 그래프 PDF 생성
그림 7. Buildroot output/ 디렉토리 — build/(빌드), host/(크로스 툴), images/(최종), staging/(sysroot), target/(루트FS)

빌드 명령어 상세

# 기본 빌드
make -j$(nproc)

# 특정 패키지 조작
make busybox-menuconfig    # BusyBox 설정
make linux-menuconfig       # 커널 설정
make uboot-menuconfig       # U-Boot 설정

# 패키지 재빌드
make busybox-rebuild        # 소스 변경 후 재빌드
make busybox-reconfigure    # configure부터 재실행
make busybox-dirclean       # 완전 삭제 후 처음부터

# 시각화
make graph-depends          # 의존성 그래프 (output/graphs/)
make graph-build            # 빌드 시간 그래프
make graph-size             # 이미지 크기 분석

# 법적 정보 수집
make legal-info             # 라이선스, 소스 코드 아카이브 생성

# SDK 생성 (외부 개발용)
make sdk                    # output/images/에 SDK 타르볼

per-package-directories 모드

Buildroot 2020.02부터 도입된 BR2_PER_PACKAGE_DIRECTORIES는 top-level 병렬 빌드를 가능하게 합니다:

# 활성화: menuconfig → Build options → Use per-package directories
BR2_PER_PACKAGE_DIRECTORIES=y

# 기존 방식: 모든 패키지가 하나의 staging/target 공유
#   → 패키지 간 빌드 순서 직렬화 필요
# per-package 방식: 각 패키지가 독립적인 staging/target 보유
#   → 의존성 없는 패키지 동시 빌드 가능
#   → 재빌드 시 영향 범위 최소화

# 디렉토리 구조 변화
output/
├── per-package/
│   ├── busybox/
│   │   ├── host/        # busybox 전용 호스트 도구
│   │   ├── staging/     # busybox 전용 sysroot
│   │   └── target/      # busybox 전용 타겟
│   ├── openssl/
│   │   ├── host/
│   │   ├── staging/
│   │   └── target/
│   └── ...
└── target/              # 최종 통합 루트FS

ccache 통합

# ccache 활성화: menuconfig → Build options → Enable compiler cache
BR2_CCACHE=y
BR2_CCACHE_DIR="/shared/buildroot-ccache"  # 공유 캐시 디렉토리

# ccache 통계 확인
make ccache-stats

# 효과: 전체 리빌드 시 50-80% 시간 단축 (캐시 히트 시)

graph-size 이미지 크기 분석

# 이미지 크기 분석 실행
make graph-size

# 결과물: output/graphs/
# ├── graph-size.pdf          # 패키지별 크기 파이 차트
# ├── package-size-stats.csv  # CSV 데이터
# └── file-size-stats.csv     # 파일별 크기

# 크기 최적화 워크플로
# 1. make graph-size 실행
# 2. 큰 패키지 확인 (보통 glibc > openssl > busybox > python)
# 3. 불필요 패키지 제거 또는 musl로 전환
# 4. BR2_STRIP_strip=y (기본) → 바이너리 strip
out-of-tree 빌드: Buildroot는 O= 변수로 여러 설정을 동시에 빌드할 수 있습니다: make O=/path/to/output1 qemu_arm_vexpress_defconfig, make O=/path/to/output2 raspberrypi4_64_defconfig. 각 output 디렉토리가 독립적인 빌드 환경을 가집니다.

Buildroot 패키지 인프라

Buildroot는 다양한 빌드 시스템에 맞춘 패키지 인프라(infrastructure)를 제공합니다. 개발자는 적절한 인프라를 선택하여 최소한의 코드로 패키지를 정의할 수 있습니다.

패키지 인프라 유형

인프라Makefile 매크로(Macro)대상 빌드 시스템예시 패키지
generic$(eval $(generic-package))커스텀 MakefileBusyBox, Linux 커널
autotools$(eval $(autotools-package))configure/make/make installcoreutils, glib
cmake$(eval $(cmake-package))CMakesystemd, json-c
meson$(eval $(meson-package))Meson + Ninjapipewire, gstreamer
python$(eval $(python-package))setuptools/pippython-requests
golang$(eval $(golang-package))Go modulescontainerd, etcd
cargo$(eval $(cargo-package))Rust Cargoripgrep
kconfig$(eval $(kconfig-package))Kconfig 기반BusyBox, Linux

Generic 패키지 예제

# package/hello/hello.mk
HELLO_VERSION = 1.0
HELLO_SOURCE = hello-$(HELLO_VERSION).tar.gz
HELLO_SITE = https://example.com/downloads
HELLO_LICENSE = MIT
HELLO_LICENSE_FILES = LICENSE
HELLO_DEPENDENCIES = host-pkgconf openssl

define HELLO_BUILD_CMDS
    $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D)
endef

define HELLO_INSTALL_TARGET_CMDS
    $(INSTALL) -D -m 0755 $(@D)/hello $(TARGET_DIR)/usr/bin/hello
endef

$(eval $(generic-package))

CMake 패키지 예제

# package/myapp/myapp.mk
MYAPP_VERSION = 2.0
MYAPP_SITE = $(call github,myorg,myapp,v$(MYAPP_VERSION))
MYAPP_LICENSE = Apache-2.0
MYAPP_INSTALL_STAGING = YES
MYAPP_DEPENDENCIES = openssl zlib

MYAPP_CONF_OPTS = \
    -DBUILD_TESTS=OFF \
    -DWITH_SSL=ON

$(eval $(cmake-package))

가상 패키지 (Virtual Packages)

Buildroot의 가상 패키지는 동일한 기능을 제공하는 대안 패키지를 추상화합니다:

# package/jpeg/Config.in — 가상 패키지 정의
config BR2_PACKAGE_HAS_JPEG
    bool  # "provides" 마커

config BR2_PACKAGE_PROVIDES_JPEG
    string
    default "libjpeg"   if BR2_PACKAGE_LIBJPEG
    default "jpeg-turbo" if BR2_PACKAGE_JPEG_TURBO

# 의존하는 패키지:
# MYAPP_DEPENDENCIES = jpeg   ← 가상 패키지명으로 의존
# → menuconfig에서 libjpeg 또는 jpeg-turbo 중 선택

호스트 패키지 (host-xxx)

# 호스트 패키지는 빌드 호스트에서 실행되는 도구를 빌드
# 패키지명에 host- 접두사 사용

HOST_MYAPP_DEPENDENCIES = host-pkgconf host-openssl

define HOST_MYAPP_BUILD_CMDS
    $(MAKE) -C $(@D)
endef

define HOST_MYAPP_INSTALL_CMDS
    $(INSTALL) -D $(@D)/mytool $(HOST_DIR)/bin/mytool
endef

$(eval $(host-generic-package))  # host- 접두사 인프라

사용자/권한 관리 (FOO_USERS, FOO_PERMISSIONS)

# package/myapp/myapp.mk — 사용자/그룹/권한 설정
MYAPP_USERS = myapp -1 myapp -1 * /var/lib/myapp - - My Application User

# 형식: username uid group gid password home shell groups comment
# -1은 자동 할당

MYAPP_PERMISSIONS = /etc/myapp.conf f 0600 root myapp - - - - -
MYAPP_PERMISSIONS += /var/lib/myapp d 0750 myapp myapp - - - - -

# systemd 서비스 설치 패턴
define MYAPP_INSTALL_INIT_SYSTEMD
    $(INSTALL) -D -m 644 $(MYAPP_PKGDIR)/myapp.service \
        $(TARGET_DIR)/usr/lib/systemd/system/myapp.service
endef

# sysvinit 서비스 설치 패턴
define MYAPP_INSTALL_INIT_SYSV
    $(INSTALL) -D -m 755 $(MYAPP_PKGDIR)/S99myapp \
        $(TARGET_DIR)/etc/init.d/S99myapp
endef

Config.in 구조

# package/hello/Config.in
config BR2_PACKAGE_HELLO
    bool "hello"
    depends on BR2_TOOLCHAIN_HAS_THREADS
    select BR2_PACKAGE_OPENSSL
    help
      A simple Hello World application.

      https://example.com/hello

BR2_EXTERNAL: 외부 트리

회사/프로젝트 전용 패키지와 설정을 Buildroot 소스 트리 외부에 관리할 수 있습니다:

# 외부 트리 구조
my-external/
├── Config.in                   # 외부 패키지 Kconfig
├── external.mk                 # 외부 패키지 포함
├── external.desc               # 이름, 설명
├── configs/                    # 커스텀 defconfig
│   └── my_board_defconfig
├── package/                    # 커스텀 패키지
│   └── my-app/
│       ├── my-app.mk
│       └── Config.in
└── board/                      # 보드 설정
    └── my-company/
        └── my-board/
            ├── linux.config
            ├── rootfs-overlay/
            ├── post-build.sh
            └── genimage.cfg

# 사용법
make BR2_EXTERNAL=/path/to/my-external menuconfig
# 또는
make BR2_EXTERNAL=/path/to/my-external my_board_defconfig

Yocto/OpenEmbedded 아키텍처

Yocto의 핵심 개념 — 레이어·레시피·BitBake

Yocto를 처음 접하면 용어가 낯설어 진입 장벽이 높습니다. 세 가지 핵심 개념만 이해하면 구조가 보입니다.

개념쉬운 설명파일 형식
레시피(Recipe) "이 패키지를 어떻게 빌드하는가"를 기술한 스크립트. Buildroot의 .mk, OpenWrt의 패키지 Makefile과 동일한 역할입니다. .bb (BitBake recipe)
레이어(Layer) 관련 레시피·설정 파일의 묶음 디렉토리. 레고 블록처럼 여러 레이어를 쌓아 최종 이미지를 구성합니다. BSP 레이어(하드웨어), 기능 레이어(보안·네트워크), 제품 레이어(내 프로젝트)로 분리해 재사용합니다. meta-xxx/ 디렉토리
BitBake 레시피를 파싱하고 의존성 그래프를 분석해 병렬 태스크(Task)로 실행하는 빌드 엔진. Make와 달리 레시피 간 공유 상태를 캐시(sstate-cache)로 관리해 증분 빌드를 지원합니다. Python/Shell 실행 엔진
왜 Make 대신 BitBake인가: Make는 파일 타임스탬프 기반으로 변경을 감지합니다. 수천 개 패키지가 얽힌 임베디드 이미지에서는 "패키지 A의 설정만 바꿔도 그것에 의존하는 모든 것을 다시 빌드"하는 정확한 의존성 추적이 필요합니다. BitBake는 레시피 변수·태스크 해시 기반으로 정확하게 무엇이 바뀌었는지 판단하고, sstate-cache를 통해 변경 없는 패키지는 캐시에서 복원합니다. 대규모 프로젝트에서 초기 풀빌드 후 증분 빌드가 몇 분 이내로 끝나는 이유입니다.

Yocto Project는 가장 유연하고 확장성 있는 임베디드 빌드 프레임워크입니다. 핵심은 레이어 아키텍처로, 독립적인 메타데이터 레이어를 쌓아 올려 맞춤형 배포판을 구성합니다.

Poky 구성요소

Poky는 Yocto의 레퍼런스 배포판으로, 다음으로 구성됩니다:

Yocto 레이어/레시피 아키텍처 스택 meta/ (OpenEmbedded-Core) 핵심 recipe, class, 기본 이미지 meta-poky (배포판 정책) DISTRO = "poky", poky.conf BSP 레이어 (meta-ti, meta-raspberrypi ...) MACHINE, 커널 recipe, DTS, 부트로더 기능 레이어 (meta-security, meta-oe ...) 추가 패키지, 보안 정책, 네트워크 등 커스텀 레이어 (meta-my-product) 프로젝트 recipe, bbappend, 이미지 정의 우선순위 (높음 →) 주요 파일 유형 .bb Recipe — 패키지 빌드 정의 .bbappend Recipe 수정/확장 .bbclass Class — 공통 빌드 로직 .conf 설정 — MACHINE, DISTRO, local .inc Include — 공유 recipe 코드 핵심 설정 파일 build/conf/local.conf build/conf/bblayers.conf meta-*/conf/machine/*.conf BitBake 엔진 레시피 파싱 → 의존성 해결 → 태스크 병렬 실행
그림 8. Yocto 레이어/레시피 아키텍처 — BitBake 엔진이 계층화된 레이어의 recipe/class/conf를 처리
Yocto class (.bbclass) 상속 계층 Yocto bbclass 상속 계층 (inherit 관계) base.bbclass do_fetch, do_unpack, do_patch 기본 구현 autotools.bbclass configure/make/install cmake.bbclass CMake 빌드 meson.bbclass Meson+Ninja 빌드 image.bbclass 이미지 생성 kernel.bbclass 커널 빌드 glib_2.78.bb inherit meson pkgconfig openssl_3.2.bb inherit autotools json-c_0.17.bb inherit cmake core-image-minimal.bb inherit image linux-yocto_6.6.bb inherit kernel 점선 화살표: recipe가 class를 inherit 실선 화살표: class가 base.bbclass를 inherit class는 do_configure, do_compile 등의 기본 구현을 제공하며, recipe에서 override 가능
그림 14. Yocto class 상속 계층 — base.bbclass를 뿌리로 autotools/cmake/meson/image/kernel class가 파생

DISTRO_FEATURES vs MACHINE_FEATURES vs IMAGE_FEATURES

변수설정 위치역할예시
DISTRO_FEATURESdistro.conf배포판 수준 기능 (소프트웨어 정책)systemd wifi bluetooth ipv6 pam
MACHINE_FEATURESmachine.conf하드웨어 기능 (보드가 제공하는 것)serial apm usbhost wifi gpu
IMAGE_FEATURES이미지 recipe/local.conf이미지에 포함할 기능 세트debug-tweaks ssh-server-openssh tools-debug
# DISTRO_FEATURES 활용 — 배포판 정책
# systemd를 init으로 사용:
DISTRO_FEATURES:append = " systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED:append = " sysvinit"
VIRTUAL-RUNTIME_init_manager = "systemd"

# MACHINE_FEATURES 활용 — 하드웨어 능력
# meta-raspberrypi/conf/machine/raspberrypi4-64.conf:
MACHINE_FEATURES = "apm usbhost wifi bluetooth vfat ext2 screen touchscreen"

# IMAGE_FEATURES 활용 — 이미지 구성
IMAGE_FEATURES += "debug-tweaks"       # root 비밀번호 없이 로그인 (개발용)
IMAGE_FEATURES += "ssh-server-openssh" # SSH 서버 포함
IMAGE_FEATURES += "tools-debug"        # gdb, strace 등 디버그 도구

multiconfig 지원

Yocto의 multiconfig는 하나의 빌드 환경에서 여러 머신/배포판을 동시에 빌드합니다:

# conf/multiconfig/board-a.conf
MACHINE = "board-a"
DISTRO = "my-distro"

# conf/multiconfig/board-b.conf
MACHINE = "board-b"
DISTRO = "my-distro"

# local.conf
BBMULTICONFIG = "board-a board-b"

# 빌드
bitbake mc:board-a:core-image-minimal mc:board-b:core-image-minimal

kas 설정 도구

kas는 Yocto 프로젝트의 레이어/설정을 YAML로 선언적 관리하는 도구입니다:

# kas-project.yml
header:
  version: 14
machine: raspberrypi4-64
distro: poky

repos:
  poky:
    url: "https://git.yoctoproject.org/poky"
    branch: scarthgap
    layers:
      meta:
      meta-poky:
  meta-raspberrypi:
    url: "https://git.yoctoproject.org/meta-raspberrypi"
    branch: scarthgap

local_conf_header:
  custom: |
    IMAGE_INSTALL:append = " openssh python3"
    PACKAGE_CLASSES = "package_rpm"

target: core-image-full-cmdline
# kas 사용
pip install kas
kas build kas-project.yml       # 전체 빌드
kas shell kas-project.yml       # 빌드 환경 셸 진입
kas checkout kas-project.yml    # 레이어 클론만

레이어 아키텍처

레이어설명제공처
meta/ (OE-Core)기본 recipe, class, qemu BSPOE 커뮤니티
meta-pokyPoky 배포판 정책Yocto Project
meta-openembedded추가 패키지 (networking, python, perl 등)OE 커뮤니티
meta-ti-bspTexas Instruments BSP (AM335x, AM62x 등)TI
meta-raspberrypiRaspberry Pi BSP커뮤니티
meta-intelIntel BSP (x86, Edison 등)Intel
meta-freescaleNXP/Freescale BSP (i.MX, Layerscape)NXP
meta-security보안 도구 (SELinux, IMA, AppArmor)커뮤니티
meta-virtualization컨테이너(Container), 하이퍼바이저(Hypervisor) (Docker, KVM)커뮤니티
OpenEmbedded Layer Index: layers.openembedded.org에서 400개 이상의 공개 레이어를 검색할 수 있습니다. 레이어의 호환 Yocto 버전, 의존성, recipe 목록을 확인할 수 있습니다.

Recipe (.bb) 구조

# recipes-example/hello/hello_1.0.bb
SUMMARY = "Hello World Application"
DESCRIPTION = "A simple Hello World for Yocto"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=..."

SRC_URI = "https://example.com/hello-${PV}.tar.gz"
SRC_URI[sha256sum] = "abc123..."

DEPENDS = "openssl"
RDEPENDS:${PN} = "libssl"

S = "${WORKDIR}/hello-${PV}"

inherit autotools pkgconfig

EXTRA_OECONF = "--with-ssl"

do_install:append() {
    install -d ${D}${sysconfdir}
    install -m 0644 ${WORKDIR}/hello.conf ${D}${sysconfdir}/
}

주요 설정 변수

# build/conf/local.conf
MACHINE = "raspberrypi4-64"       # 타겟 머신
DISTRO = "poky"                    # 배포판
PACKAGE_CLASSES = "package_rpm"    # 패키지 형식 (rpm/deb/ipk)

IMAGE_INSTALL:append = " openssh vim strace gdb"

DISTRO_FEATURES:append = " systemd wifi bluetooth"
DISTRO_FEATURES:remove = "sysvinit"

BB_NUMBER_THREADS = "8"
PARALLEL_MAKE = "-j 8"
SSTATE_DIR = "/opt/yocto/sstate"
DL_DIR = "/opt/yocto/downloads"

# build/conf/bblayers.conf
BBLAYERS = " \
  /path/to/poky/meta \
  /path/to/poky/meta-poky \
  /path/to/meta-openembedded/meta-oe \
  /path/to/meta-raspberrypi \
  /path/to/meta-my-product \
  "

Yocto 빌드 과정

BitBake 태스크 체인 BitBake 태스크 체인 (per-recipe) do_fetch do_unpack do_patch do_configure do_compile do_install do_package do_package_write do_rootfs do_image sstate-cache (Shared State) 각 태스크 완료 시 결과물을 해시 기반 캐시에 저장 → 재빌드 시 캐시 히트하면 태스크 건너뜀 (수 시간 → 수 초) 빌드 디렉토리 구조 tmp/work/<TUNE>-poky-linux/<PN>/<PV>/ tmp/deploy/images/<MACHINE>/ per-recipe 빌드 디렉토리 최종 이미지 + 패키지
그림 9. BitBake 태스크 체인 — do_fetch에서 do_image까지, sstate-cache가 각 단계를 캐시

핵심 BitBake 명령어

# 환경 초기화 (반드시 매 세션마다)
source oe-init-build-env [build-dir]

# 이미지 빌드
bitbake core-image-minimal
bitbake core-image-sato
bitbake core-image-full-cmdline

# 특정 recipe 빌드
bitbake busybox
bitbake -c compile busybox
bitbake -c devshell busybox
bitbake -c cleansstate busybox

# 레이어 관리
bitbake-layers show-layers
bitbake-layers add-layer ../meta-xxx
bitbake-layers show-recipes

# 의존성 분석
bitbake -g core-image-minimal
bitbake -e busybox | grep ^SRC_URI

# SDK 생성
bitbake -c populate_sdk core-image-minimal
bitbake -c populate_sdk_ext core-image-minimal

Hash Equivalence Server

Yocto 3.1+의 Hash Equivalence Server는 동일한 출력을 생성하는 다른 입력 해시(Hash)를 매핑(Mapping)하여 불필요한 재빌드를 추가 방지합니다:

# local.conf에서 활성화
BB_HASHSERVE = "auto"
BB_SIGNATURE_HANDLER = "OEEquivHash"

# 원리: A 패키지의 입력이 변경되었지만 출력 바이너리가 동일한 경우
# → B 패키지(A에 의존)를 재빌드하지 않음
# → 예: 주석만 변경된 소스 파일, 환경변수 순서 변경 등

재현 가능 빌드 (Reproducible Builds)

# Yocto 3.4+: 재현 가능 빌드 기본 활성화
# local.conf:
BUILD_REPRODUCIBLE_BINARIES = "1"

# 빌드 히스토리 추적 (변경사항 모니터링)
INHERIT += "buildhistory"
BUILDHISTORY_COMMIT = "1"

# 결과: buildhistory/ 디렉토리에 git 이력으로
# - 패키지 목록 변경
# - 파일 크기/권한 변경
# - 의존성 변경 추적
빌드 시간 비교: 같은 하드웨어(16코어, 64GB RAM, NVMe)에서 core-image-minimal 첫 빌드는 약 2~4시간이 소요되지만, sstate-cache가 있는 재빌드는 수 분 이내로 완료됩니다. CI/CD 환경에서 sstate-cache를 NFS/S3에 공유하면 빌드 시간을 극적으로 단축할 수 있습니다.

크로스 컴파일 툴체인 비교

툴체인 삼각관계: 호스트 · 타겟 · sysroot

크로스 컴파일 환경에는 항상 세 가지 역할이 존재합니다. 이 관계를 이해해야 툴체인 오류를 읽을 수 있습니다.

역할설명실제 예시
호스트(Host) 컴파일러가 실행되는 개발 PC Ubuntu 22.04 / x86_64
타겟(Target) 최종 바이너리가 실행될 임베디드 장치 OpenWrt 라우터 / ARM Cortex-A7
sysroot 타겟 환경의 헤더(.h)와 라이브러리(.so)를 호스트에 복사한 디렉토리. 컴파일러가 이 경로를 참조해 타겟용 바이너리를 생성합니다. staging_dir/target-arm_cortex-a7_musl/
왜 sysroot가 필요한가: 크로스 컴파일러는 타겟 라이브러리를 호스트 시스템(/usr/lib)에서 찾으면 안 됩니다. ARM용으로 컴파일된 libpthread.so를 x86용 코드에 링크하면 동작하지 않기 때문입니다. sysroot는 타겟용 바이너리만 모아 놓은 격리(Isolation)된 루트 디렉토리로, --sysroot= 옵션으로 컴파일러에 전달합니다. 빌드 시스템은 이 sysroot를 자동으로 구성하고 관리합니다.

임베디드 빌드 시스템의 핵심은 크로스 컴파일 툴체인입니다. 특히 C 라이브러리 선택은 바이너리 크기, 호환성, 성능에 큰 영향을 미칩니다. 크로스 컴파일의 이론적 배경은 LFS 문서를 참고하세요.

크로스 컴파일 C 라이브러리 비교 C 라이브러리 비교: musl vs glibc vs uClibc-ng musl 2011~ | MIT | Rich Felker 정적 링크 크기: ~700 KB POSIX 준수: 높음 스레드: NPTL (1:1) DNS: 내장 (단순) locale: UTF-8 전용 보안: stack canary 기본 사용: OpenWrt (기본), Alpine Linux, Buildroot, Yocto (선택) 크기 30% glibc 1987~ | LGPL-2.1 | GNU 정적 링크 크기: ~2.5 MB POSIX 준수: 완전 스레드: NPTL (고성능) DNS: nsswitch, nscd locale: 완전 지원 호환: glibc 확장 API 사용: Yocto (기본), Ubuntu, Fedora, Debian, Buildroot (선택) 크기 100% uClibc-ng 2000~/2015~ | LGPL-2.1 정적 링크 크기: ~600 KB POSIX 준수: 부분적 스레드: NPTL/LinuxThreads DNS: 내장 (제한적) locale: 선택적 MMU-less: 지원 (FLAT) 사용: Buildroot (선택), MMU 없는 Cortex-M, 레거시 시스템 크기 25%
그림 10. C 라이브러리 비교 — musl(경량, OpenWrt 기본), glibc(완전, Yocto 기본), uClibc-ng(레거시/MMU-less)

ABI 호환성 매트릭스

ARM 옵션접미사설명호환성
Hard-float (VFPv3)gnueabihf / musleabihf하드웨어 FPU 사용, 부동소수점 인자를 FPU 레지스터(Register)로 전달Cortex-A7+ 필수, 가장 빠름
Soft-floatgnueabi / musleabi소프트웨어 부동소수점 에뮬레이션FPU 없는 프로세서, 느림
AArch64aarch64-linux-gnu64비트 ARM, 항상 하드웨어 FPARMv8-A+
잘못된 sysroot 사용 증상: 크로스 컴파일 시 "sysroot mismatch" 오류가 나타나면:
  • cannot find -lxxx — sysroot에 해당 라이브러리 미설치. PKG_CONFIG_SYSROOT_DIR 확인
  • GLIBC_2.xx not found — 타겟 glibc 버전이 빌드 시 사용한 것보다 낮음
  • Illegal instruction — 타겟 CPU가 지원하지 않는 명령어 생성 (예: Cortex-A7에서 Cortex-A72 코드)
  • dynamic linker not found — 정적/동적 링킹(Dynamic Linking) 불일치, 또는 sysroot의 ld.so 경로 불일치

정적 vs 동적 링킹 트레이드오프

항목정적 링킹(Static Linking)동적 링킹
바이너리 크기큼 (라이브러리 포함)작음 (공유 라이브러리(Shared Library) 참조)
총 시스템 크기적은 프로그램: 작음 / 많은 프로그램: 큼라이브러리 공유로 절약
보안 업데이트모든 바이너리 재빌드 필요라이브러리만 교체
배포 편의성단일 파일, 의존성 없음.so 파일 함께 배포 필요
사용 사례initramfs, rescue 도구, 단일 앱 장비일반 임베디드 시스템

시스템별 툴체인 전략

OpenWrt 툴체인

# OpenWrt는 항상 내부 빌드 (musl 기본, glibc 선택 가능)
make menuconfig
# → Advanced configuration → Toolchain Options
#   → C Library implementation (musl / glibc)

# 결과물
staging_dir/toolchain-mips_24kc_gcc-13.3.0_musl/bin/
├── mips-openwrt-linux-musl-gcc
├── mips-openwrt-linux-musl-g++
├── mips-openwrt-linux-musl-ld
└── mips-openwrt-linux-musl-strip

Buildroot 툴체인

# 내부 빌드 (Internal)
make menuconfig
# → Toolchain → Toolchain type: Internal toolchain
#   → C library: musl / glibc / uClibc-ng

# 외부 프리빌트 (Linaro ARM)
# → Toolchain type: External toolchain
# → Toolchain: Linaro ARM 2024.xx

# 결과물 (내부 빌드 시)
output/host/bin/
├── arm-buildroot-linux-musleabihf-gcc
├── arm-buildroot-linux-musleabihf-g++
└── ...

Yocto 툴체인/SDK

# Standard SDK 생성
bitbake -c populate_sdk core-image-minimal

# SDK 설치
./poky-glibc-x86_64-...-toolchain-4.0.sh -d /opt/yocto-sdk

# SDK 환경 설정
source /opt/yocto-sdk/environment-setup-cortexa72-poky-linux

# 크로스 컴파일
$CC -o hello hello.c   # $CC = aarch64-poky-linux-gcc + sysroot flags

# Extensible SDK (eSDK) — devtool 포함
bitbake -c populate_sdk_ext core-image-minimal

Sysroot 구조

# 공통 sysroot 구조 (모든 시스템)
sysroot/
├── usr/
│   ├── include/              # 타겟 시스템 헤더
│   │   ├── linux/            # 커널 헤더
│   │   ├── stdio.h           # C 라이브러리 헤더
│   │   └── openssl/          # 패키지 헤더
│   └── lib/                  # 타겟 라이브러리
│       ├── libc.so           # C 라이브러리
│       ├── libssl.so         # OpenSSL
│       └── pkgconfig/        # pkg-config 메타데이터
└── lib/
    └── ld-musl-mips.so.1     # 동적 링커

커널 빌드 커스터마이징

각 빌드 시스템은 고유한 방식으로 커널 빌드를 관리합니다. 커널 설정, 패치 적용, Device Tree 생성, 버전 관리 방법이 모두 다릅니다.

OpenWrt 커널 커스터마이징

# 커널 설정 파일 위치
target/linux/ath79/config-6.6          # 타겟별 기본 설정
target/linux/generic/config-6.6        # 공통 설정

# 커널 패치
target/linux/ath79/patches-6.6/
├── 0001-ath79-add-new-board.patch
└── 0002-ath79-fix-pcie.patch

# 커널 소스에 추가할 파일 (drivers, DTS 등)
target/linux/ath79/files/
├── arch/mips/dts/my-board.dts
└── drivers/net/phy/my-phy.c

# 커널 menuconfig
make kernel_menuconfig

# 변경사항을 config-6.6에 반영
make kernel_oldconfig

# 특정 커널 모듈만 빌드
make target/linux/compile V=s

Buildroot 커널 커스터마이징

# menuconfig에서 커널 설정
make menuconfig
# → Kernel
#   → Kernel version: 6.6.x (또는 custom tarball)
#   → Kernel configuration: Using a custom config file
#   → Configuration file path: board/my-company/my-board/linux.config
#   → Custom kernel patches: board/my-company/my-board/patches/

# 커널 menuconfig 직접 실행
make linux-menuconfig

# 커널 설정 저장 (defconfig 형태)
make linux-update-defconfig

# 커널만 재빌드
make linux-rebuild

Yocto 커널 커스터마이징

# 커널 recipe 위치 확인
bitbake -e virtual/kernel | grep ^FILE=

# 커널 menuconfig
bitbake -c menuconfig virtual/kernel

# 커널 설정 fragment 방식 (권장)
# meta-my-layer/recipes-kernel/linux/linux-yocto_%.bbappend
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI += "file://my-config.cfg"
SRC_URI += "file://0001-my-driver.patch"

# files/my-config.cfg (config fragment)
CONFIG_MY_DRIVER=m
CONFIG_USB_GADGET=y

# KERNEL_FEATURES (Yocto 고유 기능)
# 커널 기능을 논리적으로 그룹화한 .scc 파일
KERNEL_FEATURES:append = " features/netfilter/netfilter.scc"
KERNEL_FEATURES:append = " cfg/virtio.scc"

# DTB 지정
KERNEL_DEVICETREE = "broadcom/bcm2711-rpi-4-b.dtb"

# 커널 devshell (소스 디렉토리에서 직접 작업)
bitbake -c devshell virtual/kernel

out-of-tree 커널 모듈 패턴

시스템패턴예시
OpenWrtpackage/kernel/my-module/Makefile + KernelPackagedefine KernelPackage/my-module ... FILES:=$(LINUX_DIR)/drivers/my-module.ko endef
Buildrootpackage/my-module/my-module.mk + kernel-module infra$(eval $(kernel-module)) $(eval $(generic-package))
Yoctoinherit module in recipeinherit module — do_compile은 자동으로 make -C ${STAGING_KERNEL_DIR} M=${S}

커널 버전 고정 전략

# OpenWrt: 타겟별 커널 버전은 include/kernel-version.mk에서 관리
# → 사용자 변경 비권장 (타겟-커널 호환성 보장)

# Buildroot: menuconfig에서 지정
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.6.30"
# 또는 커스텀 tarball/git:
BR2_LINUX_KERNEL_CUSTOM_TARBALL=y
BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="https://example.com/linux-6.6.30.tar.xz"

# Yocto: PREFERRED_VERSION으로 고정
PREFERRED_VERSION_linux-yocto = "6.6%"   # 6.6.x 시리즈
# 또는 정확한 커밋 고정:
SRCREV = "abc1234..."  # in linux-yocto bbappend
Config Fragment vs Full defconfig: Config fragment 방식은 변경한 옵션만 기록하므로 커널 버전 업그레이드 시 충돌이 적습니다. Yocto에서는 .cfg fragment를, Buildroot에서는 BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES를, OpenWrt에서는 config-* 파일에 delta만 기록하는 방식을 권장합니다.

커스텀 보드 지원 (BSP)

새 보드를 처음 만났을 때 — 어디서 시작하는가

BSP(Board Support Package)는 특정 하드웨어 보드에서 리눅스가 동작하게 만드는 모든 것의 묶음입니다. 새 보드를 처음 지원할 때 막막하게 느껴지는 이유는 작업 범위가 넓기 때문입니다. 아래 순서로 접근하면 단계를 나눌 수 있습니다.

단계해야 할 일확인 방법
① 유사 보드 찾기 같은 SoC(칩셋)를 사용하는 기존 지원 보드를 찾습니다. 90% 이상의 설정을 재사용할 수 있습니다. SoC 데이터시트에서 칩 이름 확인 → 빌드 시스템에서 검색
② 시리얼 콘솔 확보 UART 핀을 찾아 115200bps로 연결합니다. U-Boot 메시지가 나오면 포팅의 50%가 완료된 것입니다. picocom -b 115200 /dev/ttyUSB0
③ Device Tree 작성 보드의 하드웨어 구성(CPU, 메모리, 주변장치 연결)을 .dts로 기술합니다. SoC 공통 .dtsi를 include하고 보드 특이 사항만 추가합니다. cat /proc/device-tree/model 로 로드 확인
④ 커널 설정 최소화 유사 보드 defconfig에서 불필요한 드라이버를 제거합니다. 이더넷·플래시·UART 드라이버가 핵심입니다. dmesg | grep -i error 로 누락 드라이버 확인
⑤ 플래시 레이아웃 정의 부트로더·커널·rootfs 파티션 크기와 시작 주소를 결정합니다. SoC 부팅 요구 사항을 데이터시트에서 확인합니다. cat /proc/mtd 또는 lsblk
⑥ 빌드 시스템에 등록 위 산출물을 OpenWrt target/, Buildroot board/, Yocto meta-bsp/에 추가합니다. make menuconfig에서 보드가 보이는지 확인
TFTP 부팅으로 반복 주기 단축: 플래시에 이미지를 굽는 반복은 시간이 많이 걸립니다. U-Boot의 tftpboot 명령으로 네트워크에서 커널과 rootfs를 RAM에 직접 로드해 테스트하면 플래시 없이 빠르게 반복할 수 있습니다. 커널이 완전히 안정화된 이후에 플래시 레이아웃을 확정하는 것이 효율적입니다.

새로운 하드웨어 보드를 빌드 시스템에 통합하려면 BSP(Board Support Package)를 작성해야 합니다. BSP는 부트로더, 커널 설정, Device Tree, 보드별 드라이버, 플래시 레이아웃을 포함합니다.

BSP 구성요소 다이어그램 BSP Board Support Package 부트로더 설정 U-Boot defconfig, env, DTS 커널 설정/패치 defconfig, fragments, patches Device Tree 소스 .dts/.dtsi, 오버레이 보드별 루트FS overlay, 설정 파일, init 스크립트 플래시 레이아웃 파티션 테이블, 이미지 생성 규칙 보드별 드라이버 out-of-tree 커널 모듈, 펌웨어 blob OpenWrt: target/linux/xxx/ Buildroot: board/company/product/ Yocto: meta-xxx/
그림 11. BSP 구성요소 — 부트로더, 커널, DTS, 루트FS, 플래시 레이아웃, 드라이버를 포함

BSP 검증 체크리스트

BSP 통합 전 검증:
  • 시리얼 콘솔 출력 확인 (U-Boot → 커널 → 로그인 프롬프트)
  • 커널 부팅 시 Device Tree 올바르게 로드 확인: cat /proc/device-tree/model
  • 주요 하드웨어 초기화: 이더넷, WiFi, USB, GPIO, I2C, SPI
  • 플래시 파티션 레이아웃 확인: cat /proc/mtd 또는 lsblk
  • 부트로더 환경변수 정상 작동: fw_printenv
  • Watchdog 타이머(Timer) 설정 확인 (타이머 미설정 시 재부팅 루프)
  • 전원 관리(Power Management)/서스펜드 테스트 (해당하는 경우)

시스템별 U-Boot 커스터마이징

# OpenWrt: U-Boot은 target/linux/*/image/ 에서 관리
# 대부분 장비는 제조사 U-Boot 사용 (교체 비권장)

# Buildroot: boot/uboot/uboot.mk
make menuconfig
# → Bootloaders → U-Boot
#   → U-Boot board name: my_board
#   → U-Boot custom config file: board/my-company/my-board/uboot.config
make uboot-menuconfig          # U-Boot 설정
make uboot-rebuild              # U-Boot만 재빌드

# Yocto: u-boot recipe bbappend
# meta-my-bsp/recipes-bsp/u-boot/u-boot_%.bbappend
UBOOT_MACHINE = "my_board_defconfig"
SRC_URI += "file://0001-add-my-board.patch"
SRC_URI += "file://my-board-env.txt"

Buildroot BSP 추가

# board/my-company/my-board/
board/my-company/my-board/
├── linux.config                 # 커널 defconfig
├── uboot.config                 # U-Boot defconfig (선택)
├── rootfs-overlay/              # 루트FS에 복사될 파일
│   └── etc/
│       ├── network/interfaces
│       └── init.d/S99myapp
├── post-build.sh                # 빌드 후 스크립트
├── post-image.sh                # 이미지 생성 후 스크립트
├── genimage.cfg                 # SD 카드 이미지 레이아웃
└── readme.txt

# configs/my_board_defconfig
BR2_arm=y
BR2_cortex_a7=y
BR2_TOOLCHAIN_EXTERNAL=y
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/my-company/my-board/linux.config"
BR2_LINUX_KERNEL_DTS_SUPPORT=y
BR2_LINUX_KERNEL_CUSTOM_DTS_PATH="board/my-company/my-board/my-board.dts"
BR2_TARGET_ROOTFS_EXT2=y
BR2_ROOTFS_OVERLAY="board/my-company/my-board/rootfs-overlay"
BR2_ROOTFS_POST_BUILD_SCRIPT="board/my-company/my-board/post-build.sh"
BR2_ROOTFS_POST_IMAGE_SCRIPT="board/my-company/my-board/post-image.sh"

Yocto BSP 레이어 생성

# BSP 레이어 구조
meta-my-bsp/
├── conf/
│   ├── layer.conf
│   └── machine/
│       └── my-board.conf
├── recipes-bsp/
│   ├── u-boot/
│   │   └── u-boot_%.bbappend
│   └── my-firmware/
│       └── my-firmware_1.0.bb
├── recipes-kernel/
│   └── linux/
│       ├── linux-yocto_%.bbappend
│       └── files/
│           ├── my-board.cfg
│           └── 0001-add-driver.patch
└── wic/
    └── my-board.wks

# conf/machine/my-board.conf
MACHINE_FEATURES = "serial apm ext2 usbhost wifi"
KERNEL_IMAGETYPE = "zImage"
KERNEL_DEVICETREE = "my-board.dtb"
SERIAL_CONSOLES = "115200;ttyS0"
PREFERRED_PROVIDER_virtual/kernel = "linux-yocto"
IMAGE_FSTYPES = "wic ext4"
WKS_FILE = "my-board.wks"
UBOOT_MACHINE = "my_board_defconfig"

Device Tree 통합

Device Tree는 하드웨어 구성을 커널에 전달하는 데이터 구조로, 임베디드 리눅스 빌드 시스템에서 핵심적인 역할을 합니다. 각 시스템은 DTS(Device Tree Source) 파일의 위치와 빌드 방식이 다릅니다. 상세한 Device Tree 문법과 구조는 Device Tree 문서를 참고하세요.

시스템별 DTS 관리

시스템DTS 위치설정 방법빌드 방법
OpenWrttarget/linux/xxx/dts/자동 (profile에 따라 선택)커널 빌드 시 자동 컴파일
Buildrootboard/*/ 또는 커널 소스 내BR2_LINUX_KERNEL_CUSTOM_DTS_PATHmake linux-rebuild
Yoctometa-*/recipes-kernel/linux/files/KERNEL_DEVICETREE in machine.confbitbake virtual/kernel

DT Overlay 런타임 로딩

# DT Overlay를 런타임에 적용 (ConfigFS 방식)
# 1. 오버레이 컴파일
dtc -I dts -O dtb -o /tmp/my-overlay.dtbo my-overlay.dts

# 2. ConfigFS를 통해 적용
mkdir -p /sys/kernel/config/device-tree/overlays/my-overlay
cat /tmp/my-overlay.dtbo > /sys/kernel/config/device-tree/overlays/my-overlay/dtbo

# 3. 제거
rmdir /sys/kernel/config/device-tree/overlays/my-overlay

# fdtoverlay 도구로 오프라인 병합 (이미지 빌드 시)
fdtoverlay -i base.dtb -o merged.dtb overlay1.dtbo overlay2.dtbo

# U-Boot에서 오버레이 적용
fdt addr ${fdt_addr}
fdt resize 8192
fdt apply ${overlay_addr}

DTS 디버깅

# 컴파일된 DTB 역컴파일
dtc -I dtb -O dts -o decompiled.dts my-board.dtb

# 런타임 DT 확인
cat /sys/firmware/devicetree/base/compatible
ls /sys/firmware/devicetree/base/

# Yocto에서 커스텀 DTS 추가
# recipes-kernel/linux/linux-yocto_%.bbappend
SRC_URI += "file://my-board.dts;subdir=git/arch/arm64/boot/dts/my-vendor"
KERNEL_DEVICETREE += "my-vendor/my-board.dtb"

디버깅 및 개발 워크플로

OpenWrt 개발 도구

SDK와 Image Builder

# SDK: 패키지를 독립적으로 크로스 컴파일
tar xf openwrt-sdk-23.05-ath79-generic_gcc-13.3.0_musl.Linux-x86_64.tar.xz
cd openwrt-sdk-*/
./scripts/feeds update -a && ./scripts/feeds install -a
cp -r my-package package/
make package/my-package/compile V=s

# Image Builder: 사전 빌드된 패키지로 이미지만 재조합
tar xf openwrt-imagebuilder-23.05-ath79-generic.Linux-x86_64.tar.xz
cd openwrt-imagebuilder-*/
make image PROFILE=tplink_tl-wr1043nd-v2 \
  PACKAGES="luci luci-app-openvpn -ppp -ppp-mod-pppoe" \
  FILES=files/

원격 디버깅 (GDB)

# 타겟에서 gdbserver 실행
opkg install gdbserver
gdbserver :9000 /usr/bin/my-app

# 호스트에서 GDB 연결
./staging_dir/toolchain-*/bin/mips-openwrt-linux-musl-gdb
(gdb) target remote 192.168.1.1:9000
(gdb) set sysroot ./staging_dir/target-mips_24kc_musl/
(gdb) break main
(gdb) continue

Buildroot 개발 도구

# 디버그 빌드 활성화 + NFS root
make menuconfig
# → Build options → build packages with debugging symbols

# NFS root로 빠른 반복 개발
# U-Boot: setenv bootargs "root=/dev/nfs nfsroot=192.168.1.100:/path/to/output/target ip=dhcp"

# QEMU 통합 테스트
make qemu_arm_vexpress_defconfig && make
./output/images/start-qemu.sh

Yocto 개발 도구

# devtool: 가장 강력한 개발 도구
devtool modify busybox
# → workspace/sources/busybox/에 소스 체크아웃
devtool build busybox
devtool deploy-target busybox root@192.168.1.100
devtool finish busybox meta-my-layer

# devshell: 빌드 환경에서 직접 작업
bitbake -c devshell busybox

# QEMU 테스트
runqemu qemuarm64 nographic

Valgrind/AddressSanitizer 활용

# Buildroot에서 Valgrind 사용
make menuconfig
# → Target packages → Debugging → valgrind
make
# 타겟에서:
valgrind --leak-check=full /usr/bin/my-app

# AddressSanitizer (GCC -fsanitize=address)
# Buildroot: MYAPP_CONF_OPTS에 추가
MYAPP_CFLAGS = -fsanitize=address -fno-omit-frame-pointer
# 주의: ASan은 ~2x 메모리 오버헤드, 임베디드에서는 RAM 충분한 경우만

# Yocto에서 ASan 활성화
# local.conf:
EXTRA_OECMAKE:append:pn-myapp = " -DSANITIZE_ADDRESS=ON"

커널 크래시 덤프(Dump) 수집

# ramoops: RAM 기반 크래시 로그 (재부팅 후 확인)
# Device Tree에 예약 메모리 추가:
# reserved-memory { ramoops@... { compatible = "ramoops"; ... }; };
# 부팅 후 확인:
cat /sys/fs/pstore/console-ramoops-0   # 이전 부팅의 커널 로그

# OpenWrt에서 로그 확인
logread                # 시스템 로그
dmesg | tail -50       # 커널 링 버퍼

# coredump 수집 (Buildroot/Yocto)
# /proc/sys/kernel/core_pattern에 경로 설정
echo "/tmp/core.%e.%p" > /proc/sys/kernel/core_pattern
ulimit -c unlimited
# 크래시 발생 시 /tmp/core.myapp.1234 생성
# 호스트에서 분석:
arm-buildroot-linux-gnueabihf-gdb output/build/myapp-1.0/myapp /tmp/core.myapp.1234

시리얼 콘솔과 공통 디버깅

# 시리얼 콘솔 연결
picocom -b 115200 /dev/ttyUSB0

# strace로 시스템 콜 추적
strace -f -e trace=network ./my-app

# 네트워크 성능 측정
iperf3 -s           # 서버 모드 (타겟)
iperf3 -c 192.168.1.1 -t 30   # 클라이언트 (호스트)

# 디스크 I/O 성능 측정
dd if=/dev/zero of=/tmp/test bs=1M count=100 oflag=direct

CI/CD 통합 패턴

# Buildroot CI (가장 단순) — .gitlab-ci.yml
build:
  script:
    - make my_board_defconfig
    - make -j$(nproc)
  artifacts:
    paths:
      - output/images/

# OpenWrt CI
build:
  script:
    - ./scripts/feeds update -a && ./scripts/feeds install -a
    - cp my-config .config && make defconfig
    - make download -j8 && make -j$(nproc) V=s

# Yocto CI (sstate-cache 활용이 핵심)
build:
  script:
    - source oe-init-build-env
    - bitbake core-image-minimal
  cache:
    paths:
      - sstate-cache/
      - downloads/

성능 비교 및 선택 가이드

임베디드 빌드 시스템 선택 의사결정 플로차트 임베디드 리눅스 빌드 시스템 선택 라우터/네트워크 장비? Yes OpenWrt opkg, LuCI, UCI, SquashFS No 최소한의 임베디드 시스템? Yes Buildroot 빠른 빌드, 간단한 설정 No 장기 유지보수 기업/산업용? Yes Yocto 레이어, sstate, 벤더 BSP No 런타임 패키지 매니저 필요? Yes OpenWrt 또는 Yocto No Buildroot
그림 12. 임베디드 빌드 시스템 선택 의사결정 플로차트

성능 비교 (동일 하드웨어: 16코어, 64GB RAM, NVMe)

항목OpenWrtBuildrootYocto
첫 빌드 시간45~90분15~45분120~240분
패키지 1개 재빌드1~5분1~5분10초~2분 (sstate)
이미지 재생성2~5분1~3분5~15분
호스트 디스크 사용10~20 GB3~10 GB50~150 GB
호스트 RAM 권장4 GB+2 GB+8 GB+ (16 GB 권장)
최소 이미지 크기~4 MB~2 MB~8 MB
일반 이미지 크기8~16 MB4~32 MB50~500 MB

엔지니어링 비용 분석

항목OpenWrtBuildrootYocto
초기 학습 시간1~2주2~5일2~4주
새 패키지 추가30분~2시간15분~1시간1~4시간
새 보드 BSP1~3일1~2일2~5일
메이저 업그레이드1~3일1~2일3~10일
CI/CD 구축1일반나절2~3일

시스템 간 마이그레이션 경로

마이그레이션 팁:
  • Buildroot → Yocto: 가장 흔한 경로. 프로토타입(Buildroot)에서 양산(Yocto)으로. Buildroot의 패키지 목록(.config)을 Yocto IMAGE_INSTALL로 매핑. 커널 config는 그대로 재사용 가능.
  • OpenWrt → Buildroot: 네트워크 장비에서 범용 장비로. UCI/procd/LuCI 대체 필요. 커널 패치는 Buildroot에서도 사용 가능.
  • Buildroot → OpenWrt: 비권장. OpenWrt의 타겟 지원이 없으면 포팅 비용이 높음.
  • Yocto → Buildroot: 규모 축소 시. recipe를 .mk로 변환하는 수작업 필요.

선택 매트릭스

유스케이스추천이유
WiFi 라우터/APOpenWrtWiFi 드라이버(ath10k, mt76), LuCI, UCI 네트워크 설정
네트워크 스위치OpenWrtDSA(Distributed Switch Architecture) 기본 지원
IoT 센서 게이트웨이Buildroot최소 크기, 빠른 빌드, 단순 커스터마이징
산업용 HMI/PLCBuildroot 또는 YoctoBuildroot(프로토타입), Yocto(양산)
자동차 IVIYoctoAGL(Automotive Grade Linux), meta-ivi
의료 기기Yocto인증, 재현성, 장기 지원
반도체 벤더 SDKYoctoTI, NXP, Intel 공식 BSP 레이어
교육/학습용Buildroot가장 쉬운 학습 곡선, 명확한 구조
셋톱박스/미디어BuildrootKodi, FFmpeg 패키지 지원, 단순 구조
대규모 팀 프로젝트Yocto레이어 분리, 재사용성, 벤더 협업

실전 예제

유스케이스 1: WiFi 라우터 (OpenWrt)

# MediaTek MT7621 기반 라우터 빌드 (OpenWrt 23.05)
git clone -b openwrt-23.05 https://github.com/openwrt/openwrt.git
cd openwrt

./scripts/feeds update -a && ./scripts/feeds install -a

make menuconfig
# Target System: MediaTek Ralink MIPS
# Subtarget: MT7621 based boards
# Target Profile: Xiaomi Mi Router 4A Gigabit Edition
# LuCI → Collections → luci
# Network → VPN → wireguard-tools (선택)

make download -j8 && make -j$(nproc) V=s

ls bin/targets/ramips/mt7621/
# openwrt-23.05-ramips-mt7621-xiaomi_mi-router-4a-gigabit-squashfs-sysupgrade.bin

유스케이스 2: IoT 센서 게이트웨이 (Buildroot)

# Raspberry Pi Zero W 기반 센서 게이트웨이
git clone https://github.com/buildroot/buildroot.git
cd buildroot

make raspberrypi0w_defconfig
make menuconfig
# Target packages → Interpreter languages → python3
# Target packages → Libraries → Networking → libmosquitto (MQTT)
# Filesystem images → ext4 root filesystem

make -j$(nproc)
sudo dd if=output/images/sdcard.img of=/dev/sdX bs=4M

유스케이스 3: 산업용 게이트웨이 (Yocto)

# TI AM62x 기반 산업 게이트웨이 (Yocto scarthgap)
git clone -b scarthgap https://git.yoctoproject.org/poky
git clone -b scarthgap https://git.yoctoproject.org/meta-ti
git clone -b scarthgap https://git.openembedded.org/meta-openembedded

cd poky && source oe-init-build-env build-am62x

bitbake-layers add-layer ../meta-openembedded/meta-oe
bitbake-layers add-layer ../meta-ti/meta-ti-bsp

cat >> conf/local.conf <<'EOF'
MACHINE = "am62xx-evm"
DISTRO_FEATURES:append = " systemd wifi"
IMAGE_INSTALL:append = " openssh python3 mosquitto can-utils"
EOF

bitbake core-image-full-cmdline

CI 빌드용 Dockerfile 예시

# Dockerfile.buildroot-ci
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    build-essential gcc g++ binutils patch gawk bzip2 \
    unzip wget curl git file python3 python3-distutils \
    rsync bc cpio libncurses-dev zlib1g-dev libssl-dev \
    cmake perl-modules ca-certificates \
    && rm -rf /var/lib/apt/lists/*

RUN useradd -m builder
USER builder
WORKDIR /home/builder

# 사용법:
# docker build -t buildroot-ci -f Dockerfile.buildroot-ci .
# docker run -v $(pwd)/buildroot:/home/builder/buildroot buildroot-ci \
#   sh -c "cd buildroot && make my_board_defconfig && make -j$(nproc)"

펌웨어 업데이트 전략: A/B 파티션

A/B 파티션 업데이트 흐름 A/B 이중 파티션 업데이트 (SWUpdate/RAUC/Mender) Slot A (현재 활성 — 부팅 중) Kernel A RootFS A (v1.0) 실행 중 — 서비스 정상 동작 부트로더 플래그: active=A Slot B (비활성 — 업데이트 대상) Kernel B RootFS B (v1.0→v2.0) 업데이트 이미지 기록 중... 기존 v1.0 → 새 v2.0 덮어쓰기 1. 업데이트 다운로드 2. 서명 검증 3. Slot B에 기록 4. 부트 플래그 전환 5. Reboot → Slot B로 부팅 부팅 성공 Slot B를 active로 확정 (mark good) 부팅 실패 (Watchdog) 자동 롤백 → Slot A로 복귀 boot_count 초과 시 fallback 자동 롤백
그림 16. A/B 파티션 업데이트 흐름 — 다운로드 → 서명 검증(Signature Verification) → 비활성 슬롯 기록 → 재부팅 → 성공/롤백(Rollback)

펌웨어 업데이트 도구 비교

도구특징시스템 호환
sysupgradeOpenWrt 내장 업그레이드, 설정 보존 가능OpenWrt
SWUpdate이중 복사(A/B) 업데이트, 서명 검증, 웹 UIBuildroot, Yocto
RAUCA/B 슬롯, D-Bus API, Casync 델타 업데이트Yocto (meta-rauc)
MenderOTA 클라우드 서비스, A/B 업데이트, 모니터링Yocto (meta-mender)
OSTreeGit-like 파일시스템 스냅샷, 원자적(Atomic) 업데이트Yocto (meta-updater)
# SWUpdate A/B 업데이트 예시
swupdate -i my-firmware-update.swu

# RAUC 예시
rauc install my-update-bundle.raucb
rauc status   # 슬롯 상태 확인

# Secure Boot 체인 예시 (Yocto + RAUC)
# 1. 인증서 생성: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem
# 2. 번들 서명: rauc bundle --cert=cert.pem --key=key.pem --signing-keyring=ca.pem
# 3. 디바이스에서 검증: /etc/rauc/system.conf에 인증서 경로 설정
보안 주의: 펌웨어 업데이트 시 반드시 서명 검증을 적용하세요. SWUpdate는 CMS 서명, RAUC는 X.509 인증서, Mender는 서버 인증을 지원합니다. 서명 없는 OTA는 중간자 공격(MITM)에 취약합니다.
문서관련 주제
Linux From Scratch (LFS)크로스 컴파일 이론, 툴체인 구축 원리, sysroot 개념
빌드 시스템Makefile, CMake, Autotools, Meson 등 빌드 도구 기초
JFFS2OpenWrt overlay 파일시스템, NOR 플래시 저널링(Journaling)
SquashFSOpenWrt 읽기 전용 루트 파일시스템, 압축 옵션
Device TreeDTS 문법, DTB 컴파일, 오버레이, 바인딩
부팅 과정(Boot Process)U-Boot, 커널 부팅, initramfs, init 시스템
개발 환경 설정크로스 컴파일 환경 구축, 호스트 도구 설정
DSA 태깅OpenWrt 스위치 칩 지원, VLAN 태깅
무선 네트워크WiFi 드라이버(ath10k, mt76), cfg80211, mac80211
GCC크로스 컴파일러 옵션, 타겟 아키텍처 지정
ELF크로스 컴파일된 바이너리 형식, 동적 링킹
UFS/eMMC임베디드 스토리지, 플래시 메모리 관리(Memory Management)
BusyBox멀티콜 바이너리 아키텍처, 애플릿 시스템, init, initramfs 활용