LKML Archive on lore.kernel.org
 help / color / Atom feed
From: Peter Zijlstra <peterz@infradead.org>
To: tglx@linutronix.de, jpoimboe@redhat.com
Cc: linux-kernel@vger.kernel.org, x86@kernel.org,
	peterz@infradead.org, mhiramat@kernel.org, mbenes@suse.cz,
	brgerst@gmail.com
Subject: [PATCH v3 10/26] objtool: Optimize find_symbol_*() and read_symbols()
Date: Tue, 24 Mar 2020 16:31:23 +0100
Message-ID: <20200324160924.499016559@infradead.org> (raw)
In-Reply-To: <20200324153113.098167666@infradead.org>

All of:

  read_symbols(), find_symbol_by_offset(), find_symbol_containing(),
  find_containing_func()

do a linear search of the symbols. Add an RB tree to make it go
faster.

This about halves objtool runtime on vmlinux.o, from 34s to 18s.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/Build |    5 +
 tools/objtool/elf.c |  194 ++++++++++++++++++++++++++++++++++++----------------
 tools/objtool/elf.h |    3 
 3 files changed, 144 insertions(+), 58 deletions(-)

--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -11,6 +11,7 @@ objtool-y += objtool.o
 objtool-y += libstring.o
 objtool-y += libctype.o
 objtool-y += str_error_r.o
+objtool-y += librbtree.o
 
 CFLAGS += -I$(srctree)/tools/lib
 
@@ -25,3 +26,7 @@ $(OUTPUT)libctype.o: ../lib/ctype.c FORC
 $(OUTPUT)str_error_r.o: ../lib/str_error_r.c FORCE
 	$(call rule_mkdir)
 	$(call if_changed_dep,cc_o_c)
+
+$(OUTPUT)librbtree.o: ../lib/rbtree.c FORCE
+	$(call rule_mkdir)
+	$(call if_changed_dep,cc_o_c)
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -27,6 +27,90 @@ static inline u32 str_hash(const char *s
 	return jhash(str, strlen(str), 0);
 }
 
+static void rb_add(struct rb_root *tree, struct rb_node *node,
+		   int (*cmp)(struct rb_node *, const struct rb_node *))
+{
+	struct rb_node **link = &tree->rb_node;
+	struct rb_node *parent = NULL;
+
+	while (*link) {
+		parent = *link;
+		if (cmp(node, parent) < 0)
+			link = &parent->rb_left;
+		else
+			link = &parent->rb_right;
+	}
+
+	rb_link_node(node, parent, link);
+	rb_insert_color(node, tree);
+}
+
+static struct rb_node *rb_find_first(struct rb_root *tree, const void *key,
+			       int (*cmp)(const void *key, const struct rb_node *))
+{
+	struct rb_node *node = tree->rb_node;
+	struct rb_node *match = NULL;
+
+	while (node) {
+		int c = cmp(key, node);
+		if (c <= 0) {
+			if (!c)
+				match = node;
+			node = node->rb_left;
+		} else if (c > 0) {
+			node = node->rb_right;
+		}
+	}
+
+	return match;
+}
+
+static struct rb_node *rb_next_match(struct rb_node *node, const void *key,
+				    int (*cmp)(const void *key, const struct rb_node *))
+{
+	node = rb_next(node);
+	if (node && cmp(key, node))
+		node = NULL;
+	return node;
+}
+
+#define rb_for_each(tree, node, key, cmp) \
+	for ((node) = rb_find_first((tree), (key), (cmp)); \
+	     (node); (node) = rb_next_match((node), (key), (cmp)))
+
+static int symbol_to_offset(struct rb_node *a, const struct rb_node *b)
+{
+	struct symbol *sa = rb_entry(a, struct symbol, node);
+	struct symbol *sb = rb_entry(b, struct symbol, node);
+
+	if (sa->offset < sb->offset)
+		return -1;
+	if (sa->offset > sb->offset)
+		return 1;
+
+	if (sa->len < sb->len)
+		return -1;
+	if (sa->len > sb->len)
+		return 1;
+
+	sa->alias = sb;
+
+	return 0;
+}
+
+static int symbol_by_offset(const void *key, const struct rb_node *node)
+{
+	const struct symbol *s = rb_entry(node, struct symbol, node);
+	const unsigned long *o = key;
+
+	if (*o < s->offset)
+		return -1;
+	if (*o > s->offset + s->len)
+		return 1;
+
+	return 0;
+}
+
 struct section *find_section_by_name(struct elf *elf, const char *name)
 {
 	struct section *sec;
@@ -63,47 +147,69 @@ static struct symbol *find_symbol_by_ind
 
 struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
 {
-	struct symbol *sym;
+	struct rb_node *node;
 
-	list_for_each_entry(sym, &sec->symbol_list, list)
-		if (sym->type != STT_SECTION && sym->offset == offset)
-			return sym;
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->offset == offset && s->type != STT_SECTION)
+			return s;
+	}
 
 	return NULL;
 }
 
 struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
 {
-	struct symbol *sym;
+	struct rb_node *node;
 
-	list_for_each_entry(sym, &sec->symbol_list, list)
-		if (sym->type == STT_FUNC && sym->offset == offset)
-			return sym;
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->offset == offset && s->type == STT_FUNC)
+			return s;
+	}
 
 	return NULL;
 }
 
-struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
+struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
 {
-	struct section *sec;
-	struct symbol *sym;
+	struct rb_node *node;
 
-	list_for_each_entry(sec, &elf->sections, list)
-		list_for_each_entry(sym, &sec->symbol_list, list)
-			if (!strcmp(sym->name, name))
-				return sym;
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->type != STT_SECTION)
+			return s;
+	}
 
 	return NULL;
 }
 
-struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
+struct symbol *find_containing_func(struct section *sec, unsigned long offset)
 {
+	struct rb_node *node;
+
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->type == STT_FUNC)
+			return s;
+	}
+
+	return NULL;
+}
+
+struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
+{
+	struct section *sec;
 	struct symbol *sym;
 
-	list_for_each_entry(sym, &sec->symbol_list, list)
-		if (sym->type != STT_SECTION &&
-		    offset >= sym->offset && offset < sym->offset + sym->len)
-			return sym;
+	list_for_each_entry(sec, &elf->sections, list)
+		list_for_each_entry(sym, &sec->symbol_list, list)
+			if (!strcmp(sym->name, name))
+				return sym;
 
 	return NULL;
 }
@@ -130,18 +236,6 @@ struct rela *find_rela_by_dest(struct se
 	return find_rela_by_dest_range(sec, offset, 1);
 }
 
-struct symbol *find_containing_func(struct section *sec, unsigned long offset)
-{
-	struct symbol *func;
-
-	list_for_each_entry(func, &sec->symbol_list, list)
-		if (func->type == STT_FUNC && offset >= func->offset &&
-		    offset < func->offset + func->len)
-			return func;
-
-	return NULL;
-}
-
 static int read_sections(struct elf *elf)
 {
 	Elf_Scn *s = NULL;
@@ -225,8 +319,9 @@ static int read_sections(struct elf *elf
 static int read_symbols(struct elf *elf)
 {
 	struct section *symtab, *sec;
-	struct symbol *sym, *pfunc, *alias;
-	struct list_head *entry, *tmp;
+	struct symbol *sym, *pfunc;
+	struct list_head *entry;
+	struct rb_node *pnode;
 	int symbols_nr, i;
 	char *coldstr;
 
@@ -245,7 +340,7 @@ static int read_symbols(struct elf *elf)
 			return -1;
 		}
 		memset(sym, 0, sizeof(*sym));
-		alias = sym;
+		sym->alias = sym;
 
 		sym->idx = i;
 
@@ -283,29 +378,12 @@ static int read_symbols(struct elf *elf)
 		sym->offset = sym->sym.st_value;
 		sym->len = sym->sym.st_size;
 
-		/* sorted insert into a per-section list */
-		entry = &sym->sec->symbol_list;
-		list_for_each_prev(tmp, &sym->sec->symbol_list) {
-			struct symbol *s;
-
-			s = list_entry(tmp, struct symbol, list);
-
-			if (sym->offset > s->offset) {
-				entry = tmp;
-				break;
-			}
-
-			if (sym->offset == s->offset) {
-				if (sym->len && sym->len == s->len && alias == sym)
-					alias = s;
-
-				if (sym->len >= s->len) {
-					entry = tmp;
-					break;
-				}
-			}
-		}
-		sym->alias = alias;
+		rb_add(&sym->sec->symbol_tree, &sym->node, symbol_to_offset);
+		pnode = rb_prev(&sym->node);
+		if (pnode)
+			entry = &rb_entry(pnode, struct symbol, node)->list;
+		else
+			entry = &sym->sec->symbol_list;
 		list_add(&sym->list, entry);
 		hash_add(elf->symbol_hash, &sym->hash, sym->idx);
 	}
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -10,6 +10,7 @@
 #include <gelf.h>
 #include <linux/list.h>
 #include <linux/hashtable.h>
+#include <linux/rbtree.h>
 #include <linux/jhash.h>
 
 #ifdef LIBELF_USE_DEPRECATED
@@ -29,6 +30,7 @@ struct section {
 	struct hlist_node hash;
 	struct hlist_node name_hash;
 	GElf_Shdr sh;
+	struct rb_root symbol_tree;
 	struct list_head symbol_list;
 	struct list_head rela_list;
 	DECLARE_HASHTABLE(rela_hash, 16);
@@ -43,6 +45,7 @@ struct section {
 
 struct symbol {
 	struct list_head list;
+	struct rb_node node;
 	struct hlist_node hash;
 	GElf_Sym sym;
 	struct section *sec;



  parent reply index

Thread overview: 89+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 01/26] objtool: Introduce validate_return() Peter Zijlstra
2020-03-25  8:39   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 02/26] objtool: Rename func_for_each_insn() Peter Zijlstra
2020-03-25  8:43   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 03/26] objtool: Rename func_for_each_insn_all() Peter Zijlstra
2020-03-25  8:44   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 04/26] x86/kexec: Use RIP relative addressing Peter Zijlstra
2020-03-25  9:34   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 05/26] x86/kexec: Make relocate_kernel_64.S objtool clean Peter Zijlstra
2020-03-24 20:55   ` Josh Poimboeuf
2020-03-25  9:56   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 06/26] objtool: Optimize find_symbol_by_index() Peter Zijlstra
2020-03-25 10:01   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 07/26] objtool: Add a statistics mode Peter Zijlstra
2020-03-25 10:10   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 08/26] objtool: Optimize find_section_by_index() Peter Zijlstra
2020-03-25 10:12   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 09/26] objtool: Optimize find_section_by_name() Peter Zijlstra
2020-03-25 10:18   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` Peter Zijlstra [this message]
2020-03-25 10:20   ` [PATCH v3 10/26] objtool: Optimize find_symbol_*() and read_symbols() Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 11/26] objtool: Rename find_containing_func() Peter Zijlstra
2020-03-25 10:21   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 12/26] objtool: Resize insn_hash Peter Zijlstra
2020-03-25 10:21   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 13/26] objtool: Optimize find_symbol_by_name() Peter Zijlstra
2020-03-25 10:25   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 14/26] objtool: Optimize read_sections() Peter Zijlstra
2020-03-25 12:10   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-04-21 14:47   ` [PATCH v3 14/26] " youling257
2020-04-21 15:49     ` Peter Zijlstra
2020-04-21 15:57       ` Borislav Petkov
2020-04-21 17:50       ` youling 257
2020-03-24 15:31 ` [PATCH v3 15/26] objtool: Delete cleanup() Peter Zijlstra
2020-03-25 12:11   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 16/26] objtool: Optimize find_rela_by_dest_range() Peter Zijlstra
2020-03-25 12:19   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 17/26] objtool: Re-arrange validate_functions() Peter Zijlstra
2020-03-24 21:10   ` Josh Poimboeuf
2020-03-24 21:15     ` Peter Zijlstra
2020-03-25 12:22   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
2020-03-24 21:40   ` Josh Poimboeuf
2020-03-24 22:11     ` Peter Zijlstra
2020-03-24 23:00       ` Peter Zijlstra
2020-03-25 14:39         ` Josh Poimboeuf
2020-03-25 14:41   ` [PATCH v3.1 18a/26] objtool: Remove CFI save/restore special case Peter Zijlstra
2020-03-25 14:42   ` [PATCH v3.1 18b/26] objtool: Factor out CFI hints Peter Zijlstra
2020-03-25 14:43   ` [PATCH v3.1 18c/26] objtool: Rename struct cfi_state Peter Zijlstra
2020-03-25 14:43   ` [PATCH v3.1 18d/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 19/26] objtool: Implement noinstr validation Peter Zijlstra
2020-03-24 21:41   ` Josh Poimboeuf
2020-03-25 14:44   ` [PATCH v3.1 " Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 20/26] objtool: Optimize !vmlinux.o again Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 21/26] objtool: Use sec_offset_hash() for insn_hash Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 22/26] objtool: Detect loading function pointers across noinstr Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 23/26] kbuild/objtool: Add objtool-vmlinux.o pass Peter Zijlstra
2020-03-24 22:03   ` Josh Poimboeuf
2020-03-24 22:05     ` Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 24/26] objtool: Avoid iterating !text section symbols Peter Zijlstra
2020-03-24 22:09   ` Josh Poimboeuf
2020-03-24 15:31 ` [PATCH v3 25/26] objtool: Rearrange validate_section() Peter Zijlstra
2020-03-24 22:10   ` Josh Poimboeuf
2020-03-24 15:31 ` [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation Peter Zijlstra
2020-03-24 22:16   ` Josh Poimboeuf
2020-03-24 22:34     ` Peter Zijlstra
2020-03-25 14:42       ` Josh Poimboeuf
2020-03-25 15:53         ` Peter Zijlstra
2020-03-25 16:40           ` Josh Poimboeuf
2020-03-25 16:50             ` Peter Zijlstra
2020-03-26  8:01               ` Julien Thierry

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200324160924.499016559@infradead.org \
    --to=peterz@infradead.org \
    --cc=brgerst@gmail.com \
    --cc=jpoimboe@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mbenes@suse.cz \
    --cc=mhiramat@kernel.org \
    --cc=tglx@linutronix.de \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

LKML Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/lkml/0 lkml/git/0.git
	git clone --mirror https://lore.kernel.org/lkml/1 lkml/git/1.git
	git clone --mirror https://lore.kernel.org/lkml/2 lkml/git/2.git
	git clone --mirror https://lore.kernel.org/lkml/3 lkml/git/3.git
	git clone --mirror https://lore.kernel.org/lkml/4 lkml/git/4.git
	git clone --mirror https://lore.kernel.org/lkml/5 lkml/git/5.git
	git clone --mirror https://lore.kernel.org/lkml/6 lkml/git/6.git
	git clone --mirror https://lore.kernel.org/lkml/7 lkml/git/7.git
	git clone --mirror https://lore.kernel.org/lkml/8 lkml/git/8.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 lkml lkml/ https://lore.kernel.org/lkml \
		linux-kernel@vger.kernel.org
	public-inbox-index lkml

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-kernel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git