Copy function to RAM and call it with function pointer

Hello,

I am trying to copy the function to RAM and then call it using a function pointer like below:

It forced a system restart and it should have encountered an error.

Can you please help me deal with it? Thanks in advance!

uint8_t ram_func(uint8_t data_in)
{
  data_in++;
  return data_in;
}
/*---------------------------------------------------------------*/
int main() {
    typedef uint8_t (*func_ptr)(uint8_t);
    uint8_t func_buffer[256];
    memset(func_buffer, 0, 256);
    // Copy the machine code of func into func_buffer
    memcpy(&func_buffer, (uint8_t*)ram_func, 255);

    // Create a function pointer and cast the buffer address to it
    func_ptr fp = (func_ptr)func_buffer;

    // Call the function through the function pointer
    uint8_t data_out = fp(3);

    printf("Result = %d\n", data_out);
    return 0;
}

Parents
  • Hi,

    We do not use position independent code, so copying out a function to a different location will not work. If you want a function to reside in RAM, you need to tell the compiler to place it there (and then all offsets and jumps etc. will be correct for that position). You can read more about how ths is done under Code And Data Relocation.

  • Thanks Einar for your prompt reply!
    As I understood, the Code And Data Relocation example you provided is actually allocating the function in the Flash by the liner and calling it in the main file.

    I attempt to call the function directly from RAM buffers (copying the machine code of function) in the runtime without changing the Liner sections architecture. Do you think we have to use the position independent code for the compiler itself to specify the location where I copy the function in RAM on the basis of nRF5280 DK?

    Thanks again for your support!

  • Same code, but also returning a value on stack in case something was amiss there; seems ok:

    uint8_t ram_func(uint8_t data_in)
    {
      data_in++;
      return data_in;
    }
    static uint8_t RamFuncTestSub(void) {
        typedef uint8_t (*func_ptr)(uint8_t);
        uint32_t func_buffer[64];
        // Copy the machine code of func into func_buffer
        uint32_t *pRAM=func_buffer;
        uint32_t *pCode=(uint32_t *)((uint32_t)&ram_func & 0xFFFFFFFEUL);
        for (uint32_t i=0UL; i<sizeof(func_buffer)/sizeof(func_buffer[0]); i++)
        {
          *pRAM++ = *pCode++;
        }
        // Create a function pointer and cast the buffer address to it, remember Thumb Mode
        uint32_t *pfunc_buffer = (uint32_t*)(((uint32_t)func_buffer) | 0x00000001UL);
        func_ptr fp = (func_ptr)pfunc_buffer;
        // Test code function to verify that works
        uint8_t data_out1 = ram_func(2);
        // Call the function through the function pointer
        uint8_t data_out2 = fp(3);
        return data_out2;
    }
    static void RamFuncTest(void) {
        //printf("start\n\n");
        uint8_t result = RamFuncTestSub();
        //printf("Result = %d\n", result);
        while(1) ;
    }

    I even tested with buffer on stack; all ok.

  • Thanks!

    Please check my debugging at your code, it didn't return the data_out2 value, seems that the program didn't execute the "uint8_t data_out2 = fp(3);"

    That's weird, do you think it results from the different device we used?

  • I just tested same code on a nRF52840 Feather, doesn't work with optimisation set to High but works fine with optimisation none. Your capture shows too much optimisation :-)

  • I just give the code on the adafruit feather nrf52840 by the platform io.
    FW didn't load correctly that we can't see the COM port if RamFuncTestSub() executes the uint8_t data_out2 = fp(3);
    It gives the right answer if I comment the uint8_t data_out2 = fp(3); and return data_out1.

    uint8_t ram_func(uint8_t data_in)
    {
      data_in++;
      return data_in;
    }
    static uint8_t RamFuncTestSub(void) {
        typedef uint8_t (*func_ptr)(uint8_t);
        uint32_t func_buffer[64];
        // Copy the machine code of func into func_buffer
        uint32_t *pRAM=func_buffer;
        uint32_t *pCode=(uint32_t *)((uint32_t)&ram_func & 0xFFFFFFFEUL);
        for (uint32_t i=0UL; i<sizeof(func_buffer)/sizeof(func_buffer[0]); i++)
        {
          *pRAM++ = *pCode++;
        }
        // Create a function pointer and cast the buffer address to it, remember Thumb Mode
        uint32_t *pfunc_buffer = (uint32_t*)(((uint32_t)func_buffer) | 0x00000001UL);
        func_ptr fp = (func_ptr)pfunc_buffer;
        // Test code function to verify that works
        uint8_t data_out1 = ram_func(2);
        // Call the function through the function pointer
        // uint8_t data_out2 = fp(3);
        return data_out1;
    }
    
    void setup() {
      Serial.begin(115200);
    }
    
    void loop() {
      uint8_t result = RamFuncTestSub();
      Serial.printf("result = %d\n", result);
      delay(100);
    }

    Can you please let me know what IDE and env you used on feather nrf52840?

  • IAR v8.32.4 with optimisation set to None; Feather 840.

    What optimisation level are you using? The RAM is never read, so RAM buffer gets optimised away so the function will cause a hard fault since it is not present in RAM. Make the buffer volatile and add code to read back the data if it is not convenient to lower the optimisation level.

        volatile uint32_t func_buffer[64];
    

    Works ok on Feather 840 with SES V7.32

Reply
  • IAR v8.32.4 with optimisation set to None; Feather 840.

    What optimisation level are you using? The RAM is never read, so RAM buffer gets optimised away so the function will cause a hard fault since it is not present in RAM. Make the buffer volatile and add code to read back the data if it is not convenient to lower the optimisation level.

        volatile uint32_t func_buffer[64];
    

    Works ok on Feather 840 with SES V7.32

Children
Related