[Crash-utility] [PATCH 2/2] kdump: if vmcoreinfo is missing, try to infer kaslr offset

Sergio Lopez slp at redhat.com
Thu Mar 15 13:07:49 UTC 2018


If the kdump from a KASLR-enabled kernel is missing the vmcoreinfo
section, try to calculate phys_base and kaslr_offset by using the
technique developed by Takao Indoh.
---
 defs.h         |  7 ++++++
 kaslr_helper.c |  3 +++
 netdump.c      | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 netdump.h      |  1 +
 symbols.c      | 31 +++++++++++++++----------
 x86_64.c       | 14 +++++++++--
 6 files changed, 115 insertions(+), 14 deletions(-)

diff --git a/defs.h b/defs.h
index 18b41d0..883100a 100644
--- a/defs.h
+++ b/defs.h
@@ -6203,6 +6203,10 @@ 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);
+ulong qemu_get_idtr(void);
+ulong qemu_get_cr3(void);
 int arm_kdump_phys_base(ulong *);
 int is_proc_kcore(char *, ulong);
 int proc_kcore_init(FILE *);
@@ -6214,6 +6218,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 vmcore_kaslr_check(void);
+ulong qemu_get_cr3(void);
+ulong qemu_get_idtr(void);
 #define PRSTATUS_NOTE (1)
 #define QEMU_NOTE     (2)
 
diff --git a/kaslr_helper.c b/kaslr_helper.c
index e2da81c..95b856f 100644
--- a/kaslr_helper.c
+++ b/kaslr_helper.c
@@ -382,6 +382,9 @@ calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_base)
 	if (SADUMP_DUMPFILE()) {
 		idtr = sadump_get_idtr();
 		cr3 = sadump_get_cr3();
+	} else if (KDUMP_DUMPFILE()) {
+		idtr = qemu_get_idtr();
+		cr3 = qemu_get_cr3();
 	} else {
 		return FALSE;
 	}
diff --git a/netdump.c b/netdump.c
index 6cf7ca8..46615c0 100644
--- a/netdump.c
+++ b/netdump.c
@@ -3999,6 +3999,66 @@ no_nt_prstatus_exists:
 	return pt_regs;
 }
 
+int kdump_phys_base(ulong *phys_base)
+{
+	if (!vmcore_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 (!vmcore_kaslr_check())
+		return FALSE;
+
+	nd->phys_base = phys_base;
+
+	return TRUE;
+}
+
+#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 qemu_get_idtr(void)
+{
+	QEMUCPUState *cpustat;
+
+	cpustat = get_qemucpustate(0);
+	return cpustat->idt.base;
+}
+
+ulong qemu_get_cr3(void)
+{
+	QEMUCPUState *cpustat;
+
+	cpustat = get_qemucpustate(0);
+	return cpustat->cr[3];
+}
+#endif
+
 /*
  * 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 +4773,16 @@ error(INFO, "%s: backup region is used: %llx\n", typename, backup_offset + total
 error:
 	error(WARNING, "failed to init kexec backup region\n");
 }
+
+int
+vmcore_kaslr_check(void)
+{
+	if (!VMCORE_VALID() || !(pc->flags2 & QEMU_MEM_DUMP_ELF))
+		return FALSE;
+
+	/* If vmcore has QEMU note, need to calculate kaslr offset */
+	if (nd->num_qemu_notes)
+		return TRUE;
+	else
+		return FALSE;
+}
diff --git a/netdump.h b/netdump.h
index dbb054a..1a3e011 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..ddfb325 100644
--- a/symbols.c
+++ b/symbols.c
@@ -622,6 +622,9 @@ kaslr_init(void)
 			free(string);
 			kt->flags2 |= KASLR_CHECK;
 			st->_stext_vmlinux = UNINITIALIZED;
+		} else if (vmcore_kaslr_check()) {
+			kt->flags2 |= KASLR_CHECK;
+			st->_stext_vmlinux = UNINITIALIZED;
 		}
 	}
 
@@ -640,21 +643,25 @@ 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() ||
+	    (KDUMP_DUMPFILE() && kt->vmcoreinfo._stext_SYMBOL == 0)) {
 		ulong kaslr_offset = 0;
 		ulong phys_base = 0;
 
-		calc_kaslr_offset(&kaslr_offset, &phys_base);
+		if (calc_kaslr_offset(&kaslr_offset, &phys_base)) {
+			if (kaslr_offset) {
+				kt->relocate = kaslr_offset * -1;
+				kt->flags |= RELOC_SET;
+			}
 
-		if (kaslr_offset) {
-			kt->relocate = kaslr_offset * -1;
-			kt->flags |= RELOC_SET;
+			if (phys_base) {
+				if (SADUMP_DUMPFILE())
+					sadump_set_phys_base(phys_base);
+				else
+					kdump_set_phys_base(phys_base);
+			}
+			return;
 		}
-
-		if (phys_base)
-			sadump_set_phys_base(phys_base);
-
-		return;
 	}
 
 	if (ACTIVE()) {
@@ -3071,7 +3078,7 @@ dump_symbol_table(void)
 	else
 		fprintf(fp, "\n");
 
-	if (SADUMP_DUMPFILE()) {
+	if (SADUMP_DUMPFILE() || KDUMP_DUMPFILE()) {
 		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 +12305,7 @@ numeric_forward(const void *P_x, const void *P_y)
 		}
 	}
 
-	if (SADUMP_DUMPFILE()) {
+	if (SADUMP_DUMPFILE() || KDUMP_DUMPFILE()) {
 		/* 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..05555c9 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() || KDUMP_DUMPFILE())
 			/* 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() || KDUMP_DUMPFILE()) &&
+	    !(machdep->flags & KSYMS_START)) {
 		/*
 		 * In the case of sadump, to calculate kaslr_offset and
 		 * phys_base, kvtop is called during symtab_init(). In this
@@ -6664,6 +6665,15 @@ x86_64_calc_phys_base(void)
 		return;
 	}
 
+	if (KDUMP_DUMPFILE()) {
+		if (kdump_phys_base(&phys_base)) {
+			machdep->machspec->phys_base = phys_base;
+			if (CRASHDEBUG(1))
+				fprintf(fp, "kdump: phys_base: %lx\n",
+					phys_base);
+		}
+	}
+
 	if (SADUMP_DUMPFILE()) {
 		if (sadump_phys_base(&phys_base)) {
 			machdep->machspec->phys_base = phys_base;
-- 
2.14.3




More information about the Crash-utility mailing list