【学习日记】函数调用 和 全局变量 如何实现 位置无关码

news2024/11/13 15:30:32

问题来源

  • 在 I.MX6ull 的启动流程中,u-boot会将自身从内存一开始的位置拷贝到其他位置,以便给linux留出内存空间,防止 u-boot被覆盖
  • 如果代码中包含直接引用其链接时地址的指令,那么当代码被移动到新的地址时,这些引用将不再正确。这包括通过绝对地址直接调用函数或访问全局变量

什么是位置无关码

位置无关码(Position Independent Code,简称 PIC)是一种特殊类型的编码方式,用于生成在内存中可以任意位置运行的代码。这种代码在编译时不会固定任何特定的内存地址,使得程序或库在运行时可以被加载到内存的任意位置而无需修改。这对于操作系统中的动态链接库(DLLs)和共享库(如 Linux 中的 .so 文件)尤为重要。

实现方式

在编写位置无关的代码时,通常采取以下技术:

  • 相对寻址:使用基于程序计数器(PC)的相对地址而非绝对地址。这允许代码引用距当前执行点的相对位置,而不是固定的内存地址。
  • 全局偏移表(GOT):用于存储全局变量和函数的地址。程序通过 GOT 来间接访问这些全局资源,GOT 在程序启动时由动态链接器填充。
  • 过程链接表(PLT):在调用动态链接库中的函数时使用。每次函数调用都通过 PLT 间接进行,这使得函数地址可以在运行时解析。

bl指令是如何实现位置无关码的(函数相对地址跳转)

BL (Branch with Link) 指令在 ARM 架构中是用来实现函数调用的,它通过链接寄存器 (LR) 保存返回地址,并执行一个分支到目标函数的地址。这个指令通常使用相对地址进行跳转,这意味着它计算目标地址作为当前程序计数器(PC)加上一个偏移量(PC+offset)。这种方式本身就为位置无关代码(PIC)提供了一定的支持,但是如果需要将代码移动到内存中的不同位置,是否还能工作取决于代码是如何编写和链接的。

相对跳转

BL 指令使用相对地址进行跳转,这是实现代码位置无关的关键。相对跳转意味着跳转的目标地址是基于当前执行指令的位置计算的。这种方式保证了,只要代码整体移动,相对偏移保持不变,跳转仍然有效。

示例

假设有如下的指令序列:

00008000:   MOV R0, #0
00008004:   BL  0000800C  ; 假设跳转到 0000800C
00008008:   MOV R1, #1
0000800C:   ADD R0, R0, R1

如果整个代码块移动到地址 00009000,跳转指令会相应地调整偏移量,但跳转的行为不变(可以看到BL 跳转的地址随着代码整体被拷贝而随之改变了):

00009000:   MOV R0, #0
00009004:   BL  0000900C  ; 偏移量仍然正确地指向下一条指令
00009008:   MOV R1, #1
0000900C:   ADD R0, R0, R1

限制

BL 指令足够应对内部函数调用的相对跳转时,它不适用于以下情况:

  • 外部函数调用:如果需要调用其他模块或库中的函数,单纯的 BL 不能解决问题,因为其它模块或库可能不与主程序一起移动。
  • 绝对地址引用:对于全局变量或者需要通过绝对地址访问的资源,BL 无法提供解决方案。

解决方案

对于上述限制,通常需要额外的机制,如:

  • 动态链接:使用动态链接器来解决库函数的位置问题,通常涉及到 GOT(全局偏移表)和 PLT(程序链接表)。
  • 重定位:生成重定位表,使得在程序加载到新位置时能够调整内存中的绝对地址引用。

结论

BL 指令通过使用相对地址的方式,为实现位置无关代码提供了基础支持。它适用于代码内部的函数调用,但对于跨模块或需要绝对地址的引用,需要其他机制和支持。在设计嵌入式系统或操作系统引导程序如 u-boot 时,这些考虑是关键的,以确保系统的灵活性和可移植性。

如何实现全局变量的位置无关码

  • 有如下的C代码
static int rel_a = 0;

void rel_test(void) {
    rel_a = 100;
    printf("rel_test\r\n");
}
  • 编译后进行反汇编,左边为地址
 1  8785dcf8 <rel_a>:
 2  8785dcf8: 00000000 andeq r0, r0, r0
 3  
 4  878042b4 <rel_test>:
 5  878042b4: e59f300c ldr r3, [pc, #12] ; 878042c8 <rel_test+0x14>
 6  878042b8: e3a02064 mov r2, #100 ; 0x64
 7  878042bc: e59f0008 ldr r0, [pc, #8] ; 878042cc <rel_test+0x18>
 8  878042c0: e5832000 str r2, [r3]
 9  878042c4: ea000d64 b 87839bfc <printf>
10  878042c8: 8785dcf8 .word 0x8785dcf8
11  878042cc: 87842aaf strhi r2, [r4, pc, lsr #21]
  • 程序计数器(PC)的值为当前地址+8(ARM的三级流水线)
  • 看第5行,这里设置 r3 为 878042b4 + 8 + 12 = 878042c8 此处(878042b4+8)就是PC的值
  • 878042c8 在第 12 行,指向全局变量的位置为 8785dcf8
  • 878042c8 也被称为 Label
  • 这样就通过 PC 的相对地址指向了全局变量地址,代码在内存中的位置移动后如果全局变量的地址移动了,那么只要对全局变量加上一个整体的偏移地址,那么对全局变量的引用也不会出错

在编译的时候添加 -pie 选项

在编译程序时,使用 -pie 选项(Position Independent Executable,位置无关可执行文件)是一种常见的方法,用于生成可以在内存中任意位置执行的可执行文件。这是创建与地址空间布局随机化(ASLR, Address Space Layout Randomization)兼容的二进制文件的关键步骤,增强了程序的安全性。

使用 -pie 选项的效果

当你为编译器(如 GCC 或 Clang)添加 -pie 选项时,编译器会生成位置无关的代码。这意味着生成的可执行文件不依赖于固定的内存地址,从而可以在加载时由操作系统重新定位到内存的任何位置。这与生成动态库时使用的 -fPIC(Position Independent Code)选项类似。

比较 -fPIC-pie

  • -fPIC:生成位置无关的代码,通常用于编译动态链接库(如 .so 文件)。它确保代码可以在任何地址被加载和执行。
  • -pie:使整个可执行文件位置无关。当系统启用 ASLR 时,这允许操作系统在每次程序启动时将可执行文件加载到随机的内存地址。

注意事项

  • 当使用 -pie 时,确保所有的库也支持位置无关代码,否则可能会遇到链接错误。
  • 在一些系统上,使用 -pie 可能会导致轻微的性能开销,因为地址计算现在需要额外的间接寻址步骤。

添加位置偏移的代码

8785dcec:	87800020 	strhi	r0, [r0, r0, lsr #32]
8785dcf0:	00000017 	andeq	r0, r0, r7, lsl r0
……
8785e2fc:	878042c8	strhi	r4, [r0, r8, asr #5]
8785e300:	00000017 	andeq	r0, r0, r7, lsl r0
  • 878042c8+offset = 读取新的Label处的数据+offset
  • 这里的 00000017 用于标识其上的数据为一个Label,需要加上偏移地址
    在这里插入图片描述
    这段代码涉及到动态链接过程中的一个关键步骤:地址重定位。代码片段主要执行地址重定位,以确保程序中的引用指向正确的内存地址。这是位置无关代码(PIC)在实际运行时进行调整以适应其加载位置的一个实例。

代码解析

以下是代码的详细分析:

ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */
ldr r3, =__rel_dyn_end   /* r3 <- SRC &__rel_dyn_end */
  • 这两行加载动态重定位表的开始和结束地址到寄存器 r2r3__rel_dyn_start__rel_dyn_end 是由链接器定义的符号,分别指向重定位表的开始和结束。
fixloop:
    ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */
  • ldmia r2!, {r0-r1} 是一个“加载多个寄存器”指令,它从 r2 指向的地址加载数据到寄存器 r0r1,同时 r2 自增以指向下一个位置。这里,r0 获得需要修正的内存位置,r1 获得修正方式(或偏移)。
    and r1, r1, #0xff
    cmp r1, #23 /* relative fixup? */
    bne fixnext
  • 这段代码检查 r1 中的修正类型是否是相对修正(类型代码为23)。如果不是,跳转到 fixnext 继续处理下一个条目。
    /* relative fix: increase location by offset */
    add r0, r0, r4
    ldr r1, [r0]
    add r1, r1, r4
    str r1, [r0]
  • 如果是相对地址修正,使用寄存器 r4 中的偏移量来调整 r0 指向的位置。这里 r4 应该包含了加载地址和基地址之间的偏差。读取 r0 指向的当前值,增加偏差,然后将新值写回。
fixnext:
    cmp r2, r3
    blo fixloop
  • 检查是否已处理完所有重定位条目。如果 r2(指向当前条目的指针)仍然低于 r3(结束地址),则继续循环。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1953155.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

聊聊RNNLSTM

RNN 用于解决输入数据为&#xff0c;序列到序列(时间序列)数据&#xff0c;不能在传统的前馈神经网络(FNN)很好应用的问题。时间序列数据是指在不同时间点上收集到的数据&#xff0c;这类数据反映了某一事物、现象等随时间的变化状态或程度&#xff0c;即输入内容的上下文关联…

工业现场实测,焦化厂导烟车与装煤车风机实现无人作业

一、项目背景 作为我国重要的能源行业之一&#xff0c;焦化行业在国民经济中扮演着重要角色&#xff0c;焦化工艺是高温、高压、有毒物质等因素共同作用下进行的&#xff0c;因此存在着安全隐患&#xff0c;并伴有环境污染&#xff0c;改善焦化工艺的安全和环保问题是当前亟待…

优选算法之前缀和(下)

目录 一、和为 k 的子数组 1.题目链接&#xff1a;560. 和为 K 的子数组 2.题目描述&#xff1a; 3.解法&#xff08;前缀和 哈希表&#xff09; &#x1f33b;算法思路&#xff1a; &#x1f33b;算法代码&#xff1a; 二、和可被 k 整除的子数组 1.题目链接&#xff…

MySQL中多表查询之外连接

首先先来介绍一下我做的两个表&#xff0c;然后再用他们两个举例说明。 -- 创建教师表 create table teachers( id_t int primary key auto_increment, -- 老师编号 name_t varchar(5) -- 姓名 ); -- 创建学生表 create table students( id_s int primary key auto_increment,…

Android APK混淆处理方案分析

这里写目录标题 一、前言1.1 相关工具二、Apk 分析2.1 apk 解压文件2.2 apk 签名信息2.3 apk AndroidManifest.xml2.4 apk code三、Apk 处理3.1 添加垃圾文件3.2 AndroidManifest.xml 处理3.3 dex 混淆处理3.4 zipalign对齐3.5 apk 重新签名3.6 apk 安装测试四、总结一、前言 提…

使用Astro+Vercel+Cloudflare一天时间开发部署上线一个知识博客网站,简直简简单单

大家好&#xff0c;这里是程序猿代码之路。在当今数字化时代&#xff0c;拥有一个个人博客网站对于分享知识、展示个人品牌变得越来越重要。然而&#xff0c;许多非技术背景的用户对于搭建和维护一个网站可能会感到望而却步。幸运的是&#xff0c;随着低代码和无代码平台的兴起…

Spring高手之路21——深入剖析Spring AOP代理对象的创建

文章目录 创建代理对象核心动作的三个步骤1. 判断 Bean 是否需要增强&#xff08;源码分析时序图说明&#xff09;2. 匹配增强器 Advisors&#xff08;源码分析时序图说明&#xff09;3. 创建代理对象&#xff08;源码分析时序图说明&#xff09; 创建代理对象核心动作的三个步…

C++模版基础知识与STL基本介绍

目录 一. 泛型编程 二. 函数模板 1. 概念 2. 函数模版格式 3. 函数模版的原理 4. 模版函数的实例化 (1). 隐式实例化 (2.) 显式实例化 5. 模版参数的匹配原则 三. 类模板 1. 类模板的定义格式 2. 类模板的实例化 四. STL的介绍 1. 什么是STL&#xff1f; 2. STL的版…

netty入门-7 ByteBuf

文章目录 前言ByteBuf结构池化与非池化创建&#xff08;直接内存/堆内存&#xff09;写入和读取释放零拷贝&#xff0c;slice&#xff0c;duplicate&#xff0c; copy&#xff0c;Composite 结语 前言 ByteBuf这部分视频讲的更为详细。 主要参考视频。 ByteBuf 结构 首先这…

C# Task.WaitAll 的用法

目录 简介 1.WaitAll(Task[], Int32, CancellationToken) 2.WaitAll(Task[]) 3.WaitAll(Task[], Int32) 4.WaitAll(Task[], CancellationToken) 5.WaitAll(Task[], TimeSpan) 结束 简介 Task.WaitAll 是 C# 中用于并行编程的一个的方法&#xff0c;它属于 System.Threa…

开始尝试从0写一个项目--后端(三)

器材管理 和员工管理基本一致&#xff0c;就不赘述&#xff0c;展示代码为主 新增器材 表设计&#xff1a; 字段名 数据类型 说明 备注 id bigint 主键 自增 name varchar(32) 器材名字 img varchar(255) 图片 number BIGINT 器材数量 comment VARC…

Elasticsearch 使用误区之三——分片设置不合理

Elasticsearch 是一个强大的搜索和分析引擎&#xff0c;它通过将数据分散到多个节点的分片中来进行分布式处理。 本文将探讨分片大小和策略的概念&#xff0c;以优化 Elasticsearch 的性能并防止过度分片或分片过大等问题。 先看个分片设置不合理的真实企业案例&#xff1a; 10…

陶晶驰串口屏使用记录与教程

首先把串口屏想象成和正点原子usmart调试程序一样的程序&#xff0c;串口屏主芯片有些是GD32 STM32都是主流单片机&#xff0c;里面下载了一些固件形成了现在的操作系统 其实我更喜欢把他们&#xff08;usmart&#xff0c;串口屏主程序&#xff0c;micropython&#xff0c;at指…

uniapp vue3 使用画布分享或者收藏功能

使用HBuilder X 开发小程序&#xff0c;大多数的画布插件很多都是vue2的写法&#xff0c;vue3的很少 我自己也试了很多个插件&#xff0c;但是有一些还是有问题&#xff0c;不好用 海报画板 - DCloud 插件市场 先将插件导入项目中 自己项目亲自用过&#xff0c;功能基本是完善…

GraphRAG:基于实体的本地搜索方法:知识图谱与非结构化数据的融合

GraphRAG&#xff1a;基于实体的本地搜索方法:知识图谱与非结构化数据的融合 在自然语言处理和信息检索领域,如何有效地结合结构化知识和非结构化文本数据一直是一个重要的研究方向。本文介绍一种基于实体的本地搜索方法,该方法巧妙地融合了知识图谱中的结构化数据和输入文档中…

优化冗余代码:提升前端项目开发效率的实用方法

目录 前言代码复用与组件化模块化开发与代码分割工具辅助与自动化结束语 前言 在前端开发中&#xff0c;我们常常会遇到代码冗余的问题&#xff0c;这不仅增加了代码量&#xff0c;还影响了项目的可维护性和开发效率。还有就是有时候会接到紧急业务需求&#xff0c;要求立马完…

打造一篇完美的【数学建模竞赛论文】:从准备到撰写的全面指南

目录 一、赛前准备 1.1 报名与纪律要求 1.2 MD5码上传 1.3 竞赛准备 1.4 时间分配 二、论文格式规范 2.1 摘要 2.2 参考文献 2.3 排版要求 三、建模过程与方法 3.1 问题分析与模型假设 3.2 模型构建与求解 3.3 结果分析与检验 四、论文撰写技巧 4.1 论文结构 4…

Redisson中分布式锁继承体系

直接上图 画了好久 关于非公平锁和公平锁中差异化函数如tryLockInnerAsyc 和unsubscribe还没有时间进行探索&#xff0c;这应该是公平锁和非公平锁之间的差异所在。 说一说Redisson中的类之间关系设计 参考抽象类实现接口_一个抽象之类 如果要实现某个接口怎么办-CSDN博客 众…

电脑文件误删除如何恢复?数据恢复第一步是什么?这五点要第一时间处理!

电脑文件误删除如何恢复&#xff1f;数据删除恢复的第一时间要做什么&#xff0c;你知道吗&#xff1f; 在使用电脑的过程中&#xff0c;误删除重要文件的情况时有发生。面对这种情况&#xff0c;不必过于慌张&#xff0c;因为有多种方法可以帮助你恢复误删除的文件。以下是恢复…

金字塔监督在人脸反欺骗中的应用

介绍 论文地址&#xff1a;https://arxiv.org/pdf/2011.12032.pdf 近年来&#xff0c;人脸识别技术越来越普及。在智能手机解锁和进出机场时&#xff0c;理所当然地会用到它。人脸识别也有望被用于管理今年奥运会的相关人员。但与此同时&#xff0c;人们对人脸欺骗的关注度也…