Home > Net >  ARM LDR instruction with PC register as memory address
ARM LDR instruction with PC register as memory address

Time:06-21

I have this decompiled arm binary. The comments are based on this answer...

...
12de3c0:       e59f6400        ldr     r6, [pc, #1024] ; load into r6 a word from offset 0x12de7c8 (0x12de3c0   0x8   1024)
12de3c4:       e1a0a000        mov     sl, r0 ; ignore this
12de3c8:       e1a09001        mov     r9, r1 ; ignore this
12de3cc:       e08f6006        add     r6, pc, r6 ; r6 = pc   r6; so r6 = 0x12de3cc   0x8   0x035cbf8f = 0x48AA363 ???
12de3d0:       e5d60000        ldrb    r0, [r6] ; load a byte into r0 from the obtained address (0x48AA363)
...
12de7c8:       035cbf8f        cmpeq   ip, #572        ; 0x23c
...

However, I am confused that 0x48AA363 exceeds the size of the binary, so there has to be a mistake in my assumptions. Where might I have misinterpreted the code ?

CodePudding user response:

If you're running under an OS with virtual memory, it's normal to map executables into memory starting at a non-zero virtual address. (So NULL pointer deref can fault, instead of being a valid address!). That means it's normal for code pointers (including PC while executing) to be large numbers, much larger than the file size. Note that even the 12de3c0 address you can already see in the disassembly is quite large.

And linkers normally put gaps between sections (so array out of bounds is more likely to fault, making for easier debugging, among other reasons). So it's likely that PC-relative addressing (via add r6, pc, r6) to some address fairly distant from your .text section is .data or .bss. Unlikely .rodata since loading a constant byte wouldn't make sense.

You can use readelf -a to see the mappings given by the program headers, and find where that address is. Assuming your executable still has section headers, you can also see what section the address is in, instead of just the permissions and file-offset from the ELF segment headers.

You could also set a breakpoint here and run it, to double-check your math and see what address the ldrb loads from. (And double check for run-time fixups (aka text relocations) or whatever. Although that's generally not needed in a non-PIE executable.)

I originally wrote the above as comments. I'm posting this after you already did that:

I placed a breakpoint and r6 turned to be exactly 0x48AA363. And that memory region was containing a byte with the value of 0, probably a global / static boolean because it is followed by cmp r0, #0

Yup, sounds like a reasonable conclusion.

  • Related