|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
/ V4 S$ ?9 J& W# Y& Q
ARMCC(Keil)map 文件
" M+ q( d2 R V& S 根据选择的参数不同,map 文件中的内容肯定是有变化的!下面以 Keil 中全选以上所说的参数后生成的 map 文件为例来进行说明。map 文件中,有如下图所示的五个大部分:( l7 w& t6 `; c4 K- K3 `* I
l5 n, y/ L1 |7 I! P+ n2 K$ D
2 z! x, m( d8 d. ^其中,每一部分都是对应上面的配置中的一个或者多个选项,也就是对应链接器armlink的一个或者多个参数
. N h) c4 e% x8 C$ t/ a) i9 f; h. j+ H* y
6 _7 _7 O, i1 x# {
Section Cross References) v" |! Q) n, f0 v
9 b. ~3 q3 m8 `& \( u: e, _
该部分显示了节区之间的交叉引用,指的是各个源文件生成的模块之间相互引用的关系。交叉引用中可以分为三大部分。 d+ w G% ^$ y1 {/ M1 o& p3 y
& k2 a q; P) t- b- _
用户模块之间交叉引用' S+ n" N! f* [' a2 g4 Z
4 L- S" T- W7 A& w( L$ ? 这部分主要就是用户自己实现的代码之间的引用关系。例如gprs.o(i.GPRSGetBytes) refers to uart.o(i.UartGetData) for UartGetData 表示 gprs.o 中的函数GPRSGetBytes 引用了 uart.o 中的函数 UartGetData。其中的 gprs.o 是由用户代码 gprs.c 生成的模块; uart.o 使用用户源码 uart.c 生成的模块。' g* V7 ?7 N) V' N, z8 P) d
& g( Z# p+ e5 F9 F- W6 S5 S用户模块与 C 库交叉引用
2 U8 N+ z& Q4 h- m
# z0 I* S4 n" r* C6 D: ? 这部分主要就是用户自己实现的代码中调用 C 库函数时的引用关系。C 库的代码一般都是编译套件以二进制文件的形式提供的,用户看不到源码。例如 upcomm.o(i.UpCommFrameFilter) refers to mEMCmp.o(.text) for memcmp 表示 upcomm.o 中的函数UpCommFrameFilter 引用了 memcmp.o 中的函数 memcmp。其中,memcmp 为 C 库函数。再例如 startup_stm32f411xe.o(.text) refers to __main.o(!!!main) for __main 中的 __main 为程序的启动入口,其也位于 C 库中。
/ v8 v& {! c" s+ {+ P+ ]0 I' F2 l0 D3 @1 |5 W0 H& w9 h. x: L% I& U* ?/ w" K' W
: r: k" O3 l3 _6 Y5 Z: F
在 ARM 编译套件中,所有的 C 库由工具armar来管理,位于 ARM 编译器目录 lib 下。使用 armar 即可从指定的库文件中解压出 __main.o 等模块。至于如何操作,参见博文ARM 之九 Cortex-M/R 内核启动过程 / 程序启动流程(基于ARMCC、Keil)。
e" K9 D) p; A3 l/ U
8 T3 V/ n; C- }0 u- V1 b9 |
/ r$ l' l+ n1 T+ Q NC 库之间交叉引用$ n# A; u- a! f$ r$ f
; y3 B4 o/ c" Y3 u5 ^# w) A
C 库函数之间也有互相的引用关系。用户使用的 C 库函数之间的引用关系也会显示在该部分。例如 dflt_clz.o(x$fpl$dflt) refers (Special) to usenofp.o(x$fpl$usenofp) for __I$use$fp,其中所有的函数都是 C 库中的。同样可以使用armar解压库文件来导出符号表查看各接口。7 ^* i( S3 e& Z D7 t0 f
% q K. q4 i; y$ ^9 [6 f% w" b0 U+ {( t
Removing Unused input sections from the image' r } |. J7 q, G( X
' D5 ?- V( o8 Z6 g" Q. K, Q
这部分列出了链接器移除的我们源码中实际未使用的数据和函数。其中包含移除数据的大小。例如Removing flash.o(.rrx_text), (6 bytes). 表示移除flash.o中的 6 字节的数据;Removing virtualuart.o(i.VirtualUartBufClear), (92 bytes). 表示移除 virtualuart.o 中的函数 VirtualUartBufClear,共 92 字节。( Z6 }* U: |% [/ J% ]" I
2 A$ \3 H. B1 y1 p: |6 C/ ]& e" U9 e
需要注意的是,被移除的函数在调试时将无法进行调试。如果不注意,在调试时很容易造成困扰。如下图所示:/ H1 j# R' g r8 g k( v; T
* K+ K) j9 f; O: u+ d% g# k& ?$ h) @ ~$ e3 h# h6 `
$ L( J J c' v9 m+ u* \" G
1 X1 v3 F- L. L9 }, G8 \
' l; P0 f2 S0 b: Z5 j% y
Image Symbol Table
6 G! p; u" b# q" I; U/ b7 \5 b- h) ^; |+ k7 z
符号映射表。就是每个符号(这里的符号可以指模块、变量、函数)实际的地址等信息。% } v3 e0 `" G- ^2 c
6 Z* b9 l/ h4 z) I
: W% k# r+ d& k- j; E& W% L- `Local Symbols! s5 M! }- Y; E3 p/ y
9 Z+ x2 T+ r! A. i# J7 N! y不知道 ARMCC 是怎么分的 Local 和 Global。
+ B& {7 A1 m& m. j: D
, f5 }/ H0 i5 {/ D, V" y& D3 X7 k5 Z% v5 B+ V5 t5 V+ U0 j
Global Symbols$ v% H$ f1 c# c8 F# _. C0 B8 Y. D* t
4 ?4 T6 b0 y! g8 ~5 s) \/ ^不知道 ARMCC 是怎么分的 Local 和 Global。6 Z* Q D6 v! N
- y2 u* r q' i3 i- Y* R0 d! B% l! x
# }& [& a4 P- W% i9 fMemory Map of the image
. F8 X- o" |. p& F4 P* g2 |' K$ c+ _! o9 V' l7 \
这部分给出了镜像文件的布局。这部分与分散加载文件(Scatter File) 有密切的关系。如果使用 Keil,在 Keil 的配置界面中有如下配置:
4 C9 }- A/ F2 @) h, p4 F/ G7 @
5 I5 |- _+ X* ] h+ j$ r0 S' I, O$ m2 _# z
" H$ f) c" A! q* t3 C
1 j: z1 f5 L' N/ o+ k/ Y0 }, {* G这里的设置就是对应的 map 文件中的内容。如果我们在链接器的配置页面不选择 Use Memory Layout from Taget Dialog,则需要如上图自己指定一个分散加载文件。其实,如果选择 Use Memory Layout from Taget Dialog,Keil 会根据我们左边的配置自行生成一个分散加载文件来给链接器使用(这个文件就在我们的编译输出指定的目录中)。上图的示例就是没有使用Keil默认,而是通过自己指定的分散加载文件生成镜像文件。) ~) G& n0 h5 N8 A5 R6 ^
/ H" ~) j! [" n9 [* l/ Y' p+ t; ]& Y6 \+ z2 j& N
以上的 Keil 配置,全部是是通过链接器的参数--scatter=filename来让链接器使用该文件的。分散加载文件一次性描述了我们的镜像文件怎么布局。了解链接器的应该知道,链接器还有一些独立使用的和镜像文件生成有关的参数:--first, --last, --partial, --reloc, --ro_base, --ropi,--rosplit, --rw_base, --rwpi, --split, --startup, --xo_base, and --zi_base.,如果使用了 --scatter=filename,则以上参数就不可再用了!Keil 就是直接使用的 --scatter=filename。(默认下,Keil 根据配置界面的配置,会成一个分散加载文件)。, {7 e# J* w z$ \4 b! X5 a' i9 X
% G8 O8 h8 @4 l( e/ }7 B
接下来看看 map 文件中的内容,如下图: C# L G) [4 m+ D/ p+ F
* f; d4 b' ]7 q9 W1 M% k
( Z1 E( ^0 Q3 U, D; c+ C
% W e/ f: w" V# U- d8 ~9 n- Image Entry point 这个是镜像的入口点。就是镜像在被执行时,开始的位置(地址)。其就是指向了 0x08010218 0x00000008 Code RO 4985 * !!!main c_w.l(__main.o) 这一行。
- Load Region LR_IROM1 加载域。这是个概念。
- Execution Region ER_IROM1、Execution Region ER_IROM2、Execution Region ER_IROM3 这其中就是我们的代码以及使用的 C 库代码。
, }6 l* `2 y6 c' y% L" b0 {2 V
5 b8 H# x! U$ W0 ^. F9 L 我们先来介绍一下每个字段的含义。首先我们要明白,每一行就是一个节。# {/ T% z/ j c3 ]) w( Z5 L9 t# z
2 ^7 w |( [( Y1 B[color=rgba(0, 0, 0, 0.75)]Base AddrSizeTypeAttrIdxE Section NameObject
' C- {9 ]' L$ m/ g' U# M节的基地址节的大小类型属性索引节的名字对象(节所属的文件模块)
% t! S- }* s0 D6 b' g+ N9 h3 g/ U 为什么有 3 个?因为我本身使用了自己写的分散加载文件。手动指定了三个执行域。我的分散加载文件如下:
( e: _1 s1 U/ J6 P
7 Z' m% r7 C5 I! A; ************************************************************** P. h4 N* s9 _
; *** Scatter-Loading Description File generated by ZCShou ***" r0 s, t6 h8 [5 v* ?1 l: q. ~
; *************************************************************% i1 N$ T, B) h* R2 o v9 t( q
# B0 L& M) q+ H
2 d& W( ]# q) M) A+ a3 l2 CLR_IROM1 0x08010000 0x00030000 { ; load region size_region
- o8 \5 n5 r. g! Y" ? ER_IROM1 0x08010000 0x00030000 { ; load address = execution address
# p2 v- }4 N! d* L1 Y *.o (RESET, +First) ; 中断向量表
4 n. `* l! [. i1 Q1 \9 `( s }! {/ a3 z/ U% C) c+ ~9 C
$ L' j1 ]6 j5 s/ G% b9 Y ER_IROM2 + 0 { ; 应用程序信息
# ~, i; w" K& ]% t& ] *.o (SECTION_APP_INFO, +First)1 R- N, T% t, A& H2 Y
}& V7 w. @' A3 u- ^! R
6 f! [- v) h7 o& t/ `5 A ER_IROM3 + 0 { ; 初始化相关代码+其他代码
. [- @- ]/ r1 N) U8 X9 }! N *(InRoot\$ \$Sections) ; 初始化相关$ W( ]7 y7 i/ m
.ANY (+RO) ; 其他所有代码
$ E% v% M6 F% y: ~' M. ?( U/ v }
5 K+ r' S ~3 m) O5 ?4 B& Q( l( a9 |; j9 ^ S
RW_IRAM1 0x20000000 0x00020000 { ; 内存, w7 M4 F9 ]: p6 r" w! t
.ANY (+RW +ZI)$ p, q, J& R/ G! Q# X
}
: c7 T% ?! Z, X, Q}
- f* i5 U) q7 J, f3 t0 l1 s8 |4 J# I4 ]& X
& L5 L4 ?- ^) J* u4 [; r& ?
在这三个执行域中,有很多类型为 PAD 的行,并且这些行没有节名字也没有所属的模块。这些其实是一些链接器自己添加的对齐。除了对齐没有其他作用。关于对齐本文之前的章节有介绍。除了 PAD 之外,剩下的就全是 Code 了。+ c, I# g; i2 ]
8 J9 z I" b9 @# q- Execution Region RW_IRAM1: 这个就是内存部分,存放我们的代码中用到的各种变量数据(常量数据在以上的 ER_IROM 中)。同样,该部分也有些对齐,除此之外全部是 Data、Zero、 HEAP(堆)、STACK(栈)。
# c% T3 {) c! S7 _/ q
; C1 B& k6 P* jImage component sizes
6 I# f6 W; Q4 ~. p, g2 A
+ r T9 G7 U2 \! L4 A该部分列出了组成镜像的各部分内容的大小等详细信息。
, v) B9 p, D O; O( A
% s2 \9 }' Z9 L- 各列的具体含义如下:5 o* {6 h$ v6 |0 u. T
- Code (inc. data):这对应两列数据,分别表示代码占用的字节数和内联数据占用的字节数。inc. data 是内联数据(inline data)的缩写。 内联数据包括文字池和短字符串等。
- RO Data:显示 RO 数据占用的字节数。这是 Code(inc. data)列中除去 inc. data 外的只读数据的字节数。 This is in addition to the inline data included in the Code (inc. data) column.
- RW Data:显示 RW 数据占用的字节数。
- ZI Data:显示 ZI 数据占用的字节数。
- Debug:显示调试数据占用的字节数,例如,调试输入节以及符号和字符串表。
- 最后一列:对象文件的名字。下面会有区分。
" ?4 _0 x( _7 E) u" r
- 特殊行的含义如下:; c1 y4 a* d/ r0 U4 w4 r
- Object Totals:显示链接在一起以生成镜像的对象占用的字节数。
- (incl. Generated):armlink 在生成镜像文件时,可能会产生一些额外数据(interworking veneers, and input sections such as region tables)。如果存在这些额外数据,那么他们就位于改行中显示。
- Library Totals:显示已提取并作为单个对象添加到镜像的库成员占用的字节数。
- (incl. Padding):如果需要,armlink 会插入填充以强制部分对齐。 如果 Object Totals 行包含此类数据,则会在相关的(incl. Padding)行中显示。 同样,如果 Library Totals 行包含此类数据,则会在其关联的行中显示。
- Grand Totals:显示镜像文件的真实大小。
- ELF Image Totals:如果使用 RW 数据压缩(默认值)来优化 ROM 大小,则最终镜像的大小会发生变化,这会反映在 --info 的输出中。 比较 Grand Totals 和 ELF Image Totals 下的字节数,以查看压缩效果。
- ROM Totals:显示包含镜像所需的 ROM 的最小大小。 这不包括未存储在 ROM 中的 ZI 数据和调试信息。( V3 F3 w) m |" c
' U( _% u, \9 d- Q
0 i) U" t, L9 _6 m6 b4 b |
|