How to get PC of first instruction of inline asm block (C)?

86 views Asked by At

I am writing code for a RISCV microcontroller. I am searching for a trick to get the program counter (PC) of an extended inline asm block, from a file different from the inline asm itself. Note: this asm block is not the initial code in a function.

The reason is that the hardware will trigger some side effects when (or if) the execution reaches that specific PC.

A pseudo-C solution can be the following (the code won't work ofc).

File_A.c

//some_code
chat routine_name[10] = 'routine';
uint32_t routine_start_PC = get_start_PC(routine_name);
// some_other_code

File_B.c

void foo_rou(){
   //some_code_BEFORE (IMPORTANT!)
   routine:
   asm volatile(
   ...
   );
}

Does anyone have a suggestion?

1

There are 1 answers

7
the busybee On

This is my suggestion on godbolt:

#include <stdio.h>

void routine(void);

void foo_rou(void) {
    printf("Begin of %s()\n", __FUNCTION__);
    asm volatile(
        ".global routine\n"
        "routine:"
    );
    printf("End of %s()\n", __FUNCTION__);
}

void print_ptr(void *p) {
    printf("%p\n", p);
}

int main(void) {
    print_ptr((void*)foo_rou);
    print_ptr((void*)routine);
}

It uses the separate function print_ptr() to identify the actual addresses.

The assembly listing shows that routine is handled correctly:

main:
 addi   sp,sp,-16
 sd s0,0(sp)
 lui    a1,0x10
 lui    s0,0x10
 addi   a1,a1,1394 # 10572 <foo_rou>
 addi   a0,s0,1488 # 105d0 <_IO_stdin_used+0x28>
 sd ra,8(sp)
 jal    ra,104a0 <printf@plt>
 lui    a1,0x10
 addi   a0,s0,1488
 addi   a1,a1,1414 # 10586 <routine>
 jal    ra,104a0 <printf@plt>
 ld ra,8(sp)
 ld s0,0(sp)
 li a0,0
 addi   sp,sp,16
 ret
 ...
load_gp:
 auipc  gp,0x2
 addi   gp,gp,766 # 12800 <__global_pointer$>
 ret
 ...
foo_rou: # [my addition] 0x10572
 addi   sp,sp,-16
 sd s0,0(sp)
 lui    a0,0x10
 addi   a1,gp,-2000 # 12030 <__FUNCTION__.0>
 addi   a0,a0,1456 # 105b0 <_IO_stdin_used+0x8>
 sd ra,8(sp)
 jal    ra,104a0 <printf@plt>
routine: # [my addition] 0x10586
 addi   a1,gp,-2000 # 12030 <__FUNCTION__.0>
 ld s0,0(sp)
 ld ra,8(sp)
 lui    a0,0x10
 addi   a0,a0,1472 # 105c0 <_IO_stdin_used+0x18>
 addi   sp,sp,16
 j  104a0 <printf@plt>
print_ptr:
 mv a1,a0
 lui    a0,0x10
 addi   a0,a0,1488 # 105d0 <_IO_stdin_used+0x28>
 j  104a0 <printf@plt>

The important part is the declaration of the function routine. This lets the compiler know that there is a function of that name, so you can reference it. General advice: Don't do anything else with it.

You can declare the prototype with a parameter list and return type, and call it from C, as long as the ABI is met. (WARNING: Do so only if you exactly know what you are doing. It is very brittle! The smallest change of your source or compile options can break it.)

Since such a declaration has no definition, you define the symbol as shown in the inline assembler part.

Please note that some compilers add an underscore as a prefix to C symbols. For such compilers you will need to use _routine.