1 基础概念

MIPS(Microprocessor without Interlocked Piped Stages architecture),是一种采取精简指令集(RISC)的处理架构,由MIPS科技公司开发并授权,广泛应用在许多电子产品、网络设备、个人娱乐装置与商业装置上。最早的MPS架构是32位,最新的版本已经变成64位。

MIPS结构的基本特点是:包含大量的寄存器、指令数和字符、可视的管道延时间隙,这些特性使得MIPS架构能够提供最高的每平方毫米性能和当今SoC设计中最低的能耗。

我们平时听到很多CISC、RISC指令集,那么他们之间有什么关联呢?

  • CISC(Complex Instruction Set Computer)复杂指令计算机。Intel的Pentium、AMD的K5、K6为该类型。
  • RISC(Reduced Instruction Set Computer)精简指令集计算机。Acorn公司的ARM、IBM的PowerPC、MIPS公司的MIPS为该类型。

2 寄存器

2.1 通用寄存器

在MIPS体系结构中有32个通用寄存器(每个寄存器大小根据芯片型号不同而不同,在一般情况下,32位处理器中每个寄存器的大小是32位,即4字节),在汇编程序中可以用编号$0~$31表示,也可以用寄存器的名字表示,如$sp、$t1、$ra等。堆栈是从内存的高地址方向向低地址方向增长的。

编号 名称 说明 备注
$0 $zero 和0值作比较运算是常用该寄存器
$1 $at 汇编保留保留寄存器,不可做其他用途 Assembler Temporary
$2~$3 $v0~v1 存储函数的返回值。如果返回值为1字节,则只有$v0有效 Values
$4~$7 $a0~a3 作为函数调用时传递的前4个参数,不够时才使用堆栈传递 Arguments
$8~$15 $t0~t7 临时寄存器,无需保证函数调用的恢复 Temporaries
$16~$23 $s0~s7 函数调用后这些寄存器需要被恢复 Saved Values
$24~$25 $t8~t9 临时寄存器的拓展补充 Temporaries
$26~$27 $k0~k1 系统保留寄存器,请勿使用 Kernel reserved
$28 $gp 全局指针,静态数据访问时需要保证在gp指定的64kb范围 Global Pointer
$29 $sp 堆栈指针 Stack Pointer
$30 $s8/fp Saved value / Frame Pointer
$31 $ra 存放函数调用返回的地址 Return Address

2.2 特殊寄存器

MIPS32架构中定义了3个特殊寄存器,分别是PC、HI、和LO。这几个用的比较少,暂时先不介绍啦。

3 字节序

数据在存储器中是按照字节存放的,处理器也是按照字节访问存储器中的指令或数据的。如果要读出一个WORD(4bytes),那么有两种结果:

  • 大端模式(Big-Endian),也称MSB(Most Significant Byte),低地址存放高字节。
  • 小端模式(Little-Endian),也称LSB(Least Significant Byte),低地址存放低字节。

例如0x12345678在两种模式下的存储情况如下:

低地址                    高地址
大端 12 34 56 78
小端 78 56 34 12

4 MIPS指令集

4.1 MIPS指令特点

MIPS指令有一些特别的地方:

  • MIPS固定4字节指令长度;
  • 内存中的数据访问(load/store)必须严格对其(至少4字节对齐);
  • 跳转指令只有26位目标地址,加上2位对齐位,可寻址28位的空间,即256MB;
  • 条件分支指令只有16位跳转地址,加上2位对齐位,可寻址18位的空间,即256KB;
  • 流水线效应。MIPS采用了高度的流水线,其中最重要的就是分支延迟效应。在分支跳转语句后面那条语句叫分支延迟槽。实际上,在程序执行到分支语句时,当他刚把要跳转的地址填充好(填充到代码计数器里),还没有完成本条指令时,分支语句后面的那个指令就已经执行了,其原因就是流水线效应——几条指令同时执行,只是处于不同的阶段。

4.2 指令格式

MIPS指令长度为32位,其中指令位均为6位,其余的26位可以分为R型、I型、J型共3种类型。

R型 Opcode(6) Rd(5) Rs(5) Rt(5) Shamt(5) Funct(6)

I型 Opcode(6) Rd(5) Rs(5) Immediate(16)

J型 Opcode(6) Address(26)

各字段含义如下:

  • Opcode:指令基本操作,成为操作码;
  • Rs:第一个源操作数寄存器;
  • Rt:第二个源操作数寄存器;
  • Rd:存放操作结果的目的操作数;
  • Shamt:位移量;
  • Funct:函数,这个字段选择Opcode操作的某个特定变体。

4.3 常用指令

在下文语法表述中寄存器前面使用”$”符号进行标注,“imm”表示立即数,“MEM[]”表示RAM中的一段内存,”offset“表示偏移量。

4.3.1 LOAD/STORE指令

  • la(Load Address):用于将一个地址或标签存入一个寄存器

语法:

la $Rd, Label

示例:

la $t0, val_1    //复制val_1表示的地址到$t0寄存器中,其中val_1是一个Label
  • li(Load Immediate):用于将一个立即数存入一个寄存器

语法:

li $Rd, imm

示例:

la $t0, 40        //将寄存器$t1赋值为40
  • lui(Load Upper halfword Immediate):读取一个16位立即数放入寄存器的高16位,低16位补0。如果加载一个32位立即数(DWORD)则需要lui和addi两条指令配合完成。因为作为32位定长指令没有足够的空间存储32位立即数,只能用16位代替。
lui $Rd, imm

示例:

/*
byte 1字节 8位
word 2字节 16位
dword 4字节 32位
以小端格式,0x42为例
*/
lui $a1, 0x42 //将0x42放入$a1的高16位
  • lw(Load Word):用于从一个指定的地址加载一个word类型的值到寄存器中

语法:

lw $Rt, offset($Rs)

示例:

lw $s0, 0($sp)    //取堆栈地址偏移0内存word长度的值到$s0中,$s0 = MEM[$sp+0]
  • sw(Load Word):用于将源寄存器中的值存入指定的地址

语法:

sw $Rt, offset($Rs)

示例:

sw $a0, 0($sp)    //将$a0寄存器中的一个word大小的值存入堆栈,且$sp自动抬栈
  • move:用于寄存器之间值的传递

语法:

move $Rt, $Rs

示例:

move $t5, $t1    //$t5 = $t1

4.3.2 算数运算指令

MIP汇编指令的特点如下:

  • 算数运算指令的所有操作数都是寄存器,不能直接使用RAM地址或间接寻址
  • 操作数的大小都为word(4 Byte)
add $t0, $t1, $t2        //$t0 = $t1 + $t2,带符号数相加
sub $t0, $t1, $t2 //$t0 = $t1 - $t2,带符号数相减
addi $t0, $t1, 5 //$t0 = $t1 + 5
addu $t0, $t1, $t2 //$t0 = $t1 + $t2,无符号数相加
subu $t0, $t1, $t2 //$t0 = $t1 - $t2,无符号数相减
mult $t3, $t4 //(Hi, Lo) = $t3 * $t4
div $t5, $t6 //$Lo = $t5 / $t6 $Lo为商的整数部分, $Hi为商的余数部分
mfhi $t0 //$t0 = $Hi
mflo $t1 //$t1 = $Lo

4.3.3 类比较指令

在MIPS寄存器中没有标志寄存器,但是再MIPS指令中有一种指令——SLT系列指令,可以通过比较设置某个寄存器后与分支跳转指令联合使用。

  • slt(Set on Less Than):有符号比较
slt $Rd, $Rs, $Rt        //if ($Rs < $Rt):$Rd=1 else $Rd=0
  • slti(Set on Less Than Immediate):有符号比较
slti $Rd, $Rs, imm        //if ($Rs < imm):$Rd=1 else $Rd=0
  • sltu(Set on Less Than Unsigned):无符号比较
sltu $Rd, $Rs, $Rt        //if ($Rs < $Rt):$Rd=1 else $Rd=0
  • sltiu(Set on Less Than Immediate Unsigned):有符号比较
slt $Rd, $Rs, $Rt        //if ($Rs < $Rt):$Rd=1 else $Rd=0

4.3.4 系统调用指令

SYSCALL可以产生一个软中断,从而实现系统调用。

语法:

//系统调用号存放在$v0中
//参数存放在$a0~$a3中(如果参数过多,会有另一套机制来处理)
//系统调用的返回值通常放在$v0中
//如果系统调用出错,则会在$a3中返回一个错误号
syscall

4.3.5 分支跳转指令

在MIPS中,分支跳转指令本身可通过比较两个寄存器中的值来决定是否跳转。要想实现与立即数比较的跳转,可以结合类跳转指令实现。

b target                    //goto target
beq $Rs, $Rt, target //if ($Rs == $Rt): goto target
blt $Rs, $Rt, target //if ($Rs < $Rt): goto target
ble $Rs, $Rt, target //if ($Rs <= $Rt): goto target
bgt $Rs, $Rt, target //if ($Rs > $Rt): goto target
bge $Rs, $Rt, target //if ($Rs >= $Rt): goto target
bne $Rs, $Rt, target //if ($Rs != $Rt): goto target

4.4.6 跳转指令

//直接跳转到target标签处
j target //跳转到$Rs寄存器指向的地址处
//常用于子函数返回
//如果子函数内又调用了其他子函数,那么$Rs的值应该被保存到堆栈中
jr $Rs //跳转到target标签处,并保存返回地址到$ra
//常用于子函数的调用
//1、复制当前PC值(即子函数的返回地址)到$ra寄存器中
//2、程序跳转到子程序标签target处
jal target

5 MIPS函数调用

5.1 基本栈帧结构

完整的栈帧结构具有5个部分:

Low Address        Top of Stack frame
+----------------------------------+
| Local Data Storage Section |
+----------------------------------+
| Pad |
+----------------------------------+
| Return Address Section |
+----------------------------------+
| Saved Registers Section |
+----------------------------------+
| Argument Section |
+----------------------------------+
High Address Bottom of Stack frmae
  • 参数段(Argument Section)。参数段旨在函数调用子函数(非叶子函数)时出现。

    • 编译器在生成这个段的时候保证了它的大小足够所有的参数存放。
    • 参数段最小4*4 byte 大小。
    • 参数寄存器$a0~$a3用于传递子函数的前4个参数。这几个参数将不会复制到栈帧空间中去(注意:栈帧空间仍将为其预留等额空间),额外的参数将复制进入参数段(偏移为16,20,24等等)。
    • 参数端在栈帧的低地址端(栈顶位置)。
  • 寄存器保存段(Saved Registers Section)。当函数需要使用到$s0~$s7中的寄存器时出现。

    • 寄存器保存端将为每个需要保存的寄存器准备4 byte大小的的空间。
    • 函数的开场白(Prologue)会在函数入口处将每一个需要保存的寄存器的值保存到寄存器保存段中,然后在返回前将其恢复到对应的寄存器中。
    • 如果函数本身没有对寄存器的内容进行改变,则不需要进行保存/恢复这个过程。
    • 寄存器保存段在参数段后面(栈底方向)。
  • 返回地址段(Return Address Section)。当函数调用了其他子函数时出现。

    • 返回地址段只有1字节大小,即用1字节来保存返回地址。
    • 返回地址寄存器$ra将在函数入口处被复制到返回地址段,再函数返回前恢复到$ra中。
    • 返回地址段在寄存器保存段后面(栈底方向)。
  • 填充段(Pad)。当参数段+寄存器保存段+返回地址段的大小不是8的倍数时出现。

    • 填充段只有1字节大小(要么就没有)。
    • 填充段不存数据。
    • 填充段在返回地址段后面(栈底方向)。
  • 局部数据存储段(Local Data Storage Section)。当函数使用了局部参数或必须保存寄存器参数时出现。

    • 局部数据存储段的大小是由子程序对于局部数据存储的需求决定的,它总是8的倍数。
    • 局部数据存储段的数据是函数私有的,是被调用函数和函数的子函数都无法访问的数据。

局部数据存储段在填充段后面(栈底方向),是栈帧的最后一部分

5.2 简单叶子函数

简单叶子函数指的是不调用任何子函数,不访问任何堆栈上的内存空间的函数。

考虑如下C语言代码:

int g(int x, int y){
return (x + y);
}

用汇编语言显示其调用过程如下:

g:    add $v0, $a0, $a1    #result of args
jr $ra #return

5.3 带数据访问的叶子函数

带数据访问的叶子函数指的是访问栈空间但是不再调用子函数的函数。

考虑如下C语言代码:

int g(int x, int y){
int a[32];
#(...calculate using x, y, a)
return a[0];
}

用汇编语言显示其调用过程如下(假设计算过程中改变了$s0, $s1, $s3的值):

#start of prologue
addiu $sp, $sp, (-128) #push stack frame sw $s0, 0($sp) #save value of $s0
sw $s1, 4($sp) #save value of $s1
sw $s3, 8($sp) #save value of $s3
#end of prologue #calculate using $a0, $a1, and a array is stored at address 16($sp) to 140($sp)
... lw $v0, 0($sp) #result is a[0] #start of epilogue
lw $s0, 0($sp) #restore value of $s0
lw $s1, 4($sp) #restore value of $s1
lw $s3, 8($sp) #restore value of $s3 addiu $sp, $sp, 128 #pop stack frame
#end of epilogue jr $ra #return

栈帧结构如下:

Low Address        Top of Stack frame
+-----------------------------------+
| $s0 | 0($sp)
+-----------------------------------+
| $s1 | 4($sp)
+-----------------------------------+
| $s3 | 8($sp)
+-----------------------------------+
| empty (4 byte) | 12($sp)
+-----------------------------------+
| a[0] | 16($sp)
+-----------------------------------+
| a[1] | 20($sp)
+-----------------------------------+
| ... |
+-----------------------------------+
| a[30] | 136($sp)
+-----------------------------------+
| a[31] | 144($sp)
+-----------------------------------+
High Address Bottom of Stack frmae

注意:如果函数在计算过程中并没有改变$s0,$s1,$s3的值,那么编译器就不会对其进行保存操作。

5.4 非叶子函数

非叶子函数指的是将会调用子函数的函数,这样的函数栈帧空间将具有所有栈帧节区属性。

考虑如下C语言代码:

int g(int x, int y){
int a[32];
#(...calculate using x, y, a)
a[1] = f(y, x, a[2], a[3], a[4]);
a[0] = f(x, y, a[1], a[2], a[3]);
return a[0];
}

用汇编语言显示其调用过程如下:

#start of prologue
addiu $sp, $sp, (-168) #push stack frame sw $ra, 32($sp) #save the return address sw $s0, 20($sp) #save value of $s0
sw $s1, 24($sp) #save value of $s1
sw $s3, 28($sp) #save value of $s3
#end of prologue #start of body
#calculate using $a0, $a1, and a array is stored at address 40($sp) to 164($sp)
... #save $a0, and $a1 in caller's stack frame
sw $a0, 168($sp) #save $a0 (variable x)
sw $a1, 172($sp) #save $a1 (variable y) #first call to function f
lw $a0, 172($sp) #arg0 is variable y
lw $a1, 168($sp) #arg1 is variable x
lw $a2, 48($sp) #arg2 is a[2]
lw $a3, 52($sp) #arg3 is a[3]
lw $t0, 56($sp) #arg4 is a[4]
sw $t0, 16($sp) #BUT it is passed on the stack!
jal f #call f sw $v0, 44($sp) #store value of f into a[1] #second call to function f
lw $a0, 168($sp) #arg0 is variable x
lw $a1, 172($sp) #arg1 is variable y
lw $a2, 44($sp) #arg2 is a[1]
lw $a3, 48($sp) #arg3 is a[2]
lw $t0, 52($sp) #arg4 is a[3]
sw $t0, 16($sp) #BUT it is passed on the stack
jal f #call f sw $v0, 40($sp) #store value of f into a[0] #load return value of g into $v0
lw $v0, 40($sp) #result is a[0]
#end of body #start of epilogue
lw $s0, 20($sp) #restore value of $s0
lw $s1, 24($sp) #restore value of $s1
lw $s3, 28($sp) #restore value of $s3 lw $ra, 32($sp) #restore the return address addiu $sp, $sp, 168 #pop stack frame
#end of epilogue jr $ra #return

栈帧结构如下:

Low Address        Top of Stack frame
+-----------------------------------+
| (arg 0) | 0($sp)
+-----------------------------------+
| (arg 1) | 4($sp)
+-----------------------------------+
| (arg 2) | 8($sp)
+-----------------------------------+
| (arg 3) | 12($sp)
+-----------------------------------+
| arg 4 | 16($sp)
+-----------------------------------+
| $s0 | 20($sp)
+-----------------------------------+
| $s1 | 24($sp)
+-----------------------------------+
| $s3 | 28($sp)
+-----------------------------------+
| $ra | 32($sp)
+-----------------------------------+
| empty (4 byte) | 36($sp)
+-----------------------------------+
| a[0] | 40($sp)
+-----------------------------------+
| a[1] | 44($sp)
+-----------------------------------+
| ... |
+-----------------------------------+
| a[30] | 160($sp)
+-----------------------------------+
| a[31] | 164($sp)
+-----------------------------------+
High Address Bottom of Stack frmae

6 寻址方式

  • 模式3是基址偏移量寻址,例如lw和sw,是将16位地址字段做符号扩展成32位与PC相加
  • 模式4是PC相对寻址,将16位地址左移2位与PC(已更新为PC+4)相加。为什么要左移2位?16位偏移量左移2位以指示以字为单位的偏移量,这样做将偏移量能表示的有效范围扩大了4倍。其次,16位地址看作有符号数,一般在流水线设计时,先将16位地址符号扩展成32位,然后左移2位,再与PC相加
  • 模式5是伪直接寻址,把26位地址左移2位与PC高4位相连。这里的26位地址是无符号的,PC是加4之后的,在流水线设计中,就是将PC+4的高4位、26位地址、末尾00拼接成32位跳转目标

    转载自:https://juejin.cn/post/6844903702730375176

iot漏洞mips汇编基础的更多相关文章

  1. MIPS汇编学习

    MIPS汇编学习 mips汇编不同于x86汇编,属于精简指令集,常见于路由器等一些嵌入式设备中. mips汇编没有对堆栈的直接操作,也就是没有push和pop指令,mips汇编中保留了32个通用寄存器 ...

  2. ASM:《X86汇编语言-从实模式到保护模式》5-7章:汇编基础

    第5-7章感觉是这一本书中比较奇怪的章节,可能是作者考虑到读者人群水平的差异,故意由浅入深地讲如何在屏幕上显示字符和使用mov,jmp指令等等,但是这样讲的东西有点重复,而且看了第六,第七章以后,感觉 ...

  3. GNU ARM 汇编基础

    ARM GNU汇编基础 0 前言 全文补充提醒: 笔者在阅读ARM官方文档及查阅实际的u-boot源码中的汇编代码后,发现了一些不同于ARM官方文档中的汇编语法,查阅相关资料后,才发现主要由于汇编器的 ...

  4. 鸿蒙内核源码分析(汇编基础篇) | CPU在哪里打卡上班? | 百篇博客分析OpenHarmony源码 | v22.01

    百篇博客系列篇.本篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪里打卡上班 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在 ...

  5. win32汇编基础

    win32汇编基础知识 Debug 版本|Release 版本 Debug 是"调试"的意思,Debug 版本就是为调试而生的,编译器在生成 Debug 版本的程序时会加入调试辅助 ...

  6. MIPS汇编指令集

    MIPS有三种指令格式: R型 6 5 5 5 5 6 op rs rt rd shamt funct 功能:寄存器-寄存器ALU操作 (算术运算,逻辑运算) I型 6 5 5 16 op rs rt ...

  7. [asm] 小菜汇编基础和学习技巧小结(一)

    以下小结纯属小菜自学过程产生的dump,大神请飘过! 汇编是一门庞大复杂的学问,在计算机的世界里差不多无所不入.很多编程领域都会或多或少跟汇编打交道.本人不是科班出身的程序员,所以很多基础都为零,学历 ...

  8. MIPS汇编及模拟器下载

    1. 简述汇编语言发展 在计算机发展初期,人们用0-1序列来表示每一条语言,亦即二进制的机器指令 由于机器指令过于繁琐,程序员们开发出了一种新的语言,这种用符号表示的计算机语言被称为汇编语言 计算机继 ...

  9. iot漏洞入门

    路由器漏洞入门 下载项目https://github.com/praetorian-inc/DVRF 安装quem sudo apt install qemu-user-static 安装gdb-mu ...

随机推荐

  1. 2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest (Online Mirror, ICPC Rules) C. Berpizza (STL)

    题意:酒吧里有两个服务员,每个人每次都只能服务一名客人,服务员2按照客人进酒吧的顺序服务,服务员3按照客人的钱来服务,询问\(q\),\(1\)表示有客人进入酒吧,带着\(m\)块钱,\(2\)表示询 ...

  2. 23. 合并K个排序链表 分治

    这种k个相同的子问题,可以两两分治,总的运算次数为logk 关键部分 int dis=1; int len=lists.size(); while(dis<=len) { for(int i=0 ...

  3. git命令简写配置

    在使用git工具时,有些命令比较常用,为了加快输入速度,可以自定义一些简写配置,如下所示: git st # git status git ci # git commit git br # git b ...

  4. HDU - 5115 Dire Wolf (非原创)

    Dire wolves, also known as Dark wolves, are extraordinarily large and powerful wolves. Many, if not ...

  5. codeforces 1C (非原创)

    C. Ancient Berland Circus time limit per test 2 seconds memory limit per test 64 megabytes input sta ...

  6. 属于我的md5sum程序

    目录 前言 介绍 使用说明 总结 前言 之所以想做这个软件是因为一直在使用的http://keir.net/hash.html软件有很多功能不能满足. 经过自学C#,研究多线程,异步更新UI,等等知识 ...

  7. Virtual Reality In Action

    Virtual Reality In Action VR WebXR immersive 沉浸式 https://github.com/immersive-web/webxr https://imme ...

  8. js type automatic conversion

    js type automatic conversion String & Number `255` < 16; false `15` < 16; true `25` < 1 ...

  9. GitHub Sponsors

    GitHub Sponsors https://github.com/sponsors https://github.com/sponsors/xgqfrms?preview=true https:/ ...

  10. TypeScript 3.7 RC & Optional Chaining

    TypeScript 3.7 RC & Optional Chaining https://devblogs.microsoft.com/typescript/announcing-types ...