文档库 最新最全的文档下载
当前位置:文档库 › GUNld链接脚本浅析

GUNld链接脚本浅析

GUNld链接脚本浅析
GUNld链接脚本浅析

GUN-ld 链接脚本浅析

GNU-ld链接脚本浅析本文乃转载, 我在其基础上做了少量修改. 原作者的E-mail 是zhanglei@ 完成于

0. Contents

1. 概论

2. 基本概念

3. 脚本格式

4. 简单例子

5. 简单脚本命令

6. 对符号的赋值

7. SECTIONS命令

8. MEMORY命令

9. PHDRS命令

10. VERSION命令

11. 脚本内的表达式

12. 暗含的连接脚本

1. 概论

每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局. 但你也可以用连接命令做一些其他事情.

连接器有个默认的内置连接脚本, 可用ld --verbose查看. 连接选项-r和-N可以影响默认的连接脚本(如何影响?).

-T选项用以指定自己的链接脚本, 它将代替默认的连接脚本。你也可以使用<暗含的连接脚本>以增加自定义的链接命令.

以下没有特殊说明,连接器指的是静态连接器.

2. 基本概念

链接器把一个或多个输入文件合成一个输出文件.

输入文件: 目标文件或链接脚本文件.

输出文件: 目标文件或可执行文件.

有时把输入文件内的section称为输入section(input section), 把输出文件内的section称为输出section(output sectin).

目标文件的每个section至少包含两个信息: 名字和大小. 大部分section还包含与它相关联的一块数据, 称为section contents(section内容). 一个section可被标记为“loadable(可加载的)”或“allocatable(可分配的)”.

loadable section: 在输出文件运行时, 相应的section内容将被载入进程地址空间中.

allocatable section: 内容为空的section可被标记为“可分配的”. 在输出文件运行时, 在进程地址空间中空出大小同section指定大小的部分. 某些情况下, 这块内存必须被置零.

如果一个section不是“可加载的”或“可分配的”, 那么该section通常包含了调试信息. 可用objdump -h命令查看相关信息.

每个“可加载的”或“可分配的”输出section通常包含两个地址: VMA(virtual memory address 虚拟内存地址或程序地址空间地址)和LMA(load memory address加载内存地址或进程地址空间地址). 通常VMA和LMA是相同的.

可这样来理解VMA和LMA, 假设:

(1) .data section对应的VMA地址是0x08050000, 该section内包含了3个32位全局变量, i、j和k, 分别为1,2,3.

(2) .text section内包含由"printf( "j=%d ", j );"程序片段产生的代码.

连接时指定.data section的VMA为0x08050000, 产生的printf指令是将地址为0x08050004处的4字节内容作为一个整数打印出来。

如果.data section的LMA为0x08050000,显然结果是j=2

如果.data section的LMA为0x08050004,显然结果是j=1

还可这样理解LMA:

.text section内容的开始处包含如下两条指令(intel i386指令是10字节,每行对应5字节):

jmp 0x08048285

movl $0x1,%eax

如果.text section的LMA为0x08048280, 那么在进程地址空间内0x08048280处为“jmp

0x08048285”指令, 0x08048285处为movl $0x1,%eax指令. 假设某指令跳转到地址

gcc编程环境基础4--ld命令和u-boot中的lds文件实例和简单实例分析

gcc编程环境基础4--ld命令和u-boot中的lds文件实例和简单实例分析 ld选项和lds文件 ================================================================================== 0. Contents 1. 概论 2. 基本概念 3. 脚本格式 4. 简单例子 5. 简单脚本命令 6. 对符号的赋值 7. SECTIONS命令 8. MEMORY命令 9. PHDRS命令 10. VERSION命令 11. 脚本内的表达式 12. 暗含的连接脚本 1. 概论 -------------------------------------------------------------------------------- 每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局. 但你也可以用连接命令做一些其他事情. 连接器有个默认的内置连接脚本, 可用ld --verbose查看. 连接选项-r和-N可以影响默认的连接脚本(如何影响?). -T选项用以指定自己的链接脚本, 它将代替默认的连接脚本.你也可以使用<暗含的连接脚本>以增加自定义的链接命令. 以下没有特殊说明,连接器指的是静态连接器. 2. 基本概念 -------------------------------------------------------------------------------- 链接器把一个或多个输入文件合成一个输出文件. 输入文件: 目标文件或链接脚本文件. 输出文件: 目标文件或可执行文件. 目标文件(包括可执行文件)具有固定的格式, 在UNIX或GNU/Linux平台下, 一般为ELF格式. 若想了解更多, 可参考UNIX/Linux平台可执行文件格式分析 有时把输入文件内的section称为输入section(input section), 把输出文件内的section称为输出section(output sectin). 目标文件的每个section至少包含两个信息: 名字和大小. 大部分section还包含与它相关联的一块数据, 称为section contents(section内容). 一个section可被标记为“loadable(可加载的)”或“allocatable(可分配的)”. loadable section: 在输出文件运行时, 相应的section内容将被载入进程地址空间中. allocatable section: 内容为空的section可被标记为“可分配的”. 在输出文件运行时, 在进程地址空间中空出大小同section指定大小的部分. 某些情况下, 这块内存必须被置零. 如果一个section不是“可加载的”或“可分配的”, 那么该section通常包含了调试信息. 可用objdump -h命令查看相关信息. 每个“可加载的”或“可分配的”输出section通常包含两个地址: VMA(virtual memory address虚拟内存地址或程序地址空间地址)和LMA(load memory address加载内存地址或进程地址空间地址). 通常VMA和LMA是相同的.

链接脚本文件语法详解

我们对每个c或者汇编文件进行单独编译,但是不去连接,生成很多.o 的文件,这些.o文件首先是分散的,我们首先要考虑的如何组合起来;其次,这些.o文件存在相互调用的关系;再者,我们最后生成的bin文件是要在硬件中运行的,每一部分放在什么地址都要有仔细的说明。我觉得在写makefile的时候,最为重要的就是ld的理解,下面说说我的经验: 首先,要确定我们的程序用没有用到标准的c库,或者一些系统的库文件,这些一般是在操作系统之上开发要注意的问题,这里并不多说,熟悉在Linux编程的人,基本上都会用ld命令;这里,我们从头开始,直接进行汇编语言的连接。 我们写一个汇编程序,控制GPIO,从而控制外接的LED,代码如下; .text .global _start _start: LDR R0,=0x56000010 @GPBCON寄存器 MOV R1,# 0x00000400 str R1,[R0] LDR R0,=0x56000014 MOV R1,#0x00000000 STR R1,[R0] MAIN_LOOP: B MAIN_LOOP 代码很简单,就是一个对io口进行设置然后写数据。我们看它是如何编译的,注意我们这里使用的不是arm-linux-gcc而是arm-elf-gcc,二者之间没有什么比较大的区别,arm-linux-gcc 可能包含更多的库文件,在命令行的编译上面是没有区别。我们来看是如何编译的: arm-elf-gcc -g -c -o led_On.o led_On.s 首先纯编译不连接 arm-elf-ld -Ttext 0x00000000 -g led_On.o -o led_on_elf 用Ttext指明我们程序存储的地方,这里生成的是elf文件,还不是我们真正的bin,但是可以借助一些工具可以进行调试。然后: arm-elf-objcopy -O binary -S led_on_elf led_on.bin 生成bin文件。

Lds 语法规则基础与分析

连接脚本的格式 ==================== 连接脚本是文本文件. 你写了一系列的命令作为一个连接脚本. 每一个命令是一个带有参数的关键字,或者是一个对符号的赋值. 你可 以用分号分隔命令. 空格一般被忽略. 文件名或格式名之类的字符串一般可以被直接键入. 如果文件名含有特殊字符,比如一般作为分隔文件名用的逗号, 你可以把文件名放到双引号中. 文件名中间无法使用双引号. 你可以象在C语言中一样,在连接脚本中使用注释, 用'/*'和'*/'隔开. 就像在C中,注释在语法上等同于空格. 简单的连接脚本示例 ============================ 许多脚本是相当的简单的. 可能的最简单的脚本只含有一个命令: 'SECTIONS'. 你可以使用'SECTIONS'来描述输出文件的内存布局. 'SECTIONS'是一个功能很强大的命令. 这里这们会描述一个很简单的使用. 让我们假设你的程序只有代码节, 初始化过的数据节, 和未初始化过的数据节. 这些会存在于'.text','.data'和'.bss'节, 另外, 让我们进一 步假设在你的输入文件中只有这些节. 对于这个例子, 我们说代码应当被载入到地址'0x10000'处, 而数据应当从0x8000000处开始. 下面是一个实现 这个功能的脚本: SECTIONS { . = 0x10000; .text : { *(.text) } . = 0x8000000; .data : { *(.data) } .bss : { *(.bss) } } 你使用关键字'SECTIONS'写了这个SECTIONS命令, 后面跟有一串放在花括号中的符号赋值和输出节描述的内容.

arm链接文件规则(mynote)

Arm中的链接文件的规则 -T选项是ld命令中比较重要的一个选项,可以用它直接指明代码的代码段、数据段、博士生、 段,对于复杂的连接,可以专门写一个脚本来告诉编译器如何连接。 -Ttext addr -Tdata addr -Tbss addr arm-elf-ld -Ttext 0x00000000 -g led_On.o -o led_on_elf ,运行地址为0x00000000,由于没有data和bss,他们会默认的依次放在后面。相同的代码不同的Ttext,你可以对比一下他们之间会变的差异,ld会自动调整跳转的地址。 *简单的Linker script (1) SECTIONS命令: The SECTIONS command tells the linker how to map input sections into output sections, and how to place the output sections in memory. 命令格式如下: SECTIONS { sections-command sections-command ...... } 其中sections-command可以是ENTRY命令,符号赋值,输出段描述,也可以是overlay描述。

(2) 地址计数器‘.’(location counter): 该符号只能用于SECTIONS命令内部,初始值为‘0’,可以对该符号进行赋值,也可以使用该符号进行计算或赋值给其他符号。它会自动根据SECTIONS命令内部所描述的输出段的大小来计算当前的地址。 (3) 输出段描述(output section description): 前面提到在SECTIONS命令中可以作输出段描述,描述的格式如下: section [address] [(type)] : [AT(lma)] { output-section-command output-section-command ... } [>region] [AT>lma_region] [:phdr :phdr ...] [=fillexp] 很多附加选项是用不到的。其中的output-section-command又可以是符号赋值,输入段描述,要直接包含的数据值,或者某一特定的输出段关键字。 *linker script 实例 ============================== OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { . = 0xa3f00000; __boot_start = .; .start ALIGN(4) : { *(.text.start) }

GUNld链接脚本浅析

GUN-ld 链接脚本浅析 GNU-ld链接脚本浅析本文乃转载, 我在其基础上做了少量修改. 原作者的E-mail 是zhanglei@ 完成于 0. Contents 1. 概论 2. 基本概念 3. 脚本格式 4. 简单例子 5. 简单脚本命令 6. 对符号的赋值 7. SECTIONS命令 8. MEMORY命令 9. PHDRS命令 10. VERSION命令 11. 脚本内的表达式 12. 暗含的连接脚本 1. 概论 每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局. 但你也可以用连接命令做一些其他事情. 连接器有个默认的内置连接脚本, 可用ld --verbose查看. 连接选项-r和-N可以影响默认的连接脚本(如何影响?).

-T选项用以指定自己的链接脚本, 它将代替默认的连接脚本。你也可以使用<暗含的连接脚本>以增加自定义的链接命令. 以下没有特殊说明,连接器指的是静态连接器. 2. 基本概念 链接器把一个或多个输入文件合成一个输出文件. 输入文件: 目标文件或链接脚本文件. 输出文件: 目标文件或可执行文件. 有时把输入文件内的section称为输入section(input section), 把输出文件内的section称为输出section(output sectin). 目标文件的每个section至少包含两个信息: 名字和大小. 大部分section还包含与它相关联的一块数据, 称为section contents(section内容). 一个section可被标记为“loadable(可加载的)”或“allocatable(可分配的)”. loadable section: 在输出文件运行时, 相应的section内容将被载入进程地址空间中. allocatable section: 内容为空的section可被标记为“可分配的”. 在输出文件运行时, 在进程地址空间中空出大小同section指定大小的部分. 某些情况下, 这块内存必须被置零. 如果一个section不是“可加载的”或“可分配的”, 那么该section通常包含了调试信息. 可用objdump -h命令查看相关信息.

3月7日 text-base

关于uboot中的TEXT_BASE【转】2009年11月11日星期三 23:03都知道U-BOOT分为两个阶段,第一阶段是(~/cpu/arm920t/start.S中)在FLASH上运行(一般情况下),完成对硬件的初始化,包括看门狗,中断缓存等,并且负责把代码搬移到SDRAM中(在搬移的时候检查自身代码是否在SDRAM中),然后完成C程序运行所需要环境的建立,包括堆栈的初始化等,最后执行一句跳转指令: ldr pc, _start_armboot _start_armboot: .word start_armboot, 进入到/lib_arm/board.c中的函数void start_armboot (void),从此就进入了第二阶段。这是在很多资料上都有讲述的,所以勿需多言了。 现在对于第一阶段有几个问题,以前我一直是没有搞明白的,既然在FLASH中的代码是把自己拷贝到SDRAM中,那么在S3C2410的内存地址空间,就有两份的启动代码,第一份就是在FLASH中,第二份就是在SDRAM中。根据链接脚本文件(~/board/smdk2410/u-boot.lds) OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/ OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { . = 0x00000000; /* 后记:这个链接起始地址实际上被-Ttest $(TEST_BASE)更新了*/ . = ALIGN(4); .text : { cpu/arm920t/start.o (.text) *(.text) } . = ALIGN(4); .rodata : { *(.rodata) } . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); .got : { *(.got) } . = .; __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) }

arm-linux-ld指令详解

arm-linux-ld指令详解 arm-linux-ld指令详解 我们对每个c或者汇编文件进行单独编译,但是不去连接,生成很多.o 的文件,这些.o文件首先是分散的,我们首先要考虑的如何组合起来;其次,这些.o文件存在相互调用的关系;再者,我们最后生成的bin文件是要在硬件中运行的,每一部分放在什么地址都要有仔细的说明。我觉得在写makefile的时候,最为重要的就是ld的理解,下面说说我的经验: 首先,要确定我们的程序用没有用到标准的c库,或者一些系统的库文件,这些一般是在操作系统之上开发要注意的问题,这里并不多说,熟悉在Linux编程的人,基本上都会用ld命令;这里,我们从头开始,直接进行汇编语言的连接。 我们写一个汇编程序,控制GPIO,从而控制外接的LED,

代码如下; .text.global _start_start: LDR R0,=0x56000010 @GPBCON寄存器 MOV R1,# 0x00000400 str R1,[R0] LDR R0,=0x56000014 MOV R1,#0x00000000 STR R1,[R0] MAIN_LOOP: B MAIN_LOOP 代码很简单,就是一个对io口进行设置然后写数据。我们看它是如何编译的,注意我们这里使用的不是arm-linux-gcc而

是arm-elf- gcc,二者之间没有什么比较大的区别, arm-linux-gcc可能包含更多的库文件,在命令行的编译上面是没有区别。我们来看是如何编译的: arm-elf-gcc -g -c -o led_On.o led_On.s 首先纯编译不连接 arm-elf-ld -Ttext 0x00000000 -g led_On.o -o led_on_elf 用Ttext指明我们程序存储的地方,这里生成的是elf 文件,还不是我们真正的bin,但是可以借助一些工具可以进行调试。然后: arm-elf-objcopy -O binary -S led_on_elf led_on.bin 生成bin文件。 -T选项是ld命令中比较重要的一个选项,可以用它直接指明代码的代码段、数据段、博士生、

u-boot启动流程分析

先看board/smsk2410/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。 OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm", "elf32-littlearm") /*OUTPUT_FORMAT("elf32-arm","elf32-arm","elf32-arm")*/ OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { .= 0x00000000; /*指定可执行image文件的全局入口点,通常这个地址都放在ROM(flash)0x0位置。必须使编译器知道这个地址,通常都是修改此处来完成*/ .=ALIGN(4); .text : { cpu/arm920t/start.o (.text) *(.text) } .=ALIGN(4); .rodata : { *(.rodata) } .=ALIGN(4);

第一个要链接的是cpu/arm920t/start.o,那么U-Boot的入口指令一定位于这个程序中。下面详细分析一下程序跳转和函数的调用关系以及函数实现。 1.Stage1:cpu/arm920t/start.S 这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。

U-Boot启动代码流程图

/* the actual reset code */ reset: //复位启动子程序 /*设置CPU为SVC32模式*/ mrs r0,cpsr bic r0,r0,#0x1f ;;位清除,将某些位的值置0:r0 = r0 AND(!0x1f) orr r0,r0,#0xd3 ;;逻辑或,将r0与立即数进行逻辑或,放在r0中(第一个) msr cpsr,r0 /*关闭看门狗*/ /* turn off the watchdog */ #if defined(CONFIG_S3C2400) # define pWTCON 0x15300000 # define INTMSK 0x14400008 /* Interupt-Controller base addresses */ # define CLKDIVN 0x14800014 /* clock divisor register */ #elif defined(CONFIG_S3C2410) # define pWTCON 0x53000000 # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */ # define INTSUBMSK 0x4A00001C # define CLKDIVN 0x4C000014 /* clock divisor register */ #endif #if defined(CONFIG_S3C2400)|| defined(CONFIG_S3C2410)

ld -Ttext 与连接脚本

ld -Ttext 与连接脚本 arm-linux-ld命令 -T选项是ld命令中比较重要的一个选项,可以用它直接指明代码的代码段、数据段、博士生、 段,对于复杂的连接,可以专门写一个脚本来告诉编译器如何连接。 -Ttext addr -Tdata addr -Tbss addr arm-elf-ld -Ttext 0x00000000 -g led_On.o -o led_on_elf ,运行地址为0x00000000,由于没有data和bss,他们会默认的依次放在后面。相同的代码不同的Ttext,你可以对比一下他们之间会变的差异,ld 会自动调整跳转的地址。 *简单的Linker script (1) SECTIONS命令: The SECTIONS command tells the linker how to map input sections into output sections, and how to place the output sections in memory. 命令格式如下: SECTIONS { sections-command sections-command ...... } 其中sections-command可以是ENTRY命令,符号赋值,输出段描述,也可以是overlay描述。 (2) 地址计数器?.‘(location counter): 该符号只能用于SECTIONS命令内部,初始值为?0‘,可以对该符号进行赋值,也可以使用该符号进行计算

或赋值给其他符号。它会自动根据SECTIONS命令内部所描述的输出段的大小来计算当前的地址。(3) 输出段描述(output section description): 前面提到在SECTIONS命令中可以作输出段描述,描述的格式如下: section [address] [(type)] : [AT(lma)] { output-section-command output-section-command ... } [>region] [AT>lma_region] [:phdr :phdr ...] [=fillexp] 很多附加选项是用不到的。其中的output-section-command又可以是符号赋值,输入段描述,要直接包含的数据值,或者某一特定的输出段关键字。 *linker script 实例 ============================== OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { . = 0xa3f00000; __boot_start = .; .start ALIGN(4) : { *(.text.start) } .setup ALIGN(4) : {

GCC-LD 连接脚本分析--uboot.ld

GCC-LD 连接脚本分析--uboot.ld GCC-LD 连接脚本分析--uboot.lds (2011-03-18 09:48) 一键转载 标签: 转载 原文地址:GCC-LD 连接脚本分析--uboot.lds 作者:Embedded_Li1 Command Language The command language provides explicit control over the link process allowing complete specification of the mapping between the linker s input files and its output. It controls: input files file formats output file layout addresses of sections placement of common blocks 1.1 input section and output section

所谓的输出段,是指生成的文件,例如elf 中的每个段 所谓的输入段,是指连接的时候提供LD的所有目标文件(OBJ)中的段 1.2 ima and vma lma = load memory address vma = vitual memory address 1.3 relate with system and entry point OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) 1.4 输出段的标准格式 section [address] [(type)] : [AT(lma)] { output-section-command output-section-command ... } [>region] [AT>lma_region] [:phdr :phdr ...] [=fillexp] 前面也说了,所谓的输出段是指最终生成的文件里面的段,所以一个输出段就可以理解为 最终文件里面的一个块,那么多个块合起来就是一个完成文

linux zImage生成过程详解

可以看到,在顶层makefile的第278行,包含了scripts/Kbuild.include文件,在这里定义了大量的函数和变量,供顶层makefile和其他makefile文件使用。 在顶层makefile文件的第412行,包含了arch/arm/Makefile。这个是体系结构相关makefile 文件。它定义了体系结构相关的一些变量及规则。 当执行”make”时,arch/arm/Makefile中的185行的规则将是make遇到的第一个规则: all: $(KBUILD_IMAGE) KBUILD_IMAGE这个变量是arch/arm/Makefile的第182行定义。 KBUILD_IMAGE := zImage 然后看zImage的构建规则,在arch/arm/Makefile的第212行开始定义 zImage Image xipImage bootpImage uImage: vmlinux $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ build变量在scripts/Kbuild.include文件中第114行定义: build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj boot变量在arch/arm/Makefile的187行定义: boot := arch/arm/boot MACHINE变量的值在arch/arm/Makefile的147行开始定义 ifneq ($(machine-y),) MACHINE := arch/arm/mach-$(machine-y)/ else MACHINE := endif 这里machine-y := s3c2410,所以变量MACHINE的值为 MACHINE := arch/arm/mach-s3c2410 所以上面的规则可写为如下形势 zImage: vmlinux $(Q)$(MAKE) -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj= \

相关文档