Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

objtool: Fix find_{symbol,func}_containing()

The current find_{symbol,func}_containing() functions are broken in
the face of overlapping symbols, exactly the case that is needed for a
new ibt/endbr supression.

Import interval_tree_generic.h into the tools tree and convert the
symbol tree to an interval tree to support proper range stabs.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20220915111146.330203761@infradead.org

+230 -55
+187
tools/include/linux/interval_tree_generic.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + /* 3 + Interval Trees 4 + (C) 2012 Michel Lespinasse <walken@google.com> 5 + 6 + 7 + include/linux/interval_tree_generic.h 8 + */ 9 + 10 + #include <linux/rbtree_augmented.h> 11 + 12 + /* 13 + * Template for implementing interval trees 14 + * 15 + * ITSTRUCT: struct type of the interval tree nodes 16 + * ITRB: name of struct rb_node field within ITSTRUCT 17 + * ITTYPE: type of the interval endpoints 18 + * ITSUBTREE: name of ITTYPE field within ITSTRUCT holding last-in-subtree 19 + * ITSTART(n): start endpoint of ITSTRUCT node n 20 + * ITLAST(n): last endpoint of ITSTRUCT node n 21 + * ITSTATIC: 'static' or empty 22 + * ITPREFIX: prefix to use for the inline tree definitions 23 + * 24 + * Note - before using this, please consider if generic version 25 + * (interval_tree.h) would work for you... 26 + */ 27 + 28 + #define INTERVAL_TREE_DEFINE(ITSTRUCT, ITRB, ITTYPE, ITSUBTREE, \ 29 + ITSTART, ITLAST, ITSTATIC, ITPREFIX) \ 30 + \ 31 + /* Callbacks for augmented rbtree insert and remove */ \ 32 + \ 33 + RB_DECLARE_CALLBACKS_MAX(static, ITPREFIX ## _augment, \ 34 + ITSTRUCT, ITRB, ITTYPE, ITSUBTREE, ITLAST) \ 35 + \ 36 + /* Insert / remove interval nodes from the tree */ \ 37 + \ 38 + ITSTATIC void ITPREFIX ## _insert(ITSTRUCT *node, \ 39 + struct rb_root_cached *root) \ 40 + { \ 41 + struct rb_node **link = &root->rb_root.rb_node, *rb_parent = NULL; \ 42 + ITTYPE start = ITSTART(node), last = ITLAST(node); \ 43 + ITSTRUCT *parent; \ 44 + bool leftmost = true; \ 45 + \ 46 + while (*link) { \ 47 + rb_parent = *link; \ 48 + parent = rb_entry(rb_parent, ITSTRUCT, ITRB); \ 49 + if (parent->ITSUBTREE < last) \ 50 + parent->ITSUBTREE = last; \ 51 + if (start < ITSTART(parent)) \ 52 + link = &parent->ITRB.rb_left; \ 53 + else { \ 54 + link = &parent->ITRB.rb_right; \ 55 + leftmost = false; \ 56 + } \ 57 + } \ 58 + \ 59 + node->ITSUBTREE = last; \ 60 + rb_link_node(&node->ITRB, rb_parent, link); \ 61 + rb_insert_augmented_cached(&node->ITRB, root, \ 62 + leftmost, &ITPREFIX ## _augment); \ 63 + } \ 64 + \ 65 + ITSTATIC void ITPREFIX ## _remove(ITSTRUCT *node, \ 66 + struct rb_root_cached *root) \ 67 + { \ 68 + rb_erase_augmented_cached(&node->ITRB, root, &ITPREFIX ## _augment); \ 69 + } \ 70 + \ 71 + /* \ 72 + * Iterate over intervals intersecting [start;last] \ 73 + * \ 74 + * Note that a node's interval intersects [start;last] iff: \ 75 + * Cond1: ITSTART(node) <= last \ 76 + * and \ 77 + * Cond2: start <= ITLAST(node) \ 78 + */ \ 79 + \ 80 + static ITSTRUCT * \ 81 + ITPREFIX ## _subtree_search(ITSTRUCT *node, ITTYPE start, ITTYPE last) \ 82 + { \ 83 + while (true) { \ 84 + /* \ 85 + * Loop invariant: start <= node->ITSUBTREE \ 86 + * (Cond2 is satisfied by one of the subtree nodes) \ 87 + */ \ 88 + if (node->ITRB.rb_left) { \ 89 + ITSTRUCT *left = rb_entry(node->ITRB.rb_left, \ 90 + ITSTRUCT, ITRB); \ 91 + if (start <= left->ITSUBTREE) { \ 92 + /* \ 93 + * Some nodes in left subtree satisfy Cond2. \ 94 + * Iterate to find the leftmost such node N. \ 95 + * If it also satisfies Cond1, that's the \ 96 + * match we are looking for. Otherwise, there \ 97 + * is no matching interval as nodes to the \ 98 + * right of N can't satisfy Cond1 either. \ 99 + */ \ 100 + node = left; \ 101 + continue; \ 102 + } \ 103 + } \ 104 + if (ITSTART(node) <= last) { /* Cond1 */ \ 105 + if (start <= ITLAST(node)) /* Cond2 */ \ 106 + return node; /* node is leftmost match */ \ 107 + if (node->ITRB.rb_right) { \ 108 + node = rb_entry(node->ITRB.rb_right, \ 109 + ITSTRUCT, ITRB); \ 110 + if (start <= node->ITSUBTREE) \ 111 + continue; \ 112 + } \ 113 + } \ 114 + return NULL; /* No match */ \ 115 + } \ 116 + } \ 117 + \ 118 + ITSTATIC ITSTRUCT * \ 119 + ITPREFIX ## _iter_first(struct rb_root_cached *root, \ 120 + ITTYPE start, ITTYPE last) \ 121 + { \ 122 + ITSTRUCT *node, *leftmost; \ 123 + \ 124 + if (!root->rb_root.rb_node) \ 125 + return NULL; \ 126 + \ 127 + /* \ 128 + * Fastpath range intersection/overlap between A: [a0, a1] and \ 129 + * B: [b0, b1] is given by: \ 130 + * \ 131 + * a0 <= b1 && b0 <= a1 \ 132 + * \ 133 + * ... where A holds the lock range and B holds the smallest \ 134 + * 'start' and largest 'last' in the tree. For the later, we \ 135 + * rely on the root node, which by augmented interval tree \ 136 + * property, holds the largest value in its last-in-subtree. \ 137 + * This allows mitigating some of the tree walk overhead for \ 138 + * for non-intersecting ranges, maintained and consulted in O(1). \ 139 + */ \ 140 + node = rb_entry(root->rb_root.rb_node, ITSTRUCT, ITRB); \ 141 + if (node->ITSUBTREE < start) \ 142 + return NULL; \ 143 + \ 144 + leftmost = rb_entry(root->rb_leftmost, ITSTRUCT, ITRB); \ 145 + if (ITSTART(leftmost) > last) \ 146 + return NULL; \ 147 + \ 148 + return ITPREFIX ## _subtree_search(node, start, last); \ 149 + } \ 150 + \ 151 + ITSTATIC ITSTRUCT * \ 152 + ITPREFIX ## _iter_next(ITSTRUCT *node, ITTYPE start, ITTYPE last) \ 153 + { \ 154 + struct rb_node *rb = node->ITRB.rb_right, *prev; \ 155 + \ 156 + while (true) { \ 157 + /* \ 158 + * Loop invariants: \ 159 + * Cond1: ITSTART(node) <= last \ 160 + * rb == node->ITRB.rb_right \ 161 + * \ 162 + * First, search right subtree if suitable \ 163 + */ \ 164 + if (rb) { \ 165 + ITSTRUCT *right = rb_entry(rb, ITSTRUCT, ITRB); \ 166 + if (start <= right->ITSUBTREE) \ 167 + return ITPREFIX ## _subtree_search(right, \ 168 + start, last); \ 169 + } \ 170 + \ 171 + /* Move up the tree until we come from a node's left child */ \ 172 + do { \ 173 + rb = rb_parent(&node->ITRB); \ 174 + if (!rb) \ 175 + return NULL; \ 176 + prev = &node->ITRB; \ 177 + node = rb_entry(rb, ITSTRUCT, ITRB); \ 178 + rb = node->ITRB.rb_right; \ 179 + } while (prev == rb); \ 180 + \ 181 + /* Check if the node intersects [start;last] */ \ 182 + if (last < ITSTART(node)) /* !Cond1 */ \ 183 + return NULL; \ 184 + else if (start <= ITLAST(node)) /* Cond2 */ \ 185 + return node; \ 186 + } \ 187 + }
+41 -54
tools/objtool/elf.c
··· 16 16 #include <string.h> 17 17 #include <unistd.h> 18 18 #include <errno.h> 19 + #include <linux/interval_tree_generic.h> 19 20 #include <objtool/builtin.h> 20 21 21 22 #include <objtool/elf.h> ··· 51 50 __elf_table(name); \ 52 51 }) 53 52 54 - static bool symbol_to_offset(struct rb_node *a, const struct rb_node *b) 53 + static inline unsigned long __sym_start(struct symbol *s) 55 54 { 56 - struct symbol *sa = rb_entry(a, struct symbol, node); 57 - struct symbol *sb = rb_entry(b, struct symbol, node); 58 - 59 - if (sa->offset < sb->offset) 60 - return true; 61 - if (sa->offset > sb->offset) 62 - return false; 63 - 64 - if (sa->len < sb->len) 65 - return true; 66 - if (sa->len > sb->len) 67 - return false; 68 - 69 - sa->alias = sb; 70 - 71 - return false; 55 + return s->offset; 72 56 } 73 57 74 - static int symbol_by_offset(const void *key, const struct rb_node *node) 58 + static inline unsigned long __sym_last(struct symbol *s) 75 59 { 76 - const struct symbol *s = rb_entry(node, struct symbol, node); 77 - const unsigned long *o = key; 78 - 79 - if (*o < s->offset) 80 - return -1; 81 - if (*o >= s->offset + s->len) 82 - return 1; 83 - 84 - return 0; 60 + return s->offset + s->len - 1; 85 61 } 62 + 63 + INTERVAL_TREE_DEFINE(struct symbol, node, unsigned long, __subtree_last, 64 + __sym_start, __sym_last, static, __sym) 65 + 66 + #define __sym_for_each(_iter, _tree, _start, _end) \ 67 + for (_iter = __sym_iter_first((_tree), (_start), (_end)); \ 68 + _iter; _iter = __sym_iter_next(_iter, (_start), (_end))) 86 69 87 70 struct symbol_hole { 88 71 unsigned long key; ··· 132 147 133 148 struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) 134 149 { 135 - struct rb_node *node; 150 + struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 151 + struct symbol *iter; 136 152 137 - rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 138 - struct symbol *s = rb_entry(node, struct symbol, node); 139 - 140 - if (s->offset == offset && s->type != STT_SECTION) 141 - return s; 153 + __sym_for_each(iter, tree, offset, offset) { 154 + if (iter->offset == offset && iter->type != STT_SECTION) 155 + return iter; 142 156 } 143 157 144 158 return NULL; ··· 145 161 146 162 struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) 147 163 { 148 - struct rb_node *node; 164 + struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 165 + struct symbol *iter; 149 166 150 - rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 151 - struct symbol *s = rb_entry(node, struct symbol, node); 152 - 153 - if (s->offset == offset && s->type == STT_FUNC) 154 - return s; 167 + __sym_for_each(iter, tree, offset, offset) { 168 + if (iter->offset == offset && iter->type == STT_FUNC) 169 + return iter; 155 170 } 156 171 157 172 return NULL; ··· 158 175 159 176 struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset) 160 177 { 161 - struct rb_node *node; 178 + struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 179 + struct symbol *iter; 162 180 163 - rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 164 - struct symbol *s = rb_entry(node, struct symbol, node); 165 - 166 - if (s->type != STT_SECTION) 167 - return s; 181 + __sym_for_each(iter, tree, offset, offset) { 182 + if (iter->type != STT_SECTION) 183 + return iter; 168 184 } 169 185 170 186 return NULL; ··· 184 202 /* 185 203 * Find the rightmost symbol for which @offset is after it. 186 204 */ 187 - n = rb_find(&hole, &sec->symbol_tree, symbol_hole_by_offset); 205 + n = rb_find(&hole, &sec->symbol_tree.rb_root, symbol_hole_by_offset); 188 206 189 207 /* found a symbol that contains @offset */ 190 208 if (n) ··· 206 224 207 225 struct symbol *find_func_containing(struct section *sec, unsigned long offset) 208 226 { 209 - struct rb_node *node; 227 + struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 228 + struct symbol *iter; 210 229 211 - rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 212 - struct symbol *s = rb_entry(node, struct symbol, node); 213 - 214 - if (s->type == STT_FUNC) 215 - return s; 230 + __sym_for_each(iter, tree, offset, offset) { 231 + if (iter->type == STT_FUNC) 232 + return iter; 216 233 } 217 234 218 235 return NULL; ··· 354 373 { 355 374 struct list_head *entry; 356 375 struct rb_node *pnode; 376 + struct symbol *iter; 357 377 358 378 INIT_LIST_HEAD(&sym->pv_target); 359 379 sym->alias = sym; ··· 368 386 sym->offset = sym->sym.st_value; 369 387 sym->len = sym->sym.st_size; 370 388 371 - rb_add(&sym->node, &sym->sec->symbol_tree, symbol_to_offset); 389 + __sym_for_each(iter, &sym->sec->symbol_tree, sym->offset, sym->offset) { 390 + if (iter->offset == sym->offset && iter->type == sym->type) 391 + iter->alias = sym; 392 + } 393 + 394 + __sym_insert(sym, &sym->sec->symbol_tree); 372 395 pnode = rb_prev(&sym->node); 373 396 if (pnode) 374 397 entry = &rb_entry(pnode, struct symbol, node)->list; ··· 388 401 * can exist within a function, confusing the sorting. 389 402 */ 390 403 if (!sym->len) 391 - rb_erase(&sym->node, &sym->sec->symbol_tree); 404 + __sym_remove(sym, &sym->sec->symbol_tree); 392 405 } 393 406 394 407 static int read_symbols(struct elf *elf)
+2 -1
tools/objtool/include/objtool/elf.h
··· 30 30 struct hlist_node hash; 31 31 struct hlist_node name_hash; 32 32 GElf_Shdr sh; 33 - struct rb_root symbol_tree; 33 + struct rb_root_cached symbol_tree; 34 34 struct list_head symbol_list; 35 35 struct list_head reloc_list; 36 36 struct section *base, *reloc; ··· 53 53 unsigned char bind, type; 54 54 unsigned long offset; 55 55 unsigned int len; 56 + unsigned long __subtree_last; 56 57 struct symbol *pfunc, *cfunc, *alias; 57 58 u8 uaccess_safe : 1; 58 59 u8 static_call_tramp : 1;