0
votes

Jumping to a C function using its absolute address (0x80904) in 64arm with BLR fails:

LDR x3, =0x80904 
SUB x0, x3, x19
BL print_x0 // Prints something to uart, i.e, x0 == x19

BLR x19  // Fails!

The address is good: BLR x3 works.

x19 value is not changing after print_x0. I tested it.

Why is branching generating this exception?

Thank you!

1
Is that code address in executable memory, according to page tables or any other memory-protection settings?Peter Cordes
Also is sp valid? No idea if that would cause the same exception.Jester
If the lowest 6 bits of esr_el1 can be trusted, then according to the manual this would be an "Address size fault, level 0 of translation or translation table base register", suggesting your page tables map to a physical address out of range... otherwise I agree with @Jester: makie sure that sp is valid, and additionally check if you have SP alignment checks enabled (and if so, make sure SP is actually 16-byte aligned).Siguza
Any chance that anotherFunc screwed up the stack? Anyway, it seems like it would be useful to have a register dump at the site of the blr x19, if you have a way to produce one.Nate Eldredge
That wouldn't really help - those would both give the "intended" address of function rather than where you actually end up jumping to. What if you dump a few words from the absolute address 0x80904 and see if they match the machine code of the function?Nate Eldredge

1 Answers

0
votes

First of all, thank you all for your help! I got it to work. That said, I don't really understand what the problem was.

The function below created a structure containing the address to the function I call (see fn). This address was later loaded into x19 before I would attempt BLR x19.

struct task_struct * copy_process(unsigned long fn)
{
    struct task_struct *p;
    p = (struct task_struct *)get_free_page();

    char buff[] = "0000000000000000";
    parse_int((unsigned long)fn, buff, 16);
    uart_send_string("fn: ");
    uart_send_string(buff);
    uart_send_string("\n");

    // Configure the task struct
    p->cpu_context.x19 = fn;
    return p;
}

It suffices to comment out parse_int((unsigned long)fn, buff, 16); and everything works as expected.

Here is what parse_int looks like:

void parse_int(u64 number, char* str, int base) {
    if(base > 36)
        return;

    int length = 0;
    while (str[length] != '\0') {
        str[length] = '0';
        length++;
    }

    char remainder;
    while(number >= 0 && length >= 0) {
        length--;
        remainder = (number % base);
        if(remainder > 9) {
            remainder -= 10;
            str[length] = 'A' + remainder;    
        } else {
            str[length] = '0' + remainder;
        }
        number /= base;
    }
}

I guess somehow memory was getting corrupted in this call, I can't see how, though.

If you know the reason, feel free to jump in with an answer to the original problem and I'll mark it as the good one. I will also delete this answer of mine, as this is probably not helpful for someone with the same problem.

Thanks