The PTRACE_GETREGSET API has now existed since Linux 2.6.33. The XSAVE CPU feature should also be sufficiently common to be able to rely on it. With this, define our internal FP state to be the hosts XSAVE data. Add discovery for the hosts XSAVE size and place the FP registers at the end of task_struct so that we can adjust the size at runtime. Next we can implement the regset API on top and update the signal handling as well as ptrace APIs to use them. Also switch coredump creation to use the regset API and finally set HAVE_ARCH_TRACEHOOK. This considerably improves the signal frames. Previously they might not have contained all the registers (i386) and also did not have the sizes and magic values set to the correct values to permit userspace to decode the frame. As a side effect, this will permit UML to run on hosts with newer CPU extensions (such as AMX) that need even more register state. Signed-off-by: Benjamin Berg <benjamin.berg@intel.com> Link: https://patch.msgid.link/20241023094120.4083426-1-benjamin@sipsolutions.net Signed-off-by: Johannes Berg <johannes.berg@intel.com>
94 lines
1.8 KiB
C
94 lines
1.8 KiB
C
/*
|
|
* Copyright (C) 2004 PathScale, Inc
|
|
* Copyright (C) 2004 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
|
* Licensed under the GPL
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ptrace.h>
|
|
#ifdef __i386__
|
|
#include <sys/user.h>
|
|
#endif
|
|
#include <longjmp.h>
|
|
#include <sysdep/ptrace_user.h>
|
|
#include <sys/uio.h>
|
|
#include <asm/sigcontext.h>
|
|
#include <linux/elf.h>
|
|
#include <registers.h>
|
|
#include <sys/mman.h>
|
|
|
|
unsigned long host_fp_size;
|
|
|
|
int get_fp_registers(int pid, unsigned long *regs)
|
|
{
|
|
struct iovec iov = {
|
|
.iov_base = regs,
|
|
.iov_len = host_fp_size,
|
|
};
|
|
|
|
if (ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov) < 0)
|
|
return -errno;
|
|
return 0;
|
|
}
|
|
|
|
int put_fp_registers(int pid, unsigned long *regs)
|
|
{
|
|
struct iovec iov = {
|
|
.iov_base = regs,
|
|
.iov_len = host_fp_size,
|
|
};
|
|
|
|
if (ptrace(PTRACE_SETREGSET, pid, NT_X86_XSTATE, &iov) < 0)
|
|
return -errno;
|
|
return 0;
|
|
}
|
|
|
|
int arch_init_registers(int pid)
|
|
{
|
|
struct iovec iov = {
|
|
/* Just use plenty of space, it does not cost us anything */
|
|
.iov_len = 2 * 1024 * 1024,
|
|
};
|
|
int ret;
|
|
|
|
iov.iov_base = mmap(NULL, iov.iov_len, PROT_WRITE | PROT_READ,
|
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
|
if (iov.iov_base == MAP_FAILED)
|
|
return -ENOMEM;
|
|
|
|
/* GDB has x86_xsave_length, which uses x86_cpuid_count */
|
|
ret = ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov);
|
|
if (ret)
|
|
ret = -errno;
|
|
munmap(iov.iov_base, 2 * 1024 * 1024);
|
|
|
|
host_fp_size = iov.iov_len;
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned long get_thread_reg(int reg, jmp_buf *buf)
|
|
{
|
|
switch (reg) {
|
|
#ifdef __i386__
|
|
case HOST_IP:
|
|
return buf[0]->__eip;
|
|
case HOST_SP:
|
|
return buf[0]->__esp;
|
|
case HOST_BP:
|
|
return buf[0]->__ebp;
|
|
#else
|
|
case HOST_IP:
|
|
return buf[0]->__rip;
|
|
case HOST_SP:
|
|
return buf[0]->__rsp;
|
|
case HOST_BP:
|
|
return buf[0]->__rbp;
|
|
#endif
|
|
default:
|
|
printk(UM_KERN_ERR "get_thread_regs - unknown register %d\n",
|
|
reg);
|
|
return 0;
|
|
}
|
|
}
|