diff options
author | Pavel Emelyanov <xemul@virtuozzo.com> | 2016-06-01 14:32:00 +0300 |
---|---|---|
committer | Pavel Emelyanov <xemul@virtuozzo.com> | 2016-06-28 13:04:58 +0300 |
commit | 92486c9015e4cecb45c0cd560087b8b9732357d6 (patch) | |
tree | 28d39d877162d4470ce9a25107b021d71f52c7fe /coredump/criu_coredump/coredump.py | |
parent | 308741d059d7997bf6dc1f25760a602aa4d7898d (diff) |
coredump: Rename dir to coredump
Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
Diffstat (limited to 'coredump/criu_coredump/coredump.py')
-rw-r--r-- | coredump/criu_coredump/coredump.py | 829 |
1 files changed, 829 insertions, 0 deletions
diff --git a/coredump/criu_coredump/coredump.py b/coredump/criu_coredump/coredump.py new file mode 100644 index 000000000..df055dcb2 --- /dev/null +++ b/coredump/criu_coredump/coredump.py @@ -0,0 +1,829 @@ +# Functions and classes for creating core dump from criu images. +# Code is inspired by outdated google coredumper(RIP) [1] and +# fs/binfmt_elf.h from Linux kernel [2]. +# +# [1] https://code.google.com/p/google-coredumper/ +# probably already dead, so consider trying: +# https://github.com/efiop/google-coredumper/ +# [2] https://www.kernel.org/ +# +# On my x86_64 systems with fresh kernel ~3.17 core dump looks like: +# +# 1) Elf file header; +# 2) PT_NOTE program header describing notes section; +# 3) PT_LOAD program headers for (almost?) each vma; +# 4) NT_PRPSINFO note with elf_prpsinfo inside; +# 5) An array of notes for each thread of the process: +# NT_PRSTATUS note with elf_prstatus inside; +# NT_FPREGSET note with elf_fpregset inside; +# NT_X86_XSTATE note with x86 extended state using xsave; +# NT_SIGINFO note with siginfo_t inside; +# 6) NT_AUXV note with auxv; +# 7) NT_FILE note with mapped files; +# 8) VMAs themselves; +# +# Or, you can represent it in less details as: +# 1) Elf file header; +# 2) Program table; +# 3) Notes; +# 4) VMAs contents; +# +import io +import elf +import ctypes +from pycriu import images + +# Some memory-related constants +PAGESIZE = 4096 +status = { + "VMA_AREA_NONE" : 0 << 0, + "VMA_AREA_REGULAR" : 1 << 0, + "VMA_AREA_STACK" : 1 << 1, + "VMA_AREA_VSYSCALL" : 1 << 2, + "VMA_AREA_VDSO" : 1 << 3, + "VMA_FORCE_READ" : 1 << 4, + "VMA_AREA_HEAP" : 1 << 5, + "VMA_FILE_PRIVATE" : 1 << 6, + "VMA_FILE_SHARED" : 1 << 7, + "VMA_ANON_SHARED" : 1 << 8, + "VMA_ANON_PRIVATE" : 1 << 9, + "VMA_AREA_SYSVIPC" : 1 << 10, + "VMA_AREA_SOCKET" : 1 << 11, + "VMA_AREA_VVAR" : 1 << 12, + "VMA_AREA_AIORING" : 1 << 13, + "VMA_AREA_UNSUPP" : 1 << 31 +} + +prot = { + "PROT_READ" : 0x1, + "PROT_WRITE" : 0x2, + "PROT_EXEC" : 0x4 +} + +class elf_note: + nhdr = None # Elf_Nhdr; + owner = None # i.e. CORE or LINUX; + data = None # Ctypes structure with note data; + + +class coredump: + """ + A class to keep elf core dump components inside and + functions to properly write them to file. + """ + ehdr = None # Elf ehdr; + phdrs = [] # Array of Phdrs; + notes = [] # Array of elf_notes; + vmas = [] # Array of BytesIO with memory content; + # FIXME keeping all vmas in memory is a bad idea; + + def write(self, f): + """ + Write core dump to file f. + """ + buf = io.BytesIO() + buf.write(self.ehdr) + + for phdr in self.phdrs: + buf.write(phdr) + + for note in self.notes: + buf.write(note.nhdr) + buf.write(note.owner) + buf.write("\0"*(8-len(note.owner))) + buf.write(note.data) + + offset = ctypes.sizeof(elf.Elf64_Ehdr()) + offset += (len(self.vmas) + 1)*ctypes.sizeof(elf.Elf64_Phdr()) + + filesz = 0 + for note in self.notes: + filesz += ctypes.sizeof(note.nhdr) + ctypes.sizeof(note.data) + 8 + + note_align = PAGESIZE - ((offset + filesz) % PAGESIZE) + + if note_align == PAGESIZE: + note_align = 0 + + if note_align != 0: + scratch = (ctypes.c_char * note_align)() + ctypes.memset(ctypes.addressof(scratch), 0, ctypes.sizeof(scratch)) + buf.write(scratch) + + for vma in self.vmas: + buf.write(vma.data) + + buf.seek(0) + f.write(buf.read()) + + +class coredump_generator: + """ + Generate core dump from criu images. + """ + coredumps = {} # coredumps by pid; + + pstree = {} # process info by pid; + cores = {} # cores by pid; + mms = {} # mm by pid; + reg_files = None # reg-files; + pagemaps = {} # pagemap by pid; + + def _img_open_and_strip(self, name, single = False, pid = None): + """ + Load criu image and strip it from magic and redundant list. + """ + path = self._imgs_dir + "/" + name + if pid: + path += "-"+str(pid) + path += ".img" + + with open(path) as f: + img = images.load(f) + + if single: + return img["entries"][0] + else: + return img["entries"] + + + def __call__(self, imgs_dir): + """ + Parse criu images stored in directory imgs_dir to fill core dumps. + """ + self._imgs_dir = imgs_dir + pstree = self._img_open_and_strip("pstree") + + for p in pstree: + pid = p['pid'] + + self.pstree[pid] = p + for tid in p['threads']: + self.cores[tid] = self._img_open_and_strip("core", True, tid) + self.mms[pid] = self._img_open_and_strip("mm", True, pid) + self.pagemaps[pid] = self._img_open_and_strip("pagemap", False, pid) + + self.reg_files = self._img_open_and_strip("reg-files", False) + + for pid in self.pstree: + self.coredumps[pid] = self._gen_coredump(pid) + + return self.coredumps + + + def write(self, coredumps_dir, pid = None): + """ + Write core dumpt to cores_dir directory. Specify pid to choose + core dump of only one process. + """ + for p in self.coredumps: + if pid and p != pid: + continue + with open(coredumps_dir+"/"+"core."+str(p), 'w+') as f: + self.coredumps[p].write(f) + + def _gen_coredump(self, pid): + """ + Generate core dump for pid. + """ + cd = coredump() + + # Generate everything backwards so it is easier to calculate offset. + cd.vmas = self._gen_vmas(pid) + cd.notes = self._gen_notes(pid) + cd.phdrs = self._gen_phdrs(pid, cd.notes, cd.vmas) + cd.ehdr = self._gen_ehdr(pid, cd.phdrs) + + return cd + + def _gen_ehdr(self, pid, phdrs): + """ + Generate elf header for process pid with program headers phdrs. + """ + ehdr = elf.Elf64_Ehdr() + + ctypes.memset(ctypes.addressof(ehdr), 0, ctypes.sizeof(ehdr)) + ehdr.e_ident[elf.EI_MAG0] = elf.ELFMAG0 + ehdr.e_ident[elf.EI_MAG1] = elf.ELFMAG1 + ehdr.e_ident[elf.EI_MAG2] = elf.ELFMAG2 + ehdr.e_ident[elf.EI_MAG3] = elf.ELFMAG3 + ehdr.e_ident[elf.EI_CLASS] = elf.ELFCLASS64 + ehdr.e_ident[elf.EI_DATA] = elf.ELFDATA2LSB + ehdr.e_ident[elf.EI_VERSION] = elf.EV_CURRENT + + ehdr.e_type = elf.ET_CORE + ehdr.e_machine = elf.EM_X86_64 + ehdr.e_version = elf.EV_CURRENT + ehdr.e_phoff = ctypes.sizeof(elf.Elf64_Ehdr()) + ehdr.e_ehsize = ctypes.sizeof(elf.Elf64_Ehdr()) + ehdr.e_phentsize = ctypes.sizeof(elf.Elf64_Phdr()) + #FIXME Case len(phdrs) > PN_XNUM should be handled properly. + # See fs/binfmt_elf.c from linux kernel. + ehdr.e_phnum = len(phdrs) + + return ehdr + + def _gen_phdrs(self, pid, notes, vmas): + """ + Generate program headers for process pid. + """ + phdrs = [] + + offset = ctypes.sizeof(elf.Elf64_Ehdr()) + offset += (len(vmas) + 1)*ctypes.sizeof(elf.Elf64_Phdr()) + + filesz = 0 + for note in notes: + filesz += ctypes.sizeof(note.nhdr) + ctypes.sizeof(note.data) + 8 + + # PT_NOTE + phdr = elf.Elf64_Phdr() + ctypes.memset(ctypes.addressof(phdr), 0, ctypes.sizeof(phdr)) + phdr.p_type = elf.PT_NOTE + phdr.p_offset = offset + phdr.p_filesz = filesz + + phdrs.append(phdr) + + note_align = PAGESIZE - ((offset + filesz) % PAGESIZE) + + if note_align == PAGESIZE: + note_align = 0 + + offset += note_align + + # VMA phdrs + + for vma in vmas: + offset += filesz + filesz = vma.filesz + phdr = elf.Elf64_Phdr() + ctypes.memset(ctypes.addressof(phdr), 0, ctypes.sizeof(phdr)) + phdr.p_type = elf.PT_LOAD + phdr.p_align = PAGESIZE + phdr.p_paddr = 0 + phdr.p_offset = offset + phdr.p_vaddr = vma.start + phdr.p_memsz = vma.memsz + phdr.p_filesz = vma.filesz + phdr.p_flags = vma.flags + + phdrs.append(phdr) + + return phdrs + + def _gen_prpsinfo(self, pid): + """ + Generate NT_PRPSINFO note for process pid. + """ + pstree = self.pstree[pid] + core = self.cores[pid] + + prpsinfo = elf.elf_prpsinfo() + ctypes.memset(ctypes.addressof(prpsinfo), 0, ctypes.sizeof(prpsinfo)) + + # FIXME TASK_ALIVE means that it is either running or sleeping, need to + # teach criu to distinguish them. + TASK_ALIVE = 0x1 + # XXX A bit of confusion here, as in ps "dead" and "zombie" + # state are two separate states, and we use TASK_DEAD for zombies. + TASK_DEAD = 0x2 + TASK_STOPPED = 0x3 + if core["tc"]["task_state"] == TASK_ALIVE: + prpsinfo.pr_state = 0 + if core["tc"]["task_state"] == TASK_DEAD: + prpsinfo.pr_state = 4 + if core["tc"]["task_state"] == TASK_STOPPED: + prpsinfo.pr_state = 3 + # Don't even ask me why it is so, just borrowed from linux + # source and made pr_state match. + prpsinfo.pr_sname = '.' if prpsinfo.pr_state > 5 else "RSDTZW"[prpsinfo.pr_state] + prpsinfo.pr_zomb = 1 if prpsinfo.pr_state == 4 else 0 + prpsinfo.pr_nice = core["thread_core"]["sched_prio"] if "sched_prio" in core["thread_core"] else 0 + prpsinfo.pr_flag = core["tc"]["flags"] + prpsinfo.pr_uid = core["thread_core"]["creds"]["uid"] + prpsinfo.pr_gid = core["thread_core"]["creds"]["gid"] + prpsinfo.pr_pid = pid + prpsinfo.pr_ppid = pstree["ppid"] + prpsinfo.pr_pgrp = pstree["pgid"] + prpsinfo.pr_sid = pstree["sid"] + prpsinfo.pr_fname = core["tc"]["comm"] + prpsinfo.pr_psargs = self._gen_cmdline(pid) + + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 5 + nhdr.n_descsz = ctypes.sizeof(elf.elf_prpsinfo()) + nhdr.n_type = elf.NT_PRPSINFO + + note = elf_note() + note.data = prpsinfo + note.owner = "CORE" + note.nhdr = nhdr + + return note + + 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"] + pstree = self.pstree[pid] + + prstatus = elf.elf_prstatus() + + ctypes.memset(ctypes.addressof(prstatus), 0, ctypes.sizeof(prstatus)) + + #FIXME setting only some of the fields for now. Revisit later. + prstatus.pr_pid = tid + prstatus.pr_ppid = pstree["ppid"] + 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"] + + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 5 + nhdr.n_descsz = ctypes.sizeof(elf.elf_prstatus()) + nhdr.n_type = elf.NT_PRSTATUS + + note = elf_note() + note.data = prstatus + note.owner = "CORE" + note.nhdr = nhdr + + return note + + 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"] + + 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"]) + #fpregset.padding = regs["padding"] unused + + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 5 + nhdr.n_descsz = ctypes.sizeof(elf.elf_fpregset_t()) + nhdr.n_type = elf.NT_FPREGSET + + note = elf_note() + note.data = fpregset + note.owner = "CORE" + note.nhdr = nhdr + + return note + + def _gen_x86_xstate(self, pid, tid): + """ + Generate NT_X86_XSTATE note for thread tid of process pid. + """ + core = self.cores[tid] + fpregs = core["thread_info"]["fpregs"] + + data = elf.elf_xsave_struct() + ctypes.memset(ctypes.addressof(data), 0, ctypes.sizeof(data)) + + data.i387.cwd = fpregs["cwd"] + data.i387.swd = fpregs["swd"] + data.i387.twd = fpregs["twd"] + data.i387.fop = fpregs["fop"] + data.i387.rip = fpregs["rip"] + data.i387.rdp = fpregs["rdp"] + data.i387.mxcsr = fpregs["mxcsr"] + data.i387.mxcsr_mask = fpregs["mxcsr_mask"] + data.i387.st_space = (ctypes.c_uint * len(fpregs["st_space"]))(*fpregs["st_space"]) + data.i387.xmm_space = (ctypes.c_uint * len(fpregs["xmm_space"]))(*fpregs["xmm_space"]) + + if "xsave" in fpregs: + data.xsave_hdr.xstate_bv = fpregs["xsave"]["xstate_bv"] + data.ymmh.ymmh_space = (ctypes.c_uint * len(fpregs["xsave"]["ymmh_space"]))(*fpregs["xsave"]["ymmh_space"]) + + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 6 + nhdr.n_descsz = ctypes.sizeof(data) + nhdr.n_type = elf.NT_X86_XSTATE + + note = elf_note() + note.data = data + note.owner = "LINUX" + note.nhdr = nhdr + + return note + + def _gen_siginfo(self, pid, tid): + """ + Generate NT_SIGINFO note for thread tid of process pid. + """ + siginfo = elf.siginfo_t() + # FIXME zeroify everything for now + ctypes.memset(ctypes.addressof(siginfo), 0, ctypes.sizeof(siginfo)) + + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 5 + nhdr.n_descsz = ctypes.sizeof(elf.siginfo_t()) + nhdr.n_type = elf.NT_SIGINFO + + note = elf_note() + note.data = siginfo + note.owner = "CORE" + note.nhdr = nhdr + + return note + + def _gen_auxv(self, pid): + """ + Generate NT_AUXV note for thread tid of process pid. + """ + mm = self.mms[pid] + num_auxv = len(mm["mm_saved_auxv"])/2 + + class elf_auxv(ctypes.Structure): + _fields_ = [("auxv", elf.Elf64_auxv_t*num_auxv)] + + auxv = elf_auxv() + for i in range(num_auxv): + auxv.auxv[i].a_type = mm["mm_saved_auxv"][i] + auxv.auxv[i].a_val = mm["mm_saved_auxv"][i+1] + + nhdr = elf.Elf64_Nhdr() + nhdr.n_namesz = 5 + nhdr.n_descsz = ctypes.sizeof(elf_auxv()) + nhdr.n_type = elf.NT_AUXV + + note = elf_note() + note.data = auxv + note.owner = "CORE" + note.nhdr = nhdr + + return note + + def _gen_files(self, pid): + """ + Generate NT_FILE note for process pid. + """ + mm = self.mms[pid] + + class mmaped_file_info: + start = None + end = None + file_ofs = None + name = None + + infos = [] + for vma in mm["vmas"]: + if vma["shmid"] == 0: + # shmid == 0 means that it is not a file + continue + + shmid = vma["shmid"] + size = vma["end"] - vma["start"] + off = vma["pgoff"]/PAGESIZE + + files = self.reg_files + fname = filter(lambda x: x["id"] == shmid, files)[0]["name"] + + info = mmaped_file_info() + info.start = vma["start"] + info.end = vma["end"] + info.file_ofs = off + info.name = fname + + infos.append(info) + + # /* + # * Format of NT_FILE note: + # * + # * long count -- how many files are mapped + # * long page_size -- units for file_ofs + # * array of [COUNT] elements of + # * long start + # * long end + # * long file_ofs + # * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... + # */ + fields = [] + fields.append(("count", ctypes.c_long)) + fields.append(("page_size", ctypes.c_long)) + for i in range(len(infos)): + fields.append(("start"+str(i), ctypes.c_long)) + fields.append(("end"+str(i), ctypes.c_long)) + fields.append(("file_ofs"+str(i), ctypes.c_long)) + for i in range(len(infos)): + fields.append(("name"+str(i), ctypes.c_char*(len(infos[i].name)+1))) + + class elf_files(ctypes.Structure): + _fields_ = fields + + data = elf_files() + data.count = len(infos) + data.page_size = PAGESIZE + for i in range(len(infos)): + info = infos[i] + setattr(data, "start"+str(i), info.start) + setattr(data, "end"+str(i), info.end) + setattr(data, "file_ofs"+str(i), info.file_ofs) + setattr(data, "name"+str(i), info.name) + + nhdr = elf.Elf64_Nhdr() + + nhdr.n_namesz = 5#XXX strlen + 1 + nhdr.n_descsz = ctypes.sizeof(elf_files()) + nhdr.n_type = elf.NT_FILE + + note = elf_note() + note.nhdr = nhdr + note.owner = "CORE" + note.data = data + + return note + + def _gen_thread_notes(self, pid, tid): + notes = [] + + 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)) + + return notes + + def _gen_notes(self, pid): + """ + Generate notes for core dump of process pid. + """ + notes = [] + + notes.append(self._gen_prpsinfo(pid)) + + threads = self.pstree[pid]["threads"] + + # Main thread first + notes += self._gen_thread_notes(pid, pid) + + # Then other threads + for tid in threads: + if tid == pid: + continue + + notes += self._gen_thread_notes(pid, tid) + + notes.append(self._gen_auxv(pid)) + notes.append(self._gen_files(pid)) + + return notes + + def _get_page(self, pid, page_no): + """ + Try to find memory page page_no in pages.img image for process pid. + """ + pagemap = self.pagemaps[pid] + + # First entry is pagemap_head, we will need it later to open + # proper pages.img. + pages_id = pagemap[0]["pages_id"] + off = 0# in pages + for m in pagemap[1:]: + found = False + for i in xrange(m["nr_pages"]): + if m["vaddr"] + i*PAGESIZE == page_no*PAGESIZE: + found = True + break + off += 1 + + if not found: + continue + + if "in_parent" in m and m["in_parent"] == True: + ppid = self.pstree[pid]["ppid"] + return self._get_page(ppid, page_no) + else: + with open(self._imgs_dir+"/"+"pages-"+str(pages_id)+".img") as f: + f.seek(off*PAGESIZE) + return f.read(PAGESIZE) + + return None + + def _gen_mem_chunk(self, pid, vma, size): + """ + Obtain vma contents for process pid. + """ + f = None + + if size == 0: + return "" + + if vma["status"] & status["VMA_AREA_VVAR"]: + #FIXME this is what gdb does, as vvar vma + # is not readable from userspace? + return "\0"*size + elif vma["status"] & status["VMA_AREA_VSYSCALL"]: + #FIXME need to dump it with criu or read from + # current process. + return "\0"*size + + if vma["status"] & status["VMA_FILE_SHARED"] or \ + vma["status"] & status["VMA_FILE_PRIVATE"]: + # Open file before iterating vma pages + shmid = vma["shmid"] + off = vma["pgoff"] + + files = self.reg_files + fname = filter(lambda x: x["id"] == shmid, files)[0]["name"] + + f = open(fname) + f.seek(off) + + start = vma["start"] + end = vma["start"] + size + + # Split requested memory chunk into pages, so it could be + # pictured as: + # + # "----" -- part of page with memory outside of our vma; + # "XXXX" -- memory from our vma; + # + # Start page Pages in the middle End page + # [-----XXXXX]...[XXXXXXXXXX][XXXXXXXXXX]...[XXX-------] + # + # Each page could be found in pages.img or in a standalone + # file described by shmid field in vma entry and + # corresponding entry in reg-files.img. + # For VMA_FILE_PRIVATE vma, unchanged pages are taken from + # a file, and changed ones -- from pages.img. + # Finally, if no page is found neither in pages.img nor + # in file, hole in inserted -- a page filled with zeroes. + start_page = start/PAGESIZE + end_page = end/PAGESIZE + + buf = "" + for page_no in range(start_page, end_page+1): + page = None + + # Search for needed page in pages.img and reg-files.img + # and choose appropriate. + page_mem = self._get_page(pid, page_no) + + if f != None: + page = f.read(PAGESIZE) + + if page_mem != None: + # Page from pages.img has higher priority + # than one from maped file on disk. + page = page_mem + + if page == None: + # Hole + page = PAGESIZE*"\0" + + # If it is a start or end page, we need to read + # only part of it. + if page_no == start_page: + n_skip = start - page_no*PAGESIZE + if start_page == end_page: + n_read = size + else: + n_read = PAGESIZE - n_skip + elif page_no == end_page: + n_skip = 0 + n_read = end - page_no*PAGESIZE + else: + n_skip = 0 + n_read = PAGESIZE + + buf += page[n_skip : n_skip + n_read] + + # Don't forget to close file. + if f != None: + f.close() + + return buf + + def _gen_cmdline(self, pid): + """ + Generate full command with arguments. + """ + mm = self.mms[pid] + + vma = {} + vma["start"] = mm["mm_arg_start"] + vma["end"] = mm["mm_arg_end"] + # Dummy flags and status. + vma["flags"] = 0 + vma["status"] = 0 + size = vma["end"] - vma["start"] + + chunk = self._gen_mem_chunk(pid, vma, size) + + # Replace all '\0's with spaces. + return chunk.replace('\0', ' ') + + def _get_vma_dump_size(self, vma): + """ + Calculate amount of vma to put into core dump. + """ + if vma["status"] & status["VMA_AREA_VVAR"] or \ + vma["status"] & status["VMA_AREA_VSYSCALL"] or \ + vma["status"] & status["VMA_AREA_VDSO"]: + size = vma["end"] - vma["start"] + elif vma["prot"] == 0: + size = 0 + elif vma["prot"] & prot["PROT_READ"] and \ + vma["prot"] & prot["PROT_EXEC"]: + size = PAGESIZE + elif vma["status"] & status["VMA_ANON_SHARED"] or \ + vma["status"] & status["VMA_FILE_SHARED"] or \ + vma["status"] & status["VMA_ANON_PRIVATE"] or \ + vma["status"] & status["VMA_FILE_PRIVATE"]: + size = vma["end"] - vma["start"] + else: + size = 0 + + return size + + def _get_vma_flags(self, vma): + """ + Convert vma flags int elf flags. + """ + flags = 0 + + if vma['prot'] & prot["PROT_READ"]: + flags = flags | elf.PF_R + + if vma['prot'] & prot["PROT_WRITE"]: + flags = flags | elf.PF_W + + if vma['prot'] & prot["PROT_EXEC"]: + flags = flags | elf.PF_X + + return flags + + def _gen_vmas(self, pid): + """ + Generate vma contents for core dump for process pid. + """ + mm = self.mms[pid] + + class vma_class: + data = None + filesz = None + memsz = None + flags = None + start = None + + vmas = [] + for vma in mm["vmas"]: + size = self._get_vma_dump_size(vma) + + chunk = self._gen_mem_chunk(pid, vma, size) + + v = vma_class() + v.filesz = self._get_vma_dump_size(vma) + v.data = self._gen_mem_chunk(pid, vma, v.filesz) + v.memsz = vma["end"] - vma["start"] + v.start = vma["start"] + v.flags = self._get_vma_flags(vma) + + vmas.append(v) + + return vmas |