diff options
| -rw-r--r-- | tools/objtool/check.c | 93 | ||||
| -rw-r--r-- | tools/objtool/elf.c | 42 | ||||
| -rw-r--r-- | tools/objtool/elf.h | 2 |
3 files changed, 93 insertions, 44 deletions
diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 264522d4e4af..14daf6a27d9f 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c | |||
| @@ -59,6 +59,31 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file, | |||
| 59 | return next; | 59 | return next; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | static struct instruction *next_insn_same_func(struct objtool_file *file, | ||
| 63 | struct instruction *insn) | ||
| 64 | { | ||
| 65 | struct instruction *next = list_next_entry(insn, list); | ||
| 66 | struct symbol *func = insn->func; | ||
| 67 | |||
| 68 | if (!func) | ||
| 69 | return NULL; | ||
| 70 | |||
| 71 | if (&next->list != &file->insn_list && next->func == func) | ||
| 72 | return next; | ||
| 73 | |||
| 74 | /* Check if we're already in the subfunction: */ | ||
| 75 | if (func == func->cfunc) | ||
| 76 | return NULL; | ||
| 77 | |||
| 78 | /* Move to the subfunction: */ | ||
| 79 | return find_insn(file, func->cfunc->sec, func->cfunc->offset); | ||
| 80 | } | ||
| 81 | |||
| 82 | #define func_for_each_insn_all(file, func, insn) \ | ||
| 83 | for (insn = find_insn(file, func->sec, func->offset); \ | ||
| 84 | insn; \ | ||
| 85 | insn = next_insn_same_func(file, insn)) | ||
| 86 | |||
| 62 | #define func_for_each_insn(file, func, insn) \ | 87 | #define func_for_each_insn(file, func, insn) \ |
| 63 | for (insn = find_insn(file, func->sec, func->offset); \ | 88 | for (insn = find_insn(file, func->sec, func->offset); \ |
| 64 | insn && &insn->list != &file->insn_list && \ | 89 | insn && &insn->list != &file->insn_list && \ |
| @@ -149,10 +174,14 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func, | |||
| 149 | if (!strcmp(func->name, global_noreturns[i])) | 174 | if (!strcmp(func->name, global_noreturns[i])) |
| 150 | return 1; | 175 | return 1; |
| 151 | 176 | ||
| 152 | if (!func->sec) | 177 | if (!func->len) |
| 153 | return 0; | 178 | return 0; |
| 154 | 179 | ||
| 155 | func_for_each_insn(file, func, insn) { | 180 | insn = find_insn(file, func->sec, func->offset); |
| 181 | if (!insn->func) | ||
| 182 | return 0; | ||
| 183 | |||
| 184 | func_for_each_insn_all(file, func, insn) { | ||
| 156 | empty = false; | 185 | empty = false; |
| 157 | 186 | ||
| 158 | if (insn->type == INSN_RETURN) | 187 | if (insn->type == INSN_RETURN) |
| @@ -167,28 +196,17 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func, | |||
| 167 | * case, the function's dead-end status depends on whether the target | 196 | * case, the function's dead-end status depends on whether the target |
| 168 | * of the sibling call returns. | 197 | * of the sibling call returns. |
| 169 | */ | 198 | */ |
| 170 | func_for_each_insn(file, func, insn) { | 199 | func_for_each_insn_all(file, func, insn) { |
| 171 | if (insn->sec != func->sec || | ||
| 172 | insn->offset >= func->offset + func->len) | ||
| 173 | break; | ||
| 174 | |||
| 175 | if (insn->type == INSN_JUMP_UNCONDITIONAL) { | 200 | if (insn->type == INSN_JUMP_UNCONDITIONAL) { |
| 176 | struct instruction *dest = insn->jump_dest; | 201 | struct instruction *dest = insn->jump_dest; |
| 177 | struct symbol *dest_func; | ||
| 178 | 202 | ||
| 179 | if (!dest) | 203 | if (!dest) |
| 180 | /* sibling call to another file */ | 204 | /* sibling call to another file */ |
| 181 | return 0; | 205 | return 0; |
| 182 | 206 | ||
| 183 | if (dest->sec != func->sec || | 207 | if (dest->func && dest->func->pfunc != insn->func->pfunc) { |
| 184 | dest->offset < func->offset || | ||
| 185 | dest->offset >= func->offset + func->len) { | ||
| 186 | /* local sibling call */ | ||
| 187 | dest_func = find_symbol_by_offset(dest->sec, | ||
| 188 | dest->offset); | ||
| 189 | if (!dest_func) | ||
| 190 | continue; | ||
| 191 | 208 | ||
| 209 | /* local sibling call */ | ||
| 192 | if (recursion == 5) { | 210 | if (recursion == 5) { |
| 193 | /* | 211 | /* |
| 194 | * Infinite recursion: two functions | 212 | * Infinite recursion: two functions |
| @@ -199,7 +217,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func, | |||
| 199 | return 0; | 217 | return 0; |
| 200 | } | 218 | } |
| 201 | 219 | ||
| 202 | return __dead_end_function(file, dest_func, | 220 | return __dead_end_function(file, dest->func, |
| 203 | recursion + 1); | 221 | recursion + 1); |
| 204 | } | 222 | } |
| 205 | } | 223 | } |
| @@ -426,7 +444,7 @@ static void add_ignores(struct objtool_file *file) | |||
| 426 | if (!ignore_func(file, func)) | 444 | if (!ignore_func(file, func)) |
| 427 | continue; | 445 | continue; |
| 428 | 446 | ||
| 429 | func_for_each_insn(file, func, insn) | 447 | func_for_each_insn_all(file, func, insn) |
| 430 | insn->ignore = true; | 448 | insn->ignore = true; |
| 431 | } | 449 | } |
| 432 | } | 450 | } |
| @@ -786,9 +804,8 @@ out: | |||
| 786 | return ret; | 804 | return ret; |
| 787 | } | 805 | } |
| 788 | 806 | ||
| 789 | static int add_switch_table(struct objtool_file *file, struct symbol *func, | 807 | static int add_switch_table(struct objtool_file *file, struct instruction *insn, |
| 790 | struct instruction *insn, struct rela *table, | 808 | struct rela *table, struct rela *next_table) |
| 791 | struct rela *next_table) | ||
| 792 | { | 809 | { |
| 793 | struct rela *rela = table; | 810 | struct rela *rela = table; |
| 794 | struct instruction *alt_insn; | 811 | struct instruction *alt_insn; |
| @@ -798,18 +815,13 @@ static int add_switch_table(struct objtool_file *file, struct symbol *func, | |||
| 798 | if (rela == next_table) | 815 | if (rela == next_table) |
| 799 | break; | 816 | break; |
| 800 | 817 | ||
| 801 | if (rela->sym->sec != insn->sec || | 818 | alt_insn = find_insn(file, rela->sym->sec, rela->addend); |
| 802 | rela->addend <= func->offset || | 819 | if (!alt_insn) |
| 803 | rela->addend >= func->offset + func->len) | ||
| 804 | break; | 820 | break; |
| 805 | 821 | ||
| 806 | alt_insn = find_insn(file, insn->sec, rela->addend); | 822 | /* Make sure the jmp dest is in the function or subfunction: */ |
| 807 | if (!alt_insn) { | 823 | if (alt_insn->func->pfunc != insn->func->pfunc) |
| 808 | WARN("%s: can't find instruction at %s+0x%x", | 824 | break; |
| 809 | file->rodata->rela->name, insn->sec->name, | ||
| 810 | rela->addend); | ||
| 811 | return -1; | ||
| 812 | } | ||
| 813 | 825 | ||
| 814 | alt = malloc(sizeof(*alt)); | 826 | alt = malloc(sizeof(*alt)); |
| 815 | if (!alt) { | 827 | if (!alt) { |
| @@ -947,7 +959,7 @@ static int add_func_switch_tables(struct objtool_file *file, | |||
| 947 | struct rela *rela, *prev_rela = NULL; | 959 | struct rela *rela, *prev_rela = NULL; |
| 948 | int ret; | 960 | int ret; |
| 949 | 961 | ||
| 950 | func_for_each_insn(file, func, insn) { | 962 | func_for_each_insn_all(file, func, insn) { |
| 951 | if (!last) | 963 | if (!last) |
| 952 | last = insn; | 964 | last = insn; |
| 953 | 965 | ||
| @@ -978,8 +990,7 @@ static int add_func_switch_tables(struct objtool_file *file, | |||
| 978 | * the beginning of another switch table in the same function. | 990 | * the beginning of another switch table in the same function. |
| 979 | */ | 991 | */ |
| 980 | if (prev_jump) { | 992 | if (prev_jump) { |
| 981 | ret = add_switch_table(file, func, prev_jump, prev_rela, | 993 | ret = add_switch_table(file, prev_jump, prev_rela, rela); |
| 982 | rela); | ||
| 983 | if (ret) | 994 | if (ret) |
| 984 | return ret; | 995 | return ret; |
| 985 | } | 996 | } |
| @@ -989,7 +1000,7 @@ static int add_func_switch_tables(struct objtool_file *file, | |||
| 989 | } | 1000 | } |
| 990 | 1001 | ||
| 991 | if (prev_jump) { | 1002 | if (prev_jump) { |
| 992 | ret = add_switch_table(file, func, prev_jump, prev_rela, NULL); | 1003 | ret = add_switch_table(file, prev_jump, prev_rela, NULL); |
| 993 | if (ret) | 1004 | if (ret) |
| 994 | return ret; | 1005 | return ret; |
| 995 | } | 1006 | } |
| @@ -1753,15 +1764,13 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |||
| 1753 | while (1) { | 1764 | while (1) { |
| 1754 | next_insn = next_insn_same_sec(file, insn); | 1765 | next_insn = next_insn_same_sec(file, insn); |
| 1755 | 1766 | ||
| 1756 | 1767 | if (file->c_file && func && insn->func && func != insn->func->pfunc) { | |
| 1757 | if (file->c_file && func && insn->func && func != insn->func) { | ||
| 1758 | WARN("%s() falls through to next function %s()", | 1768 | WARN("%s() falls through to next function %s()", |
| 1759 | func->name, insn->func->name); | 1769 | func->name, insn->func->name); |
| 1760 | return 1; | 1770 | return 1; |
| 1761 | } | 1771 | } |
| 1762 | 1772 | ||
| 1763 | if (insn->func) | 1773 | func = insn->func ? insn->func->pfunc : NULL; |
| 1764 | func = insn->func; | ||
| 1765 | 1774 | ||
| 1766 | if (func && insn->ignore) { | 1775 | if (func && insn->ignore) { |
| 1767 | WARN_FUNC("BUG: why am I validating an ignored function?", | 1776 | WARN_FUNC("BUG: why am I validating an ignored function?", |
| @@ -1782,7 +1791,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |||
| 1782 | 1791 | ||
| 1783 | i = insn; | 1792 | i = insn; |
| 1784 | save_insn = NULL; | 1793 | save_insn = NULL; |
| 1785 | func_for_each_insn_continue_reverse(file, func, i) { | 1794 | func_for_each_insn_continue_reverse(file, insn->func, i) { |
| 1786 | if (i->save) { | 1795 | if (i->save) { |
| 1787 | save_insn = i; | 1796 | save_insn = i; |
| 1788 | break; | 1797 | break; |
| @@ -1869,7 +1878,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |||
| 1869 | case INSN_JUMP_UNCONDITIONAL: | 1878 | case INSN_JUMP_UNCONDITIONAL: |
| 1870 | if (insn->jump_dest && | 1879 | if (insn->jump_dest && |
| 1871 | (!func || !insn->jump_dest->func || | 1880 | (!func || !insn->jump_dest->func || |
| 1872 | func == insn->jump_dest->func)) { | 1881 | insn->jump_dest->func->pfunc == func)) { |
| 1873 | ret = validate_branch(file, insn->jump_dest, | 1882 | ret = validate_branch(file, insn->jump_dest, |
| 1874 | state); | 1883 | state); |
| 1875 | if (ret) | 1884 | if (ret) |
| @@ -2064,7 +2073,7 @@ static int validate_functions(struct objtool_file *file) | |||
| 2064 | 2073 | ||
| 2065 | for_each_sec(file, sec) { | 2074 | for_each_sec(file, sec) { |
| 2066 | list_for_each_entry(func, &sec->symbol_list, list) { | 2075 | list_for_each_entry(func, &sec->symbol_list, list) { |
| 2067 | if (func->type != STT_FUNC) | 2076 | if (func->type != STT_FUNC || func->pfunc != func) |
| 2068 | continue; | 2077 | continue; |
| 2069 | 2078 | ||
| 2070 | insn = find_insn(file, sec, func->offset); | 2079 | insn = find_insn(file, sec, func->offset); |
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index c1c338661699..4e60e105583e 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c | |||
| @@ -79,6 +79,19 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) | |||
| 79 | return NULL; | 79 | return NULL; |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | struct symbol *find_symbol_by_name(struct elf *elf, const char *name) | ||
| 83 | { | ||
| 84 | struct section *sec; | ||
| 85 | struct symbol *sym; | ||
| 86 | |||
| 87 | list_for_each_entry(sec, &elf->sections, list) | ||
| 88 | list_for_each_entry(sym, &sec->symbol_list, list) | ||
| 89 | if (!strcmp(sym->name, name)) | ||
| 90 | return sym; | ||
| 91 | |||
| 92 | return NULL; | ||
| 93 | } | ||
| 94 | |||
| 82 | struct symbol *find_symbol_containing(struct section *sec, unsigned long offset) | 95 | struct symbol *find_symbol_containing(struct section *sec, unsigned long offset) |
| 83 | { | 96 | { |
| 84 | struct symbol *sym; | 97 | struct symbol *sym; |
| @@ -203,10 +216,11 @@ static int read_sections(struct elf *elf) | |||
| 203 | 216 | ||
| 204 | static int read_symbols(struct elf *elf) | 217 | static int read_symbols(struct elf *elf) |
| 205 | { | 218 | { |
| 206 | struct section *symtab; | 219 | struct section *symtab, *sec; |
| 207 | struct symbol *sym; | 220 | struct symbol *sym, *pfunc; |
| 208 | struct list_head *entry, *tmp; | 221 | struct list_head *entry, *tmp; |
| 209 | int symbols_nr, i; | 222 | int symbols_nr, i; |
| 223 | char *coldstr; | ||
| 210 | 224 | ||
| 211 | symtab = find_section_by_name(elf, ".symtab"); | 225 | symtab = find_section_by_name(elf, ".symtab"); |
| 212 | if (!symtab) { | 226 | if (!symtab) { |
| @@ -281,6 +295,30 @@ static int read_symbols(struct elf *elf) | |||
| 281 | hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx); | 295 | hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx); |
| 282 | } | 296 | } |
| 283 | 297 | ||
| 298 | /* Create parent/child links for any cold subfunctions */ | ||
| 299 | list_for_each_entry(sec, &elf->sections, list) { | ||
| 300 | list_for_each_entry(sym, &sec->symbol_list, list) { | ||
| 301 | if (sym->type != STT_FUNC) | ||
| 302 | continue; | ||
| 303 | sym->pfunc = sym->cfunc = sym; | ||
| 304 | coldstr = strstr(sym->name, ".cold."); | ||
| 305 | if (coldstr) { | ||
| 306 | coldstr[0] = '\0'; | ||
| 307 | pfunc = find_symbol_by_name(elf, sym->name); | ||
| 308 | coldstr[0] = '.'; | ||
| 309 | |||
| 310 | if (!pfunc) { | ||
| 311 | WARN("%s(): can't find parent function", | ||
| 312 | sym->name); | ||
| 313 | goto err; | ||
| 314 | } | ||
| 315 | |||
| 316 | sym->pfunc = pfunc; | ||
| 317 | pfunc->cfunc = sym; | ||
| 318 | } | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 284 | return 0; | 322 | return 0; |
| 285 | 323 | ||
| 286 | err: | 324 | err: |
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index d86e2ff14466..de5cd2ddded9 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h | |||
| @@ -61,6 +61,7 @@ struct symbol { | |||
| 61 | unsigned char bind, type; | 61 | unsigned char bind, type; |
| 62 | unsigned long offset; | 62 | unsigned long offset; |
| 63 | unsigned int len; | 63 | unsigned int len; |
| 64 | struct symbol *pfunc, *cfunc; | ||
| 64 | }; | 65 | }; |
| 65 | 66 | ||
| 66 | struct rela { | 67 | struct rela { |
| @@ -86,6 +87,7 @@ struct elf { | |||
| 86 | struct elf *elf_open(const char *name, int flags); | 87 | struct elf *elf_open(const char *name, int flags); |
| 87 | struct section *find_section_by_name(struct elf *elf, const char *name); | 88 | struct section *find_section_by_name(struct elf *elf, const char *name); |
| 88 | struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); | 89 | struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); |
| 90 | struct symbol *find_symbol_by_name(struct elf *elf, const char *name); | ||
| 89 | struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); | 91 | struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); |
| 90 | struct rela *find_rela_by_dest(struct section *sec, unsigned long offset); | 92 | struct rela *find_rela_by_dest(struct section *sec, unsigned long offset); |
| 91 | struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, | 93 | struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, |
