简体   繁体   中英

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)

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.

There are a couple of problems.

  1. You don't flush your D-Cache and I-Cache, so most times the I-Cache will fetch stale data from L2. Under linux there is a libc/sys-call which does that for you. Either use __clear_cache(begin, end) or _ builtin _clear_cache(begin, end).
  2. You output Thumb-Code, but you don't take care of how your code gets called. The easiest way to fix that would be to use some asm-code to do the actual blx call and OR the address with 1, as this bit sets the mode the processor runs in. As you're malloc address will always be aligned to a word boundary, making you call thumb-code in arm-mode.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM