TIL/2025

C/C++ 코드 빌드 과정 이해하기

고무 오리 2025. 2. 14. 20:23
728x90
📢 C/C++ 코드를 짜고 gcc 명령 한 방이면 실행 파일이 뚝딱 만들어지지만 그 안에는 굉장한 절차가 숨어있어요

 

 

🤖 컴파일러?

목적에 따라 다양한 컴파일러가 있어요

  • gcc, clang, arm-none-eabi...

우린 ARM Cortex 시스템을 위한 arm-none-eabi 컴파일러를 예제로 살펴봐요

 

 

🪜 C/C++ 코드 빌드 6 Step

Step 1. 전처리 (Preprocessing) - 재료 손질하기 🥦

소스 코드가 요리라면, 전처리는 재료 손질 단계!

  • #include, #define 같은 전처리 지시문을 실제 코드로 바꾸는 작업을 해요
  • 예를 들어, #include <stdio.h> 같은 헤더 파일이 실제 코드로 포함이 돼요

컴파일러를 -E 옵션과 함께 실행하면 .i 파일을 얻을 수 있어요

  • 💡main.i 파일을 열어보면 #include가 모두 해석된 코드가 들어가 있어요!
arm-none-eabi-g++ -E main.cpp -o main.i

 

Step 2. 컴파일 (Compilation) - 요리법 정리하기 📜

이제 손질된 재료를 레시피에 맞게 정리할 차례예요!

  • 전처리 된 코드(.i)를 CPU가 이해할 수 있는 Assembly 코드(.s)로 변환해요
  • 이제 사람보다 기계가 읽기 편한 형태가 되었죠

컴파일러를 -S 옵션과 함께 실행하면 .s 파일을 얻을 수 있어요

arm-none-eabi-g++ -S -mcpu=cortex-m3 -mthumb main.i -o main.s

 

Step 3. 어셈블 (Assembly) - 조리 시작! 🍳

어셈블리는 요리하는 과정이에요.

  • 어셈블리 코드(.s)를 기계어(Byte code)로 변환하여 Object 파일(.o)을 만들죠 

아래처럼 arm-none-eabi-as 명령어를 실행하면  .o 파일을 얻을 수 있죠

  • 💡 전처리 ~ 어셈블까지 한 번에 진행하고 싶다면 컴파일러를 -c 옵션과 함께 실행하면 되요!
arm-none-eabi-as -mcpu=cortex-m3 -mthumb main.s -o main.o

 

Step 4. 링킹 (Linking) - 요리를 완성하자! 🍽️

이제 모든 재료가 준비됐으니 접시에 예쁘게 담아봐요

  • 링커(Linker)는 여러 개의 .o 파일과 라이브러리를 결합하여 실행 가능한 파일(.elf)를 만들어요

보통 임베디드 컴파일러는 이 단계에서 메모리 설정을 위해 linker.ld 파일을 -T 옵션과 함께 넣어줘요

arm-none-eabi-g++ -T linker.ld -nostartfiles -mcpu=cortex-m3 -mthumb main.o -o main.elf

 

Step 5. 디스어셈블 (Disassembly) - 요리 해체쇼 🔍

"이 요리는 어떻게 만들었을까?" 실행 파일을 다시 어셈블리 코드로 변환하면 그 비밀을 알 수 있어요

아래 명령을 사용하면 디버깅을 위한 .list 파일을 만들 수 있어요.

arm-none-eabi-objdump -d main.elf > main.list

 

Step 6. 바이너리 변환 (Binary Conversion) - 테이크아웃 포장 🎁

ELF 파일은 실행에 필요한 정보까지 포함되어 있어 용량이 커요

아래 명령어를 통해 가볍게 최적화하면 진짜 순수한 기계어 코드(.bin)가 돼요

arm-none-eabi-objcopy -O binary main.elf main.bin

 

 

👉 보통 Step1~3은 한 번에 처리

대형 프로젝트에서 빌드 과정 디버깅을 하는 경우가 아니면

보통 -c 옵션을 바로 수행해서 -E, -S도 한 번에 처리해요

 

 

🍀 정리

컴파일 과정은 단순히 "코드를 실행 파일로 변환하는 것"이 아니라, 여러 단계를 거쳐 최적화된 실행 파일을 만드는 과정이에요
Makefile을 활용하면 이 모든 단계를 자동화할 수도 있어요!

 

728x90