编写MIPS机器指令和从C执行它们 [英] Writing MIPS machine instructions and executing them from C

查看:544
本文介绍了编写MIPS机器指令和从C执行它们的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试着写在C和MIPS一些自我修改code。

因为我想以后修改code,我想写实际的机器指令(相对于内联汇编),我试图执行这些指令。有人告诉我,这将有可能为只是malloc的一些记忆,写指令出现,指向一个C函数指针,然后跳转到它。(包括我下面的例子)

我已经与我的交叉编译器(的Sourcery codebench工具链)尝试这样做,它不工作(是的,在后视力,我想它似乎相当幼稚)。我怎么能正确地做到这一点?

 的#include<&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&stdint.h GT;
空隙增量(){
    INT I = 41;
    uint32_t的* addone =的malloc(sizeof的(* addone)* 2); //我们的asm函数,我们的malloc空间
    *(addone)= 0x20820001; //这是阿迪$ V0 $ A0 1,增加了一到我们的ARG(GCC调用CON)
    *(addone + 1)= 0x23e00000; //这是JR $ RA    INT(* F)(INT X)= addone; //我们的函数指针
    I =(* F)(I)
    的printf(%D,我);
}诠释主(){
    增量();
出口(0);}

我按照这里的海湾合作​​委员会调用约定,这里的参数传递到$ a0和功能的结果预计将在$ V0。我是不知道如果返回地址将被放入$ RA(但我不能测试它没有因为我不能编译。
我用int对于我的指示,因为我编译MIPS32(因此一个32位的int应该是足够了)


解决方案

的OP的code书面编译没有与codesourcery MIPS-Linux的GNU-GCC错误。

正如其他人上面提到的,在MIPS自修改code要求的指令缓存中的code被写入后要与数据缓存同步。加入MIPS架构的MIPS32R2版的 SYNCI 指令这是一个用户态指令,可以做什么您这里需要。所有现代的CPU MIPS实现MIPS32R2,包括 SYNCI

内存保护是在MIPS一种选择,但大多数MIPS CPU都不会选择,所以使用mprotect的系统调用,很可能不需要在最真实的MIPS硬件此内置功能。<​​/ P>

请注意,如果你使用任何除了优化 -O0 编译器能够而且确实优化掉门店 * addone 和函数调用,它伤了你的code。使用挥发性关键字$ P $从这样pvents编译器。

以下code生成正确MIPS汇编,但是我没有MIPS硬件方便测试它:

  INT INC(){
    挥发性INT I = 41;
    // malloc的8×的sizeof(int)的分配32字节,即一个高速缓存行,
    //还确保功能addone的地址对齐
    //一个高速缓存行。
    挥发性为int * addone =的malloc(sizeof的(* addone)* 8);
    *(addone)= 0x20820001; //这是阿迪$ V0 $ A0 1
    *(addone + 1)= 0x23e00000; //这是JR $ RA
    //使用SYNCI指令,以刷新上面写数据
    //为D缓存并刷新从我缓存中的任何过时的数据
    ASM挥发性(SYNCI 0(0%):R(addone));
    挥发性INT(* F)(INT X)= addone; //我们的函数指针
    INT J =(* F)(I)
    复位J;
}诠释主(){
    INT K = 0;
    K =增量();
    的printf(%D,K);
    出口(0);
}

I'm trying to write some self modifying code in C and MIPS.

Since I want to modify the code later on, I'm trying to write actual machine instructions (as opposed to inline assembly) and am trying to execute those instructions. Someone told me that it would be possible to just malloc some memory, write the instructions there, point a C function pointer to it and then jump to it. (I include the example below)

I've tried this with my cross compiler (sourcery codebench toolchain) and it doesn't work (yes, in hind sight I suppose it does seem rather naive). How could I properly do this?

#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);}

I follow the gcc calling convention here, where the arguments are passed to $a0 and the results of the functions are expected to be in $v0. I don't actually know if the return address will be put into $ra (but I can't test it yet since I can't compile. I use int for my instructions because I'm compiling MIPS32(hence a 32 bit int should be enough)

解决方案

The OP's code as written compiles without errors with Codesourcery mips-linux-gnu-gcc.

As others have mentioned above, self modifying code on MIPS requires the instruction cache to be synchronized with the data cache after the code is written. The MIPS32R2 version of the MIPS architecture added the SYNCI instruction which is a user mode instruction that does what you need here. All modern MIPS CPUs implement MIPS32R2, including SYNCI.

Memory protection is an option on MIPS, but most MIPS CPUs are not built with this feature selected, so using the mprotect system call is likely not needed on most real MIPS hardware.

Note that if you use any optimization besides -O0 the compiler can and does optimize away the stores to *addone and the function call, which breaks your code. Using the volatile keyword prevents the compiler from doing this.

The following code generates correct MIPS assembly, but I don't have MIPS hardware handy to test it on:

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);
}

这篇关于编写MIPS机器指令和从C执行它们的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆