blob: 282a60368b14df9e88b304d7e60aac77fd8e6bc0 [file] [log] [blame]
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001/*
2 * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19 * objtool check:
20 *
21 * This command analyzes every .o file and ensures the validity of its stack
22 * trace metadata. It enforces a set of rules on asm code and C inline
23 * assembly code so that stack traces can be reliable.
24 *
25 * For more information, see tools/objtool/Documentation/stack-validation.txt.
26 */
27
28#include <string.h>
Arnaldo Carvalho de Melod0761e32016-07-07 15:42:33 -030029#include <stdlib.h>
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060030#include <subcmd/parse-options.h>
31
32#include "builtin.h"
33#include "elf.h"
34#include "special.h"
35#include "arch.h"
36#include "warn.h"
37
Josh Poimboeuf042ba732016-03-09 00:07:00 -060038#include <linux/hashtable.h>
Arnaldo Carvalho de Melo00614592017-04-17 11:58:55 -030039#include <linux/kernel.h>
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060040
41#define STATE_FP_SAVED 0x1
42#define STATE_FP_SETUP 0x2
43#define STATE_FENTRY 0x4
44
45struct instruction {
46 struct list_head list;
Josh Poimboeuf042ba732016-03-09 00:07:00 -060047 struct hlist_node hash;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060048 struct section *sec;
49 unsigned long offset;
50 unsigned int len, state;
51 unsigned char type;
52 unsigned long immediate;
Josh Poimboeufd1091c72017-02-21 15:35:32 -060053 bool alt_group, visited, dead_end;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060054 struct symbol *call_dest;
55 struct instruction *jump_dest;
56 struct list_head alts;
Josh Poimboeufb1547d32016-04-15 09:17:10 -050057 struct symbol *func;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060058};
59
60struct alternative {
61 struct list_head list;
62 struct instruction *insn;
63};
64
65struct objtool_file {
66 struct elf *elf;
Josh Poimboeufa196e172016-03-09 00:06:57 -060067 struct list_head insn_list;
Josh Poimboeuf042ba732016-03-09 00:07:00 -060068 DECLARE_HASHTABLE(insn_hash, 16);
69 struct section *rodata, *whitelist;
Josh Poimboeufb1547d32016-04-15 09:17:10 -050070 bool ignore_unreachables, c_file;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060071};
72
73const char *objname;
74static bool nofp;
75
Josh Poimboeuf74aec052016-03-09 00:06:55 -060076static struct instruction *find_insn(struct objtool_file *file,
77 struct section *sec, unsigned long offset)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060078{
79 struct instruction *insn;
80
Josh Poimboeuf042ba732016-03-09 00:07:00 -060081 hash_for_each_possible(file->insn_hash, insn, hash, offset)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060082 if (insn->sec == sec && insn->offset == offset)
83 return insn;
84
85 return NULL;
86}
87
Josh Poimboeuf74aec052016-03-09 00:06:55 -060088static struct instruction *next_insn_same_sec(struct objtool_file *file,
89 struct instruction *insn)
90{
91 struct instruction *next = list_next_entry(insn, list);
92
Josh Poimboeufa196e172016-03-09 00:06:57 -060093 if (&next->list == &file->insn_list || next->sec != insn->sec)
Josh Poimboeuf74aec052016-03-09 00:06:55 -060094 return NULL;
95
96 return next;
97}
98
Josh Poimboeuf9cfffb12016-10-13 16:22:53 -050099static bool gcov_enabled(struct objtool_file *file)
100{
101 struct section *sec;
102 struct symbol *sym;
103
104 list_for_each_entry(sec, &file->elf->sections, list)
105 list_for_each_entry(sym, &sec->symbol_list, list)
106 if (!strncmp(sym->name, "__gcov_.", 8))
107 return true;
108
109 return false;
110}
111
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600112#define for_each_insn(file, insn) \
Josh Poimboeufa196e172016-03-09 00:06:57 -0600113 list_for_each_entry(insn, &file->insn_list, list)
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600114
115#define func_for_each_insn(file, func, insn) \
116 for (insn = find_insn(file, func->sec, func->offset); \
Josh Poimboeufa196e172016-03-09 00:06:57 -0600117 insn && &insn->list != &file->insn_list && \
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600118 insn->sec == func->sec && \
119 insn->offset < func->offset + func->len; \
120 insn = list_next_entry(insn, list))
121
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500122#define func_for_each_insn_continue_reverse(file, func, insn) \
123 for (insn = list_prev_entry(insn, list); \
124 &insn->list != &file->insn_list && \
125 insn->sec == func->sec && insn->offset >= func->offset; \
126 insn = list_prev_entry(insn, list))
127
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600128#define sec_for_each_insn_from(file, insn) \
129 for (; insn; insn = next_insn_same_sec(file, insn))
130
131
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600132/*
133 * Check if the function has been manually whitelisted with the
134 * STACK_FRAME_NON_STANDARD macro, or if it should be automatically whitelisted
135 * due to its use of a context switching instruction.
136 */
137static bool ignore_func(struct objtool_file *file, struct symbol *func)
138{
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600139 struct rela *rela;
140 struct instruction *insn;
141
142 /* check for STACK_FRAME_NON_STANDARD */
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600143 if (file->whitelist && file->whitelist->rela)
Josh Poimboeuf0ea5ad82016-06-15 15:45:58 -0500144 list_for_each_entry(rela, &file->whitelist->rela->rela_list, list) {
145 if (rela->sym->type == STT_SECTION &&
146 rela->sym->sec == func->sec &&
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600147 rela->addend == func->offset)
148 return true;
Josh Poimboeuf0ea5ad82016-06-15 15:45:58 -0500149 if (rela->sym->type == STT_FUNC && rela->sym == func)
150 return true;
151 }
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600152
153 /* check if it has a context switching instruction */
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600154 func_for_each_insn(file, func, insn)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600155 if (insn->type == INSN_CONTEXT_SWITCH)
156 return true;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600157
158 return false;
159}
160
161/*
162 * This checks to see if the given function is a "noreturn" function.
163 *
164 * For global functions which are outside the scope of this object file, we
165 * have to keep a manual list of them.
166 *
167 * For local functions, we have to detect them manually by simply looking for
168 * the lack of a return instruction.
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600169 *
170 * Returns:
171 * -1: error
172 * 0: no dead end
173 * 1: dead end
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600174 */
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600175static int __dead_end_function(struct objtool_file *file, struct symbol *func,
176 int recursion)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600177{
178 int i;
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600179 struct instruction *insn;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600180 bool empty = true;
181
182 /*
183 * Unfortunately these have to be hard coded because the noreturn
184 * attribute isn't provided in ELF data.
185 */
186 static const char * const global_noreturns[] = {
187 "__stack_chk_fail",
188 "panic",
189 "do_exit",
Josh Poimboeufc1fad9e2016-09-22 16:21:25 -0500190 "do_task_dead",
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600191 "__module_put_and_exit",
192 "complete_and_exit",
193 "kvm_spurious_fault",
194 "__reiserfs_panic",
195 "lbug_with_loc"
196 };
197
198 if (func->bind == STB_WEAK)
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600199 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600200
201 if (func->bind == STB_GLOBAL)
202 for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
203 if (!strcmp(func->name, global_noreturns[i]))
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600204 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600205
206 if (!func->sec)
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600207 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600208
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600209 func_for_each_insn(file, func, insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600210 empty = false;
211
212 if (insn->type == INSN_RETURN)
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600213 return 0;
Josh Poimboeuf81bfafc2016-03-09 00:06:51 -0600214 }
215
216 if (empty)
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600217 return 0;
Josh Poimboeuf81bfafc2016-03-09 00:06:51 -0600218
219 /*
220 * A function can have a sibling call instead of a return. In that
221 * case, the function's dead-end status depends on whether the target
222 * of the sibling call returns.
223 */
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600224 func_for_each_insn(file, func, insn) {
Josh Poimboeuf81bfafc2016-03-09 00:06:51 -0600225 if (insn->sec != func->sec ||
226 insn->offset >= func->offset + func->len)
227 break;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600228
229 if (insn->type == INSN_JUMP_UNCONDITIONAL) {
230 struct instruction *dest = insn->jump_dest;
231 struct symbol *dest_func;
232
233 if (!dest)
234 /* sibling call to another file */
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600235 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600236
237 if (dest->sec != func->sec ||
238 dest->offset < func->offset ||
239 dest->offset >= func->offset + func->len) {
240 /* local sibling call */
241 dest_func = find_symbol_by_offset(dest->sec,
242 dest->offset);
243 if (!dest_func)
244 continue;
245
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600246 if (recursion == 5) {
247 WARN_FUNC("infinite recursion (objtool bug!)",
248 dest->sec, dest->offset);
249 return -1;
250 }
251
252 return __dead_end_function(file, dest_func,
253 recursion + 1);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600254 }
255 }
256
Josh Poimboeufb1547d32016-04-15 09:17:10 -0500257 if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600258 /* sibling call */
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600259 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600260 }
261
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600262 return 1;
263}
264
265static int dead_end_function(struct objtool_file *file, struct symbol *func)
266{
267 return __dead_end_function(file, func, 0);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600268}
269
270/*
271 * Call the arch-specific instruction decoder for all the instructions and add
Josh Poimboeufa196e172016-03-09 00:06:57 -0600272 * them to the global instruction list.
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600273 */
274static int decode_instructions(struct objtool_file *file)
275{
276 struct section *sec;
Josh Poimboeufb1547d32016-04-15 09:17:10 -0500277 struct symbol *func;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600278 unsigned long offset;
279 struct instruction *insn;
280 int ret;
281
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600282 list_for_each_entry(sec, &file->elf->sections, list) {
283
284 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
285 continue;
286
287 for (offset = 0; offset < sec->len; offset += insn->len) {
288 insn = malloc(sizeof(*insn));
289 memset(insn, 0, sizeof(*insn));
290
291 INIT_LIST_HEAD(&insn->alts);
292 insn->sec = sec;
293 insn->offset = offset;
294
295 ret = arch_decode_instruction(file->elf, sec, offset,
296 sec->len - offset,
297 &insn->len, &insn->type,
298 &insn->immediate);
299 if (ret)
300 return ret;
301
302 if (!insn->type || insn->type > INSN_LAST) {
303 WARN_FUNC("invalid instruction type %d",
304 insn->sec, insn->offset, insn->type);
305 return -1;
306 }
307
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600308 hash_add(file->insn_hash, &insn->hash, insn->offset);
Josh Poimboeufa196e172016-03-09 00:06:57 -0600309 list_add_tail(&insn->list, &file->insn_list);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600310 }
Josh Poimboeufb1547d32016-04-15 09:17:10 -0500311
312 list_for_each_entry(func, &sec->symbol_list, list) {
313 if (func->type != STT_FUNC)
314 continue;
315
316 if (!find_insn(file, sec, func->offset)) {
317 WARN("%s(): can't find starting instruction",
318 func->name);
319 return -1;
320 }
321
322 func_for_each_insn(file, func, insn)
323 if (!insn->func)
324 insn->func = func;
325 }
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600326 }
327
328 return 0;
329}
330
331/*
Josh Poimboeufd1091c72017-02-21 15:35:32 -0600332 * Find all uses of the unreachable() macro, which are code path dead ends.
333 */
334static int add_dead_ends(struct objtool_file *file)
335{
336 struct section *sec;
337 struct rela *rela;
338 struct instruction *insn;
339 bool found;
340
Josh Poimboeufe390f9a2017-03-01 12:04:44 -0600341 sec = find_section_by_name(file->elf, ".rela.discard.unreachable");
Josh Poimboeufd1091c72017-02-21 15:35:32 -0600342 if (!sec)
343 return 0;
344
345 list_for_each_entry(rela, &sec->rela_list, list) {
346 if (rela->sym->type != STT_SECTION) {
Josh Poimboeufe390f9a2017-03-01 12:04:44 -0600347 WARN("unexpected relocation symbol type in %s", sec->name);
Josh Poimboeufd1091c72017-02-21 15:35:32 -0600348 return -1;
349 }
350 insn = find_insn(file, rela->sym->sec, rela->addend);
351 if (insn)
352 insn = list_prev_entry(insn, list);
353 else if (rela->addend == rela->sym->sec->len) {
354 found = false;
355 list_for_each_entry_reverse(insn, &file->insn_list, list) {
356 if (insn->sec == rela->sym->sec) {
357 found = true;
358 break;
359 }
360 }
361
362 if (!found) {
363 WARN("can't find unreachable insn at %s+0x%x",
364 rela->sym->sec->name, rela->addend);
365 return -1;
366 }
367 } else {
368 WARN("can't find unreachable insn at %s+0x%x",
369 rela->sym->sec->name, rela->addend);
370 return -1;
371 }
372
373 insn->dead_end = true;
374 }
375
376 return 0;
377}
378
379/*
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600380 * Warnings shouldn't be reported for ignored functions.
381 */
Josh Poimboeufa196e172016-03-09 00:06:57 -0600382static void add_ignores(struct objtool_file *file)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600383{
384 struct instruction *insn;
385 struct section *sec;
386 struct symbol *func;
387
388 list_for_each_entry(sec, &file->elf->sections, list) {
Josh Poimboeufa196e172016-03-09 00:06:57 -0600389 list_for_each_entry(func, &sec->symbol_list, list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600390 if (func->type != STT_FUNC)
391 continue;
392
393 if (!ignore_func(file, func))
394 continue;
395
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600396 func_for_each_insn(file, func, insn)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600397 insn->visited = true;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600398 }
399 }
400}
401
402/*
403 * Find the destination instructions for all jumps.
404 */
Josh Poimboeufa196e172016-03-09 00:06:57 -0600405static int add_jump_destinations(struct objtool_file *file)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600406{
407 struct instruction *insn;
408 struct rela *rela;
409 struct section *dest_sec;
410 unsigned long dest_off;
411
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600412 for_each_insn(file, insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600413 if (insn->type != INSN_JUMP_CONDITIONAL &&
414 insn->type != INSN_JUMP_UNCONDITIONAL)
415 continue;
416
417 /* skip ignores */
418 if (insn->visited)
419 continue;
420
421 rela = find_rela_by_dest_range(insn->sec, insn->offset,
422 insn->len);
423 if (!rela) {
424 dest_sec = insn->sec;
425 dest_off = insn->offset + insn->len + insn->immediate;
426 } else if (rela->sym->type == STT_SECTION) {
427 dest_sec = rela->sym->sec;
428 dest_off = rela->addend + 4;
429 } else if (rela->sym->sec->idx) {
430 dest_sec = rela->sym->sec;
431 dest_off = rela->sym->sym.st_value + rela->addend + 4;
432 } else {
433 /* sibling call */
434 insn->jump_dest = 0;
435 continue;
436 }
437
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600438 insn->jump_dest = find_insn(file, dest_sec, dest_off);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600439 if (!insn->jump_dest) {
440
441 /*
442 * This is a special case where an alt instruction
443 * jumps past the end of the section. These are
444 * handled later in handle_group_alt().
445 */
446 if (!strcmp(insn->sec->name, ".altinstr_replacement"))
447 continue;
448
449 WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
450 insn->sec, insn->offset, dest_sec->name,
451 dest_off);
452 return -1;
453 }
454 }
455
456 return 0;
457}
458
459/*
460 * Find the destination instructions for all calls.
461 */
Josh Poimboeufa196e172016-03-09 00:06:57 -0600462static int add_call_destinations(struct objtool_file *file)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600463{
464 struct instruction *insn;
465 unsigned long dest_off;
466 struct rela *rela;
467
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600468 for_each_insn(file, insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600469 if (insn->type != INSN_CALL)
470 continue;
471
472 rela = find_rela_by_dest_range(insn->sec, insn->offset,
473 insn->len);
474 if (!rela) {
475 dest_off = insn->offset + insn->len + insn->immediate;
476 insn->call_dest = find_symbol_by_offset(insn->sec,
477 dest_off);
478 if (!insn->call_dest) {
479 WARN_FUNC("can't find call dest symbol at offset 0x%lx",
480 insn->sec, insn->offset, dest_off);
481 return -1;
482 }
483 } else if (rela->sym->type == STT_SECTION) {
484 insn->call_dest = find_symbol_by_offset(rela->sym->sec,
485 rela->addend+4);
486 if (!insn->call_dest ||
487 insn->call_dest->type != STT_FUNC) {
488 WARN_FUNC("can't find call dest symbol at %s+0x%x",
489 insn->sec, insn->offset,
490 rela->sym->sec->name,
491 rela->addend + 4);
492 return -1;
493 }
494 } else
495 insn->call_dest = rela->sym;
496 }
497
498 return 0;
499}
500
501/*
502 * The .alternatives section requires some extra special care, over and above
503 * what other special sections require:
504 *
505 * 1. Because alternatives are patched in-place, we need to insert a fake jump
506 * instruction at the end so that validate_branch() skips all the original
507 * replaced instructions when validating the new instruction path.
508 *
509 * 2. An added wrinkle is that the new instruction length might be zero. In
510 * that case the old instructions are replaced with noops. We simulate that
511 * by creating a fake jump as the only new instruction.
512 *
513 * 3. In some cases, the alternative section includes an instruction which
514 * conditionally jumps to the _end_ of the entry. We have to modify these
515 * jumps' destinations to point back to .text rather than the end of the
516 * entry in .altinstr_replacement.
517 *
518 * 4. It has been requested that we don't validate the !POPCNT feature path
519 * which is a "very very small percentage of machines".
520 */
521static int handle_group_alt(struct objtool_file *file,
522 struct special_alt *special_alt,
523 struct instruction *orig_insn,
524 struct instruction **new_insn)
525{
526 struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump;
527 unsigned long dest_off;
528
529 last_orig_insn = NULL;
530 insn = orig_insn;
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600531 sec_for_each_insn_from(file, insn) {
532 if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600533 break;
534
535 if (special_alt->skip_orig)
536 insn->type = INSN_NOP;
537
538 insn->alt_group = true;
539 last_orig_insn = insn;
540 }
541
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600542 if (!next_insn_same_sec(file, last_orig_insn)) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600543 WARN("%s: don't know how to handle alternatives at end of section",
544 special_alt->orig_sec->name);
545 return -1;
546 }
547
548 fake_jump = malloc(sizeof(*fake_jump));
549 if (!fake_jump) {
550 WARN("malloc failed");
551 return -1;
552 }
553 memset(fake_jump, 0, sizeof(*fake_jump));
554 INIT_LIST_HEAD(&fake_jump->alts);
555 fake_jump->sec = special_alt->new_sec;
556 fake_jump->offset = -1;
557 fake_jump->type = INSN_JUMP_UNCONDITIONAL;
558 fake_jump->jump_dest = list_next_entry(last_orig_insn, list);
559
560 if (!special_alt->new_len) {
561 *new_insn = fake_jump;
562 return 0;
563 }
564
565 last_new_insn = NULL;
566 insn = *new_insn;
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600567 sec_for_each_insn_from(file, insn) {
568 if (insn->offset >= special_alt->new_off + special_alt->new_len)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600569 break;
570
571 last_new_insn = insn;
572
573 if (insn->type != INSN_JUMP_CONDITIONAL &&
574 insn->type != INSN_JUMP_UNCONDITIONAL)
575 continue;
576
577 if (!insn->immediate)
578 continue;
579
580 dest_off = insn->offset + insn->len + insn->immediate;
581 if (dest_off == special_alt->new_off + special_alt->new_len)
582 insn->jump_dest = fake_jump;
583
584 if (!insn->jump_dest) {
585 WARN_FUNC("can't find alternative jump destination",
586 insn->sec, insn->offset);
587 return -1;
588 }
589 }
590
591 if (!last_new_insn) {
592 WARN_FUNC("can't find last new alternative instruction",
593 special_alt->new_sec, special_alt->new_off);
594 return -1;
595 }
596
597 list_add(&fake_jump->list, &last_new_insn->list);
598
599 return 0;
600}
601
602/*
603 * A jump table entry can either convert a nop to a jump or a jump to a nop.
604 * If the original instruction is a jump, make the alt entry an effective nop
605 * by just skipping the original instruction.
606 */
607static int handle_jump_alt(struct objtool_file *file,
608 struct special_alt *special_alt,
609 struct instruction *orig_insn,
610 struct instruction **new_insn)
611{
612 if (orig_insn->type == INSN_NOP)
613 return 0;
614
615 if (orig_insn->type != INSN_JUMP_UNCONDITIONAL) {
616 WARN_FUNC("unsupported instruction at jump label",
617 orig_insn->sec, orig_insn->offset);
618 return -1;
619 }
620
621 *new_insn = list_next_entry(orig_insn, list);
622 return 0;
623}
624
625/*
626 * Read all the special sections which have alternate instructions which can be
627 * patched in or redirected to at runtime. Each instruction having alternate
628 * instruction(s) has them added to its insn->alts list, which will be
629 * traversed in validate_branch().
630 */
Josh Poimboeufa196e172016-03-09 00:06:57 -0600631static int add_special_section_alts(struct objtool_file *file)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600632{
633 struct list_head special_alts;
634 struct instruction *orig_insn, *new_insn;
635 struct special_alt *special_alt, *tmp;
636 struct alternative *alt;
637 int ret;
638
639 ret = special_get_alts(file->elf, &special_alts);
640 if (ret)
641 return ret;
642
643 list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
644 alt = malloc(sizeof(*alt));
645 if (!alt) {
646 WARN("malloc failed");
647 ret = -1;
648 goto out;
649 }
650
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600651 orig_insn = find_insn(file, special_alt->orig_sec,
652 special_alt->orig_off);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600653 if (!orig_insn) {
654 WARN_FUNC("special: can't find orig instruction",
655 special_alt->orig_sec, special_alt->orig_off);
656 ret = -1;
657 goto out;
658 }
659
660 new_insn = NULL;
661 if (!special_alt->group || special_alt->new_len) {
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600662 new_insn = find_insn(file, special_alt->new_sec,
663 special_alt->new_off);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600664 if (!new_insn) {
665 WARN_FUNC("special: can't find new instruction",
666 special_alt->new_sec,
667 special_alt->new_off);
668 ret = -1;
669 goto out;
670 }
671 }
672
673 if (special_alt->group) {
674 ret = handle_group_alt(file, special_alt, orig_insn,
675 &new_insn);
676 if (ret)
677 goto out;
678 } else if (special_alt->jump_or_nop) {
679 ret = handle_jump_alt(file, special_alt, orig_insn,
680 &new_insn);
681 if (ret)
682 goto out;
683 }
684
685 alt->insn = new_insn;
686 list_add_tail(&alt->list, &orig_insn->alts);
687
688 list_del(&special_alt->list);
689 free(special_alt);
690 }
691
692out:
693 return ret;
694}
695
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600696static int add_switch_table(struct objtool_file *file, struct symbol *func,
697 struct instruction *insn, struct rela *table,
698 struct rela *next_table)
699{
700 struct rela *rela = table;
701 struct instruction *alt_insn;
702 struct alternative *alt;
703
704 list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) {
705 if (rela == next_table)
706 break;
707
708 if (rela->sym->sec != insn->sec ||
709 rela->addend <= func->offset ||
710 rela->addend >= func->offset + func->len)
711 break;
712
713 alt_insn = find_insn(file, insn->sec, rela->addend);
714 if (!alt_insn) {
715 WARN("%s: can't find instruction at %s+0x%x",
716 file->rodata->rela->name, insn->sec->name,
717 rela->addend);
718 return -1;
719 }
720
721 alt = malloc(sizeof(*alt));
722 if (!alt) {
723 WARN("malloc failed");
724 return -1;
725 }
726
727 alt->insn = alt_insn;
728 list_add_tail(&alt->list, &insn->alts);
729 }
730
731 return 0;
732}
733
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500734/*
735 * find_switch_table() - Given a dynamic jump, find the switch jump table in
736 * .rodata associated with it.
737 *
738 * There are 3 basic patterns:
739 *
740 * 1. jmpq *[rodata addr](,%reg,8)
741 *
742 * This is the most common case by far. It jumps to an address in a simple
743 * jump table which is stored in .rodata.
744 *
745 * 2. jmpq *[rodata addr](%rip)
746 *
747 * This is caused by a rare GCC quirk, currently only seen in three driver
748 * functions in the kernel, only with certain obscure non-distro configs.
749 *
750 * As part of an optimization, GCC makes a copy of an existing switch jump
751 * table, modifies it, and then hard-codes the jump (albeit with an indirect
752 * jump) to use a single entry in the table. The rest of the jump table and
753 * some of its jump targets remain as dead code.
754 *
755 * In such a case we can just crudely ignore all unreachable instruction
756 * warnings for the entire object file. Ideally we would just ignore them
757 * for the function, but that would require redesigning the code quite a
758 * bit. And honestly that's just not worth doing: unreachable instruction
759 * warnings are of questionable value anyway, and this is such a rare issue.
760 *
761 * 3. mov [rodata addr],%reg1
762 * ... some instructions ...
763 * jmpq *(%reg1,%reg2,8)
764 *
765 * This is a fairly uncommon pattern which is new for GCC 6. As of this
766 * writing, there are 11 occurrences of it in the allmodconfig kernel.
767 *
768 * TODO: Once we have DWARF CFI and smarter instruction decoding logic,
769 * ensure the same register is used in the mov and jump instructions.
770 */
771static struct rela *find_switch_table(struct objtool_file *file,
772 struct symbol *func,
773 struct instruction *insn)
774{
775 struct rela *text_rela, *rodata_rela;
Josh Poimboeuf37327102016-10-13 16:22:52 -0500776 struct instruction *orig_insn = insn;
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500777
778 text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
779 if (text_rela && text_rela->sym == file->rodata->sym) {
780 /* case 1 */
781 rodata_rela = find_rela_by_dest(file->rodata,
782 text_rela->addend);
783 if (rodata_rela)
784 return rodata_rela;
785
786 /* case 2 */
787 rodata_rela = find_rela_by_dest(file->rodata,
788 text_rela->addend + 4);
789 if (!rodata_rela)
790 return NULL;
791 file->ignore_unreachables = true;
792 return rodata_rela;
793 }
794
795 /* case 3 */
796 func_for_each_insn_continue_reverse(file, func, insn) {
Josh Poimboeuf37327102016-10-13 16:22:52 -0500797 if (insn->type == INSN_JUMP_DYNAMIC)
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500798 break;
799
Josh Poimboeuf37327102016-10-13 16:22:52 -0500800 /* allow small jumps within the range */
801 if (insn->type == INSN_JUMP_UNCONDITIONAL &&
802 insn->jump_dest &&
803 (insn->jump_dest->offset <= insn->offset ||
Josh Poimboeuf56fb2d62016-10-26 10:34:08 -0500804 insn->jump_dest->offset > orig_insn->offset))
Josh Poimboeuf37327102016-10-13 16:22:52 -0500805 break;
806
Josh Poimboeuf5c51f4a2017-03-02 16:57:23 -0600807 /* look for a relocation which references .rodata */
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500808 text_rela = find_rela_by_dest_range(insn->sec, insn->offset,
809 insn->len);
Josh Poimboeuf5c51f4a2017-03-02 16:57:23 -0600810 if (!text_rela || text_rela->sym != file->rodata->sym)
811 continue;
812
813 /*
814 * Make sure the .rodata address isn't associated with a
815 * symbol. gcc jump tables are anonymous data.
816 */
817 if (find_symbol_containing(file->rodata, text_rela->addend))
818 continue;
819
820 return find_rela_by_dest(file->rodata, text_rela->addend);
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500821 }
822
823 return NULL;
824}
825
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600826static int add_func_switch_tables(struct objtool_file *file,
827 struct symbol *func)
828{
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500829 struct instruction *insn, *prev_jump = NULL;
830 struct rela *rela, *prev_rela = NULL;
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600831 int ret;
832
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600833 func_for_each_insn(file, func, insn) {
834 if (insn->type != INSN_JUMP_DYNAMIC)
835 continue;
836
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500837 rela = find_switch_table(file, func, insn);
838 if (!rela)
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600839 continue;
840
841 /*
842 * We found a switch table, but we don't know yet how big it
843 * is. Don't add it until we reach the end of the function or
844 * the beginning of another switch table in the same function.
845 */
846 if (prev_jump) {
847 ret = add_switch_table(file, func, prev_jump, prev_rela,
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500848 rela);
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600849 if (ret)
850 return ret;
851 }
852
853 prev_jump = insn;
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500854 prev_rela = rela;
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600855 }
856
857 if (prev_jump) {
858 ret = add_switch_table(file, func, prev_jump, prev_rela, NULL);
859 if (ret)
860 return ret;
861 }
862
863 return 0;
864}
865
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600866/*
867 * For some switch statements, gcc generates a jump table in the .rodata
868 * section which contains a list of addresses within the function to jump to.
869 * This finds these jump tables and adds them to the insn->alts lists.
870 */
Josh Poimboeufa196e172016-03-09 00:06:57 -0600871static int add_switch_table_alts(struct objtool_file *file)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600872{
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600873 struct section *sec;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600874 struct symbol *func;
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600875 int ret;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600876
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600877 if (!file->rodata || !file->rodata->rela)
878 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600879
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600880 list_for_each_entry(sec, &file->elf->sections, list) {
881 list_for_each_entry(func, &sec->symbol_list, list) {
882 if (func->type != STT_FUNC)
883 continue;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600884
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600885 ret = add_func_switch_tables(file, func);
886 if (ret)
887 return ret;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600888 }
889 }
890
891 return 0;
892}
893
894static int decode_sections(struct objtool_file *file)
895{
896 int ret;
897
898 ret = decode_instructions(file);
899 if (ret)
900 return ret;
901
Josh Poimboeufd1091c72017-02-21 15:35:32 -0600902 ret = add_dead_ends(file);
903 if (ret)
904 return ret;
905
Josh Poimboeufa196e172016-03-09 00:06:57 -0600906 add_ignores(file);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600907
Josh Poimboeufa196e172016-03-09 00:06:57 -0600908 ret = add_jump_destinations(file);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600909 if (ret)
910 return ret;
911
Josh Poimboeufa196e172016-03-09 00:06:57 -0600912 ret = add_call_destinations(file);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600913 if (ret)
914 return ret;
915
Josh Poimboeufa196e172016-03-09 00:06:57 -0600916 ret = add_special_section_alts(file);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600917 if (ret)
918 return ret;
919
Josh Poimboeufa196e172016-03-09 00:06:57 -0600920 ret = add_switch_table_alts(file);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600921 if (ret)
922 return ret;
923
924 return 0;
925}
926
927static bool is_fentry_call(struct instruction *insn)
928{
929 if (insn->type == INSN_CALL &&
930 insn->call_dest->type == STT_NOTYPE &&
931 !strcmp(insn->call_dest->name, "__fentry__"))
932 return true;
933
934 return false;
935}
936
937static bool has_modified_stack_frame(struct instruction *insn)
938{
939 return (insn->state & STATE_FP_SAVED) ||
940 (insn->state & STATE_FP_SETUP);
941}
942
943static bool has_valid_stack_frame(struct instruction *insn)
944{
945 return (insn->state & STATE_FP_SAVED) &&
946 (insn->state & STATE_FP_SETUP);
947}
948
Josh Poimboeufd8d1b2c2016-03-09 00:06:54 -0600949static unsigned int frame_state(unsigned long state)
950{
951 return (state & (STATE_FP_SAVED | STATE_FP_SETUP));
952}
953
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600954/*
955 * Follow the branch starting at the given instruction, and recursively follow
956 * any other branches (jumps). Meanwhile, track the frame pointer state at
957 * each instruction and validate all the rules described in
958 * tools/objtool/Documentation/stack-validation.txt.
959 */
960static int validate_branch(struct objtool_file *file,
961 struct instruction *first, unsigned char first_state)
962{
963 struct alternative *alt;
964 struct instruction *insn;
965 struct section *sec;
Josh Poimboeufb1547d32016-04-15 09:17:10 -0500966 struct symbol *func = NULL;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600967 unsigned char state;
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -0600968 int ret;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600969
970 insn = first;
971 sec = insn->sec;
972 state = first_state;
973
974 if (insn->alt_group && list_empty(&insn->alts)) {
975 WARN_FUNC("don't know how to handle branch to middle of alternative instruction group",
976 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -0600977 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600978 }
979
980 while (1) {
Josh Poimboeufb1547d32016-04-15 09:17:10 -0500981 if (file->c_file && insn->func) {
982 if (func && func != insn->func) {
983 WARN("%s() falls through to next function %s()",
984 func->name, insn->func->name);
985 return 1;
986 }
987
988 func = insn->func;
989 }
990
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600991 if (insn->visited) {
Josh Poimboeufd8d1b2c2016-03-09 00:06:54 -0600992 if (frame_state(insn->state) != frame_state(state)) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600993 WARN_FUNC("frame pointer state mismatch",
994 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -0600995 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600996 }
997
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -0600998 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600999 }
1000
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001001 insn->visited = true;
1002 insn->state = state;
1003
1004 list_for_each_entry(alt, &insn->alts, list) {
1005 ret = validate_branch(file, alt->insn, state);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001006 if (ret)
1007 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001008 }
1009
1010 switch (insn->type) {
1011
1012 case INSN_FP_SAVE:
1013 if (!nofp) {
1014 if (state & STATE_FP_SAVED) {
1015 WARN_FUNC("duplicate frame pointer save",
1016 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001017 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001018 }
1019 state |= STATE_FP_SAVED;
1020 }
1021 break;
1022
1023 case INSN_FP_SETUP:
1024 if (!nofp) {
1025 if (state & STATE_FP_SETUP) {
1026 WARN_FUNC("duplicate frame pointer setup",
1027 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001028 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001029 }
1030 state |= STATE_FP_SETUP;
1031 }
1032 break;
1033
1034 case INSN_FP_RESTORE:
1035 if (!nofp) {
1036 if (has_valid_stack_frame(insn))
1037 state &= ~STATE_FP_SETUP;
1038
1039 state &= ~STATE_FP_SAVED;
1040 }
1041 break;
1042
1043 case INSN_RETURN:
1044 if (!nofp && has_modified_stack_frame(insn)) {
1045 WARN_FUNC("return without frame pointer restore",
1046 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001047 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001048 }
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001049 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001050
1051 case INSN_CALL:
1052 if (is_fentry_call(insn)) {
1053 state |= STATE_FENTRY;
1054 break;
1055 }
1056
Josh Poimboeufb1e03242016-03-09 00:06:52 -06001057 ret = dead_end_function(file, insn->call_dest);
1058 if (ret == 1)
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001059 return 0;
Josh Poimboeufb1e03242016-03-09 00:06:52 -06001060 if (ret == -1)
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001061 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001062
1063 /* fallthrough */
1064 case INSN_CALL_DYNAMIC:
1065 if (!nofp && !has_valid_stack_frame(insn)) {
1066 WARN_FUNC("call without frame pointer save/setup",
1067 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001068 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001069 }
1070 break;
1071
1072 case INSN_JUMP_CONDITIONAL:
1073 case INSN_JUMP_UNCONDITIONAL:
1074 if (insn->jump_dest) {
1075 ret = validate_branch(file, insn->jump_dest,
1076 state);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001077 if (ret)
1078 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001079 } else if (has_modified_stack_frame(insn)) {
1080 WARN_FUNC("sibling call from callable instruction with changed frame pointer",
1081 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001082 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001083 } /* else it's a sibling call */
1084
1085 if (insn->type == INSN_JUMP_UNCONDITIONAL)
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001086 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001087
1088 break;
1089
1090 case INSN_JUMP_DYNAMIC:
1091 if (list_empty(&insn->alts) &&
1092 has_modified_stack_frame(insn)) {
1093 WARN_FUNC("sibling call from callable instruction with changed frame pointer",
1094 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001095 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001096 }
1097
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001098 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001099
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001100 default:
1101 break;
1102 }
1103
Josh Poimboeufd1091c72017-02-21 15:35:32 -06001104 if (insn->dead_end)
1105 return 0;
1106
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001107 insn = next_insn_same_sec(file, insn);
1108 if (!insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001109 WARN("%s: unexpected end of section", sec->name);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001110 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001111 }
1112 }
1113
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001114 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001115}
1116
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001117static bool is_kasan_insn(struct instruction *insn)
1118{
1119 return (insn->type == INSN_CALL &&
1120 !strcmp(insn->call_dest->name, "__asan_handle_no_return"));
1121}
1122
1123static bool is_ubsan_insn(struct instruction *insn)
1124{
1125 return (insn->type == INSN_CALL &&
1126 !strcmp(insn->call_dest->name,
1127 "__ubsan_handle_builtin_unreachable"));
1128}
1129
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001130static bool ignore_unreachable_insn(struct symbol *func,
1131 struct instruction *insn)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001132{
1133 int i;
1134
1135 if (insn->type == INSN_NOP)
1136 return true;
1137
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001138 /*
1139 * Check if this (or a subsequent) instruction is related to
1140 * CONFIG_UBSAN or CONFIG_KASAN.
1141 *
1142 * End the search at 5 instructions to avoid going into the weeds.
1143 */
1144 for (i = 0; i < 5; i++) {
1145
1146 if (is_kasan_insn(insn) || is_ubsan_insn(insn))
1147 return true;
1148
1149 if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest) {
1150 insn = insn->jump_dest;
1151 continue;
1152 }
1153
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001154 if (insn->offset + insn->len >= func->offset + func->len)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001155 break;
1156 insn = list_next_entry(insn, list);
1157 }
1158
1159 return false;
1160}
1161
1162static int validate_functions(struct objtool_file *file)
1163{
1164 struct section *sec;
1165 struct symbol *func;
1166 struct instruction *insn;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001167 int ret, warnings = 0;
1168
1169 list_for_each_entry(sec, &file->elf->sections, list) {
Josh Poimboeufa196e172016-03-09 00:06:57 -06001170 list_for_each_entry(func, &sec->symbol_list, list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001171 if (func->type != STT_FUNC)
1172 continue;
1173
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001174 insn = find_insn(file, sec, func->offset);
Josh Poimboeufb1547d32016-04-15 09:17:10 -05001175 if (!insn)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001176 continue;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001177
1178 ret = validate_branch(file, insn, 0);
1179 warnings += ret;
1180 }
1181 }
1182
1183 list_for_each_entry(sec, &file->elf->sections, list) {
Josh Poimboeufa196e172016-03-09 00:06:57 -06001184 list_for_each_entry(func, &sec->symbol_list, list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001185 if (func->type != STT_FUNC)
1186 continue;
1187
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001188 func_for_each_insn(file, func, insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001189 if (insn->visited)
1190 continue;
1191
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001192 insn->visited = true;
Josh Poimboeuf7e578442016-04-14 14:52:24 -05001193
1194 if (file->ignore_unreachables || warnings ||
1195 ignore_unreachable_insn(func, insn))
1196 continue;
1197
Josh Poimboeuf9cfffb12016-10-13 16:22:53 -05001198 /*
1199 * gcov produces a lot of unreachable
1200 * instructions. If we get an unreachable
1201 * warning and the file has gcov enabled, just
1202 * ignore it, and all other such warnings for
1203 * the file.
1204 */
1205 if (!file->ignore_unreachables &&
1206 gcov_enabled(file)) {
1207 file->ignore_unreachables = true;
1208 continue;
1209 }
1210
Josh Poimboeuf7e578442016-04-14 14:52:24 -05001211 WARN_FUNC("function has unreachable instruction", insn->sec, insn->offset);
1212 warnings++;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001213 }
1214 }
1215 }
1216
1217 return warnings;
1218}
1219
1220static int validate_uncallable_instructions(struct objtool_file *file)
1221{
1222 struct instruction *insn;
1223 int warnings = 0;
1224
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001225 for_each_insn(file, insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001226 if (!insn->visited && insn->type == INSN_RETURN) {
1227 WARN_FUNC("return instruction outside of a callable function",
1228 insn->sec, insn->offset);
1229 warnings++;
1230 }
1231 }
1232
1233 return warnings;
1234}
1235
1236static void cleanup(struct objtool_file *file)
1237{
1238 struct instruction *insn, *tmpinsn;
1239 struct alternative *alt, *tmpalt;
1240
Josh Poimboeufa196e172016-03-09 00:06:57 -06001241 list_for_each_entry_safe(insn, tmpinsn, &file->insn_list, list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001242 list_for_each_entry_safe(alt, tmpalt, &insn->alts, list) {
1243 list_del(&alt->list);
1244 free(alt);
1245 }
1246 list_del(&insn->list);
Josh Poimboeuf042ba732016-03-09 00:07:00 -06001247 hash_del(&insn->hash);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001248 free(insn);
1249 }
1250 elf_close(file->elf);
1251}
1252
1253const char * const check_usage[] = {
1254 "objtool check [<options>] file.o",
1255 NULL,
1256};
1257
1258int cmd_check(int argc, const char **argv)
1259{
1260 struct objtool_file file;
1261 int ret, warnings = 0;
1262
1263 const struct option options[] = {
1264 OPT_BOOLEAN('f', "no-fp", &nofp, "Skip frame pointer validation"),
1265 OPT_END(),
1266 };
1267
1268 argc = parse_options(argc, argv, options, check_usage, 0);
1269
1270 if (argc != 1)
1271 usage_with_options(check_usage, options);
1272
1273 objname = argv[0];
1274
1275 file.elf = elf_open(objname);
1276 if (!file.elf) {
1277 fprintf(stderr, "error reading elf file %s\n", objname);
1278 return 1;
1279 }
1280
Josh Poimboeufa196e172016-03-09 00:06:57 -06001281 INIT_LIST_HEAD(&file.insn_list);
Josh Poimboeuf042ba732016-03-09 00:07:00 -06001282 hash_init(file.insn_hash);
Josh Poimboeufe390f9a2017-03-01 12:04:44 -06001283 file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard");
Josh Poimboeuf7e578442016-04-14 14:52:24 -05001284 file.rodata = find_section_by_name(file.elf, ".rodata");
1285 file.ignore_unreachables = false;
Josh Poimboeufb1547d32016-04-15 09:17:10 -05001286 file.c_file = find_section_by_name(file.elf, ".comment");
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001287
1288 ret = decode_sections(&file);
1289 if (ret < 0)
1290 goto out;
1291 warnings += ret;
1292
1293 ret = validate_functions(&file);
1294 if (ret < 0)
1295 goto out;
1296 warnings += ret;
1297
1298 ret = validate_uncallable_instructions(&file);
1299 if (ret < 0)
1300 goto out;
1301 warnings += ret;
1302
1303out:
1304 cleanup(&file);
1305
1306 /* ignore warnings for now until we get all the code cleaned up */
1307 if (ret || warnings)
1308 return 0;
1309 return 0;
1310}