一、手工编译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 Information
File Name: hello.axf
Machine class: ELFCLASS32 (32-bit)
Data encoding: ELFDATA2LSB (Little endian)
Header version: EV_CURRENT (Current version)
Operating System ABI: none
ABI Version: 0
File Type: ET_EXEC (Executable) (2)
Machine: EM_ARM (ARM)
Image Entry point: 0x00008001
Flags: EF_ARM_HASENTRY + EF_ARM_ABI_FLOAT_SOFT (0x05000202)
ARM ELF revision: 5 (ABI version 2)
Conforms to Soft float procedure-call standard
Built with
Component: 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: 1
Section header entries: 9
Program 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
__main
0x00008000: f000f802 .... BL __scatterload ; 0x8008
0x00008004: f000f82c ..,. BL __rt_entry ; 0x8060
!!!scatter
__scatterload
__scatterload_rt2
__scatterload_rt2_thumb_only
0x00008008: a00a .. ADR r0,{pc}+0x2c ; 0x8034
0x0000800a: e8900c00 .... LDM r0,{r10,r11}
0x0000800e: 4482 .D ADD r10,r10,r0
0x00008010: 4483 .D ADD r11,r11,r0
0x00008012: f1aa0701 .... SUB r7,r10,#1
__scatterload_null
0x00008016: 45da .E CMP r10,r11
0x00008018: d101 .. BNE 0x801e ; __scatterload_null + 8
0x0000801a: f000f821 ..!. BL __rt_entry ; 0x8060
0x0000801e: f2af0e09 .... ADR lr,{pc}-7 ; 0x8017
0x00008022: e8ba000f .... LDM r10!,{r0-r3}
0x00008026: f0130f01 .... TST r3,#1
0x0000802a: bf18 .. IT NE
0x0000802c: 1afb .. SUBNE r3,r7,r3
0x0000802e: f0430301 C... ORR r3,r3,#1
0x00008032: 4718 .G BX r3
$d
0x00008034: 000000fc .... DCD 252
0x00008038: 0000010c .... DCD 268
$t
!!handler_zi
__scatterload_zeroinit
0x0000803c: 2300 .# MOVS r3,#0
0x0000803e: 2400 .$ MOVS r4,#0
0x00008040: 2500 .% MOVS r5,#0
0x00008042: 2600 .& MOVS r6,#0
0x00008044: 3a10 .: SUBS r2,r2,#0x10
0x00008046: bf28 (. IT CS
0x00008048: c178 x. STMCS r1!,{r3-r6}
0x0000804a: d8fb .. BHI 0x8044 ; __scatterload_zeroinit + 8
0x0000804c: 0752 R. LSLS r2,r2,#29
0x0000804e: bf28 (. IT CS
0x00008050: c130 0. STMCS r1!,{r4,r5}
0x00008052: bf48 H. IT MI
0x00008054: 600b .` STRMI r3,[r1,#0]
0x00008056: 4770 pG BX lr
.ARM.Collect$$libinit$$00000000
__rt_lib_init
0x00008058: 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_1
0x0000805a: bd1f .. POP {r0-r4,pc}
.ARM.Collect$$libshutdown$$00000000
__rt_lib_shutdown
0x0000805c: 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_1
0x0000805e: bd10 .. POP {r4,pc}
.ARM.Collect$$rtentry$$00000000
.ARM.Collect$$rtentry$$00000002
.ARM.Collect$$rtentry$$00000004
__rt_entry
__rt_entry_presh_1
__rt_entry_sh
0x00008060: f000f80d .... BL __user_setup_stackheap ; 0x807e
0x00008064: 4611 .F MOV r1,r2
.ARM.Collect$$rtentry$$00000009
.ARM.Collect$$rtentry$$0000000A
__rt_entry_li
__rt_entry_postsh_1
0x00008066: f7fffff7 .... BL __rt_lib_init ; 0x8058
.ARM.Collect$$rtentry$$0000000C
.ARM.Collect$$rtentry$$0000000D
__rt_entry_main
__rt_entry_postli_1
0x0000806a: f000f85b ..[. BL main ; 0x8124
0x0000806e: f000f82b ..+. BL exit ; 0x80c8
.ARM.Collect$$rtexit$$00000000
__rt_exit
0x00008072: b403 .. PUSH {r0,r1}
.ARM.Collect$$rtexit$$00000002
.ARM.Collect$$rtexit$$00000003
__rt_exit_ls
__rt_exit_prels_1
0x00008074: f7fffff2 .... BL __rt_lib_shutdown ; 0x805c
.ARM.Collect$$rtexit$$00000004
__rt_exit_exit
0x00008078: bc03 .. POP {r0,r1}
0x0000807a: f000f84d ..M. BL _sys_exit ; 0x8118
.text
__user_setup_stackheap
0x0000807e: 4675 uF MOV r5,lr
0x00008080: f000f82c ..,. BL __user_libspace ; 0x80dc
0x00008084: 46ae .F MOV lr,r5
0x00008086: 0005 .. MOVS r5,r0
0x00008088: 4669 iF MOV r1,sp
0x0000808a: 4653 SF MOV r3,r10
0x0000808c: f0200007 ... BIC r0,r0,#7
0x00008090: 4685 .F MOV sp,r0
0x00008092: b018 .. ADD sp,sp,#0x60
0x00008094: b520 . PUSH {r5,lr}
0x00008096: f000f825 ..%. BL __user_initial_stackheap ; 0x80e4
0x0000809a: e8bd4020 .. @ POP {r5,lr}
0x0000809e: f04f0600 O... MOV r6,#0
0x000080a2: f04f0700 O... MOV r7,#0
0x000080a6: f04f0800 O... MOV r8,#0
0x000080aa: f04f0b00 O... MOV r11,#0
0x000080ae: 46ac .F MOV r12,r5
0x000080b0: 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,#7
0x000080c4: 468d .F MOV sp,r1
0x000080c6: 4770 pG BX lr
.text
exit
0x000080c8: b510 .. PUSH {r4,lr}
0x000080ca: 4604 .F MOV r4,r0
0x000080cc: f3af8000 .... NOP.W
0x000080d0: 4620 F MOV r0,r4
0x000080d2: e8bd4010 ...@ POP {r4,lr}
0x000080d6: f7ffbfcc .... B.W __rt_exit ; 0x8072
0x000080da: 0000 .. MOVS r0,r0
.text
__user_libspace
__user_perproc_libspace
__user_perthread_libspace
0x000080dc: 4800 .H LDR r0,[pc,#0] ; [0x80e0] = 0x8140
0x000080de: 4770 pG BX lr
$d
0x000080e0: 00008140 @... DCD 33088
$t
.text
__user_initial_stackheap
0x000080e4: b500 .. PUSH {lr}
0x000080e6: b085 .. SUB sp,sp,#0x14
0x000080e8: 4669 iF MOV r1,sp
0x000080ea: aa01 .. ADD r2,sp,#4
0x000080ec: 600a .` STR r2,[r1,#0]
0x000080ee: f04f0016 O... MOV r0,#0x16
0x000080f2: beab .. BKPT #0xab
0x000080f4: 9801 .. LDR r0,[sp,#4]
0x000080f6: 2800 .( CMP r0,#0
0x000080f8: bf02 .. ITTT EQ
0x000080fa: 4805 .H LDREQ r0,_RW_Limit ; [0x8110] = 0x81a0
0x000080fc: 1dc0 .. ADDEQ r0,r0,#7
0x000080fe: f0200007 ... BICEQ r0,r0,#7
0x00008102: 9903 .. LDR r1,[sp,#0xc]
0x00008104: 9a02 .. LDR r2,[sp,#8]
0x00008106: 9b04 .. LDR r3,[sp,#0x10]
0x00008108: b005 .. ADD sp,sp,#0x14
0x0000810a: bd00 .. POP {pc}
$d
0x0000810c: 00000009 .... DCD 9
_RW_Limit
0x00008110: 000081a0 .... DCD 33184
$t
.text
__I$use$semihosting
__use_no_semihosting_swi
0x00008114: 4770 pG BX lr
.text
__semihosting_library_function
0x00008116: 0000 .. MOVS r0,r0
.text
_sys_exit
0x00008118: 4901 .I LDR r1,[pc,#4] ; [0x8120] = 0x20026
0x0000811a: 2018 . MOVS r0,#0x18
0x0000811c: beab .. BKPT #0xab
0x0000811e: e7fe .. B 0x811e ; _sys_exit + 6
$d
0x00008120: 00020026 &... DCD 131110
$t.0
main
0x00008124: b081 .. SUB sp,sp,#4
0x00008126: 2000 . MOVS r0,#0
0x00008128: 9000 .. STR r0,[sp,#0]
0x0000812a: e7ff .. B 0x812c ; main + 8
0x0000812c: e7fe .. B 0x812c ; main + 8
0x0000812e: 0000 .. MOVS r0,r0
$d.realdata
Region$$Table$$Base
0x00008130: 00008140 @... DCD 33088
0x00008134: 00008140 @... DCD 33088
0x00008138: 00000060 `... DCD 96
0x0000813c: 0000803c <... DCD 32828
Region$$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 0x1000
AREA 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 0x1000
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
PRESERVE8
THUMB
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC
B .
ENDP
ALIGN
;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
IF :DEF:__MICROLIB
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ALIGN
ENDIF
END
目录结构如下:
在 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" 2
int 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!"
#endif
int main() {
#ifdef DEBUG
printf("Debug mode is on.\n");
#endif
printf("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 DEBUG
printf("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" 3
typedef unsigned int size_t;
# 68 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
typedef __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" 3
extern __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" 3
extern __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编译器的扩展属性,表示被修饰的函数不会抛出异常(即使函数内部有异常也会被抑制)。
其中具体的语法会在之后章节描述。