svn commit: r41340 - projects/sysctl/share/tools/sysctl

Dag-Erling Smørgrav des at FreeBSD.org
Mon Apr 1 18:41:28 UTC 2013


Author: des
Date: Mon Apr  1 18:41:26 2013
New Revision: 41340
URL: http://svnweb.freebsd.org/changeset/doc/41340

Log:
  Improve the Elf explorer API to make it easier to retrieve data:
  
   - Most of the time elfx_get_section_by_{name,addr}() is called, the
     caller is looking the same section as the last time, so cache it
     internally rather than have the caller pass in a hint.
  
   - Preload the symbol table and add an elfx_get_symbol_by_name()
     function that does what the name says.
  
   - Replace the elfx_get_data() macro with a function that takes care
     of the looking up which section the data is in, rather than have
     the caller look it up every time.
  
  This greatly simplifies the sysctl OID retrieval code.
  
  Approved by:	mentor (gjb) (implicit)

Modified:
  projects/sysctl/share/tools/sysctl/elfexplorer.c
  projects/sysctl/share/tools/sysctl/elfexplorer.h
  projects/sysctl/share/tools/sysctl/sysctlfromelf.c

Modified: projects/sysctl/share/tools/sysctl/elfexplorer.c
==============================================================================
--- projects/sysctl/share/tools/sysctl/elfexplorer.c	Mon Apr  1 17:02:50 2013	(r41339)
+++ projects/sysctl/share/tools/sysctl/elfexplorer.c	Mon Apr  1 18:41:26 2013	(r41340)
@@ -65,6 +65,17 @@ elfx_compare_sections_by_addr(const void
 }
 
 /*
+ * Compare two symbols by their names.
+ */
+static int
+elfx_compare_symbols_by_name(const void *ap, const void *bp)
+{
+	const elfx_symbol *a = ap, *b = bp;
+
+	return (strcmp(a->name, b->name));
+}
+
+/*
  * Free memory that was allocated to store information about sections.
  */
 static void
@@ -99,17 +110,18 @@ static int
 elfx_load_sections(elfx_file *ef)
 {
 	elfx_section *es;
-	size_t sections_size;
+	size_t size;
 
 	/* get translated ELF header */
 	if (gelf_getehdr(ef->elf, &ef->hdr) == NULL)
 		goto elf_error;
 	/* get number of sections in file */
-	if (elf_getshdrnum(ef->elf, &ef->nsections) != ELF_E_NONE)
+	if (elf_getshdrnum(ef->elf, &size) != ELF_E_NONE)
 		goto elf_error;
-	sections_size = ef->nsections * sizeof *ef->sections;
-	if ((ef->sections = calloc(sections_size, 1)) == NULL)
-		goto mem_error;
+	ef->nsections = size;
+	size *= sizeof *ef->sections;
+	if ((ef->sections = calloc(size, 1)) == NULL)
+		return (-1);
 	verbose("%4s %4s %8s %8s %s", "sect", "type", "start", "size", "name");
 	/* iterate over sections */
 	for (unsigned int i = 0; i < ef->nsections; ++i) {
@@ -145,26 +157,53 @@ elfx_load_sections(elfx_file *ef)
 		}
 	}
 	/* create copy of section list, sorted by name */
-	if ((ef->sections_by_name = calloc(sections_size, 1)) == NULL)
-		goto mem_error;
-	memcpy(ef->sections_by_name, ef->sections, sections_size);
+	if ((ef->sections_by_name = calloc(size, 1)) == NULL)
+		return (-1);
+	memcpy(ef->sections_by_name, ef->sections, size);
 	mergesort(ef->sections_by_name, ef->nsections,
 	    sizeof *ef->sections_by_name, elfx_compare_sections_by_name);
 	/* create copy of section list, sorted by address */
-	if ((ef->sections_by_addr = calloc(sections_size, 1)) == NULL)
-		goto mem_error;
-	memcpy(ef->sections_by_addr, ef->sections, sections_size);
+	if ((ef->sections_by_addr = calloc(size, 1)) == NULL)
+		return (-1);
+	memcpy(ef->sections_by_addr, ef->sections, size);
 	mergesort(ef->sections_by_addr, ef->nsections,
 	    sizeof *ef->sections_by_addr, elfx_compare_sections_by_addr);
 	return (ef->nsections);
 elf_error:
 	info("%s: %s", ef->path, elf_errmsg(elf_errno()));
-mem_error:
-	elfx_free_sections(ef);
 	return (-1);
 }
 
 /*
+ * Retrieve and sort the symbol table.
+ */
+static int
+elfx_load_symbols(elfx_file *ef)
+{
+	elfx_section *symtab;
+	GElf_Sym sym;
+	size_t size;
+
+	if ((symtab = elfx_get_section_by_name(ef, ".symtab")) == NULL)
+		return (-1);
+	ef->nsymbols = size = symtab->hdr.sh_size / symtab->hdr.sh_entsize;
+	size *= sizeof *ef->symbols;
+	if ((ef->symbols = calloc(size, 1)) == NULL)
+		return (-1);
+	for (unsigned int i = 0; i < ef->nsymbols; ++i) {
+		gelf_getsym(symtab->data, i, &sym);
+		ef->symbols[i].file = ef;
+		ef->symbols[i].name = elf_strptr(ef->elf, symtab->hdr.sh_link,
+		    sym.st_name);
+		ef->symbols[i].addr = sym.st_value;
+		ef->symbols[i].size = sym.st_size;
+	}
+	mergesort(ef->symbols, ef->nsymbols, sizeof *ef->symbols,
+	    elfx_compare_symbols_by_name);
+	return (0);
+}
+
+/*
  * Open an ELF file and retrieve the information we need.
  */
 elfx_file *
@@ -191,6 +230,8 @@ elfx_open(const char *path)
 		goto fail;
 	if (elfx_load_sections(ef) < 0)
 		goto fail;
+	if (elfx_load_symbols(ef) < 0)
+		goto fail;
 	return (ef);
 fail:
 	elfx_close(ef);
@@ -226,6 +267,9 @@ elfx_get_section_by_name(elfx_file *ef, 
 	int lo, hi, mid;
 	int cmp;
 
+	if (ef->last_section_by_name != 0 &&
+	    strcmp(name, ef->last_section_by_name->name) == 0)
+		return (ef->last_section_by_name);
 	es = ef->sections_by_name;
 	lo = 0;
 	hi = ef->nsections - 1;
@@ -234,7 +278,7 @@ elfx_get_section_by_name(elfx_file *ef, 
 //		verbose("(%d, %d, %d) %s == %s",
 //		    lo, hi, mid, es[mid].name, name);
 		if ((cmp = strcmp(name, es[mid].name)) == 0)
-			return (&es[mid]);
+			return (ef->last_section_by_name = &es[mid]);
 		else if (lo == hi)
 			return (NULL);
 		else if (cmp < 0)
@@ -248,13 +292,15 @@ elfx_get_section_by_name(elfx_file *ef, 
  * Retrieve the section that contains a specified address.
  */
 elfx_section *
-elfx_get_section_by_addr(elfx_file *ef, uintptr_t addr, elfx_section *hint)
+elfx_get_section_by_addr(elfx_file *ef, uintptr_t addr)
 {
 	elfx_section *es;
 	int lo, hi, mid;
 
-	if (hint != NULL && hint->baddr <= addr && addr <= hint->eaddr)
-		return (hint);
+	if (ef->last_section_by_addr != NULL &&
+	    ef->last_section_by_addr->baddr <= addr &&
+	    addr <= ef->last_section_by_addr->eaddr)
+		return (ef->last_section_by_addr);
 	es = ef->sections_by_addr;
 	lo = 0;
 	hi = ef->nsections - 1;
@@ -263,7 +309,7 @@ elfx_get_section_by_addr(elfx_file *ef, 
 //		verbose("(%d, %d, %d) %08x <= %08x <= %08x", lo, mid, hi,
 //		    es[mid].baddr, addr, es[mid].eaddr);
 		if (es[mid].baddr <= addr && addr <= es[mid].eaddr)
-			return (&es[mid]);
+			return (ef->last_section_by_addr = &es[mid]);
 		else if (lo == hi)
 			return (NULL);
 		else if (addr < es[mid].baddr)
@@ -274,30 +320,45 @@ elfx_get_section_by_addr(elfx_file *ef, 
 }
 
 /*
- * Look up a symbol in the symbol table
+ * Return a pointer to the data at the specified address.
  */
-uintptr_t
-elfx_get_symbol(elfx_file *ef, const char *name)
+void *
+elfx_get_data(elfx_file *ef, uintptr_t addr)
 {
-	elfx_section *symtab;
-	GElf_Sym sym;
-	int nsyms;
-	char *symname;
+	elfx_section *es;
 
-	/* XXX cache symtab */
-	if ((symtab = elfx_get_section_by_name(ef, ".symtab")) == NULL)
-		return (0);
-	nsyms = symtab->hdr.sh_size / symtab->hdr.sh_entsize;
-	for (int i = 0; i < nsyms; ++i) {
-		gelf_getsym(symtab->data, i, &sym);
-		if ((symname = elf_strptr(ef->elf, symtab->hdr.sh_link,
-		    sym.st_name)) != NULL) {
-//			verbose("%08x %s", sym.st_value, symname);
-			if (strcmp(symname, name) == 0) {
-//				verbose("found %s at %08x", name, sym.st_value);
-				return (sym.st_value);
-			}
-		}
+	if ((es = elfx_get_section_by_addr(ef, addr)) == NULL)
+		return (NULL);
+	return ((char *)es->ptr + addr - es->baddr);
+}
+
+/*
+ * Look up a symbol by name.
+ */
+elfx_symbol *
+elfx_get_symbol_by_name(elfx_file *ef, const char *name)
+{
+	elfx_symbol *es;
+	int lo, hi, mid;
+	int cmp;
+
+	if (ef->last_symbol_by_name != 0 &&
+	    strcmp(name, ef->last_symbol_by_name->name) == 0)
+		return (ef->last_symbol_by_name);
+	es = ef->symbols;
+	lo = 0;
+	hi = ef->nsymbols - 1;
+	for (;;) {
+		mid = (lo + hi) / 2;
+//		verbose("(%d, %d, %d) %s == %s",
+//		    lo, hi, mid, es[mid].name, name);
+		if ((cmp = strcmp(name, es[mid].name)) == 0)
+			return (ef->last_symbol_by_name = &es[mid]);
+		else if (lo == hi)
+			return (NULL);
+		else if (cmp < 0)
+			hi = mid - 1;
+		else
+			lo = mid + 1;
 	}
-	return (0);
 }

Modified: projects/sysctl/share/tools/sysctl/elfexplorer.h
==============================================================================
--- projects/sysctl/share/tools/sysctl/elfexplorer.h	Mon Apr  1 17:02:50 2013	(r41339)
+++ projects/sysctl/share/tools/sysctl/elfexplorer.h	Mon Apr  1 18:41:26 2013	(r41340)
@@ -39,10 +39,15 @@ typedef struct elfx_file {
 	void			*map;
 	Elf			*elf;
 	GElf_Ehdr		 hdr;
-	size_t			 nsections;
+	unsigned int		 nsections;
 	struct elfx_section	*sections;
 	struct elfx_section	*sections_by_name;
+	struct elfx_section	*last_section_by_name;
 	struct elfx_section	*sections_by_addr;
+	struct elfx_section	*last_section_by_addr;
+	unsigned int		 nsymbols;
+	struct elfx_symbol	*symbols;
+	struct elfx_symbol	*last_symbol_by_name;
 } elfx_file;
 
 typedef struct elfx_section {
@@ -58,15 +63,20 @@ typedef struct elfx_section {
 	void			*ptr;
 } elfx_section;
 
+typedef struct elfx_symbol {
+	struct elfx_file	*file;
+	char			*name;
+	uintptr_t		 addr;
+	size_t			 size;
+} elfx_symbol;
 
 elfx_file *elfx_open(const char *);
 void elfx_close(elfx_file *);
 
 elfx_section *elfx_get_section_by_name(elfx_file *, const char *);
-elfx_section *elfx_get_section_by_addr(elfx_file *, uintptr_t, elfx_section *);
-#define elfx_get_data(s, a) \
-	(void *)((char *)(s)->ptr + (uintptr_t)(a) - (s)->baddr)
-uintptr_t elfx_get_symbol(elfx_file *, const char *);
+elfx_section *elfx_get_section_by_addr(elfx_file *, uintptr_t);
+void *elfx_get_data(elfx_file *, uintptr_t);
+elfx_symbol *elfx_get_symbol_by_name(elfx_file *, const char *);
 
 
 #endif

Modified: projects/sysctl/share/tools/sysctl/sysctlfromelf.c
==============================================================================
--- projects/sysctl/share/tools/sysctl/sysctlfromelf.c	Mon Apr  1 17:02:50 2013	(r41339)
+++ projects/sysctl/share/tools/sysctl/sysctlfromelf.c	Mon Apr  1 18:41:26 2013	(r41340)
@@ -80,18 +80,13 @@ process_file(const char *path)
 {
 	elfx_file *ef;
 	elfx_section *sect;
+	elfx_symbol *rootsym;
 	unsigned int nsysctls;
 	struct sysctl_oid_list *sysctl_root;
 	struct sysctl_oid *oid, **oids;
-	uintptr_t rootaddr;
 
 	if ((ef = elfx_open(path)) == NULL)
 		return (-1);
-	/* find the root list */
-	rootaddr = elfx_get_symbol(ef, "sysctl__children");
-	sect = elfx_get_section_by_addr(ef, rootaddr, NULL);
-	assert(sect != NULL);
-	sysctl_root = elfx_get_data(sect, rootaddr);
 	/* find the sysctl linker set */
 	if ((sect = elfx_get_section_by_name(ef, "set_sysctl_set")) == NULL) {
 		verbose("no sysctl linker set");
@@ -101,46 +96,31 @@ process_file(const char *path)
 	assert(sect->size % sizeof(uintptr_t) == 0);
 	nsysctls = sect->size / sizeof(uintptr_t);
 	oids = (struct sysctl_oid **)sect->ptr;
-	verbose("%zd sysctls found in section %d", nsysctls, sect->index);
+	verbose("%zd sysctls found in section %d", nsysctls, sect->index);	
+	/* find the root list */
+	rootsym = elfx_get_symbol_by_name(ef, "sysctl__children");
+	assert(rootsym != NULL);
+	sysctl_root = elfx_get_data(ef, rootsym->addr);
 	/* retrieve OIDs and fix up various pointers */
 	for (unsigned int i = 0; i < nsysctls; ++i) {
-		sect = elfx_get_section_by_addr(ef, (uintptr_t)oids[i], sect);
-		assert(sect != NULL);
-		oids[i] = oid = elfx_get_data(sect, oids[i]);
+		oids[i] = oid = elfx_get_data(ef, (uintptr_t)oids[i]);
 		/* name */
-		sect = elfx_get_section_by_addr(ef,
-		    (uintptr_t)oid->oid_name, sect);
-		assert(sect != NULL);
-		oid->oid_name = elfx_get_data(sect, oid->oid_name);
+		oid->oid_name = elfx_get_data(ef, (uintptr_t)oid->oid_name);
 		/* descriptions */
-		if (oid->oid_descr != NULL) {
-			sect = elfx_get_section_by_addr(ef,
-			    (uintptr_t)oid->oid_descr, sect);
-			assert(sect != NULL);
-			oid->oid_descr = elfx_get_data(sect, oid->oid_descr);
-		}
+		if (oid->oid_descr != NULL)
+			oid->oid_descr = elfx_get_data(ef,
+			    (uintptr_t)oid->oid_descr);
 		/* siblings */
-		if (oid->oid_link.sle_next != NULL) {
-			sect = elfx_get_section_by_addr(ef,
-			    (uintptr_t)oid->oid_link.sle_next, sect);
-			assert(sect != NULL);
-			oid->oid_link.sle_next = elfx_get_data(sect,
-			    oid->oid_link.sle_next);
-		}
+		if (oid->oid_link.sle_next != NULL)
+			oid->oid_link.sle_next = elfx_get_data(ef,
+			    (uintptr_t)oid->oid_link.sle_next);
 		/* children */
 		if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE &&
-		    oid->oid_arg1 != 0) {
-			sect = elfx_get_section_by_addr(ef,
-			    (uintptr_t)oid->oid_arg1, sect);
-			assert(sect != NULL);
-			oid->oid_arg1 = elfx_get_data(sect,
+		    oid->oid_arg1 != 0)
+			oid->oid_arg1 = elfx_get_data(ef,
 			    (uintptr_t)oid->oid_arg1);
-		}
 		/* parent */
-		sect = elfx_get_section_by_addr(ef,
-		    (uintptr_t)oid->oid_parent, sect);
-		assert(sect != NULL);
-		oid->oid_parent = elfx_get_data(sect, oid->oid_parent);
+		oid->oid_parent = elfx_get_data(ef, (uintptr_t)oid->oid_parent);
 		SLIST_INSERT_HEAD(oid->oid_parent, oid, oid_link);
 	}
 	/* list all OIDs! */


More information about the svn-doc-projects mailing list