|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
! P) Q. d& M5 Q* ~4 B
ARMCC(Keil)map 文件: y8 ?! ^; A* J( `
根据选择的参数不同,map 文件中的内容肯定是有变化的!下面以 Keil 中全选以上所说的参数后生成的 map 文件为例来进行说明。map 文件中,有如下图所示的五个大部分:! S# y: I8 v/ D; X: m% T
) R& N! F6 a0 L* ]0 I6 Q
7 Z: B& i4 n8 x% r其中,每一部分都是对应上面的配置中的一个或者多个选项,也就是对应链接器armlink的一个或者多个参数
/ d; t; V; L. h7 A: {3 v! N9 e6 c) C4 `: I# s0 N6 ^* d! o6 p0 f
4 w9 e' ]0 X8 m
Section Cross References/ C# ^: w. s6 S j$ W% S0 O2 v
! B* {8 d7 o% _3 F" p2 z 该部分显示了节区之间的交叉引用,指的是各个源文件生成的模块之间相互引用的关系。交叉引用中可以分为三大部分。
) U7 R7 N* S0 k# w3 W+ t0 w/ X9 ]
用户模块之间交叉引用7 P6 b" l4 n. X& F1 W9 \2 \
( I- J& @ e1 F- g 这部分主要就是用户自己实现的代码之间的引用关系。例如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 生成的模块。
( @; K* N' i" I5 T$ ^2 G; M
) u/ J1 b0 m) y. t: _2 e用户模块与 C 库交叉引用
" L4 k# t- [$ k7 l, h, \( d m! N/ W) a4 I' h' P: j
这部分主要就是用户自己实现的代码中调用 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 库中。
/ J+ }# r" x" t9 h7 g
, W5 r& \9 X# c Y. e
e1 G/ C7 c$ ^5 H w 在 ARM 编译套件中,所有的 C 库由工具armar来管理,位于 ARM 编译器目录 lib 下。使用 armar 即可从指定的库文件中解压出 __main.o 等模块。至于如何操作,参见博文ARM 之九 Cortex-M/R 内核启动过程 / 程序启动流程(基于ARMCC、Keil)。
: M6 r8 W G, V1 l
( X' Z+ |9 [) t; r7 g) s* @. {
" M# I' a$ Q- }0 `- K9 n/ [5 CC 库之间交叉引用
! x) x6 j j H7 A. i1 x$ k. a' F* u' D* ~' ?8 I
C 库函数之间也有互相的引用关系。用户使用的 C 库函数之间的引用关系也会显示在该部分。例如 dflt_clz.o(x$fpl$dflt) refers (Special) to usenofp.o(x$fpl$usenofp) for __I$use$fp,其中所有的函数都是 C 库中的。同样可以使用armar解压库文件来导出符号表查看各接口。
& l0 [( |" Z: V5 W* ]
2 i0 ? J( _, Z$ z' i& W6 d* I9 d7 U7 @3 w$ D, f
Removing Unused input sections from the image& _. Z h) _0 U
& i, P, K% U$ Z9 U# l 这部分列出了链接器移除的我们源码中实际未使用的数据和函数。其中包含移除数据的大小。例如Removing flash.o(.rrx_text), (6 bytes). 表示移除flash.o中的 6 字节的数据;Removing virtualuart.o(i.VirtualUartBufClear), (92 bytes). 表示移除 virtualuart.o 中的函数 VirtualUartBufClear,共 92 字节。7 |5 o' L4 ~ a y R
2 w4 b6 Z- c- Q) N* Q
& y6 `5 l( P) s/ c1 t7 v 需要注意的是,被移除的函数在调试时将无法进行调试。如果不注意,在调试时很容易造成困扰。如下图所示:
( z* z5 L; T5 s8 ?# `- A
# v5 a5 x5 s# [) Y; }3 f7 ?; k& ~8 [3 D/ a( j/ ?+ d
" {" J8 m2 v9 k/ K8 M$ V" T% W+ G
1 j0 W* g- [& e- W- Q/ u4 I
Image Symbol Table
) j) P% i z0 L
1 l s! q( W& V2 \- ]符号映射表。就是每个符号(这里的符号可以指模块、变量、函数)实际的地址等信息。$ C* B, w4 Y; q5 ]) s
) _; ]; I5 x x
8 a0 B L. `! S+ W/ Y
Local Symbols: S/ A' M( A" q: L" W
' e/ n& U: H2 T- b
不知道 ARMCC 是怎么分的 Local 和 Global。
" E) G, p* p+ O1 n9 m9 j+ l* I3 ~# [, V$ I4 E ^! \- U
& X2 U0 V' W4 \3 m/ H9 k5 h
Global Symbols/ C. u5 r: S$ }- o
1 D; b. G4 Q6 n0 ?: T( F0 r
不知道 ARMCC 是怎么分的 Local 和 Global。7 j' g1 N8 [& c5 c; z" V
. a. | A2 e) n- J9 F! A: L- `" I6 q0 X3 [) f) k
Memory Map of the image
8 X- P# j; P" J1 N, i% A5 P0 L6 o9 a2 H/ d
这部分给出了镜像文件的布局。这部分与分散加载文件(Scatter File) 有密切的关系。如果使用 Keil,在 Keil 的配置界面中有如下配置:
) u* L$ Q& o/ M5 X. f2 t
$ a& N6 _% P# q( }$ P5 {, G0 a
1 S3 [7 m! d4 H3 d1 Z1 G7 E6 ?: p# O
* L" H7 T- r7 T- V; q. `' S0 M
1 n5 o$ a7 V( D- X5 U, ]6 G这里的设置就是对应的 map 文件中的内容。如果我们在链接器的配置页面不选择 Use Memory Layout from Taget Dialog,则需要如上图自己指定一个分散加载文件。其实,如果选择 Use Memory Layout from Taget Dialog,Keil 会根据我们左边的配置自行生成一个分散加载文件来给链接器使用(这个文件就在我们的编译输出指定的目录中)。上图的示例就是没有使用Keil默认,而是通过自己指定的分散加载文件生成镜像文件。) V, b; |5 T' r' K9 I
9 } |9 q; z, F o4 E' W6 Q
* @$ c% S/ U0 o9 m8 Z- { 以上的 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 根据配置界面的配置,会成一个分散加载文件)。
- @6 }5 q: w" q- |9 A. T" f3 }. ?0 c/ O
接下来看看 map 文件中的内容,如下图:" g* Y- j" Q- v: G3 r$ U
; ^4 {- q: {0 b8 R$ R
) s E6 T- C8 f' l' @
% T1 x' Y! L0 v& G/ j( y2 h3 u
- 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 库代码。& W6 w3 f3 b# m7 N8 o- Y
O' B" p1 A/ B$ Y 我们先来介绍一下每个字段的含义。首先我们要明白,每一行就是一个节。
a5 O5 P9 H1 d; ]( N$ B. ? F
3 Z j8 ]7 s" M' V' @/ H' _# a[color=rgba(0, 0, 0, 0.75)]Base AddrSizeTypeAttrIdxE Section NameObject
& A/ o+ |/ j& @, n: ?9 Y节的基地址节的大小类型属性索引节的名字对象(节所属的文件模块)3 E9 C- E9 J( e$ i
为什么有 3 个?因为我本身使用了自己写的分散加载文件。手动指定了三个执行域。我的分散加载文件如下:" o1 [; m5 F J$ C, }
' W2 N( [* L K/ \* O: z' Y0 h; *************************************************************) j% S# H* _" g
; *** Scatter-Loading Description File generated by ZCShou ***7 D% Y' r: y2 z5 _
; *************************************************************
, l( T7 ]& q1 H0 W) \' v. F k2 \) q8 J3 J7 g, V, v; ]8 ]1 h
- i( }$ Y+ `+ }+ Z, q/ iLR_IROM1 0x08010000 0x00030000 { ; load region size_region$ H! h5 Q9 y" y: [/ r* D
ER_IROM1 0x08010000 0x00030000 { ; load address = execution address4 @" y) W+ M: w- o6 ~9 v
*.o (RESET, +First) ; 中断向量表- u: U" _, y1 }/ J4 Y, \6 a/ J
}. w3 f+ b h) `; B' y. a( }: b
6 c6 u" k$ O; M) u' {* v* r$ D
ER_IROM2 + 0 { ; 应用程序信息
$ k |; I$ T0 r6 w2 V *.o (SECTION_APP_INFO, +First)4 g* A( G5 s: o/ m, g; P" B9 u
}9 p) X: Q) u! t5 h$ ?1 \/ |
- d! R! j, }/ ?: k1 G8 u
ER_IROM3 + 0 { ; 初始化相关代码+其他代码
: p1 J& L, Y" Q7 \! Q) N *(InRoot\$ \$Sections) ; 初始化相关
" I/ G: G* ~0 k2 T5 t .ANY (+RO) ; 其他所有代码
5 \8 {- {( K% @ }
{7 N0 g- s: F
: M0 J" I, v) V/ D5 w; F RW_IRAM1 0x20000000 0x00020000 { ; 内存
6 A+ x; T8 D( f, W& \: P .ANY (+RW +ZI)' E( L6 n1 y8 F/ s
}7 Z m( M) j4 R, G
}
3 H1 ^: L9 @/ k3 K8 Y
7 p3 Y$ y' V7 T. \& j7 V3 F& z/ f9 |+ i; u4 x7 Q( N
在这三个执行域中,有很多类型为 PAD 的行,并且这些行没有节名字也没有所属的模块。这些其实是一些链接器自己添加的对齐。除了对齐没有其他作用。关于对齐本文之前的章节有介绍。除了 PAD 之外,剩下的就全是 Code 了。
5 K3 L0 X# S: i+ ` x5 ~5 |: i: g$ Q7 W- I+ D
- Execution Region RW_IRAM1: 这个就是内存部分,存放我们的代码中用到的各种变量数据(常量数据在以上的 ER_IROM 中)。同样,该部分也有些对齐,除此之外全部是 Data、Zero、 HEAP(堆)、STACK(栈)。
$ E* B. B% p# X; J 5 p* u* V) ~" M
Image component sizes3 T# w' Q6 q' z8 X' E3 [( o, P
* Q4 K) ~0 H7 ~- H
该部分列出了组成镜像的各部分内容的大小等详细信息。
+ O% n. y. O' x, l/ l( x7 C* L$ e
- 各列的具体含义如下:
8 b) J( J; x1 y7 x3 i* 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:显示调试数据占用的字节数,例如,调试输入节以及符号和字符串表。
- 最后一列:对象文件的名字。下面会有区分。( X# y. F3 m' y; Y- m$ p
- 特殊行的含义如下:
: h6 q1 w5 R/ m, h
- 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 数据和调试信息。
2 e! f. M+ ?/ w" m( q 7 N0 v }) [* o
% k# \# E( R: z |
|