|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
, d* N3 L' d( T" [
ARMCC(Keil)map 文件) |- Y9 t* a- U( ~% W
根据选择的参数不同,map 文件中的内容肯定是有变化的!下面以 Keil 中全选以上所说的参数后生成的 map 文件为例来进行说明。map 文件中,有如下图所示的五个大部分:4 E9 N- _- o: |; T
4 k" u, t! W! Y5 O3 ~& ^
- p; E% ?3 J+ n$ s# X3 V3 o; g. J其中,每一部分都是对应上面的配置中的一个或者多个选项,也就是对应链接器armlink的一个或者多个参数
9 X* K1 b- i1 U" U* P& F2 i' i, _; V6 s! e
& b3 w! C& r# `Section Cross References! s3 \+ d, e( m' x% B2 `3 W9 T6 e
! y M9 `: Q. ^3 Y5 u- u$ [2 g$ K! \( a 该部分显示了节区之间的交叉引用,指的是各个源文件生成的模块之间相互引用的关系。交叉引用中可以分为三大部分。
6 l- P+ T5 x2 D5 F) k& y8 D/ M* E2 w7 S% d$ }: z+ S
用户模块之间交叉引用
) `0 U8 E3 Z! Z1 J {2 v5 M/ i! n# z7 ^) e5 V9 J$ c
这部分主要就是用户自己实现的代码之间的引用关系。例如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 生成的模块。8 z- F/ F) ~/ @" K2 q: S
. I0 [) C' r8 d8 e. R* p用户模块与 C 库交叉引用
$ D8 O( g% ?8 p; J- T7 o, h3 B; ~ t
这部分主要就是用户自己实现的代码中调用 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 库中。
( ]3 \+ ?6 N4 U& D9 T/ |( i
: ~9 `( L4 A% Q, I( G( e. U- [8 H3 H# E
在 ARM 编译套件中,所有的 C 库由工具armar来管理,位于 ARM 编译器目录 lib 下。使用 armar 即可从指定的库文件中解压出 __main.o 等模块。至于如何操作,参见博文ARM 之九 Cortex-M/R 内核启动过程 / 程序启动流程(基于ARMCC、Keil)。 }. S/ w! |7 y2 `0 J; g' s
; ^( y4 n4 Z6 B2 \. H
" p: c; C) Y( k# ^7 @" \4 V
C 库之间交叉引用) u" K3 v4 E$ z* f; m8 A7 l0 T
2 G' } E6 K$ n* C- {1 U; F7 s; z
C 库函数之间也有互相的引用关系。用户使用的 C 库函数之间的引用关系也会显示在该部分。例如 dflt_clz.o(x$fpl$dflt) refers (Special) to usenofp.o(x$fpl$usenofp) for __I$use$fp,其中所有的函数都是 C 库中的。同样可以使用armar解压库文件来导出符号表查看各接口。. W) D# Z) R1 k$ x* W B6 y
$ b; x9 V0 f( c9 s6 @* k
4 u& d/ |1 N9 L5 m) d# eRemoving Unused input sections from the image- |( b3 g5 @# l/ @7 q8 }
9 g, x7 W9 ^: o6 y
这部分列出了链接器移除的我们源码中实际未使用的数据和函数。其中包含移除数据的大小。例如Removing flash.o(.rrx_text), (6 bytes). 表示移除flash.o中的 6 字节的数据;Removing virtualuart.o(i.VirtualUartBufClear), (92 bytes). 表示移除 virtualuart.o 中的函数 VirtualUartBufClear,共 92 字节。9 G% z% T; D# v$ w9 {
% `. ?/ U4 ~* q( Q, {. [
, K T. e& S6 f% v4 Q
需要注意的是,被移除的函数在调试时将无法进行调试。如果不注意,在调试时很容易造成困扰。如下图所示:: R/ {. W0 Q8 T8 `# p) o
! c0 e, Q4 [. p# q: O# r
( H7 S) L$ E2 ~6 Q$ {2 v# j* e" Q
" {6 q4 L6 r; L- @
% q; d% _2 w( ~4 x, ^! V5 @0 w2 T, R
Image Symbol Table
1 O. T" k7 Y6 E- p" ~) [- B' h$ {: j, |5 s# p( E+ E9 o
符号映射表。就是每个符号(这里的符号可以指模块、变量、函数)实际的地址等信息。) F# Q1 _- @+ J9 A. m$ x) x, J
. R! I: f5 @! G$ [8 E7 k" T7 R2 J5 ~5 x
1 j7 j. \: Z2 C- d. Q8 lLocal Symbols
/ F* f N6 ^% f5 |9 g/ @" u
% m, y6 B# \& \5 b* D不知道 ARMCC 是怎么分的 Local 和 Global。: }- t0 m& |) S. a& i" U
, @. Q% _8 A) s* I& i" I# S+ h3 `& b0 v
Global Symbols4 {. O4 N% L' |, n( J6 {! z( J
! W# c5 R& Y3 ]1 f% {9 n1 E8 M不知道 ARMCC 是怎么分的 Local 和 Global。
' M; u7 u- Y; Y- C% b' Y+ ?8 p2 a2 V+ q; ?6 u) ?
' W0 a7 U% p) n e$ |) D, `Memory Map of the image
3 c: _* S9 T g- K' \6 F' D
& S% z+ o& F) P4 j) d& P$ `1 U! }6 _ 这部分给出了镜像文件的布局。这部分与分散加载文件(Scatter File) 有密切的关系。如果使用 Keil,在 Keil 的配置界面中有如下配置:) v& }* ]8 E3 u2 s) K t' I; C/ m; R, N s
# y4 m7 T, p( s
; g) K( k3 d: [; j/ n, {# `3 c* F% G, O/ S
4 [6 z2 Y2 g- A5 B" J
8 X* S' O% O& E- K6 z U这里的设置就是对应的 map 文件中的内容。如果我们在链接器的配置页面不选择 Use Memory Layout from Taget Dialog,则需要如上图自己指定一个分散加载文件。其实,如果选择 Use Memory Layout from Taget Dialog,Keil 会根据我们左边的配置自行生成一个分散加载文件来给链接器使用(这个文件就在我们的编译输出指定的目录中)。上图的示例就是没有使用Keil默认,而是通过自己指定的分散加载文件生成镜像文件。7 |8 e! G5 C4 w+ Z; M* G2 U8 `
, B p b' v' }1 P) U
; v0 h% n8 r. G c' Y
以上的 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 根据配置界面的配置,会成一个分散加载文件)。/ _/ F0 Y9 d( n O; A# V
( l3 W E ~( [* a接下来看看 map 文件中的内容,如下图:
8 {5 u7 U4 X$ D# o, P( `
: K- s1 M/ A8 h: A' R0 A
- C W1 }9 l7 q4 @0 S% {* z, l8 ]( n+ _3 H+ u/ e
- 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 库代码。
& D7 G( X! p6 X8 Y8 f
+ T" X2 Y& n; x4 Z0 P) u/ D( u; N F 我们先来介绍一下每个字段的含义。首先我们要明白,每一行就是一个节。( J$ y( m/ K( P& c- y' [+ r! ]
% M/ h4 ^* f6 l- n4 c* ~[color=rgba(0, 0, 0, 0.75)]Base AddrSizeTypeAttrIdxE Section NameObject
7 L! [ k \) {: S `节的基地址节的大小类型属性索引节的名字对象(节所属的文件模块)
6 n0 i( |3 h, h 为什么有 3 个?因为我本身使用了自己写的分散加载文件。手动指定了三个执行域。我的分散加载文件如下:0 a( ~1 d- H' M1 r9 z$ Q* y P
. D. S8 j$ e% M% m& g( @# h; *************************************************************
8 L" P0 B( s/ k* G6 c* D( \; *** Scatter-Loading Description File generated by ZCShou ***
% [' F9 m* c. C; *************************************************************
& D+ Q% U+ b6 V4 @9 B! n6 V& t3 c- i3 s1 q: _8 {/ P( G o6 B6 O; ]
2 q6 V, ]8 ]" z3 NLR_IROM1 0x08010000 0x00030000 { ; load region size_region3 N5 Y' k. u G" K1 v6 N; m
ER_IROM1 0x08010000 0x00030000 { ; load address = execution address
( ]/ |$ c# y! t1 B* i *.o (RESET, +First) ; 中断向量表# V7 A& i. z/ s9 @" z
}
* \) T! B1 l: b7 m. Z( j% [2 O( n- ]4 V( B7 |
ER_IROM2 + 0 { ; 应用程序信息( ^5 w% M3 v0 U# [
*.o (SECTION_APP_INFO, +First)' w2 o+ ]: h2 G1 @+ C7 x0 b
}: @- y; C+ Y7 \0 j
4 ^* K/ \$ O/ w% t3 a
ER_IROM3 + 0 { ; 初始化相关代码+其他代码0 D0 ]7 c* M( ^ ]' O' X5 W3 q
*(InRoot\$ \$Sections) ; 初始化相关( O$ f6 n) R ~3 e1 W
.ANY (+RO) ; 其他所有代码
& S* u- ~' f# C( e( I }* F3 ]1 R" C/ r8 r) z9 q
" ?/ s- b2 W7 g+ u RW_IRAM1 0x20000000 0x00020000 { ; 内存# l1 @8 t9 R' Z
.ANY (+RW +ZI)
0 M" C; Q9 F+ n7 J: f- } }
" D2 X4 A" k) S, E}" U4 N; W F2 c4 d) p
9 M- o2 B' c# |. K7 J& O
' U6 E2 N: {' n& k# m& k在这三个执行域中,有很多类型为 PAD 的行,并且这些行没有节名字也没有所属的模块。这些其实是一些链接器自己添加的对齐。除了对齐没有其他作用。关于对齐本文之前的章节有介绍。除了 PAD 之外,剩下的就全是 Code 了。0 x' w) A3 `/ {7 }
. ?& f! x! r0 M& l
- Execution Region RW_IRAM1: 这个就是内存部分,存放我们的代码中用到的各种变量数据(常量数据在以上的 ER_IROM 中)。同样,该部分也有些对齐,除此之外全部是 Data、Zero、 HEAP(堆)、STACK(栈)。
' V1 T1 T; `/ x& E + i/ w0 ?" \$ K. M) w5 @
Image component sizes
7 X7 B( x4 n( a) t" ^% E: n& @1 p B$ B) O8 K& R" ^* R ]; E% Y
该部分列出了组成镜像的各部分内容的大小等详细信息。0 v4 L9 `6 q0 s, G3 G7 x* H
$ j8 n# b! J, _0 g
- 各列的具体含义如下:
3 ~ R" u$ H; R8 v% X
- 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:显示调试数据占用的字节数,例如,调试输入节以及符号和字符串表。
- 最后一列:对象文件的名字。下面会有区分。
. J+ o, D8 E; U0 X
- 特殊行的含义如下:
0 _$ H( K7 A! g8 v( |$ @
- 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 数据和调试信息。
' V" H1 h8 L) D3 \; s " t) ~0 B9 u- o' k
5 P' n/ P7 A0 \* S8 r& Y9 O |
|