aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/objtool/check.c93
-rw-r--r--tools/objtool/elf.c42
-rw-r--r--tools/objtool/elf.h2
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
62static 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
789static int add_switch_table(struct objtool_file *file, struct symbol *func, 807static 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
82struct 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
82struct symbol *find_symbol_containing(struct section *sec, unsigned long offset) 95struct 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
204static int read_symbols(struct elf *elf) 217static 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
286err: 324err:
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
66struct rela { 67struct rela {
@@ -86,6 +87,7 @@ struct elf {
86struct elf *elf_open(const char *name, int flags); 87struct elf *elf_open(const char *name, int flags);
87struct section *find_section_by_name(struct elf *elf, const char *name); 88struct section *find_section_by_name(struct elf *elf, const char *name);
88struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); 89struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
90struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
89struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); 91struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
90struct rela *find_rela_by_dest(struct section *sec, unsigned long offset); 92struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
91struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, 93struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,