gcc ld链接脚语法简明讲解

什么是链接脚本

链接脚本的主要功能是描述程序在内存空间是如何排布的,链接器根据链接脚本的描述最后输出可执行文件。当需要指示链接器执行其他的操作时,就需要手动编写链接脚本,因此要“随心所欲”的操作链接器,必须先学会链接脚本的语言。

链接器一定会使用链接器脚本的,当不指定链接脚本文件时, 链接器将会使用一个缺省的脚本。

linux环境下可以使用:

$ld -verbose 查看这个默认的链接脚本。

可以使用“  -T ”文件名“   ”命令指定链接脚本文件。

链接脚本的主要包括的内容:命令语句和赋值语句。

 

什么叫输入段,什么叫输出段

所谓的输出段,是指生成的文件,例如 elf 中的每个段

所谓的输入段,是指连接的时候提供LD的所有目标文件(OBJ)中的段。

常用的4个段:

• .text program code;

• .rodata read-only data;

• .data read-write initialized data;

• .bss read-write zero initialized data. 

 

LMA(加载时域) 和 VMA(运行时域)

LMA = load memory address

VMA = vitual memory address

运行时域和加载时域可以通俗的理解为:运行地址和加载(存储)地址;加载地址就是程序放置的地址,运行地址就是程序定位的绝对地址,即在编译连接时定位的地址。如果程序是在flash里运行,则运行地址和加载地址是相同的。如果程序是在ram里运行,但程序是存储在flash里,则运行地址指向ram,而加载地址是指向flash。

 

连接脚本基本语法

1、分号,作为分隔符号  

2、注释使用/**/

3、表达式和运算符和c一样

4、命令语句

 

ENTRY(symbol)

指定符号symbol的值为入口地址(Entry Point)。入口地址即进程的第一条用户空间的指令所在地址,在进程的地址空间中的,被指定在ELF文件头的ELF32_Ehdr的e_entry成员中。ld有多种指定程序入口方式:(优先级逐渐降低)

a、ld命令 -e选项;

b、连接脚本中ENTRY(symbol)的命令;

c、如果定义了_start符号,使用_start符号的值;

d、如果存在.text段,则使用.text段的第一字节的地址;

e、使用地址0x00000000;

 

MEMORY

{

name [(attr)] : ORIGIN = origin, LENGTH = len

...

}

name是链接描述文件中用于引用该区域的名称。 区域名称在链接描述文件之外没有任何意义。 区域名称存储在单独的名称空间中,不会与符号名称,文件名称或节名称冲突。 每个内存区域在MEMORY命令中必须有一个不同的名称。

attr字符串是可选的属性列表,用于为链接器脚本中未设置属性的输入段指定特定的内存区域。 如果没有为某些输入段指定输出段,则链接器将创建一个与输入段同名的输出段。 如果定义区域属性,则链接器将使用它们为它创建的输出段选择存储区域。

‘R’  Read-only section 只读段

‘W’ Read/write section 读写段

‘X’  Executable section 可执行段

‘A’ Allocatable section 可分配段

‘I’ Initialized section 初始化段

‘L’ Same as ‘I’

‘!’ Invert the sense of any of the preceding attributes 反转任何前述属性的含义

ORIGIN可以缩写为org或o:表示存储区起始地址。

origin表达式必须计算为常数,并且不能包含任何符号。如(0x08000000,0x20000000)

LENGTH可以缩写为len或l:表示存储区大小。

len表达式必须计算为常数,并且不能包含任何符号。(如 512 、128K、16M)

可以通过ORIGIN(memory)和LENGTH(memory)函数访问表达式中的存储器的原点和长度:_fstack = ORIGIN(ram) + LENGTH(ram) - 4;

 

SECTIONS

{

sections-command

sections-command

...

}

SECTIONS命令告诉链接器如何将输入段映射到输出段,以及如何将输出段放置在内存中。

每个小节命令(sections-command)可以是以下之一:

•an ENTRY command ENTRY命令(基本不用)

•a symbol assignment 符号分配(当如设置一些必要的符号,会在汇编中使用这些符号,一般主要用于地址定位,便于快速寻址)

•an output section description输出节描述

•an overlay description覆盖描述(多代码段的VMA相同,加载时选择加载选中的段到VMA地址)

为了方便在命令中使用位置计数器,SECTIONS命令中允许使用ENTRY命令和符号分配。 这也使链接描述文件更容易理解,因为可以快速地找到输出文件布局中有意义的位置,并在命中使用它。

输出段描述和叠加说明如下:

如果您在链接描述文件中未使用SECTIONS命令,则链接器会将每个输入段放置在名称相同的输出段中,顺序是在输入文件中首先遇到这些节。 例如,如果所有输入节都出现在第一文件中,则输出文件中的节顺序将与第一文件中的顺序相匹配。 第一部分将位于地址零。

 

Output Section Description (输出段描述)

section [address] [(type)] :

[AT(lma)]

[ALIGN(section_align)]

[SUBALIGN(subsection_align)]

[constraint]

{

output-section-command

output-section-command

...

} [>region] [AT>lma_region] [:phdr :phdr ...] [=fillexp]

每个output-section-command可以是以下之一::

• a symbol assignment 符号分配

• an input section description 输入段描述

• data values to include directly 直接包含的数据值

• a special output section keyword 特殊的输出段关键字

 

(1)、输出段名称

 section位置表示段名,段名必须符合输出格式的约束,例如a.out 输出格式包含'.text' '.data' '.bss' 三个输出段名。

'/DISCARD/ '是一个特殊的段名,用于标记需要丢弃的输入段。名为“ / DISCARD /”的输出段中分配的任何输入段均不包含在输出文件中。

 

(2)、输出段地址

address表示输出段的VMA(运行时域:运行地址)。如果没提供address这个值,那么连接器将根据region(内存区域)(如果存在)地址设置输出段的VMA,否则连接器将基于当前位置计数器的值设置输出段的VMA。

如果提供address得值,则输出段的地址将精确设置为该地址。

输出段的对齐要求是输出段中包含的任何输入段的最严格对齐。

例如:

.text . : { *(.text) } 设置‘.text'输出段的地址为当前位置计数器的值。
.text : { *(.text) }设置'.text'输出段的地址为当前位置计数器的值与输入段中'.text'以最严格对齐方式对齐。

 

(4)、输出段属性type

NOLOAD 该部分应标记为不可加载,以便在程序运行时不会将其加载到内存中。

DSECT

COPY

INFO

OVERLAY 支持这些类型名称以实现向后兼容,并且很少使用。 它们都具有相同的效果:该段应标记为不可分配,以便在程序运行时不为该段分配内存。

(5)输出段LMA(加载地址)

每个段都有一个运行地址(VMA)和一个加载地址(LMA);address的值将设置为输出段的VMA。

AT关键字后面的表达式lma指定该段的加载地址。另外,您也可以使用“ AT> lma_region”表达式为该段的加载地址指定一个存储区域。

如果未为可分配段指定AT或AT>,则链接器将设置LMA,将当前段的VMA和LMA与同一区域中的先前输出段的设置相同。 如果没有前面的输段或该段不可分配,则链接器会将LMA与VMA设置为相同的值。

(6)强制输出对齐ALIGN

ALIGN(exp,align)

ALIGN(align) == ALIGN(.,align)  返回 位置计数器(.)或任意表达式 与下一个对齐边界对齐的地址。

(白话就是把当前位置计数器的值或者传入的exp的值,以align字节对齐后的地址)align必须是2的幂。
例如:

                                ALIGN(1)      ALIGN(2)       ALIGN(4)        ALIGN(8)      ALIGN(16)     ALIGN(32)     ALIGN(64)

. = 0x33e00000; 0x33e00000 0x33e00000 0x33e00000 0x33e00000 0x33e00000 0x33e00000 0x33e00000     

. = 0x33e00001; 0x33e00004 0x33e00004 0x33e00004 0x33e00008 0x33e00010 0x33e00020 0x33e00030     

. = 0x33e00002; 0x33e00004 0x33e00004 0x33e00004 0x33e00008 0x33e00010 0x33e00020 0x33e00030

. = 0x33e00003; 0x33e00004 0x33e00004 0x33e00004 0x33e00008 0x33e00010 0x33e00020 0x33e00030

. = 0x33e00004; 0x33e00004 0x33e00004 0x33e00004 0x33e00008 0x33e00010 0x33e00020 0x33e00030

(7)强制输入对齐SUBALIGN

可以使用SUBALIGN在输入段中强制输入段对齐。 指定的值会覆盖输入段给出的任何对齐方式。

 

(8)、输入段描述

最常见的输出段命令是输入段描述。

输入段描述是最基本的链接描述文件操作。你可以使用输出段来告诉链接器如何在内存中布置程序。你可以使用输入段描述来告诉链接器如何将输入文件映射到您的内存布局中。

输入段描述由一个文件名(可选)和一个圆括号内的段名称列表组成。

例如:

*(.text ) “*”是一个通配符,它匹配所有文件名。

*(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors) EXCLUDE_FILE(文件列表)表示剔除指定的输入文件,即不包含这些文件的指定段。

包含多个段

*(.text .rdata) 这种方法两个段顺序不定的。连接器先找见谁谁就在前面
*(.text) *(.rdata) 这个种方法两个段的循序是一定。

data.o(.data) 指定某个文件的某个段。

输入段通配符模式

‘*’ 匹配任意数量的字符
‘?’ 匹配任何单个字符
‘[chars]’ 匹配任何字符的单个实例(chars); '-'字符可用于指定字符范围,如'[a-z]'表示匹配任何小写字母
‘\’引用紧跟的字符

输入段通用符号(common symbols)

在许多目标文件格式中,通用符号并没有占用一个section。连接器认为:输入文件的所有通用符号在名为COMMON的section内。

在大多数情况下,输入文件中的通用符号将放置在输出文件中的“ .bss”部分。 例如:

.bss { *(.bss) *(COMMON) }

 

(9)输出段内存区域

可以使用“> region”将段分配给先前定义的内存区域。

例如:

MEMORY { rom : ORIGIN = 0x1000, LENGTH = 0x1000 }

SECTIONS { ROM : { *(.text) } >rom }

 

(10)输出段填充

可以使用“ = fillexp”设置整个段的填充样式。 fllexp是一个表达式。 段中任何其他未指定的内存区域(例如,由于输入段的所需对齐而留下的间隙)将使用表达式的值将其填充,FILL语句覆盖在段定义中发生的位置之后的内存位置; 通过包含多个FILL语句,您可以在输出段的不同部分中使用不同的FILL模式。

例如:

使用值“ 0x90”填充未指定的内存区域:FILL(0x90909090)== SECTIONS { .text : { *(.text) } =0x90909090 }

 

(11)覆盖描述Overlay Description 

OVERLAY [start] : [NOCROSSREFS] [AT ( ldaddr )]

{

secname1

{

output-section-command

output-section-command

...

} [:phdr...] [=fill]

secname2

{

output-section-command

output-section-command

...

} [:phdr...] [=fill]

...

} [>region] [:phdr...] [=fill]

简要的概述就是:多代码段的VMA相同,加载时选择加载选中的段到VMA地址。

对于OVERLAY中的每个部分,链接器会自动提供两个符号。 符号__load_start_secname定义为该节的起始加载地址。 符号__load_stop_secname定义为该节的最终加载地址。 secname中所有在C标识符中不合法的字符都将被删除。 C(或汇编程序)代码可以根据需要使用这些符号来移动覆盖的部分。在覆盖的末尾,位置计数器的值设置为覆盖的起始地址加上最大部分的大小。

例如:

OVERLAY 0x1000 : AT (0x4000)

{

.text0 { o1/*.o(.text) }

.text1 { o2/*.o(.text) }

}

text0与text1的运行地址都是0x1000。“ .text0”与“ .text0”按照顺序在地址0x4000处加载(储存)。 如果引用,将定义以下符号:__load_start_text0,__load_stop_text0,__load_start_text1,__load_stop_text1。

用C语言编写搬移代码如下:

extern char __load_start_text1, __load_stop_text1;

memcpy ((char *) 0x1000, &__load_start_text1,(&__load_stop_text1 - &__load_start_text1));

 

垃圾收集(防止被优化掉KEEP)

使用链接垃圾收集(“ --gc-sections”)时,标记不应删除的部分通常很有用。 

这可以通过在KEEP(*(.init))或KEEP(SORT_BY_NAME(*)(.ctors))中用KEEP()包围输入节的通配符条目来实现。

 

LOADADDR(section)

返回目标段的绝对LMA地址(加载地址)。

 

ADDR(section)

返回目标段的绝对LMA地址(运行地址)。

 

PROVIDE (symbol = expression)
链接脚本仅在符号被引用且未由链接中包含的任何对象定义的情况下定义symbol符号。

 

PROVIDE_HIDDEN(symbol)

该符号将被隐藏并且不会导出。

SECTIONS

{

.text :

{

*(.text)

_etext = .;

PROVIDE(etext = .);

}

}

在此示例中,如果程序定义“ _etext”(带有下划线),则链接程序将给出多次定义错误。 另一方面,如果程序定义了“ etext”(没有前导下划线),则链接程序将在程序中静默使用该定义。 如果程序引用了“ etext”但未定义它,则链接器将使用链接器脚本中的定义。

参考:

https://blog.csdn.net/cat_lover/article/details/50727988

https://blog.csdn.net/yam_killer/article/details/7909308

https://blog.csdn.net/btoh_workstation/article/details/27510869

https://www.cnblogs.com/cjjnjust/articles/1617086.html

  • 8
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: GCC LD手册中文版是指GNU Compiler Collection(GCC)和GNU链接器(LD)的开发者文档的中文翻译版本。GCC是一套自由软件编译器,支持多种编程语言,如C、C++、Objective-C、Fortran、Ada和D等。LD是一款用于将可重定位目标文件链接成可执行文件或共享库的工具。这些工具是开源的,并且广泛用于Linux、Unix和其他操作系统中。 GCC LD手册中文版包含了GCCLD的详细介绍、使用方法、命令语法、选项和示例等。这些文档提供了极其详细和全面的指导,适合开发人员使用。无论是刚入门编程的新手,还是有丰富编程经验的专业人士,都可以从中获得优质的指导和帮助。 GCC LD手册中文版对于在Linux或Unix平台开发软件的开发人员来说是非常有用的。它可以帮助彻底理解GCCLD的特点、功能和用法,并能够提高工作效率和代码质量。此外,为了更好的使用这些工具,学习英文也是很有必要的。 总之,GCC LD手册中文版是一份非常有用的文档。它是一款开源软件开发中的必备工具,有助于开发人员更加深入地理解这些工具的使用。 ### 回答2: GCCLD均是常见的编译工具,其中GCC是编译器,LD链接器。而gcc ld手册中文版,即GCCLD的中文使用手册。 该手册中包含GCCLD的基础语法、参数选项、使用方法等内容,是GCCLD的使用参考书。对于初学者来说,该手册能够帮助他们更好地学习和使用GCCLD。 手册主要内容包括GCCLD的编译链接流程和用法,GCCLD还支持一些选项和参数,如优化选项、调试选项、链接脚本等等。同时,手册中也介绍了GCCLD在不同操作系统上的使用方式。 总之,gcc ld手册中文版对于使用GCCLD的人来说是非常重要的,它帮助用户更好地理解和使用这两个工具,提高编译和链接的效率,减少错误发生的概率。因此,对于程序员来说,掌握gcc ld手册中文版是非常必要的。 ### 回答3: GCCLD都是在Linux系统上最常用的编译器和链接器。它们被广泛用于开发各种软件应用。它们的手册是帮助开发人员正确地使用这些工具的关键资源。 GCC手册介绍了GCC编译器的功能和使用方法。它提供了丰富的信息,包括语法、选项和参数等,以便开发人员正确地编译和构建代码。此外,GCC手册还描述了所支持的平台和版本、调试技术以及其他编译器相关的主题。这本手册对于想要使用GCC编译器的人来说是一个重要的参考资料。 另一方面,LD手册介绍了LD链接器的功能和使用方法。它详细描述了链接器如何将代码、数据和库文件组合成可执行文件,以便运行程序。LD手册解释了链接器所需的选项和参数,以及常见问题的解决方案。此外,这本手册还包括链接器支持的平台和版本,以及与链接器相关的其他主题。这本手册对于想要深入了解链接器的开发人员来说是非常有用的。 总而言之, GCCLD手册中文版 提供了编译器和链接器的完整指南,以帮助开发人员正确地使用它们来创建高效、可靠的应用程序。这些手册对于程序员学习、理解和使用这些工具是非常重要的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值