실행 코드가 들어가는 섹션. 플래시에 배치되고 실행 중에는 읽기 전용이다.
무엇이 들어가나
- 컴파일된 함수 본문 (기계어)
- 인라인 어셈블리
- 벡터 테이블 (.isr_vector 로 별도 배치하는 것이 관례)
- 컴파일러가 생성한 보조 코드 (나눗셈 루틴 등)
.rodata(읽기 전용 데이터)도 보통 .text와 함께 플래시에 배치된다.
섹션 전체 지도
| 섹션 | 내용 | 저장 위치 | 실행 위치 |
|---|---|---|---|
| .text | 코드 | 플래시 | 플래시(직접 실행) |
| .rodata | const 데이터, 문자열 리터럴 | 플래시 | 플래시 |
| .data | 초기값 있는 전역/static | 플래시 | RAM(복사됨) |
| .bss | 초기값 없는 전역/static | 없음 | RAM(0으로 채움) |
| .heap / .stack | 동적/스택 | 없음 | RAM |
크기 확인
arm-none-eabi-size firmware.elf
text data bss dec hex
45231 412 8192 53835 d24b
플래시 사용량 = text + data (data의 초기값도 플래시에 저장되므로)
RAM 사용량 = data + bss (+ 런타임 스택·힙)
data가 양쪽에 들어간다는 점이 헷갈리기 쉽다.
초기값은 플래시에 있고 실체는 RAM에 있기 때문이다.
무엇이 크기를 늘리나 — 진단
# 심볼 크기 순 정렬 — 무엇이 큰지 바로 보인다
arm-none-eabi-nm --print-size --size-sort -r firmware.elf | head -20
# 맵 파일에서 각 오브젝트의 기여도 확인
-Wl,-Map=firmware.map
전형적인 범인
- printf 계열 — — float 지원까지 켜면 10~20KB
- -u _printf_float 를 빼거나 경량 구현 사용
- 표준 라이브러리 — → nano.specs 로 경량 newlib 사용
- math 라이브러리 — — sin, cos, pow 등
- 큰 룩업 테이블 — const 라면 .rodata
- HAL 라이브러리 — — 안 쓰는 모듈은 conf.h 에서 제외
크기 줄이기
- -Os — 크기 최적화
- -ffunction-sections
- -fdata-sections — 함수·데이터마다 별도 섹션으로 나눈다
- -Wl,--gc-sections — 참조되지 않는 섹션을 제거 ★
- --specs=nano.specs — 경량 C 라이브러리
- -flto — 링크 타임 최적화
★ --gc-sections가 효과가 크다. 앞의 두 옵션과 반드시 함께 써야
"함수 단위"로 제거할 수 있다. 안 그러면 오브젝트 파일 단위라 효과가 적다.
주의 — 벡터 테이블처럼 참조가 없는 것은 KEEP()으로 보호해야 한다.
RAM에서 실행하기 — .ramfunc
__attribute__((section(".RamFunc"))) void flash_write(uint32_t addr, uint32_t d) {
// 플래시에 쓰는 동안 플래시에서 명령어를 인출할 수 없다
}
언제 필요한가
- ① 플래시 쓰기·소거 루틴 — 자기 자신을 지울 수는 없다
- ② 매우 빠른 실행이 필요한 코드 — 플래시 웨이트 스테이트 회피
- ③ 지터를 줄여야 하는 ISR
링커 스크립트가 이 섹션을 LMA는 플래시, VMA는 RAM으로 두고
startup에서 복사한다. .data와 같은 원리다.
XIP — 외부 플래시에서 직접 실행
- QSPI 플래시를 메모리 맵에 매핑하면 그 주소에서 코드를 직접 실행할 수 있다
- 내장 플래시보다 큰 코드를 담을 수 있다
- 대신 느리고, 캐시 설정이 중요해진다
코드 무결성 보호
// 링커가 계산한 .text 영역의 CRC를 저장해 두고 부팅 시 검증
extern uint32_t _stext, _etext;
uint32_t crc = crc32((uint8_t *)&_stext, (uint8_t *)&_etext - (uint8_t *)&_stext);
if (crc != stored_crc) { /* 플래시 손상 */ }
안전이 중요한 기기에서는 필수다. 플래시 비트 반전(방사선, 노후화)으로 코드가 손상되는 것을 감지한다. IEC 61508 등에서 요구된다.
MPU로 보호
.text 영역 → 읽기 + 실행, 쓰기 금지 .data 영역 → 읽기 + 쓰기, 실행 금지 (XN) 버퍼 오버플로로 심어진 코드가 실행되는 것을 막는다.