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 | 7 ++++++
kaslr_helper.c | 7 ++++++
netdump.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
netdump.h | 1 +
symbols.c | 20 ++++++++++-----
x86_64.c | 22 ++++++++++++++--
6 files changed, 128 insertions(+), 8 deletions(-)
diff --git a/defs.h b/defs.h
index 2ba058a..84fc85c 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,9 @@ 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);
+ulong kdump_get_idtr(void);
+ulong kdump_get_cr3(void);
#define PRSTATUS_NOTE (1)
#define QEMU_NOTE (2)
diff --git a/kaslr_helper.c b/kaslr_helper.c
index e2da81c..1079863 100644
--- a/kaslr_helper.c
+++ b/kaslr_helper.c
@@ -382,6 +382,13 @@ calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_base)
if (SADUMP_DUMPFILE()) {
idtr = sadump_get_idtr();
cr3 = sadump_get_cr3();
+ } else if (QEMU_MEM_DUMP_NO_VMCOREINFO()) {
+ if (KDUMP_DUMPFILE()) {
+ idtr = kdump_get_idtr();
+ cr3 = kdump_get_cr3();
+ } else {
+ return FALSE;
+ }
} else {
return FALSE;
}
diff --git a/netdump.c b/netdump.c
index 6cf7ca8..e8c2537 100644
--- a/netdump.c
+++ b/netdump.c
@@ -3999,6 +3999,29 @@ no_nt_prstatus_exists:
return pt_regs;
}
+int kdump_phys_base(ulong *phys_base)
+{
+ if (!kdump_kaslr_check())
+ return FALSE;
+
+ if (nd->phys_base) {
+ *phys_base = nd->phys_base;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+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 +4736,59 @@ 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
+static QEMUCPUState * 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];
+}
+
+ulong kdump_get_idtr(void)
+{
+ QEMUCPUState *cpustat;
+
+ cpustat = get_qemucpustate(0);
+ if (!cpustat) {
+ return 0;
+ }
+ return cpustat->idt.base;
+}
+
+ulong kdump_get_cr3(void)
+{
+ QEMUCPUState *cpustat;
+
+ cpustat = get_qemucpustate(0);
+ if (!cpustat) {
+ return 0;
+ }
+ return cpustat->cr[3];
+}
+#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..ed5985a 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,23 @@ x86_64_calc_phys_base(void)
* Get relocation value from whatever dumpfile format is being used.
*/
+ if (QEMU_MEM_DUMP_NO_VMCOREINFO() && KDUMP_DUMPFILE()) {
+ if (kdump_phys_base(&phys_base)) {
+ machdep->machspec->phys_base = phys_base;
+ if (CRASHDEBUG(1))
+ fprintf(fp, "kdump-novmci: phys base: %lx\n",
+ phys_base);
+ } else {
+ machdep->machspec->phys_base = phys_base;
+ if (!x86_64_virt_phys_base())
+ error(WARNING,
+ "cannot determine physical base address:"
+ " defaulting to %lx\n\n",
+ phys_base);
+ }
+ return;
+ }
+
if (DISKDUMP_DUMPFILE()) {
if (diskdump_phys_base(&phys_base)) {
machdep->machspec->phys_base = phys_base;
--
2.14.3