2

I am trying to define and call a function at run-time with c language in arm cpu(cortex a72). in order to do that, i implemented a code like below:

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

char* ibuf;
int   pbuf = 0;
#define ADD_BYTE(val) do{ibuf[pbuf] = val; pbuf++;} while(0)
void (*routine)(void);

void MakeRoutineSimpleFunc(void)
{
    //nop
    ADD_BYTE(0x00);
    ADD_BYTE(0xf0);
    ADD_BYTE(0x20);
    ADD_BYTE(0xe3);

    //bx lr
    ADD_BYTE(0x1e);
    ADD_BYTE(0xff);
    ADD_BYTE(0x2f);
    ADD_BYTE(0xe1);
}

int main(void)
{
    ibuf = (char*)mmap(NULL, 4 * 1024, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    MakeRoutineSimpleFunc();
    routine = (void(*)())(ibuf);
    routine();
}

as you can see, in above code at first i allocate an executable memory region and assign address of that to ibuf, then i put some simple instruction in ibuf (a "nop" and "bx lr" that means return in arm) and then i try to call this function through a function pointer.

but when i want to call function through function pointer i got an "segmentation fault" error. BTW when i try to run the app with GDB debugger program run successfully without any error.

there is anything that i missed in above code that cause "segmentation fault"?

i want to add, when i add above instructions (a "nop" and "bx lr" that means return in arm) at compile-time to a function like below, function work without any error.

void f2(void)
{
    __asm__ volatile (".byte 0x00, 0xf0, 0x20, 0xe3");
    __asm__ volatile (".byte 0x1e, 0xff, 0x2f, 0xe1");
}

EDIT1: in order to check validity of run-time function, i have removed f2 prolog and epilogue with ghidra disassembler, so assembly code of f2 is like this:

**************************************************************
FUNCTION                     
**************************************************************
undefined FUN_0000083c()
undefined         r0:1           <RETURN>
undefined4        Stack[-0x4]:4  local_4
FUN_0000083c                                    XREF[1]: FUN_00000868:000008a4(c)  
        0000083c 00 f0 20 e3     nop
        00000840 00 f0 20 e3     nop
        00000844 00 f0 20 e3     nop
        00000848 00 f0 20 e3     nop
        0000084c 00 f0 20 e3     nop
        00000850 00 f0 20 e3     nop
        00000854 1e ff 2f e1     bx         lr
        00000858 00 f0 20 e3     nop
        0000085c 00 f0 20 e3     nop
        00000860 00 f0 20 e3     nop
        00000864 00 f0 20 e3     nop

and also it work again without problem.

EDIT2: something that i want to add that may be helpful to solve the problem, as i saw in the assembler, compiler call "routine" function with "blx r3" instruction while it call other functions with "bl 'symbol name'". as i know blx can change processor state from ARM to Thumb or vise versa. could this point cause the problem?

EDIT3: disassemble of main function is something like below:

**************************************************************
FUNCTION
**************************************************************
int __stdcall main(void)
int               r0:4           <RETURN>
undefined4        Stack[-0xc]:4  local_c                                 XREF[1]:     00010d44(W)  
undefined4        Stack[-0x10]:4 local_10                                XREF[1]:     00010d4c(W)  
                             main                                            XREF[4]:     Entry Point(*), 
                                                                                          _start:00010394(*), 000103a8(*), 
                                                                                          .debug_frame::000000a0(*)  
        00010d34 00 48 2d e9     stmdb      sp!,{ r11 lr }
        00010d38 04 b0 8d e2     add        r11,sp,#0x4
        00010d3c 08 d0 4d e2     sub        sp,sp,#0x8
        00010d40 00 30 a0 e3     mov        r3,#0x0
        00010d44 04 30 8d e5     str        r3,[sp,#local_c]
        00010d48 00 30 e0 e3     mvn        r3,#0x0
        00010d4c 00 30 8d e5     str        r3,[sp,#0x0]=>local_10
        00010d50 22 30 a0 e3     mov        r3,#0x22
        00010d54 07 20 a0 e3     mov        r2,#0x7
        00010d58 01 1a a0 e3     mov        r1,#0x1000
        00010d5c 00 00 a0 e3     mov        r0,#0x0
        00010d60 7d fd ff eb     bl         mmap                                         
        00010d64 00 20 a0 e1     cpy        r2,r0
        00010d68 50 30 9f e5     ldr        r3,[->ibuf]                                      
        00010d6c 00 20 83 e5     str        r2,[r3,#0x0]=>ibuf
        00010d70 48 30 9f e5     ldr        r3,[->ibuf]
        00010d74 00 30 93 e5     ldr        r3,[r3,#0x0]=>ibuf
        00010d78 03 10 a0 e1     cpy        r1,r3
        00010d7c 40 00 9f e5     ldr        r0=>s_ibuf:_%x_00010e40,[PTR_s_ibuf:_%x_00010d
        00010d80 69 fd ff eb     bl         printf
        00010d84 ae fe ff eb     bl         MakeRoutineSimpleFunc
        00010d88 30 30 9f e5     ldr        r3,[->ibuf]
        00010d8c 00 30 93 e5     ldr        r3,[r3,#0x0]=>ibuf
        00010d90 03 20 a0 e1     cpy        r2,r3
        00010d94 2c 30 9f e5     ldr        r3,[->routine]
        00010d98 00 20 83 e5     str        r2,[r3,#0x0]=>routine
        00010d9c 24 30 9f e5     ldr        r3,[->routine]
        00010da0 00 30 93 e5     ldr        r3,[r3,#0x0]=>routine
        00010da4 33 ff 2f e1     blx        r3
        00010da8 1c 00 9f e5     ldr        r0=>DAT_00010e4c,
        00010dac 61 fd ff eb     bl         puts
        00010db0 00 30 a0 e3     mov        r3,#0x0
        00010db4 03 00 a0 e1     cpy        r0,r3
        00010db8 04 d0 4b e2     sub        sp,r11,#0x4
        00010dbc 00 88 bd e8     ldmia      sp!,{ r11 pc }

as you can see, routine called with "blx r3" instruction at address "00010da4". and also i printed address of ibuf, it is was "0xb6ff8000".

  • Related: https://stackoverflow.com/q/11016078/1025391 and https://stackoverflow.com/q/4911993/1025391 – moooeeeep Feb 18 '20 at 11:12
  • how are you insuring the ibuf array is word aligned? – old_timer Feb 18 '20 at 11:36
  • are you in arm or thumb mode when you call the function and is your target code arm or thumb? – old_timer Feb 18 '20 at 11:37
  • @old_timer thanks for your comment, as i allocate a memory page with "mmap" function, first 12bit of ibuf will be zero (for 4KB page) and surely ibuf is dividable by 4 and is word aligned. – alireza sadeghpour Feb 18 '20 at 11:41
  • @old_timer i am not so familiar with arm, but as i see in dissassembler each instruction occupy 4 byte in main function, can we conclude with this fact that we are in arm mode? – alireza sadeghpour Feb 18 '20 at 11:44
  • @old_timer i have added an update about arm and thumb state. could you please read this and see this may cause the problem? – alireza sadeghpour Feb 18 '20 at 13:38
  • yes, blx vs bl can be the problem. what is the disassembly of the calling code? you still want all of this aligned so ibuf wants to be either a short for thumb or int for arm – old_timer Feb 18 '20 at 14:28
  • bl cant be used here it would be a bx or blx so then it is a matter of addressing and alignment you want arm mode for your self modified code, so lsbit is zero so you need the instructions to start on an address where both lsbits are zero then bx (with lr prepped ahead of time) or blx to that address unmodified. – old_timer Feb 18 '20 at 14:31
  • printf the address of ibuf, what came back from the mmap instead of or before calling the function, what do you see? – old_timer Feb 18 '20 at 14:36
  • it is either permissions or alignment, if not alignment then permissions. – old_timer Feb 18 '20 at 14:41
  • did you check the return from mmap to confirm that it was able to allocate? I have found odds are better if you ask for a larger amount of memory 4MBytes instead of 4kbytes for example. always check the return address before using it. that would definitely segfault if mmap failed and you use the pointer – old_timer Feb 18 '20 at 14:43
  • @old_timer thanks for your response and excuse me for the delay, i have added disassemble of main function and also i checked ibuf address after calling mmap function. something that is strange is "segmentation fault" did not occur when i run programe with gdb debugger. and also i can ran a code with this strategy on x86 machine, so i think that there is not a problem with os and something is missed with ARM cpu. – alireza sadeghpour Feb 19 '20 at 10:08
  • @old_timer excuse me for too many question, there is anyway that i could create label in run-time for start of ibuf, so then maybe i could call it with "bl" instead of "blx"(as arm documentation specified "bl" syntax is "bl label"). and also i can assign a specific address to ibuf when allocating it with mmap if it could help to assign a label to it. – alireza sadeghpour Feb 19 '20 at 10:12
  • @old_timer with increasing 4kb to 4mb in mmap func, error from "segmentation fault" has changed to "illegal instruction"!! and also again when i run code in gdb step-by-step it works as expected and when i run it in debuger(no step-by-step) it encounter with "illegal instruction"!!! – alireza sadeghpour Feb 19 '20 at 11:25
  • did you switch to using words not bytes, it should work the way you are doing it...did you examine the memory location of the instruction before branching, does the blx have the right address before stepping into it and the right value 0xe320f000, basic debug stuff? – old_timer Feb 19 '20 at 14:34

1 Answers1

2

I think, you can enter the opcodes directly in a string "binary-code" and execute the code using ((void*)STRING)(). However, you may want to read also about how gcc implements trampolines, because this is how gcc generates code that creates code on the stack and jumps the execution there.

alinsoar
  • 15,386
  • 4
  • 57
  • 74
  • Thanks for your response, just one point that i did not told in question. i need to define function in specific address of programme. with my code if i send specific address to mmap instead of NULL i can do this (if i could fix "segmentation fault" problem :) ). but i could not imagine how can i do that with your example. – alireza sadeghpour Feb 18 '20 at 12:30
  • @alirezasadeghpour You need to know the target assembler very well and do not use instructions that make use of absolute values but of relative values, etc. Also, to be able to return from your code you need to know how the compiler will be implemented , to know where the return address can be found. – alinsoar Feb 18 '20 at 12:39
  • @alirezasadeghpour To mix assembler with C is very implementation specific and you need to be very motivated to learn the internals of a given implementation in order to be able to mix such things. – alinsoar Feb 18 '20 at 12:44
  • stack and return is fine the compiler is calling this as a function so everything will be in place. it is a matter of what instruction is being used to call (in this case it should be bx or blx since it cant be determined at link time), the alignment of the code, and permissions. 25% chance of success based on alignment, but mmap may return aligned all the time. the OP is 99% of the way there, not doing much if anything wrong – old_timer Feb 18 '20 at 14:40