Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
编写MIPS机器指令并从C执行_C_Assembly_Mips_Inline Assembly_Mips32 - Fatal编程技术网

编写MIPS机器指令并从C执行

编写MIPS机器指令并从C执行,c,assembly,mips,inline-assembly,mips32,C,Assembly,Mips,Inline Assembly,Mips32,我正在尝试用C和MIPS编写一些自我修改的代码 因为我想稍后修改代码,所以我尝试编写实际的机器指令(与内联汇编相反),并尝试执行这些指令。有人告诉我,可以只调用一些内存,在那里编写指令,将C函数指针指向它,然后跳转到它。(我包括下面的示例) 我已经用我的交叉编译器(sourcery codebench toolchain)尝试过了,但它不起作用(是的,事后看来,我认为它确实看起来很幼稚)。我怎样才能正确地做到这一点 #include <stdio.h> #include <st

我正在尝试用C和MIPS编写一些自我修改的代码

因为我想稍后修改代码,所以我尝试编写实际的机器指令(与内联汇编相反),并尝试执行这些指令。有人告诉我,可以只调用一些内存,在那里编写指令,将C函数指针指向它,然后跳转到它。(我包括下面的示例)

我已经用我的交叉编译器(sourcery codebench toolchain)尝试过了,但它不起作用(是的,事后看来,我认为它确实看起来很幼稚)。我怎样才能正确地做到这一点

#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);}
#包括
#包括
#包括
void公司(){
int i=41;
uint32_t*addone=malloc(sizeof(*addone)*2);//我们为asm函数分配malloc空间
*(addone)=0x20820001;//这是addi$v0$a0 1,它将一个添加到我们的arg(gcc调用con)
*(addone+1)=0x23e00000;//这是jr$ra
int(*f)(int x)=addone;//我们的函数指针
i=(*f)(i);
printf(“%d”,i);
}
int main(){
公司();
退出(0);}
我在这里遵循gcc调用约定,其中参数被传递到$a0,函数的结果预计在$v0中。我实际上不知道返回地址是否会被放入$ra(但我还不能测试它,因为我无法编译)。
我使用int作为指令,因为我正在编译MIPS32(因此32位int应该足够了)

调用函数比直接跳到指令要复杂得多

  • 参数是如何传递的?它们是存储在寄存器中还是被推送到调用堆栈中

  • 如何返回值

  • 返回跳转的返回地址在哪里?如果有递归函数,
    $ra
    不会将其剪切

  • 调用方或被调用方是否负责在被调用函数完成时弹出堆栈帧


对于这些问题,不同的编译器有不同的答案。虽然我从未尝试过类似于您所做的任何事情,但我会假设您必须编写符合约定的机器代码,然后告诉编译器您的函数指针使用该约定(不同的编译器有不同的方法执行此操作-).

您不恰当地使用了指针。或者,更准确地说,您没有在应该使用指针的地方使用指针

试试这个尺码:

uint32_t *addone = malloc(sizeof(*addone) * 2);
addone[0] = 0x20820001; // addi $v0, $a0, 1
addone[1] = 0x23e00000; // jr $ra

int (*f)(int x) = addone; //our function pointer
i = (*f)(i);
printf("%d\n",i);
您可能还需要在写入内存后,但在调用内存之前,将其设置为可执行:

mprotect(addone, sizeof(int) * 2, PROT_READ | PROT_EXEC);

为了实现这一点,您可能还需要分配相当大的内存块(4k左右)因此,地址是页面对齐的。

您还需要确保所讨论的内存是可执行的,并确保在写入dcache后正确地从dcache中刷新该内存,并在执行之前将其加载到icache中。如何做到这一点取决于mips机器上运行的操作系统

在Linux上,可以使用mprotect系统调用使内存可执行,使用cacheflush系统调用进行缓存刷新

编辑

例如:

#include <unistd.h>
#include <sys/mman.h>
#include <asm/cachecontrol.h>

#define PALIGN(P)  ((char *)((uintptr_t)(P) & (pagesize-1)))
uintptr_t  pagesize;

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

    pagesize = sysconf(_SC_PAGESIZE);  // only needs to be done once
    mprotect(PALIGN(addone), PALIGN(addone+1)-PALIGN(addone)+pagesize,
             PROT_READ | PROT_WRITE | PROT_EXEC);
    cacheflush(addone, 2*sizeof(*addone), ICACHE|DCACHE);

    int (*f)(int x) = addone; //our function pointer
    i = (*f)(i);
    printf("%d",i);    
}
#包括
#包括
#包括
#定义paign(P)((char*)((uintpttr__t)(P)和(pagesize-1)))
uintptr_t页面大小;
void公司(){
int i=41;
uint32_t*addone=malloc(sizeof(*addone)*2);//我们为asm函数分配malloc空间
*(addone)=0x20820001;//这是addi$v0$a0 1,它将一个添加到我们的arg(gcc调用con)
*(addone+1)=0x23e00000;//这是jr$ra
pagesize=sysconf(_SC_pagesize);//只需执行一次
mprotect(PALIGN(addone),PALIGN(addone+1)-PALIGN(addone)+页面大小,
保护读取|保护写入|保护执行|);
cacheflush(addone,2*sizeof(*addone),ICACHE | DCACHE);
int(*f)(int x)=addone;//我们的函数指针
i=(*f)(i);
printf(“%d”,i);
}

请注意,我们将包含代码的整个页面设置为可写和可执行。这是因为内存保护在每个页面都有效,我们希望malloc能够继续使用页面的其余部分对于其他方面,您可以使用
valloc
memalign
来分配整个页面,在这种情况下,您可以使代码成为只读的可执行文件。

OP编写的代码使用Codesourcery mips linux gnu gcc编译时不会出错

如上所述,MIPS上的自修改代码要求在写入代码后指令缓存与数据缓存同步。MIPS体系结构的MIPS32R2版本添加了
SYNCI
,这是一条用户模式指令,可以满足您的需要。所有现代MIPS CPU都实现了MIPS32R2,包括g
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);
}

当你说“它不工作”时,会发生什么情况呢?你将
malloc
返回的指针存储在一个int中。int是否足够大?为什么不使用合适的指针类型?另外,你确定你的int是指令的正确大小吗?我对MIPS了解不够,但是当我完成这类工作时(1)您需要确保内存具有使用
mprotect
的正确权限,(2)如果体系结构具有不一致的单独指令/数据缓存,则可能需要刷新指令和数据缓存。请记住
jr
在将控制权转移到t之前从其延迟槽执行指令