svn commit: r362458 - in head/sys: amd64/amd64 arm/arm arm64/arm64 ddb dev/ksyms i386/i386 kern mips/mips powerpc/powerpc

Brandon Bergren bdragon at FreeBSD.org
Sun Jun 21 03:39:29 UTC 2020


Author: bdragon
Date: Sun Jun 21 03:39:26 2020
New Revision: 362458
URL: https://svnweb.freebsd.org/changeset/base/362458

Log:
  [PowerPC] More relocation fixes
  
  It turns out relocating the symbol table itself can cause issues, like fbt
  crashing because it applies the offsets to the kernel twice.
  
  This had been previously brought up in rS333447 when the stoffs hack was
  added, but I had been unaware of this and reimplemented symtab relocation.
  
  Instead of relocating the symbol table, keep track of the relocation base
  in ddb, so the ddb symbols behave like the kernel linker-provided symbols.
  
  This is intended to be NFC on platforms other than PowerPC, which do not
  use fully relocatable kernels. (The relbase will always be 0)
  
   * Remove the rest of the stoffs hack.
   * Remove my half-baked displace_symbol_table() function.
   * Extend ddb initialization to cope with having a relocation offset on the
     kernel symbol table.
   * Fix my kernel-as-initrd hack to work with booke64 by using a temporary
     mapping to access the data.
   * Fix another instance of __powerpc__ that is actually RELOCATABLE_KERNEL.
   * Change the behavior or X_db_symbol_values to apply the relocation base
     when updating valp, to match link_elf_symbol_values() behavior.
  
  Reviewed by:	jhibbits
  Sponsored by:	Tag1 Consulting, Inc.
  Differential Revision:	https://reviews.freebsd.org/D25223

Modified:
  head/sys/amd64/amd64/machdep.c
  head/sys/arm/arm/machdep_boot.c
  head/sys/arm64/arm64/machdep_boot.c
  head/sys/ddb/db_main.c
  head/sys/ddb/ddb.h
  head/sys/dev/ksyms/ksyms.c
  head/sys/i386/i386/machdep.c
  head/sys/kern/link_elf.c
  head/sys/mips/mips/machdep.c
  head/sys/powerpc/powerpc/machdep.c

Modified: head/sys/amd64/amd64/machdep.c
==============================================================================
--- head/sys/amd64/amd64/machdep.c	Sun Jun 21 02:49:56 2020	(r362457)
+++ head/sys/amd64/amd64/machdep.c	Sun Jun 21 03:39:26 2020	(r362458)
@@ -1508,7 +1508,7 @@ native_parse_preload_data(u_int64_t modulep)
 #ifdef DDB
 	ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, uintptr_t);
 	ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t);
-	db_fetch_ksymtab(ksym_start, ksym_end);
+	db_fetch_ksymtab(ksym_start, ksym_end, 0);
 #endif
 	efi_systbl_phys = MD_FETCH(kmdp, MODINFOMD_FW_HANDLE, vm_paddr_t);
 

Modified: head/sys/arm/arm/machdep_boot.c
==============================================================================
--- head/sys/arm/arm/machdep_boot.c	Sun Jun 21 02:49:56 2020	(r362457)
+++ head/sys/arm/arm/machdep_boot.c	Sun Jun 21 03:39:26 2020	(r362458)
@@ -302,7 +302,7 @@ freebsd_parse_boot_param(struct arm_boot_params *abp)
 #ifdef DDB
 	ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, uintptr_t);
 	ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t);
-	db_fetch_ksymtab(ksym_start, ksym_end);
+	db_fetch_ksymtab(ksym_start, ksym_end, 0);
 #endif
 	return lastaddr;
 }

Modified: head/sys/arm64/arm64/machdep_boot.c
==============================================================================
--- head/sys/arm64/arm64/machdep_boot.c	Sun Jun 21 02:49:56 2020	(r362457)
+++ head/sys/arm64/arm64/machdep_boot.c	Sun Jun 21 03:39:26 2020	(r362458)
@@ -208,7 +208,7 @@ freebsd_parse_boot_param(struct arm64_bootparams *abp)
 #ifdef DDB
 	ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, uintptr_t);
 	ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t);
-	db_fetch_ksymtab(ksym_start, ksym_end);
+	db_fetch_ksymtab(ksym_start, ksym_end, 0);
 #endif
 	return (lastaddr);
 }

Modified: head/sys/ddb/db_main.c
==============================================================================
--- head/sys/ddb/db_main.c	Sun Jun 21 02:49:56 2020	(r362457)
+++ head/sys/ddb/db_main.c	Sun Jun 21 03:39:26 2020	(r362458)
@@ -48,6 +48,14 @@ __FBSDID("$FreeBSD$");
 #include <ddb/db_command.h>
 #include <ddb/db_sym.h>
 
+struct db_private {
+	char*		strtab;
+	vm_offset_t	relbase;
+};
+typedef struct db_private *db_private_t;
+
+#define DB_PRIVATE(x) ((db_private_t)(x->private))
+
 SYSCTL_NODE(_debug, OID_AUTO, ddb, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
     "DDB settings");
 
@@ -64,7 +72,8 @@ KDB_BACKEND(ddb, db_init, db_trace_self_wrapper, db_tr
  * the symtab and strtab in memory. This is used when loaded from
  * boot loaders different than the native one (like Xen).
  */
-vm_offset_t ksymtab, kstrtab, ksymtab_size;
+vm_offset_t ksymtab, kstrtab, ksymtab_size, ksymtab_relbase;
+static struct db_private ksymtab_private;
 
 bool
 X_db_line_at_pc(db_symtab_t *symtab, c_db_sym_t sym, char **file, int *line,
@@ -86,7 +95,8 @@ X_db_lookup(db_symtab_t *symtab, const char *symbol)
 		sym = (Elf_Sym *)symtab->start;
 		while ((char *)sym < symtab->end) {
 			if (sym->st_name != 0 &&
-			    !strcmp(symtab->private + sym->st_name, symbol))
+			    !strcmp(DB_PRIVATE(symtab)->strtab +
+			    sym->st_name, symbol))
 				return ((c_db_sym_t)sym);
 			sym++;
 		}
@@ -101,7 +111,7 @@ X_db_search_symbol(db_symtab_t *symtab, db_addr_t off,
 	c_linker_sym_t lsym;
 	Elf_Sym *sym, *match;
 	unsigned long diff;
-	db_addr_t stoffs;
+	db_addr_t stoffs = off;
 
 	if (symtab->private == NULL) {
 		if (!linker_ddb_search_symbol((caddr_t)off, &lsym, &diff)) {
@@ -110,10 +120,11 @@ X_db_search_symbol(db_symtab_t *symtab, db_addr_t off,
 		}
 		return (NULL);
 	}
+	else
+		stoffs -= DB_PRIVATE(symtab)->relbase;
 
 	diff = ~0UL;
 	match = NULL;
-	stoffs = DB_STOFFS(off);
 	for (sym = (Elf_Sym*)symtab->start; (char*)sym < symtab->end; sym++) {
 		if (sym->st_name == 0 || sym->st_shndx == SHN_UNDEF)
 			continue;
@@ -171,15 +182,17 @@ X_db_symbol_values(db_symtab_t *symtab, c_db_sym_t sym
 			*valp = (db_expr_t)lval.value;
 	} else {
 		if (namep != NULL)
-			*namep = (const char *)symtab->private +
+			*namep = (const char *)DB_PRIVATE(symtab)->strtab +
 			    ((const Elf_Sym *)sym)->st_name;
 		if (valp != NULL)
-			*valp = (db_expr_t)((const Elf_Sym *)sym)->st_value;
+			*valp = (db_expr_t)((const Elf_Sym *)sym)->st_value +
+			    DB_PRIVATE(symtab)->relbase;
 	}
 }
 
 int
-db_fetch_ksymtab(vm_offset_t ksym_start, vm_offset_t ksym_end)
+db_fetch_ksymtab(vm_offset_t ksym_start, vm_offset_t ksym_end,
+    vm_offset_t relbase)
 {
 	Elf_Size strsz;
 
@@ -190,9 +203,11 @@ db_fetch_ksymtab(vm_offset_t ksym_start, vm_offset_t k
 		kstrtab = ksymtab + ksymtab_size;
 		strsz = *(Elf_Size*)kstrtab;
 		kstrtab += sizeof(Elf_Size);
+		ksymtab_relbase = relbase;
 		if (kstrtab + strsz > ksym_end) {
 			/* Sizes doesn't match, unset everything. */
-			ksymtab = ksymtab_size = kstrtab = 0;
+			ksymtab = ksymtab_size = kstrtab = ksymtab_relbase
+			    = 0;
 		}
 	}
 
@@ -209,8 +224,10 @@ db_init(void)
 	db_command_init();
 
 	if (ksymtab != 0 && kstrtab != 0 && ksymtab_size != 0) {
+		ksymtab_private.strtab = (char *)kstrtab;
+		ksymtab_private.relbase = ksymtab_relbase;
 		db_add_symbol_table((char *)ksymtab,
-		    (char *)(ksymtab + ksymtab_size), "elf", (char *)kstrtab);
+		    (char *)(ksymtab + ksymtab_size), "elf", (char *)&ksymtab_private);
 	}
 	db_add_symbol_table(NULL, NULL, "kld", NULL);
 	return (1);	/* We're the default debugger. */

Modified: head/sys/ddb/ddb.h
==============================================================================
--- head/sys/ddb/ddb.h	Sun Jun 21 02:49:56 2020	(r362457)
+++ head/sys/ddb/ddb.h	Sun Jun 21 03:39:26 2020	(r362458)
@@ -72,10 +72,6 @@ SYSCTL_DECL(_debug_ddb);
 #define	DB_MAXSCRIPTRECURSION	3
 #endif
 
-#ifndef DB_STOFFS
-#define DB_STOFFS(offs)		(offs)
-#endif
-
 #ifndef DB_CALL
 #define	DB_CALL	db_fncall_generic
 #else
@@ -87,7 +83,7 @@ int	DB_CALL(db_expr_t, db_expr_t *, int, db_expr_t[]);
  * Most users should use db_fetch_symtab in order to set them from the
  * boot loader provided values.
  */
-extern vm_offset_t ksymtab, kstrtab, ksymtab_size;
+extern vm_offset_t ksymtab, kstrtab, ksymtab_size, ksymtab_relbase;
 
 /*
  * There are three "command tables":
@@ -232,7 +228,8 @@ bool		db_value_of_name_vnet(const char *name, db_expr_
 int		db_write_bytes(vm_offset_t addr, size_t size, char *data);
 void		db_command_register(struct command_table *, struct command *);
 void		db_command_unregister(struct command_table *, struct command *);
-int		db_fetch_ksymtab(vm_offset_t ksym_start, vm_offset_t ksym_end);
+int		db_fetch_ksymtab(vm_offset_t ksym_start, vm_offset_t ksym_end,
+		    vm_offset_t relbase);
 
 db_cmdfcn_t	db_breakpoint_cmd;
 db_cmdfcn_t	db_capture_cmd;

Modified: head/sys/dev/ksyms/ksyms.c
==============================================================================
--- head/sys/dev/ksyms/ksyms.c	Sun Jun 21 02:49:56 2020	(r362457)
+++ head/sys/dev/ksyms/ksyms.c	Sun Jun 21 03:39:26 2020	(r362458)
@@ -202,7 +202,7 @@ ksyms_add(linker_file_t lf, void *arg)
 	strsz = LINKER_STRTAB_GET(lf, &strtab);
 	symsz = numsyms * sizeof(Elf_Sym);
 
-#ifdef __powerpc__
+#ifdef RELOCATABLE_KERNEL
 	fixup = true;
 #else
 	fixup = lf->id > 1;

Modified: head/sys/i386/i386/machdep.c
==============================================================================
--- head/sys/i386/i386/machdep.c	Sun Jun 21 02:49:56 2020	(r362457)
+++ head/sys/i386/i386/machdep.c	Sun Jun 21 03:39:26 2020	(r362458)
@@ -2180,7 +2180,7 @@ static void
 i386_kdb_init(void)
 {
 #ifdef DDB
-	db_fetch_ksymtab(bootinfo.bi_symtab, bootinfo.bi_esymtab);
+	db_fetch_ksymtab(bootinfo.bi_symtab, bootinfo.bi_esymtab, 0);
 #endif
 	kdb_init();
 #ifdef KDB

Modified: head/sys/kern/link_elf.c
==============================================================================
--- head/sys/kern/link_elf.c	Sun Jun 21 02:49:56 2020	(r362457)
+++ head/sys/kern/link_elf.c	Sun Jun 21 03:39:26 2020	(r362458)
@@ -389,6 +389,21 @@ link_elf_link_common_finish(linker_file_t lf)
 }
 
 #ifdef RELOCATABLE_KERNEL
+/*
+ * __startkernel and __endkernel are symbols set up as relocation canaries.
+ *
+ * They are defined in locore to reference linker script symbols at the
+ * beginning and end of the LOAD area. This has the desired side effect of
+ * giving us variables that have relative relocations pointing at them, so
+ * relocation of the kernel object will cause the variables to be updated
+ * automatically by the runtime linker when we initialize.
+ *
+ * There are two main reasons to relocate the kernel:
+ * 1) If the loader needed to load the kernel at an alternate load address.
+ * 2) If the kernel is switching address spaces on machines like POWER9
+ *    under Radix where the high bits of the effective address are used to
+ *    differentiate between hypervisor, host, guest, and problem state.
+ */
 extern vm_offset_t __startkernel, __endkernel;
 #endif
 
@@ -427,6 +442,7 @@ link_elf_init(void* arg)
 	ef = (elf_file_t) linker_kernel_file;
 	ef->preloaded = 1;
 #ifdef RELOCATABLE_KERNEL
+	/* Compute relative displacement */
 	ef->address = (caddr_t) (__startkernel - KERNBASE);
 #else
 	ef->address = 0;

Modified: head/sys/mips/mips/machdep.c
==============================================================================
--- head/sys/mips/mips/machdep.c	Sun Jun 21 02:49:56 2020	(r362457)
+++ head/sys/mips/mips/machdep.c	Sun Jun 21 03:39:26 2020	(r362458)
@@ -447,7 +447,7 @@ mips_postboot_fixup(void)
 		kernel_kseg0_end += symtabsize;
 		/* end of .strtab */
 		ksym_end = kernel_kseg0_end;
-		db_fetch_ksymtab(ksym_start, ksym_end);
+		db_fetch_ksymtab(ksym_start, ksym_end, 0);
 	}
 #endif
 }

Modified: head/sys/powerpc/powerpc/machdep.c
==============================================================================
--- head/sys/powerpc/powerpc/machdep.c	Sun Jun 21 02:49:56 2020	(r362457)
+++ head/sys/powerpc/powerpc/machdep.c	Sun Jun 21 03:39:26 2020	(r362458)
@@ -251,7 +251,6 @@ void booke_cpu_init(void);
 
 #ifdef DDB
 static void	load_external_symtab(void);
-static void	displace_symbol_table(vm_offset_t, vm_offset_t, vm_offset_t);
 #endif
 
 uintptr_t
@@ -360,16 +359,8 @@ powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offs
 			ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t);
 			ksym_sz = *(Elf_Size*)ksym_start;
 
-			/*
-			 * Loader already handled displacing to the load
-			 * address, but we still need to displace it to the
-			 * DMAP.
-			 */
-			displace_symbol_table(
-			    (vm_offset_t)(ksym_start + sizeof(Elf_Size)),
-			    ksym_sz, md_offset);
-
-			db_fetch_ksymtab(ksym_start, ksym_end);
+			db_fetch_ksymtab(ksym_start, ksym_end, md_offset);
+			/* Symbols provided by loader. */
 			symbols_provided = true;
 #endif
 		}
@@ -509,45 +500,22 @@ powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offs
 
 #ifdef DDB
 /*
- * XXX Figure out where to move this.
+ * On powernv and some booke systems, we might not have symbols loaded via
+ * loader. However, if the user passed the kernel in as the initrd as well,
+ * we can manually load it via reinterpreting the initrd copy of the kernel.
+ *
+ * In the BOOKE case, we don't actually have a DMAP yet, so we have to use
+ * temporary maps to inspect the memory, but write DMAP addresses to the
+ * configuration variables.
  */
 static void
-displace_symbol_table(vm_offset_t ksym_start,
-    vm_offset_t ksym_sz, vm_offset_t displacement) {
-	Elf_Sym *sym;
-
-	/*
-	 * Relocate the symbol table to our final load address.
-	 */
-	for (sym = (Elf_Sym *)ksym_start;
-	    (vm_paddr_t)sym < (ksym_start + ksym_sz);
-	    sym++) {
-		if (sym->st_name == 0 ||
-		    sym->st_shndx == SHN_UNDEF ||
-		    sym->st_value == 0)
-			continue;
-		if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT &&
-		    ELF_ST_TYPE(sym->st_info) != STT_FUNC &&
-		    ELF_ST_TYPE(sym->st_info) != STT_NOTYPE)
-			continue;
-		/* Skip relocating any implausible symbols */
-		if (sym->st_value > KERNBASE)
-			sym->st_value += displacement;
-	}
-}
-
-/*
- * On powernv, we might not have symbols loaded via loader. However, if the
- * user passed the kernel in as the initrd as well, we can manually load it
- * via reinterpreting the initrd copy of the kernel.
- */
-static void
 load_external_symtab(void) {
 	phandle_t chosen;
 	vm_paddr_t start, end;
 	pcell_t cell[2];
 	ssize_t size;
-	u_char *kernelimg;
+	u_char *kernelimg;		/* Temporary map */
+	u_char *kernelimg_final;	/* Final location */
 
 	int i;
 
@@ -555,7 +523,8 @@ load_external_symtab(void) {
 	Elf_Phdr *phdr;
 	Elf_Shdr *shdr;
 
-        vm_offset_t ksym_start, ksym_sz, kstr_start, kstr_sz;
+	vm_offset_t ksym_start, ksym_sz, kstr_start, kstr_sz,
+	    ksym_start_final, kstr_start_final;
 
 	if (!hw_direct_map)
 		return;
@@ -587,27 +556,48 @@ load_external_symtab(void) {
 	if (!(end - start > 0))
 		return;
 
-	kernelimg = (u_char *) PHYS_TO_DMAP(start);
-
+	kernelimg_final = (u_char *) PHYS_TO_DMAP(start);
+#ifdef	AIM
+	kernelimg = kernelimg_final;
+#else	/* BOOKE */
+	kernelimg = (u_char *)pmap_early_io_map(start, PAGE_SIZE);
+#endif
 	ehdr = (Elf_Ehdr *)kernelimg;
 
-	if (!IS_ELF(*ehdr))
+	if (!IS_ELF(*ehdr)) {
+#ifdef	BOOKE
+		pmap_early_io_unmap(start, PAGE_SIZE);
+#endif
 		return;
+	}
 
+#ifdef	BOOKE
+	pmap_early_io_unmap(start, PAGE_SIZE);
+	kernelimg = (u_char *)pmap_early_io_map(start, (end - start));
+#endif
+
 	phdr = (Elf_Phdr *)(kernelimg + ehdr->e_phoff);
 	shdr = (Elf_Shdr *)(kernelimg + ehdr->e_shoff);
 
 	ksym_start = 0;
 	ksym_sz = 0;
+	ksym_start_final = 0;
 	kstr_start = 0;
 	kstr_sz = 0;
+	kstr_start_final = 0;
 	for (i = 0; i < ehdr->e_shnum; i++) {
 		if (shdr[i].sh_type == SHT_SYMTAB) {
 			ksym_start = (vm_offset_t)(kernelimg +
 			    shdr[i].sh_offset);
+			ksym_start_final = (vm_offset_t)
+			    (kernelimg_final + shdr[i].sh_offset);
 			ksym_sz = (vm_offset_t)(shdr[i].sh_size);
 			kstr_start = (vm_offset_t)(kernelimg +
 			    shdr[shdr[i].sh_link].sh_offset);
+			kstr_start_final = (vm_offset_t)
+			    (kernelimg_final +
+			    shdr[shdr[i].sh_link].sh_offset);
+
 			kstr_sz = (vm_offset_t)
 			    (shdr[shdr[i].sh_link].sh_size);
 		}
@@ -615,13 +605,22 @@ load_external_symtab(void) {
 
 	if (ksym_start != 0 && kstr_start != 0 && ksym_sz != 0 &&
 	    kstr_sz != 0 && ksym_start < kstr_start) {
-
-		displace_symbol_table(ksym_start, ksym_sz,
-		    (__startkernel - KERNBASE));
-		ksymtab = ksym_start;
+		/*
+		 * We can't use db_fetch_ksymtab() here, because we need to
+		 * feed in DMAP addresses that are not mapped yet on booke.
+		 *
+		 * Write the variables directly, where db_init() will pick
+		 * them up later, after the DMAP is up.
+		 */
+		ksymtab = ksym_start_final;
 		ksymtab_size = ksym_sz;
-		kstrtab = kstr_start;
+		kstrtab = kstr_start_final;
+		ksymtab_relbase = (__startkernel - KERNBASE);
 	}
+
+#ifdef	BOOKE
+	pmap_early_io_unmap(start, (end - start));
+#endif
 
 };
 #endif


More information about the svn-src-all mailing list