Skip to content

Commit

Permalink
coredump: enable coredump generation on aarch64
Browse files Browse the repository at this point in the history
Add relevant elf header constants and notes for the aarch64 platform
to enable coredump generation.

Signed-off-by: समीर सिंह Sameer Singh <[email protected]>
  • Loading branch information
ss141309 committed Jan 22, 2025
1 parent b7cbd2c commit 6732abe
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 54 deletions.
7 changes: 3 additions & 4 deletions coredump/coredump
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import sys

import criu_coredump

PLATFORMS = ["aarch64", "x86_64"]

def coredump(opts):
generator = criu_coredump.coredump_generator()
Expand All @@ -16,7 +17,6 @@ def coredump(opts):
with open(os.path.realpath(opts['out']) + "/core." + str(pid), 'wb+') as f:
cores[pid].write(f)


def main():
desc = 'CRIU core dump'
parser = argparse.ArgumentParser(description=desc,
Expand All @@ -37,8 +37,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 only supported on %s" % (sys.argv[0], PLATFORMS))
sys.exit(1)

try:
Expand All @@ -48,6 +48,5 @@ def main():
print('Exiting')
sys.exit(1)


if __name__ == '__main__':
main()
164 changes: 121 additions & 43 deletions coredump/criu_coredump/coredump.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io
import sys
import ctypes
import platform

from pycriu import images
from . import elf
Expand Down Expand Up @@ -130,6 +131,10 @@ class coredump_generator:
reg_files = None # reg-files;
pagemaps = {} # pagemap by pid;

thread_info_key = {"aarch64": "ti_aarch64", "x86_64": "thread_info"} # thread info key based on the current arch

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.
Expand Down Expand Up @@ -213,7 +218,7 @@ def _gen_ehdr(self, pid, phdrs):
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())
Expand All @@ -224,6 +229,13 @@ def _gen_ehdr(self, pid, phdrs):

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.
Expand Down Expand Up @@ -332,7 +344,7 @@ def _gen_prstatus(self, pid, tid):
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()
Expand All @@ -345,33 +357,7 @@ def _gen_prstatus(self, pid, tid):
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
Expand All @@ -385,28 +371,65 @@ def _gen_prstatus(self, pid, tid):

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
Expand All @@ -420,6 +443,58 @@ def _gen_fpregset(self, pid, tid):

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.
Expand Down Expand Up @@ -593,8 +668,11 @@ def _gen_thread_notes(self, pid, tid):

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

Expand Down
51 changes: 47 additions & 4 deletions coredump/criu_coredump/elf.py
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -39,6 +42,7 @@

# 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 */
Expand Down Expand Up @@ -119,6 +123,7 @@ class Elf64_auxv_t(ctypes.Structure): # typedef struct
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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -276,12 +281,31 @@ class user_regs_struct(ctypes.Structure): # struct user_regs_struct
) # __extension__ unsigned long long int gs;
]

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
_fields_ = [
Expand Down Expand Up @@ -420,7 +444,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),
Expand All @@ -446,8 +470,27 @@ class user_fpregs_struct(ctypes.Structure): # struct user_fpregs_struct
("padding", ctypes.c_uint * 24),
]

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,
}

elf_fpregset_t = 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.

Expand Down
5 changes: 2 additions & 3 deletions test/others/criu-coredump/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 6732abe

Please sign in to comment.