常见编译、烧录与调试工具介绍
本文介绍软件开发和嵌入式开发中常见的编译、链接、调试、烧录工具,例如 gcc、g++、gdb、pyocd、openocd 等。理解这些工具之间的关系,有助于看懂一个程序从源代码变成可运行程序,或者从源代码变成单片机固件的完整过程。
1. 从源码到运行的大致流程
一个 C/C++ 程序通常会经历以下步骤:
text
源代码
|
| 编译
v
目标文件 .o
|
| 链接
v
可执行文件 .elf / .exe
|
| 格式转换
v
固件文件 .bin / .hex
|
| 烧录
v
芯片 Flash
|
| 调试
v
运行、断点、单步、查看变量
在普通桌面程序中,生成可执行文件后通常就可以直接运行。
在嵌入式开发中,还需要把程序烧录到单片机、开发板或其他目标设备中。
2. 编译器
编译器负责把人写的源代码转换成机器能够理解的目标代码。
gcc
gcc 是 GNU Compiler Collection 中最常见的 C 语言编译器。
常见用途:
- 编译 C 语言源文件。
- 生成目标文件
.o。 - 生成可执行程序。
- 在嵌入式开发中,也可以通过交叉编译器生成目标芯片可运行的程序。
示例:
bash
gcc main.c -o main
含义是把 main.c 编译成名为 main 的可执行文件。
g++
g++ 是 GNU 的 C++ 编译器,用于编译 C++ 程序。
示例:
bash
g++ main.cpp -o main
gcc 和 g++ 的区别:
gcc默认用于 C 语言。g++默认用于 C++ 语言。g++在链接时会自动链接 C++ 标准库。- 用
gcc编译 C++ 程序时,可能需要手动指定 C++ 标准库。
clang / clang++
clang 和 clang++ 是 LLVM 项目的 C/C++ 编译器。
特点:
- 编译速度快。
- 报错信息通常比较友好。
- 常用于现代 C/C++ 项目。
- 在 macOS、Linux、嵌入式和大型工程中都很常见。
交叉编译
交叉编译器是指"在一种平台上编译出另一种平台运行的程序"的编译器。
例如:
- 在电脑上编译 STM32 单片机程序。
- 在 x86 Linux 上编译 ARM Linux 程序。
- 在电脑上编译 RISC-V 裸机程序。
常见交叉编译器:
arm-none-eabi-gcc:ARM Cortex-M 裸机嵌入式 C 编译器。arm-none-eabi-g++:ARM Cortex-M 裸机嵌入式 C++ 编译器。riscv64-unknown-elf-gcc:RISC-V 裸机交叉编译器。avr-gcc:AVR 单片机常用编译器。
其中 none-eabi 大致表示:
none:没有操作系统。eabi:嵌入式应用二进制接口。
所以 arm-none-eabi-gcc 常用于没有 Linux、Windows 这类操作系统的 ARM 单片机项目。
3. 汇编、链接和二进制工具
编译器不是单独完成所有工作的。一个完整工具链中,还包括汇编器、链接器和各种二进制分析工具。
as
as 是 GNU 汇编器,用于把汇编代码转换成目标文件。
源文件可能是:
.s.S
.S 文件通常会先经过预处理器处理,而 .s 一般直接作为汇编文件处理。
ld
ld 是 GNU 链接器。
链接器负责把多个 .o 目标文件、库文件和启动文件合并成最终的可执行文件。
在嵌入式开发中,链接器还要根据链接脚本 .ld 决定代码和数据放到芯片内存的什么位置。
例如:
- 程序代码放到 Flash。
- 全局变量放到 RAM。
- 中断向量表放到 Flash 开头。
ar
ar 用来创建和管理静态库,常见输出文件是 .a。
示例:
bash
ar rcs libmath.a add.o sub.o
这表示把 add.o 和 sub.o 打包成静态库 libmath.a。
objcopy
objcopy 用于转换二进制文件格式。
在嵌入式中非常常见,例如把 .elf 转成 .bin:
bash
arm-none-eabi-objcopy -O binary firmware.elf firmware.bin
也可以转成 Intel HEX 格式:
bash
arm-none-eabi-objcopy -O ihex firmware.elf firmware.hex
objdump
objdump 用于查看目标文件或可执行文件的内容。
常见用途:
- 反汇编。
- 查看段信息。
- 分析生成的机器指令。
示例:
bash
arm-none-eabi-objdump -d firmware.elf
readelf
readelf 用于查看 ELF 文件结构。
常见用途:
- 查看 ELF 头。
- 查看段表。
- 查看符号表。
- 查看程序入口地址。
示例:
bash
readelf -h firmware.elf
nm
nm 用于查看目标文件、库文件或 ELF 文件中的符号。
常见用途:
- 查看函数是否存在。
- 查看全局变量符号。
- 分析链接错误。
示例:
bash
nm firmware.elf
size
size 用于查看程序占用的代码和数据大小。
示例:
bash
arm-none-eabi-size firmware.elf
常见输出中的字段:
text:代码段,通常放在 Flash。data:已初始化全局变量,通常需要占用 Flash 和 RAM。bss:未初始化全局变量,通常占用 RAM。
strip
strip 用于去掉可执行文件中的符号和调试信息,从而减小文件体积。
桌面程序发布时可能会用到。
嵌入式调试阶段一般不要随便去掉调试信息,否则会影响 GDB 调试体验。
4. 调试工具
调试工具用于观察程序运行状态,例如设置断点、单步执行、查看变量、查看寄存器、查看内存。
gdb
gdb 是 GNU Debugger,是 C/C++ 开发中非常常见的调试器。
常见功能:
- 设置断点。
- 单步执行。
- 查看变量。
- 查看调用栈。
- 查看寄存器。
- 查看内存。
- 远程连接目标设备。
示例:
bash
gdb ./main
常见 GDB 命令:
gdb
break main
run
next
step
continue
print variable_name
backtrace
quit
arm-none-eabi-gdb
arm-none-eabi-gdb 是面向 ARM 裸机嵌入式目标的 GDB。
它通常不会直接运行程序,而是连接到一个调试服务,例如:
OpenOCDpyOCDJ-Link GDB ServerST-LINK GDB Server
示例:
bash
arm-none-eabi-gdb firmware.elf
连接远程调试服务:
gdb
target remote localhost:3333
lldb
lldb 是 LLVM 项目的调试器。
它和 gdb 类似,常见于:
- macOS 开发。
- LLVM/Clang 生态。
- 一些现代 C/C++ 项目。
gdbserver
gdbserver 用于远程调试 Linux 程序。
典型场景:
- 目标设备上运行
gdbserver。 - 开发电脑上运行
gdb。 - 两者通过网络连接。
示例:
bash
gdbserver :1234 ./app
电脑端连接:
gdb
target remote target_ip:1234
5. 嵌入式调试和烧录工具
嵌入式开发中,程序需要下载到芯片 Flash 中。调试时,还需要通过调试器访问芯片内部状态。
常见调试接口:
- JTAG
- SWD
- UART Bootloader
- USB DFU
pyOCD
pyOCD 是一个 Python 编写的 ARM Cortex-M 调试和烧录工具。
它常用于支持 CMSIS-DAP / DAPLink 的开发板。
常见功能:
- 擦除芯片。
- 烧录固件。
- 启动 GDB Server。
- 复位目标板。
- 查看目标芯片信息。
常见命令:
bash
pyocd list
pyocd flash firmware.bin
pyocd erase --chip
pyocd gdbserver
适合场景:
- ARM Cortex-M 单片机。
- CMSIS-DAP 调试器。
- DAPLink 开发板。
- 希望用 Python 工具链管理烧录和调试流程。
OpenOCD
OpenOCD 是 Open On-Chip Debugger 的缩写,是非常常见的开源嵌入式调试和烧录工具。
常见功能:
- 启动 GDB Server。
- 通过 JTAG/SWD 调试芯片。
- 烧录 Flash。
- 复位目标芯片。
- 查看寄存器和内存。
OpenOCD 通常需要指定:
- 调试器配置文件。
- 目标芯片配置文件。
示例:
bash
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
启动后,GDB 可以连接:
gdb
target remote localhost:3333
适合场景:
- STM32。
- nRF。
- RISC-V。
- 使用 ST-LINK、J-Link、CMSIS-DAP 等调试器。
- 需要灵活配置调试流程。
J-Link 工具
J-Link 是 SEGGER 公司的调试器,支持大量芯片。
常见工具:
JLinkExeJLinkGDBServerJFlash
特点:
- 支持芯片多。
- 速度快。
- 商业工具链中很常见。
- 与 Keil、IAR、VS Code、GDB 等工具都可以配合。
ST-LINK 工具
ST-LINK 是 STMicroelectronics 常用的 STM32 调试器。
常见工具:
STM32CubeProgrammerST-LINK GDB Serverst-flash
常见用途:
- 烧录 STM32 固件。
- 擦除 STM32 芯片。
- 通过 SWD 调试 STM32。
示例:
bash
st-flash write firmware.bin 0x08000000
其中 0x08000000 通常是 STM32 内部 Flash 的起始地址。
esptool.py
esptool.py 是 Espressif 官方常用的 ESP8266 / ESP32 烧录工具。
常见用途:
- 烧录 ESP32 固件。
- 擦除 Flash。
- 读取芯片信息。
示例:
bash
esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash
avrdude
avrdude 常用于 AVR 单片机烧录,例如 ATmega328P。
Arduino UNO 背后也经常会使用 avrdude 完成程序下载。
示例:
bash
avrdude -p m328p -c arduino -P /dev/ttyUSB0 -b 115200 -U flash:w:firmware.hex
dfu-util
dfu-util 用于通过 USB DFU 协议烧录固件。
DFU 是 Device Firmware Upgrade 的缩写,很多芯片和开发板支持通过 USB 进入 DFU 模式后升级固件。
示例:
bash
dfu-util -a 0 -D firmware.bin
nrfjprog
nrfjprog 是 Nordic nRF 系列芯片常用的命令行烧录工具。
常见用途:
- 烧录 nRF52 / nRF53 固件。
- 擦除芯片。
- 复位芯片。
- 读取芯片状态。
示例:
bash
nrfjprog --program firmware.hex --chiperase --reset
probe-rs
probe-rs 是 Rust 生态中的嵌入式调试和烧录工具。
常见用途:
- 烧录 ARM / RISC-V 固件。
- 与 Rust 嵌入式项目配合。
- 启动调试会话。
它在 Rust 嵌入式开发中越来越常见。
6. 构建系统
项目变大以后,不可能每次都手动输入一长串编译命令。构建系统用于自动管理编译、链接、依赖关系和输出文件。
make
make 是经典构建工具,读取 Makefile 执行构建规则。
示例:
bash
make
make clean
优点:
- 简单直接。
- 在 C/C++ 和嵌入式项目中非常常见。
- 很多开源项目都支持。
缺点:
- 复杂项目中的 Makefile 可能比较难维护。
CMake
CMake 是跨平台构建系统生成器。
它本身通常不直接编译代码,而是生成其他构建系统需要的文件,例如:
- Makefile
- Ninja 构建文件
- Visual Studio 工程
常见流程:
bash
cmake -S . -B build
cmake --build build
适合场景:
- 跨平台 C/C++ 项目。
- 需要管理多个库和可执行文件。
- 需要支持 Linux、Windows、macOS。
- 需要和 IDE 配合。
Ninja
Ninja 是一个高速构建工具。
它通常由 CMake 或 Meson 生成构建文件,然后由 Ninja 执行实际构建。
示例:
bash
cmake -S . -B build -G Ninja
ninja -C build
特点:
- 速度快。
- 输出简洁。
- 适合大型项目。
Meson
Meson 是现代构建系统,通常配合 Ninja 使用。
示例:
bash
meson setup build
meson compile -C build
west
west 是 Zephyr RTOS 常用的项目管理和构建工具。
常见用途:
- 管理多个 Git 仓库。
- 配置 Zephyr 工程。
- 编译固件。
- 烧录固件。
- 调试目标板。
示例:
bash
west build -b nrf52840dk_nrf52840 app
west flash
west debug
7. 包管理和工具链管理
包管理器用于安装编译器、调试器、库和其他开发工具。
常见工具:
apt:Debian / Ubuntu 包管理器。dnf:Fedora 包管理器。pacman:Arch Linux 包管理器。brew:macOS 常用包管理器。pip:Python 包管理器,常用于安装pyocd、esptool.py。conda:Python 环境和包管理工具。vcpkg:C/C++ 包管理器。conan:C/C++ 包管理器。rustup:Rust 工具链管理器。cargo:Rust 构建和包管理工具。
示例:
bash
pip install pyocd
pip install esptool
8. 常见文件类型
源代码文件
.c:C 源文件。.cpp/.cc/.cxx:C++ 源文件。.h:C/C++ 头文件。.hpp:C++ 头文件。.s:汇编源文件。.S:会经过预处理的汇编源文件。
编译中间文件
.o:目标文件,已经编译但还没有完成链接。.a:静态库。.so:Linux 动态库。.dll:Windows 动态库。.dylib:macOS 动态库。
可执行和固件文件
.elf:Executable and Linkable Format,嵌入式开发中非常常见,包含程序、符号、调试信息等。.exe:Windows 可执行文件。.bin:裸二进制文件,通常用于烧录。.hex:Intel HEX 格式文件,常用于烧录。.uf2:常见于一些开发板的拖拽式烧录格式。
辅助文件
.map:链接映射文件,可以查看函数、变量和段的地址分布。.ld:链接脚本,用于控制程序在内存中的布局。.json:配置文件,某些工程和工具会使用。.cfg:配置文件,OpenOCD 等工具常见。
9. 常见硬件调试器
软件工具通常需要配合硬件调试器,才能访问单片机或 SoC。
ST-LINK
ST-LINK 是 STM32 开发中最常见的调试器之一。
特点:
- 常见于 STM32 Nucleo、Discovery 开发板。
- 支持 SWD。
- 可用于烧录和调试 STM32。
J-Link
J-Link 是 SEGGER 的调试器。
特点:
- 支持芯片种类多。
- 速度快。
- 工业和商业开发中很常见。
- 可配合 GDB、Keil、IAR、OpenOCD 等工具。
CMSIS-DAP
CMSIS-DAP 是 ARM 定义的一种调试接口标准。
特点:
- 开源生态支持较好。
pyOCD支持良好。- 很多开发板集成 CMSIS-DAP 或 DAPLink。
DAPLink
DAPLink 是基于 CMSIS-DAP 的开源调试和烧录方案。
常见特点:
- 支持调试。
- 支持串口转发。
- 有些开发板支持拖拽
.bin或.hex文件完成烧录。
Black Magic Probe
Black Magic Probe 是一种特殊的调试器,它本身内置 GDB Server。
使用时通常可以直接让 GDB 连接调试器,不一定需要 OpenOCD 或 pyOCD 作为中间层。
ULINK
ULINK 是 Keil 生态中常见的 ARM 调试器。
常见于:
- Keil MDK。
- 商业 ARM 嵌入式开发。
PICkit
PICkit 是 Microchip 常见的调试和烧录工具。
常见用于:
- PIC 单片机。
- AVR 单片机。
- Microchip 生态项目。
10. 一个典型 STM32 编译烧录调试流程
下面以 STM32 为例,说明这些工具如何配合。
第一步:编写源码
常见文件:
text
main.c
startup_stm32.s
stm32.ld
其中:
main.c是主程序。startup_stm32.s是启动文件。stm32.ld是链接脚本。
第二步:编译
bash
arm-none-eabi-gcc -c main.c -mcpu=cortex-m4 -mthumb -o main.o
生成:
text
main.o
第三步:链接
bash
arm-none-eabi-gcc main.o startup_stm32.o -T stm32.ld -o firmware.elf
生成:
text
firmware.elf
第四步:查看大小
bash
arm-none-eabi-size firmware.elf
用于确认程序是否超过芯片 Flash 或 RAM 容量。
第五步:转换固件格式
bash
arm-none-eabi-objcopy -O binary firmware.elf firmware.bin
生成:
text
firmware.bin
第六步:烧录
使用 OpenOCD:
bash
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program firmware.elf verify reset exit"
或者使用 st-flash:
bash
st-flash write firmware.bin 0x08000000
第七步:调试
先启动 OpenOCD:
bash
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
再启动 GDB:
bash
arm-none-eabi-gdb firmware.elf
在 GDB 中连接目标:
gdb
target remote localhost:3333
break main
continue
这样就可以在单片机上设置断点、单步执行和查看变量。
11. 各工具之间的关系总结
| 类别 | 工具 | 主要作用 |
|---|---|---|
| C 编译器 | gcc |
编译 C 程序 |
| C++ 编译器 | g++ |
编译 C++ 程序 |
| LLVM 编译器 | clang / clang++ |
编译 C/C++ 程序 |
| ARM 交叉编译器 | arm-none-eabi-gcc |
编译 ARM 裸机程序 |
| 汇编器 | as |
汇编源码生成目标文件 |
| 链接器 | ld |
把目标文件和库链接成可执行文件 |
| 静态库工具 | ar |
创建 .a 静态库 |
| 格式转换 | objcopy |
.elf 转 .bin / .hex |
| 反汇编 | objdump |
查看汇编和目标文件内容 |
| ELF 分析 | readelf |
查看 ELF 文件结构 |
| 符号分析 | nm |
查看函数和变量符号 |
| 大小分析 | size |
查看代码和数据占用 |
| 调试器 | gdb |
调试本机程序 |
| 嵌入式调试器 | arm-none-eabi-gdb |
调试 ARM 嵌入式程序 |
| 调试烧录服务 | OpenOCD |
连接 GDB 和目标芯片 |
| 调试烧录工具 | pyOCD |
ARM Cortex-M 烧录和调试 |
| STM32 烧录 | st-flash / STM32CubeProgrammer |
烧录 STM32 |
| ESP 烧录 | esptool.py |
烧录 ESP8266 / ESP32 |
| AVR 烧录 | avrdude |
烧录 AVR 单片机 |
| DFU 烧录 | dfu-util |
通过 USB DFU 烧录 |
| 构建工具 | make |
根据 Makefile 构建项目 |
| 构建生成器 | CMake |
生成 Makefile 或 Ninja 工程 |
| 高速构建 | Ninja |
快速执行构建 |
| Zephyr 工具 | west |
管理、构建、烧录 Zephyr 项目 |
12. 初学者应该先掌握哪些
如果刚开始学习 C/C++ 或嵌入式开发,建议按下面顺序理解:
gcc/g++:知道如何把源代码编译成程序。.c/.cpp/.h/.o/.elf/.bin:知道常见文件的含义。make:知道如何自动化编译。gdb:知道如何断点调试。objcopy/size:知道嵌入式固件如何转换和查看大小。OpenOCD或pyOCD:知道如何烧录和调试单片机。CMake/Ninja:知道现代项目如何组织构建。
13. 最核心的一句话
这些工具可以按职责理解:
text
gcc / g++ / clang 负责把源码编译成目标文件
ld 负责把目标文件链接成程序
objcopy / size / nm 负责转换和分析生成文件
gdb / lldb 负责调试程序
OpenOCD / pyOCD 负责连接调试器和芯片
st-flash / esptool 负责把固件写入芯片
make / CMake / Ninja 负责自动化整个构建过程
把这些工具放到完整流程中看,就不会觉得它们是零散的命令,而是一条从"源代码"到"程序运行"的工具链。