[转]keil编译链接过程以及ARMCC、ARMASM、FROMELF、ARMLINK、ARMAR的使用
1、keil5 MDK的编译工具
armar.exe armasm.exe armcc.exe armlink.exe fromelf.exe
以及动态链接库
armcompiler_libFNP.dll
2、各工具用法
>>>armar.exe
可以在windows下使用命令行切换到该程序所在文件夹(keil5\ARM\ARMCC\bin),执行armar.exe -h进行命令查看。若有gitbash的话直接在该文件夹下右键选择gitbash here,之后运行./armar.exe -h。获得以下结果(下面是已经翻译过的,省略部分不重要的)
产品: MDK Professional 5.14 组件: ARM Compiler 5.05 update 1 (build 106) 工具: armar [4d0efa] 文件创建以及维护工具。 库文件:.lib或者.a 命令格式: armar options archive [ file_list ] 选项: -r 插入在file_list中的文件, 替换掉已经存在的同名成员. -d 删除在file_list中的成员. -x 在archive中提取file_list中同名的成员. -m 在file_list中移动文件. -p 打印文件到标准输出设备. -a pos 插入/删除pos后面的文件. -b pos 插入/删除pos前面的文件. -u 只更新旧的文件, 与 -r 一起使用. -n 不要向object文件中添加符号表. -s 强制重新生成文档符号表. -t 打印文档的内容表. --zs 显示符号表. --zt 汇总文档内容 (大小和输入). -c 当一个新文档被创建的时候不显示警告. -C 提取的时候不要覆盖一个已经存在的文件. -T 截取系统最大长度文件名. -v 提供详细输出. --create 强制创建一个新的文档. --via file 从 via 文件中获取额外参数. --sizes 列出所有成员大小与库的总大小. --entries 列出包括入口点的部分. --vsn 打印最新的armar版本. --help 打印帮助信息.例子:
D:\Keil\ARM\ARMCC\bin>armar.exe -r testlib __main.o _rcc.o _it.o //把__main.o _rcc.o _it.o 合并到testlib中 Creating archive 'testlib' D:\Keil\ARM\ARMCC\bin>armar.exe -tv testlib //打印文档的内容表 rw-rw-rw- 0/ 0 1072 Sep 18 18:09 2016 __main.o (offset 934) rw-rw-rw- 0/ 0 338632 Sep 08 20:23 2016 _rcc.o (offset 2066) rw-rw-rw- 0/ 0 328392 Sep 08 20:23 2016 _it.o (offset 340758) D:\Keil\ARM\ARMCC\bin>armar.exe -d testlib _it.o D:\Keil\ARM\ARMCC\bin>armar.exe -tv testlib rw-rw-rw- 0/ 0 1072 Sep 18 18:09 2016 __main.o (offset 774) rw-rw-rw- 0/ 0 338632 Sep 08 20:23 2016 _rcc.o (offset 1906)
>>fromelf.exe
ARM 映像转换工具 fromelf [options] input_file 选项: --help 显示帮助信息 --vsn 显示版本信息 --output file 输出文件名. (默认输出 -text 格式) --nodebug 不要输出调试信息到映像文件中 --nolinkview 不要输出段信息到映像文件中 二进制输出格式: --bin 普通二进制 --m32 摩托罗拉32位Hex码 --i32 英特尔32位Hex码 --vhx 定向字节的 Hex 格式 --base addr 为 m32,i32设置基地址(可选的) 输出格式要求的调试信息 --fieldoffsets Structures/Classes的汇编描述 --expandarrays Arrays inside and outside structures are expanded 其他输出格式: --elf ELF格式 --text 文本信息 文本信息的标志 -v 详细信息 -a 打印数据的地址信息 (得到的.axf映像文件) -c 汇编码 -d 打印数据的段内容 -e 打印例表 -g 打印调试表 -r 打印重定位信息 -s 打印符号表 -t 打印字符表 -y 打印段内容分析 -z 打印代码与数据的大小信息例子
fromelf.exe --i32 --output=./test_prj.hex --base=0x08000000 ./test_prj.axf
>>armcc.exe
Product: MDK Standard 5.12 Component: ARM Compiler 5.05 (build 41) Tool: armcc [4d0eb9] For support see http://www.arm.com/support Software supplied by: ARM Limited Usage: armcc [options] file1 file2 ... filen 主要选项: --arm 创建 ARM 代码 --thumb 创建 Thumb 代码 --c90 切换到C模式 (默认是 .c 文件) --cpp 切换到C++模式 (默认 .cpp 文件) -O0 最小优化级别 -O1 受限的调试级别优化 -O2 高优化 -O3 最大优化 -Ospace 对代码大小进行优化 -Otime 优化最大优化级别的运行时间 --cpu <cpu> 选择CPU --cpu list 输出所有可以选择的CPU列表 --device 设置目标设备类型 --device list 输出所有可以选择的目标设备列表 -o file 最终输出文件的名字 -c 只进行编译,不链接 --asm 输出汇编以及obj文件 -S 只输出汇编文件 --interleave 交叉反汇编 (use with --asm or -S) -E 仅仅预处理C代码 -D <symbol> 定义<symbol>并且传入编译过程 -g 为高级别调试创建表 -I <directory> 在编译的时候包含<directory>作为头文件搜索目录keil中经过配置后的选项:
-c --cpu Cortex-M4.fp -g -O0 --apcs=interwork --split_sections -I../system/usart -I../system/sys -I../system/delay -I../hardware/head -I E:\ProjectFiles\keil5\STM32\test_prj\user\RTE -I D:\msprograms\keil5\ARM\PACK\Keil\STM32F4xx_DFP\1.0.8\Device\Include -I D:\msprograms\keil5\ARM\CMSIS\Include -D__UVISION_VERSION="514" -DSTM32F40_41xxx -o "..\obj\*.o" --omf_browse "..\obj\*.crf" --depend "..\obj\*.d"这种方式编译就不在赘述,比较简单,介绍下如何在自制makefile并且进行编译
1. 首先下载make.exe,链接在此:http://www.equation.com/servlet/equation.cmd?fa=make
2. 拷贝到keil的armcc的bin目录下,在工程文件中编写makefile
3. 执行make即可实现自制make
>>armlink.exe
Usage: armlink option-list input-file-list where option-list是不区分大小写的选项列表. input-file-list是输入对象和库文件列表. General options (abbreviations shown capitalised): --output file 指定输出文件名. Options for specifying memory map information: (指定map的信息) --partial 创建一个被分散链接的对象文件. --scatter file 按照分散加载文件的描述创建map文件. (Create the memory map as described in file.) --ro-base n 设置执行地址空间域,包含RO段(只读数据段). --rw-base n 设置执行地址空间域,包含RW/ZI段. Options for controlling image contents: (控制image的内容) --bestdebug 添加调试信息为image提供调试视图. --datacompressor off 不要压缩RW数据段. --no_debug 不添加调试信息. --entry 指定输入段与输入点. --libpath 指定系统库文件路径. --userlibpath 指定用户库文件路径. --no_locals 不要添加局部标号到image的标号列表. --no_remove 不要移除image的未使用段. Options for controlling image related information: --callgraph 创建一个函数静态调用图. --info topic 列出image信息. Available topics: (separate multiple topics with comma) common List common sections eliminated from the image. debug List eliminated input debug sections. sizes List code and data sizes for objects in image. totals List total sizes of all objects in image. veneers List veneers that have been generated. unused List sections eliminated from the image. --map 显示image内存映射. --symbols 列出image中的符号. --xref 列出输入的段之间所有的交叉引用.最终输出会放在.map文件里面keil里面的配置
--cpu Cortex-M4.fp *.o --strict --scatter "..\obj\test_prj.sct" --summary_stderr --info summarysizes --map --xref --callgraph --symbols --info sizes --info totals --info unused --info veneers --list "..\obj\test_prj.map" -o ..\obj\test_prj.axf
>>armasm.exe
Usage: armasm [options] sourcefile Options: --list listingfile 生成列表文件 -o outputfile 最终输出文件名 --depend dependfile 保留 'make' 源文件依赖 --errors errorsfile 把标准错误判断放入errorsfile -I dir[,dir] 添加源文件的搜索目录 --pd --predefine directive 预执行 SET{L,A,S} 指令 --maxcache <n>最大闪存空间 (default 8MB) --no_esc 忽略C文件 --no_warn 关闭警告信息 -g 输出调试表 --apcs / //比较复杂,暂不关心 --li ARM小端模式 --bi ARM大端模式 --cpu 设置目标ARMcpu类型 --device 设置目标设备类型 --fpu 设置目标 FP 体系结构版本 --thumb 以 Thumb 指令集编译 --arm 以 ARM 指令集编译keil里面的配置
--cpu Cortex-M4.fp -g --apcs=interwork -I E:\ProjectFiles\keil5\STM32\test_prj\user\RTE -I D:\msprograms\keil5\ARM\PACK\Keil\STM32F4xx_DFP\1.0.8\Device\Include -I D:\msprograms\keil5\ARM\CMSIS\Include --pd "__UVISION_VERSION SETA 514" --pd "STM32F40_41xxx SETA 1" --list "..\obj\*.lst" --xref -o "*.o" --depend "*.d"
3、自己写一个makefile文件进行make
下载make工具。链接:http://www.equation.com/servlet/equation.cmd?fa=make
拷贝make.exe到自定义文件目录,只要能够找到即可
编写makefile文件,如下
1 #armasm.exe程序的路径 2 ASMCOMPILE_PATH = /d/msprograms/keil5/ARM/ARMCC/bin/armasm.exe 3 #汇编编译选项 4 #--cpu Cortex-M4.fp cpu型号是Cortex-M4.fp 5 #--pd "__UVISION_VERSION SETA 514" 编译之前将__UVISION_VERSION赋值为514,后者同理 6 ASMCOMPILE_FLAG = --cpu Cortex-M4.fp -g --apcs=interwork --pd "__UVISION_VERSION SETA 514" --pd "STM32F40_41xxx SETA 1" --xref 7 8 #armcc.exe程序的路径 9 CCOMPILE_PATH = /d/msprograms/keil5/ARM/ARMCC/bin/armcc.exe 10 #汇编编译选项 11 #--cpu Cortex-M4.fp cpu型号是Cortex-M4.fp 12 #后面的解释看上面的相关指令选项注释 13 CCOMPILE_FLAG = --cpu Cortex-M4.fp -g -O0 --apcs=interwork --split_sections -D__UVISION_VERSION="514" -DSTM32F40_41xxx 14 15 #fromelf.exe程序的路径 16 FROM_ELF_PATH = /d/msprograms/keil5/ARM/ARMCC/bin/fromelf.exe 17 #intel 32位hex格式 18 #输出文件名为test_prj.hex 19 #基地址为0x08000000 20 FROM_ELF_FLAG = --i32 --output=./test_prj.hex --base=0x08000000 21 22 #头文件查找目录,如果要添加直接添加CINCLUDE_FILE +=样式即可 23 CINCLUDE_FILE += -I../system/sys 24 CINCLUDE_FILE += -I../system/delay 25 CINCLUDE_FILE += -I../hardware/head 26 CINCLUDE_FILE += -I../system/usart 27 CINCLUDE_FILE += -I /e/ProjectFiles/keil5/STM32/test_prj/user/RTE 28 CINCLUDE_FILE += -I /d/msprograms/keil5/ARM/PACK/Keil/STM32F4xx_DFP/1.0.8/Device/Include 29 CINCLUDE_FILE += -I /d/msprograms/keil5/ARM/CMSIS/Include 30 31 #目标文件列表 32 OBJS += ../user/startup_stm32f40_41xxx.o 33 OBJS += ../system/usart/usart.o 34 OBJS += ../system/sys/sys.o 35 OBJS += ../system/delay/delay.o 36 OBJS += ../hardware/src/gpio.o 37 OBJS += ../user/test.o 38 39 LINK_PATH = /d/msprograms/keil5/ARM/ARMCC/bin/armlink.exe 40 41 LINK_FLAG = --cpu Cortex-M4.fp --strict --scatter "test_prj.sct" --summary_stderr --info summarysizes --map --xref --callgraph --symbols --info sizes --info totals --info unused --info veneers --list "..\obj\test_prj.map" 42 43 test_prj.axf : $(OBJS) 44 echo "hellow" 45 $(LINK_PATH) $(LINK_FLAG) -o $@ $^ 46 $(FROM_ELF_PATH) $(FROM_ELF_FLAG) ./test_prj.axf 47 %.o : %.c 48 $(CCOMPILE_PATH) $(CINCLUDE_FILE) $(CCOMPILE_FLAG) -o $@ -c $< --omf_browse $(subst .c,.crf,$<) --depend $(subst .c,.d,$<) 49 %.o : %.s 50 $(ASMCOMPILE_PATH) $(CINCLUDE_FILE) $(ASMCOMPILE_FLAG) -o $@ $< --list $(subst .s,.lst,$<) --depend $(subst .s,.d,$<) 51 52 clean : 53 rm -rf $(OBJS) 54 55 #字符串查找替换 56 #$(subst <from>,<to>,<text>) 57 #把text中的from字符串替换为to字符串 58 #例:$(subst he,HE,hellow) 59 #把hellow中的he替换为HE</text></to></from></code>
4、scatter文件(分散加载文件)
keil建立STM32F407ZG工程之后进行build的时候会相应的生成一个此文件,在编译与链接之前,由此推测在keil中次文件内容应该是由工程配置选项中的单片机型号所对应的信息生成的。
; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* LR_IROM1 0x08000000 0x00100000 { ; 加载域,也就是代码文件下载到0x08000000 代码段最大尺寸为0x00100000(1M) ER_IROM1 0x08000000 0x00100000 { ; 执行域,也就是从0x08000000开始执行,执行空间为0x00100000大小 *.o (RESET, +First) ;所有的.o文件存放位置,First标示该段位于该执行域的最开始,+ 表示连续放置,以RESET段作为开始,RESET在启动文件里面 *(InRoot$$Sections) ;一些库文件的加载,启动文件STM32F4xx.s里面跳转到__main而不是main函数就有这句话的作用。
而__main函数里面会执行RW,ZI的"解压缩",也就是把这些数据初始化并且放在它的执行域当中去。
__main是一个库函数,执行完解压缩之后才会跳转到真正的main函数处执行代码 .ANY (+RO) ;所有的RO数据存放位置,RO数据为常量数据,运行过程不会被改变。
.ANY则是编译器根据情况可将该段放在该执行域任意位置,+ 表示连续放置 } RW_IRAM1 0x20000000 0x00020000 { ; 变量数据段,从0x20000000开始,最大尺寸为0x00020000,此属于RAM区 .ANY (+RW +ZI) ;所有的RW以及ZI数据 } }RW:可读可写的变量数据,所以不可放在ROM区,也就是单片机的Flash
ZI:初始化为0的变量数据,依然不可放在ROM
RO:只读数据,放在ROM区,掉电不丢失
5、关于__main
在keil编译后生成的.map文件当中可以看到类似于下面的信息
---------------------------------------------------------------------- Code (inc. data) RO Data RW Data ZI Data Debug Library Member Name 8 0 0 0 0 68 __main.o 104 0 0 0 0 84 __printf.o 0 0 0 0 0 0 __rtentry.o 12 0 0 0 0 0 __rtentry2.o 6 0 0 0 0 0 __rtentry4.o 52 8 0 0 0 0 __scatter.o 26 0 0 0 0 0 __scatter_copy.o 28 0 0 0 0 0 __scatter_zi.o 44 0 0 0 0 108 _printf_char.o 48 6 0 0 0 96 _printf_char_common.o 36 4 0 0 0 80 _printf_char_file.o 6 0 0 0 0 0 _printf_d.o 120 16 0 0 0 92 _printf_dec.o 178 0 0 0 0 88 _printf_intcommon.o 0 0 0 0 0 0 _printf_percent.o 4 0 0 0 0 0 _printf_percent_end.o 6 0 0 0 0 0 _printf_s.o 82 0 0 0 0 80 _printf_str.o 12 0 0 0 0 72 exit.o 8 0 0 0 0 68 ferror.o 6 0 0 0 0 152 heapauxi.o 2 0 0 0 0 0 libinit.o 6 0 0 0 0 0 libinit2.o 2 0 0 0 0 0 libshutdown.o 2 0 0 0 0 0 libshutdown2.o 8 4 0 0 96 68 libspace.o 24 4 0 0 0 84 noretval__2printf.o 2 0 0 0 0 0 rtexit.o 10 0 0 0 0 0 rtexit2.o 74 0 0 0 0 80 sys_stackheap_outer.o 2 0 0 0 0 68 use_no_semi.o 2 0 0 0 0 68 use_no_semi_2.o 10 0 0 0 0 116 fpinit.o ---------------------------------------------------------------------- 942 42 0 0 100 1472 Library Totals 12 0 0 0 4 0 (incl. Padding) ---------------------------------------------------------------------- Code (inc. data) RO Data RW Data ZI Data Debug Library Name 920 42 0 0 96 1356 c_w.l 10 0 0 0 0 116 fz_wm.l ---------------------------------------------------------------------- 942 42 0 0 100 1472 Library Totals</code>说明该工程用到了两个库文件c_w.l与fz_wm.l,在keil的安装目录下搜索可以得到它们在\ARM\ARMCC\lib\armlib目录下。做一个小实验
把c_w.l文件拷贝到\ARM\ARMCC\bin目录下,还记得这个目录里面装的有armar.exe吧,现在可以派上用场了,在该目录下打开命令行(shift+鼠标右键)。
执行./armar.exe –zt c_w.l可以看到里面所有的.o文件,其中就有__main.o,更多的文件请自行查看。 使用./armar.exe -x c_w.l __main.o提取出来__main.o文件,可以自行查看其反汇编文件
总之__main的作用是做RW,ZI等等数据的重定位以及初始化,之后再次跳转到真正的main函数处执行用户代码。
注:转载自http://www.2cto.com/kf/201605/504594.html
对原文进行整理,也进行了一些简单的测试,纠正了几处翻译的错误。