Search code examples
armstack-unwinding

Converting an prel31 address to the actual address from an .ARM.exidx section


I want to learn about ARM stack unwinding and for this reason, I took a closer look into the .ARM.exidx section of my binary. The binary was build using gcc for arm, little endian, for an Cortex M0 32bit.

As expected, I got the exidx table, here is an example I dumped with objdump:

8012d34 c09bff7f ab07b180 909cff7f a907b180  ................
8012d44 c09cff7f b0ab0e80 509eff7f b0ab0c80  ........P.......
8012d54 249fff7f b0aa0580 b49fff7f b0ab0880  $...............
8012d64 aca0ff7f b0ab1080 74a1ff7f b0ab0880  ........t.......
8012d74 dca1ff7f b0b0b080 e4a1ff7f aa03b180  ................
8012d84 34a2ff7f b0ab0880 20a3ff7f b0ab1280  4....... .......
8012d94 20a4ff7f b0b0b080 28a4ff7f b0b0a880   .......(.......
8012da4 44a4ff7f 01000000 10a6ff7f b0b0b080  D...............

As far as I understand is the first entry the call address of a function inside my programm (The ones that end in 0x7f) and are in the prel31 format to indicate the general model.

Now, my question is, how can I convert these addresses into the actual function addresses? I found the conversion inside the unwind.h inside the linux for ARM kernel

/* Convert a prel31 symbol to an absolute address */
#define prel31_to_addr(ptr)             \
({                          \
    /* sign-extend to 32 bits */            \
    long offset = (((long)*(ptr)) << 1) >> 1;   \
    (unsigned long)(ptr) + offset;          \
})

but I can not get the addresses from the exidx section to match up with the addresses I can look up inside the map file.

Should the addresses from the exidx match up with the from the map file or is there a misunderstanding on my side? If the should match up, how can I convert them?


Solution

  • See: Structure of ARM extab.

    The exidx section is linked with the executable. The distance between the exidx and the code is important. You can get the linker to dump the table address. It is actually the 'binary offset'. For example,

         10000: 8012d34 c09bff7f
    

    Then you take 0x7fff9bc0, the 2nd little endian entry and run it through the macro.

    Offset is 0xffff9bc0 -> 6440 So the binary offset is 10000-6440 -> 9bc0.

    Unfortunately, your 'exidx' offset is probably not so nice as 0x10000. If you have a linker file, you can add padding to make it nice and easy to translate for an experiment.

    The offset matters and this is what make a pointer relative of length 31 to an address (prel31addr).