你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

实战经验 | STM32CubeIDE 实用技巧之 ld 链接文件

[复制链接]
STMCU-管管 发布时间:2021-5-20 16:28
实战经验 | STM32CubeIDE 实用技巧之 ld 链接文件

6 T' L) K/ i! a' x; A
# W7 N4 L9 g& `% ^; |STM32CubeIDE 是 ST 推出的免费集成编译环境,基于 Eclipse 开源框架,集成了 GCC、GDB 等免费的编译器、链接器,支持 STM32 全系列芯片,可以创建 C/C++工程,支持调试、波形实时仿真、一键下载等。5 S2 h6 A- N& a. }

: u- [8 a5 Q  g" o# U5 z# j7 N

5 W, |( R$ N' T在实际项目中,有时候需要对内存进行细分时,比如指定变量/函数/文件到特殊地址等等,KEIL 可以通过“*.sct”文件来实现;IAR 可以通过“*.icf”文件来实现;对于 STM32CubeIDE,可以通过“*.ld”链接文件来实现。本文将介绍 GCC 的“*.ld”链接文件的常见用法,供大家参考使用。5 x) _5 `- w; k0 J0 K( {
+ d9 A7 J- S8 R. J

" [3 n- ]  d% ~7 d
STM32CubeIDE 实用技巧之ld 链接文件
- q) y6 i) A* D- X( M' F3 w
6 C$ x# V7 b  e  g) \/ _
7 r5 j7 v0 [) ?% E
基本概念
- K% s# x! ]( h. d6 h; E$ l

, G% Z! c- w& l' T+ u) D# y. S0 X" n

. u1 W, @. l; d* q" X4 F" M+ ^$ K“*.ld”链接文件组合了许多对象和归档文件,重新定位它们的数据并绑定符号引用。通常,编译程序的最后一步是运行“*.ld”链接文件。! q6 O1 ~, f' S

- j9 e2 q0 q$ |2 r( b

6 i1 K" d  M( C6 I! T$ M0 s; g通俗来讲,链接文件可以描述输入文件中的段,将其映射到输出文件中,并指定输出文件中的内存分配。
0 C; G5 h8 m9 }* `" Q
2 V; `# R1 J* r* d2 ^) f
2 _2 M8 Z9 C7 E2 ^, {/ u4 }
以下就是链接文件涉及到的相关概念:
, j# e3 D- ]. K. x" w  _
2 g& L! M; |: p# g* R5 D" j4 j- d" |

) _9 X' Y; f$ q- Y* ^4 _" K  SPART1.内存(Memory)
# O. P2 u0 s# z# P' t& L6 I0 I8 X" {
' I8 a, e: K; ?4 g7 u# ~
语法:
. @' I* n9 @2 M) n- A) ?# ~4 F4 N  j
4 s. A/ G: D) M( o# v8 L: Z2 W
1.png
  W# w9 Z" F% Z, Y8 B) y2 z

4 [% U( n9 \; U* Y/ o

& h  c- Y7 Y9 I注释:
3 T% C. \7 L: z  N* E0 k  _这里的“attr”只能由以下特性组成:( [5 q/ B+ V; R% P: ]3 t0 n# U6 X  h

; z! a9 i% j9 C$ y
: D- I) t+ k$ J' w# x2 }
‘R’ Read-only section% L+ q5 a4 w4 T8 x
‘W’ -- Read/write section
9 p6 x0 u( ?( z1 V! d: V‘X’ -- Executable section6 a! y- N1 \7 t& M# j, ], W0 }1 J) Q
‘A’ -- Allocatable section8 }+ l! X, ?* r7 y( _7 L- F* s
‘I’ -- Initialized section$ J: z8 G/ G6 Q9 x0 i/ D
‘L’ -- Same as ‘I’' J, a3 ^; D: r) e* q7 f
‘!’ -- Invert the sense of any of the attributes that follow. w, M: ?; j  ?7 _# A! f, h: n
% s, q( N( e8 |

5 J4 `6 B0 |" R% n- `% m) j$ U$ ~4 R' k示例:
  h. C6 Y0 w8 _1 t- c
2.png

! Z5 Q6 s% H. R; \
$ p" B, i0 K  S+ s6 {0 p& k2 T
+ r+ |% @  n1 x' `
注释:1 o' U3 p/ Y: b  Y# N& j1 Z
“xrw”表示“RAM”区是可读、可写和可执行的,且 RAM 的起始地址为“0x20000000”,长度为 36K。
% }0 w) S4 L8 W' H, q/ y1 ~“rx”表示“FLASH”区是可读和可执行的,FLASH 的起始地址为“0x08000000”,长度为 128K。0 B' J/ o& T! n+ W, h0 g' `
7 [  T7 J; F% |* c3 j; ?: ~

. @& ~3 b1 a6 k) u! R  A7 TPART2.段(Section)
# d* u+ J. o  ]" T5 b  f9 j- [, |0 |6 a1 H/ y1 |' y1 F4 {
, M! k/ |. c% M2 e
Section 有 loadable(可加载) 和 allocatable(可分配)两种类型。不可加载也不可分配的内存段,通常包含某些调试信息。
# A1 W  s2 v' k3 S0 ~! T; K4 m) A2 t9 b

: Y8 u  x6 M' O, @1 Z# n( n4 {loadable(可加载)是指:程序运行时,该段内容应该被加载到内存中。  \* G: L' U0 F+ q  r9 H

: W5 `8 u7 |, K* l; P
1 j3 l1 M% W$ }: K# L- s
allocatable(可分配)是指:该段的内容应该被预留出,但不应该加载任何别的内容(某些情况下,这些内存必须归零)。  X# V* V, o; s; K0 }6 Q( h

% ~4 x* c9 f& @

& ?9 J; w# o' F“可加载”和“可分配”的 section 都有两个地址:“VMA”和“LMA”。+ o% S5 o) ?2 m$ \1 X" q! P/ h$ K
% g" k) h# Q/ r" `
+ f) Z4 @2 M& J$ [
VMA(the virtual memory address):这是运行输出文件时,该 section 的地址。VMA 是可选项,可以不设置。/ l0 [+ [& r( T! f& i8 i" a
7 M) `# d1 K2 `: S) U2 E
  {. H) O: N, \# C- }
LMA(load memory address):这是加载 section 时的地址。! a; T! ~+ T, a( d

3 }! z, [/ E* B: _) K' a2 j1 G6 A
0 M4 _8 S) F4 Q' r; x  W
在大多数情况下,这两个地址是相同的。当然也可以不相等,比如下面的例子就是 LMA 和 VMA 不同的案例:数据段被加载到 ROM 中,然后在程序启动时复制到 RAM 中(通常用于初始化全局变量)。此时 ROM 地址就是LMA,RAM 地址就是 VMA。
8 n3 ~- M# j4 M( N$ G' u3 m) e
' c4 _/ V8 W* G8 U$ d" O
  \5 K! j: F/ ]
语法:
# p0 r5 [4 d4 p9 {
3.png

* e# L0 E5 w. l' V注释:$ n/ P) B1 y7 ~8 {
大多数的段仅使用了上述的一部分属性。
) ?# b: d( N* i; T' \
+ K' w8 k8 y( r3 R+ @6 ]: ^! X
/ W2 T* x/ |% j* S
示例:' q9 [' _/ ?! Q
4.png

- n3 R2 G- U; R* s( U  n
6 G& \) ]+ i1 g! o" I$ P' z3 a
/ h' S1 W* }: k8 \, \
注释:6 v) t6 j# ~2 U/ b1 J, Z5 k7 j
* f. _" x1 A' e5 W( n

: e7 w. W% K, b) j上述示例中“.isr_vector”的 LMA 与 VMA 是相等的。“.data”因为有“>RAM AT> FLASH”的修饰,表示.data 段的 VMA 为 RAM,LMA 为 FLASH。即.data 段的内容会放在 FLASH 中,但是运行时,会加载到 RAM 中。  u$ r8 D2 B3 d5 {
3 i; X3 ]! @. }4 b# Y4 B. K' E

/ B, R3 A/ V  D3 \& V1 Q; D2 Z, C
链接脚本

; |  f' B0 ^7 C* n$ [) A1 s. Z- ]; Z" r2 O! n0 D( T
7 R; h4 |1 K( Z' ?+ s( i9 D0 t
完整的“*.ld”链接文件通常会包含入口点、memory 以及 section 的内容。1 T3 N5 g0 @" [5 I6 U/ w
  v8 Y5 A. |5 C' Y
5 b1 l" d* @# B1 ^0 _% f( f' ~
PART1.入口点, P8 j* m: t1 k6 I6 y/ O  c
* \3 r2 H! }$ h: D% w- z

1 i# _) D, f: T. }5 ^3 G语法:ENTRY(symbol)5 n& z8 A& Z$ {' ~
用途:程序中要执行的第一个指令,也称为入口点。
8 O2 L; \" p1 X+ u/ o
6 o. Z* G+ w4 s8 \$ l, a% g

/ F* u; L; {7 @示例:6 f% l) P2 L) i0 V- t4 A. U' [
5.png
" b8 w$ @9 Q# z! ]9 n2 J
注释:
6 X" V/ p6 N7 n$ e在 STM32 的工程中,默认的入口点是“Reset_Handler”函数。
# X4 X% z% k4 a/ s1 g: t$ \. N- I% W
% A$ e9 c4 i# z4 ?4 a6 x8 y* ?. o
PART2.
1 t; E* \2 ~+ k: {常用命令
5 v0 a! \# @! w8 N/ `, |
. A( C3 F. Z0 s
0 x3 `. a6 w/ v/ ^0 W; e
1. ASSERT
" ]; u" j9 ^% L- j% Q. @( F9 A/ O! {+ i& @; o5 M+ F

1 ]+ v" d1 l- ]) Z; w0 K9 o5 d: O语法:ASSERT(exp, message)确保 exp 是非零值,如果为零,将以错误码的形式退出链接文件,并输出 message。
# w6 e- J7 P, h( A% k用途:在必要的位置添加断言,可以清晰的定位问题。, D! d9 [. Z0 X- `( w% B  L
示例:  z. Z. k! p& [$ d
6.png

( u6 O# b* `  {/ K注释:
: ^( W' t8 p* ]当示例中的“_estack”大于“_Min_Stack_Size + _Min_Heap_Size”时,会出现如下的信息。8 r1 y7 P; b7 i5 H6 L* N
7.png

9 r2 Y; Q5 F( M: a5 `3 k* {
; c3 o6 F' ~& K' P* s% P) @- I: p

  ]2 v( U. ~7 Y' b, v2. PROVIDE5 k8 D+ M) s% f/ `/ l' J6 A- C
语法:PROVIDE(symbol = expression)$ `: W8 e, _2 s
用途:在某些情况下,链接器脚本只需要定义一个被引用的符号,并且该符号不是由链接中包含的任何对象定义的。/ }( [* `) c$ e, |& t) h" Q5 d1 q' I
示例:4 u4 U' k  N2 u# a0 x, G7 h
8.png
7 j" r9 F2 J7 P( S) }: d

. t. a" p; E- m. m1 ^% t) l" b5 {
+ P& A" d) |6 P% W; J7 s& X# p& o
3. HIDDEN4 ?3 j5 q$ U9 I, B( r% g
语法:HIDDEN(symbol = expression)- H+ D0 I, ^) d, ?! {. f1 J7 r
用途:对于 ELF 目标端口,符号将被隐藏且不被导出。( t: [# E  K- _) V% p) Y2 X3 H0 X

: A+ j6 T* v. D+ ]

8 P9 b% m% u2 j: m, W% t示例:
2 w- \! v3 n7 N# ]0 R8 g4 I
9.png

0 g' g2 Y; c3 w( d! \! v) M. z& o" j# W$ H. l9 D

, {$ T2 ~/ n( X  ^9 c- w, n2 q# Y4. PROVIDE_HIDDEN
3 r3 y/ k2 Q- |- M3 {% v( E6 Q' w6 W# u' [, }6 E

$ I/ h/ j4 V; f) Y* \语法:PROVIDE_HIDDEN(symbol = expression)
) Y6 I3 A" h. }5 n- N用途:和 PROVIDE 用法相同,是 PROVIDE 和 HIDDEN 的结合体。
- v( f& ?/ Q: U0 i5 t$ D5 Z: Z: }6 m

# ?( \0 N% J- o& L* f3 u! ~8 o( |* }$ c示例:7 l% L* A) @5 {4 i9 @5 C4 F8 x9 l
10.png
+ C# s; d, B. |2 P' A7 `4 w6 W
7 ]+ @7 ^  O' C; d
9 P' N- Q# i/ j8 P; A
5. KEEP
+ k( B( R$ [- o  D' g4 u# r4 }# T7 g2 q5 j
$ j. ]7 ?& h6 o: d% V- e5 P
语法:PROVIDE_HIDDEN(symbol = expression)
  S: ]- m% y; T! a) F: w2 L用途:当链接器使用('--gc-sections') 进行垃圾回收时,KEEP()可以使得被标记段的内容不被清除。
! r) i$ z: L2 L  z
3 v8 |* M7 c: A) T# `* P/ k
0 ~3 M3 g; M, b, Q+ x- e
示例:
7 _1 u8 C  a( U0 R" M" X; Y
11.png

0 g" A( p6 v5 {( ^1 X( V3 V; ^
! A, w. b6 @8 B) Q0 y

9 w, V# M6 w. u$ d% h, D7 IPART1.
' `6 z# `) e: h& G1 d入口点/ d. }' i8 ]; I1 v+ }
- P. F- r& h; g

7 F$ u/ M: Q4 e+ X% x5 jPART3.简单脚本示例
: _5 w! P) L  W* |4 V+ `- p
' X) b/ t# r1 n; b* b. |
( u& W9 x" ]+ B5 S4 {
首先我们来看一个简单的脚本示例:$ e) T% ^9 l4 ]- \( s3 H3 v  Y
12.png
! K$ F9 }5 q" [$ W+ l( R3 K
" L2 i% }& c: k1 m
5 H7 S4 P5 Z- ^2 l/ ^
注释:
6 g  o) C. Z$ q/ W) m9 Z这里指定了 code、已初始化的数据以及未初始化的数据的内存分布。对于这个示例,程序会在地址 0x10000 处装载代码,并且数据会从地址 0x8000000 开始。特殊符号“.”,是位置计数器(location counter),按输出段的大小递增,设置位置计数器可以改变输出段的地址在“SECTIONS”命令的开头,位置计数器的值为“0”。位置计数器可以进行算数运算。* ?6 _7 }5 o/ v* s
  n2 w2 {; u$ n/ A5 Y$ A7 `3 ^
2 k! o% b' R- G( {! \- t
高阶使用

) d# `# V7 A+ D0 l" t1 Z( @& OPART1.位置计数器
3 Z/ |: G# a0 m' R! W0 _( I! j( e6 O' ^3 L& Y3 q+ O
5 _! P* b; h$ S. X5 {2 L
在 section 的描述中,位置计数器“.”可以进行算术运算,由此产生空隙来满足特定需求。
) z) K9 e4 T) C7 D

5 d3 O" |: m' m9 L; K8 ~' M8 W6 I- ?: ]0 n: Y) G7 Q) B8 C
# Z3 e. m; T) r6 }5 a$ b  ~8 e
注释:
; `1 ^4 G) {" W例如这里的“. = . + 0x04;”就实现了 SE_CallGate_Fun 段的空间中,插入一段 0x04 的空隙。# O& `: A' V/ R. K0 T! G

# f5 p2 D4 M4 }) u% j+ z# n6 d( w
; l  o0 ?4 V- L# g1 h8 [; _
PART2.
% D$ h, M" E' V8 r. b3 @; q指定“变量”的输出地址
$ P3 u3 P7 C$ p" D6 r) U
% l3 _1 L  H, t+ V& ?  u可以定义如下的 memory,然后将“变量”存放于该 memory,就能控制“变量”的输出地址。; i, q2 h, M& L9 D' ^
13.png

0 ~) G" Y2 Y1 H4 a7 ~7 _3 G5 Y) }
# Y% d' H- \* p3 I% l
! @; z( G, b! s3 `1 X! h
同时在 c 文件中,在定义“变量”时,添加如下对应的属性
6 S! a7 u9 y: Q: S- o
14.png
1 s0 z3 ?) S, z  O# K
; F4 t& Z- ~7 }- V' h

/ _6 |% Q0 j9 W+ i  N' `  z3 N7 r注释:
, g& B/ D5 w' Y) _# f- d. j变量将位于“0x20000000 ~ 0x200002FF”区域(如果仅仅只有 key 数组位于该区域,将从 0x20000000 开始存放,如果有多个变量存储于该区域,将按照编译的顺序,从 0x20000000 依次存放)。
$ i+ d" L: z9 o. k: e
5 s0 o5 H; X* u
( s* d* q% n2 J: g4 M: @
PART3.
% |0 s4 F% z& S; [/ b; j/ [指定“函数”的输出地址
+ J5 n: m- @) w$ s. E* H: ?% g' ]; h
: x3 d9 [+ J2 Q9 J2 k) O; `: H, }1 d

( S$ C; u$ `; Q8 H4 \可以定义如下的 memory 和 section,然后将“函数”存放于该 section,就能控制“函数”的输出地址。
3 I$ K, j* x& W  b$ p5 \
15.png

9 @# ]% l, h. r. z' g1 A9 e- l  u# k- k. z( S. W; [4 T

" b6 d: ^! G# x同时在 c 文件中,在“函数”的实现部分,添加如下对应的属性:: y& H: z8 b2 j5 R  m1 r- {
16.png

. ~5 _% }: b9 K0 @3 I+ S' \. I
3 N2 O4 ]+ I" {/ E
+ `9 F/ l5 I' P+ z( m* `
注释:. G: `# S% |7 r) J

: B* D* }& f6 X
. P  F* g: O" R, \; R
函数“call_gate”将存放于 0x08000304 处(留意此处的位置计数器将产生 0x04 的内存间隙)。
' |7 B0 t/ N  u' Q- m+ I' j3 f' Q4 D0 U" J4 ^
PART4.
* d  b  }+ y' L2 E- O5 S. B; y( p指定“文件”的输出地址
! L' L; q( [; k
  R, b0 Q8 i" T% p+ a' L
) E' t. S5 a, N( I4 M: F
可以定义如下的 memory 和 section,然后将指定的文件存放于该 section,就能控制“文件”的输出地址。
( J4 S/ N0 S6 d8 `
17.png
+ C+ J: [$ e- S7 v+ x9 J+ G: F
. {5 b# X1 i3 N/ A/ _! @  `
! G3 k7 }) F4 Z4 x/ e, r
注释:
! v4 r& a- C% B, |% M
0 v$ G  N/ _' g, _2 \

% X& Z, }% m( P: D8 \7 k示例中将 main.o 指定到 FLASH 区域中;更改 FLASH 的地址或者 main_section 的 LMA,就可以实现将特定文件指定到特定内存区域。, o+ r+ ^2 D( h6 F& o
  i; X4 i# h/ W, l

* h. o" G" N+ j% A2 Z总结
5 H% t/ K# a: y# [6 ]6 R" t: o3 Z! T7 k) G( W) N' D) t
( c( U* c6 ?7 t4 }
STM32CubeIDE 链接文件(*.ld)的详细说明文档位于“Help”->“Read STM32CubeIDE Documentation”->“C/C++ Linker”(ld.pdf)。该文档对 ld 文件的格式和内容进行了详细的说明,需要时可以查阅。实际项目中,通过综合利用上述的技巧,可以实现对内存的准确分配,来满足不同的应用场景。
' {; i- ?! g2 o1 s7 m
% d) j. ~! P+ O

  {* p- I$ t1 i5 `9 d0 P5 U; V# ?, a7 i1 S  m- U. |
! w' b0 r! g4 e0 D3 w

3 s" S9 u4 M% S, K3 J
收藏 评论0 发布时间:2021-5-20 16:28

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版