|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
( U# R: u3 }. v) I# f' G9 G3 }/ y
ARMCC(Keil)map 文件2 S9 F. n4 }2 ^0 y1 v# p
根据选择的参数不同,map 文件中的内容肯定是有变化的!下面以 Keil 中全选以上所说的参数后生成的 map 文件为例来进行说明。map 文件中,有如下图所示的五个大部分:3 a8 r+ `7 y4 r$ y# E
+ Q& c6 [* R) Q
. T1 Z+ {4 `4 M$ ^其中,每一部分都是对应上面的配置中的一个或者多个选项,也就是对应链接器armlink的一个或者多个参数% T' Q X9 H% _
/ k/ f7 U) _; I- }4 e2 R/ \
: }; y: n4 f0 BSection Cross References
; i7 t; ]& d7 n3 o, d' E( O5 |3 k+ D( |7 r) o
该部分显示了节区之间的交叉引用,指的是各个源文件生成的模块之间相互引用的关系。交叉引用中可以分为三大部分。4 |9 \; s6 u; l; B! ]+ d
1 U+ O8 s9 i1 D; [8 s! a# \- i" n
用户模块之间交叉引用( f& t& G9 P$ c h$ M" Y% x3 l
8 ?, G5 g; B% \7 F3 `0 f" I# N4 ^ 这部分主要就是用户自己实现的代码之间的引用关系。例如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 生成的模块。) l) y B4 v/ m. \! P3 n
7 T+ o% n' Y0 l! K8 v& G
用户模块与 C 库交叉引用
" s6 P! }% {) f0 P C5 n/ V
+ s B2 h0 W& A: f6 D) Z 这部分主要就是用户自己实现的代码中调用 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 库中。 ^- u# B; C6 E. Z1 `1 j" o5 I+ A
0 n" i% L) c/ _8 f7 X/ k1 u
% X0 Z- x& m" Z! X9 n4 b 在 ARM 编译套件中,所有的 C 库由工具armar来管理,位于 ARM 编译器目录 lib 下。使用 armar 即可从指定的库文件中解压出 __main.o 等模块。至于如何操作,参见博文ARM 之九 Cortex-M/R 内核启动过程 / 程序启动流程(基于ARMCC、Keil)。. {# R7 C0 i+ Y) q$ p, F7 h
# f# r: m9 t9 L6 v* X/ U) _
: ^7 l7 Y9 E" ]( m) e2 `6 ZC 库之间交叉引用
2 _* C) G+ r& x+ l# _
; ~! @3 F4 y9 ?2 Z. J$ K9 d C 库函数之间也有互相的引用关系。用户使用的 C 库函数之间的引用关系也会显示在该部分。例如 dflt_clz.o(x$fpl$dflt) refers (Special) to usenofp.o(x$fpl$usenofp) for __I$use$fp,其中所有的函数都是 C 库中的。同样可以使用armar解压库文件来导出符号表查看各接口。/ I1 n, u+ P' X- D4 K, g4 @
6 ~/ ?" p1 F# l+ Z' \# I: B* v# D
# f) \' L6 u) x7 S( qRemoving Unused input sections from the image
+ o8 s+ S' H. c8 s* y( p3 W3 I1 X$ O( X) V1 F+ Z& P
这部分列出了链接器移除的我们源码中实际未使用的数据和函数。其中包含移除数据的大小。例如Removing flash.o(.rrx_text), (6 bytes). 表示移除flash.o中的 6 字节的数据;Removing virtualuart.o(i.VirtualUartBufClear), (92 bytes). 表示移除 virtualuart.o 中的函数 VirtualUartBufClear,共 92 字节。, T& U3 m' h& g( v1 P/ V: Q H3 g
& w) A! p7 Y T s1 W
}; N0 J7 A3 I6 b! B) w 需要注意的是,被移除的函数在调试时将无法进行调试。如果不注意,在调试时很容易造成困扰。如下图所示:
' O9 r9 ^! {) N6 H' h# W' f+ k/ t
; K0 P/ T' [2 G" G4 s
: D7 ]( j2 I' C
# O0 y' L) {' Y5 R( |9 ~3 d& p1 F
+ A% u' W# z7 iImage Symbol Table) }1 k D& Y& W6 _
6 O6 O W# p$ {$ r( }: i# B符号映射表。就是每个符号(这里的符号可以指模块、变量、函数)实际的地址等信息。
2 s7 X/ H$ Q) {/ J5 B" ~3 Y4 @ U& u5 w6 k e
; x2 U- ~$ n7 D" _( MLocal Symbols1 I- W9 M& {. u7 z
$ [( [+ F# x% M不知道 ARMCC 是怎么分的 Local 和 Global。& _# j1 W6 }/ H
2 f X& R; j3 ]; a& c
- l& Y3 e* {' z- P: ^
Global Symbols" u0 G T8 a5 k1 H6 ]' ^1 L* O. @
1 ]6 B6 z; A. w: P' c! `# E
不知道 ARMCC 是怎么分的 Local 和 Global。
- k7 \0 T; \9 _5 r; @
+ D, E0 E) b! v5 X. y
, T3 N1 y- `8 jMemory Map of the image
; t/ }2 R3 [ U7 C) O) p% N6 Y) ^* p
这部分给出了镜像文件的布局。这部分与分散加载文件(Scatter File) 有密切的关系。如果使用 Keil,在 Keil 的配置界面中有如下配置:
2 ^2 [* g4 i. ~
! L8 b5 K: P* C7 j8 T7 B# K. X0 k. @- R7 f! Z0 n' k
% L5 m: j5 e# j& @, o1 P3 U" R
3 g3 H6 \4 i% t
这里的设置就是对应的 map 文件中的内容。如果我们在链接器的配置页面不选择 Use Memory Layout from Taget Dialog,则需要如上图自己指定一个分散加载文件。其实,如果选择 Use Memory Layout from Taget Dialog,Keil 会根据我们左边的配置自行生成一个分散加载文件来给链接器使用(这个文件就在我们的编译输出指定的目录中)。上图的示例就是没有使用Keil默认,而是通过自己指定的分散加载文件生成镜像文件。
% V# T! P! _6 s$ Q+ D' m. H0 T$ x1 r1 q
. z4 H( |/ e7 d, Q 以上的 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 根据配置界面的配置,会成一个分散加载文件)。
$ E% z8 h) q& R: L4 F; A
7 O$ [) Z+ O- x3 e$ B( k! U: ^& C接下来看看 map 文件中的内容,如下图:% o/ L9 v+ S7 `1 o: t8 ~- Y: k
3 l* }6 }# f; g2 {. r) T9 @
' ~, a) C" F1 S: Z; V
/ n! ~ q7 A. X4 Y- 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 库代码。4 x6 K. }3 V0 r
# Q, Q$ z; [- p4 {6 ` {0 ~
我们先来介绍一下每个字段的含义。首先我们要明白,每一行就是一个节。
. ?( @3 [1 e9 \, F6 I) p4 R& f( m' a2 k
[color=rgba(0, 0, 0, 0.75)]Base AddrSizeTypeAttrIdxE Section NameObject
2 t$ ?! c$ D% [9 D4 n4 L4 [5 a5 l6 o2 R节的基地址节的大小类型属性索引节的名字对象(节所属的文件模块)5 i; j3 E i$ W, w
为什么有 3 个?因为我本身使用了自己写的分散加载文件。手动指定了三个执行域。我的分散加载文件如下:
) H2 K" s9 ~5 r% ?+ T- X) E
1 v9 G" [* O6 r5 D; *************************************************************
% Q2 L. {/ ]6 C' ~& U/ ]; *** Scatter-Loading Description File generated by ZCShou ***' q0 b9 |- f. x! T _/ z1 U
; *************************************************************
: _3 E3 V( ?1 Z. D# P
+ y7 Q2 y0 \ D& X! \* u* N: L$ ]) I1 n! \' j
LR_IROM1 0x08010000 0x00030000 { ; load region size_region! v2 n* {! I/ y9 b
ER_IROM1 0x08010000 0x00030000 { ; load address = execution address$ r: k6 G: l) T
*.o (RESET, +First) ; 中断向量表( a9 b' J+ e% x0 k$ x. v# n0 c8 ], S
}
1 A: r& X+ V- f' O X! C1 `9 H6 G6 d; |3 r' W
ER_IROM2 + 0 { ; 应用程序信息
$ }9 b9 \) W( y3 ] *.o (SECTION_APP_INFO, +First)( C# X6 j' M9 I! j- J) i p; X
}
- S- H9 D" f& Y4 v* i
' ~7 y# h, f9 {: {2 q7 r3 i ER_IROM3 + 0 { ; 初始化相关代码+其他代码6 Q; ]9 E, g* i+ N' G% E
*(InRoot\$ \$Sections) ; 初始化相关
! |8 h; M/ e8 h( L .ANY (+RO) ; 其他所有代码0 f! e" d6 y- G0 V
}7 Y1 \. b- s7 U9 [
4 m9 `% a2 X1 p6 z* I3 t! k0 E RW_IRAM1 0x20000000 0x00020000 { ; 内存) W0 n2 L' X. \0 Y% Q! u- x
.ANY (+RW +ZI)
' `6 i; W# D* k; i& V$ `$ U" e }8 ~3 ^7 P- ?, y9 z( O& q a" T
}5 S+ W: d9 Q0 [" [. s! e
! b! @% L. \' z0 d0 w
& O* T( D+ t0 w+ c+ w4 x# R7 U V
在这三个执行域中,有很多类型为 PAD 的行,并且这些行没有节名字也没有所属的模块。这些其实是一些链接器自己添加的对齐。除了对齐没有其他作用。关于对齐本文之前的章节有介绍。除了 PAD 之外,剩下的就全是 Code 了。
J2 m# u& D! j- f' @$ w9 G8 T/ C! h' v( q* n
- Execution Region RW_IRAM1: 这个就是内存部分,存放我们的代码中用到的各种变量数据(常量数据在以上的 ER_IROM 中)。同样,该部分也有些对齐,除此之外全部是 Data、Zero、 HEAP(堆)、STACK(栈)。9 K# _2 D' |! p
! P7 B: G# g& aImage component sizes3 P+ ^: K3 A) P* a) c ~) S
. O& v: I+ |; n- t. T" V! O该部分列出了组成镜像的各部分内容的大小等详细信息。( b% ]* Z* o5 E! X) ?# {' B6 j b
& B% D5 X: ] {8 F- 各列的具体含义如下:
# l; S" e- V1 F$ G$ Q6 S0 O0 w+ 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:显示调试数据占用的字节数,例如,调试输入节以及符号和字符串表。
- 最后一列:对象文件的名字。下面会有区分。
9 R9 B/ {( b) z
- 特殊行的含义如下:0 |7 P& E6 t# O `$ ]9 L9 X
- 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 数据和调试信息。
6 m# x4 U% s0 x! d) B: @
7 [7 v+ `$ K9 t; l3 ^! V. O0 A; Q3 O8 S% C
|
|