ARM Cortex-M의 총 주소 공간은 6개의 큰 구역으로 나뉘며 각 구역은 그 목적에 따라 각기 다른 권한을 가진다. 개발자가 링커 스크립터와 소스코드에서 사용 가능한 주소 공간을 파악하기 위해서는 타겟 플랫폼의 하드웨어의 메모리 섹션과 위치를 data sheet를 통해 올바르게 파악해야만 한다.
.text와 .rodata section 처럼 읽기 전용으로 flash에 저장됐던 내용들은 이 구역으로 매핑된다. 따라서 IVT에 대한 내용도 여기에 매핑된다.0이 아닌 초깃값으로 정의된 심볼도 이 영역에 위치한다. 하지만, 런타임 시에 수정될 수 있기 때문에 이 쓰기 가능한 구역으로 복제 또는 재매핑되야 한다.
우리가 작성하는 모든 펌웨어 애플리케이션은 적정 크기의 스택 영역을 할당받아 실행을 시작한다.
실행 스택은 branch 시 return address와 함수 local 변수가 저장되며 stack pointer는 위에서 아래로 자란다.
우리는 항상 코딩 중 스택 사용량을 인지하며 잠재적 overflow를 염두해야 한다. 따라서 재귀함수처럼 스택 사용량을 예측하고 추적하기 어려운 코드는 작성하지 않는 것이 좋다.
반복해서 말하지만 실행 스택의 stack pointer는 컴파일 타임에 상수로 결정돼서 IVT에 정의된다. 사용 가능한 가장 높은 주소에 위치시킨 다음 낮은 주소로 자라게끔 하는 것이 일반적인 전략이다. 이때 동적 메모리의 heap 영역이 반대 방향으로 자라기 때문에 충돌이 발생할 수 있음을 주의하자. 따라서 최선의 전략은 다른 메모리 섹션과 분리된 적절한 실행 스택 공간을 할당한 뒤 런타임에 스택 사용량을 계속 점검하는 것이다.
필요한 스택 공간의 양을 측정하는 효율적인 방법은 스택 페인팅 (Stack Painting)이라는 메커니즘을 사용하는 것이다.
임베디드 시스템은 안전이 가장 중요하기 때문에 일반적으로 어떠한 동적 메모리 할당도 불가능하도록 설계된다. 하지만 동적 할당은 메모리의 lifecycle과 size에 대한 사용자의 완벽한 제어를 주기 때문에 매력적이고 강력해 임베디드 시스템에서도 수요가 높다. 따라서 할당된 메모리의 상태와 크기에 대한 추적을 유지하고 메모리 해제와 재사용에 대한 원칙을 반드시 준수한다면 사용해도 좋으며 디버깅에 소요되는 방대한 시간을 절약할 수 있다.
Heap을 사용할 경우 고려해야 하는 주요 문제는 아래 5가지다.
(책에는 각 항목에 대한 조금 더 자세한 내용이 있지만, 번역이 너무 이해하기 어려워 이 정도만 언급한다.)
MMU가 없어 가상 주소를 사용할 수 없는 MCU에서는 각 memory section 간 구분과 런타임 시 영역 침범을 예방하는 것이 어렵다. ARM은 MPU를 사용해 로컬 권한과 속성을 사용해 section을 명확히 분리한다.
MPU는 5개의 레지스터와 8개의 프로그래밍 가능한 구역을 지원한다. 엄격한 환경설정을 통해 1KB 짜리 guard region을 만들 수 있다. 예를 들어 위 그림처럼 heap과 stack 사이에 MPU로 guard region을 선언하면 충돌을 막을 수 있다.