GNU链接脚本详解
0. 前言
每一个链接都是由链接脚本控制的,链接脚本是用链接命令语言编写的脚本。链接都会用到一个链接脚本,如果你没有指定自己的脚本,就会使用默认的链接脚本。可以用 "--verbose" 命令行选项显示默认的连接脚本。指定命令行参数,比如 '-r'、'-N'都会影响默认的链接脚本。也可以用 '-T' 来指定自己的链接脚本,也可以隐式地把自己的连接脚本当成链接输入文件,就像普通的链接文件一
样,参见链接文件说明。
如上图,链接器是将多个对象文件链接成可执行程序。
链接器输入文件:目标文件或链接脚本文件;
链接器输出文件:可执行文件;
目标文件(包括可执行文件) 具有固定的格式,在 Unix 或GNU/linux 平台下,一般为 ELF 格式。
1. 链接器脚本
链接器是由链接器脚本控制,该链接器脚本控制输入文件的链接方式。该脚本以连接器命令语言编写,控制一下链接属性:
- 输入文件中的部分如何映射到输出文件;
- 输出文件的文件格式和内存布局;
- 已创建段的运行时加载属性;
- 代码执行入口点;
- 共享库版本;
链接器有一个内置脚本,它用作将代码和数据分配到内存的默认链接器脚本。用户不能修改默认脚本,但是,它可以通过两种方式进行更改:
- 它可以完全由用户定义的脚本文件替换。在这种情况下,文件名在链接器命令行上指定为链接器选项 -T 的参数;
- 可以通过将链接器命令上的用户定义脚本文件指定为普通链接器输入文件来扩充它;
指定为普通链接器输入文件的脚本称为隐式脚本。因为它们扩充了默认链接描述文件,所以隐式脚本通常只包含 symbol assignments(符号分配) 或INPUT、GROUP、VERSION 命令。
2. 基础知识
2.1 分号
在连接器脚本中,分号通常仅出于美观用作分隔符,否则将被忽略。但一下地方是必需的:
- 分号必需出现在 symbol assignments 的末尾;
- 分号必需出现在 PHDRS 命令的末尾;
2.2 注释
可以使用标准 C 分隔符将注释包含在链接描述文件中:
/* ... */
2.3 字符串
文件或格式名称等字符串通常可以直接输入,无需分隔符。
如果文件名包含逗号等字符,否则该字符将用于分隔文件名,则文件名可以用双引号引起来。 文件名中不能使用双引号字符。
2.4 表达式
许多命令参数接受算术表达式。 表达式的语法与 C 中的表达式语法相同,具有以下特点:
- 所有表达式都被计算为 long 或 unsigned long 类型的整数
- 所有常量都是整数
- 提供所有 C 算术运算符
- 可以定义、创建和引用全局变量
- 支持多种预定义功能
2.5 位置计数器
链接器中有个特殊变量:点号 ' . '
点号始终包含当前输出位置计数器,由于 点号 始终引入输出节中的位置,它必须始终出现在 SECTIONS 命令中的表达式中。
点号 可以出现在表达式中允许使用普通符号的任何位置,但它的赋值有副作用。
3. 脚本命令
3.1 指定入口地址命令
程序运行的第一条指令就是调用入口地址,可以使用 ENTRY 链接命令来指定程序的入口地址。
入口地址的指定方式有:
- 通过命令行指定:gcc -e symbol;
- 链接脚本指定:ENTRY(symbol)
4. 文件处理命令
4.1 INCLUDE
使用 INCLUDE 将其他链接脚本包含到当前脚本,链接器会在当前目录和用 -L 参数指定过的目录下查找被包含的文件。
链接脚本可以嵌套包含,最多层数为 10 层。
INCLUDE 既可以放在链接脚本的开始,也可以放在 MEMROY 或 SECTIONS 命令里面,或放在输出节的描述里面。
格式:INCLUDE filename
功能:包含其他脚本文件。
搜索路径:当前目录、-L添加的目录。
放置位置:链接脚本开始、MEMORY或SECTIONS中、输出节描述中。
4.2 INPUT
INPUT 命令直接链接指定的文件名,就像从命令行输入一样。
例如如果要包含subr.o,
但是又不想在每一条链接命令中都写上的话,就可以在链接脚本中使用 ‘INPUT(subr.o)’。实际上,还可以把所有输入文件 (*.o) 都写在链接脚本里面,然后只要用 ' -T ' 指定一下链接脚本就好了。
为了防止设置了根目录,文件名要用'/'开始,这样连接脚本就会从根目录开始检索文件,否则,链接器就会在当前目录下查找文件,如果找不到文件,链接器就会在所有归档库里面检索。根目录也可以在文件名一开始的时候用 ‘=’ 来强制指定,或者在文件名前面加上 $SYSROOT。
如果使用 ‘INPUT (-lfile)’,链接器会自动翻译成libfile.a,就好像使用命令行参数 "-l" 一样。如果使用INPUT命令在链接脚本中包含文件的话,文件会从链接脚本所在的目录开始检索。这会影响到归档文件的检索。
格式:
INPUT(file, file, …)
INPUT(file file …)
功能:指定要链接的输入文件(.o,.a)。 搜索路径:$SYSROOT、当前目录。
subr.o包含:INPUT(subr.o)
libfile.a包含:INPUT(-lfile)
4.3 GROUP
格式:
GROUP(file, file, …)
GROUP(file file …)
GROUP命令语法跟 INPUT 差不多,但是专门用来指定归档文件 (*.a),这个会不断的检索直到发现一个新的未定义的符号引用。参见命令行参数里面关于 '-(' 的描述。
4.4 OUTPUT
OUTPUT用来设置输出文件的名称,等价的命令行参数为-o filename。默认输出文件名称为a.out。
格式:OUTPUT(filename)
功能:设置输出文件名称。 等价命令行参数:-o filename
4.5 SEARCH_DIR
SEARCH_DIR命令用来添加链接器的搜索路径,等价的命令行参数是 -L path。
如果即用了 -L 也用了SEARCH_DIR,那么链接器会优先使用 -L 设置的路径。
格式:SEARCH_DIR(path)
功能:添加链接器的搜索路径。
等价命令行参数:-L path
4.6 STARTUP
STARTUP命令用来指定第一个被链接的输入文件,等价于命令行中第一个输入文件,当目标操作系统的要求程序入口地址必须位于第一个输入文件的时候使用。
格式:STARTUP(filename)
功能:指定第一个被链接的输入文件。
使用场景:目标操作系统的要求程序入口地址必须位于第一个输入文件的时候。
5. 输出文件格式命令
5.1 OUTPUT_FORMAT
OUTPUT_FORMAT命令用来设置输出文件使用的BFD格式。等价的命令行参数为 --oformat bfdname。命令行参数优先。
OUTPUT_FORMAT可以设置三个格式,当命令行没有 -EB 和 -EL的时候,使用第一个格式,当有-EB的时候使用第二个参数,当有 -EL 的时候,使用第三个参数。
格式:
OUTPUT_FORMAT(bfdname)
OUTPUT_FORMAT(default, big, little)
功能:设置输出文件使用的BFD格式。 等价命令行:--oformat bfdname
5.2 TARGET
TARGET命令用来设置链接器读取输入文件的时候使用的BFD格式。等价命令行参数 -b bfdname。
格式:
TARGET(bfdname)
6. 其他命令
7. 符号赋值
7.1 像C 一样简单的赋值
symbol = expression ;
symbol += expression ;
symbol -= expression ;
symbol *= expression ;
symbol /= expression ;
symbol <<= expression ;
symbol >>= expression ;
symbol &= expression ;
symbol |= expression ;
符号被定义为在脚本中具有全局范围;
符号赋值语句在两方面与链接器脚本表达式中使用运算符不同:
- 赋值只能在表达式的根部进行,例如, a = b+3; 是允许的,但 a+b=3; 是一个错误;
- 赋值语句必须以尾部分号结束
赋值语句可以出现在下面位置:
- 作为链接器脚本中的独立命令;
- 作为 SECTIONS 命令中的独立语句;
- 作为 SECTIONS 命令中某一节定义内容的一部分;
7.2 HIDDEN
格式:
HIDDEN(symbol = expression)
定义成 HIDDEN 的符号不会被输出到目标文件,如:
HIDDEN(floating_point = 0);
SECTIONS
{.text :{*(.text)HIDDEN(_etext = .);}HIDDEN(_bdata = (. + 3) & ~ 3);.data : { *(.data) }
}
7.3 PROVIDE
格式:
PROVIDE(symbol = expression)
定义一个输入文件里面引用但未定义的符号,如:
SECTIONS
{.text :{*(.text)_etext = .;PROVIDE(etext = .);}
}
_etext 可以在输入文件中引用,如果输入文件中也定义了 _etext,那么优先使用输入文件中的,但是如果输入文件中定义了_etext,链接的时候就会报多重定义的错误。
7.4 PROVIDE_HIDDEN
跟 PROVIDE 功能类似,但不会输出到目标文件中。
8. SECTIONS 命令
SECTIONS
{sections-commandsections-command…
}
在一个脚本文件中只能声明一条 SECTIONS 命令,但是该命令可以包含任意数量的语句来指定必要的映射和放置信息。
每一个SECTIONS 的组成都有:
- ENTRY 命令;
- 符号排列;
- 输出section 的描述;
- 覆盖描述;
如果链接文件中没有定义SECTIONS,那么输入文件中的节就会原封不动的输出到目标文件中。
8.1 输出section 描述
SECTIONS命令中最常用的语句是输出部分描述,它指定了输出部分的属性:它的位置、对齐方式、内容、填充模式和目标内存区域。
格式:
section [address] [(type)] :[AT(lma)][ALIGN(section_align) | ALIGN_WITH_INPUT][SUBALIGN(subsection_align)][constraint]{output-section-commandoutput-section-command…} [>region] [AT>lma_region] [:phdr :phdr …] [=fillexp] [,]
- 一个输出section 的名称可以由任何字符序列组成;
- section 名称声明周围需要留白,以确保名称的明确性;
- 输出section 名称后面,冒号 ' : ' 和 ' { } ' 是必需的;
- 每个输出section 都有一个虚拟地址和加载地址,虚拟地址 VMA就是 section 加载到内存中的地址,加载地址 LMA 就是在目标文件中的地址。LMA 可以通过 AT(lma) 来设置;
- ALIGN 强制输出section地址对齐,SUBALIGN 强制输入section 地址对齐;
- 一个输出section 的描述,由一个或多个语句组成,包括:
- 符号分配;
- 输入section 描述;
- 直接包含是输出section 的数据;
- 特殊的输出section 关键字;
8.2 输入 section 描述
输入section 的描述指定了被映射到输出section 的输入部分。
*(.init.rodata .init.rodata.*)
这里使用了通配符,表示输入部分为:
- * 代表所有目标文件;
- 后面括号,是指.init.rodata 段 和 .init.rodata.* 段;
这句话意思是:将所有目标文件中的 .init.rodata 段和 .init.rodata.* 段都包含到输出 section 中。
8.2.1 输入 section 描述的通配符
* 号:任意数量的字符;
? 号:任何单个字符;
[CHARS] 号:匹配任意一个CHARS内的单个字符,可用 ' - ' 号表示范围。例如,[ A-Z ],表示匹配 A~Z之间的单个字符;
8.2.2 KEEP
KEEP(*(.initcallearly.init))
当链接命令行使用选项 --gc-secionts 后,链接器可能将某些它认为没用的 section 过滤掉。
KEEP 用来强制链接器保留一些特定的 section。
例如上面代码,其实可以看成:
*(.initcallearly.init)
加上KEEP,则要求链接器不能优化掉这个section,哪怕是没用。
参考:
https://blog.csdn.net/shenjin_s/article/details/88712249
https://zhuanlan.zhihu.com/p/516338675
相关文章:

GNU链接脚本详解
0. 前言 每一个链接都是由链接脚本控制的,链接脚本是用链接命令语言编写的脚本。链接都会用到一个链接脚本,如果你没有指定自己的脚本,就会使用默认的链接脚本。可以用 "--verbose" 命令行选项显示默认的连接脚本。指定命令行参数…...

酷柚易汛ERP-账户管理操作指南
1、应用场景 对账户进行管理,可设置账户当前余额、期初余额和设置是否为默认账户。 2、主要操作 2.1 新增支付账户 打开【资料】-【账款管理】,点击【新增】添加账户类别,输入相关信息并保存,账户编号和名称为必录项。&#x…...
函数的连续性
函数在某一点极限存在,不一定连续 函数的左极限 函数的右极限 函数在某点连续需要满足三个条件 1、左右极限存在 2、左右极限相等 3、函数在该点的极限值等于在该点的函数值 满足1、2两个条件函数在该点极限存在。...
Pandas groupby方法中的group_keys属性
pandas版本1.5.3中groupby方法,当设置group_keysTrue时,会以groupby的字段为第一级索引,如下述代码中time_id作为第一级索引,同时保留了原dataframe(df)中的索引作为第二级索引。 >>> df.groupby…...

win 命令替代鼠标的操作
操作方式都是在 winR 输入框输入或者终端输入 1、快速打开 控制面板 运行control 2、快速打开 电源选项 运行powercfg.cpl 3、快速打开 网络连接 运行ncpa.cpl 4、快速打开 程序和功能 运行appwiz.cpl 5、快速打开 Windows Defender防火墙 运行Firewall.cpl 6、快速打开 鼠标 …...

Shopee活动取消规则是什么?shopee官方促销活动怎么取消?
作为一家知名的电商平台,shopee官方对于消费者取消促销活动的请求给予了相应的规定和处理流程。 shopee活动取消规则是什么? 首先,消费者应该明确了解虾皮的促销活动取消规则。根据虾皮的官方规定,消费者在参与促销活动之前&…...
安卓常见设计模式2------构建者模式(Kotlin版)
1. W1 是什么,什么是构建者模式? 构建者模式(Builder Pattern)是一种创建复杂对象的设计模式。它通过使用链式调用的方式,逐步构建对象,使得代码更易读、可维护,并且可以处理许多可选参数的情况…...

redis主从复制+哨兵
1.主从复制 redis配置文件redis.conf master机器:IP 192.168.1.5 ,端口 6379 设置配置参数 daemonize yes #bind 127.0.0.1 -::1 protected-mode no port 6379 dbfilename "dump.rdb" dir "/root/redis/my_redis_conf/dumpdir" l…...

html动态爱心超文本标记代码,丝滑流畅有特效,附源码
没想到现在看个剧(点燃我,温暖你)要的同款居然是代码,李峋 这盛世如你所愿啊!李峋的同款爱心代码来啦,拿走试试吧~ <!DOCTYPE html> <html><head><title></title&g…...
力扣:162. 寻找峰值(Python3)
题目: 峰值元素是指其值严格大于左右相邻值的元素。 给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。 你可以假设 nums[-1] nums[n] -∞ 。 你必须实现时…...

【Python】20大报告生成词云
这个我其实写过一篇类似的博客,但是那个的文件对象是.csv,对应到.docx文件的话,就不太适用了。如下: Python生成词云-CSDN博客 代码: import jieba import os import wordcloud import numpy as np from PIL import…...
目标检测YOLO实战应用案例100讲-基于无人机的轻量化目标检测系统设计
目录 前言 国内外研究现状 国外研究现状 国内研究现状...

ansible-第二天
ansible 第二天 以上学习了ping、command、shell、script模块,但一般不建议使用以上三个,因为这三个模块没有幂等性。举例如下: [rootcontrol ansible]# ansible test -a "mkdir /tmp/1234"[WARNING]: Consider using the file …...
【测试工具】UnixBench 测试
一、UnixBench 简介 UnixBench 原本叫做 BYTE UNIX benchmark suite。软件为 Unix 类的系统提供了一些基本的性能指标。通过不同的测试来测试系统不同方面的性能(2D,3D,CPU,内存等等)。这些测试的结果将和一些标准的系…...

软件测试金融项目,在测试的时候一定要避开的一些雷区
软件测试金融项目需要格外谨慎和专注,因为这些项目通常涉及大量的交易、用户隐私和其他敏感信息。以下是一些软件测试金融项目时需要关注的方面: 1. 数据保护 在测试金融项目时,必须确保用户数据和投资信息得到保护。测试人员必须确保测试环…...

顺序图——画法详解
百度百科的定义: 顺序图是将交互关系表示为一个二维图。纵向是时间轴,时间沿竖线向下延伸。横向轴代表了在协作中各独立对象的类元角色。类元角色用生命线表示。当对象存在时,角色用一条虚线表示,当对象的过程处于激活状态时&…...

easyexcel==省市区三级联动
省市区三级联动,不选前面的就没法选后面的 package com.example.demoeasyexcel.jilian2; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; import org.apache.poi.ss.use…...

Linux进程控制(二)--进程等待(一)
前言:之前我们讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。 另外,进程一旦变成僵尸状态,那就刀枪不入,就连 kill -9 也无能为力࿰…...

【C++】C++11常用特性梳理
C11特性梳理 1. 列表初始化2. auto & decltype3. 右值引用3.1. 左右值引用比较3.2. 右值引用的意义3.3. 万能引用与完美转发3.4. 移动构造与移动赋值 4. default & delete5. 可变参数模板6. push_back 与 emplace_back7. lambda表达式7.1. 捕捉列表 8. function包装器8…...

修改iframe生成的pdf的比例
如图想要设置这里的默认比例 在iframe连接后面加上#zoom50即可,50是可以随便设置的,设置多少就是多少比例 <iframe src"name.pdf#zoom50" height"100%" width"100%"></iframe>...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...