mips架构初探(一)
字数 3018 2025-08-22 12:22:48
MIPS架构初探:从基础到调试
1. MIPS架构概述
MIPS架构是一种采用精简指令集(RISC)的处理器架构,由MIPS科技公司于1981年开发。主要特点包括:
- 固定长度的定期编码指令集
- 采用导入/存储(Load/Store)数据模型
- 算术和逻辑运算采用三个操作数的形式
- 支持32位和64位版本
- 广泛应用于电子产品、网络设备和个人娱乐装置
2. 环境搭建
2.1 安装必要工具
#!/bin/sh
# 安装QEMU及相关工具
sudo apt install qemu
sudo apt install qemu-system qemu-user-static binfmt-support
# 安装ARM架构编译器和依赖
sudo apt install libncurses5-dev gcc-arm-linux-gnueabi build-essential
# 安装MIPS架构编译器
sudo apt-get install gcc-mips-linux-gnu
sudo apt-get install gcc-mipsel-linux-gnu
sudo apt-get install gcc-mips64-linux-gnuabi64
sudo apt-get install gcc-mips64el-linux-gnuabi64
# 安装多架构gdb调试依赖
sudo apt install gdb-multiarch
2.2 QEMU简介
QEMU是一个快速模拟器,能够:
- 在单个硬件平台上运行多个操作系统
- 仿真广泛的客户系统和架构
- 直接与物理硬件接口
- 整合Intel VT和AMD-V等硬件虚拟化技术
3. MIPS程序编译
3.1 示例程序
#include <stdio.h>
int main() {
printf("Hello, MIPS!\n");
return 0;
}
3.2 编译选项
MIPS有大端(mips)和小端(mipsel)两种格式,支持32位和64位指令集:
| 架构 | 编译命令 | 输出文件 |
|---|---|---|
| 32位小端序 | mipsel-linux-gnu-gcc -g test.c -o test_mipsel_32 |
test_mipsel_32 |
| 32位大端序 | mips-linux-gnu-gcc -g test.c -o test_mips_32 |
test_mips_32 |
| 64位小端序 | mips64el-linux-gnuabi64-gcc -g test.c -o test_mipsel_64 |
test_mipsel_64 |
| 64位大端序 | mips64-linux-gnuabi64-gcc -g test.c -o test_mips_64 |
test_mips_64 |
3.3 运行程序
使用QEMU模拟运行:
qemu-mipsel -L /usr/mips-linux-gnu/ ./test_mipsel_32
如果库路径不正确,可以使用以下命令查找:
dpkg -L libc6-mipsel-cross
4. MIPS指令集
4.1 指令类型
MIPS指令分为三种类型:
- R型指令:寄存器-寄存器操作
- I型指令:立即数操作
- J型指令:跳转操作
4.2 指令分类
算术运算指令
| 指令 | 功能 | 格式 |
|---|---|---|
| add | 有符号整数加法 | add rd, rs, rt |
| addi | 有符号整数加法(立即数) | addi rt, rs, imm |
| sub | 有符号整数减法 | sub rd, rs, rt |
| mult | 有符号乘法 | mult rs, rt |
| div | 有符号除法 | div rs, rt |
逻辑运算指令
| 指令 | 功能 | 格式 |
|---|---|---|
| and | 按位与 | and rd, rs, rt |
| andi | 按位与(立即数) | andi rt, rs, imm |
| or | 按位或 | or rd, rs, rt |
| ori | 按位或(立即数) | ori rt, rs, imm |
| xor | 按位异或 | xor rd, rs, rt |
| nor | 按位取反或 | nor rd, rs, rt |
移位指令
| 指令 | 功能 | 格式 |
|---|---|---|
| sll | 左移 | sll rd, rt, shamt |
| srl | 逻辑右移 | srl rd, rt, shamt |
| sra | 算术右移 | sra rd, rt, shamt |
数据传输指令
| 指令 | 功能 | 格式 |
|---|---|---|
| lw | 加载字 | lw rt, offset(rs) |
| sw | 存储字 | sw rt, offset(rs) |
| lb | 加载字节 | lb rt, offset(rs) |
| sb | 存储字节 | sb rt, offset(rs) |
条件分支指令
| 指令 | 功能 | 格式 |
|---|---|---|
| beq | 等于则分支跳转 | beq rs, rt, label |
| bne | 不等则分支跳转 | bne rs, rt, label |
| bgtz | 大于零则跳转 | bgtz rs, label |
| blez | 小于等于零则跳转 | blez rs, label |
跳转指令
| 指令 | 功能 | 格式 |
|---|---|---|
| j | 无条件跳转 | j label |
| jal | 跳转并链接 | jal label |
| jr | 寄存器跳转 | jr rs |
特殊指令
syscall:用于系统调用nop:空操作指令
5. MIPS寄存器
MIPS有32个通用寄存器,每个寄存器有特定用途:
| 寄存器 | 名称 | 用途 |
|---|---|---|
| $0 | $zero | 恒为零 |
| $1 | $at | 汇编器保留 |
| \(2-\)3 | \(v0-\)v1 | 函数返回值 |
| \(4-\)7 | \(a0-\)a3 | 函数参数 |
| \(8-\)15 | \(t0-\)t7 | 临时寄存器 |
| \(16-\)23 | \(s0-\)s7 | 保存寄存器 |
| \(24-\)25 | \(t8-\)t9 | 临时寄存器 |
| \(26-\)27 | \(k0-\)k1 | 内核保留 |
| $28 | $gp | 全局指针 |
| $29 | $sp | 栈指针 |
| $30 | $fp | 帧指针 |
| $31 | $ra | 返回地址 |
6. MIPS特性
6.1 固定指令长度
所有MIPS指令长度固定为4字节。
6.2 栈增长方向
栈从内存的高地址向低地址方向增长。
6.3 函数分类
- 叶子函数:函数内部没有再调用其他函数
- 非叶子函数:函数内部调用其他函数的函数
6.4 流水线效应
MIPS采用高度流水线,导致分支延迟效应:
- 跳转指令后的指令称为分支延迟槽
- 跳转指令执行时,下一条指令已经被执行
- 为避免问题,通常在跳转指令后放置
nop或其他有用指令
6.5 缓存刷新机制
MIPS CPU有两个独立缓存:
- 指令cache
- 数据cache
攻击payload通常存储在数据缓存中,需要触发flush才能写入主内存。通常执行sleep(1)来刷新缓存。
7. 函数调用机制
7.1 函数调用
使用jal(Jump and Link)指令:
- 跳转到目标地址
- 将返回地址保存在
$ra寄存器中
7.2 函数返回
使用jr $ra指令跳转到$ra寄存器中的地址。
7.3 栈帧管理
函数进入时:
addiu $sp, $sp, -0x20 ; 分配栈空间
sw $ra, 0x1c($sp) ; 保存返回地址
sw $fp, 0x18($sp) ; 保存帧指针
move $fp, $sp ; 设置新帧指针
函数退出时:
lw $ra, 0x1c($sp) ; 恢复返回地址
lw $fp, 0x18($sp) ; 恢复帧指针
addiu $sp, $sp, 0x20 ; 释放栈空间
jr $ra ; 返回
7.4 参数传递
- 前4个参数通过
$a0到$a3传递 - 多于4个参数通过栈传递
- 调用者会在栈顶预留空间保存被调用者的参数
8. GDB调试
8.1 调试脚本
#!/bin/bash
# Ensure the script exits on error
set -e
# Define variables for file name, GDB port, and architecture
target_file="test_mipsel_32"
gdb_port="1234"
# Check if the target file exists
if [ ! -f "$target_file" ]; then
echo "Error: Target file '$target_file' does not exist."
exit 1
fi
# Step 1: Start QEMU with GDB server enabled
echo "Starting QEMU with GDB server on port $gdb_port..."
qemu-mipsel -L /usr/mipsel-linux-gnu/ -g $gdb_port "$target_file" &
qemu_pid=$!
# Wait for QEMU to be ready
sleep 2
echo "QEMU started with PID $qemu_pid."
# Step 2: Start GDB and connect to QEMU GDB server
mips_gdb="gdb-multiarch"
# Check if GDB is installed
if ! command -v $mips_gdb &> /dev/null; then
echo "Error: GDB ($mips_gdb) not found. Please install it and try again."
exit 1
fi
echo "Starting GDB and connecting to QEMU..."
$mips_gdb -ex "target remote :$gdb_port" "$target_file"
# Cleanup after GDB session ends
echo "Stopping QEMU..."
kill $qemu_pid
8.2 调试技巧
- 修改目标文件:
target_file="your_new_mips_file"
- 修改库路径:
qemu-mipsel -L /path/to/new/lib/ -g $gdb_port "$target_file" &
- 添加GDB启动命令:
$mips_gdb -ex "target remote :$gdb_port" -ex "break main" "$target_file"
- 设置环境变量:
qemu-mipsel -L /usr/mipsel-linux-gnu/ -g $gdb_port -E VAR_NAME=VALUE "$target_file" &