arm ld link scrip编写

转载 2015年07月08日 19:42:57

从以前的经验,链接脚本是嵌入式开发,单片机开发相当重要的一个东西。它完成的工作是做PC机软件的同志们不用关心的,但是也是很复杂的一项工作。总结来看链接脚本要告诉连接器

1:输出什么

2:输入是什么,那么obj文件

3:要用什么库,库放在什么地方

4:内存分布地址

5:提供启动代码一些全局地址变量

 

一般来说链接脚本需要搞清楚这几样事情后才能编写,那arm-gcc-ld的脚本也一定要实现这些功能。对于大多数的链接器来说,对于简单的项目不需要脚本,只是使用命令参数就可以完成了。

 

MEMORY:

它是用来补充SECTIONS命令的,用来描述目标CPU中可用的内存区域。它是可选的,如果没有这个命令,LD会认为SECTIONS描述的相邻的内存块之间有足够可用的内存。其实很容易理解但是却很少用(我没用过,嘿嘿),在SECTIONS中每个段的分布都没有考虑ARM能够寻址的地址中,ROM,RAM,FLASH是不是连续的。如果不是连续的怎么办?MEMORY就是设置各个区的起始位置,大小,属性的命令,在一个脚本中只能有一个。

 

举一个例子:

如果你的板子有两段存储,而且很遗憾的是不是连续的,一段是从0x0开始,大小为256K,另一段是从0x40000000开始的大小为4M,你可以在脚本中写入如下的代码来描述你的板子的内存信息。

复制代码
1 MEMORY 2 { 3 rom (rx) : ORIGIN = 0, LENGTH = 256K 4 ram (!rx) : org = 0x40000000, l = 4M 5 }
复制代码

很显然下面的一句用了简略标签,这并不重要,重要的是怎样使用它,不过在那之前还是想再仔细研究下MEMORY命令的细节。

MEMORY命令的语法是:

MEMORY
{
      name (attr) : ORIGIN = origin, LENGTH = len
      ...
}

name:一个用户定义的名字,Linker将在内部使用它,所以别把它和SECTIONS里用到的文件名,段名等搞重复了,它要求是独一无二的。

 

attr  :如同它的名字一样,这是内存段的属性描述。

`R'    Read-only sections.
`W'   Read/write sections.
`X'    Sections containing executable code.
`A'    Allocated sections.
`I'     Initialized sections.
`L'    Same as I.
`!'    Invert the sense of any of the following attributes.

别怪我懒,确实不想再打一遍这个的翻译,而且很久没用英文的俺翻译的估计也不好。总体来说,它是属性就行了。 

ORIGIN:这是起始地址

LENGTH:段长

由此可见上面那段实例显示ROM和RAM的明确位置,而且还显示了他们的只能,一个存代码,一个除了存代码什么都可以。

 

接着就是老问题了,怎么用这个。如果仅仅是规定我的板子有什么特点又不用的话那就是脱了裤子放屁,多此一举。这个问题留在SECTIONS命令中回顾。

 

SECTIONS:

它是脚本文件中最重要的元素,不可缺省。它的作用就是用来描述输出文件的布局。

SECTIONS命令的语法:

SECTIONS

{
       ...
      secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
      { contents } >region :phdr =fill
      ...
}

这么多的参数中,只有secname和contents是必须的,其他都是可选的参数。也就说它的最简单的格式就是:

SECTIONS

{
       ...
      secname  : {

                 contents

      } 
      ...
}

但是注意:secname前后的两个空格是必须的,否则就是不合法输入。

secname定义了段名,其实最开始就忽略了一个重要的因素,arm-gcc-ld脚本需要描述输入和输出,而表面上一看却看不出来什么是输入什么事输入,其实secname和contents就是描述这两个信息的参数。secname是输出文件的段,即输出文件有哪些段,而contents就是描述输出文件的这个段从哪些文件里抽取而来。明确这个了就不难理解为什么SECTIONS命令什么都可以不要就是不能没有这两个参数了。

secname:定义段,但是别以为定义的段一定要是教科书上写的.data,.text这些科班的必须品,你甚至可以创建一个段来放一个美女的图片。

contents:它的语法开始复杂起来了,但是你可以简单的把输入文件写到代码中:

                   .data : { main.o led.o}

但是结果被列的目标文件中所有的代码都被链接到.data中去了,显然不大符合我们的要求啊。那么还有一种写法:

               .data : {

                                 main.o(.data)

                                 main.o(.text) // 也可以这样写 main.o(.data  .text)或者main.o(.data , .text)

                                led.o(.data)

                   }

 

 这个写法让只有被选中的文件的特殊段被链接到输出文件的.data段了。当然,我们似乎还有更好的写法:

               .data : {

                                 *(.data)

                   }

  这样的话,所有目标文件的.data段都被连接到了输出文件中了(这似乎是最常用的方法)。

 

核心的部分讲完了,开始回顾前面说到了的那些参数:

start:强制链接地址。也许没有讲清楚的是,在SECTIONS中,各个段是按次序排列的,前一个段用到什么地方下一个段接着用,而start就是强迫链接器将当前的段连接到指定的地址中。

                  .data  0x400000000 : { ..... }

BLOCK(align):说实话,没看懂。只知道用的时候用的比较多的是ALIGN(4)这样的标记,表示排列地址的时候按4的倍数排列,这样做的理由很简单,系统会快。

AT(addr):实现存放地址和加载地址不一致的功能,AT表示在文件中存放的位置,而在内存里呢,按照普通方式存储。至于用处,目前在下不知。

>region:好戏来了,这个region就是前面说的MEMORY命令定义的位置信息。表明当前section所放置的mem有什么特点,如果不符合会怎么样呢?不晓得嘛。

其他略了吧,累了,主要是没中文资料(屁话,有了还用我刻薄吗),其实有点日文资料也行啊,英文我比较苦手)。

 

注释:

 和C语言一样的哦,/**/

 

其它:

其实ARM-GCC-LD脚本还真的和别的不一样,它的功能要强一些,从manual看,它有三大功能:

1:设置入口函数

2:定义一个变量并赋值

3:描述输入输出文件的链接规则

 

其实上面的介绍是功能3,1和2都没有讲过。对于arm-gcc-ld脚本来说设置入口函数和定义变量可以在SECTIONS命令大括号里,也可以在外面。语法是:

ENTRY(symbol)

这个symbol应该是某个函数,或者是汇编代码里的一个入口。然而,其实ARM-GCC-LD有很多种方式定义入口,所以当你看到你的脚本里没有这句话而板子运行的很正常的时候别大吃一斤。

1:在连接的时候使用-e参数。

2:在脚本里使用ENTRY

3:如果定义过start这个入口(如果你在汇编里如果本身就有这个名字叫start的入口,那么不用特别的声明也可以)

4:SECTION中.text的第一个入口函数

5:地址为0的指令

其实看了这个我们可以理解是ARM对入口的一个选择优先级,1和2是一样的,显示的指明入口,这也是推荐的方法,没人会觉得程序员故弄玄虚是什么好事情。3是连接器的智能吧,而4和5就是无奈的选择了,程序员没干好的事情CPU只要猜着来处理了,有.text段的话就从它开始执行吧,连.text都没有的就从0x00000000开始执行,至于执行到哪里去了,火星人知道。

 

关于定义变量,其实一般的脚本都会有的,目的只有一个,给汇编启动代码提地址信息。比如说,一段需要清零的区域在脚本里定义了,而脚本自己不是变形金刚,他不能主动给你清零的,需要你自己的启动代码来清零,清零的代码当然在汇编的启动代码里,它怎么知道需要清零的内存在什么地方?就靠脚本里定义的变量了。没错,事情就是这样的,那也就说一定会有一个提取地址的方法将地址赋给变量了哦,yes!就是小小的一个点"."。

RAM_START = .;

定义了一个RAM_START变量,地址是当前的地址,什么是当前的地址啊?就是链接器在连接的时候根据前面的段排列后的当前位置。当然也可以设置当前位置的值,不过最好不要小于前面排列需要的最小内存。

. = 0x00000000

定义当前地址为0x0。

 

常用的基本上就这么多了,看一个实例吧:

复制代码
1 SECTIONS 2 { 3 .= 0x30000000; 4 .text : { *(.text) }
9 .data : { *(.data) } 10 .rodata : { *(.rodata) }
11 Image_ZI_Base = .; 12 .bss : { *(.bss) } 13 Image_ZI_Limit = .;
21 .debug_info 0 : { *(.debug_info) } 22 .debug_line 0 : { *(.debug_line) } 23 .debug_abbrev 0 : { *(.debug_abbrev)} 24 .debug_frame 0 : { *(.debug_frame) } 25 }
18 PROVIDE (__stack = .); 19 end = .; 20 _end = .;14 __bss_start__ = .; 15 __bss_end__ = .; 16 __EH_FRAME_BEGIN__ = .; 17 __EH_FRAME_END__ = .; 5 Image_RO_Limit = .; 6 Image_RW_Base = .; 7 Image_RO_Base = .; 8 Image_RW_Limit = .;
复制代码

abb FF device link ld800hse 英文说明书

  • 2013年02月26日 09:28
  • 1.58MB
  • 下载

link script(链接器ld吃的文件)中使用宏定义的解决方案

问题:工作中遇到一个需求:需要在ld script中使用类似C语言的define等宏定义来做一些判断和替换 实验: 1:理论上*.c中都能用,是否gcc也支持在ld script中直接用宏呢,结果: ...
  • ytmayer
  • ytmayer
  • 2015年04月11日 19:34
  • 1624

arm-linux-gcc/ld/objcopy/objdump参数总结

arm-linux-gcc/ld/objcopy/objdump参数总结 arm-linux-gcc -wall -O2 -c -o $@ $   -o 只激活预处理,编译,和汇编,也就是他只把...

arm-linux-ld 命令详解

在开始后续实验之前,我们得了解一下arm-linux-ld连接命令的使用。在上述实验中,我们一直使用类似如下的命令进行连接: arm-linux-ld -Ttext 0x00000000 crt...
  • xungjhj
  • xungjhj
  • 2017年04月27日 11:34
  • 159

arm-linux-gcc/ld/objcopy/objdump使用总结

arm-linux工具的功能如下: arm-linux-addr2line 把程序地址转换为文件名和行号。在命令行中给它一个地址和一个可执行文件名,它就会使用这个可执行文件的调试信息指出在给出的...

GNU ARM 链接脚本 ld

内容概要 1. 概论 2. 基本概念 3. 脚本格式 4. 简单例子 5. 简单脚本命令 6. 对符号的赋值 7. SECTIONS命令 8. MEMORY命令 9. PHDRS...

arm-linux-系列工具,ld,ar,as,objcopy

http://blog.163.com/ji_wei8888/blog/static/4868044620117611461317/ arm-linu...

TQ2440 学习笔记—— 10、嵌入式编程基础知识【arm-linux-ld 选项】

(韦东山——嵌入式Linux 应用开发完全手册) arm-linux-ld 用于将多个目标文件、库文件链接成可执行文件。 介绍 “ -T ” 选项,可以直接使用它来指定代码段、数据段、bss ...
  • Cowena
  • Cowena
  • 2015年09月01日 16:05
  • 447

arm-linux-ld命令

我们对每个c或者汇编文件进行单独编译,但是不去连接,生成很多.o 的文件,这些.o文件首先是分散的,我们首先要考虑的如何组合起来;其次,这些.o文件存在相互调用的关系;再者,我们最后生成的bin文件是...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:arm ld link scrip编写
举报原因:
原因补充:

(最多只允许输入30个字)