스타트업 코드 분석


  1. 전원을 인가하면 ARM core는 최초 Reset 신호(PORESETn)가 인가 된다.
  2. Reset_Handler가 호출된다.

0x0000004에 어떻게 Reset_Handler의 어드레스 값이 저장되는지 확인해 보았다.

스타트업 코드를 확인하면 아래와 같이 Vector Table이 정의 되어 있다.

vector_imagevector_table_reference
스타트업 코드에 정의된 Vector Table레퍼런스 문서에 정의된 Vector Table

Vector Table이란 프로그램이 동작중에 문제가 생기거나 인터럽트가 걸리기 되었을 때 Jump되는 Address를 정의한 Table 이다.

Cortex-M3에 정의된 Vector Table 가이드: https://developer.arm.com/documentation/dui0552/a/the-cortex-m3-processor/exception-model/vector-table?lang=en

.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors

.section 이란 memory map에 공간을 차지하는 영역(chunk)이다.
.section name[, “flags”] : 새로운 코드 섹션 또는 데이터 섹션을 지정한다.
                                         섹션 명(name)은 링크 디스크립터에 정의 되어 있어야 한다.
참고 홈페이지: https://damduc.tistory.com/398
참고 홈페이지: https://developer.arm.com/documentation/100068/0612/migrating-from-armasm-to-the-armclang-integrated-assembler/sections

.section .isr_vector,"a",%progbits

위 문법을 분석하면 메모리 공간에 “a” : allocatable 가능한 .isr_vector를 할당을 한다.
%progbits은 초기화된 data와 instruction 혹은 instruction만을 section이 담는다는 의미를 갖는다.
참고 홈페이지: https://developer.arm.com/documentation/101754/0618/armclang-Reference/armclang-Integrated-Assembler/Section-directives
.isr_vector는 아래와 같이 링크 스트립트(STM32F103C8TX_FLASH.ld)에 정의 되어 있다.

/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */ 
/* => 계산하면 (0x20000000 + 20*1024) = 0x20005000 */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Memories definition */
MEMORY
{
  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 20K
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 64K
}

/* Sections */
SECTIONS
{
  /* The startup code into "FLASH" Rom type memory */
  .isr_vector :
  {
    . = ALIGN(4);        /* 주소를 4의 배수로 맞추기 위함 작업 */
    KEEP(*(.isr_vector)) /* Startup code */ /* 링크 시 가비지 컬랙션을 (--gc-sections)한다면, 제거 되지 않도록 표시 
    . = ALIGN(4);        /* 주소를 4의 배수로 맞추기 위함 작업 */
  } >FLASH               /* .isr_vector를 FLASH로 정의된 메모리 영역으로 할당한다. */


참고 홈페이지: http://korea.gnu.org/manual/release/ld/ld-sjp/ld-ko_3.html#SEC21
참고 홈페이지: https://stackoverflow.com/questions/8458084/align-in-linker-scripts
참고 홈페이지: http://korea.gnu.org/manual/release/ld/ld-mahajjh/ld_3.html#SEC26
참고 홈페이지: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_linker_migration_gcc_to_tiarmclang.html
참고 홈페이지: https://en-support.renesas.com/knowledgeBase/19597376

.type g_pfnVectors, %object

g_pfnVectors는 data object이다.


참고 홈페이지: https://developer.arm.com/documentation/100067/0612/armclang-Integrated-Assembler/Type-directive

.size g_pfnVectors, .-g_pfnVectors

g_pfnVectors의 size를 할당한다. 해석하면 g_pfnVectors라는 symbol의 size를 현재 주소에서 g_pfnVector의 시작주소 값을 뺀 값으로 하겠다는 의미이다.


참고 홈페이지: http://web.mit.edu/rhel-doc/3/rhel-as-en-3/size.html
참고 홈페이지: https://stackoverflow.com/questions/11058361/meaning-of-main-expression
참고 홈페ㅣ지: https://hackmd.io/@SgyGB0h2QGWBfbHp9n_ArQ/BygAD3fU_

g_pfnVectors:

  .word _estack
  .word Reset_Handler
  .word NMI_Handler
  .word HardFault_Handler
  .word MemManage_Handler
  .word BusFault_Handler
  .word UsageFault_Handler
  .word 0
  .word 0
  .word 0
  .word 0
  .word SVC_Handler
  .word DebugMon_Handler
  .word 0
  .word PendSV_Handler
  .word SysTick_Handler
  .word WWDG_IRQHandler
  .word PVD_IRQHandler
  .word TAMPER_IRQHandler
  .word RTC_IRQHandler
  .word FLASH_IRQHandler
  .word RCC_IRQHandler
  .word EXTI0_IRQHandler
  .word EXTI1_IRQHandler
  .word EXTI2_IRQHandler
  .word EXTI3_IRQHandler
  .word EXTI4_IRQHandler
  .word DMA1_Channel1_IRQHandler
  .word DMA1_Channel2_IRQHandler
  .word DMA1_Channel3_IRQHandler
  .word DMA1_Channel4_IRQHandler
  .word DMA1_Channel5_IRQHandler
  .word DMA1_Channel6_IRQHandler
  .word DMA1_Channel7_IRQHandler
  .word ADC1_2_IRQHandler
  .word USB_HP_CAN1_TX_IRQHandler
  .word USB_LP_CAN1_RX0_IRQHandler
  .word CAN1_RX1_IRQHandler
  .word CAN1_SCE_IRQHandler
  .word EXTI9_5_IRQHandler
  .word TIM1_BRK_IRQHandler
  .word TIM1_UP_IRQHandler
  .word TIM1_TRG_COM_IRQHandler
  .word TIM1_CC_IRQHandler
  .word TIM2_IRQHandler
  .word TIM3_IRQHandler
  .word TIM4_IRQHandler
  .word I2C1_EV_IRQHandler
  .word I2C1_ER_IRQHandler
  .word I2C2_EV_IRQHandler
  .word I2C2_ER_IRQHandler
  .word SPI1_IRQHandler
  .word SPI2_IRQHandler
  .word USART1_IRQHandler
  .word USART2_IRQHandler
  .word USART3_IRQHandler
  .word EXTI15_10_IRQHandler
  .word RTC_Alarm_IRQHandler
  .word USBWakeUp_IRQHandler
  .word 0
  .word 0
  .word 0
  .word 0
  .word 0
  .word 0
  .word 0
  .word BootRAM          /* @0x108. This is for boot in RAM mode for
                            STM32F10x Medium Density devices. */

.word expr 현재 주소에 4byte를 생성 후 expr로 채워 넣는다.

g_pfnVectors 주소에 4byte를 생성 후 expr로 채워 넣는다.
참고홈페이지: https://velog.io/@coral2cola/ARM-Instruction-Set-Architecture-1

.isr_vector 섹션은 링커스크립터에서 FLASH 메모리에 가장 처음에 위치해 있도록 정의되어 있다. 이 의미는 g_pfnVectors라는 것은 FLASH의 맨처음 주소인 0x8000000 주소에 위치한다는 것이다. 따라서 _estack은 0x8000000 위치에 들어가게 되며 address가 4 바트씩 증가하면서 해당 expr 값을 저장하는 것이다. 정리하면 아래 같다.

vector들이 flash memory에 저장된 위치
memory_map

링커 스크립터에서 정의한 메모리 구역
memory_map

Flash 메모리에 정의된 Section들
memory_map

링커는 링커스크립트와 스타트업 코드에 정의된 Section들을 기준으로 Flash 메모리에 0x8000000 부터 시작하는 메모리 맵을 설정한다. 이 메모리 맵으로 설정된 실행 파일이 생성 되며 부팅 후 STM32는 0x0000000 주소 부터 0x8000000 부터 시작하는 메모리의 주소의 값을 복사한다. STM32에 리셋 신호가 들어오고 PC(Program Counter) 레지스터는 0x0000004에 있는 값으로 설정이 되는데 이 값이 Reset_Handler가 되어 부팅 시 Reset_Handler부터 시작이 되는 것이다.

–gc-sections 옵션 시 링크 스트립트에 정의된 KEEP을 정의하지 않으면 어떻게 삭제가 되는지 확인해 보았다.

    KEEP(*(.isr_vector)) /* Startup code */ /* 링크 시 가비지 컬랙션을 (--gc-sections)한다면, 제거 되지 않도록 표시 

아래 그림과 같이 Command Line에 ,–print-gc-sections 을 추가하면 사용하지 않는 삭제된 section들을 확인할 수 있다.
memory_map

아래 그림과 같이 KEEP Command를 제거한 후 빌드를 해 보았다.
memory_map

빌드 결과 아래 그림과 같이 로그가 출력되는 것을 확인할 수 있다.
memory_map

빌드로그 확인 결과 .isr_vector가 삭제되었다는 메시지는 출력이 되지 않았다. 아래 그림과 같이 Memory Details에서는 .isr_vector가 삭제 되었다. –print-gc-sections 이 정보는 .s에 대한 정보는 출력되지 않는 것 같다.
memory_map

아래 그림과 같이 스타트업 코드에서 .isr_vector를 삭제하며 링크 스크립트에는 아래 그림과 같이 KEEP을 유지하여 빌드를 한 후 Memory Details에서 is_vector를 확인한 결과 존재하지 않았다.

.isr_vector 정의 부분이 삭제된 스타트업 코드
memory_map

링커 스크립터 코드
memory_map

Flash 메모리에 정의된 Section들
memory_map

링크 스크립트에는 KEEP 을 사용하였지만 .s나 .c에 정의되어 있지 않은 section은 삭제된다.

–gc-sections 옵션 시 .c 또는 .s 파일(input file)에서 정의는 되어 있으나 사용되지 않으면 링커에서 삭제를 한다. section을 함수명 또는 변수라고 생각하면 이해하기 쉬울 것 같다. 삭제를 방지하기 위해 KEEP 옵션을 사용하는 것이다.

–gc-sections 옵션 시 링커 스크립트에 section이 정의 되어 있어도 input file에서 정의 되어 있지 않으면 삭제 된다. input section이란 input file 안에 있는 section을 의미한다.


참고홈페이지: http://korea.gnu.org/manual/release/ld/ld-mahajjh/ld_2.html
참고홈페이지: https://blog.dasomoli.org/linker-linker-scripts-ld/
참고홈페이지: https://embed-avr.tistory.com/86

아래와 같이 빌드를 하여 테스트를 진행해 보았다. 맥북에서는 -gc-sections 옵션시 컴파일 진행이 되지 않는다.
참고홈페이지: https://hyeyoo.com/136