Skip to content

Commit 2a362ec

Browse files
author
Peter Zijlstra
committed
objtool: Optimize find_symbol_*() and read_symbols()
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) <[email protected]> Reviewed-by: Miroslav Benes <[email protected]> Acked-by: Josh Poimboeuf <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent ae35819 commit 2a362ec

File tree

3 files changed

+144
-58
lines changed

3 files changed

+144
-58
lines changed

tools/objtool/Build

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ objtool-y += objtool.o
1111
objtool-y += libstring.o
1212
objtool-y += libctype.o
1313
objtool-y += str_error_r.o
14+
objtool-y += librbtree.o
1415

1516
CFLAGS += -I$(srctree)/tools/lib
1617

@@ -25,3 +26,7 @@ $(OUTPUT)libctype.o: ../lib/ctype.c FORCE
2526
$(OUTPUT)str_error_r.o: ../lib/str_error_r.c FORCE
2627
$(call rule_mkdir)
2728
$(call if_changed_dep,cc_o_c)
29+
30+
$(OUTPUT)librbtree.o: ../lib/rbtree.c FORCE
31+
$(call rule_mkdir)
32+
$(call if_changed_dep,cc_o_c)

tools/objtool/elf.c

Lines changed: 136 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,90 @@ static inline u32 str_hash(const char *str)
2727
return jhash(str, strlen(str), 0);
2828
}
2929

30+
static void rb_add(struct rb_root *tree, struct rb_node *node,
31+
int (*cmp)(struct rb_node *, const struct rb_node *))
32+
{
33+
struct rb_node **link = &tree->rb_node;
34+
struct rb_node *parent = NULL;
35+
36+
while (*link) {
37+
parent = *link;
38+
if (cmp(node, parent) < 0)
39+
link = &parent->rb_left;
40+
else
41+
link = &parent->rb_right;
42+
}
43+
44+
rb_link_node(node, parent, link);
45+
rb_insert_color(node, tree);
46+
}
47+
48+
static struct rb_node *rb_find_first(struct rb_root *tree, const void *key,
49+
int (*cmp)(const void *key, const struct rb_node *))
50+
{
51+
struct rb_node *node = tree->rb_node;
52+
struct rb_node *match = NULL;
53+
54+
while (node) {
55+
int c = cmp(key, node);
56+
if (c <= 0) {
57+
if (!c)
58+
match = node;
59+
node = node->rb_left;
60+
} else if (c > 0) {
61+
node = node->rb_right;
62+
}
63+
}
64+
65+
return match;
66+
}
67+
68+
static struct rb_node *rb_next_match(struct rb_node *node, const void *key,
69+
int (*cmp)(const void *key, const struct rb_node *))
70+
{
71+
node = rb_next(node);
72+
if (node && cmp(key, node))
73+
node = NULL;
74+
return node;
75+
}
76+
77+
#define rb_for_each(tree, node, key, cmp) \
78+
for ((node) = rb_find_first((tree), (key), (cmp)); \
79+
(node); (node) = rb_next_match((node), (key), (cmp)))
80+
81+
static int symbol_to_offset(struct rb_node *a, const struct rb_node *b)
82+
{
83+
struct symbol *sa = rb_entry(a, struct symbol, node);
84+
struct symbol *sb = rb_entry(b, struct symbol, node);
85+
86+
if (sa->offset < sb->offset)
87+
return -1;
88+
if (sa->offset > sb->offset)
89+
return 1;
90+
91+
if (sa->len < sb->len)
92+
return -1;
93+
if (sa->len > sb->len)
94+
return 1;
95+
96+
sa->alias = sb;
97+
98+
return 0;
99+
}
100+
101+
static int symbol_by_offset(const void *key, const struct rb_node *node)
102+
{
103+
const struct symbol *s = rb_entry(node, struct symbol, node);
104+
const unsigned long *o = key;
105+
106+
if (*o < s->offset)
107+
return -1;
108+
if (*o > s->offset + s->len)
109+
return 1;
110+
111+
return 0;
112+
}
113+
30114
struct section *find_section_by_name(struct elf *elf, const char *name)
31115
{
32116
struct section *sec;
@@ -63,47 +147,69 @@ static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
63147

64148
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
65149
{
66-
struct symbol *sym;
150+
struct rb_node *node;
67151

68-
list_for_each_entry(sym, &sec->symbol_list, list)
69-
if (sym->type != STT_SECTION && sym->offset == offset)
70-
return sym;
152+
rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
153+
struct symbol *s = rb_entry(node, struct symbol, node);
154+
155+
if (s->offset == offset && s->type != STT_SECTION)
156+
return s;
157+
}
71158

72159
return NULL;
73160
}
74161

75162
struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
76163
{
77-
struct symbol *sym;
164+
struct rb_node *node;
78165

79-
list_for_each_entry(sym, &sec->symbol_list, list)
80-
if (sym->type == STT_FUNC && sym->offset == offset)
81-
return sym;
166+
rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
167+
struct symbol *s = rb_entry(node, struct symbol, node);
168+
169+
if (s->offset == offset && s->type == STT_FUNC)
170+
return s;
171+
}
82172

83173
return NULL;
84174
}
85175

86-
struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
176+
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
87177
{
88-
struct section *sec;
89-
struct symbol *sym;
178+
struct rb_node *node;
90179

91-
list_for_each_entry(sec, &elf->sections, list)
92-
list_for_each_entry(sym, &sec->symbol_list, list)
93-
if (!strcmp(sym->name, name))
94-
return sym;
180+
rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
181+
struct symbol *s = rb_entry(node, struct symbol, node);
182+
183+
if (s->type != STT_SECTION)
184+
return s;
185+
}
95186

96187
return NULL;
97188
}
98189

99-
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
190+
struct symbol *find_containing_func(struct section *sec, unsigned long offset)
191+
{
192+
struct rb_node *node;
193+
194+
rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
195+
struct symbol *s = rb_entry(node, struct symbol, node);
196+
197+
if (s->type == STT_FUNC)
198+
return s;
199+
}
200+
201+
return NULL;
202+
}
203+
204+
struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
100205
{
206+
struct section *sec;
101207
struct symbol *sym;
102208

103-
list_for_each_entry(sym, &sec->symbol_list, list)
104-
if (sym->type != STT_SECTION &&
105-
offset >= sym->offset && offset < sym->offset + sym->len)
106-
return sym;
209+
list_for_each_entry(sec, &elf->sections, list)
210+
list_for_each_entry(sym, &sec->symbol_list, list)
211+
if (!strcmp(sym->name, name))
212+
return sym;
107213

108214
return NULL;
109215
}
@@ -130,18 +236,6 @@ struct rela *find_rela_by_dest(struct section *sec, unsigned long offset)
130236
return find_rela_by_dest_range(sec, offset, 1);
131237
}
132238

133-
struct symbol *find_containing_func(struct section *sec, unsigned long offset)
134-
{
135-
struct symbol *func;
136-
137-
list_for_each_entry(func, &sec->symbol_list, list)
138-
if (func->type == STT_FUNC && offset >= func->offset &&
139-
offset < func->offset + func->len)
140-
return func;
141-
142-
return NULL;
143-
}
144-
145239
static int read_sections(struct elf *elf)
146240
{
147241
Elf_Scn *s = NULL;
@@ -225,8 +319,9 @@ static int read_sections(struct elf *elf)
225319
static int read_symbols(struct elf *elf)
226320
{
227321
struct section *symtab, *sec;
228-
struct symbol *sym, *pfunc, *alias;
229-
struct list_head *entry, *tmp;
322+
struct symbol *sym, *pfunc;
323+
struct list_head *entry;
324+
struct rb_node *pnode;
230325
int symbols_nr, i;
231326
char *coldstr;
232327

@@ -245,7 +340,7 @@ static int read_symbols(struct elf *elf)
245340
return -1;
246341
}
247342
memset(sym, 0, sizeof(*sym));
248-
alias = sym;
343+
sym->alias = sym;
249344

250345
sym->idx = i;
251346

@@ -283,29 +378,12 @@ static int read_symbols(struct elf *elf)
283378
sym->offset = sym->sym.st_value;
284379
sym->len = sym->sym.st_size;
285380

286-
/* sorted insert into a per-section list */
287-
entry = &sym->sec->symbol_list;
288-
list_for_each_prev(tmp, &sym->sec->symbol_list) {
289-
struct symbol *s;
290-
291-
s = list_entry(tmp, struct symbol, list);
292-
293-
if (sym->offset > s->offset) {
294-
entry = tmp;
295-
break;
296-
}
297-
298-
if (sym->offset == s->offset) {
299-
if (sym->len && sym->len == s->len && alias == sym)
300-
alias = s;
301-
302-
if (sym->len >= s->len) {
303-
entry = tmp;
304-
break;
305-
}
306-
}
307-
}
308-
sym->alias = alias;
381+
rb_add(&sym->sec->symbol_tree, &sym->node, symbol_to_offset);
382+
pnode = rb_prev(&sym->node);
383+
if (pnode)
384+
entry = &rb_entry(pnode, struct symbol, node)->list;
385+
else
386+
entry = &sym->sec->symbol_list;
309387
list_add(&sym->list, entry);
310388
hash_add(elf->symbol_hash, &sym->hash, sym->idx);
311389
}

tools/objtool/elf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <gelf.h>
1111
#include <linux/list.h>
1212
#include <linux/hashtable.h>
13+
#include <linux/rbtree.h>
1314
#include <linux/jhash.h>
1415

1516
#ifdef LIBELF_USE_DEPRECATED
@@ -29,6 +30,7 @@ struct section {
2930
struct hlist_node hash;
3031
struct hlist_node name_hash;
3132
GElf_Shdr sh;
33+
struct rb_root symbol_tree;
3234
struct list_head symbol_list;
3335
struct list_head rela_list;
3436
DECLARE_HASHTABLE(rela_hash, 16);
@@ -43,6 +45,7 @@ struct section {
4345

4446
struct symbol {
4547
struct list_head list;
48+
struct rb_node node;
4649
struct hlist_node hash;
4750
GElf_Sym sym;
4851
struct section *sec;

0 commit comments

Comments
 (0)