Search code examples
carmcpu-registersarm64ptrace

Getting/setting registers for arm32 executable on aarch64


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?


Solution

  • 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 and error checking):

    test.c

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

    attacher.c

    int main(int argc, char **argv)
    {
        unsigned int expected_size;
        pid_t pid;
        struct {
            uint32_t r[18];
        } regs;
        struct iovec iov;
    
        pid = atoi(argv[1]); // Yes, I know I should use strtol.  However, this suffices for this example.
        ptrace(PTRACE_ATTACH, pid, NULL, NULL);
    
        iov.iov_base = &regs;
        iov.iov_len = expected_size = sizeof(regs);
        ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov);
        if ( iov.iov_len != expected_size ) {
            fprintf(stderr, "Unexpected size (%zu instead of %u)\n", iov.iov_len, expected_size);
            // abort
        }
        printf("PC is 0x%x\n", regs.r[15]);
    
        ptrace(PTRACE_DETACH, pid, NULL, NULL);
        return 0;
    }
    

    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).