coredump: enable coredump generation on aarch64

Add relevant elf header constants and notes for the aarch64 platform
to enable coredump generation.

Signed-off-by: समीर सिंह Sameer Singh <lumarzeli30@gmail.com>
This commit is contained in:
समीर सिंह Sameer Singh 2025-01-23 04:07:42 +05:30 committed by Andrei Vagin
parent 030fa4affd
commit da90b33a42
4 changed files with 178 additions and 52 deletions

View file

@ -6,6 +6,8 @@ import sys
import criu_coredump
PLATFORMS = ["aarch64", "x86_64"]
def coredump(opts):
generator = criu_coredump.coredump_generator()
@ -37,8 +39,8 @@ def main():
opts = vars(parser.parse_args())
if platform.machine() != 'x86_64':
print('ERROR: %s only supported on x86_64' % sys.argv[0])
if platform.machine() not in PLATFORMS:
print("ERROR: %s is only supported on: %s" % (sys.argv[0], ', '.join(PLATFORMS)))
sys.exit(1)
try:

View file

@ -31,6 +31,7 @@
import io
import sys
import ctypes
import platform
from pycriu import images
from . import elf
@ -130,6 +131,11 @@ class coredump_generator:
reg_files = None # reg-files;
pagemaps = {} # pagemap by pid;
# thread info key based on the current arch
thread_info_key = {"aarch64": "ti_aarch64", "x86_64": "thread_info"}
machine = platform.machine() # current arch
def _img_open_and_strip(self, name, single=False, pid=None):
"""
Load criu image and strip it from magic and redundant list.
@ -213,7 +219,7 @@ class coredump_generator:
ehdr.e_ident[elf.EI_VERSION] = elf.EV_CURRENT
ehdr.e_type = elf.ET_CORE
ehdr.e_machine = elf.EM_X86_64
ehdr.e_machine = self._get_e_machine()
ehdr.e_version = elf.EV_CURRENT
ehdr.e_phoff = ctypes.sizeof(elf.Elf64_Ehdr())
ehdr.e_ehsize = ctypes.sizeof(elf.Elf64_Ehdr())
@ -224,6 +230,13 @@ class coredump_generator:
return ehdr
def _get_e_machine(self):
"""
Get the e_machine field based on the current architecture.
"""
e_machine_dict = {"aarch64": elf.EM_AARCH64, "x86_64": elf.EM_X86_64}
return e_machine_dict[self.machine]
def _gen_phdrs(self, pid, notes, vmas):
"""
Generate program headers for process pid.
@ -332,7 +345,7 @@ class coredump_generator:
Generate NT_PRSTATUS note for thread tid of process pid.
"""
core = self.cores[tid]
regs = core["thread_info"]["gpregs"]
regs = self._get_gpregs(core)
pstree = self.pstree[pid]
prstatus = elf.elf_prstatus()
@ -345,33 +358,7 @@ class coredump_generator:
prstatus.pr_pgrp = pstree["pgid"]
prstatus.pr_sid = pstree["sid"]
prstatus.pr_reg.r15 = regs["r15"]
prstatus.pr_reg.r14 = regs["r14"]
prstatus.pr_reg.r13 = regs["r13"]
prstatus.pr_reg.r12 = regs["r12"]
prstatus.pr_reg.rbp = regs["bp"]
prstatus.pr_reg.rbx = regs["bx"]
prstatus.pr_reg.r11 = regs["r11"]
prstatus.pr_reg.r10 = regs["r10"]
prstatus.pr_reg.r9 = regs["r9"]
prstatus.pr_reg.r8 = regs["r8"]
prstatus.pr_reg.rax = regs["ax"]
prstatus.pr_reg.rcx = regs["cx"]
prstatus.pr_reg.rdx = regs["dx"]
prstatus.pr_reg.rsi = regs["si"]
prstatus.pr_reg.rdi = regs["di"]
prstatus.pr_reg.orig_rax = regs["orig_ax"]
prstatus.pr_reg.rip = regs["ip"]
prstatus.pr_reg.cs = regs["cs"]
prstatus.pr_reg.eflags = regs["flags"]
prstatus.pr_reg.rsp = regs["sp"]
prstatus.pr_reg.ss = regs["ss"]
prstatus.pr_reg.fs_base = regs["fs_base"]
prstatus.pr_reg.gs_base = regs["gs_base"]
prstatus.pr_reg.ds = regs["ds"]
prstatus.pr_reg.es = regs["es"]
prstatus.pr_reg.fs = regs["fs"]
prstatus.pr_reg.gs = regs["gs"]
self._set_pr_regset(prstatus.pr_reg, regs)
nhdr = elf.Elf64_Nhdr()
nhdr.n_namesz = 5
@ -385,28 +372,64 @@ class coredump_generator:
return note
def _get_gpregs(self, core):
"""
Get the general purpose registers based on the current architecture.
"""
thread_info_key = self.thread_info_key[self.machine]
thread_info = core[thread_info_key]
return thread_info["gpregs"]
def _set_pr_regset(self, pr_reg, regs):
"""
Set the pr_reg struct based on the current architecture.
"""
if self.machine == "aarch64":
pr_reg.regs = (ctypes.c_ulonglong * len(regs["regs"]))(*regs["regs"])
pr_reg.sp = regs["sp"]
pr_reg.pc = regs["pc"]
pr_reg.pstate = regs["pstate"]
elif self.machine == "x86_64":
pr_reg.r15 = regs["r15"]
pr_reg.r14 = regs["r14"]
pr_reg.r13 = regs["r13"]
pr_reg.r12 = regs["r12"]
pr_reg.rbp = regs["bp"]
pr_reg.rbx = regs["bx"]
pr_reg.r11 = regs["r11"]
pr_reg.r10 = regs["r10"]
pr_reg.r9 = regs["r9"]
pr_reg.r8 = regs["r8"]
pr_reg.rax = regs["ax"]
pr_reg.rcx = regs["cx"]
pr_reg.rdx = regs["dx"]
pr_reg.rsi = regs["si"]
pr_reg.rdi = regs["di"]
pr_reg.orig_rax = regs["orig_ax"]
pr_reg.rip = regs["ip"]
pr_reg.cs = regs["cs"]
pr_reg.eflags = regs["flags"]
pr_reg.rsp = regs["sp"]
pr_reg.ss = regs["ss"]
pr_reg.fs_base = regs["fs_base"]
pr_reg.gs_base = regs["gs_base"]
pr_reg.ds = regs["ds"]
pr_reg.es = regs["es"]
pr_reg.fs = regs["fs"]
pr_reg.gs = regs["gs"]
def _gen_fpregset(self, pid, tid):
"""
Generate NT_FPREGSET note for thread tid of process pid.
"""
core = self.cores[tid]
regs = core["thread_info"]["fpregs"]
regs = self._get_fpregs(core)
fpregset = elf.elf_fpregset_t()
ctypes.memset(ctypes.addressof(fpregset), 0, ctypes.sizeof(fpregset))
fpregset.cwd = regs["cwd"]
fpregset.swd = regs["swd"]
fpregset.ftw = regs["twd"]
fpregset.fop = regs["fop"]
fpregset.rip = regs["rip"]
fpregset.rdp = regs["rdp"]
fpregset.mxcsr = regs["mxcsr"]
fpregset.mxcr_mask = regs["mxcsr_mask"]
fpregset.st_space = (ctypes.c_uint * len(regs["st_space"]))(
*regs["st_space"])
fpregset.xmm_space = (ctypes.c_uint * len(regs["xmm_space"]))(
*regs["xmm_space"])
self._set_fpregset(fpregset, regs)
nhdr = elf.Elf64_Nhdr()
nhdr.n_namesz = 5
@ -420,6 +443,58 @@ class coredump_generator:
return note
def _get_fpregs(self, core):
"""
Get the floating point register dictionary based on the current architecture.
"""
fpregs_key_dict = {"aarch64": "fpsimd", "x86_64": "fpregs"}
fpregs_key = fpregs_key_dict[self.machine]
thread_info_key = self.thread_info_key[self.machine]
return core[thread_info_key][fpregs_key]
def _set_fpregset(self, fpregset, regs):
"""
Set the fpregset struct based on the current architecture.
"""
if self.machine == "aarch64":
fpregset.vregs = (ctypes.c_ulonglong * len(regs["vregs"]))(*regs["vregs"])
fpregset.fpsr = regs["fpsr"]
fpregset.fpcr = regs["fpcr"]
elif self.machine == "x86_64":
fpregset.cwd = regs["cwd"]
fpregset.swd = regs["swd"]
fpregset.ftw = regs["twd"]
fpregset.fop = regs["fop"]
fpregset.rip = regs["rip"]
fpregset.rdp = regs["rdp"]
fpregset.mxcsr = regs["mxcsr"]
fpregset.mxcr_mask = regs["mxcsr_mask"]
fpregset.st_space = (ctypes.c_uint * len(regs["st_space"]))(
*regs["st_space"])
fpregset.xmm_space = (ctypes.c_uint * len(regs["xmm_space"]))(
*regs["xmm_space"])
def _gen_arm_tls(self, tid):
"""
Generate NT_ARM_TLS note for thread tid of process pid.
"""
core = self.cores[tid]
tls = ctypes.c_ulonglong(core["ti_aarch64"]["tls"])
nhdr = elf.Elf64_Nhdr()
nhdr.n_namesz = 6
nhdr.n_descsz = ctypes.sizeof(ctypes.c_ulonglong)
nhdr.n_type = elf.NT_ARM_TLS
note = elf_note()
note.data = tls
note.owner = b"LINUX"
note.nhdr = nhdr
return note
def _gen_x86_xstate(self, pid, tid):
"""
Generate NT_X86_XSTATE note for thread tid of process pid.
@ -593,8 +668,11 @@ class coredump_generator:
notes.append(self._gen_prstatus(pid, tid))
notes.append(self._gen_fpregset(pid, tid))
notes.append(self._gen_x86_xstate(pid, tid))
notes.append(self._gen_siginfo(pid, tid))
if self.machine == "aarch64":
notes.append(self._gen_arm_tls(tid))
elif self.machine == "x86_64":
notes.append(self._gen_x86_xstate(pid, tid))
return notes

View file

@ -1,5 +1,8 @@
# Define structures and constants for generating elf file.
import ctypes
import platform
MACHINE = platform.machine()
Elf64_Half = ctypes.c_uint16 # typedef uint16_t Elf64_Half;
Elf64_Word = ctypes.c_uint32 # typedef uint32_t Elf64_Word;
@ -39,6 +42,7 @@ ET_CORE = 4 # #define ET_CORE 4 /* Core file */
# Legal values for e_machine (architecture).
EM_X86_64 = 62 # #define EM_X86_64 62 /* AMD x86-64 architecture */
EM_AARCH64 = 183 # #define EM_AARCH64 183 /* ARM AARCH64 */
# Legal values for e_version (version).
EV_CURRENT = 1 # #define EV_CURRENT 1 /* Current version */
@ -119,6 +123,7 @@ NT_AUXV = 6 # #define NT_AUXV 6 /* Contains copy of auxv array */
NT_SIGINFO = 0x53494749 # #define NT_SIGINFO 0x53494749 /* Contains copy of siginfo_t, size might increase */
NT_FILE = 0x46494c45 # #define NT_FILE 0x46494c45 /* Contains information about mapped files */
NT_X86_XSTATE = 0x202 # #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
NT_ARM_TLS = 0x401 # #define NT_ARM_TLS 0x401 /* ARM TLS register */
class Elf64_Nhdr(ctypes.Structure): # typedef struct
@ -218,7 +223,7 @@ class timeval(ctypes.Structure): # struct timeval
]
class user_regs_struct(ctypes.Structure): # struct user_regs_struct
class x86_64_user_regs_struct(ctypes.Structure): # struct x86_64_user_regs_struct
_fields_ = [
("r15",
ctypes.c_ulonglong), # __extension__ unsigned long long int r15;
@ -277,10 +282,31 @@ class user_regs_struct(ctypes.Structure): # struct user_regs_struct
]
class aarch64_user_regs_struct(ctypes.Structure): # struct aarch64_user_regs_struct
_fields_ = [
("regs",
ctypes.c_ulonglong * 31), # unsigned long long int regs[31];
("sp",
ctypes.c_ulonglong), # unsigned long long int sp;
("pc",
ctypes.c_ulonglong), # unsigned long long int pc;
("pstate",
ctypes.c_ulonglong), # unsigned long long int pstate;
]
# elf_greg_t = ctypes.c_ulonglong
# ELF_NGREG = ctypes.sizeof(user_regs_struct)/ctypes.sizeof(elf_greg_t)
# elf_gregset_t = elf_greg_t*ELF_NGREG
elf_gregset_t = user_regs_struct
user_regs_dict = {
"aarch64": aarch64_user_regs_struct,
"x86_64": x86_64_user_regs_struct,
}
try:
elf_gregset_t = user_regs_dict[MACHINE]
except KeyError:
raise ValueError("Current architecture %s is not supported." % MACHINE)
class elf_prstatus(ctypes.Structure): # struct elf_prstatus
@ -420,7 +446,7 @@ class elf_prpsinfo(ctypes.Structure): # struct elf_prpsinfo
]
class user_fpregs_struct(ctypes.Structure): # struct user_fpregs_struct
class x86_64_user_fpregs_struct(ctypes.Structure): # struct x86_64_user_fpregs_struct
_fields_ = [
# unsigned short int cwd;
("cwd", ctypes.c_ushort),
@ -447,7 +473,28 @@ class user_fpregs_struct(ctypes.Structure): # struct user_fpregs_struct
]
elf_fpregset_t = user_fpregs_struct
class aarch64_user_fpregs_struct(ctypes.Structure): # struct aarch64_user_fpregs_struct
_fields_ = [
# unsigned long long int vregs[64];
("vregs", ctypes.c_ulonglong * 64),
# unsigned int fpsr;
("fpsr", ctypes.c_uint),
# unsigned int fpcr;
("fpcr", ctypes.c_uint),
# unsigned int padding[2];
("padding", ctypes.c_uint * 2),
]
user_fpregs_dict = {
"aarch64": aarch64_user_fpregs_struct,
"x86_64": x86_64_user_fpregs_struct,
}
try:
elf_fpregset_t = user_fpregs_dict[MACHINE]
except KeyError:
raise ValueError("Current architecture %s is not supported." % MACHINE)
# siginfo_t related constants.

View file

@ -45,9 +45,8 @@ function run_test {
UNAME_M=$(uname -m)
if [ "$UNAME_M" != "x86_64" ]; then
# the criu-coredump script is only x86_64 aware
echo "criu-coredump only support x86_64. skipping."
if [[ "$UNAME_M" != "aarch64" && "$UNAME_M" != "x86_64" ]]; then
echo "criu-coredump only supports aarch64 and x86_64. skipping."
exit 0
fi