Writing ARM machine instructions and executing them from C (on raspberry pi)

I am trying to write self-study code in C and ARM. I previously asked a similar question about MIPS and am now trying to transfer the project to ARM.
My system: = Raspbian on raspberries pi, ARMv6, GCC

There are a few things I'm not sure about:

  • Is ARM required to refuse write / I-cache D-cache (cache-flash)? If so, how can we do this?

I also 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 segmentation error. I use the aapcs call convention, which I was given to understand, this is the default value for all ARM

I would really appreciate if someone pointed me in the right direction

Bonus question (that is, it really doesn’t need to be answered, but it would be nice to know) - I “came from the MIPS background”, how the hell are ARM programmers without a register of 0? (e.g. case encoded to value 0)

+4
source share
3 answers

Read the Caches and self-modifying code at blogs.arm.com . The article also contains an example that does what you describe.

To answer your question from an article

... ARM architecture is often seen as a modified Harvard architecture ....

A typical drawback of Harvard's clean architecture is that command memory is not directly accessible from the same address space as data memory, although this limitation does not apply to ARM. In ARM, you can write instructions to memory, but since the D-cache and I-cache are not consistent, newly written instructions can be masked by the existing contents of the I-cache, forcing the processor to execute old (or possibly incorrect) instructions.

See __ clear_cache for how to invalidate caching.

I hope you also know the ARM / Thumb instruction set if you plan on entering your instructions into memory.

+2
source

There are several issues.

  • You do not clear your D-Cache and I-Cache, so in most cases I-Cache retrieves obsolete data from L2. There is libc / sys-call on linux that does this for you. Either use __clear_cache (start, end) or _builtin_clear_cache (start, end).
  • You output Thumb-Code, but you do not care about how the code is called. The easiest way to fix this is to use some asm code to make the actual blx call and the OR address from 1, since this bit sets the mode in which the processor works. Since you are the malloc address, you will always be aligned with the word boundary, forcing you to call your thumb in arm mode.
+1
source

Alright, so it 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);} 
+1
source

All Articles