keil编译链接过程以及ARMCC、ARMASM、FROMELF、ARMLINK、ARMAR的使用

  • 1keil5 MDK的编译工具
  • 2各工具用法
    • armarexe
    • fromelfexe
    • armccexe
    • armlinkexe
    • armasmexe
  • 3自己写一个makefile文件进行make
  • 4scatter文件分散加载文件
  • 5关于__main

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 在 插入文件, 替换掉已经存在的同名成员. -d 在 中删除成员. -x 在 中提取同名的成员. -m 在 中移动文件. -p 打印文件到标准输出设备. -a pos 插入/删除 后面的文件. -b 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 摩托罗拉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

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

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 最大闪存空间 (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" 

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

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

  • 下载make工具。链接:http://www.equation.com/servlet/equation.cmd?fa=make
  • 拷贝make.exe到自定义文件目录,只要能够找到即可

  • 编写makefile文件,如下

#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函数处执行用户代码

你可能感兴趣的:(makefile,stm32,编译链接,ARM编译工具链)