MIPS架构下LW指令的重定位过程
通常我们不会去关心指令重定位(relocation)的细节,编译器的ld过程已经帮助我们做好了。由于最近在移植CRIU,涉及到指令的重定位计算,不得不细细研究代码重定位的细节知识。之前的文章介绍了MIPS架构下函数跳转指令bal和jal和重定位过程,感兴趣的同学可以跳转到https://blog.csdn.net/weixin_38669561/article/details/100536803查看,本章通过分析lw 指令来分析从内存加载数据到寄存器的重定位的过程。
本文有点烧脑,看完注意休息 “_”
一、准备工作和基础知识
可以跳过
首先看下面的示例汇编语句:
//test.S
ENTRY(__export_parasite_head_start).set noreorderlw a0, __valuejr ra__value:.long 0
END(__export_parasite_head_start)
这里lw a0,__value 就是我们要分析的汇编指令。我们用gcc编译
$ gcc -save-temps -g -c -fno-builtin -mno-abicalls test.S
其中 -save-temps 参数意味着保留中间临时文件,所以这条命令执行完成会发现当前目录多了两个文件,test.s和test.o。其中test.s就是GCC编译生成的中间编译文件,而test.o是汇编文件。编译文件和汇编文件有什么区别呢?首先要了解GCC编译是有4个过程,预编译(生成.i文件)-> 编译(生成.s文件)->汇编(生成.o文件)->链接(生成可执行文件,默认为a.out)。这里的test.s就是编译阶段产生的文件,test.o就是汇编阶段产生的文件,编译和汇编的区别可以通过查看文件内容得知:
//test.s.section .head.text, "ax"
.globl __export_parasite_head_start; .align 4; .type __export_parasite_head_start, @function; __export_parasite_head_start:.set noreorderlw $4, __export_parasite_cmdjr $31
__export_parasite_cmd:.long 2
.size __export_parasite_head_start, . - __export_parasite_head_start
可以看出test.s和test.S几乎没有差别,都是汇编指令。
test.o已经是ELF格式的文件了,所以不能直接打开,需要使用objdump命令
$ objdump -D test.o > test.o.dump
然后打开文件test.o.dump
test.o: 文件格式 elf64-tradlittlemips...
Disassembly of section .head.text:
0000000000000000 <__export_parasite_head_start>:0: 3c040000 lui a0,0x04: 3c010000 lui at,0x08: 64840000 daddiu a0,a0,0c: 0004203c dsll32 a0,a0,0x010: 0081202d daddu a0,a0,at14: 8c840000 lw a0,0(a0)18: 03e00008 jr ra000000000000001c <__export_parasite_cmd>:1c: 00000002 srl zero,zero,0x0...
汇编过程就是将汇编代码(test.s)转变成机器可以执行的指令(test.o),有些汇编语句和机器指令一一对应,不需要扩展,比如上面的 "jr ra"
指令。有些汇编指令可能扩展成多条机器指令,比如test.S里里面的 "lw a0, __value"
扩展成了6条指令
0: 3c040000 lui a0,0x04: 3c010000 lui at,0x08: 64840000 daddiu a0,a0,0c: 0004203c dsll32 a0,a0,0x010: 0081202d daddu a0,a0,at14: 8c840000 lw a0,0(a0)
同时我们有知道,此时的test.o还是没有做重定位的指令集,从起始地址"0000000000000000"
就可以看出,或者使用file命令
$ file test.o
test.o: ELF 64-bit LSB relocatable, MIPS, MIPS64 rel2 version 1 (SYSV), not stripped
这里 "relocatable"
就意味着这是需要重定位的文件,或者说需要做指令修正的文件。
那么重定位什么时候做呢?链接阶段。上面我在使用gcc工具时有一个参数 "-c"
。这个值意思是制作编译、汇编,不进行链接。链接过程主要包括了地址和空间分配、符号决议和重定位。
下面我们使用ld工具来进行test.o的链接过程。为了便于分析LW指令的重定位的过程,ld使用自定义的链接脚本,内容如下:
// ld.lds
OUTPUT_ARCH(mips)
ENTRY(__export_parasite_head_start) /*指定了程序入口函数*/
SECTIONS
{. = 0x120000000; /*0xfff70c4000; 指定当前虚拟地址*/tinytext : { *(.head.text)*(.text*)*(.data)*(.rodata)}/DISCARD/ : { /*释义:需要丢弃的输入段*/*(.comment)*(.pdr)}/* Parasite args should have 4 bytes align, as we have futex inside. */
. = ALIGN(4);
__export_parasite_args = .;
}
在这个文件中,你只要关注两点, 一、"ENTRY(__export_parasite_head_start)"
指定了程序运行的入口地址,没有它,接下来的ld命令会失败 。二、". = 0x120000000"
指定了当前虚拟起始地址。接下来执行l命令。
$ ld -static -T ld.lds -o test test.o
这时查看生成的test文件已经是可执行的,relocation已经完成。
$ file test
test: ELF 64-bit LSB executable, MIPS, MIPS64 rel2 version 1 (SYSV), statically linked, not stripped
二、relocation分析
敲黑板上面的内容都是准备工作,接下来开始分析重定位(relocation)的过程。
首先反汇编test文件
$ objdump -D test > test.dump
打开test.dump文件
test: 文件格式 elf64-tradlittlemips...Disassembly of section tinytext:000000fff70c4030 <__export_parasite_head_start>:fff70c4030: 3c040000 lui a0,0x0fff70c4034: 3c01f70c lui at,0xf70cfff70c4038: 64840100 daddiu a0,a0,256fff70c403c: 0004203c dsll32 a0,a0,0x0fff70c4040: 0081202d daddu a0,a0,atfff70c4044: 8c84404c lw a0,16460(a0)fff70c4048: 03e00008 jr ra000000fff70c404c <__export_parasite_cmd>:fff70c404c: 00000002 srl zero,zero,0x0
发现和上面的test.o文件反汇编文件的不同点了吗?我把区别标记下来并开始分析:
其中蓝色部分是做了重定向的结果。 R_MIPS_HIGHEST、R_MIPS_HI16 、R_MIPS_HIGHER、R_MIPS_LO16是重定位入口类型。每种类型的指令修正方式可以通过查看mipsabi文档可以找到
目前我从https://elinux.org网站上下载下来的mipsabi.pdf文档里对重定向入口类型介绍的也不全,最好的分析办法是看 binutils 的源码
还要明确一下,我当前运行的是在龙芯处理器上。mips寄存器为64位,指令32位,寻址48位(我还不确定)。lw指令的描述是 lw rt,offset(base) ,这里base可以寻址64位。按上面的"lw a0,16460(a0)"
为例,base值应该是0xfff70c0000(等于ld.lds里面设置的初始虚拟地址) 。16460是10进制数,对应的16进制数为0x404c。那么寻址后的a0值应该为0xfff70c0000+0x404c = 0xfff70c404c。base值应该是(也就是上面的a0)
接下来我开始分析上面的每一条指令来了解lw a0,__export_parasite_cmd是怎么加载上来的。
第一条指令 lui a0,0x0
lui 功能为上位加载立即数,描述为a0 = 0x0<<16位,操作后的a0值为:
a0 :0x0000 0000 0000 0000
第二条指令 lui at,0xf70c
这里使用了at寄存器做中间变量,描述为at = 0xf70c<<16,操作后的at值为:
at:0xffff ffff f70c 0000
这里是最让人费解的地方,f70c左移16位后应该是0x0000 0000 f70c 0000。而这里却是0xffff ffff f70c 0000 这是我通过gdb调试确认过的结果,可能是MIPS实现48位内存寻址的策略
第三条指令 daddiu a0,a0,256
daddiu 功能为64位立即数加法,描述为a0 = a0+256 ,256为10进制对应0x0100,操作后的a0值为:
a0:0x0000 0000 0000 0100
第四条指令 dsll32 a0,a0,0x0
dsll32 功能为32位左移,描述为a0 = a0<<(32+0x0),操作后的a0值为:
a0:0x0000 0000 0100 0000
第五条指令 daddu a0,a0,at
daddu功能同daddiu,为64位加法,但是操作数不是立即数而是寄存器。描述为a0 = a0+at,结果为:
a0:0x0000 00ff f70c 0000
此时你可能明白点为啥at值的高32位填充全f的原因没?
分析的最后一条 lw a0,16460(a0)
lw 功能为32位加载,64位CPU上进行符号扩展。 描述为a0 = memory(0xff f70c 0000+0x404c),a0结果为:
a0 = 2 //如果不信,你可以使用gdb调试
也就是lw执行后,a0可以加载到内存地址为fff70c404c上的数据。
到这里,我们已经通过重定位的指令分析了lw的基址计算过程。如果让你去实现relocation过程,你该怎么做?可能我们不被逼到死路是不会考虑这个问题。此刻,我就在死路上。
敲黑板:指令修正方式
上面提到了重定位入口类型R_MIPS_HIGHEST、R_MIPS_HI16 、R_MIPS_HIGHER、R_MIPS_LO16,但是分析过程一点没有用到,那是由于ld已经帮我们根据这几个类型实现了指令修正。重定位入口类型只有在test.o 到可执行文件test 的汇编过程才会用到。test.o是ELF格式的文件,ELF格式中会存储哪些段需要重定位以及指令修正的类型等信息。我们可以通过readelf命令查看
$ readelf -r test.o重定位节 '.rela.head.text' 位于偏移量 0x4b0 含有 4 个条目:Offset Info Type Sym. Value Sym. Name + Addend
000000000000 00040000001d R_MIPS_HIGHEST 0000000000000000 .head.text + 1cType2: R_MIPS_NONE Type3: R_MIPS_NONE
000000000004 000400000005 R_MIPS_HI16 0000000000000000 .head.text + 1cType2: R_MIPS_NONE Type3: R_MIPS_NONE
000000000008 00040000001c R_MIPS_HIGHER 0000000000000000 .head.text + 1cType2: R_MIPS_NONE Type3: R_MIPS_NONE
000000000014 000400000006 R_MIPS_LO16 0000000000000000 .head.text + 1cType2: R_MIPS_NONE Type3: R_MIPS_NONE
offset是指当前指令在elf文件中的位置,Type即为重定位入口类型,Addend会参与到指令修正的运算。
MIPS上的重定位类型对应的计算方式可以通过mipsabs.pdf查看到一些(但不是全部),如下图:
在此我就上面的几个类型结合代码讲解test.o中relocation的计算过程。
重定位类型 R_MIPS_HIGHEST
通过elf格式解析过程能够看到test.o中第一条指令 lui a0,0x0 ,指令码为 3c040000,重定位类型为R_MIPS_HIGHEST。怎么计算呢?mipsabi上还真没有,我参考了binutils的源码中mips.cc得出计算过程为:
((vbase(0xfff70c4000)+ 0x800080008000llu)>>48) & 0xffff
上述的计算结果(0x0)放在lui指令的低16位。修正后的lui指令码还是 3c040000
重定位类型 R_MIPS_HI16
test.o中第二条指令 lui at,0x0,指令码为 3c010000,重定位类型 R_MIPS_HI16,计算方式根据不同的符号类型有不同的计算方式,此处的符号类型为Local,计算过程为:
((vbase(0xfff70c4000)+ 0x8000)>>16) & 0xffff
上述的计算结果(0xf70c)放在lui指令的低16位,修正后的lui指令码为 3c01f70c
重定位类型 R_MIPS_HIGHER
第三条指令 daddiu a0,a0,256 ,指令码为 64840000 ,重定位类型为R_MIPS_HIGHER,计算方式也只能参考binutils的源码中mips.cc文件
((vbase(0xfff70c4000)+ 0x80008000)>>32) & 0xffff
上述计算结果(0x0100)放在daddiu指令的低16位,修正后的daddiu指令码为64840100
重定位类型 R_MIPS_LO16 ( fixme)
第六条指令 lw a0,0(a0) ,指令码为 8c840000 ,重定位类型为R_MIPS_LO16,计算方式根据不同的符号类型有不同的计算方式,此处的符号类型为Local,计算过程为:
(vbase(0xfff70c4000)& 0xffff)+A (0x4c)
上述计算结果(0x404c)放在lw指令的低16位,修正后的lw指令码为8c84404c
MIPS架构下LW指令的重定位过程相关推荐
- MIPS架构下的逆向初探
0x00 前言 本人是新接触mips下的逆向,本文参考了网上众多大佬的文章,权当是个人的学习总结,如有问题还请斧正. 本文主要是借助4道ctf里的题目来阐述. 参考文章: https://blog.c ...
- 【论文笔记】开放场景下的实时视觉重定位方法 HF-Net 2019
HF-Net: From Coarse to Fine: Robust Hierarchical Localization at Large Scale 作者:Paul-Edouard Sarlin1 ...
- NFS - MIPS架构下构建NFS共享目录服务
文章目录 概 Lin 和 Win 共享文件 需求 原理 环境信息 检查依赖 如何找mips的rpm包 NFS服务端 上传RPM安装包 安装RPCBIND和NFSSERVER 创建共享目录 配置文件/e ...
- [SystemVerilog] MIPS架构下的五级流水线CPU设计
完整代码已上传 github 众所周知,MIPS体系的五级流水线CPU分为五个阶段:取指(IF).译码(ID).执行(EX).存储器(MEM).写回(WB).所以这根本算不上"设计" ...
- MIPS架构——汇编代码转机器代码编译器 Matlab GUI
MIPS架构下的MCU,指令集包含R-Type.I-Type.J-Type三种,在数电课程设计时为了给MCU编写指令集,需要将汇编语言转化成机器代码,这里分享一下自己写的Matlab 的 GUI. 主 ...
- 24. PE结构-PE详解之基址重定位详解
问题一:什么是基址重定位? 答:重定位就是你本来这个程序理论上要占据这个地址,但是由于某种原因,这个地址现在不能让你霸占,你必须转移到别的地址,这就需要基址重定位.打个比方:例如你现在计划在某某地方建 ...
- S5PV210裸机之重定位
1.重定位相关概念 位置无关码(PIC,position independent code):汇编源文件被编译成二进制可执行文件时编码方式与位置(内存地址)无关. 位置有关码:汇编源文件被编译成二进 ...
- S5PV210体系结构与接口04:代码重定位 SDRAM初始化
目录 1. C语言环境初始化 1.1 C语言运行所需环境 1.2 初始化栈 1.2.1 栈的概念 1.2.2 栈的作用 1.2.3 如何初始化 1.3 初始化bss段 1.3.1 bss段的作用 1. ...
- 嵌入式学习(二)——刷机和led实验(看门狗、c语言、icache、重定位、SDRAM)
目录 一.刷机和裸机实验 1.1 刷机步骤 1.2 交叉编译链 1.2.1 环境变量配置 二.led实验 2.1 实验准备 2.2 实验开始 2.2.1 Makefile 2.2.2 mkv210_i ...
最新文章
- Hadoop基础-网络拓扑机架感知及其实现
- 假如 IDEA 也加入防沉迷功能...
- web前端技术分享:前端开发与后端开发的区别是什么?
- nginx1.9基于端口的四层负载均衡实践,基于端口的转的负载均衡
- Mysql索引是有序的吗_mysql组合索引的有序性转
- 小学有学计算机课程,如何进行小学计算机课程有效教学.doc
- http://blog.chinaunix.net/uid-20577907-id-3519578.html
- NHibernate从入门到精通系列(5)——持久对象的生命周期(下)
- jsx就是高级点的HTML拼接,JSX与HTML的那些不同
- 用批处理命令加WinRAR实现自动备份文件数据
- C#代码与javaScript函数的相互调用(转)
- 美国计算机科学教师协会,2020-2021 ACSL AMERICAN COMPUTER SCIENCE LEAGUE 美国计算机科学联赛...
- 5G无线关键技术 — 高频段信号传输技术
- 关于uIP移植以及部分特性解析和勘误
- 多线程爬取学习通题库
- 广州橙色优学:Java为什么这么火?Java好学吗?
- 简单说明CGI是什么
- 通达信经典实用选股公式
- 「Upwork高手攻略」月入1-4万,一个低门槛的大机会,入门指南
- Qt4.8.6移植到hi3559(四)