ARM-GCC-LD脚本

从以前的经验,链接脚本是嵌入式开发,单片机开发相当重要的一个东西。它完成的工作是做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 = .;

 

 (以上)

 

转载于:https://www.cnblogs.com/cbs-soft/archive/2010/01/08/1642388.html

<think>嗯,用户现在问的是关于在交叉编译时使用make命令并指定各种工具链参数的含义。我需要先回顾一下之前的历史对话。之前用户询问了如何在ARM架构上交叉编译H.264库,比如x264,我详细介绍了步骤,包括安装工具链、配置、编译安装等。现在用户可能是在实际应用中遇到了具体的make命令参数的问题,想了解每个参数的意义。 首先,用户给出的命令是:make CC=arm-linux-gcc AR=arm-linux-ar LD=arm-linux-gcc RANLIB=arm-linux-ranlib STRIP=arm-linux-strip。这看起来是在调用make时覆盖了默认的编译工具,指定了交叉编译器的各个工具。我需要逐个解释每个参数的作用,以及它们在整个编译过程中的角色。 接下来,我需要确保每个变量的含义准确无误。CC是C编译器,AR是静态库归档工具,LD链接器,RANLIB用于生成静态库的索引,STRIP用于去除调试符号。这些都是在交叉编译时常用的变量,用来覆盖项目默认的本地工具链,确保使用ARM架构的工具链。 然后,我需要考虑用户可能的背景。用户可能正在尝试手动指定这些工具,而不是通过配置脚本(如configure)。这可能是因为某些项目可能需要手动设置,或者用户遇到configure无法正确识别工具链的情况。需要解释这些参数的重要性,以及为什么在交叉编译时需要指定它们。 另外,用户可能在之前的步骤中按照指导使用了交叉编译工具链,但在实际执行make时遇到了问题,比如工具链未正确应用,导致编译出的仍然是x86的可执行文件。这时候,手动指定这些变量可以确保整个构建过程使用正确的工具。 还需要注意用户可能的误区。例如,是否所有项目都支持通过make参数指定这些变量?或者是否需要同时设置其他环境变量,如CXX、CFLAGS、LDFLAGS等。这时候可能需要提醒用户,不同项目的构建系统可能不同,有些可能需要更全面的配置。 此外,用户可能没有意识到有些项目在configure阶段已经确定了工具链,如果在make阶段再次覆盖,是否会导致冲突?或者是否需要两者结合使用,比如在configure时指定--host和交叉编译前缀,同时在make时覆盖某些工具?需要指出这一点,并建议用户优先使用项目的配置脚本,除非必要才手动指定。 最后,用户可能希望了解如何验证这些参数是否生效,比如检查生成的中间文件或最终二进制文件的架构是否正确,或者通过查看构建日志确认使用的工具链是否正确。可能需要给出一些验证的方法,如使用file命令检查二进制文件类型,或者查看make的输出信息。 总结来说,用户的需求是理解这些make参数的具体作用,确保交叉编译的正确性,并可能解决在编译过程中遇到的问题。我的回答需要详细解释每个参数,说明其在编译流程中的作用,并给出实际应用的注意事项,帮助用户正确应用这些参数,避免常见错误。</think>在使用 `make` 进行交叉编译时,通过 `CC=arm-linux-gcc AR=arm-linux-ar ...` 的形式指定工具链参数,是为了覆盖构建系统默认的本地工具链,强制使用针对目标架构(如ARM)的交叉编译工具。以下是每个参数的具体含义和作用: --- ### **参数详解** #### **1. `CC=arm-linux-gcc`** - **含义**:指定 **C编译器**(C Compiler)为 `arm-linux-gcc`。 - **作用**: 用于编译C语言源代码。交叉编译时需替换为针对目标架构(如ARM)的编译器,确保生成的二进制文件兼容目标平台(而非宿主机x86/x64)。 #### **2. `AR=arm-linux-ar`** - **含义**:指定 **静态库归档工具**(Archiver)为 `arm-linux-ar`。 - **作用**: 用于将多个目标文件(`.o`)打包成静态库(`.a`)。交叉编译时必须使用目标平台的归档工具,否则生成的静态库可能无法在目标设备上链接。 #### **3. `LD=arm-linux-gcc`** - **含义**:指定 **链接器**(Linker)为 `arm-linux-gcc`(或直接指定 `arm-linux-ld`)。 - **作用**: 负责将目标文件和库链接为最终可执行文件或动态库。直接使用 `arm-linux-gcc` 调用链接器可能更便捷(隐含调用 `ld` 并自动传递标准库路径)。 #### **4. `RANLIB=arm-linux-ranlib`** - **含义**:指定 **静态库索引生成工具**(Random Library Generator)为 `arm-linux-ranlib`。 - **作用**: 为静态库(`.a`)生成符号索引,加速链接过程。若未指定交叉编译版本的 `ranlib`,可能因索引格式不兼容导致链接错误。 #### **5. `STRIP=arm-linux-strip`** - **含义**:指定 **符号剥离工具**(Strip Tool)为 `arm-linux-strip`。 - **作用**: 移除可执行文件或库中的调试符号(如 `.debug` 段),减少文件体积。需使用目标平台的 `strip` 工具,否则可能损坏二进制文件。 --- ### **典型应用场景** 此命令用于 **直接覆盖构建系统的默认工具链**,常见于以下情况: 1. **未使用 `./configure` 配置工具链**: 某些项目可能未通过 `./configure` 脚本配置交叉编译参数,需手动指定工具链。 2. **覆盖配置阶段的默认值**: 即使已运行 `./configure`,也可在 `make` 阶段强制使用特定工具链。 3. **简化小型项目的编译**: 对于简单的 `Makefile` 项目,直接通过命令行参数指定工具链更快捷。 --- ### **示例命令** ```bash make \ CC=arm-linux-gcc \ AR=arm-linux-ar \ LD=arm-linux-gcc \ RANLIB=arm-linux-ranlib \ STRIP=arm-linux-strip ``` --- ### **注意事项** #### 1. **工具链路径问题** - 需确保交叉编译工具(如 `arm-linux-gcc`)已正确安装,且在系统 `PATH` 环境变量中,否则需指定绝对路径(如 `/opt/toolchain/bin/arm-linux-gcc`)。 #### 2. **依赖其他工具** - 若项目依赖其他工具(如 `CXX` 用于C++编译),需补充指定: ```bash CXX=arm-linux-g++ ... ``` #### 3. **与 `./configure` 配合使用** - 对于支持 `./configure` 的项目(如 `x264`),更推荐通过 `--host` 和 `--cross-prefix` 配置交叉编译,例如: ```bash ./configure --host=arm-linux --cross-prefix=arm-linux- ``` 这会自动设置 `CC`、`AR` 等参数,避免手动指定。 #### 4. **验证工具链兼容性** - 编译完成后,可通过 `file` 命令检查生成文件的架构: ```bash file output_binary # 期望输出:ELF 32-bit LSB executable, ARM... ``` --- ### **常见问题** #### **Q1: 为何要指定 `LD=arm-linux-gcc` 而不是 `arm-linux-ld`?** - 直接使用 `arm-linux-gcc` 调用链接器,可以自动传递标准库路径(如 `libc`),避免手动指定 `-L` 参数,简化编译流程。 #### **Q2: 如果漏掉某些参数(如 `RANLIB`)会怎样?** - 可能导致静态库索引生成失败,链接时出现 `undefined reference` 错误。建议完整指定所有工具链参数。 #### **Q3: 是否需要同时设置环境变量(如 `CROSS_COMPILE`)?** - 部分项目支持通过 `CROSS_COMPILE=arm-linux-` 自动推导工具链前缀(如 `CC=$(CROSS_COMPILE)gcc`),但需具体项目支持。手动指定各参数更通用。 --- ### **总结** 通过 `CC`、`AR`、`LD` 等参数显式指定交叉编译工具链,是确保生成文件兼容目标平台(如ARM)的关键步骤。尤其适用于未通过 `configure` 脚本配置的简单项目或需手动覆盖默认工具链的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值