MIPS基础入门基础介绍 首先我们来介绍一下什么是mips,MIPS架构是一种采取 精简指令集(RISC)的处理器架构,1981年出现,由MIPS科技公司开发并授权,它是基于一种固定长度的定期编码指令集,并采用 导入/存储(Load/Store)数据模型。经改进,这种架构可支持高级语言的优化执行。其算术和逻辑运算采用三个操作数的形式,允许编译器优化复杂的表达式。
如今基于该架构的芯片广泛被使用在许多电子产品、网络设备、个人娱乐装置与商业装置上。最早的MIPS架构是32位,最新的版本已经变成64位。
环境模拟 我们的虚拟机基本都是ubuntu,也就是基于8086架构,所以这里我们学习mips需要理由qemu来模拟这个架构,而QEMU代表快速模拟器,是虚拟化领域的一个重要工具,能够在单个硬件平台上同时运行多个操作系统。以其能够仿真广泛的客户系统和架构而闻名,QEMU是创建和管理虚拟环境的多功能解决方案。它作为一个类型1虚拟机运行,直接与物理硬件接口,这与其他虚拟化技术有显著的不同。通过整合像Intel VT和AMD-V这样的硬件虚拟化技术,QEMU优化了虚拟机的性能,为开发人员和IT专业人员提供了一个强大的平台,用于模拟各种计算环境,无需为每个系统提供专用硬件。
下面是一个自动化脚本,运行可以得到完整配置的qemu和mips,现在我们来编译一个demo,看看怎么个事儿。
123456#include
对于MIPS来说,有大端和小端两种格式,mips是大端,mipsel是小端,且都支持32位和64位的指令集。
1234567891011121314151617#32位小端序:mipsel-linux-gnu-gcc -g test.c -o test_mipsel_32#使用小端 MIPS 架构的编译器生成 32 位小端格式的可执行文件。#32位大端序:mips-linux-gnu-gcc -g test.c -o test_mips_32#使用大端 MIPS 架构的编译器生成 32 位大端格式的可执行文件。#64位小端序:mips64el-linux-gnuabi64-gcc -g test.c -o test_mipsel_64#使用小端 MIPS 64 位架构的编译器生成小端 64 位的可执行文件。#64位大端序:mips64-linux-gnuabi64-gcc -g test.c -o test_mips_64#使用大端 MIPS 64 位架构的编译器生成大端 64 位的可执行文件。
现在我们测试一下其中一个
然后我们就能模拟运行这个mips框架的elf文件了
12qemu-mips64el-static -L /usr/mips64el-linux-gnuabi64 ./test_mipsel_64
这里的-L /usr/mips-linux-gnu/指向MIPS库的位置,根据具体环境可能需要调整。这里是默认的位置,当然如果你的并不可以,那可以选择使用如下的语句来寻找
12dpkg -L libc6-mipsel-cross
MIPS指令集1. MIPS 指令集结构MIPS 指令集是精简指令集(RISC)架构的典型代表,所有指令长度为 32 位。指令分为三种类型:R 型、I 型 和 J 型,每种类型有不同的操作码格式。
R 型指令(寄存器-寄存器操作):适用于寄存器之间的运算,通常包括算术和逻辑操作。Op段为0,使用funct字段区分指令
I 型指令(立即数操作):适用于需要立即数或地址的操作。使用Op字段区分load/store指令
J 型指令(跳转操作):用于跳转指令。使用Op字段区分指令
2. 指令分类MIPS 指令集主要分为以下几类:
以下是 MIPS 指令的详细执行表,包括操作数的来源和结果存储位置:
(1)算术运算指令
指令
功能
格式
执行描述
add
有符号整数加法
add rd, rs, rt
rd = rs + rt
addi
有符号整数加法(立即数)
addi rt, rs, imm
rt = rs + imm
sub
有符号整数减法
sub rd, rs, rt
rd = rs - rt
mult
有符号乘法
mult rs, rt
HI, LO = rs × rt(结果存储在 HI/LO 寄存器)
div
有符号除法
div rs, rt
LO = rs / rt, HI = rs % rt(商存 LO,余数存 HI)
(2)逻辑运算指令
指令
功能
格式
执行描述
and
按位与
and rd, rs, rt
rd = rs & rt
andi
按位与(立即数)
andi rt, rs, imm
rt = rs & imm
or
按位或
or rd, rs, rt
rd = rs
ori
按位或(立即数)
ori rt, rs, imm
rt = rs
xor
按位异或
xor rd, rs, rt
rd = rs ^ rt
nor
按位取反或
nor rd, rs, rt
rd = ~(rs
(3)移位指令
指令
功能
格式
执行描述
sll
左移
sll rd, rt, shamt
rd = rt << shamt
srl
逻辑右移
srl rd, rt, shamt
rd = rt >> shamt(高位补 0)
sra
算术右移
sra rd, rt, shamt
rd = rt >> shamt(高位补符号位)
(4)数据传输指令
指令
功能
格式
执行描述
lw
加载字
lw rt, offset(rs)
rt = *(rs + offset)(从 rs + offset 地址加载 4 字节到 rt)
sw
存储字
sw rt, offset(rs)
*(rs + offset) = rt(将 rt 存到 rs + offset 地址)
lb
加载字节
lb rt, offset(rs)
rt = *(rs + offset)(加载 1 字节,符号扩展到 32 位)
sb
存储字节
sb rt, offset(rs)
*(rs + offset) = rt(仅存储 rt 的低 8 位到 rs + offset)
(5)条件分支指令
指令
功能
格式
执行描述
beq
等于则跳转
beq rs, rt, label
if (rs == rt) goto label
bne
不等则跳转
bne rs, rt, label
if (rs != rt) goto label
bgtz
大于零则跳转
bgtz rs, label
if (rs > 0) goto label
blez
小于等于零则跳转
blez rs, label
if (rs <= 0) goto label
(6)跳转指令
指令
功能
格式
执行描述
j
无条件跳转
j label
goto label
jal
跳转并链接
jal label
ra = PC + 4; goto label(保存返回地址)
jr
寄存器跳转
jr rs
goto rs(跳转到 rs 指向的地址)
3. 特殊指令
syscall:用于系统调用,通常用于程序和操作系统之间的交互。
nop:空操作指令,执行时不产生任何效果,通常用于指令延迟槽。
MIPS 寄存器常用寄存器
名称
编号
作用
说明
$zero
$0
常数 0
值恒为 0,无法更改
$at
$1
汇编器临时寄存器
由汇编器内部使用,不建议手动操作
$v0-$v1
$2-$3
函数返回值
32 位返回值存于 $v0,64 位返回值存于 $v0, $v1
$a0-$a3
$4-$7
函数参数
依次存放第 1-4 个参数,更多参数需使用栈
$t0-$t7
$8-$15
临时寄存器
不需要保持值,函数调用后可能被覆盖
$s0-$s7
$16-$23
保存寄存器
需要保持值,函数调用时需保存和恢复
$t8-$t9
$24-$25
额外临时寄存器
用法同 $t0-$t7
$k0-$k1
$26-$27
内核保留寄存器
仅操作系统使用,用户程序不应修改
$gp
$28
全局指针
指向全局数据区
$sp
$29
栈指针
指向栈顶,函数调用时维护
$fp
$30
帧指针
用于存储栈帧地址(可选)
$ra
$31
返回地址
jal 指令跳转时存储返回地址
特殊寄存器
名称
作用
HI
存储乘法的高 32 位结果或除法的余数
LO
存储乘法的低 32 位结果或除法的商
PC
程序计数器,存储当前执行指令的地址
MIPS特性1. 指令集架构
固定指令长度:所有 MIPS 指令都是 4 字节(32 位),符合 RISC(精简指令集计算机)设计理念。
流水线优化:MIPS 采用 多级流水线,提高指令执行效率,但也带来了分支延迟效应和载入延迟效应。
分支延迟槽(Branch Delay Slot)
:
由于流水线特性,分支跳转指令后的一条指令仍会执行,称为分支延迟槽。
典型情况下,这条指令是 nop(无操作),但可以替换为对程序无害的有用指令。
2. 内存与栈管理
栈增长方向:MIPS 架构的栈(Stack)从高地址向低地址增长。
缓存(Cache)机制
:
指令缓存(I-Cache) 和 数据缓存(D-Cache) 独立存储,指令和数据不会混用。
执行代码必须先经过缓存刷新,否则数据缓存中的 shellcode 可能不会被执行,通常执行 sleep(1) 触发缓存刷新。
3. 函数调用约定
叶子函数(Leaf Function):函数内部没有调用其他函数,仅使用 $t0-$t9 作为临时寄存器。
非叶子函数(Non-Leaf Function):函数内部调用了其他函数,需要保存 $ra(返回地址寄存器) 以及可能被覆盖的 $s0-$s7 寄存器。
4. MIPS 代码执行安全性
指令与数据分离
:
由于指令和数据分别存储在 I-Cache 和 D-Cache,在代码注入攻击中,如果 D-Cache 没有同步刷新到 I-Cache,则 shellcode 可能不会执行。
解决方案:使用 sleep(1) 或 flush cache 触发缓存同步。
5. MIPS 指令特点
RISC 设计:指令种类少,每条指令执行时间固定。
加载/存储架构(Load/Store Architecture)
:
只有 lw(加载字) 和 sw(存储字) 指令可以访问内存,其他指令只能操作寄存器。
延迟槽优化:跳转指令的下一条指令仍会被执行,需手动填充 nop 或有用的指令。
MIPS 体系结构核心特性概述
特性
说明
指令长度
所有指令都是 4 字节(32 位)
流水线效应
分支延迟槽,跳转指令后的一条指令仍会执行
栈方向
从高地址向低地址增长
缓存结构
独立的 I-Cache 和 D-Cache,代码执行前需刷新缓存
调用约定
叶子函数不保存 $ra,非叶子函数需保存 $ra 和 $s0-$s7
加载存储架构
只有 lw / sw 访问内存,其他指令操作寄存器
安全性
shellcode 可能因缓存未同步导致执行失败,需要 flush
MIPS系统调用码MIPS 系统调用(syscall)使用 $v0 寄存器指定调用码,不同的调用码执行不同的系统功能。以下是常见的 MIPS 系统调用码表:
调用码($v0)
函数名字
功能
调用条件
1
print_int
打印整数
$a0 = 要打印的整数
2
print_float
打印浮点数(单精度)
$f12 = 要打印的浮点数
3
print_double
打印双精度浮点数
$f12 = 要打印的双精度浮点数
4
print_string
打印字符串
$a0 = 字符串地址
5
read_int
读取整数
读取的整数存入 $v0
6
read_float
读取浮点数(单精度)
读取的浮点数存入 $f0
7
read_double
读取双精度浮点数
读取的双精度浮点数存入 $f0
8
read_string
读取字符串
$a0 = 缓冲区地址, $a1 = 最大长度
9
sbrk
分配内存(返回地址)
$a0 = 需要分配的字节数,返回分配的内存地址于 $v0
10
exit
退出程序
无
11
print_char
打印字符
$a0 = 要打印的字符
12
read_char
读取字符
读取的字符存入 $v0
13
open
打开文件
$a0 = 文件名地址, $a1 = 访问模式, $a2 = 权限(可选),返回文件描述符
14
read
读取文件
$a0 = 文件描述符, $a1 = 读取缓冲区, $a2 = 读取字节数,返回读取的字节数
15
write
写入文件
$a0 = 文件描述符, $a1 = 写入缓冲区, $a2 = 写入字节数,返回写入的字节数
16
close
关闭文件
$a0 = 文件描述符
17
exit2
退出程序(带返回值)
$a0 = 退出码
示例:1234li $v0, 1 # 设置 syscall 码为 1(print_int)li $a0, 123 # 要打印的整数syscall # 执行系统调用
以上代码会打印 123。
MIPS syscall 机制允许程序通过 syscall 指令调用操作系统提供的服务,适用于 MIPS 汇编语言模拟器(如 MARS、SPIM)。