blob: 293a23e2796864f5ff7d50c34b89a5e7229b2da8 [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>
39
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060040#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
41
42#define STATE_FP_SAVED 0x1
43#define STATE_FP_SETUP 0x2
44#define STATE_FENTRY 0x4
45
46struct instruction {
47 struct list_head list;
Josh Poimboeuf042ba732016-03-09 00:07:00 -060048 struct hlist_node hash;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060049 struct section *sec;
50 unsigned long offset;
51 unsigned int len, state;
52 unsigned char type;
53 unsigned long immediate;
54 bool alt_group, visited;
55 struct symbol *call_dest;
56 struct instruction *jump_dest;
57 struct list_head alts;
Josh Poimboeufb1547d32016-04-15 09:17:10 -050058 struct symbol *func;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060059};
60
61struct alternative {
62 struct list_head list;
63 struct instruction *insn;
64};
65
66struct objtool_file {
67 struct elf *elf;
Josh Poimboeufa196e172016-03-09 00:06:57 -060068 struct list_head insn_list;
Josh Poimboeuf042ba732016-03-09 00:07:00 -060069 DECLARE_HASHTABLE(insn_hash, 16);
70 struct section *rodata, *whitelist;
Josh Poimboeufb1547d32016-04-15 09:17:10 -050071 bool ignore_unreachables, c_file;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060072};
73
74const char *objname;
75static bool nofp;
76
Josh Poimboeuf74aec052016-03-09 00:06:55 -060077static struct instruction *find_insn(struct objtool_file *file,
78 struct section *sec, unsigned long offset)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060079{
80 struct instruction *insn;
81
Josh Poimboeuf042ba732016-03-09 00:07:00 -060082 hash_for_each_possible(file->insn_hash, insn, hash, offset)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060083 if (insn->sec == sec && insn->offset == offset)
84 return insn;
85
86 return NULL;
87}
88
Josh Poimboeuf74aec052016-03-09 00:06:55 -060089static struct instruction *next_insn_same_sec(struct objtool_file *file,
90 struct instruction *insn)
91{
92 struct instruction *next = list_next_entry(insn, list);
93
Josh Poimboeufa196e172016-03-09 00:06:57 -060094 if (&next->list == &file->insn_list || next->sec != insn->sec)
Josh Poimboeuf74aec052016-03-09 00:06:55 -060095 return NULL;
96
97 return next;
98}
99
Josh Poimboeuf9cfffb12016-10-13 16:22:53 -0500100static bool gcov_enabled(struct objtool_file *file)
101{
102 struct section *sec;
103 struct symbol *sym;
104
105 list_for_each_entry(sec, &file->elf->sections, list)
106 list_for_each_entry(sym, &sec->symbol_list, list)
107 if (!strncmp(sym->name, "__gcov_.", 8))
108 return true;
109
110 return false;
111}
112
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600113#define for_each_insn(file, insn) \
Josh Poimboeufa196e172016-03-09 00:06:57 -0600114 list_for_each_entry(insn, &file->insn_list, list)
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600115
116#define func_for_each_insn(file, func, insn) \
117 for (insn = find_insn(file, func->sec, func->offset); \
Josh Poimboeufa196e172016-03-09 00:06:57 -0600118 insn && &insn->list != &file->insn_list && \
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600119 insn->sec == func->sec && \
120 insn->offset < func->offset + func->len; \
121 insn = list_next_entry(insn, list))
122
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500123#define func_for_each_insn_continue_reverse(file, func, insn) \
124 for (insn = list_prev_entry(insn, list); \
125 &insn->list != &file->insn_list && \
126 insn->sec == func->sec && insn->offset >= func->offset; \
127 insn = list_prev_entry(insn, list))
128
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600129#define sec_for_each_insn_from(file, insn) \
130 for (; insn; insn = next_insn_same_sec(file, insn))
131
132
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600133/*
134 * Check if the function has been manually whitelisted with the
135 * STACK_FRAME_NON_STANDARD macro, or if it should be automatically whitelisted
136 * due to its use of a context switching instruction.
137 */
138static bool ignore_func(struct objtool_file *file, struct symbol *func)
139{
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600140 struct rela *rela;
141 struct instruction *insn;
142
143 /* check for STACK_FRAME_NON_STANDARD */
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600144 if (file->whitelist && file->whitelist->rela)
Josh Poimboeuf0ea5ad82016-06-15 15:45:58 -0500145 list_for_each_entry(rela, &file->whitelist->rela->rela_list, list) {
146 if (rela->sym->type == STT_SECTION &&
147 rela->sym->sec == func->sec &&
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600148 rela->addend == func->offset)
149 return true;
Josh Poimboeuf0ea5ad82016-06-15 15:45:58 -0500150 if (rela->sym->type == STT_FUNC && rela->sym == func)
151 return true;
152 }
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600153
154 /* check if it has a context switching instruction */
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600155 func_for_each_insn(file, func, insn)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600156 if (insn->type == INSN_CONTEXT_SWITCH)
157 return true;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600158
159 return false;
160}
161
162/*
163 * This checks to see if the given function is a "noreturn" function.
164 *
165 * For global functions which are outside the scope of this object file, we
166 * have to keep a manual list of them.
167 *
168 * For local functions, we have to detect them manually by simply looking for
169 * the lack of a return instruction.
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600170 *
171 * Returns:
172 * -1: error
173 * 0: no dead end
174 * 1: dead end
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600175 */
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600176static int __dead_end_function(struct objtool_file *file, struct symbol *func,
177 int recursion)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600178{
179 int i;
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600180 struct instruction *insn;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600181 bool empty = true;
182
183 /*
184 * Unfortunately these have to be hard coded because the noreturn
185 * attribute isn't provided in ELF data.
186 */
187 static const char * const global_noreturns[] = {
188 "__stack_chk_fail",
189 "panic",
190 "do_exit",
Josh Poimboeufc1fad9e2016-09-22 16:21:25 -0500191 "do_task_dead",
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600192 "__module_put_and_exit",
193 "complete_and_exit",
194 "kvm_spurious_fault",
195 "__reiserfs_panic",
196 "lbug_with_loc"
197 };
198
199 if (func->bind == STB_WEAK)
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600200 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600201
202 if (func->bind == STB_GLOBAL)
203 for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
204 if (!strcmp(func->name, global_noreturns[i]))
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600205 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600206
207 if (!func->sec)
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600208 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600209
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600210 func_for_each_insn(file, func, insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600211 empty = false;
212
213 if (insn->type == INSN_RETURN)
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600214 return 0;
Josh Poimboeuf81bfafc2016-03-09 00:06:51 -0600215 }
216
217 if (empty)
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600218 return 0;
Josh Poimboeuf81bfafc2016-03-09 00:06:51 -0600219
220 /*
221 * A function can have a sibling call instead of a return. In that
222 * case, the function's dead-end status depends on whether the target
223 * of the sibling call returns.
224 */
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600225 func_for_each_insn(file, func, insn) {
Josh Poimboeuf81bfafc2016-03-09 00:06:51 -0600226 if (insn->sec != func->sec ||
227 insn->offset >= func->offset + func->len)
228 break;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600229
230 if (insn->type == INSN_JUMP_UNCONDITIONAL) {
231 struct instruction *dest = insn->jump_dest;
232 struct symbol *dest_func;
233
234 if (!dest)
235 /* sibling call to another file */
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600236 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600237
238 if (dest->sec != func->sec ||
239 dest->offset < func->offset ||
240 dest->offset >= func->offset + func->len) {
241 /* local sibling call */
242 dest_func = find_symbol_by_offset(dest->sec,
243 dest->offset);
244 if (!dest_func)
245 continue;
246
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600247 if (recursion == 5) {
248 WARN_FUNC("infinite recursion (objtool bug!)",
249 dest->sec, dest->offset);
250 return -1;
251 }
252
253 return __dead_end_function(file, dest_func,
254 recursion + 1);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600255 }
256 }
257
Josh Poimboeufb1547d32016-04-15 09:17:10 -0500258 if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600259 /* sibling call */
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600260 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600261 }
262
Josh Poimboeufb1e03242016-03-09 00:06:52 -0600263 return 1;
264}
265
266static int dead_end_function(struct objtool_file *file, struct symbol *func)
267{
268 return __dead_end_function(file, func, 0);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600269}
270
271/*
272 * Call the arch-specific instruction decoder for all the instructions and add
Josh Poimboeufa196e172016-03-09 00:06:57 -0600273 * them to the global instruction list.
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600274 */
275static int decode_instructions(struct objtool_file *file)
276{
277 struct section *sec;
Josh Poimboeufb1547d32016-04-15 09:17:10 -0500278 struct symbol *func;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600279 unsigned long offset;
280 struct instruction *insn;
281 int ret;
282
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600283 list_for_each_entry(sec, &file->elf->sections, list) {
284
285 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
286 continue;
287
288 for (offset = 0; offset < sec->len; offset += insn->len) {
289 insn = malloc(sizeof(*insn));
290 memset(insn, 0, sizeof(*insn));
291
292 INIT_LIST_HEAD(&insn->alts);
293 insn->sec = sec;
294 insn->offset = offset;
295
296 ret = arch_decode_instruction(file->elf, sec, offset,
297 sec->len - offset,
298 &insn->len, &insn->type,
299 &insn->immediate);
300 if (ret)
301 return ret;
302
303 if (!insn->type || insn->type > INSN_LAST) {
304 WARN_FUNC("invalid instruction type %d",
305 insn->sec, insn->offset, insn->type);
306 return -1;
307 }
308
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600309 hash_add(file->insn_hash, &insn->hash, insn->offset);
Josh Poimboeufa196e172016-03-09 00:06:57 -0600310 list_add_tail(&insn->list, &file->insn_list);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600311 }
Josh Poimboeufb1547d32016-04-15 09:17:10 -0500312
313 list_for_each_entry(func, &sec->symbol_list, list) {
314 if (func->type != STT_FUNC)
315 continue;
316
317 if (!find_insn(file, sec, func->offset)) {
318 WARN("%s(): can't find starting instruction",
319 func->name);
320 return -1;
321 }
322
323 func_for_each_insn(file, func, insn)
324 if (!insn->func)
325 insn->func = func;
326 }
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600327 }
328
329 return 0;
330}
331
332/*
333 * Warnings shouldn't be reported for ignored functions.
334 */
Josh Poimboeufa196e172016-03-09 00:06:57 -0600335static void add_ignores(struct objtool_file *file)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600336{
337 struct instruction *insn;
338 struct section *sec;
339 struct symbol *func;
340
341 list_for_each_entry(sec, &file->elf->sections, list) {
Josh Poimboeufa196e172016-03-09 00:06:57 -0600342 list_for_each_entry(func, &sec->symbol_list, list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600343 if (func->type != STT_FUNC)
344 continue;
345
346 if (!ignore_func(file, func))
347 continue;
348
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600349 func_for_each_insn(file, func, insn)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600350 insn->visited = true;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600351 }
352 }
353}
354
355/*
356 * Find the destination instructions for all jumps.
357 */
Josh Poimboeufa196e172016-03-09 00:06:57 -0600358static int add_jump_destinations(struct objtool_file *file)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600359{
360 struct instruction *insn;
361 struct rela *rela;
362 struct section *dest_sec;
363 unsigned long dest_off;
364
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600365 for_each_insn(file, insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600366 if (insn->type != INSN_JUMP_CONDITIONAL &&
367 insn->type != INSN_JUMP_UNCONDITIONAL)
368 continue;
369
370 /* skip ignores */
371 if (insn->visited)
372 continue;
373
374 rela = find_rela_by_dest_range(insn->sec, insn->offset,
375 insn->len);
376 if (!rela) {
377 dest_sec = insn->sec;
378 dest_off = insn->offset + insn->len + insn->immediate;
379 } else if (rela->sym->type == STT_SECTION) {
380 dest_sec = rela->sym->sec;
381 dest_off = rela->addend + 4;
382 } else if (rela->sym->sec->idx) {
383 dest_sec = rela->sym->sec;
384 dest_off = rela->sym->sym.st_value + rela->addend + 4;
Josh Poimboeuf3adb52ab2018-01-11 21:46:23 +0000385 } else if (strstr(rela->sym->name, "_indirect_thunk_")) {
386 /*
387 * Retpoline jumps are really dynamic jumps in
388 * disguise, so convert them accordingly.
389 */
390 insn->type = INSN_JUMP_DYNAMIC;
391 continue;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600392 } else {
393 /* sibling call */
394 insn->jump_dest = 0;
395 continue;
396 }
397
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600398 insn->jump_dest = find_insn(file, dest_sec, dest_off);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600399 if (!insn->jump_dest) {
400
401 /*
402 * This is a special case where an alt instruction
403 * jumps past the end of the section. These are
404 * handled later in handle_group_alt().
405 */
406 if (!strcmp(insn->sec->name, ".altinstr_replacement"))
407 continue;
408
409 WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
410 insn->sec, insn->offset, dest_sec->name,
411 dest_off);
412 return -1;
413 }
414 }
415
416 return 0;
417}
418
419/*
420 * Find the destination instructions for all calls.
421 */
Josh Poimboeufa196e172016-03-09 00:06:57 -0600422static int add_call_destinations(struct objtool_file *file)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600423{
424 struct instruction *insn;
425 unsigned long dest_off;
426 struct rela *rela;
427
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600428 for_each_insn(file, insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600429 if (insn->type != INSN_CALL)
430 continue;
431
432 rela = find_rela_by_dest_range(insn->sec, insn->offset,
433 insn->len);
434 if (!rela) {
435 dest_off = insn->offset + insn->len + insn->immediate;
436 insn->call_dest = find_symbol_by_offset(insn->sec,
437 dest_off);
438 if (!insn->call_dest) {
439 WARN_FUNC("can't find call dest symbol at offset 0x%lx",
440 insn->sec, insn->offset, dest_off);
441 return -1;
442 }
443 } else if (rela->sym->type == STT_SECTION) {
444 insn->call_dest = find_symbol_by_offset(rela->sym->sec,
445 rela->addend+4);
446 if (!insn->call_dest ||
447 insn->call_dest->type != STT_FUNC) {
448 WARN_FUNC("can't find call dest symbol at %s+0x%x",
449 insn->sec, insn->offset,
450 rela->sym->sec->name,
451 rela->addend + 4);
452 return -1;
453 }
454 } else
455 insn->call_dest = rela->sym;
456 }
457
458 return 0;
459}
460
461/*
462 * The .alternatives section requires some extra special care, over and above
463 * what other special sections require:
464 *
465 * 1. Because alternatives are patched in-place, we need to insert a fake jump
466 * instruction at the end so that validate_branch() skips all the original
467 * replaced instructions when validating the new instruction path.
468 *
469 * 2. An added wrinkle is that the new instruction length might be zero. In
470 * that case the old instructions are replaced with noops. We simulate that
471 * by creating a fake jump as the only new instruction.
472 *
473 * 3. In some cases, the alternative section includes an instruction which
474 * conditionally jumps to the _end_ of the entry. We have to modify these
475 * jumps' destinations to point back to .text rather than the end of the
476 * entry in .altinstr_replacement.
477 *
478 * 4. It has been requested that we don't validate the !POPCNT feature path
479 * which is a "very very small percentage of machines".
480 */
481static int handle_group_alt(struct objtool_file *file,
482 struct special_alt *special_alt,
483 struct instruction *orig_insn,
484 struct instruction **new_insn)
485{
486 struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump;
487 unsigned long dest_off;
488
489 last_orig_insn = NULL;
490 insn = orig_insn;
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600491 sec_for_each_insn_from(file, insn) {
492 if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600493 break;
494
495 if (special_alt->skip_orig)
496 insn->type = INSN_NOP;
497
498 insn->alt_group = true;
499 last_orig_insn = insn;
500 }
501
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600502 if (!next_insn_same_sec(file, last_orig_insn)) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600503 WARN("%s: don't know how to handle alternatives at end of section",
504 special_alt->orig_sec->name);
505 return -1;
506 }
507
508 fake_jump = malloc(sizeof(*fake_jump));
509 if (!fake_jump) {
510 WARN("malloc failed");
511 return -1;
512 }
513 memset(fake_jump, 0, sizeof(*fake_jump));
514 INIT_LIST_HEAD(&fake_jump->alts);
515 fake_jump->sec = special_alt->new_sec;
516 fake_jump->offset = -1;
517 fake_jump->type = INSN_JUMP_UNCONDITIONAL;
518 fake_jump->jump_dest = list_next_entry(last_orig_insn, list);
519
520 if (!special_alt->new_len) {
521 *new_insn = fake_jump;
522 return 0;
523 }
524
525 last_new_insn = NULL;
526 insn = *new_insn;
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600527 sec_for_each_insn_from(file, insn) {
528 if (insn->offset >= special_alt->new_off + special_alt->new_len)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600529 break;
530
531 last_new_insn = insn;
532
533 if (insn->type != INSN_JUMP_CONDITIONAL &&
534 insn->type != INSN_JUMP_UNCONDITIONAL)
535 continue;
536
537 if (!insn->immediate)
538 continue;
539
540 dest_off = insn->offset + insn->len + insn->immediate;
541 if (dest_off == special_alt->new_off + special_alt->new_len)
542 insn->jump_dest = fake_jump;
543
544 if (!insn->jump_dest) {
545 WARN_FUNC("can't find alternative jump destination",
546 insn->sec, insn->offset);
547 return -1;
548 }
549 }
550
551 if (!last_new_insn) {
552 WARN_FUNC("can't find last new alternative instruction",
553 special_alt->new_sec, special_alt->new_off);
554 return -1;
555 }
556
557 list_add(&fake_jump->list, &last_new_insn->list);
558
559 return 0;
560}
561
562/*
563 * A jump table entry can either convert a nop to a jump or a jump to a nop.
564 * If the original instruction is a jump, make the alt entry an effective nop
565 * by just skipping the original instruction.
566 */
567static int handle_jump_alt(struct objtool_file *file,
568 struct special_alt *special_alt,
569 struct instruction *orig_insn,
570 struct instruction **new_insn)
571{
572 if (orig_insn->type == INSN_NOP)
573 return 0;
574
575 if (orig_insn->type != INSN_JUMP_UNCONDITIONAL) {
576 WARN_FUNC("unsupported instruction at jump label",
577 orig_insn->sec, orig_insn->offset);
578 return -1;
579 }
580
581 *new_insn = list_next_entry(orig_insn, list);
582 return 0;
583}
584
585/*
586 * Read all the special sections which have alternate instructions which can be
587 * patched in or redirected to at runtime. Each instruction having alternate
588 * instruction(s) has them added to its insn->alts list, which will be
589 * traversed in validate_branch().
590 */
Josh Poimboeufa196e172016-03-09 00:06:57 -0600591static int add_special_section_alts(struct objtool_file *file)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600592{
593 struct list_head special_alts;
594 struct instruction *orig_insn, *new_insn;
595 struct special_alt *special_alt, *tmp;
596 struct alternative *alt;
597 int ret;
598
599 ret = special_get_alts(file->elf, &special_alts);
600 if (ret)
601 return ret;
602
603 list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
604 alt = malloc(sizeof(*alt));
605 if (!alt) {
606 WARN("malloc failed");
607 ret = -1;
608 goto out;
609 }
610
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600611 orig_insn = find_insn(file, special_alt->orig_sec,
612 special_alt->orig_off);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600613 if (!orig_insn) {
614 WARN_FUNC("special: can't find orig instruction",
615 special_alt->orig_sec, special_alt->orig_off);
616 ret = -1;
617 goto out;
618 }
619
620 new_insn = NULL;
621 if (!special_alt->group || special_alt->new_len) {
Josh Poimboeuf74aec052016-03-09 00:06:55 -0600622 new_insn = find_insn(file, special_alt->new_sec,
623 special_alt->new_off);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600624 if (!new_insn) {
625 WARN_FUNC("special: can't find new instruction",
626 special_alt->new_sec,
627 special_alt->new_off);
628 ret = -1;
629 goto out;
630 }
631 }
632
633 if (special_alt->group) {
634 ret = handle_group_alt(file, special_alt, orig_insn,
635 &new_insn);
636 if (ret)
637 goto out;
638 } else if (special_alt->jump_or_nop) {
639 ret = handle_jump_alt(file, special_alt, orig_insn,
640 &new_insn);
641 if (ret)
642 goto out;
643 }
644
645 alt->insn = new_insn;
646 list_add_tail(&alt->list, &orig_insn->alts);
647
648 list_del(&special_alt->list);
649 free(special_alt);
650 }
651
652out:
653 return ret;
654}
655
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600656static int add_switch_table(struct objtool_file *file, struct symbol *func,
657 struct instruction *insn, struct rela *table,
658 struct rela *next_table)
659{
660 struct rela *rela = table;
661 struct instruction *alt_insn;
662 struct alternative *alt;
663
664 list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) {
665 if (rela == next_table)
666 break;
667
668 if (rela->sym->sec != insn->sec ||
669 rela->addend <= func->offset ||
670 rela->addend >= func->offset + func->len)
671 break;
672
673 alt_insn = find_insn(file, insn->sec, rela->addend);
674 if (!alt_insn) {
675 WARN("%s: can't find instruction at %s+0x%x",
676 file->rodata->rela->name, insn->sec->name,
677 rela->addend);
678 return -1;
679 }
680
681 alt = malloc(sizeof(*alt));
682 if (!alt) {
683 WARN("malloc failed");
684 return -1;
685 }
686
687 alt->insn = alt_insn;
688 list_add_tail(&alt->list, &insn->alts);
689 }
690
691 return 0;
692}
693
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500694/*
695 * find_switch_table() - Given a dynamic jump, find the switch jump table in
696 * .rodata associated with it.
697 *
698 * There are 3 basic patterns:
699 *
700 * 1. jmpq *[rodata addr](,%reg,8)
701 *
702 * This is the most common case by far. It jumps to an address in a simple
703 * jump table which is stored in .rodata.
704 *
705 * 2. jmpq *[rodata addr](%rip)
706 *
707 * This is caused by a rare GCC quirk, currently only seen in three driver
708 * functions in the kernel, only with certain obscure non-distro configs.
709 *
710 * As part of an optimization, GCC makes a copy of an existing switch jump
711 * table, modifies it, and then hard-codes the jump (albeit with an indirect
712 * jump) to use a single entry in the table. The rest of the jump table and
713 * some of its jump targets remain as dead code.
714 *
715 * In such a case we can just crudely ignore all unreachable instruction
716 * warnings for the entire object file. Ideally we would just ignore them
717 * for the function, but that would require redesigning the code quite a
718 * bit. And honestly that's just not worth doing: unreachable instruction
719 * warnings are of questionable value anyway, and this is such a rare issue.
720 *
721 * 3. mov [rodata addr],%reg1
722 * ... some instructions ...
723 * jmpq *(%reg1,%reg2,8)
724 *
725 * This is a fairly uncommon pattern which is new for GCC 6. As of this
726 * writing, there are 11 occurrences of it in the allmodconfig kernel.
727 *
728 * TODO: Once we have DWARF CFI and smarter instruction decoding logic,
729 * ensure the same register is used in the mov and jump instructions.
730 */
731static struct rela *find_switch_table(struct objtool_file *file,
732 struct symbol *func,
733 struct instruction *insn)
734{
735 struct rela *text_rela, *rodata_rela;
Josh Poimboeuf37327102016-10-13 16:22:52 -0500736 struct instruction *orig_insn = insn;
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500737
738 text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
739 if (text_rela && text_rela->sym == file->rodata->sym) {
740 /* case 1 */
741 rodata_rela = find_rela_by_dest(file->rodata,
742 text_rela->addend);
743 if (rodata_rela)
744 return rodata_rela;
745
746 /* case 2 */
747 rodata_rela = find_rela_by_dest(file->rodata,
748 text_rela->addend + 4);
749 if (!rodata_rela)
750 return NULL;
751 file->ignore_unreachables = true;
752 return rodata_rela;
753 }
754
755 /* case 3 */
756 func_for_each_insn_continue_reverse(file, func, insn) {
Josh Poimboeuf37327102016-10-13 16:22:52 -0500757 if (insn->type == INSN_JUMP_DYNAMIC)
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500758 break;
759
Josh Poimboeuf37327102016-10-13 16:22:52 -0500760 /* allow small jumps within the range */
761 if (insn->type == INSN_JUMP_UNCONDITIONAL &&
762 insn->jump_dest &&
763 (insn->jump_dest->offset <= insn->offset ||
Josh Poimboeuf56fb2d62016-10-26 10:34:08 -0500764 insn->jump_dest->offset > orig_insn->offset))
Josh Poimboeuf37327102016-10-13 16:22:52 -0500765 break;
766
Josh Poimboeuf3e51ccb2017-03-02 16:57:23 -0600767 /* look for a relocation which references .rodata */
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500768 text_rela = find_rela_by_dest_range(insn->sec, insn->offset,
769 insn->len);
Josh Poimboeuf3e51ccb2017-03-02 16:57:23 -0600770 if (!text_rela || text_rela->sym != file->rodata->sym)
771 continue;
772
773 /*
774 * Make sure the .rodata address isn't associated with a
775 * symbol. gcc jump tables are anonymous data.
776 */
777 if (find_symbol_containing(file->rodata, text_rela->addend))
778 continue;
779
780 return find_rela_by_dest(file->rodata, text_rela->addend);
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500781 }
782
783 return NULL;
784}
785
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600786static int add_func_switch_tables(struct objtool_file *file,
787 struct symbol *func)
788{
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500789 struct instruction *insn, *prev_jump = NULL;
790 struct rela *rela, *prev_rela = NULL;
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600791 int ret;
792
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600793 func_for_each_insn(file, func, insn) {
794 if (insn->type != INSN_JUMP_DYNAMIC)
795 continue;
796
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500797 rela = find_switch_table(file, func, insn);
798 if (!rela)
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600799 continue;
800
801 /*
802 * We found a switch table, but we don't know yet how big it
803 * is. Don't add it until we reach the end of the function or
804 * the beginning of another switch table in the same function.
805 */
806 if (prev_jump) {
807 ret = add_switch_table(file, func, prev_jump, prev_rela,
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500808 rela);
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600809 if (ret)
810 return ret;
811 }
812
813 prev_jump = insn;
Josh Poimboeuf6d01f282016-07-28 19:14:58 -0500814 prev_rela = rela;
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600815 }
816
817 if (prev_jump) {
818 ret = add_switch_table(file, func, prev_jump, prev_rela, NULL);
819 if (ret)
820 return ret;
821 }
822
823 return 0;
824}
825
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600826/*
827 * For some switch statements, gcc generates a jump table in the .rodata
828 * section which contains a list of addresses within the function to jump to.
829 * This finds these jump tables and adds them to the insn->alts lists.
830 */
Josh Poimboeufa196e172016-03-09 00:06:57 -0600831static int add_switch_table_alts(struct objtool_file *file)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600832{
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600833 struct section *sec;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600834 struct symbol *func;
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600835 int ret;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600836
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600837 if (!file->rodata || !file->rodata->rela)
838 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600839
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600840 list_for_each_entry(sec, &file->elf->sections, list) {
841 list_for_each_entry(func, &sec->symbol_list, list) {
842 if (func->type != STT_FUNC)
843 continue;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600844
Josh Poimboeuf8133fbb2016-03-09 00:06:58 -0600845 ret = add_func_switch_tables(file, func);
846 if (ret)
847 return ret;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600848 }
849 }
850
851 return 0;
852}
853
854static int decode_sections(struct objtool_file *file)
855{
856 int ret;
857
858 ret = decode_instructions(file);
859 if (ret)
860 return ret;
861
Josh Poimboeufa196e172016-03-09 00:06:57 -0600862 add_ignores(file);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600863
Josh Poimboeufa196e172016-03-09 00:06:57 -0600864 ret = add_jump_destinations(file);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600865 if (ret)
866 return ret;
867
Josh Poimboeufa196e172016-03-09 00:06:57 -0600868 ret = add_call_destinations(file);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600869 if (ret)
870 return ret;
871
Josh Poimboeufa196e172016-03-09 00:06:57 -0600872 ret = add_special_section_alts(file);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600873 if (ret)
874 return ret;
875
Josh Poimboeufa196e172016-03-09 00:06:57 -0600876 ret = add_switch_table_alts(file);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600877 if (ret)
878 return ret;
879
880 return 0;
881}
882
883static bool is_fentry_call(struct instruction *insn)
884{
885 if (insn->type == INSN_CALL &&
886 insn->call_dest->type == STT_NOTYPE &&
887 !strcmp(insn->call_dest->name, "__fentry__"))
888 return true;
889
890 return false;
891}
892
893static bool has_modified_stack_frame(struct instruction *insn)
894{
895 return (insn->state & STATE_FP_SAVED) ||
896 (insn->state & STATE_FP_SETUP);
897}
898
899static bool has_valid_stack_frame(struct instruction *insn)
900{
901 return (insn->state & STATE_FP_SAVED) &&
902 (insn->state & STATE_FP_SETUP);
903}
904
Josh Poimboeufd8d1b2c2016-03-09 00:06:54 -0600905static unsigned int frame_state(unsigned long state)
906{
907 return (state & (STATE_FP_SAVED | STATE_FP_SETUP));
908}
909
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600910/*
911 * Follow the branch starting at the given instruction, and recursively follow
912 * any other branches (jumps). Meanwhile, track the frame pointer state at
913 * each instruction and validate all the rules described in
914 * tools/objtool/Documentation/stack-validation.txt.
915 */
916static int validate_branch(struct objtool_file *file,
917 struct instruction *first, unsigned char first_state)
918{
919 struct alternative *alt;
920 struct instruction *insn;
921 struct section *sec;
Josh Poimboeufb1547d32016-04-15 09:17:10 -0500922 struct symbol *func = NULL;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600923 unsigned char state;
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -0600924 int ret;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600925
926 insn = first;
927 sec = insn->sec;
928 state = first_state;
929
930 if (insn->alt_group && list_empty(&insn->alts)) {
931 WARN_FUNC("don't know how to handle branch to middle of alternative instruction group",
932 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -0600933 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600934 }
935
936 while (1) {
Josh Poimboeufb1547d32016-04-15 09:17:10 -0500937 if (file->c_file && insn->func) {
938 if (func && func != insn->func) {
939 WARN("%s() falls through to next function %s()",
940 func->name, insn->func->name);
941 return 1;
942 }
943
944 func = insn->func;
945 }
946
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600947 if (insn->visited) {
Josh Poimboeufd8d1b2c2016-03-09 00:06:54 -0600948 if (frame_state(insn->state) != frame_state(state)) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600949 WARN_FUNC("frame pointer state mismatch",
950 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -0600951 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600952 }
953
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -0600954 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600955 }
956
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600957 insn->visited = true;
958 insn->state = state;
959
960 list_for_each_entry(alt, &insn->alts, list) {
961 ret = validate_branch(file, alt->insn, state);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -0600962 if (ret)
963 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600964 }
965
966 switch (insn->type) {
967
968 case INSN_FP_SAVE:
969 if (!nofp) {
970 if (state & STATE_FP_SAVED) {
971 WARN_FUNC("duplicate frame pointer save",
972 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -0600973 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600974 }
975 state |= STATE_FP_SAVED;
976 }
977 break;
978
979 case INSN_FP_SETUP:
980 if (!nofp) {
981 if (state & STATE_FP_SETUP) {
982 WARN_FUNC("duplicate frame pointer setup",
983 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -0600984 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600985 }
986 state |= STATE_FP_SETUP;
987 }
988 break;
989
990 case INSN_FP_RESTORE:
991 if (!nofp) {
992 if (has_valid_stack_frame(insn))
993 state &= ~STATE_FP_SETUP;
994
995 state &= ~STATE_FP_SAVED;
996 }
997 break;
998
999 case INSN_RETURN:
1000 if (!nofp && has_modified_stack_frame(insn)) {
1001 WARN_FUNC("return without frame pointer restore",
1002 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001003 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001004 }
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001005 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001006
1007 case INSN_CALL:
1008 if (is_fentry_call(insn)) {
1009 state |= STATE_FENTRY;
1010 break;
1011 }
1012
Josh Poimboeufb1e03242016-03-09 00:06:52 -06001013 ret = dead_end_function(file, insn->call_dest);
1014 if (ret == 1)
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001015 return 0;
Josh Poimboeufb1e03242016-03-09 00:06:52 -06001016 if (ret == -1)
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001017 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001018
1019 /* fallthrough */
1020 case INSN_CALL_DYNAMIC:
1021 if (!nofp && !has_valid_stack_frame(insn)) {
1022 WARN_FUNC("call without frame pointer save/setup",
1023 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001024 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001025 }
1026 break;
1027
1028 case INSN_JUMP_CONDITIONAL:
1029 case INSN_JUMP_UNCONDITIONAL:
1030 if (insn->jump_dest) {
1031 ret = validate_branch(file, insn->jump_dest,
1032 state);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001033 if (ret)
1034 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001035 } else if (has_modified_stack_frame(insn)) {
1036 WARN_FUNC("sibling call from callable instruction with changed frame pointer",
1037 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001038 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001039 } /* else it's a sibling call */
1040
1041 if (insn->type == INSN_JUMP_UNCONDITIONAL)
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001042 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001043
1044 break;
1045
1046 case INSN_JUMP_DYNAMIC:
1047 if (list_empty(&insn->alts) &&
1048 has_modified_stack_frame(insn)) {
1049 WARN_FUNC("sibling call from callable instruction with changed frame pointer",
1050 sec, insn->offset);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001051 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001052 }
1053
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001054 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001055
1056 case INSN_BUG:
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001057 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001058
1059 default:
1060 break;
1061 }
1062
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001063 insn = next_insn_same_sec(file, insn);
1064 if (!insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001065 WARN("%s: unexpected end of section", sec->name);
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001066 return 1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001067 }
1068 }
1069
Josh Poimboeuf1bcb58a2016-03-09 00:07:01 -06001070 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001071}
1072
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001073static bool is_kasan_insn(struct instruction *insn)
1074{
1075 return (insn->type == INSN_CALL &&
1076 !strcmp(insn->call_dest->name, "__asan_handle_no_return"));
1077}
1078
1079static bool is_ubsan_insn(struct instruction *insn)
1080{
1081 return (insn->type == INSN_CALL &&
1082 !strcmp(insn->call_dest->name,
1083 "__ubsan_handle_builtin_unreachable"));
1084}
1085
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001086static bool ignore_unreachable_insn(struct symbol *func,
1087 struct instruction *insn)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001088{
1089 int i;
1090
1091 if (insn->type == INSN_NOP)
1092 return true;
1093
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001094 /*
1095 * Check if this (or a subsequent) instruction is related to
1096 * CONFIG_UBSAN or CONFIG_KASAN.
1097 *
1098 * End the search at 5 instructions to avoid going into the weeds.
1099 */
1100 for (i = 0; i < 5; i++) {
1101
1102 if (is_kasan_insn(insn) || is_ubsan_insn(insn))
1103 return true;
1104
1105 if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest) {
1106 insn = insn->jump_dest;
1107 continue;
1108 }
1109
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001110 if (insn->offset + insn->len >= func->offset + func->len)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001111 break;
1112 insn = list_next_entry(insn, list);
1113 }
1114
1115 return false;
1116}
1117
1118static int validate_functions(struct objtool_file *file)
1119{
1120 struct section *sec;
1121 struct symbol *func;
1122 struct instruction *insn;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001123 int ret, warnings = 0;
1124
1125 list_for_each_entry(sec, &file->elf->sections, list) {
Josh Poimboeufa196e172016-03-09 00:06:57 -06001126 list_for_each_entry(func, &sec->symbol_list, list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001127 if (func->type != STT_FUNC)
1128 continue;
1129
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001130 insn = find_insn(file, sec, func->offset);
Josh Poimboeufb1547d32016-04-15 09:17:10 -05001131 if (!insn)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001132 continue;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001133
1134 ret = validate_branch(file, insn, 0);
1135 warnings += ret;
1136 }
1137 }
1138
1139 list_for_each_entry(sec, &file->elf->sections, list) {
Josh Poimboeufa196e172016-03-09 00:06:57 -06001140 list_for_each_entry(func, &sec->symbol_list, list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001141 if (func->type != STT_FUNC)
1142 continue;
1143
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001144 func_for_each_insn(file, func, insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001145 if (insn->visited)
1146 continue;
1147
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001148 insn->visited = true;
Josh Poimboeuf7e578442016-04-14 14:52:24 -05001149
1150 if (file->ignore_unreachables || warnings ||
1151 ignore_unreachable_insn(func, insn))
1152 continue;
1153
Josh Poimboeuf9cfffb12016-10-13 16:22:53 -05001154 /*
1155 * gcov produces a lot of unreachable
1156 * instructions. If we get an unreachable
1157 * warning and the file has gcov enabled, just
1158 * ignore it, and all other such warnings for
1159 * the file.
1160 */
1161 if (!file->ignore_unreachables &&
1162 gcov_enabled(file)) {
1163 file->ignore_unreachables = true;
1164 continue;
1165 }
1166
Josh Poimboeuf7e578442016-04-14 14:52:24 -05001167 WARN_FUNC("function has unreachable instruction", insn->sec, insn->offset);
1168 warnings++;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001169 }
1170 }
1171 }
1172
1173 return warnings;
1174}
1175
1176static int validate_uncallable_instructions(struct objtool_file *file)
1177{
1178 struct instruction *insn;
1179 int warnings = 0;
1180
Josh Poimboeuf74aec052016-03-09 00:06:55 -06001181 for_each_insn(file, insn) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001182 if (!insn->visited && insn->type == INSN_RETURN) {
1183 WARN_FUNC("return instruction outside of a callable function",
1184 insn->sec, insn->offset);
1185 warnings++;
1186 }
1187 }
1188
1189 return warnings;
1190}
1191
1192static void cleanup(struct objtool_file *file)
1193{
1194 struct instruction *insn, *tmpinsn;
1195 struct alternative *alt, *tmpalt;
1196
Josh Poimboeufa196e172016-03-09 00:06:57 -06001197 list_for_each_entry_safe(insn, tmpinsn, &file->insn_list, list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001198 list_for_each_entry_safe(alt, tmpalt, &insn->alts, list) {
1199 list_del(&alt->list);
1200 free(alt);
1201 }
1202 list_del(&insn->list);
Josh Poimboeuf042ba732016-03-09 00:07:00 -06001203 hash_del(&insn->hash);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001204 free(insn);
1205 }
1206 elf_close(file->elf);
1207}
1208
1209const char * const check_usage[] = {
1210 "objtool check [<options>] file.o",
1211 NULL,
1212};
1213
1214int cmd_check(int argc, const char **argv)
1215{
1216 struct objtool_file file;
1217 int ret, warnings = 0;
1218
1219 const struct option options[] = {
1220 OPT_BOOLEAN('f', "no-fp", &nofp, "Skip frame pointer validation"),
1221 OPT_END(),
1222 };
1223
1224 argc = parse_options(argc, argv, options, check_usage, 0);
1225
1226 if (argc != 1)
1227 usage_with_options(check_usage, options);
1228
1229 objname = argv[0];
1230
1231 file.elf = elf_open(objname);
1232 if (!file.elf) {
1233 fprintf(stderr, "error reading elf file %s\n", objname);
1234 return 1;
1235 }
1236
Josh Poimboeufa196e172016-03-09 00:06:57 -06001237 INIT_LIST_HEAD(&file.insn_list);
Josh Poimboeuf042ba732016-03-09 00:07:00 -06001238 hash_init(file.insn_hash);
Josh Poimboeuf35aee622017-03-01 12:04:44 -06001239 file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard");
Josh Poimboeuf7e578442016-04-14 14:52:24 -05001240 file.rodata = find_section_by_name(file.elf, ".rodata");
1241 file.ignore_unreachables = false;
Josh Poimboeufb1547d32016-04-15 09:17:10 -05001242 file.c_file = find_section_by_name(file.elf, ".comment");
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001243
1244 ret = decode_sections(&file);
1245 if (ret < 0)
1246 goto out;
1247 warnings += ret;
1248
1249 ret = validate_functions(&file);
1250 if (ret < 0)
1251 goto out;
1252 warnings += ret;
1253
1254 ret = validate_uncallable_instructions(&file);
1255 if (ret < 0)
1256 goto out;
1257 warnings += ret;
1258
1259out:
1260 cleanup(&file);
1261
1262 /* ignore warnings for now until we get all the code cleaned up */
1263 if (ret || warnings)
1264 return 0;
1265 return 0;
1266}