[转]keil编译链接过程以及ARMCCARMASMFROMELFARMLINKARMAR的使用

Posted zhangsx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[转]keil编译链接过程以及ARMCCARMASMFROMELFARMLINKARMAR的使用相关的知识,希望对你有一定的参考价值。

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 [email protected] $^ 
46     $(FROM_ELF_PATH) $(FROM_ELF_FLAG) ./test_prj.axf
47 %.o : %.c
48      $(CCOMPILE_PATH) $(CINCLUDE_FILE) $(CCOMPILE_FLAG) -o [email protected] -c $< --omf_browse $(subst .c,.crf,$<) --depend $(subst .c,.d,$<)
49 %.o : %.s
50      $(ASMCOMPILE_PATH) $(CINCLUDE_FILE) $(ASMCOMPILE_FLAG) -o [email protected] $< --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.lfz_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

对原文进行整理,也进行了一些简单的测试,纠正了几处翻译的错误。

以上是关于[转]keil编译链接过程以及ARMCCARMASMFROMELFARMLINKARMAR的使用的主要内容,如果未能解决你的问题,请参考以下文章

Keil C51工程转VSCode Keil Assistant开发全过程

Keil C51工程转VSCode Keil Assistant开发全过程

Keil编译过程及程序在Flash和SRAM的空间分配

Keil> 编译器特有的功能 > 关键字和运算符 > __weak

keil4如何在编译时不编译未调用的函数?

keil怎样看编译产生的汇编代码