Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing ARM machine instructions and executing them from C (On the Raspberry pi)

I'm trying to write some self modifying code in C and ARM. I previously asked a similar question about MIPS and am now trying to port over the project to ARM.
My system := Raspbian on raspberry pi, ARMv6, GCC

There are a few things I am unsure of:

  • Does ARM require a D-cache write-back/I-cache invalidate (cache flush)? If so, how can we do this?

Also I tried an example

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int inc(int x){ //increments x
    uint16_t *ret = malloc(2 * sizeof(uint16_t));

    *(ret + 0) = 0x3001; //add r0 1 := r0 += 1
    *(ret + 1) = 0x4770; //bx lr    := jump back to inc()

    int(*f)(int) = (int (*)(int)) ret;
    return (*f)(x);
}

int main(){
    printf("%d",inc(6)); //expect '7' to be printed
exit(0);}

but I keep getting a segmentation fault. I'm using the aapcs calling convention, which I've been given to understand is the default for all ARM

I'd be much obliged if someone pointed me in the right direction

Bonus question (meaning, it doesn't really have to be answered, but would be cool to know) - I "come from a MIPS background", how the heck do ARM programmers do without a 0 register? (as in, a register hardcoded to the value 0)

like image 528
C.E.Sally Avatar asked Apr 04 '13 18:04

C.E.Sally


2 Answers

Read Caches and Self-Modifying Code on blogs.arm.com. Article includes an example as well which does what you are describing.

To answer your question from article

... the ARM architecture is often considered to be a Modified Harvard Architecture. ...

The typical drawback of a pure Harvard architecture is that instruction memory is not directly accessible from the same address space as data memory, though this restriction does not apply to ARM. On ARM, you can write instructions into memory, but because the D-cache and I-cache are not coherent, the newly-written instructions might be masked by the existing contents of the I-cache, causing the processor to execute old (or possibly invalid) instructions.

See __clear_cache for how to invalidate cache(s).

I hope you are also aware of ARM/Thumb instruction sets, if you are planning to push your instructions into memory.

like image 147
auselen Avatar answered Sep 22 '22 07:09

auselen


Ok, so this works on my raspberry Pi.

#include <stdio.h>
#include <sys/mman.h>
#include <stdint.h>
#include <stdlib.h>

int inc(int x){ //increments x

    uint32_t *ret = mmap(NULL,
            2 * sizeof(uint32_t),  // Space for 16 instructions. (More than enough.)
            PROT_READ | PROT_WRITE | PROT_EXEC,
            MAP_PRIVATE | MAP_ANONYMOUS,
            -1,0);
    if (ret == MAP_FAILED) {
        printf("Could not mmap a memory buffer with the proper permissions.\n");
        return -1;
    }

    *(ret + 0) = 0xE2800001; //add r0 r0 #1 := r0 += 1
    *(ret + 1) = 0xE12FFF1E; //bx lr        := jump back to inc()

    __clear_cache((char*) ret, (char*) (ret+2));

    int(*f)(int) = (int (*)(int)) ret;
    return (*f)(x);
}

int main(){
    printf("%d\n",inc(6)); //expect '7' to be printed
exit(0);}
like image 35
C.E.Sally Avatar answered Sep 22 '22 07:09

C.E.Sally