Execute binary machine code from c on RIOT-OS

Hello,

I have a question: How to a C array executable in RIOT-OS? From this stackoverflow, we have a solution, I don’t know how to do it on RIOT-OS

ps: my board: nrf52840dk + host OS: ubuntu

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>

char code[] = {0x55,0x48,0x89,0xe5,0x89,0x7d,0xfc,0x48,
    0x89,0x75,0xf0,0xb8,0x2a,0x00,0x00,0x00,0xc9,0xc3,0x00};
/*
00000000004004b4 <main> 55                          push   %rbp
00000000004004b5 <main+0x1>  48 89 e5               mov    %rsp,%rbp
00000000004004b8 <main+0x4>  89 7d fc               mov    %edi,-0x4(%rbp)
00000000004004bb <main+0x7>  48 89 75 f0            mov    %rsi,-0x10(%rbp)
'return 42;'
00000000004004bf <main+0xb>  b8 2a 00 00 00         mov    $0x2a,%eax
'}'
00000000004004c4 <main+0x10> c9                     leaveq 
00000000004004c5 <main+0x11> c3                     retq 
*/

int main(int argc, char **argv) { 
    void *buf;

    /* copy code to executable buffer */    
    buf = mmap (0,sizeof(code),PROT_READ|PROT_WRITE|PROT_EXEC,
                MAP_PRIVATE|MAP_ANON,-1,0);
    memcpy (buf, code, sizeof(code));
    __builtin___clear_cache(buf, buf+sizeof(code)-1);  // on x86 this just stops memcpy from optimizing away as a dead store

    /* run code */
    int i = ((int (*) (void))buf)();
    printf("get this done. returned: %d\n", i);

    return 0;
}

(This is for Cortex-M / nrf5284dk)

If you have an array of valid (position independent) code, you should be able to directly execute it. there should be no need for the mmap / memcpy dance.

The code needs to start at a 4 byte boundary, so the array might need an __attribute((aligned(4))). Also consider that we’re using thumb only mode, so the code in the array needs to be compiled for thumb, and the function pointer needs to have the LSB set.

Hello Kaspar, I tried an example on my board, but failed to get the result, could you please give me some suggestions?

//main.c
#include <stdio.h>
#include <string.h>

__attribute((aligned(4))) char code[] = {
 0xb5, 0x80, 0xb0, 0x84, 0xaf, 0x00, 0x23, 0x0c,
 0x60, 0xfb, 0x23, 0x14, 0x60, 0xbb, 0x68, 0xfa,
 0x68, 0xbb, 0x18, 0xd3, 0x60, 0x7b, 0x68, 0x7b,
 0x00, 0x18, 0x46, 0xbd, 0xb0, 0x04, 0xbc, 0x80,
 0xbc, 0x02, 0x47, 0x08
  };

/* source code
int main(void){
  int x, y, z;
  x = 12;
  y = 20;
  z = x + y;
  return z;
}
*/
/* arm-none-eabi-gcc -c -O0 -mthumb armtest.c
00000000 <main>:
   0:	b580      	push	{r7, lr}
   2:	b084      	sub	sp, #16
   4:	af00      	add	r7, sp, #0
   6:	230c      	movs	r3, #12
   8:	60fb      	str	r3, [r7, #12]
   a:	2314      	movs	r3, #20
   c:	60bb      	str	r3, [r7, #8]
   e:	68fa      	ldr	r2, [r7, #12]
  10:	68bb      	ldr	r3, [r7, #8]
  12:	18d3      	adds	r3, r2, r3
  14:	607b      	str	r3, [r7, #4]
  16:	687b      	ldr	r3, [r7, #4]
  18:	0018      	movs	r0, r3
  1a:	46bd      	mov	sp, r7
  1c:	b004      	add	sp, #16
  1e:	bc80      	pop	{r7}
  20:	bc02      	pop	{r1}
  22:	4708      	bx	r1
*/

int main(void) {
    int (*func)();
    func = (int (*) (void)) (*code | 0x1); // set the lsb
    int i = (*func)();
    printf("get this done. returned: %d\n", i);

    return 0;
}
$ BOARD=nrf52840dk make -C tests/bench_exec_array WERROR=0 flash
2022-10-06 13:59:05,136 # Context before hardfault:
2022-10-06 13:59:05,143 # main(): This is RIOT! (Version: ...)
2022-10-06 13:59:05,145 # *** RIOT kernel panic:
2022-10-06 13:59:05,147 # MEM MANAGE HANDLER
2022-10-06 13:59:05,147 # 
2022-10-06 13:59:05,148 # *** halted.
2022-10-06 13:59:05,148 # 
2022-10-06 13:59:05,149 # Inside isr -12

Seems like the mem manage handler kicked in, which might be the MPU forbidding something - try FEATURES_BLACKLIST += cortexm_mpu.

Is that function pointer calculation correct? (*code) would point to the first value in the array, whereas you want the address. that would be (&code[0]).

Can you step through with gdb? Where exactly does it memfault?

(I’d start with way simpler code, like, mov r0, #42; bx lr;.)

Hello Kaspar,

I changed the makefile and also the code, but can not execute it correctly.

include ../Makefile.tests_common

FEATURES_BLACKLIST += cortexm_mpu
CFLAGS += -I$(CURDIR)

include $(RIOTBASE)/Makefile.include
#include <stdio.h>
#include <string.h>

__attribute((aligned(4))) char code[] = {
 0xe3, 0xa0, 0x00, 0x2a,
 0xe1, 0x2f, 0xff, 0x1e
  };

/*
  __asm volatile ("mov r0, #42\n\t"
    "bx lr\n\t"
    : :  : 
  );
*/
/*
   8:	e3a0002a 	mov	r0, #42	; 0x2a
   c:	e12fff1e 	bx	lr
*/

int main(void) {
    int i;

    __asm volatile ("orr %[input_0], #0x1\n\t"
    "blx %[input_0]\n\t"
    : [result] "=r" (i)
    : [input_0] "r" (code)
    :
    );
    printf("get this done. returned: %d\n", i);

    return 0;
}


$ BOARD=nrf52840dk make -C tests/bench_exec_array WERROR=0 flash debug
2022-10-06 14:21:21,295 # main(): This is RIOT! (Version: ...)
2022-10-06 14:21:21,295 # 
2022-10-06 14:21:21,298 # Context before hardfault:
2022-10-06 14:21:21,299 #    r0: 0x00000000
2022-10-06 14:21:21,301 #    r1: 0x00000000
2022-10-06 14:21:21,302 #    r2: 0x000000aa
2022-10-06 14:21:21,304 #    r3: 0x00000000
2022-10-06 14:21:21,305 #   r12: 0x00000000
2022-10-06 14:21:21,307 #    lr: 0x0000010b
2022-10-06 14:21:21,309 #    pc: 0x20000748
2022-10-06 14:21:21,310 #   psr: 0x41000000
2022-10-06 14:21:21,310 # 
2022-10-06 14:21:21,311 # FSR/FAR:
2022-10-06 14:21:21,313 #  CFSR: 0x00010000
2022-10-06 14:21:21,314 #  HFSR: 0x40000000
2022-10-06 14:21:21,316 #  DFSR: 0x00000000
2022-10-06 14:21:21,317 #  AFSR: 0x00000000
2022-10-06 14:21:21,318 # Misc
2022-10-06 14:21:21,319 # EXC_RET: 0xfffffffd
2022-10-06 14:21:21,322 # Active thread: 1 "main"
2022-10-06 14:21:21,325 # Attempting to reconstruct state for debugging...
2022-10-06 14:21:21,326 # In GDB:
2022-10-06 14:21:21,328 #   set $pc=0x20000748
2022-10-06 14:21:21,329 #   frame 0
2022-10-06 14:21:21,329 #   bt
2022-10-06 14:21:21,329 # 
2022-10-06 14:21:21,333 # ISR stack overflowed by at least 16 bytes.

GDB result:

Reading symbols from /home/shyuan/GitHub/RIOT/tests/bench_exec_array/bin/nrf52840dk/tests_bench_exec_array.elf...
Remote debugging using :3333
hard_fault_handler (sp=0x0 <cortex_vector_base>, corrupted=9, exc_return=8, 
    r4_to_r11_stack=0x77777777)
    at /home/shyuan/GitHub/RIOT/cpu/cortexm_common/vectors_cortexm.c:452
452	    __BKPT(1);
(gdb) set $pc=0x20000748
(gdb) bt
#0  0x20000748 in main_stack ()
#1  0x0000010a in main ()
    at /home/shyuan/GitHub/RIOT/tests/bench_exec_array/main.c:16
(gdb) frame 0
#0  0x20000748 in main_stack ()
(gdb) bt
#0  0x20000748 in main_stack ()
#1  0x0000010a in main ()
    at /home/shyuan/GitHub/RIOT/tests/bench_exec_array/main.c:16
(gdb) s
Single stepping until exit from function main_stack,
which has no line number information.
^Z

Hello Kaspar,

I find this paragraph from RIOT-OS " #ifdef MODULE_MPU_NOEXEC_RAM This marks the memory region from 0x20000000 to 0x3FFFFFFF as non * executable. This is the Cortex-M SRAM region used for on-chip RAM ", maybe because of it? If so, how to disable it…

mpu_noexec_ram is disabled by default.

I think your arm assembly is no correct thumb code.

I arrived at this:

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

__attribute__((aligned(4))) unsigned char code[] = {
  0x4f, 0xf0, 0x2a, 0x00, 0x70, 0x47
};

int main(void) {
    union {
      uintptr_t as_int;
      int(*fn)(void);
    } helper;

    helper.as_int = ((uintptr_t)&code[0] | 0x1);

    int i = helper.fn();

    printf("get this done. returned: %d\n", i);

    return 0;
}

that’s mov r0, #42; bx lr, in thumb. it took me a while to figure out the byte ordering, in the end I manually printed a C function byte-wise via printf. :slight_smile:

Hello Kaspar,

It solves my proble! Many Thanks for your help! :+1:

BTW, I tried to replace your code with inline assembly code, but It returns a different result, do you have any ideas?

    /*
    union {
      uintptr_t as_int;
      int(*fn)(void);
    } helper;

    helper.as_int = ((uintptr_t)&code[0] | 0x1);

    int i = helper.fn();

    printf("get this done. returned: %d\n", i);
    */
    
    int i;
    
    __asm volatile ("orr %[input_0], #0x1\n\t"
    "blx %[input_0]\n\t"
    : [result] "=r" (i)
    : [input_0] "r" (code)
    :
    );

    printf("get this done. returned: %x\n", i);
   // print: get this done. returned: 20000201

Hello Kaspar,

By GDB, I understand I lose one instruction

    "orr %[input_0], #0x1\n\t"
    "blx %[input_0]\n\t"
    "mov r1, r0\n\t"  //add this one

Thanks a lot!

Hi @Kaspar

BTW, you mentioned RIOT-OS is using thumb only mode on nrf5284dk, is it possible to exchange to arm mode?

nope, Cortex-M cores only support thumb.

I see, thx