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         在 <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      打印帮助信息.

例子如下:

        armar -r  mylib.a obj1 obj2 obj3...
        armar -x  mylib.a ?sort*
        armar -d  mylib.a hash.o
        armar -tv ansilib.a

一个小测试,先拷贝一些.o文件到当前目录下

./armar.exe -r testlib *.o //命令
Creating archive 'testlib'
./armar.exe -tv testlib //命令
rw-rw-rw-     0/     0 286432 Apr 23 11:40 2016 delay.o (offset    768)
rw-rw-rw-     0/     0 288120 Apr 24 10:56 2016 gpio.o (offset 287260)
rw-rw-rw-     0/     0 299804 Apr 23 11:40 2016 sys.o (offset 575440)
rw-rw-rw-     0/     0 286784 Apr 24 10:58 2016 test.o (offset 875304)

Administrator@PC-BigYellow MINGW64 /d/msprograms/keil5/ARM/ARMCC/bin
 ./armar.exe --zs testlib //命令
__asm___7_delay_c_f6a9c549____REV16 from delay.o    at offset    768
__asm___7_delay_c_f6a9c549____REVSH from delay.o    at offset    768
delay_init           from delay.o    at offset    768
delay_ms             from delay.o    at offset    768
delay_us             from delay.o    at offset    768
delay_xms            from delay.o    at offset    768
__asm___6_gpio_c_c8a4be98____REV16 from gpio.o     at offset 287260
__asm___6_gpio_c_c8a4be98____REVSH from gpio.o     at offset 287260
gpio_get             from gpio.o     at offset 287260
gpio_init            from gpio.o     at offset 287260
gpio_set_alt_speed   from gpio.o     at offset 287260
MSR_MSP              from sys.o      at offset 575440
__asm___5_sys_c_d144038c____REV16 from sys.o      at offset 575440
__asm___5_sys_c_d144038c____REVSH from sys.o      at offset 575440
Ex_NVIC_Config       from sys.o      at offset 575440
GPIO_AF_Set          from sys.o      at offset 575440
GPIO_Set             from sys.o      at offset 575440
INTX_DISABLE         from sys.o      at offset 575440
INTX_ENABLE          from sys.o      at offset 575440
MY_NVIC_Init         from sys.o      at offset 575440
MY_NVIC_PriorityGroupConfig from sys.o      at offset 575440
MY_NVIC_SetVectorTable from sys.o      at offset 575440
Stm32_Clock_Init     from sys.o      at offset 575440
Sys_Clock_Set        from sys.o      at offset 575440
Sys_Soft_Reset       from sys.o      at offset 575440
Sys_Standby          from sys.o      at offset 575440
WFI_SET              from sys.o      at offset 575440
__asm___6_test_c_main____REV16 from test.o     at offset 875304
__asm___6_test_c_main____REVSH from test.o     at offset 875304
__ARM_use_no_argv    from test.o     at offset 875304
main                 from test.o     at offset 875304
./armar.exe --zt testlib //命令

      Code    RO Data    RW Data    ZI Data      Debug   Object Name
       248          0          4          0     270012   delay.o
       356          0          0          0     272025   gpio.o
      1132          0          0          0     275254   sys.o
       140          0          0          0     271228   test.o
      1876          0          4          0    1088519   TOTAL

如果在keil里面勾选生成lib文件,把此lib文件复制到当前目录,执行./armar.exe –zt test_prj.lib会得到下面类似的结果

Code    RO Data    RW Data    ZI Data      Debug   Object Name
       176          5          0          0     271296   test.o
        68        392          0       1024        864   startup_stm32f40_41xxx.o
       438          0          6        200     273101   usart.o
      1132          0          0          0     275254   sys.o
       248          0          4          0     270012   delay.o
       356          0          0          0     272025   gpio.o
      2418        397         10       1224    1362552   TOTAL

>>fromelf.exe

ARM 映像转换工具
fromelf [options] input_file

选项:
       --help         显示帮助信息
       --vsn          显示版本信息
       --output file  输出文件名. (默认输出 -text 格式)
       --nodebug      不要输出调试信息到映像文件中
       --nolinkview   不要输出段信息到映像文件中

二进制输出格式:
       --bin          普通二进制
       --m32          摩托罗拉32Hex       --i32          英特尔32Hex       --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

生成hex文件此文件可直接下载到板子上执行,也由此可见,该STM32代码段从0x08000000开始,反应为生成的hex文件第一行:020000040800F2,由STM32内部的‘bootloader’决定

>>armcc.exe

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 <dev> 设置目标设备类型
--device list  输出所有被设置的目标设备列表
-o <file>      最终输出文件的名字
-c             只进行编译,不链接
--asm          输出汇编以及obj文件
-S             只输出汇编文件
--interleave   交叉反汇编 (use with --asm or -S)
-E             仅仅预处理C代码
-D<symbol>     定义 <symbol> 符号并且传入编译过程
-g             为高级别调试创建表
-I<directory>  在编译的时候包含 <directory> 作为头文件搜索目录

keil5中经过配置后的编译选项

-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

./armlink.exe -h

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:
   --partial       创建一个被分散链接的对象文件.
   --scatter file  分散加载文件.
   --ro-base n     设置执行地址空间域,包含RO段(只读数据段).
   --rw-base n     设置执行地址空间域,包含RW/ZI段.

Options for controlling image contents:
   --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       /<quals>      //比较复杂,暂不关心
--li                       ARM小端模式
--bi                       ARM大端模式

--cpu        <target-cpu>  设置目标ARMcpu类型
--device     <device>      设置目标设备类型
--fpu        <target-arch> 设置目标 FP 体系结构版本
--thumb                    以 Thumb 指令集编译
--arm                      以 ARM 指令集编译
</font></td></table>

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" 

更多文档查看这里http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.set.cortexm/index.html

3、自己写一个makefile文件进行make

#armasm.exe程序的路径
ASMCOMPILE_PATH = /d/msprograms/keil5/ARM/ARMCC/bin/armasm.exe
#汇编编译选项
#--cpu Cortex-M4.fp cpu型号是Cortex-M4.fp
#--pd "__UVISION_VERSION SETA 514"  编译之前将__UVISION_VERSION赋值为514,后者同理
ASMCOMPILE_FLAG = --cpu Cortex-M4.fp -g --apcs=interwork --pd "__UVISION_VERSION SETA 514" --pd "STM32F40_41xxx SETA 1" --xref

#armcc.exe程序的路径
CCOMPILE_PATH = /d/msprograms/keil5/ARM/ARMCC/bin/armcc.exe
#汇编编译选项
#--cpu Cortex-M4.fp cpu型号是Cortex-M4.fp
#后面的解释看上面的相关指令选项注释
CCOMPILE_FLAG = --cpu Cortex-M4.fp -g -O0 --apcs=interwork --split_sections -D__UVISION_VERSION="514" -DSTM32F40_41xxx

#fromelf.exe程序的路径
FROM_ELF_PATH = /d/msprograms/keil5/ARM/ARMCC/bin/fromelf.exe
#intel 32位hex格式
#输出文件名为test_prj.hex
#基地址为0x08000000
FROM_ELF_FLAG = --i32 --output=./test_prj.hex --base=0x08000000

#头文件查找目录,如果要添加直接添加CINCLUDE_FILE +=样式即可
CINCLUDE_FILE += -I../system/sys
CINCLUDE_FILE += -I../system/delay
CINCLUDE_FILE += -I../hardware/head
CINCLUDE_FILE += -I../system/usart
CINCLUDE_FILE += -I /e/ProjectFiles/keil5/STM32/test_prj/user/RTE
CINCLUDE_FILE += -I /d/msprograms/keil5/ARM/PACK/Keil/STM32F4xx_DFP/1.0.8/Device/Include
CINCLUDE_FILE += -I /d/msprograms/keil5/ARM/CMSIS/Include

#目标文件列表
OBJS += ../user/startup_stm32f40_41xxx.o
OBJS += ../system/usart/usart.o
OBJS += ../system/sys/sys.o
OBJS += ../system/delay/delay.o
OBJS += ../hardware/src/gpio.o
OBJS += ../user/test.o
#armlink.exe 程序所在的目录
LINK_PATH = /d/msprograms/keil5/ARM/ARMCC/bin/armlink.exe
#test_prj.sct 分散加载文件,由此文件指定代码加载地址以及代码运行地址
#info 输出指定信息
#--list 生成指定的映射文件,包括变量以及段的地址等等
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"

test_prj.axf : $(OBJS)
    echo "hellow"
    $(LINK_PATH) $(LINK_FLAG) -o $@ $^ 
    $(FROM_ELF_PATH) $(FROM_ELF_FLAG) ./test_prj.axf
%.o : %.c
    $(CCOMPILE_PATH) $(CINCLUDE_FILE) $(CCOMPILE_FLAG) -o $@ -c $< --omf_browse $(subst .c,.crf,$<) --depend $(subst .c,.d,$<)
%.o : %.s
    $(ASMCOMPILE_PATH) $(CINCLUDE_FILE) $(ASMCOMPILE_FLAG) -o $@ $< --list $(subst .s,.lst,$<) --depend $(subst .s,.d,$<)

clean :
    rm -rf $(OBJS)

#字符串查找替换
#$(subst <from>,<to>,<text>)
#把text中的from字符串替换为to字符串
#例:$(subst he,HE,hellow)
#把hellow中的he替换为HE

更多有关makefile文件书写格式,请看这里makefile书写格式

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

说明该工程用到了两个库文件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函数处执行用户代码


如果觉得本文章不错,请关注微信公众号-YellowMax多多支持,查看更多文章

欢迎转发、关注、点赞一波

微信公众号

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页