If a QEMU ELF dump from a KASLR-enabled kernel is missing the vmcoreinfo data, try to calculate phys_base and kaslr_offset by using the technique developed by Takao Indoh. --- defs.h | 6 ++++++ kaslr_helper.c | 25 +++++++++++++++++++++++-- netdump.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ netdump.h | 1 + symbols.c | 20 ++++++++++++++------ x86_64.c | 17 +++++++++++++++-- 6 files changed, 113 insertions(+), 10 deletions(-) diff --git a/defs.h b/defs.h index c5252f6..ec2d61c 100644 --- a/defs.h +++ b/defs.h @@ -283,6 +283,8 @@ struct number_option { #define LKCD_KERNTYPES() (pc->flags & KERNTYPES) #define KVMDUMP_DUMPFILE() (pc->flags & KVMDUMP) #define SADUMP_DUMPFILE() (pc->flags & SADUMP) +#define QEMU_MEM_DUMP_NO_VMCOREINFO() \ + ((pc->flags2 & (QEMU_MEM_DUMP_ELF)) && !(pc->flags2 & VMCOREINFO)) #define NETDUMP_LOCAL (0x1) /* netdump_data flags */ #define NETDUMP_REMOTE (0x2) @@ -6226,6 +6228,8 @@ int get_netdump_arch(void); int exist_regs_in_elf_notes(struct task_context *); void *get_regs_from_elf_notes(struct task_context *); void map_cpus_to_prstatus(void); +int kdump_phys_base(ulong *); +int kdump_set_phys_base(ulong); int arm_kdump_phys_base(ulong *); int is_proc_kcore(char *, ulong); int proc_kcore_init(FILE *); @@ -6237,6 +6241,8 @@ void kdump_backup_region_init(void); void display_regs_from_elf_notes(int, FILE *); void display_ELF_note(int, int, void *, FILE *); void *netdump_get_prstatus_percpu(int); +int kdump_kaslr_check(void); +QEMUCPUState *kdump_get_qemucpustate(int); #define PRSTATUS_NOTE (1) #define QEMU_NOTE (2) diff --git a/kaslr_helper.c b/kaslr_helper.c index 65256ab..d02ef72 100644 --- a/kaslr_helper.c +++ b/kaslr_helper.c @@ -236,6 +236,22 @@ get_vmcoreinfo(ulong elfcorehdr, ulong *addr, int *len) return TRUE; } +static int +qemu_get_cr3_idtr(ulong *cr3, ulong *idtr) +{ + QEMUCPUState *cpustat; + + cpustat = kdump_get_qemucpustate(0); + if (!cpustat) { + return FALSE; + } + + *cr3 = cpustat->cr[3]; + *idtr = cpustat->idt.base; + + return TRUE; +} + /* * Check if current kaslr_offset/phys_base is for 1st kernel or 2nd kernel. * If we are in 2nd kernel, get kaslr_offset/phys_base from vmcoreinfo. @@ -379,9 +395,14 @@ calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_base) if (!machine_type("X86_64")) return FALSE; - if (SADUMP_DUMPFILE() && !sadump_get_cr3_idtr(&cr3, &idtr)) { + if (SADUMP_DUMPFILE()) { + if (!sadump_get_cr3_idtr(&cr3, &idtr)) + return FALSE; + } else if (QEMU_MEM_DUMP_NO_VMCOREINFO()) { + if (!qemu_get_cr3_idtr(&cr3, &idtr)) + return FALSE; + } else return FALSE; - } if (st->pti_init_vmlinux || st->kaiser_init_vmlinux) pgd = cr3 & ~(CR3_PCID_MASK|PTI_USER_PGTABLE_MASK); diff --git a/netdump.c b/netdump.c index 6cf7ca8..0f7c884 100644 --- a/netdump.c +++ b/netdump.c @@ -3999,6 +3999,26 @@ no_nt_prstatus_exists: return pt_regs; } +int kdump_phys_base(ulong *phys_base) +{ + if (!kdump_kaslr_check()) + return FALSE; + + *phys_base = nd->phys_base; + + return TRUE; +} + +int kdump_set_phys_base(ulong phys_base) +{ + if (!kdump_kaslr_check()) + return FALSE; + + nd->phys_base = phys_base; + + return TRUE; +} + /* * In case of ARM we need to determine correct PHYS_OFFSET from the kdump file. * This is done by taking lowest physical address (LMA) from given load @@ -4713,3 +4733,37 @@ error(INFO, "%s: backup region is used: %llx\n", typename, backup_offset + total error: error(WARNING, "failed to init kexec backup region\n"); } + +int +kdump_kaslr_check(void) +{ + if (!QEMU_MEM_DUMP_NO_VMCOREINFO()) + return FALSE; + + /* If vmcore has QEMU note, need to calculate kaslr offset */ + if (nd->num_qemu_notes) + return TRUE; + else + return FALSE; +} + +#ifdef X86_64 +QEMUCPUState * kdump_get_qemucpustate(int cpu) +{ + if (cpu >= nd->num_qemu_notes) { + if (CRASHDEBUG(1)) + error(INFO, + "Invalid index for QEMU Note: %d (>= %d)\n", + cpu, nd->num_qemu_notes); + return NULL; + } + + if (!nd->elf64 || (nd->elf64->e_machine != EM_X86_64)) { + if (CRASHDEBUG(1)) + error(INFO, "Only x86_64 64bit is supported.\n"); + return NULL; + } + + return (QEMUCPUState *)nd->nt_qemu_percpu[cpu]; +} +#endif diff --git a/netdump.h b/netdump.h index 87d51ca..5474593 100644 --- a/netdump.h +++ b/netdump.h @@ -78,6 +78,7 @@ struct vmcore_data { ulong backup_src_size; ulonglong backup_offset; ulong arch_data; + ulong phys_base; }; #define DUMP_ELF_INCOMPLETE 0x1 /* dumpfile is incomplete */ diff --git a/symbols.c b/symbols.c index 54aa5b2..348d9ae 100644 --- a/symbols.c +++ b/symbols.c @@ -610,7 +610,11 @@ kaslr_init(void) st->_stext_vmlinux = UNINITIALIZED; } - if (KDUMP_DUMPFILE() || DISKDUMP_DUMPFILE()) { + if (QEMU_MEM_DUMP_NO_VMCOREINFO()) { + if (KDUMP_DUMPFILE() && kdump_kaslr_check()) { + kt->flags2 |= KASLR_CHECK; + } + } else if (KDUMP_DUMPFILE() || DISKDUMP_DUMPFILE()) { if ((string = pc->read_vmcoreinfo("SYMBOL(_stext)"))) { kt->vmcoreinfo._stext_SYMBOL = htol(string, RETURN_ON_ERROR, NULL); @@ -640,7 +644,7 @@ derive_kaslr_offset(bfd *abfd, int dynamic, bfd_byte *start, bfd_byte *end, unsigned long relocate; ulong _stext_relocated; - if (SADUMP_DUMPFILE()) { + if (SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO()) { ulong kaslr_offset = 0; ulong phys_base = 0; @@ -651,8 +655,12 @@ derive_kaslr_offset(bfd *abfd, int dynamic, bfd_byte *start, bfd_byte *end, kt->flags |= RELOC_SET; } - if (phys_base) - sadump_set_phys_base(phys_base); + if (phys_base) { + if (SADUMP_DUMPFILE()) + sadump_set_phys_base(phys_base); + else if (KDUMP_DUMPFILE()) + kdump_set_phys_base(phys_base); + } return; } @@ -3071,7 +3079,7 @@ dump_symbol_table(void) else fprintf(fp, "\n"); - if (SADUMP_DUMPFILE()) { + if (SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO()) { fprintf(fp, "divide_error_vmlinux: %lx\n", st->divide_error_vmlinux); fprintf(fp, " idt_table_vmlinux: %lx\n", st->idt_table_vmlinux); fprintf(fp, "saved_command_line_vmlinux: %lx\n", st->saved_command_line_vmlinux); @@ -12298,7 +12306,7 @@ numeric_forward(const void *P_x, const void *P_y) } } - if (SADUMP_DUMPFILE()) { + if (SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO()) { /* Need for kaslr_offset and phys_base */ if (STREQ(x->name, "divide_error")) st->divide_error_vmlinux = valueof(x); diff --git a/x86_64.c b/x86_64.c index 0d5e150..f6f6c0e 100644 --- a/x86_64.c +++ b/x86_64.c @@ -202,7 +202,7 @@ x86_64_init(int when) machdep->machspec->kernel_image_size = dtol(string, QUIET, NULL); free(string); } - if (SADUMP_DUMPFILE()) + if (SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO()) /* Need for calculation of kaslr_offset and phys_base */ machdep->kvtop = x86_64_kvtop; break; @@ -2220,7 +2220,8 @@ x86_64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbo ulong pte; physaddr_t physpage; - if (SADUMP_DUMPFILE() && !(machdep->flags & KSYMS_START)) { + if ((SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO()) + && !(machdep->flags & KSYMS_START)) { /* * In the case of sadump, to calculate kaslr_offset and * phys_base, kvtop is called during symtab_init(). In this @@ -6631,6 +6632,18 @@ x86_64_calc_phys_base(void) * Get relocation value from whatever dumpfile format is being used. */ + if (QEMU_MEM_DUMP_NO_VMCOREINFO()) { + if (KDUMP_DUMPFILE() && kdump_phys_base(&phys_base)) + machdep->machspec->phys_base = phys_base; + + if (!x86_64_virt_phys_base()) + error(WARNING, + "cannot determine physical base address:" + " defaulting to %lx\n\n", + machdep->machspec->phys_base); + return; + } + if (DISKDUMP_DUMPFILE()) { if (diskdump_phys_base(&phys_base)) { machdep->machspec->phys_base = phys_base; -- 2.14.3 -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility