一、手工编译bin文件
1.1 KEIL 自带的编译组件
ARM v6编译器(Arm Compiler for Embedded)包含以下几个工具:
armclang | 编译器和汇编器,可对 C、C++ 汇编文件进行编译和汇编 |
armlink | 链接器,用于将目标文件和库文件进行链接,形成可执行文件。 |
fromelf | 文件转换工具,可将可执行文件转换成二进制格式、HEX 格式,也可生成反汇编文件,包含代码、数据大小信息的文件 |
armar | 归档工具,可将多个 ELF 格式的目标文件进行归档或者集合成库文件,从而避免每次都重新编译或者公开源码。 |
1.2 手工编译过程
- 使用编辑器编写 C/C++ 源文件,使用 armclang 编译器进行编译,生成相应的目标文件。
- 使用编辑器编写汇编文件,使用 armclang 进行汇编,生成对应的目标文件。
- 使用 armlink 将所有的目标文件和库一起链接,形成最终的可执行文件(ELF格式)。
- 如需要,可使用 fromelf 进行文件转换,生成二进制文件格式,或者显示代码和数据的使用信息。
1.3 手动编译开始
1.3.1 配置环境变量
首先确保环境变量包括 keil 下的 armclang 目录,这里找自己的 keil 安装目录下的 ARMCLANG 即可。
1.3.2 新建目录与文件
新建两个文件,目录结构与内容如下:
hello.c 内容如下:
int main (void) {for (;;) {}return 0;
}
start.c 内容如下:
int main (void);void start (void) {main();
}
1.3.3 执行编译命令
之后在这个目录执行:
armclang --target=arm-arm-none-eabi -mcpu=cortex-m3 -c .\start.c
armclang --target=arm-arm-none-eabi -mcpu=cortex-m3 -c .\hello.c
armlink start.o hello.o -o hello.axf
上述代码工作流程如下:
- 将 start.c 编译为目标代码,并使用默认名称 start.o 保存。
- 将 hello.c 编译为目标代码,并使用默认名称 hello.o 保存。
- 链接目标文件 start.o 和 hello.o,生成名为 hello.axf 的可执行文件。
之后该目录下就多了 hello.axf、hello.o 和 start.o 三个文件
1.3.4 执行编译命令
选项 | 描述 |
-c | 执行编译步骤,但不进行链接。 |
-x | 指定后续源文件的语言,例如 -xc inputfile.s 或 -xc++ inputfile.s。 |
-std | 指定要编译的语言标准,例如 -std=c90,其它可用选项如c99等等。 |
--target=arch-vendor-os-abi | 为选定的执行状态(AArch32 或 AArch64)生成代码,例如 --target=aarch64-arm-none-eabi 或 --target=arm-arm-none-eabi。 |
-march=name | 为指定的架构生成代码,例如 -march=armv8-a 或 -march=armv7-a。 |
-march=list | 显示所选执行状态支持的所有架构列表。 |
-mcpu=name | 为指定的处理器生成代码,例如 -mcpu=cortex-a53、-mcpu=cortex-a57 或 -mcpu=cortex-a15。 |
-mcpu=list | 显示所选执行状态支持的所有处理器列表。 |
-marm | 请求编译器针对仅由32位指令组成的A32指令集生成代码。例如,--target=arm-arm-none-eabi -march=armv7-a -marm。此选项强调性能。 |
-mthumb | 请求编译器针对由16位和32位指令组成的T32指令集生成代码。例如,--target=arm-arm-none-eabi -march=armv8-a -mthumb。此选项强调代码密度。 |
-mfloat-abi | 指定是否使用硬件指令或软件库函数进行浮点运算。 |
-mfpu | 指定目标FPU架构。 |
-g (armclang) | 生成与DWARF 4标准兼容的DWARF调试表。 |
-e | 仅执行预处理步骤。 |
-o (armclang) | 指定输出文件的名称。 |
-Onum | 指定编译源文件时使用的性能优化级别。 |
-Os | 在代码大小与速度之间取得平衡。 |
-Oz | 针对代码大小进行优化。 |
-S | 输出编译器生成的机器代码的反汇编代码。 |
-### | 显示调用编译器和链接器时将使用的选项的诊断输出,但不执行编译或链接步骤。 |
1.3.5 反汇编输出
可以查看反汇编文件,执行以下命令:
fromelf --text -c hello.axf
至此,编译器就给我们输出了反汇编文件:
PS C:\Users\P793\Desktop\new> fromelf --text -c hello.axf========================================================================** ELF Header InformationFile Name: hello.axfMachine class: ELFCLASS32 (32-bit)Data encoding: ELFDATA2LSB (Little endian)Header version: EV_CURRENT (Current version)Operating System ABI: noneABI Version: 0File Type: ET_EXEC (Executable) (2)Machine: EM_ARM (ARM)Image Entry point: 0x00008001Flags: EF_ARM_HASENTRY + EF_ARM_ABI_FLOAT_SOFT (0x05000202)ARM ELF revision: 5 (ABI version 2)Conforms to Soft float procedure-call standardBuilt withComponent: ARM Compiler 6.16 Tool: armlink [5dfeaa00]Header size: 52 bytes (0x34)Program header entry size: 32 bytes (0x20)Section header entry size: 40 bytes (0x28)Program header entries: 1Section header entries: 9Program header offset: 7728 (0x00001e30)Section header offset: 7760 (0x00001e50)Section header string table index: 8========================================================================** Program header #0 (PT_LOAD) [PF_X + PF_W + PF_R + PF_ARM_ENTRY]Size : 416 bytes (320 bytes in file)Virtual address: 0x00008000 (Alignment 4)========================================================================** Section #1 'ER_RO' (SHT_PROGBITS) [SHF_ALLOC + SHF_EXECINSTR]Size : 320 bytes (alignment 4)Address: 0x00008000$t!!!main__main0x00008000: f000f802 .... BL __scatterload ; 0x80080x00008004: f000f82c ..,. BL __rt_entry ; 0x8060!!!scatter__scatterload__scatterload_rt2__scatterload_rt2_thumb_only0x00008008: a00a .. ADR r0,{pc}+0x2c ; 0x80340x0000800a: e8900c00 .... LDM r0,{r10,r11}0x0000800e: 4482 .D ADD r10,r10,r00x00008010: 4483 .D ADD r11,r11,r00x00008012: f1aa0701 .... SUB r7,r10,#1__scatterload_null0x00008016: 45da .E CMP r10,r110x00008018: d101 .. BNE 0x801e ; __scatterload_null + 80x0000801a: f000f821 ..!. BL __rt_entry ; 0x80600x0000801e: f2af0e09 .... ADR lr,{pc}-7 ; 0x80170x00008022: e8ba000f .... LDM r10!,{r0-r3}0x00008026: f0130f01 .... TST r3,#10x0000802a: bf18 .. IT NE0x0000802c: 1afb .. SUBNE r3,r7,r30x0000802e: f0430301 C... ORR r3,r3,#10x00008032: 4718 .G BX r3$d0x00008034: 000000fc .... DCD 2520x00008038: 0000010c .... DCD 268$t!!handler_zi__scatterload_zeroinit0x0000803c: 2300 .# MOVS r3,#00x0000803e: 2400 .$ MOVS r4,#00x00008040: 2500 .% MOVS r5,#00x00008042: 2600 .& MOVS r6,#00x00008044: 3a10 .: SUBS r2,r2,#0x100x00008046: bf28 (. IT CS0x00008048: c178 x. STMCS r1!,{r3-r6}0x0000804a: d8fb .. BHI 0x8044 ; __scatterload_zeroinit + 80x0000804c: 0752 R. LSLS r2,r2,#290x0000804e: bf28 (. IT CS0x00008050: c130 0. STMCS r1!,{r4,r5}0x00008052: bf48 H. IT MI0x00008054: 600b .` STRMI r3,[r1,#0]0x00008056: 4770 pG BX lr.ARM.Collect$$libinit$$00000000__rt_lib_init0x00008058: b51f .. PUSH {r0-r4,lr}.ARM.Collect$$libinit$$00000002.ARM.Collect$$libinit$$00000004.ARM.Collect$$libinit$$0000000A.ARM.Collect$$libinit$$0000000C.ARM.Collect$$libinit$$0000000E.ARM.Collect$$libinit$$00000011.ARM.Collect$$libinit$$00000013.ARM.Collect$$libinit$$00000015.ARM.Collect$$libinit$$00000017.ARM.Collect$$libinit$$00000019.ARM.Collect$$libinit$$0000001B.ARM.Collect$$libinit$$0000001D.ARM.Collect$$libinit$$0000001F.ARM.Collect$$libinit$$00000021.ARM.Collect$$libinit$$00000023.ARM.Collect$$libinit$$00000025.ARM.Collect$$libinit$$0000002C.ARM.Collect$$libinit$$0000002E.ARM.Collect$$libinit$$00000030.ARM.Collect$$libinit$$00000032.ARM.Collect$$libinit$$00000033__rt_lib_init_alloca_1__rt_lib_init_argv_1__rt_lib_init_atexit_1__rt_lib_init_clock_1__rt_lib_init_cpp_1__rt_lib_init_exceptions_1__rt_lib_init_fp_1__rt_lib_init_fp_trap_1__rt_lib_init_getenv_1__rt_lib_init_heap_1__rt_lib_init_lc_collate_1__rt_lib_init_lc_ctype_1__rt_lib_init_lc_monetary_1__rt_lib_init_lc_numeric_1__rt_lib_init_lc_time_1__rt_lib_init_preinit_1__rt_lib_init_rand_1__rt_lib_init_return__rt_lib_init_signal_1__rt_lib_init_stdio_1__rt_lib_init_user_alloc_10x0000805a: bd1f .. POP {r0-r4,pc}.ARM.Collect$$libshutdown$$00000000__rt_lib_shutdown0x0000805c: b510 .. PUSH {r4,lr}.ARM.Collect$$libshutdown$$00000002.ARM.Collect$$libshutdown$$00000004.ARM.Collect$$libshutdown$$00000007.ARM.Collect$$libshutdown$$0000000A.ARM.Collect$$libshutdown$$0000000C.ARM.Collect$$libshutdown$$0000000F.ARM.Collect$$libshutdown$$00000010__rt_lib_shutdown_cpp_1__rt_lib_shutdown_fp_trap_1__rt_lib_shutdown_heap_1__rt_lib_shutdown_return__rt_lib_shutdown_signal_1__rt_lib_shutdown_stdio_1__rt_lib_shutdown_user_alloc_10x0000805e: bd10 .. POP {r4,pc}.ARM.Collect$$rtentry$$00000000.ARM.Collect$$rtentry$$00000002.ARM.Collect$$rtentry$$00000004__rt_entry__rt_entry_presh_1__rt_entry_sh0x00008060: f000f80d .... BL __user_setup_stackheap ; 0x807e0x00008064: 4611 .F MOV r1,r2.ARM.Collect$$rtentry$$00000009.ARM.Collect$$rtentry$$0000000A__rt_entry_li__rt_entry_postsh_10x00008066: f7fffff7 .... BL __rt_lib_init ; 0x8058.ARM.Collect$$rtentry$$0000000C.ARM.Collect$$rtentry$$0000000D__rt_entry_main__rt_entry_postli_10x0000806a: f000f85b ..[. BL main ; 0x81240x0000806e: f000f82b ..+. BL exit ; 0x80c8.ARM.Collect$$rtexit$$00000000__rt_exit0x00008072: b403 .. PUSH {r0,r1}.ARM.Collect$$rtexit$$00000002.ARM.Collect$$rtexit$$00000003__rt_exit_ls__rt_exit_prels_10x00008074: f7fffff2 .... BL __rt_lib_shutdown ; 0x805c.ARM.Collect$$rtexit$$00000004__rt_exit_exit0x00008078: bc03 .. POP {r0,r1}0x0000807a: f000f84d ..M. BL _sys_exit ; 0x8118.text__user_setup_stackheap0x0000807e: 4675 uF MOV r5,lr0x00008080: f000f82c ..,. BL __user_libspace ; 0x80dc0x00008084: 46ae .F MOV lr,r50x00008086: 0005 .. MOVS r5,r00x00008088: 4669 iF MOV r1,sp0x0000808a: 4653 SF MOV r3,r100x0000808c: f0200007 ... BIC r0,r0,#70x00008090: 4685 .F MOV sp,r00x00008092: b018 .. ADD sp,sp,#0x600x00008094: b520 . PUSH {r5,lr}0x00008096: f000f825 ..%. BL __user_initial_stackheap ; 0x80e40x0000809a: e8bd4020 .. @ POP {r5,lr}0x0000809e: f04f0600 O... MOV r6,#00x000080a2: f04f0700 O... MOV r7,#00x000080a6: f04f0800 O... MOV r8,#00x000080aa: f04f0b00 O... MOV r11,#00x000080ae: 46ac .F MOV r12,r50x000080b0: e8ac09c0 .... STM r12!,{r6-r8,r11}0x000080b4: e8ac09c0 .... STM r12!,{r6-r8,r11}0x000080b8: e8ac09c0 .... STM r12!,{r6-r8,r11}0x000080bc: e8ac09c0 .... STM r12!,{r6-r8,r11}0x000080c0: f0210107 !... BIC r1,r1,#70x000080c4: 468d .F MOV sp,r10x000080c6: 4770 pG BX lr.textexit0x000080c8: b510 .. PUSH {r4,lr}0x000080ca: 4604 .F MOV r4,r00x000080cc: f3af8000 .... NOP.W0x000080d0: 4620 F MOV r0,r40x000080d2: e8bd4010 ...@ POP {r4,lr}0x000080d6: f7ffbfcc .... B.W __rt_exit ; 0x80720x000080da: 0000 .. MOVS r0,r0.text__user_libspace__user_perproc_libspace__user_perthread_libspace0x000080dc: 4800 .H LDR r0,[pc,#0] ; [0x80e0] = 0x81400x000080de: 4770 pG BX lr$d0x000080e0: 00008140 @... DCD 33088$t.text__user_initial_stackheap0x000080e4: b500 .. PUSH {lr}0x000080e6: b085 .. SUB sp,sp,#0x140x000080e8: 4669 iF MOV r1,sp0x000080ea: aa01 .. ADD r2,sp,#40x000080ec: 600a .` STR r2,[r1,#0]0x000080ee: f04f0016 O... MOV r0,#0x160x000080f2: beab .. BKPT #0xab0x000080f4: 9801 .. LDR r0,[sp,#4]0x000080f6: 2800 .( CMP r0,#00x000080f8: bf02 .. ITTT EQ0x000080fa: 4805 .H LDREQ r0,_RW_Limit ; [0x8110] = 0x81a00x000080fc: 1dc0 .. ADDEQ r0,r0,#70x000080fe: f0200007 ... BICEQ r0,r0,#70x00008102: 9903 .. LDR r1,[sp,#0xc]0x00008104: 9a02 .. LDR r2,[sp,#8]0x00008106: 9b04 .. LDR r3,[sp,#0x10]0x00008108: b005 .. ADD sp,sp,#0x140x0000810a: bd00 .. POP {pc}$d0x0000810c: 00000009 .... DCD 9_RW_Limit0x00008110: 000081a0 .... DCD 33184$t.text__I$use$semihosting__use_no_semihosting_swi0x00008114: 4770 pG BX lr.text__semihosting_library_function0x00008116: 0000 .. MOVS r0,r0.text_sys_exit0x00008118: 4901 .I LDR r1,[pc,#4] ; [0x8120] = 0x200260x0000811a: 2018 . MOVS r0,#0x180x0000811c: beab .. BKPT #0xab0x0000811e: e7fe .. B 0x811e ; _sys_exit + 6$d0x00008120: 00020026 &... DCD 131110$t.0main0x00008124: b081 .. SUB sp,sp,#40x00008126: 2000 . MOVS r0,#00x00008128: 9000 .. STR r0,[sp,#0]0x0000812a: e7ff .. B 0x812c ; main + 80x0000812c: e7fe .. B 0x812c ; main + 80x0000812e: 0000 .. MOVS r0,r0$d.realdataRegion$$Table$$Base0x00008130: 00008140 @... DCD 330880x00008134: 00008140 @... DCD 330880x00008138: 00000060 `... DCD 960x0000813c: 0000803c <... DCD 32828Region$$Table$$Limit** Section #2 'ER_ZI' (SHT_NOBITS) [SHF_ALLOC + SHF_WRITE]Size : 96 bytes (alignment 4)Address: 0x00008140** Section #3 '.debug_frame' (SHT_PROGBITS)Size : 428 bytes** Section #4 '.symtab' (SHT_SYMTAB)Size : 2960 bytes (alignment 4)String table #5 '.strtab'Last local symbol no. 118** Section #5 '.strtab' (SHT_STRTAB)Size : 3260 bytes** Section #6 '.note' (SHT_NOTE)Size : 24 bytes (alignment 4)** Section #7 '.comment' (SHT_PROGBITS)Size : 608 bytes** Section #8 '.shstrtab' (SHT_STRTAB)Size : 76 bytes
这段输出是 ARM 编译器生成的 ELF 格式可执行文件 hello.axf
的反汇编和段信息。以下是关键点的中文分析:
- 文件头信息
- 32位ARM架构(ELFCLASS32)
- 小端格式(ELFDATA2LSB)
- 入口地址:0x00008001
- 使用软件浮点(Soft float)
- ARM编译器版本:6.16
- 程序执行流程
- 从
__main
开始(0x8000) - 调用
__scatterload
初始化数据(0x8008) - 调用
__rt_entry
进入运行时环境(0x8060) - 最终跳转到
main
函数(0x8124)
- 关键函数
__scatterload
:负责加载初始化数据(0x8008-0x8032)__scatterload_zeroinit
:清零ZI(未初始化)数据区(0x803c-0x8056)main
函数:位于0x8124,当前是一个空循环
- 内存布局
- 代码段(ER_RO):0x8000-0x813F (320字节)
- 未初始化数据段(ER_ZI):0x8140开始(96字节)
- 特殊说明
- 使用半主机模式(semihosting)进行调试输出
- 包含ARM库的初始化代码(
__rt_lib_init
等) - 程序最终进入无限循环(0x812c处的B指令)
1.4.6 bin 文件输出
而如果希望生成bin文件,则命令如下:
fromelf --bin --output=hello.bin hello.axf
bin 文件和反汇编文件的数据一样只是变成了大端模式:
1.4.6 总结
无论是我们使用手动方案还是 keil 自动所编译的,均是这个流程:
预处理部分包括:
包含头文件:将标准库头文件或自定义头文件插入到当前文件的当前位置。
宏定义与替换:处理 #define 指令,将宏定义的常量、函数式宏或代码片段替换为相应的结果。
条件编译:处理条件编译指令,用于根据条件决定是否编译某段代码
删除所有注释:删除源代码中的所有注释(/*...*/和 课//..),以减少编译器的处理负担。
#Pragma等指令:保留#pragma等指令,从而向编译器传递特定实现相关的指令。
某些特殊指令:处理某些特殊指令,如#error,用于在预处理阶段生成错误信息并停止编译,或者完成其它操作。
二、Keil 的工程构建分析
2.1 编译选项
在Option for Target中,提供了对整个工程中所有文件的相关编译、链接配置选项。
2.1.1 预处理(Preprocessor Symbols):
这里主要就是预定义功能,相当于在程序中的#define xxxx。
2.1.2 语言/代码生成(Language / Code Generation)
- Execute only Code:只生成执行代码 【设置编译器命令行:--execute_only】 只生成执行代码防止编译器生成任何数据访问代码部分。
- Warnings:警告 【No Warnings设置编译器命令行:-W】 No Warnings:不会有警告提示和输出; All Warnings:所有警告提示和输出。
- Language C。选择 C 语言规范风格。
- Optimize:优化选择项,有Level0 - Level3四个选项 【设置编译器命令行:-Onum】 初学者、在线调试建议使用Level0,也就是不优化,这样执行的效果才和代码一样。如果配置成Level3,在线调试可能有些地方优化而不能打断点。
- Plain Char is Signed:纯字符标记为字符 【设置编译器命令行:--signed_chars】 代码举例:char a[] = “abcd”; 也就是说将“abcd”标记为字符型。
- Split Load and Store Multiple:加载和存储多个分裂 【设置编译器命令行:--split_ldm】 非对齐数据采用多次访问方式。当 LMD/STM 指令有 4 个以上产生时,列分裂LMD 和 STM 指令,以减不中断延迟。
- Read-Only Position Independent:为常量生成独立的代码空间 【设置编译器命令行:--apcs=/ropi】 比如:我们定义字库变量为常量,勾选该选项,会将这些字库变量放在独立的代码空间。
- One ELF Section per Function:优化每一个函数 ELF 段 【设置编译器命令行:--split_sections】 每个函数都会产生一个 ELF 段,勾选上,允许优化每一个 ELF 段。这个选项可以减少潜在的共享地址、数据和函数之间的字符串。直白的意思:可以减少代码量ROM的大小(内存RAM不会减小)。举一个例子,勾选之前和勾选之后,编译后存储大小对比: 勾选之前: Program Size: Code=2540 RO-data=336 RW-data=40 ZI-data=1024。勾选之后: Program Size: Code=908 RO-data=320 RW-data=40 ZI-data=1024Read-Write Position Independent:为可读写代码生成独立的代码空间 【设置编译器命令行:--apcs=/rwpi】
- No Auto Includes:不自动添加头文件 不勾选该选项,编译器就会在Keil安装路径寻找你工程中.h文件。 举例:我们定义uint8_t是定义在stdint.h文件里面的,但是我们工程目录下一般是没有stdint.h文件。这时候,编译器就会在Keil路径下去寻找stdint.h文件。
在我们选择这些配置项时,下方的 Compiler control string 自动生成编译的配置选项,此选项对于我们之前的生成 .o 文件命令,只是加了一些我们勾选的参数。
armclang --target=arm-arm-none-eabi -mcpu=cortex-m3 -c .\start.c
2.2 链接选项
- Use Memory Layout from Target Dialog:勾选后,链接器直接使用“Target”选项卡中定义的内存地址(如Flash/RAM的起始和大小)。
- 作用**:避免手动维护分散加载文件(Scatter File),适合简单项目。
- Make RW Sections Position Independent:使读写段(RAM中的数据)支持地址无关(适用于动态加载或固件升级)。
- Make RO Sections Position Independent:使只读段(Flash中的代码/常量)支持地址无关(较少使用,可能增加代码大小)。
- Don't Search Standard Libraries:禁止链接标准库(需手动指定所有依赖库),用于精简代码或自定义库。
- Report 'might fail' Conditions as Errors:将潜在链接问题(如内存不足)视为错误(严格模式)。
同样的,在勾选配置时,下方的 Linker control string 也会变动,此对应我们之前的:
armlink start.o hello.o -o hello.axf
链接命令。
2.3 预处理结果
考虑一个很简单的 Keil 工程,只有两个文件:
main.c:
#define MAX(a, b) ((a) > (b) ? (a) : (b))int main()
{int max = MAX(3, 4);return 0;
}void SystemInit(void)
{// 空实现
}
startup.s:
Stack_Size EQU 0x1000AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>Heap_Size EQU 0x1000AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limitPRESERVE8THUMB; Vector Table Mapped to Address 0 at ResetAREA RESET, DATA, READONLYEXPORT __VectorsEXPORT __Vectors_EndEXPORT __Vectors_Size__Vectors DCD __initial_sp ; Top of StackDCD Reset_Handler ; Reset HandlerDCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ; Hard Fault HandlerDCD MemManage_Handler ; MPU Fault HandlerDCD BusFault_Handler ; Bus Fault HandlerDCD UsageFault_Handler ; Usage Fault HandlerDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD SVC_Handler ; SVCall HandlerDCD DebugMon_Handler ; Debug Monitor HandlerDCD 0 ; ReservedDCD PendSV_Handler ; PendSV HandlerDCD SysTick_Handler ; SysTick Handler__Vectors_End__Vectors_Size EQU __Vectors_End - __VectorsAREA |.text|, CODE, READONLY; Reset handler
Reset_Handler PROCEXPORT Reset_Handler [WEAK]IMPORT SystemInitIMPORT __mainLDR R0, =SystemInitBLX R0LDR R0, =__mainBX R0ENDP; Dummy Exception Handlers (infinite loops which can be modified)NMI_Handler PROCEXPORT NMI_Handler [WEAK]B .ENDP
HardFault_Handler\PROCEXPORT HardFault_Handler [WEAK]B .ENDP
MemManage_Handler\PROCEXPORT MemManage_Handler [WEAK]B .ENDP
BusFault_Handler\PROCEXPORT BusFault_Handler [WEAK]B .ENDP
UsageFault_Handler\PROCEXPORT UsageFault_Handler [WEAK]B .ENDP
SVC_Handler PROCEXPORT SVC_Handler [WEAK]B .ENDP
DebugMon_Handler\PROCEXPORT DebugMon_Handler [WEAK]B .ENDP
PendSV_Handler PROCEXPORT PendSV_Handler [WEAK]B .ENDP
SysTick_Handler PROCEXPORT SysTick_Handler [WEAK]B .ENDPDefault_Handler PROCB .ENDPALIGN;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************IF :DEF:__MICROLIBEXPORT __initial_spEXPORT __heap_baseEXPORT __heap_limitELSEIMPORT __use_two_region_memoryEXPORT __user_initial_stackheap__user_initial_stackheapLDR R0, = Heap_MemLDR R1, =(Stack_Mem + Stack_Size)LDR R2, = (Heap_Mem + Heap_Size)LDR R3, = Stack_MemBX LRALIGNENDIFEND
目录结构如下:
在 Keil 勾选上这个选项即可生成 .i 预编译后的文件:
在 目录下Listings 可以发现生成的 main.i 文件:
这个文件内就是预编译后的文件,因为我们的程序比较简单,预编译器只做了宏替换。
# 1 "Source/main.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 363 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "Source/main.c" 2int main()
{int max = ((3) > (4) ? (3) : (4));return 0;
}void SystemInit(void)
{}
让我们考虑一个比这个更复杂的 main.c 程序:
#include <stdio.h>
#define PI 3.14159
#define MAX(a, b) ((a) > (b) ? (a) : (b))// test
#define VERSION 1.0#ifndef VERSION
#error "VERSION is not defined!"
#endifint main() {#ifdef DEBUGprintf("Debug mode is on.\n");#endifprintf("The value of PI is: %f\n", PI);int max = MAX(3, 4);return 0;
}
在这里我们使用了:
包含头文件(#include)
处理 #include 指令,将指定文件的内容(标准库头文件(如<stdio.h>)或自定义头文件(如"myheader.h"))插入到当前文件的当前位置中。
#include <stdio.h>
#include "myheader.h"
宏定义与替换(#define)
处理#define指令,将宏定义的常量、函数式宏或代码片段替换为相应的结果。
#define PI 3.14159
#define MAX(a, b) ((a) > (b) ? (a) : (b))
条件编译(#if, #ifdef, #ifndef, #else, #elif, #endif)
处理条件编译指令,用于根据条件决定是否编译某段代码。例如,对于下面的代码,如果开启了DEBUG,则包含 printf() 调用语句,否则就跳过该语句。
#ifdef DEBUGprintf("Debug mode is on.\n");
#endif
某些特殊指令(#error)
预处理器支持某些特殊指令,如#error,用于在预处理阶段生成错误信息并停止编译,或者完成其它操作。
#ifndef VERSION
#error "VERSION is not defined!"
#endif
#Pragma等指令(#pragma)
保留#pragma等指令,从而向编译器传递特定实现相关的指令。
#pragma once // 防止头文件重复包含(非标准但广泛支持)
此时 mian.i 文件就会把所有的预编译命令进行预处理后显示在出来:
# 1 "Source/main.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 363 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "Source/main.c" 2
# 1 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 1 3
# 53 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3typedef unsigned int size_t;
# 68 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3typedef __builtin_va_list __va_list;
# 87 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
typedef struct __fpos_t_struct {unsigned long long int __pos;struct {unsigned int __state1, __state2;} __mbstate;
} fpos_t;
# 108 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
typedef struct __FILE FILE;
# 119 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
struct __FILE {union {long __FILE_alignment;char __FILE_size[84];} __FILE_opaque;
};
# 138 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern FILE __stdin, __stdout, __stderr;
extern FILE *__aeabi_stdin, *__aeabi_stdout, *__aeabi_stderr;
# 224 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int remove(const char * ) __attribute__((__nonnull__(1)));extern __attribute__((__nothrow__)) int rename(const char * , const char * ) __attribute__((__nonnull__(1,2)));
# 243 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) FILE *tmpfile(void);extern __attribute__((__nothrow__)) char *tmpnam(char * );
# 265 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fclose(FILE * ) __attribute__((__nonnull__(1)));
# 275 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fflush(FILE * );
# 285 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) FILE *fopen(const char * __restrict ,const char * __restrict ) __attribute__((__nonnull__(1,2)));
# 329 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) FILE *freopen(const char * __restrict ,const char * __restrict ,FILE * __restrict ) __attribute__((__nonnull__(2,3)));
# 342 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) void setbuf(FILE * __restrict ,char * __restrict ) __attribute__((__nonnull__(1)));extern __attribute__((__nothrow__)) int setvbuf(FILE * __restrict ,char * __restrict ,int , size_t ) __attribute__((__nonnull__(1)));
# 370 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __printf_args
extern __attribute__((__nothrow__)) int fprintf(FILE * __restrict ,const char * __restrict , ...) __attribute__((__nonnull__(1,2)));
# 393 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __printf_args
extern __attribute__((__nothrow__)) int _fprintf(FILE * __restrict ,const char * __restrict , ...) __attribute__((__nonnull__(1,2)));#pragma __printf_args
extern __attribute__((__nothrow__)) int printf(const char * __restrict , ...) __attribute__((__nonnull__(1)));#pragma __printf_args
extern __attribute__((__nothrow__)) int _printf(const char * __restrict , ...) __attribute__((__nonnull__(1)));#pragma __printf_args
extern __attribute__((__nothrow__)) int sprintf(char * __restrict , const char * __restrict , ...) __attribute__((__nonnull__(1,2)));#pragma __printf_args
extern __attribute__((__nothrow__)) int _sprintf(char * __restrict , const char * __restrict , ...) __attribute__((__nonnull__(1,2)));#pragma __printf_args
extern __attribute__((__nothrow__)) int __ARM_snprintf(char * __restrict , size_t ,const char * __restrict , ...) __attribute__((__nonnull__(3)));#pragma __printf_args
extern __attribute__((__nothrow__)) int snprintf(char * __restrict , size_t ,const char * __restrict , ...) __attribute__((__nonnull__(3)));
# 460 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __printf_args
extern __attribute__((__nothrow__)) int _snprintf(char * __restrict , size_t ,const char * __restrict , ...) __attribute__((__nonnull__(3)));#pragma __scanf_args
extern __attribute__((__nothrow__)) int fscanf(FILE * __restrict ,const char * __restrict , ...) __attribute__((__nonnull__(1,2)));
# 503 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __scanf_args
extern __attribute__((__nothrow__)) int _fscanf(FILE * __restrict ,const char * __restrict , ...) __attribute__((__nonnull__(1,2)));#pragma __scanf_args
extern __attribute__((__nothrow__)) int scanf(const char * __restrict , ...) __attribute__((__nonnull__(1)));#pragma __scanf_args
extern __attribute__((__nothrow__)) int _scanf(const char * __restrict , ...) __attribute__((__nonnull__(1)));#pragma __scanf_args
extern __attribute__((__nothrow__)) int sscanf(const char * __restrict ,const char * __restrict , ...) __attribute__((__nonnull__(1,2)));
# 541 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __scanf_args
extern __attribute__((__nothrow__)) int _sscanf(const char * __restrict ,const char * __restrict , ...) __attribute__((__nonnull__(1,2)));extern __attribute__((__nothrow__)) int vfscanf(FILE * __restrict , const char * __restrict , __va_list) __attribute__((__nonnull__(1,2)));
extern __attribute__((__nothrow__)) int vscanf(const char * __restrict , __va_list) __attribute__((__nonnull__(1)));
extern __attribute__((__nothrow__)) int vsscanf(const char * __restrict , const char * __restrict , __va_list) __attribute__((__nonnull__(1,2)));extern __attribute__((__nothrow__)) int _vfscanf(FILE * __restrict , const char * __restrict , __va_list) __attribute__((__nonnull__(1,2)));
extern __attribute__((__nothrow__)) int _vscanf(const char * __restrict , __va_list) __attribute__((__nonnull__(1)));
extern __attribute__((__nothrow__)) int _vsscanf(const char * __restrict , const char * __restrict , __va_list) __attribute__((__nonnull__(1,2)));
extern __attribute__((__nothrow__)) int __ARM_vsscanf(const char * __restrict , const char * __restrict , __va_list) __attribute__((__nonnull__(1,2)));extern __attribute__((__nothrow__)) int vprintf(const char * __restrict , __va_list ) __attribute__((__nonnull__(1)));extern __attribute__((__nothrow__)) int _vprintf(const char * __restrict , __va_list ) __attribute__((__nonnull__(1)));extern __attribute__((__nothrow__)) int vfprintf(FILE * __restrict ,const char * __restrict , __va_list ) __attribute__((__nonnull__(1,2)));
# 584 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int vsprintf(char * __restrict ,const char * __restrict , __va_list ) __attribute__((__nonnull__(1,2)));
# 594 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int __ARM_vsnprintf(char * __restrict , size_t ,const char * __restrict , __va_list ) __attribute__((__nonnull__(3)));extern __attribute__((__nothrow__)) int vsnprintf(char * __restrict , size_t ,const char * __restrict , __va_list ) __attribute__((__nonnull__(3)));
# 609 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int _vsprintf(char * __restrict ,const char * __restrict , __va_list ) __attribute__((__nonnull__(1,2)));extern __attribute__((__nothrow__)) int _vfprintf(FILE * __restrict ,const char * __restrict , __va_list ) __attribute__((__nonnull__(1,2)));extern __attribute__((__nothrow__)) int _vsnprintf(char * __restrict , size_t ,const char * __restrict , __va_list ) __attribute__((__nonnull__(3)));
# 635 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __printf_args
extern __attribute__((__nothrow__)) int __ARM_asprintf(char ** , const char * __restrict , ...) __attribute__((__nonnull__(2)));
extern __attribute__((__nothrow__)) int __ARM_vasprintf(char ** , const char * __restrict , __va_list ) __attribute__((__nonnull__(2)));
# 649 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fgetc(FILE * ) __attribute__((__nonnull__(1)));
# 659 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) char *fgets(char * __restrict , int ,FILE * __restrict ) __attribute__((__nonnull__(1,3)));
# 673 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fputc(int , FILE * ) __attribute__((__nonnull__(2)));
# 683 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fputs(const char * __restrict , FILE * __restrict ) __attribute__((__nonnull__(1,2)));extern __attribute__((__nothrow__)) int getc(FILE * ) __attribute__((__nonnull__(1)));
# 704 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3extern __attribute__((__nothrow__)) int (getchar)(void);
# 713 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) char *gets(char * ) __attribute__((__nonnull__(1)));
# 725 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int putc(int , FILE * ) __attribute__((__nonnull__(2)));
# 737 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3extern __attribute__((__nothrow__)) int (putchar)(int );extern __attribute__((__nothrow__)) int puts(const char * ) __attribute__((__nonnull__(1)));extern __attribute__((__nothrow__)) int ungetc(int , FILE * ) __attribute__((__nonnull__(2)));
# 778 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) size_t fread(void * __restrict ,size_t , size_t , FILE * __restrict ) __attribute__((__nonnull__(1,4)));
# 794 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) size_t __fread_bytes_avail(void * __restrict ,size_t , FILE * __restrict ) __attribute__((__nonnull__(1,3)));
# 810 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) size_t fwrite(const void * __restrict ,size_t , size_t , FILE * __restrict ) __attribute__((__nonnull__(1,4)));
# 822 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fgetpos(FILE * __restrict , fpos_t * __restrict ) __attribute__((__nonnull__(1,2)));
# 833 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fseek(FILE * , long int , int ) __attribute__((__nonnull__(1)));
# 850 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fsetpos(FILE * __restrict , const fpos_t * __restrict ) __attribute__((__nonnull__(1,2)));
# 863 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) long int ftell(FILE * ) __attribute__((__nonnull__(1)));
# 877 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) void rewind(FILE * ) __attribute__((__nonnull__(1)));
# 886 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) void clearerr(FILE * ) __attribute__((__nonnull__(1)));extern __attribute__((__nothrow__)) int feof(FILE * ) __attribute__((__nonnull__(1)));extern __attribute__((__nothrow__)) int ferror(FILE * ) __attribute__((__nonnull__(1)));extern __attribute__((__nothrow__)) void perror(const char * );
# 917 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int _fisatty(FILE * ) __attribute__((__nonnull__(1)));extern __attribute__((__nothrow__)) void __use_no_semihosting_swi(void);
extern __attribute__((__nothrow__)) void __use_no_semihosting(void);
# 2 "Source/main.c" 2
# 12 "Source/main.c"
int main() {printf("The value of PI is: %f\n", 3.14159);int max = ((3) > (4) ? (3) : (4));return 0;
}
其中大部分内容都是如:
extern __attribute__((__nothrow__)) int rename(const char * , const char * ) __attribute__((__nonnull__(1,2)));
# 243 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) FILE *tmpfile(void);
使用 __attribute__((__nothrow__)) 命令声明 stdio.h 文件内的函数和变量等。
__attribute__((__nothrow__)) 是GCC/Clang编译器的扩展属性,表示被修饰的函数不会抛出异常(即使函数内部有异常也会被抑制)。
其中具体的语法会在之后章节描述。