분석하다 보니 똑같은 코드 패턴이 여러 곳에 있었다.

// 함수 A에서
*(uint32_t *)0x40021000 |= 0x01000000;
while ((*(uint32_t *)0x40021000 & 0x02000000) == 0);

// 함수 B에서
*(uint32_t *)0x40021000 |= 0x01000000;
while ((*(uint32_t *)0x40021000 & 0x02000000) == 0);

// 함수 C에서도...

복붙? 인라인? 매크로?


인라인 함수

컴파일러가 함수 호출 대신 코드를 삽입한다.

static inline void HSE_Enable(void) {
    RCC->CR |= RCC_CR_HSEON;
    while (!(RCC->CR & RCC_CR_HSERDY));
}

-O2 옵션이면 bl HSE_Enable 대신 코드가 직접 들어간다.


매크로

전처리기가 텍스트 치환한다.

#define HSE_ENABLE() do { \
    RCC->CR |= RCC_CR_HSEON; \
    while (!(RCC->CR & RCC_CR_HSERDY)); \
} while(0)

결과는 인라인이랑 똑같다.


구분할 수 있나?

바이너리만 보면 구분 못한다. 둘 다 같은 기계어.

힌트가 될 수 있는 것:

  • 디버그 심볼 있으면 인라인 함수명 나옴
  • 매크로는 행 번호가 호출 위치로 나옴

근데 릴리즈 빌드면 둘 다 없다.


분석할 때 어떻게?

그냥 “공통 패턴"으로 인식하고 라벨 달아두면 된다.

// Ghidra 주석
// Pattern: HSE Enable sequence
*(uint32_t *)0x40021000 |= 0x01000000;
while ((*(uint32_t *)0x40021000 & 0x02000000) == 0);

원본이 인라인이든 매크로든 상관없다. 의미만 파악하면 됨.


HAL 라이브러리 패턴

ST HAL 쓰면 이런 패턴 많다:

__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_CAN1_CLK_ENABLE();

매크로라서 전부 인라인된다. 비슷한 코드가 반복되면 HAL 매크로일 가능성 높음.


다음 글에서 컴파일러 최적화 패턴.

#26 - 컴파일러 최적화 패턴