임베디드 면접 용어 사전
펌웨어 빌드.ld

링커 스크립트

섹션을 어느 메모리 영역에 어떤 주소로 배치할지 지정하는 파일.

컴파일된 오브젝트들을 메모리의 어느 주소에 어떻게 배치할지 지시하는 파일 (.ld).

기본 구조

  • ENTRY(Reset_Handler) — /* 진입점 */
MEMORY {
  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 512K
  RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
  CCM   (rw)  : ORIGIN = 0x10000000, LENGTH = 64K
}
SECTIONS {
  .isr_vector : { KEEP(*(.isr_vector)) } > FLASH
  .text       : { *(.text*) *(.rodata*) } > FLASH
  .data       : { … } > RAM AT> FLASH
  .bss        : { … } > RAM
}

MEMORY — 물리적 메모리 선언

  • FLASH (rx) — r=읽기, x=실행
  • RAM — (rwx) w=쓰기까지

속성은 정보 제공용이라 실제 보호를 하지는 않는다(그것은 MPU의 일). 링커가 경고를 내는 데 쓰인다.

SECTIONS — 배치 규칙

.text : {
    . = ALIGN(4);           /* 현재 위치를 4바이트 정렬 */
    KEEP(*(.isr_vector))    /* gc-sections 로부터 보호 */
    *(.text)                /* 모든 오브젝트의 .text */
    *(.text*)               /* .text.func 같은 하위 섹션도 */
    *(.rodata*)
    . = ALIGN(4);
    _etext = .;             /* 현재 주소를 심볼로 정의 */
} > FLASH

.(점)은 현재 위치 카운터다. 이것을 조작해 정렬하거나 공간을 띄운다.

심볼 정의 — C에서 쓰는 법

_sdata = .;

extern uint32_t _sdata;
uint32_t *p = &_sdata;      // ★ 반드시 & 를 붙인다

★ 링커 심볼에는 값이 없고 주소만 있다. _sdata 자체를 읽으면 그 주소에 있는 데이터를 읽게 되어 엉뚱한 값이 나온다. 주소 자체가 우리가 원하는 값이므로 &가 필요하다.

초심자가 가장 많이 틀리는 부분이다.

실전 활용 ① — 부트로더와 앱 분리

/* 부트로더 */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K

/* 애플리케이션 (별도 프로젝트) */ FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 480K

앱의 벡터 테이블도 0x08008000에 놓이고, 앱은 시작 시 SCB->VTOR = 0x08008000 을 설정한다.

실전 활용 ② — 특정 RAM에 배치

.ccmram : {

  • _sccm = .;
  • (.ccmram)
  • _eccm = .; } > CCM AT> FLASH
__attribute__((section(".ccmram"))) uint32_t fast_buf[256];

주의 — CCM은 DMA가 접근하지 못하는 경우가 있다. DMA 버퍼를 여기 두면 전송이 안 된다.

실전 활용 ③ — 크기 검사

  • _Min_Heap_Size — = 0x200; _Min_Stack_Size = 0x800;

._user_heap_stack : {

  • . = . + _Min_Heap_Size;
  • . = . + _Min_Stack_Size; } > RAM

ASSERT(_ebss + _Min_Heap_Size + _Min_Stack_Size <= ORIGIN(RAM) + LENGTH(RAM),

- "RAM 부족: 스택·힙 공간이 확보되지 않음")

빌드 시점에 실패하므로 런타임에 스택 오버플로로 죽는 것보다 훨씬 낫다.

실전 활용 ④ — 펌웨어 정보 심기

.fw_info 0x08007F00 : {

  • KEEP(*(.fw_info)) } > FLASH
__attribute__((section(".fw_info"), used))
const FwInfo info = { .magic = 0xA5A5, .version = 0x010203, .crc = 0 };

고정된 주소에 버전 정보를 두면 부트로더가 앱을 실행하기 전에 유효성과 버전을 확인할 수 있다. OTA 구현의 기본 요소다.

맵 파일 — 문제 진단의 핵심 도구

-Wl,-Map=firmware.map

맵 파일에서 확인할 것

  • 각 섹션의 실제 시작 주소와 크기
  • 어느 오브젝트 파일이 얼마나 기여했는지
  • 심볼별 주소 (HardFault의 PC 값을 여기서 찾는다)
  • Discarded input sections (gc-sections 로 제거된 것)

HardFault 분석에서 PC 값을 맵 파일과 대조하면 어느 함수에서 죽었는지 알 수 있다. 맵 파일을 읽을 줄 아는 것이 임베디드 디버깅의 기본기다.

흔한 오류

  • "region FLASH overflowed by N bytes"

  • 코드가 너무 크다. -Os, gc-sections, nano.specs 검토

  • "undefined reference to _sdata"

  • 링커 스크립트에 심볼 정의가 없거나 이름이 다르다

  • "section .data VMA overlaps section .bss"

  • 배치가 겹쳤다. ALIGN 또는 순서 확인

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 펌웨어 빌드·부팅 — 빌드 과정·메모리 섹션(.data/.bss)·링커·startup·OTA