Getting/setting registers for arm32 executable on aarch64

Viewed 72

My goal is to use ptrace to set the program counter (and other registers) of a process running a 32-bit ARM executable on a 64-bit processor.

If this was a 64-bit executable, then I could use the pc field of struct user_pt_regs (defined in asm/ptrace.h).

Which structure would I use for a 32-bit executable? I see struct user_regs (in sys/user.h) defined as

struct user_regs {
    unsigned long uregs[18];
};

I read the answer for this question (though the author admitted that this was an educated guess) which suggests the use of uregs[15] from struct user_regs. However, would that still work in my case since I actually have a 64-bit system?

1 Answers

Even when running on a 64-bit processor, ptrace hands back the effective 32-bit registers. To confirm this, consider the following two executables (for brevity's sake, I've omitted the header files):

test.c

int main()
{
    printf("PID = %li\n", (long)getpid());
    pause();
    return 0;
}

attacher.c

int main(int argc, char **argv)
{
    int ret = 1;
    unsigned int expected_size;
    pid_t pid;
    struct {
        uint32_t r[18];
    } regs;
    struct iovec iov;

    if ( argc < 2 ) {
        return 1;
    }
    pid = atoi(argv[1]); // Yes, I know I should use strtol.  However, this suffices for this example.
    if ( ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 ) {
        perror("ptrace (ATTACH)");
        return 1;
    }

    iov.iov_base = &regs;
    iov.iov_len = expected_size = sizeof(regs);
    if ( ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) != 0 ) {
        perror("perror (GETREGSET)");
        goto done;
    }
    if ( iov.iov_len != expected_size ) {
        fprintf(stderr, "Unexpected size (%zu instead of %u)\n", iov.iov_len, expected_size);
        goto done;
    }
    printf("PC is 0x%x\n", regs.r[15]);

    ret = 0;

done:
    if ( ptrace(PTRACE_DETACH, pid, NULL, NULL) != 0 ) {
        perror("ptrace (DETACH)");
        ret = 1;
    }
    return ret;
}

I compiled the first into a 32-bit executable and the second into a 64-bit executable.

Taking the output from the attacher, I looked up the address in /proc/<pid>/maps and saw that it lands in the executable section of libc (corresponding to pause).

Related