GNU 链接脚本LDS介绍

2023-11-16

前言

程序的从C语言代码变成可以在目标机器上执行额文件,可以分为如下步骤

  • 编译
    • 预编译:将宏定义等转义编译:将C语言变成目标文件(.o档案)
    • 编译/汇编:将预编译过后的目标变为目标文件
  • 链接:合并多个目标文件(.o/.a)等为最终的可执行文件。

LD命令是GNU链接程序,它可以接受 ld -T 输入链接脚本,根据链接脚本的定义来决定链接方式。在 Linux中断(2) 一文中,有简单提到过Linux里用到了很多链接技巧。因此,学习Linux内核,多少需要对链接脚本基本语法格式有初步了解。 本文即对GNU链接脚本的基本格式进行一些介绍。

先看一个基本的链接脚本。在执行_ld_链接时,如果不传入链接脚本,会使用默认的链接脚本。_ld –verbose_可以显示默认的链接脚本,如下:

[lambert ~]$ ld --verbose
GNU ld version 2.25.1-32.base.el7_4.1
  Supported emulations:
   elf_x86_64
   elf32_x86_64
   elf_i386
   i386linux
   elf_l1om
   elf_k1om
using internal linker script:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
/* Copyright (C) 2014 Free Software Foundation, Inc.
   Copying and distribution of this script, with or without modification,
   are permitted in any medium without royalty provided the copyright
   notice and this notice are preserved.  */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
              "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("/usr/x86_64-redhat-linux/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_D                                                                                                                                                             IR("/usr/local/lib64"); SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/x86_64-redhat-lin                                                                                                                                                             ux/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib                                                                                                                                                             ");
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SE                                                                                                                                                             GMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
  .interp         : { *(.interp) }
  .note.gnu.build-id : { *(.note.gnu.build-id) }
  .hash           : { *(.hash) }
  .gnu.hash       : { *(.gnu.hash) }
  .dynsym         : { *(.dynsym) }
  .dynstr         : { *(.dynstr) }
  .gnu.version    : { *(.gnu.version) }
  .gnu.version_d  : { *(.gnu.version_d) }
  .gnu.version_r  : { *(.gnu.version_r) }
/* 以下部分省略 */

基本概念

SECTION

我们知道一个可执行文件有多个段,如.text段、BSS段、data段等。这里SECTION即对应可执行程序的段。一个段由以下几个部分组成:

  • 段名称
  • 段内容
  • 段长度信息

段可以定义为Loadable和allocatable两种加载方式

  • loadable: 执行时该section是否需要被加载到内存
  • allocatable: 先保留内存的一块空间让程序执行时使用,如.bss段

Symbol

一个object 档案存放多个symbol,又称为symbol table(符号表)。Symbol通常就是全局变量、静态变量或是函数的名称。我们可以使用objdump -t 可执行文件 或者 readelf -a 可执行文件_或者nm 可执行文件_来查看对应可执行文件的符号表。

LMA V.S. VMA

LMA: Load Memory Address, 表示程序被装载到内存哪个位置

VMA: Virutal Memroy Address,表示程序执行的位置,即CPU执行对应程序指令的位置。

通常,LMA = VMA。但是在一些嵌入式体系中, LMA和VMA不一样。而其中最常见的一种情况就是,程序被放到ROM中。而程序要运行时候的地址是内存虚拟地址,也就是VMA。之后小节会有例子做介绍。

Link Script可以基本总结如下:

  • 以文本形式存放
  • 由多个command組成
  • 每个command可能是
    • keyword + 参数
    • 设定symbol
  • command 可以用;分開,空白會被忽略
  • 使用/_* * _/注释
  • 字串直接打,如果有用到script保留的字元如.可以用"包住

简单链接脚本范例

SECTIONS
{
  . = 0x10000;
  .text : { *(.text) }
  . = 0x8000000;
  .data : { *(.data) }
  .bss : { *(.bss) }
}

.表示内存位置,起始值为0。结束值则由链接器计算把所有input section的数据整合到output section的长度。而.如果没有指定明确的内存地址的话,就会被设定为上一个地址的结束地址。

第三行:设定内存位置为0x10000

第四行:把所有输入object档中({ (.text) })存放到*输出**object档案的.text区块中。

第五行:设定内存位置为0x8000000

第六行、第七行:先放已初始化全局变量(.data),所有输入目标文件中的.data字段都会被打包到此位置;紧接着再放未初始值的全局变量(.bss)。

简单链接脚本命令

ENTRY(symbol) :设置程序入口点

文件相关命令

_INCLUDE filename
_
在看到这个命令的时候才去载入filename这个linker script。可以被放在不同的命令如SETCTION, MEMORY等。
_INPUT(file1 file2 …)
**_指定加载的输入object档案,如abc.o这样的档案。
_
GROUP(file1 file2 …)
_指定加载的输入archieve档案,如libabc.a这样的档案。
_
AS_NEEDED(file1 file2 …)
_在INPUT和GROUP使用的命令,用来告诉linker说如果object里面的数据有被reference到才link进来,猜测应该可以减少储存空间。范例(未测试请自行斟酌):INPUT(file1.o file2.o AS_NEEDED(file3.o file4.o))
_
OUTPUT(filename)
_和gcc -o filename 一样
**_SEARCH_DIR(path)
_
和-L path一样
_*STARTUP(filename)
*
_和INPUT相同,唯一差别是ld保证这个档案一定是第一个被link

输出文件格式相关命令

_OUTPUT_FORMAT(bfdname)
**_指定输出object档案的binary 文件格式,可以使用objdump -i列出支持的binary 文件格式
_
OUTPUT_FORMAT(default, big, little)
_指定输出object档案预设的binary 文件格式,big endian的binary 文件格式以及little endian的binary 文件格式。可以使用objdump -i列出支持的binary 文件格式
_
TARGET(bfdname)
**_告诉ld用那种binary 文件格式读取输入object档案要,可以使用objdump -i列出支持的binary 文件格式

设定内存块别名的命令

REGION_ALIAS(alias, region) 对内存区域设定别名,这样输出段可以灵活地映射。具体可以参考<LD:REGION_ALIAS>

其他链接器相关命令

ASSERT(exp, message) 条件不成立打印message并结束链接过程
EXTERN(symbol1 symbol2 …) 强迫让指定的symbol设成undefined,手册说一般用在刻意要使用非标准的API。
FORCE_COMMON_ALLOCATION 手册和男人说和兼容性有关,手册上是说强迫分配空间给common symbols,即使是link relocate档案。(common symbols不知道是什么)
OUTPUT_ARCH(bfdarch) 指定输出的平台,可以透过objdump -i查询支持平台
INSERT [ AFTER | BEFORE ] output_section 指定在预设linker script命令被执行之前或是之后加上或加入特定的输入section到输出section

符号赋值

简单赋值命令范例

可以在脚本中使用类C语言语法对符号进行赋值,例:

 symbol = expression ;
 symbol += expression ;
 symbol -= expression ;
 symbol *= expression ;
 symbol /= expression ;
 symbol <<= expression ; symbol >>= expression ;
 symbol &= expression ;
 symbol |= expression ;

一段简单的范例:

floating_point = 0;
SECTIONS
{
  .text :
    {
      *(.text)
      _etext = .;
    }
  _bdata = (. + 3) & ~ 3;
  .data : { *(.data) }
}

第一行:设定floating_point变量的链接地址为0

第七行:设置_etext的地址为.text之后

第九行:设置_bdata地址为_etext之后4字节alignment的位置。实则是对.data段的起始位置进行字节对齐

HIDDEN

对输出ELF,把其中某个符号定义为目标文件内可见。对上文范例的symbol改写后:

HIDDEN(floating_point = 0);
SECTIONS
{
  .text :
    {
      *(.text)
      HIDDEN(_etext = .);
    }
  HIDDEN(_bdata = (. + 3) & ~ 3);
  .data : { *(.data) }
}

floating_point/_etext/_bdata 为ELF内可见,但是无法导出。即假设目标文件是库文件,其他文件也无法引用使用。

PROVIDE

链接器直接定义一个symbol,如果它没有在任何输入目标文件中定义,则链接时使用PROVIDE定义的符号。如果目标文件中有定义,则使用目标文件中的定义。例如:

SECTIONS
{
  .text :
    {
      *(.text)
      _etext = .;
      PROVIDE(etext = .);
    }
}
  • __etext_不能在程序中定义,否则链接时有重复定义
  • _etext_则可定义在程序中定义,这样链接器会使用程序中的定义。

PROVIDE_HIDDEN

类似PROVIDE,差异只在于符号是HIDDEN

源代码引用

C语言内可以引用链接器脚本中定义的变量。例如:

start_of_ROM   = .ROM;
end_of_ROM     = .ROM + sizeof (.ROM);
start_of_FLASH = .FLASH;

则C语言中可以如下引用:

extern char start_of_ROM, end_of_ROM, start_of_FLASH;
memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);

SECTION

SECTION命令告诉链接器如何映射输入的段到输出段,且摆到内存的哪个位置。基本语法如下:

SECTIONS
{
  sections-command
  sections-command
  …
}

SECTION命令可能是以下命令的一种

  • ENTRY命令
  • 符号赋值
  • 输出段描述
  • 覆盖描述

待补充。。。

参考文献

  1. <GNU Link Scripts>
  2. <Linker Script初探 - GNU Linker Ld手冊略讀>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GNU 链接脚本LDS介绍 的相关文章

  • Unix 命令列出包含字符串但*不*包含另一个字符串的文件

    如何递归查看包含一个字符串且不包含另一个字符串的文件列表 另外 我的意思是评估文件的文本 而不是文件名 结论 根据评论 我最终使用了 find name html exec grep lR base maps xargs grep L ba
  • 修改linux下的路径

    虽然我认为我已经接近 Linux 专业人士 但显然我仍然是一个初学者 当我登录服务器时 我需要使用最新版本的R 统计软件 R 安装在 2 个地方 当我运行以下命令时 which R I get usr bin R 进而 R version
  • bluetoothctl 到 hcitool 等效命令

    在 Linux 中 我曾经使用 hidd connect mmac 来连接 BT 设备 但自 Bluez5 以来 这种情况已经消失了 我可以使用 bluetoothctl 手动建立连接 但我需要从我的应用程序使用这些命令 并且使用 blue
  • 为什么我收到“无法进行二进制日志记录”的信息。在我的 MySQL 服务器上?

    当我今天启动 MySQL 服务器并尝试使用以下命令进行一些更改时用于 MySQL 的 Toad http www quest com toad for mysql 我收到此消息 MySQL 数据库错误 无法进行二进制日志记录 消息 交易级别
  • 在 Linux 上更快地分叉大型进程?

    在现代 Linux 上达到与 Linux 相同效果的最快 最好的方法是什么 fork execve combo 从一个大的过程 我的问题是进程分叉大约 500MByte 大 并且一个简单的基准测试只能从进程中实现约 50 个分叉 秒 比较最
  • 无法加载 JavaHL 库。- linux/eclipse

    在尝试安装 Subversion 插件时 当 Eclipse 启动时出现此错误 Failed to load JavaHL Library These are the errors that were encountered no libs
  • Linux 中的无缓冲 I/O

    我正在写入大量的数据 这些数据数周内都不会再次读取 由于我的程序运行 机器上的可用内存量 显示为 空闲 或 顶部 很快下降 我的内存量应用程序使用量不会增加 其他进程使用的内存量也不会增加 这让我相信内存正在被文件系统缓存消耗 因为我不打算
  • 抑制 makefile 中命令调用的回显?

    我为一个作业编写了一个程序 该程序应该将其输出打印到标准输出 分配规范需要创建一个 Makefile 当调用它时make run gt outputFile应该运行该程序并将输出写入一个文件 该文件的 SHA1 指纹与规范中给出的指纹相同
  • 如何检测并找出程序是否陷入死锁?

    这是一道面试题 如何检测并确定程序是否陷入死锁 是否有一些工具可用于在 Linux Unix 系统上执行此操作 我的想法 如果程序没有任何进展并且其状态为运行 则为死锁 但是 其他原因也可能导致此问题 开源工具有valgrind halgr
  • 如何根据 HTTP 请求使用 Python 和 Flask 执行 shell 命令并流输出?

    下列的这个帖子 https stackoverflow com questions 15092961 how to continuously display python output in a webpage 我能够tail f网页的日志
  • 为什么 Linux perf 使用事件 l1d.replacement 来处理 x86 上的“L1 dcache misses”?

    在英特尔 x86 上 Linux用途 https stackoverflow com a 52172985 149138事件l1d replacements来实施其L1 dcache load misses event 该事件定义如下 计数
  • 如何查明CONFIG_FANOTIFY_ACCESS_PERMISSIONS是否启用?

    我想利用fanotify 7 http man7 org linux man pages man7 fanotify 7 html我遇到的问题是在某些内核上CONFIG FANOTIFY ACCESS PERMISSIONS不起作用 虽然C
  • 为什么内核需要虚拟寻址?

    在Linux中 每个进程都有其虚拟地址空间 例如 32位系统为4GB 其中3GB为进程保留 1GB为内核保留 这种虚拟寻址机制有助于隔离每个进程的地址空间 对于流程来说这是可以理解的 因为有很多流程 但既然我们只有 1 个内核 那么为什么我
  • 加载数据infile,Windows和Linux的区别

    我有一个需要导入到 MySQL 表的文件 这是我的命令 LOAD DATA LOCAL INFILE C test csv INTO TABLE logs fields terminated by LINES terminated BY n
  • 如何有效截断文件头?

    大家都知道truncate file size 函数 通过截断文件尾部将文件大小更改为给定大小 但是如何做同样的事情 只截断文件的尾部和头部呢 通常 您必须重写整个文件 最简单的方法是跳过前几个字节 将其他所有内容复制到临时文件中 并在完成
  • 如何使用GDB修改内存内容?

    我知道我们可以使用几个命令来访问和读取内存 例如 print p x 但是如何更改任何特定位置的内存内容 在 GDB 中调试时 最简单的是设置程序变量 参见GDB 分配 http sourceware org gdb current onl
  • 设备树不匹配:.probe 从未被调用

    我无法理解设备树的工作原理 或者具体来说为什么该驱动程序无法初始化 这是在 android 版本 3 10 的 rockchip 供应商内核中 驱动程序 看门狗 rk29 wdt c 为了可读性而减少 static const struct
  • 英特尔的最后分支记录功能是英特尔处理器独有的吗?

    最后分支记录是指存储与最近执行的分支相关的源地址和目标地址的寄存器对 MSR 的集合 它们受英特尔酷睿 2 英特尔至强和英特尔凌动处理器系列的支持 http css csail mit edu 6 858 2012 readings ia3
  • jpegtran 优化而不更改文件名

    我需要优化一些图像 但不更改它们的名称 jpegtran copy none optimize image jpg gt image jpg 但是 这似乎创建了 0 的文件大小 当我对不同的文件名执行此操作时 大小仍然完全相同 怎么样 jp
  • GCC 的“-Wl,option”和“-Xlinker option”语法之间有区别吗?

    我一直在查看一些配置文件 并且看到它们都被使用 尽管在不同的体系结构上 如果您在 Linux 机器上使用 GCC 将选项传递给链接器的两种语法之间有区别吗 据我所知 阅读 GCC 手册时 他们的解释几乎相同 From man gcc Xli

随机推荐

  • 输入3个整数,按从小到大的顺序输出

    include
  • mysql每秒最多能插入多少条数据 ? 死磕性能压测

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 前段时间搞优化 最后瓶颈发现都在数据库单点上 问DBA 给我的写入答案是在1W 机械硬盘 左右 联想起前几天infoQ上一篇文章说他们最好的硬件写入速度在2W后也无法提高
  • MobileNet V3 网络结构的原理与 Tensorflow2.0 实现

    文章目录 介绍 MobileNet V3 的创新 1 SE模块的加入 2 修改尾部结构 3 修改通道数量 4 改变激活函数 SE 模块中 BottleNeck 模块中 MobileNet V3 网络结构 1 MobileNet V3 Lar
  • transformer包中的bert预训练模型的调用详解

    文章目录 Bert调用 安装transformers BertTokenizer BertModel Bert调用 2018年的10月11日 Google发布的论文 Pre training of Deep Bidirectional Tr
  • JS获取URL中的Query参数

    需求描述 获取 URL 中的 Query 参数 例如 https www example com test html a param1 b param2 代码片段 实现一 使用URLSearchParams对象 兼容性见Can I use
  • ov5640 PLL时钟、图像大小、帧率寄存器配置

    不同像素和时钟的ov5640摄像头寄存器配置方法 配置摄像头寄存器 网上没有明说的 参考正点原子的文档进行介绍 像素大小配置 0x3801 0x3807 设置ISP大小 0 0 2631 1951 0x3808 0x380B 设置输出图像大
  • 经验分享-前端与后端的接口、HTML分离

    在WEB项目中 前后端不分离多人开放效率还不及一个人开发效率来的高 今天分享一个概念 后端写一个同一个的接口 此接口用于前端根据自己放在服务器上的文件地址生成 但访问处此地址的时候加载相关的一个总的JS用于加载其他的JS CSS HTML文
  • 计算机中丢失msvcp140.dll无法启动此程序怎么办(修复教程)

    DLL是Dynamic Link Library的缩写 意为动态链接库 dll文件是电脑系统及软件运行的重要文件 电脑如果丢失dll文件 那么很多软件跟游戏都是无法运行的 msvcp140 dll丢失这个问题就有很多小伙伴遇到 小编今天就分
  • JS 实现body背景颜色切换

    使用JS点击按钮 实现背景颜色的切换 效果如下 代码实现
  • 使用 React 18 流式传输 SSR 的指南

    React 18 引入了许多令人兴奋的变化和特性 这可能是您已经听说过很多的事情 并且有充分的理由 尽管稍微不那么引人注目 但在 React SSR 架构中也有一些非常令人兴奋的发展 要了解 React 18 带来的突破 必须查看整个时间线
  • 参加2013年大连软件开发者大会

    昨天参加了东软和QClub合办的2013大连软件开发者大会 在此总结一下 这次活动是东软的攻城狮俱乐部主办的 本来是想要向大连的广大程序员朋友征集讲师 但是大家的响应不是特别积极 所以就改成了找讲师的形式 我报了个名 然后通过InfoQ找到
  • 任务6 学生宿舍信息管理系统

    系列文章 任务6 学生宿舍信息管理系统 已知宿舍的信息包括 宿舍楼号 宿舍号 床位号 对应床位号的学生学号 楼长姓名等 设计程序能实现以下功能 1 宿舍信息录入 可随时增加宿舍信息到数据文件中 2 宿舍信息浏览 宿舍的信息输出到屏幕 3 查
  • 【NoSQL数据库技术与应用】【课本代码】【课后题答案】【暂不更新了】

    文章目录 一 课本代码 第1章 初识NoSQL 第2章 文档存储数据库MongoDB 第3章 MongoDB数据库操作 3 8 使用Java操作MongoDB 1 搭建JAVA环境 1 Java配置 2 Maven配置 3 基于Java A
  • nginx进程监控(Keepalived)

    bin bash nginx 家目录 NG HOME xxx xxx nginx Keepalived家目录 HA HOME xxx xxx keepalived RE pid of nginx wc l if RE eq 0 then N
  • 语义熵:QoE-Aware Resource Allocation for Semantic Communication Networks

    目录 论文简介 语义熵的意义 语义熵的定义 论文简介 作者 Lei Yan Zhijin Qin Rui Zhang Yongzhao Li Geoffrey Ye Li 发表期刊or会议 GLOBECOM 发表时间 2022 12 语义熵
  • Python Selenium搭建UI自动化测试框架

    自动化测试是软件测试中非常重要的一部分 可以提高测试效率和测试覆盖率 在UI自动化测试中 Selenium是非常流行的工具 本文将介绍如何使用Python和Selenium搭建UI自动化测试框架 一 环境准备 在开始搭建UI自动化测试框架之
  • phpmyadmin版本对应的各php版本

    phpmyadmin支持的php版本如下 phpmyadmin4 0 PHP5 2 PHP5 3 PHP5 4 PHP5 5 PHP5 6 PHP7 0 PHP7 1 PHP7 2 PHP7 3 PHP7 4 phpmyadmin4 4 P
  • linux 之 内核实时性

    1 linux 实时性方案 单内核方案 主线软实时内核 打PREEMPT RT补丁 使内核成为硬实时内核 双内核方案 主线普通内核 实时内核 例如 RT linux RTAI Xenomai 2 实时性和抢占性 实时性 实时分为硬实时和软实
  • 项目实现读写分离操作(mysql)

    读写分离 1 问题说明 2 读写分离 Master 主库 数据同步 gt Slave 从库 Mysql主从复制 mysql主从复制 介绍 mysql主从复制是一个异步的复制过程 底层是基于mysql数据库自带的二进制日志功能 就是一台或多台
  • GNU 链接脚本LDS介绍

    前言 程序的从C语言代码变成可以在目标机器上执行额文件 可以分为如下步骤 编译 预编译 将宏定义等转义编译 将C语言变成目标文件 o档案 编译 汇编 将预编译过后的目标变为目标文件 链接 合并多个目标文件 o a 等为最终的可执行文件 LD