Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 27 Sep 2012 14:55:15 +0000 (UTC)
From:      Mikolaj Golub <trociny@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r240997 - head/sys/kern
Message-ID:  <201209271455.q8REtF0E038539@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: trociny
Date: Thu Sep 27 14:55:15 2012
New Revision: 240997
URL: http://svn.freebsd.org/changeset/base/240997

Log:
  Kernel and modules have "set_vnet" linker set, where virtualized
  global variables are placed. When a module is loaded by link_elf
  linker its variables from "set_vnet" linker set are copied to the
  kernel "set_vnet" ("modspace") and all references to these variables
  inside the module are relocated accordingly.
  
  The issue is when a module is loaded that has references to global
  variables from another, previously loaded module: these references are
  not relocated so an invalid address is used when the module tries to
  access the variable. The example is V_layer3_chain, defined in ipfw
  module and accessed from ipfw_nat.
  
  The same issue is with DPCPU variables, which use "set_pcpu" linker
  set.
  
  Fix this making the link_elf linker on a module load recognize
  "external" DPCPU/VNET variables defined in the previously loaded
  modules and relocate them accordingly. For this set_pcpu_list and
  set_vnet_list are used, where the addresses of modules' "set_pcpu" and
  "set_vnet" linker sets are stored.
  
  Note, archs that use link_elf_obj (amd64) were not affected by this
  issue.
  
  Reviewed by:	jhb, julian, zec (initial version)
  MFC after:	1 month

Modified:
  head/sys/kern/link_elf.c

Modified: head/sys/kern/link_elf.c
==============================================================================
--- head/sys/kern/link_elf.c	Thu Sep 27 14:29:18 2012	(r240996)
+++ head/sys/kern/link_elf.c	Thu Sep 27 14:55:15 2012	(r240997)
@@ -123,6 +123,15 @@ typedef struct elf_file {
 #endif
 } *elf_file_t;
 
+struct elf_set {
+	Elf_Addr	es_start;
+	Elf_Addr	es_stop;
+	Elf_Addr	es_base;
+	TAILQ_ENTRY(elf_set)	es_link;
+};
+
+TAILQ_HEAD(elf_set_head, elf_set);
+
 #include <kern/kern_ctf.c>
 
 static int	link_elf_link_common_finish(linker_file_t);
@@ -181,6 +190,75 @@ static int	parse_dynamic(elf_file_t);
 static int	relocate_file(elf_file_t);
 static int	link_elf_preload_parse_symbols(elf_file_t);
 
+static struct elf_set_head set_pcpu_list;
+#ifdef VIMAGE
+static struct elf_set_head set_vnet_list;
+#endif
+
+static void
+elf_set_add(struct elf_set_head *list, Elf_Addr start, Elf_Addr stop, Elf_Addr base)
+{
+	struct elf_set *set, *iter;
+
+	set = malloc(sizeof(*set), M_LINKER, M_WAITOK);
+	set->es_start = start;
+	set->es_stop = stop;
+	set->es_base = base;
+
+	TAILQ_FOREACH(iter, list, es_link) {
+
+		KASSERT((set->es_start < iter->es_start && set->es_stop < iter->es_stop) ||
+		    (set->es_start > iter->es_start && set->es_stop > iter->es_stop),
+		    ("linker sets intersection: to insert: 0x%jx-0x%jx; inserted: 0x%jx-0x%jx",
+		    (uintmax_t)set->es_start, (uintmax_t)set->es_stop,
+		    (uintmax_t)iter->es_start, (uintmax_t)iter->es_stop));
+
+		if (iter->es_start > set->es_start) {
+			TAILQ_INSERT_BEFORE(iter, set, es_link);
+			break;
+		}
+	}
+
+	if (iter == NULL)
+		TAILQ_INSERT_TAIL(list, set, es_link);
+}
+
+static int
+elf_set_find(struct elf_set_head *list, Elf_Addr addr, Elf_Addr *start, Elf_Addr *base)
+{
+	struct elf_set *set;
+
+	TAILQ_FOREACH(set, list, es_link) {
+		if (addr < set->es_start)
+			return (0);
+		if (addr < set->es_stop) {
+			*start = set->es_start;
+			*base = set->es_base;
+			return (1);
+		}
+	}
+
+	return (0);
+}
+
+static void
+elf_set_delete(struct elf_set_head *list, Elf_Addr start)
+{
+	struct elf_set *set;
+
+	TAILQ_FOREACH(set, list, es_link) {
+		if (start < set->es_start)
+			break;
+		if (start == set->es_start) {
+			TAILQ_REMOVE(list, set, es_link);
+			free(set, M_LINKER);
+			return;
+		}
+	}
+	KASSERT(0, ("deleting unknown linker set (start = 0x%jx)",
+	    (uintmax_t)start));
+}
+
 #ifdef GDB
 static void	r_debug_state(struct r_debug *, struct link_map *);
 
@@ -345,6 +423,10 @@ link_elf_init(void* arg)
 
 	(void)link_elf_link_common_finish(linker_kernel_file);
 	linker_kernel_file->flags |= LINKER_FILE_LINKED;
+	TAILQ_INIT(&set_pcpu_list);
+#ifdef VIMAGE
+	TAILQ_INIT(&set_vnet_list);
+#endif
 }
 
 SYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_THIRD, link_elf_init, 0);
@@ -515,6 +597,8 @@ parse_dpcpu(elf_file_t ef)
 		return (ENOSPC);
 	memcpy((void *)ef->pcpu_base, (void *)ef->pcpu_start, count);
 	dpcpu_copy((void *)ef->pcpu_base, count);
+	elf_set_add(&set_pcpu_list, ef->pcpu_start, ef->pcpu_stop,
+	    ef->pcpu_base);
 
 	return (0);
 }
@@ -544,6 +628,8 @@ parse_vnet(elf_file_t ef)
 		return (ENOSPC);
 	memcpy((void *)ef->vnet_base, (void *)ef->vnet_start, count);
 	vnet_data_copy((void *)ef->vnet_base, count);
+	elf_set_add(&set_vnet_list, ef->vnet_start, ef->vnet_stop,
+	    ef->vnet_base);
 
 	return (0);
 }
@@ -996,11 +1082,13 @@ link_elf_unload_file(linker_file_t file)
 	if (ef->pcpu_base != 0) {
 		dpcpu_free((void *)ef->pcpu_base,
 		    ef->pcpu_stop - ef->pcpu_start);
+		elf_set_delete(&set_pcpu_list, ef->pcpu_start);
 	}
 #ifdef VIMAGE
 	if (ef->vnet_base != 0) {
 		vnet_data_free((void *)ef->vnet_base,
 		    ef->vnet_stop - ef->vnet_start);
+		elf_set_delete(&set_vnet_list, ef->vnet_start);
 	}
 #endif
 #ifdef GDB
@@ -1439,6 +1527,7 @@ elf_lookup(linker_file_t lf, Elf_Size sy
 	elf_file_t ef = (elf_file_t)lf;
 	const Elf_Sym *sym;
 	const char *symbol;
+	Elf_Addr addr, start, base;
 
 	/* Don't even try to lookup the symbol if the index is bogus. */
 	if (symidx >= ef->nchains)
@@ -1470,7 +1559,15 @@ elf_lookup(linker_file_t lf, Elf_Size sy
 	if (*symbol == 0)
 		return (0);
 
-	return ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps));
+	addr = ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps));
+
+	if (elf_set_find(&set_pcpu_list, addr, &start, &base))
+		addr = addr - start + base;
+#ifdef VIMAGE
+	else if (elf_set_find(&set_vnet_list, addr, &start, &base))
+		addr = addr - start + base;
+#endif
+	return addr;
 }
 
 static void



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201209271455.q8REtF0E038539>