嵌入式Linux-linux连接脚本( 语法 )_及补充
补充:
https://blog.csdn.net/XiaoXiaoPengBo/article/details/78743348
输出section描述
输出section描述具有如下格式:
SECTION [ADDRESS] [(TYPE)] : [AT(LMA)]
{
OUTPUT-SECTION-COMMAND
OUTPUT-SECTION-COMMAND
...
} [>REGION] [AT>LMA_REGION] [:PHDR :PHDR ...] [=FILLEXP]
[ ]内的内容为可选选项, 一般不需要.
SECTION:section名字
SECTION左右的空白、圆括号、冒号是必须的,换行符和其他空格是可选的。
每个OUTPUT-SECTION-COMMAND为以下四种之一,
符号赋值语句
一个输入section描述
直接包含的数据值
一个特殊的输出section关键字
输出section名字(SECTION):
输出section名字必须符合输出文件格式要求,比如:a.out格式的文件只允许存在.text、.data和.bsssection名。而有的格式只允许存在数字名字,那么此时应该用引号将所有名字内的数字组合在一起;另外,还有一些格式允许任何序列的字符存在于section名字内,此时如果名字内包含特殊字符(比如空格、逗号等),那么需要用引号将其组合在一起。
输出section地址(ADDRESS):
ADDRESS是一个表达式,它的值用于设置VMA。如果没有该选项且有REGION选项,那么连接器将根据REGION设置VMA;如果也没有REGION选项,那么连接器将根据定位符号‘.’的值设置该section的VMA,将定位符号的值调整到满足输出section对齐要求后的值,输出section的对齐要求为:该输出section描述内用到的所有输入section的对齐要求中最严格的。
例子:
.text . : { *(.text) }
和
.text : { *(.text) }
这两个描述是截然不同的,第一个将.textsection的VMA设置为定位符号的值,而第二个则是设置成定位符号的修调值,满足对齐要求后的。
ADDRESS可以是一个任意表达式,比如ALIGN(0x10)这将把该section的VMA设置成定位符号的修调值,满足16字节对齐后的。
注意:设置ADDRESS值,将更改定位符号的值。
输入section描述:
最常见的输出section描述命令是输入section描述。
输入section描述是最基本的连接脚本描述。
输入section描述基础:
基本语法:FILENAME([EXCLUDE_FILE(FILENAME1 FILENAME2 ...) SECTION1 SECTION2 ...)
FILENAME文件名,可以是一个特定的文件的名字,也可以是一个字符串模式。
SECTION名字,可以是一个特定的section名字,也可以是一个字符串模式
例子是最能说明问题的,
*(.text) :表示所有输入文件的.text section
(*(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors)) :表示除crtend.o、otherfile.o文件外的所有输入文件的.ctorssection。
data.o(.data) :表示data.o文件的.datasection
data.o :表示data.o文件的所有section
*(.text .data) :表示所有文件的.textsection和.datasection,顺序是:第一个文件的.text section,第一个文件的.data section,第二个文件的.textsection,第二个文件的.datasection,...
*(.text) *(.data) :表示所有文件的.text section和.data section,顺序是:第一个文件的.textsection,第二个文件的.textsection,...,最后一个文件的.textsection,第一个文件的.datasection,第二个文件的.datasection,...,最后一个文件的.datasection
下面看连接器是如何找到对应的文件的。
当FILENAME是一个特定的文件名时,连接器会查看它是否在连接命令行内出现或在INPUT命令中出现。
当FILENAME是一个字符串模式时,连接器仅仅只查看它是否在连接命令行内出现。
注意:如果连接器发现某文件在INPUT命令内出现,那么它会在-L指定的路径内搜寻该文件。
字符串模式内可存在以下通配符:
* :表示任意多个字符
:表示任意一个字符
[CHARS]:表示任意一个CHARS内的字符,可用-号表示范围,如:a-z
:表示引用下一个紧跟的字符
在文件名内,通配符不匹配文件夹分隔符/,但当字符串模式仅包含通配符*时除外。
任何一个文件的任意section只能在SECTIONS命令内出现一次。看如下例子,
SECTIONS {
.data : { *(.data) }
.data1 : { data.o(.data) }
}
data.o文件的.datasection在第一个OUTPUT-SECTION-COMMAND命令内被使用了,那么在第二个OUTPUT-SECTION-COMMAND命令内将不会再被使用,也就是说即使连接器不报错,输出文件的.data1section的内容也是空的。
再次强调:连接器依次扫描每个OUTPUT-SECTION-COMMAND命令内的文件名,任何一个文件的任何一个section都只能使用一次。
读者可以用-M连接命令选项来产生一个map文件,它包含了所有输入section到输出section的组合信息。
再看个例子,
SECTIONS {
.text : { *(.text) }
.DATA : { [A-Z]*(.data) }
.data : { *(.data) }
.bss : { *(.bss) }
}
这个例子中说明,所有文件的输入.text section组成输出.text section;所有以大写字母开头的文件的.datasection组成输出.DATAsection,其他文件的.datasection组成输出.datasection;所有文件的输入.bsssection组成输出.bsssection。
可以用SORT()关键字对满足字符串模式的所有名字进行递增排序,如SORT(.text*)。
通用符号(commonsymbol)的输入section:
在许多目标文件格式中,通用符号并没有占用一个section。连接器认为:输入文件的所有通用符号在名为COMMON的section内。
例子,
.bss { *(.bss) *(COMMON) }
这个例子中将所有输入文件的所有通用符号放入输出.bsssection内。可以看到COMMOMsection的使用方法跟其他section的使用方法是一样的。
有些目标文件格式把通用符号分成几类。例如,在MIPSelf目标文件格式中,把通用符号分成standard common symbols(标准通用符
----------------------------------------------------------------------------------------------分隔符 ----------------------------------------------------------------------------------------------
https://www.cnblogs.com/AbeDay/p/5026843.html
介绍
每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制。 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局。
链接器在进行链接时,会根据链接脚本从输入的.o文件中挑选出感兴趣的section,把它们合并生成新的section,这些新产生的section归属于目标文件的某个segment(段),并出现在目标文件中。例如file1.o和file2.o分别有两个.text,它们在链接后生产的目标文件也会有一个.text,而这个.text既是由file1.o和file2.o的.text合并而来的。
终端中使用链接脚本
代码示例
//提供名为:test.lds的连接脚本
arm-linux-ld -Ttest.lds -o test test1.o test2.o
//不提供连接脚本
arm-linux-ld -o test test1.o test2.o
具体介绍
-
如果你不提供链接脚本,则链接器会使用一个缺省的脚本,这个脚本是被编译进链接器可执行文件的.
-
可以使用–verbose命令行显示缺省的链接器脚本的内容.
arm-linux-ld --verbose //查看arm-linux-ld连接的默认连接脚本
ld --verbose //ld连接的默认连接脚本
- 你可以使用-T命令行来提供你自己的链接脚本来替换缺省的链接脚本.
//提供名为:test.dls的连接脚本
arm-linux-ld -Ttest.dls -o test test1.o test2.o
- -T选项可以指定数据段,代码段,bss段起始位置。(使用默认连接脚本的选项)
-Ttext startaddr
-Tdata startaddr
-Tbss startaddr
//例子示范
ld –Ttext 0x00000000 –g led_on.o –o led_on_elf
语法基本介绍
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命令内部,初始值为‘0’,可以对该符号进行赋值,也可以使用该符号进行计算或赋值给其他符号。它会自动根据SECTIONS命令内部所描述的输出段的大小来计算当前的地址。
输出段描述
前面提到在SECTIONS命令中可以作输出段描述
命令格式如下:
section [address] [(type)] : [AT(lma)]
{
output-section-command
output-section-command ...
} [>region] [AT>lma_region] [:phdr :phdr ...] [=fillexp]
secname和contents是必须的,其他的都是可选的。下面挑几个常用的看看:
- secname:段名
- contents:决定哪些内容放在本段,可以是整个目标文件,也可以是目标文件中的某段(代码段、数据段等)
- start:定义本段连接(运行)的地址,如果没有使用
AT(ldadr),本段存储的地址也是start。GNU网站上说start可以用任意一种描述地址的符号来描述。(连接(运行)的地址:就是当可执行文件,运行时,段的位置) -
AT(ldadr):定义本段存储(加载)的地址。(本段存储的地址:就是编译完之后,相应段存储在ELF运行文件中的存储地址)
例子+解释
例子
/* nand.lds */
SECTIONS {
firtst 0x00000000 : { head.o init.o }
second 0x30000000 : AT(4096) { main.o }
}
解释
-
head.o放在0x00000000地址开始处,init.o放在head.o后面,他们的运行地址也是0x00000000,即连接和存储地址相同(没有AT指定);
-
main.o放在4096(0x1000,是AT指定的,存储地址)开始处,但是它的运行地址在0x30000000,运行之前需要从0x1000(加载处)复制到0x30000000(运行处),此过程也就用到了读取Nand flash。
-
这就是存储地址和连接(运行)地址的不同,称为加载时域和运行时域,可以在.lds连接脚本文件中分别指定
例子+解释
OUTPUT_ARCH(arm) //设置编译出来可执行文件的架构
ENTRY(_start) //设置可执行文件起始的的段位置。这里设置了_start,因此在在输入段中,一定存在一个名为_start的段。
SECTIONS {
. = 0xa3f00000; //设置起始位置
__boot_start = .; //复制参数,在最后用到了
.start ALIGN(4) :{
*(.text.start)
} //ALIGN(4)表示4字节对齐
.setup ALIGN(4) : {
setup_block = .;
*(.setup)
setup_block_end = .;
}
.text ALIGN(4) : {
*(.text)
}
.rodata ALIGN(4) : {
*(.rodata)
}
.data ALIGN(4) : {
*(.data)
}
.got ALIGN(4) : {
*(.got)
}
__boot_end = .;
.bss ALIGN(16) : {
bss_start = .;
*(.bss)
*(COMMON)
bss_end = .;
} //ALIGN(16)表示16字节对齐
.comment ALIGN(16) : {
*(.comment)
}
stack_point = __boot_start + 0x00100000;
loader_size = __boot_end - __boot_start;
setup_size = setup_block_end - setup_block;
}
参考
Linux链接脚本学习–lds(写的比较基础的,可以作为入门看看)
百度文库解释(前半部分还不错的)
lds链接脚本*三篇博客(超级长的,排版不好的文章.不过很多人转了,可以看一下。)
浙公网安备 33010602011771号