1

I'm reversing an ARM64 bootloader using Ghidra. The bootloader doesn't use the MMU and so works exclusively with physical addresses, which on this particular chip all fit in 32 bits (the memory map is only 4GB). As such, some peripherals' DMA address registers are only 32 bits wide even though the core is ARM64.

I've found that the decompiler omits important instructions for one particular function that deals with these registers. The function sets up some data structures on its stack and then loads a (32-bit) pointer to them into a DMA address register. Because the pointer gets truncated to fit in the register, though, Ghidra doesn't appear to recognize that the stack variables are ever referenced and so the decompiler output completely omits them!

Here's a demo in C with similar code flow (plus an extra hack to make it run successfully for pointers that don't fit in 32 bits). Compiling it with GCC exhibits the issue for me both on ARM64 (either with or without optimization) and on x86 (without optimization):

#include <stdio.h>
#include <stdint.h>

void print_stack_data(uint32_t addr_low) {
    uint32_t *ptr;

    // Infer the top half of the address from our own stack's location. This
    // is purely to make this demo work; in my real binary, the addresses in
    // question are physical addresses that are known to always have a top
    // half of zero.
    ptr = (uint32_t *)(((uint64_t)&ptr & 0xffffffff00000000) | addr_low);

    printf("%08x, %08x, %08x, %08x\n", ptr[0], ptr[1], ptr[2], ptr[3]);
}

int main() {
    // Some arbitrary data.
    uint32_t numbers[4] = {0x1234, 0x5678, 0x99999999, 0};

    // More operations, yay!
    numbers[3] = (numbers[0] << 16) + numbers[1];

    // Put the parameter into a new variable so that the compiler doesn't
    // just put the entire pointer into the argument register, which Ghidra
    // does understand.
    uint32_t numbers_low = (uint64_t)numbers;

    // Ghidra doesn't realize we're giving out a stack address here, so the
    // decompiler doesn't show all the previous operations on numbers.
    print_stack_data(numbers_low);
}

Here's what Ghidra's decompilation for the optimized ARM64 binary looks like. Note that numbers isn't present at all:

Ghidra decompiler screenshot

Is there a way around this issue that doesn't involve patching the binary (which is what I'm currently doing)? Can I force the decompiler to treat a range of stack addresses as used, even if it can't see where they're used?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.