gcc链接脚本学习.doc_第1页
gcc链接脚本学习.doc_第2页
gcc链接脚本学习.doc_第3页
gcc链接脚本学习.doc_第4页
gcc链接脚本学习.doc_第5页
已阅读5页,还剩1页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

GCC的连接脚本学习笔记来源: ChinaUnix博客 日期: 2009.04.29 14:26(共有条评论)我要评论连接脚本将我整整蒙了1天零一个上午,做了很多实验,看了人家不少例子代码勉强能驾驭了,让linker按照我想要的来处理,做个笔记。1,什么叫输入段,什么叫输出段不知道怎么回事,我对GCC系列的输入和输出两个单词总是进入思维死角,很简单就是 input section 和 output section,这里不是说翻译的问题,我觉得是一种思考的方式的问题。我的问题就是:既然叫输入端,那输入什么?同理,输出的是什么?不知道其他人不会不理解这个问题,我自己的话是理解了不少时间了 -v-所谓的输出段,是指生成的文件,例如 elf 中的每个段所谓的输入段,是指连接的时候提供LD的所有目标文件(OBJ)中的段。2,lma 和 vmalma =load memory addressvma =vitual memory address如果有研究过ADS的估计有印象,那里有个 RO BASE 和 RW BASE 和 ZI BASE,也就是说,lma 是装载地址,vma 是运行地址,想搞清楚这两个问题,可以阅读一下ARM学习报告(杜云海)作者写的很好,将这个问题分析的很透澈。lma 和vma只是GCC的叫法而已,其实原理是一样的。3,两个基本架构OUTPUT_FORMAT(elf32-littlearm, elf32-bigarm, elf32-littlearm)OUTPUT_ARCH(arm)一句话,照抄.因为我们没有修改的余地,都是系统默认的关键字。第一句指示系统可以有生成两种格式,默认是 elf32-arm,端格式是 little endian4,ENTRY(_ENTRY)指定入口点,LD的手册说,ENTRY POINT 就是程序第一条执行的指令,但是,说老实话,我并不理解,因为这里跟我的理解矛盾了,首先,通常情况,系统需要一个初始化的 STARTUP.S文件来初始化硬件,也就是 bootloader的第一阶段了。那么很自然,入口点需要设置在这段代码的第一条指令中,那么正常运行的时候从第一条指令开始运行。所以这里设置了_ENTRY为入口点,这个在汇编代码中必须得先声明一下为全局,才能用,否则系统找不到。例如:.global _ENTRY但是问题是,如果我用同样的办法,设置另外一个不是第一条指令的入口点,LD并没有报错,但是问题来了,生成的文件和刚才设置入口点为 _ENTRY 的时候一模一样,这就蒙了,到底这个入口点是怎么回事?记得以前ADS的时候也碰到过 entry point的问题,下载仿真的时候确实是自动跳转到 entry point中运行。我想到的可能的原因,第一,生成 elf 文件并不是能直接用在嵌入式平台上面裸跑的,因为我们并没有操作系统,我们不需要elf文件头的那些指示信息提供给操作系统,指示系统怎么去加载文件,在嵌入式上面的完全没有那个必要,只需要将实际的代码提取出来,直接运行就OK,也就是 objcopy的操作,所以我觉得,在裸奔的嵌入式系统上面,entry point是没有意义的,只需要指向整个代码最开始的指令就OK了。暂时我还是不能清晰的理解这个东西。先放下。以后碰到问题再分析。5,一个输出段的标准格式section address (type) : AT(lma) output-section-command output-section-command . region ATlma_region :phdr :phdr . =fillexp前面也说了,所谓的输出段是指最终生成的文件里面的段,所以一个输出段就可以理解为最终文件里面的一个块,那么多个块合起来就是一个完成文件了。而每个小块又分别有什么文件来组成呢?那就是输入段了。我自己实际用到有下面的一些,其他暂时不会用。section_namevma : AT(lma) output-section-command output-section-command . ATlma_regionsection_name 根据ld手册说是有个确定的名字,其他没啥,自己添加一些新段也是可以的。默认的4个段是必须有的.text 代码.rodata 常量,例如字符串什么的.data 初始化的全局变量.bss没有初始化的全局变量其实没什么,可以说,都是固定的,所以一句话,照抄。段名字后面紧跟的是 vma ,也就是这个段在程序运行的时候的地址,例如.text 0x30000000 : *(.text)表示的是代码的运行时地址为 0x30000000 假如你的ROM在 0x0 地址,程序放在ROM中,那个时候程序是不能正常运行的(位置无关代码除外),必须将代码COPY到VMA也就是 0x30000000 中才能正常运行。至于那个 AT(lma) 的关键字,只指示代码连接的时候应该放在什么地方,注意好这个英文是 load memory address,是指程序应该装载在什么地方,而不是指这个段应该在最后生成的bin文件的位置!这个东西蒙骗了我,让我郁闷了1天。elf格式的文件里面不但包含了代码,还包含了各种各样的信息,例如上面说的每个段的lma 和vma,还有其他信息都包含在里面了。默认状态下,lma 是等于当前的vma的,例如 .text 0x30000000 :*(.text)*(.rodata) .data 0x33ffff00 :*(.data)例如我们基本的两个段,我们指定了.text 和.data段的vma,但是没有指定lma,那么lma到底应该是多少?很简单,ld认为当前的lma和vma是相同的,所以lma应该分别是0x30000000 0x33ffff00,编译生成的elf文件很小,但是objcopy出来的文件却非常巨大,达到了60多MB,这是什么问题?elf文件很聪明,他只是保存了信息,.text段的lma是0x30000000,那么elf就保存了知道本程序的代码应该加载到 0x30000000,然后又保存了.data的lma,我们留意到中间有很多的地址空间是空的,并没有实际的代码,elf怎么处理?elf只保存了两个地址和实际的代码,而对于其他空间里面的代码他并不处理,所以可以看出,最后生成的elf文件并不大,也就100多KB而已,但是后来的OBJCOPY操作中,从elf文件中copy出程序代码,这下就糟了,objcopy是从最开始的lma开始,这里是 0x30000000一直复制到最尾段的lma,这里是 0x33ffff00,中间没有代码地方全部补零,那么60多MB的大bin文件就是这样来的。可以验证一下,如果手动指定开始的lma为0的话 .text 0x30000000 : AT(0)*(.text)*(.rodata) .data 0x33ffff00 :*(.data)其中.text段的lma被AT强制指定为0,那么objcop出来的bin文件相当的华丽,达到了700多MB,为什么?都说了,从0开始到 0x33ffff00,中间补零,字节数相当的可观呢。一般我们常用的做法是:1,.data段的 lma 和 vma 都是紧跟着 .text 的,或者用ARM的说法就是 RW段紧跟着RO段,这样的做法非常简单 .text 0x30000000 :*(.text)*(.rodata) .data :*(.data) .bss: *(.bss) *(.COMMON) 只指定RO BASE,然后所有代码都是跟着RO BASE分配,这样非常简单。2,.data段分离出来,连接到不同的vma运行时地址。 .text 0x30000000 :*(.text)*(.rodata) .data 0x31000000 : AT(LOADADDR(.text) + SIZEOF(.text)*(.data) .bss: *(.bss) *(.COMMON) 其实也不难解决,像上面的代码那样做就OK了,上面也分析了,如果vma不同的话,objcopy会一直复制,这样生成的bin文件会很大,怎么解决?很简单,手工指定 .data段的lma地址让 .data段的 lma 紧紧跟着 .text段的末尾,这样生成的 bin 文件就会很漂亮,跟第一种办法生成的bin文件结构一模一样!AT(LOADADDR(.text) + SIZEOF(.text)这个指令大概解释一下,AT 是指定lma 的,然后里面用了两个指令 LOADADDR ,和名字一样这个指令是用来求 lma 地址的! SIZEOF 也就是名字那样,求大小的LOADADDR(.text) 求出 .text 段的 lma,注意是开始地址SIZEOF(.text) 求出 .text 段的大小AT(LOADADDR(.text) + SIZEOF(.text)的效果就是,指定 .data段的lma在 .text段lma的结尾处!这里补充一下,还有一个指令 ADDR(.text) 这个是求vma的,不是求lma。另外,注意一下 .bss段的lma和 .data段的 lma是一样的,这也反映了一个实质问题 .bss 段只分配运行地址 vma,并不实际占空间的。3,如果我想自己添加一些段,应该怎么去实现?例如我要添加一个 .vector 的段,里面放的是一些数据,怎么实现?(1)如果在汇编代码里面添加,那么可以新启动一个段例如在 2440init.S 中添加 .vector 段.section .text. (其他代码).section .vector 在这里声明一个段,并且放连个数据.word0x55.word0xaa汇编代码段的开始由.section 声明,接着后面的都属于这个段,直到第二个 .section 声明为止。我这个 .vector段是需要连接到 0x33ffff00 的,非常的特殊,那么按照前面的办法 .text 0x30000000 :*(.text)*(.rodata) .data 0x31000000 : AT(LOADADDR(.text) + SIZEOF(.text)*(.data) .bss: *(.bss) *(.COMMON) .vector 0x33ffff00 : AT(LOADADDR(.data) + SIZEOF(.data) *(.vector) 可以看出,形式其实是一样,不过看一下,添加的段的lma放在 .data 段的lma的后面,前面也说看 .bss 和 .data的lma是一样的,所以其实无视掉 .bss段就OK了。(2)在C语言中怎么添加一个变量指定放到 .vector段很简单,用GNU扩展语法(注意了,是GNU系列工具通用而已,例如gcc,这个并不是C的标准)格式如下unsigned int _attribute_(section(.vector) vec=0x9988;定义一个 vec 变量,值为 0x9988,分配在 .vector 段,编译后用 objdump 一下查看汇编代码可以发现到Disassembly of section .vector:33ffff00 :33ffff00: 00009988.word 0x0000998833ffff04: 00000055.word 0x0000005533ffff08: 000000aa.word 0x000000aa看到没有?刚才说的在汇编代码和C代码里面定义的数值都被连接进去了 .vector段了,vma也正确最后还可以看看生成的 bin 文件,看看最后的几个数据是不是就是 0x9988 0x55 0xaa ?这样应该就理解了整个连接的过程了吧?4,MEMORY 命令在指定lma中的使用每个段都要用 AT 来指定具体的位置,其实挺烦的,我们有更加简单的办法,我们定义一个内存区域让,然后将所有的段都扔进去。MEMORYrom (rx) : ORIGIN = 0x30000000, LENGTH = 1M注意,我们现在要实现的是lma,并不是vma,也就是说在最后生成的 bin文件中怎么将所有段合在一起。定义一个开始地址为 0x30000000 ,也就是lma,对应上面的 .text段的lma,长度自己设,我设置为 1M ,其实溢出会提示的,随便设就OK了。 .text 0x30000000 :*(.text)*(.rodata) ATrom .data 0x31000000 :*(.data) ATrom .bss: *(.bss) *(.COMMON) .vector 0x33ffff00 : *(.vector) ATrom看到每个输出段的末尾都有个 ATrom 的操作吧?应该大概猜到,通俗一点说就是:将这个输出段扔到rom指定的那个内存区域! ,rom是上面已经定义了,那么这些操作之后,.text .data .vector 都乖乖的扔进 rom 指向的那个区域,注意了,我们说的是lma,所以不要在意那个开始地址,刚才不是说了吗?那个objcopy是从最开始的lma开始copy而已,这样出来的效果和第三点中生成

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论