我正在尝试用 C 和 MIPS 编写一些自修改代码。
因为我想稍后修改代码,所以我尝试编写实际的机器指令(而不是内联汇编)并尝试执行这些指令。有人告诉我可以只 malloc 一些内存,在那里写指令,将 C 函数指针指向它然后跳转到它。(我包括下面的示例)
我已经用我的交叉编译器(sourcery codebench 工具链)尝试过这个,但它不起作用(是的,事后看来,我认为它看起来确实很幼稚)。我怎样才能正确地做到这一点?
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
void inc(){
int i = 41;
uint32_t *addone = malloc(sizeof(*addone) * 2); //we malloc space for our asm function
*(addone) = 0x20820001; // this is addi $v0 $a0 1, which adds one to our arg (gcc calling con)
*(addone + 1) = 0x23e00000; //this is jr $ra
int (*f)(int x) = addone; //our function pointer
i = (*f)(i);
printf("%d",i);
}
int main(){
inc();
exit(0);}
我在这里遵循 gcc 调用约定,其中参数传递给 $a0,函数的结果预计在 $v0 中。我实际上不知道返回地址是否会被放入 $ra (但我还不能测试它,因为我无法编译。 我在指令中使用 int 因为我正在编译 MIPS32(因此 32 位 int 应该足够了)
最佳答案
OP 编写的代码使用 Codesourcery mips-linux-gnu-gcc 编译时没有错误。
正如其他人在上面提到的,MIPS 上的自修改代码需要在编写代码后将指令缓存与数据缓存同步。 MIPS 架构的 MIPS32R2 版本添加了 SYNCI
instruction这是一条用户模式指令,可以满足您的需求。所有现代 MIPS CPU 都实现 MIPS32R2,包括 SYNCI
。
内存保护是 MIPS 上的一个选项,但大多数 MIPS CPU 在构建时并未选择此功能,因此在大多数真正的 MIPS 硬件上可能不需要使用 mprotect 系统调用。
请注意,如果您使用 -O0
之外的任何优化,编译器可以并且确实优化了存储到 *addone
和函数调用,这会破坏您的代码。使用 volatile
关键字可防止编译器执行此操作。
以下代码生成正确的 MIPS 程序集,但我手头没有 MIPS 硬件来测试它:
int inc() {
volatile int i = 41;
// malloc 8 x sizeof(int) to allocate 32 bytes ie one cache line,
// also ensuring that the address of function addone is aligned to
// a cache line.
volatile int *addone = malloc(sizeof(*addone) * 8);
*(addone) = 0x20820001; // this is addi $v0 $a0 1
*(addone + 1) = 0x23e00000; //this is jr $ra
// use a SYNCI instruction to flush the data written above from
// the D cache and to flush any stale data from the I cache
asm volatile("synci 0(%0)": : "r" (addone));
volatile int (*f)(int x) = addone; //our function pointer
int j = (*f)(i);
return j;
}
int main(){
int k = 0;
k = inc();
printf("%d",k);
exit(0);
}
关于c - 编写 MIPS 机器指令并从 C 执行它们,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13162464/