使用 Keil 编译 C 语言文件时,从 .c 文件到最终的 .bin 文件的生成过程是这样的:

1. 预处理阶段 (Preprocessing)

  • 输入.c (源文件), .h (头文件)

  • 动作纯文本替换(Copy-Paste)。

  • 细节

    • 宏展开:把所有的 #define 替换成实际的数字或代码片段。

    • 文件包含:遇到 #include "A.h",就找到 A.h 文件,把它里面的所有内容递归地复制粘贴到当前 .c 文件里。

    • extern 的处理:预处理器不关心 extern,它只是把头文件里的 extern int status; 这行字原封不动地搬进 .c 文件的文本流里。

  • 输出.i 文件(体积很大的纯 C 代码,无任何 # 指令)。

2. 编译阶段 (Compilation) —— "承诺与占位"

  • 输入.i 文件

  • 动作语法翻译(C $\rightarrow$ 汇编)。

  • extern 的作用(占位)

    • 当编译器遇到 extern void FunctionB();extern int g_Var; 时,它会检查语法是否正确。

    • 关键点:编译器不会g_Var 分配内存,也不会生成 FunctionB 的机器码。

    • 它会在生成的汇编代码中留下一个 "未解决符号 (Unresolved Symbol)" 的标记。这就好比编译器开了一张 "欠条":“这里需要用到 g_Var 的地址,但我现在不知道它在哪,先空着(占位),等链接器来填。”

  • 输出.s 文件(汇编代码,人类可读)。

3. 汇编阶段 (Assembly) —— "各路汇合"

  • 输入.s 文件(来自 main.c 等),以及启动文件 (.s)

  • 动作机器码生成

  • 关键角色:启动文件 (Startup File, e.g., startup_stm32fxxx.s)

    • 它是什么:包含了复位中断服务函数 Reset_Handler(芯片上电执行的第一句指令)和中断向量表。

    • 它从哪来:通常由芯片厂商 (ST/NXP) 提供的固件包 (SDK) 中自带。你在 Keil 工程左侧目录里手动添加的那个 .s 文件就是它。

    • 处理:汇编器会把你的 main.s 和厂商的 startup.s 分别转换成机器码。

  • 输出.o 文件(目标文件)。

    • 此时,.o 文件里的代码地址都是从 0 开始的相对地址

    • extern 的地方依然是一个空洞(欠条)

4. 链接阶段 (Linking) —— "分房与兑现"(这里gcc对应的链接脚本文件是.ld文件)

这是 IAP 开发最核心的步骤。

  • 输入:多个 .o 文件 + .lib (库文件) + 分散加载文件 (.sct)

  • 动作地址重定位 (Relocation) 和 符号解析 (Symbol Resolution)。

  • 关键角色:分散加载文件 (Scatter File, .sct / .ld)

    • 它是什么内存分配地图。它告诉链接器:“Flash 从 0x080000000x08080000 是合法的,RAM 从 0x20000000 开始。请把代码段放在 Flash,变量放在 RAM。”

    • 它从哪来

      1. 自动生成:Keil 默认根据 Options -> Target 里的设置(Start/Size)自动生成。

      2. 手动编写:你可以取消 Use Memory Layout... 勾选,自己写 .sct 文件(IAP 中常用于把特定函数强制放到 RAM 或 Flash 的特定扇区)。

  • extern 的最终归宿(兑现欠条)

    • 链接器扫描所有 .o 文件,发现 main.o 里有个 extern int g_Var 的欠条。

    • 它在 other.o 里找到了 int g_Var实体定义

    • 根据 .sct 文件,链接器决定把 g_Var 放在 RAM 的 0x20000004 位置。

    • 回填:链接器回到 main.o 的机器码里,把之前那个“占位空洞”填上 0x20000004 这个实际地址。

  • 输出.axf / .elf 文件(包含绝对物理地址的完整程序,带调试信息)。

5. 格式转换阶段 (Format Conversion)(这里gcc对应.hex文件)

  • 输入.axf 文件

  • 动作:提取与剥离。

  • 输出.hex.bin

  • 最后,如果需要生成纯二进制格式的文件,Keil 会使用 转换工具(例如 fromelf)将 .axf 文件转换成 .bin 文件。这个文件是纯粹的二进制代码,去除了所有调试和符号信息,只包含程序的机器码部分。

  • 命令示例(在Keil中使用 fromelf):

    fromelf --bin program.axf --output program.bin
    

总结:

整个流程是:

  1. .c 文件 预处理生成 .i 文件

  2. .i 文件 编译生成 .s 文件

  3. .s 文件 汇编生成 .o 文件

  4. .o 文件 链接生成 .axf 文件

  5. 最后使用 fromelf 或类似工具生成 .bin 文件(纯二进制文件)

这个流程适用于嵌入式开发环境,特别是使用 Keil 的 ARM 开发板。

 

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐