Skip to content

Chapter 03

Hongjayeong-02 edited this page Oct 4, 2026 · 1 revision

03. Build Process & GNU Toolchain

Goal

Understand what actually happens when an embedded C project is built.

In this chapter:

  • Build the Ch02 program without relying on an IDE build button
  • Run each GNU ARM toolchain stage manually
  • Inspect .i, .s, .o, .elf, and .hex
  • Understand compiler options for Cortex-M4
  • Inspect generated machine code with objdump
  • Understand where compile errors and link errors occur

1. Why Study the Build Process?

STM32CubeIDE에서는 보통:

Build

버튼 한 번으로 프로그램이 만들어진다.

하지만 실제 내부에서는 여러 단계가 순서대로 실행된다.

main.c
  │
  ▼
Preprocessor
  │
  ▼
main.i
  │
  ▼
Compiler
  │
  ▼
main.s
  │
  ▼
Assembler
  │
  ▼
main.o
  │
  ▼
Linker
  │
  ▼
program.elf
  │
  ▼
objcopy
  │
  ├── program.hex
  └── program.bin

이 과정을 알아야 다음과 같은 오류가 어느 단계에서 발생했는지 판단할 수 있다.

syntax error
undefined reference
multiple definition
region FLASH overflowed
section overlaps

예를 들어:

undefined reference to `foo`

는 C 문법 오류가 아니다.

각 .c 파일은 정상적으로 compile되었지만, link 단계에서 foo의 실제 정의를 찾지 못했다는 뜻이다.


2. GNU ARM Embedded Toolchain

STM32F439ZI는 ARM Cortex-M4 기반 MCU이므로 일반 PC용 GCC가 아니라 ARM target용 cross compiler를 사용한다.

대표 명령:

arm-none-eabi-gcc
arm-none-eabi-as
arm-none-eabi-ld
arm-none-eabi-objcopy
arm-none-eabi-objdump
arm-none-eabi-size

3. What Does arm-none-eabi Mean?

Toolchain prefix:

arm-none-eabi

를 분해하면 다음과 같이 이해할 수 있다.

arm
 │
 └── Target architecture

none
 │
 └── No operating-system dependency

eabi
 │
 └── Embedded Application Binary Interface

즉:

ARM 기반 bare-metal embedded system을 위한 toolchain

이라고 이해하면 된다.

PC에서 GCC를 실행하지만 생성되는 machine code는 x86이 아니라 ARM Cortex-M4용 코드다.

Development PC
    x86/x64
       │
       │ arm-none-eabi-gcc
       ▼
ARM Machine Code
       │
       ▼
STM32F439ZI

이것이 Cross Compilation이다.


4. Full Build Pipeline

이번 실습에서는 다음 5단계를 직접 실행한다.

1. Preprocessing
2. Compilation
3. Assembly
4. Linking
5. Binary/Image Generation

각 단계의 입력과 출력:

main.c
  ↓ preprocessing
main.i
  ↓ compilation
main.s
  ↓ assembly
main.o
  ↓ linking
ch03_build.elf
  ↓ objcopy
ch03_build.hex

5. Practice Source Code

Ch03에서는 Ch02에서 사용한 LED blink source를 그대로 사용한다.

main.c

#define PERIPH_BASE        (0x40000000UL)
#define AHB1PERIPH_BASE    (PERIPH_BASE + 0x00020000UL)

#define RCC_BASE           (AHB1PERIPH_BASE + 0x3800UL)
#define GPIOB_BASE         (AHB1PERIPH_BASE + 0x0400UL)

/* Register mapping */
#define RCC_AHB1ENR   (*(volatile unsigned int *)(RCC_BASE + 0x30UL))
#define GPIOB_MODER   (*(volatile unsigned int *)(GPIOB_BASE + 0x00UL))
#define GPIOB_ODR     (*(volatile unsigned int *)(GPIOB_BASE + 0x14UL))

/* Bit definitions */
#define GPIOBEN       (1UL << 1)
#define LD1_PIN       (1UL << 0)

static void delay(volatile unsigned int count)
{
    while (count--)
    {
        __asm__("nop");
    }
}

int main(void)
{
    /* Enable GPIOB clock */
    RCC_AHB1ENR |= GPIOBEN;

    /* PB0 -> General Purpose Output */
    GPIOB_MODER &= ~(0x3UL << (0 * 2));
    GPIOB_MODER |=  (0x1UL << (0 * 2));

    while (1)
    {
        GPIOB_ODR ^= LD1_PIN;
        delay(800000);
    }
}

The hardware operation is unchanged from Chapter 02.

The focus of this chapter is:

How does main.c become machine code?

6. Stage 1 — Preprocessing

Command:

arm-none-eabi-gcc \
    -mcpu=cortex-m4 \
    -mthumb \
    -mfloat-abi=soft \
    -E main.c \
    -o main.i

Input:

main.c

Output:

main.i

7. What Does the Preprocessor Do?

Preprocessor는 실제 C compiler보다 먼저 동작한다.

주요 작업:

#include
#define
#if
#ifdef
#ifndef

등을 처리한다.

가장 중요한 점:

Preprocessor는 C 문법의 의미를 이해하는 compiler가 아니다.

주로 text substitution을 수행한다.

예를 들어:

#define GPIOBEN (1UL << 1)

RCC_AHB1ENR |= GPIOBEN;

는 preprocessing 후 개념적으로:

RCC_AHB1ENR |= (1UL << 1);

처럼 치환된다.


8. Register Macro Expansion

Ch02에서 사용한:

#define GPIOB_ODR \
    (*(volatile unsigned int *)(GPIOB_BASE + 0x14UL))

도 preprocessing 단계에서 치환된다.

그리고:

#define GPIOB_BASE \
    (AHB1PERIPH_BASE + 0x0400UL)

등도 연쇄적으로 치환된다.

결국 compiler가 보는 코드는 개념적으로 다음과 가까워진다.

(*(volatile unsigned int *)
    ((0x40000000UL + 0x00020000UL)
     + 0x0400UL
     + 0x14UL))
    ^= (1UL << 0);

즉 #define은 새로운 변수나 함수를 만드는 것이 아니다.

Source Text
    ↓
Preprocessor
    ↓
Expanded Source Text

이다.


9. Inspecting main.i

생성된 파일 확인:

less main.i

또는:

head main.i

.i 파일은 preprocessing이 끝난 C source다.

아직 ARM assembly도 아니고 machine code도 아니다.

main.c
 │
 │ #define / #include expanded
 ▼
main.i

10. Stage 2 — Compilation

Command:

arm-none-eabi-gcc \
    -mcpu=cortex-m4 \
    -mthumb \
    -mfloat-abi=soft \
    -S main.i \
    -o main.s

Input:

main.i

Output:

main.s

11. What Does the Compiler Do?

Compiler는 C 언어를 target processor의 assembly language로 변환한다.

C
 ↓
Compiler
 ↓
ARM / Thumb Assembly

예를 들어 C 코드:

count--;

는 architecture와 optimization에 따라 assembly에서:

subs r3, r3, #1

같은 instruction으로 바뀔 수 있다.

그리고:

while (count--)

같은 구조는 일반적으로:

subtract
compare
branch

계열 instruction으로 구현된다.


12. C Control Flow vs CPU Instructions

C에서는:

while (count--)
{
    __asm__("nop");
}

라고 작성하지만 CPU는 while이라는 개념을 모른다.

CPU가 이해하는 것은:

Load
Subtract
Compare
Branch

같은 instruction뿐이다.

개념적으로:

while(count--)
      │
      ▼
load count
      │
      ▼
subtract
      │
      ▼
compare with zero
      │
      ▼
branch if not finished

이다.

이 부분이 C programming과 Computer Architecture가 직접 연결되는 지점이다.


13. Inspecting main.s

less main.s

또는:

grep -A30 main main.s

실제 assembly를 확인하면:

C variable
C loop
C pointer access

가 Cortex-M4 instruction으로 어떻게 변환되는지 볼 수 있다.


14. Stage 3 — Assembly

Command:

arm-none-eabi-gcc \
    -mcpu=cortex-m4 \
    -mthumb \
    -mfloat-abi=soft \
    -c main.s \
    -o main.o

Input:

main.s

Output:

main.o

15. What Does the Assembler Do?

Assembler는 assembly instruction을 machine code로 변환한다.

ARM Assembly
     ↓
Assembler
     ↓
Binary Machine Instructions

예:

nop

같은 assembly mnemonic이 실제 CPU가 실행할 binary instruction으로 encoding된다.

하지만 .o 파일은 아직 완전한 실행 이미지가 아니다.


16. What Is an Object File?

main.o는 object file이다.

Object file에는 보통 다음 정보가 들어있다.

Machine Code
Sections
Symbols
Relocation Information
Debug Information

중요한 점:

.o에는 machine code가 존재하지만 아직 최종 주소가 확정되지 않은 symbol이 있을 수 있다.

예를 들어:

foo();

를 호출한다고 하자.

Compiler와 assembler는:

foo라는 symbol을 호출한다

는 정보는 만들 수 있다.

그러나 foo()가 다른 object file에 정의되어 있다면 실제 주소는 아직 알 수 없다.

main.o
 └── needs symbol: foo

foo.o
 └── defines symbol: foo

이 둘을 연결하는 단계가 Linking이다.


17. Object File Is Not a Flash Image

.o는 바로 MCU Flash에 쓸 파일이 아니다.

main.o
startup.o
driver.o
...

각 object file은 아직 분리되어 있다.

따라서 다음 단계에서 linker가 합쳐야 한다.


18. Inspecting main.o

Disassemble:

arm-none-eabi-objdump -d main.o

이 명령은 object file 안의 machine code를 사람이 읽기 쉬운 assembly 형태로 다시 보여준다.

Machine Code
    ↓
objdump
    ↓
Assembly View

이 과정을 Disassembly라고 한다.


19. objdump Example

Notion 실습에서는 delay()의:

while (count--)

가 실제로 저수준에서:

subtract
compare
conditional branch

형태의 instruction으로 바뀌는 것을 확인했다.

실제 instruction은 compiler option에 따라 달라질 수 있지만 대표적으로:

subs
cmp
bne

같은 instruction을 볼 수 있다.


20. Stage 4 — Linking

Command:

arm-none-eabi-gcc \
    -mcpu=cortex-m4 \
    -mthumb \
    -mfloat-abi=soft \
    -T linker.ld \
    main.o \
    startup.o \
    -o ch03_build.elf

Input:

main.o
startup.o
linker.ld

Output:

ch03_build.elf

21. What Does the Linker Do?

Linker의 중요한 역할은 크게 세 가지다.

1. Combine Object Files

main.o
startup.o
other.o
   │
   ▼
Linker
   │
   ▼
One Executable Image

2. Resolve Symbols

예:

main.o
 └── references Reset_Handler

startup.o
 └── defines Reset_Handler

혹은 반대로 startup code가:

main

symbol을 참조한다.

Linker가 실제 주소를 찾아 연결한다.

3. Place Sections in Memory

.text
.rodata
.data
.bss
.vector_table

등을 실제 FLASH / SRAM address에 배치한다.

이 위치를 지정하는 것이:

linker.ld

즉 Linker Script다.


22. Why Is a Linker Script Required?

PC 프로그램은 Operating System과 runtime environment가 많은 부분을 관리한다.

Bare-metal MCU는 그렇지 않다.

우리가 직접 알려줘야 한다.

예:

FLASH starts here
FLASH size is this

RAM starts here
RAM size is this

.text goes to FLASH
.data initial image goes to FLASH
.data runtime location goes to RAM
.bss goes to RAM

이를 linker에게 알려주는 파일이:

linker.ld

이다.

Chapter 04에서 직접 작성한다.


23. STM32F439ZI Memory Concept

개념적으로:

FLASH
0x08000000
│
├── Vector Table
├── .text
├── .rodata
└── Initial values for .data

SRAM
0x20000000
│
├── .data
├── .bss
├── Heap
└── Stack

처럼 code와 data가 서로 다른 memory 영역에 배치된다.

정확한 section placement는 linker script가 결정한다.


24. Why startup.o Is Needed

Bare-metal MCU에서 processor는 power-on 직후 바로 main()을 실행하지 않는다.

대략:

RESET
  ↓
Vector Table
  ↓
Initial Stack Pointer
  ↓
Reset_Handler
  ↓
Runtime Initialization
  ↓
main()

따라서 Reset_Handler와 vector table을 가진 startup code가 필요하다.

Chapter 03에서는 기존 startup object를 사용하고, 자세한 구현은 Chapter 04에서 다룬다.


25. Link Error Example

Suppose:

void foo(void);

int main(void)
{
    foo();
}

그런데 어디에도:

void foo(void)
{
}

가 없다면 compiler는 prototype을 알고 있기 때문에 compile은 가능하다.

하지만 linker는 실제 foo implementation을 찾지 못한다.

결과:

undefined reference to `foo'

즉:

Compile
  PASS
    ↓
Link
  FAIL

이다.


26. Compile Error vs Link Error

Compile Error

Examples:

syntax error
undeclared identifier
wrong type
missing semicolon

보통:

.c → .o

과정에서 실패한다.

Link Error

Examples:

undefined reference
multiple definition
region overflowed

보통:

.o files → .elf

과정에서 실패한다.

이 차이를 아는 것이 build debugging에서 매우 중요하다.


27. ELF File

Linking이 성공하면:

ch03_build.elf

가 생성된다.

ELF:

Executable and Linkable Format

이다.

ELF에는 단순 machine code 외에도 여러 정보가 포함될 수 있다.

Executable Code
Data
Sections
Symbols
Debug Information
Address Information

따라서 debugger가:

main()
uart_init()
line 37
variable x

등을 찾을 수 있다.


28. Inspecting ELF Size

Command:

arm-none-eabi-size ch03_build.elf

실습 결과:

text    data    bss
252       0      0

Meaning:

text

Program code and read-only data.

주로 Flash에 위치한다.

data

초기값이 있는 global/static variable.

Example:

int count = 10;

초기값은 Flash image 안에 저장되어 있고 runtime에는 RAM으로 복사된다.

bss

초기값이 없거나 0으로 초기화되는 global/static variable.

Example:

int count;

보통 Flash image에 실제 0 데이터를 전부 저장하는 대신:

이 영역을 startup에서 0으로 초기화하라

는 정보만 가진다.


29. Why Is .data = 0 and .bss = 0?

이번 Ch02/Ch03 source는 global variable을 거의 사용하지 않는다.

Registers는:

#define

macro이고 실제 RAM variable이 아니다.

예:

#define GPIOB_ODR \
    (*(volatile unsigned int *)0x40020414UL)

는 RAM 공간을 차지하는 전역 변수가 아니다.

따라서:

data = 0
bss  = 0

이 나오는 것이 자연스럽다.


30. Stage 5 — Generate Flash Image

Command:

arm-none-eabi-objcopy \
    -O ihex \
    ch03_build.elf \
    ch03_build.hex

Input:

ch03_build.elf

Output:

ch03_build.hex

31. What Does objcopy Do?

ELF에는 flashing에 필요하지 않은 정보도 많이 포함되어 있다.

예:

debug symbols
section metadata
symbol names
source mapping

Flash programmer가 필요한 것은 실제 MCU memory에 기록할 content다.

따라서:

ELF
 │
 │ objcopy
 ▼
Flash Image

로 변환한다.


32. HEX vs BIN

Intel HEX

ch03_build.hex

Text-based format.

내용에는:

Address
Data
Record Type
Checksum

등이 포함된다.

예:

:1000000068030020650100006D0100006F01000021

사람이 text file로 열 수 있다.


Binary

Binary image를 만들려면:

arm-none-eabi-objcopy \
    -O binary \
    ch03_build.elf \
    ch03_build.bin

.bin은 순수 binary byte stream이다.

HEX
→ address information included

BIN
→ raw bytes only

33. Complete Practice Build Script

Notion Ch03에서 사용한 핵심 build sequence:

arm-none-eabi-gcc \
    -mcpu=cortex-m4 \
    -mthumb \
    -mfloat-abi=soft \
    -E main.c \
    -o main.i

arm-none-eabi-gcc \
    -mcpu=cortex-m4 \
    -mthumb \
    -mfloat-abi=soft \
    -S main.i \
    -o main.s

arm-none-eabi-gcc \
    -mcpu=cortex-m4 \
    -mthumb \
    -mfloat-abi=soft \
    -c main.s \
    -o main.o

arm-none-eabi-gcc \
    -mcpu=cortex-m4 \
    -mthumb \
    -mfloat-abi=soft \
    -T linker.ld \
    main.o startup.o \
    -o ch03_build.elf

arm-none-eabi-objcopy \
    -O ihex \
    ch03_build.elf \
    ch03_build.hex

34. Recommended build.sh

The same sequence can be written as:

#!/bin/bash

CPU="-mcpu=cortex-m4"
THUMB="-mthumb"
FLOAT="-mfloat-abi=soft"

echo "[1] Preprocess"
arm-none-eabi-gcc \
    $CPU $THUMB $FLOAT \
    -E main.c \
    -o main.i

echo "[2] Compile"
arm-none-eabi-gcc \
    $CPU $THUMB $FLOAT \
    -S main.i \
    -o main.s

echo "[3] Assemble"
arm-none-eabi-gcc \
    $CPU $THUMB $FLOAT \
    -c main.s \
    -o main.o

echo "[4] Link"
arm-none-eabi-gcc \
    $CPU $THUMB $FLOAT \
    -T linker.ld \
    main.o startup.o \
    -o ch03_build.elf

echo "[5] Generate HEX"
arm-none-eabi-objcopy \
    -O ihex \
    ch03_build.elf \
    ch03_build.hex

echo "[6] Size"
arm-none-eabi-size ch03_build.elf

Execute:

chmod +x build.sh
./build.sh

35. Compiler Option — -mcpu=cortex-m4

-mcpu=cortex-m4

Compiler에게 target CPU architecture를 알려준다.

STM32F439ZI core:

ARM Cortex-M4

따라서 compiler는 Cortex-M4가 지원하는 instruction을 기준으로 code를 생성한다.

잘못된 CPU option을 선택하면:

  • 지원하지 않는 instruction 생성
  • 최적화 방식 차이
  • architecture incompatibility

문제가 발생할 수 있다.


36. Compiler Option — -mthumb

-mthumb

Thumb instruction set을 사용하도록 지정한다.

Cortex-M series는 Thumb instruction execution을 기반으로 한다.

C Source
   ↓
Thumb Instructions
   ↓
Cortex-M4

따라서 STM32 firmware build에서는 일반적으로 필요한 option이다.


37. Compiler Option — -mfloat-abi

Ch03 practice:

-mfloat-abi=soft

를 사용했다.

현재 code는:

GPIO register access
integer operation
bit operation

만 사용하므로 hardware floating-point unit을 사용할 필요가 없다.


38. STM32F439ZI FPU

STM32F439ZI의 Cortex-M4 core는 hardware floating-point support를 제공한다.

Floating-point를 실제 hardware FPU로 처리하려면 일반적으로 다음과 같은 option 조합을 사용할 수 있다.

-mfloat-abi=hard
-mfpu=fpv4-sp-d16

개념:

soft
 └── Floating-point ABI does not require hardware FPU instructions

hard
 └── Hardware FPU calling convention / instructions used

중요한 점은 object files와 libraries가 호환되는 ABI 설정을 사용해야 한다는 것이다.

서로 다른 floating-point ABI의 object/library를 섞으면 link 문제가 발생할 수 있다.


39. gcc vs as vs ld

GNU toolchain에는 각각 독립된 프로그램이 존재한다.

gcc
as
ld

하지만 실습에서는 대부분:

arm-none-eabi-gcc

driver를 사용했다.

gcc driver는 option에 따라 내부적으로 필요한 tool을 호출한다.

예:

gcc -c main.s

는 내부적으로 assembler를 사용한다.

그리고:

gcc main.o startup.o -o app.elf

는 linker를 호출한다.

따라서:

GCC는 단순히 C compiler 하나만 의미하는 것이 아니라 build toolchain의 driver 역할도 한다.


40. Source File to CPU Execution

전체 과정을 hardware 관점까지 연결하면:

main.c
  │
  ▼
Preprocessor
  │
  ▼
Compiler
  │
  ▼
ARM Assembly
  │
  ▼
Assembler
  │
  ▼
Machine Code
  │
  ▼
Linker
  │
  ▼
Absolute Memory Layout
  │
  ▼
ELF
  │
  ▼
HEX / BIN
  │
  ▼
FLASH
  │
  ▼
Cortex-M4 Instruction Fetch
  │
  ▼
Execution

즉 우리가 작성한 C code가 최종적으로는 Flash memory에 저장된 machine instruction이 된다.


41. Build Result

Notion에 기록된 Ch03 결과:

text : 252 bytes
data : 0 bytes
bss  : 0 bytes

Ch02의 Makefile build 결과와 동일했다.

Ch02
Makefile
   │
   └── 252 / 0 / 0

Ch03
Manual Toolchain
   │
   └── 252 / 0 / 0

같은 source와 같은 build configuration을 사용했기 때문에 동일한 result가 나온다.

이 실습의 핵심은:

IDE나 Make가 특별한 binary를 만드는 것이 아니라, 결국 동일한 compiler / assembler / linker를 순서대로 실행한다.

는 것을 확인한 것이다.


42. Useful Inspection Commands

Preprocessed C

less main.i

Generated Assembly

less main.s

Object Disassembly

arm-none-eabi-objdump -d main.o

ELF Disassembly

arm-none-eabi-objdump -d ch03_build.elf

Section Headers

arm-none-eabi-objdump -h ch03_build.elf

Symbols

arm-none-eabi-nm ch03_build.elf

Program Size

arm-none-eabi-size ch03_build.elf

HEX Preview

head ch03_build.hex

이 명령들을 같이 익혀두면 build/debugging 때 매우 유용하다.


43. Common Error Mapping

Build error를 보면 어느 단계를 먼저 의심해야 하는지 정리하면:

#include file not found
        ↓
Preprocessor

syntax error
type error
undeclared identifier
        ↓
Compiler

invalid instruction
        ↓
Assembler

undefined reference
multiple definition
FLASH overflow
RAM overflow
        ↓
Linker

HEX conversion failure
        ↓
objcopy

44. undefined reference

Example:

undefined reference to `MYX_LWIP_Init'

이 종류의 error는 대표적인 linker error다.

의미:

Function call exists
        ↓
Compiler accepts it
        ↓
Object file references symbol
        ↓
Linker searches definition
        ↓
Definition not found
        ↓
FAIL

따라서 이 오류에서는:

C syntax

보다:

source file included?
object file generated?
function actually defined?
library linked?

를 확인해야 한다.


45. multiple definition

두 object file이 같은 global symbol을 정의하면:

file1.o
 └── foo

file2.o
 └── foo

linker가 어느 것을 사용할지 결정할 수 없다.

따라서:

multiple definition of `foo'

가 발생할 수 있다.


46. region overflowed

Linker script가:

FLASH size
RAM size

를 정의한다.

Program section이 해당 memory region보다 커지면:

region `FLASH' overflowed

같은 link error가 발생한다.

따라서 이 오류는 compile 문제가 아니라 memory placement 문제다.


47. Makefile and IDE Relationship

이번 실습에서는 직접 명령어를 실행했지만 실제 project에서는 보통:

Makefile
IDE
CMake
Build Script

등이 동일한 명령을 자동으로 관리한다.

Manual Commands
      ↓
Shell Script
      ↓
Makefile
      ↓
IDE Build System

추상화 수준만 다를 뿐 underlying build flow는 같다.


48. Key Questions

Q1. What is the complete build order?

Preprocess
   ↓
Compile
   ↓
Assemble
   ↓
Link
   ↓
Generate Flash Image

Q2. What does .i contain?

Preprocessed C source.

#define expanded
#include expanded

Q3. What does .s contain?

Target assembly code generated by the compiler.

For STM32F439ZI:

ARM Cortex-M4 / Thumb assembly

Q4. What does .o contain?

Machine code plus section, symbol and relocation information.

It is not yet the final firmware image.


Q5. What does .elf contain?

Linked executable information including:

  • machine code
  • section layout
  • addresses
  • symbols
  • optional debug information

Q6. Why generate .hex?

Because a programmer/debugger needs an image describing the bytes to write into MCU memory.


Q7. What is the difference between .elf and .hex?

ELF
 ├── machine code
 ├── symbols
 ├── sections
 ├── addresses
 └── debug information

HEX
 ├── memory addresses
 ├── data
 └── checksum

Q8. Why can F439ZI use hardware floating point?

Because the Cortex-M4 implementation in STM32F439ZI includes an FPU.

For hardware-FPU builds, compatible options such as:

-mfloat-abi=hard
-mfpu=fpv4-sp-d16

can be used.


Q9. What stage causes undefined reference?

Linking

Q10. Why learn this if the IDE already builds automatically?

Because understanding the build stages makes errors much easier to diagnose.

Error Message
     ↓
Identify Build Stage
     ↓
Identify Responsible File / Tool
     ↓
Fix Root Cause

49. Key Takeaways

C Source
   ↓
Preprocessor
   ↓
Expanded C
   ↓
Compiler
   ↓
Assembly
   ↓
Assembler
   ↓
Object File
   ↓
Linker + Linker Script
   ↓
ELF
   ↓
objcopy
   ↓
HEX / BIN
   ↓
MCU Flash

The most important concepts are:

  1. A build is not one operation.
  2. Each intermediate file has a different purpose.
  3. Compilation and linking are separate processes.
  4. Object files can contain unresolved symbols.
  5. The linker resolves symbols and assigns addresses.
  6. The linker script defines the MCU memory layout.
  7. ELF is useful for debugging and analysis.
  8. HEX/BIN are used as programming images.
  9. objdump, nm, and size are essential low-level debugging tools.
  10. IDE, Makefile, and shell scripts eventually invoke the same toolchain.

Clone this wiki locally