blob: f8f7a40c6ef359b6c24c55d17ac01f5f73167000 [file] [log] [blame]
Thomas Gleixner1ccea772019-05-19 15:51:43 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002/*
3 * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05004 */
5
6#include <string.h>
7#include <stdlib.h>
8
Peter Zijlstra43a45252018-01-16 17:16:32 +01009#include "builtin.h"
Matt Helsley0decf1f2020-05-19 13:55:33 -070010#include "cfi.h"
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050011#include "arch.h"
Matt Helsley0decf1f2020-05-19 13:55:33 -070012#include "check.h"
13#include "special.h"
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050014#include "warn.h"
Peter Zijlstra0f1441b2020-06-12 16:05:26 +020015#include "arch_elf.h"
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050016
17#include <linux/hashtable.h>
18#include <linux/kernel.h>
Josh Poimboeuf1e7e4782020-08-18 15:57:45 +020019#include <linux/static_call_types.h>
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050020
Josh Poimboeufe6da9562019-05-13 12:01:31 -050021#define FAKE_JUMP_OFFSET -1
22
Josh Poimboeuf87b512d2019-06-27 20:50:46 -050023#define C_JUMP_TABLE_SECTION ".rodata..c_jump_table"
24
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050025struct alternative {
26 struct list_head list;
27 struct instruction *insn;
Peter Zijlstra764eef42019-03-01 11:19:03 +010028 bool skip_orig;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050029};
30
31const char *objname;
Peter Zijlstraa3608f52020-03-25 15:34:50 +010032struct cfi_init_state initial_func_cfi;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050033
Josh Poimboeuf627fce12017-07-11 10:33:42 -050034struct instruction *find_insn(struct objtool_file *file,
35 struct section *sec, unsigned long offset)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050036{
37 struct instruction *insn;
38
Peter Zijlstra87ecb582020-03-16 15:47:27 +010039 hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050040 if (insn->sec == sec && insn->offset == offset)
41 return insn;
Peter Zijlstra87ecb582020-03-16 15:47:27 +010042 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050043
44 return NULL;
45}
46
47static struct instruction *next_insn_same_sec(struct objtool_file *file,
48 struct instruction *insn)
49{
50 struct instruction *next = list_next_entry(insn, list);
51
Josh Poimboeufbaa414692017-06-28 10:11:07 -050052 if (!next || &next->list == &file->insn_list || next->sec != insn->sec)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050053 return NULL;
54
55 return next;
56}
57
Josh Poimboeuf13810432018-05-09 22:39:15 -050058static struct instruction *next_insn_same_func(struct objtool_file *file,
59 struct instruction *insn)
60{
61 struct instruction *next = list_next_entry(insn, list);
62 struct symbol *func = insn->func;
63
64 if (!func)
65 return NULL;
66
67 if (&next->list != &file->insn_list && next->func == func)
68 return next;
69
70 /* Check if we're already in the subfunction: */
71 if (func == func->cfunc)
72 return NULL;
73
74 /* Move to the subfunction: */
75 return find_insn(file, func->cfunc->sec, func->cfunc->offset);
76}
77
Josh Poimboeuf1119d262020-04-28 16:45:16 -050078static struct instruction *prev_insn_same_sym(struct objtool_file *file,
79 struct instruction *insn)
80{
81 struct instruction *prev = list_prev_entry(insn, list);
82
83 if (&prev->list != &file->insn_list && prev->func == insn->func)
84 return prev;
85
86 return NULL;
87}
88
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +010089#define func_for_each_insn(file, func, insn) \
Josh Poimboeuf13810432018-05-09 22:39:15 -050090 for (insn = find_insn(file, func->sec, func->offset); \
91 insn; \
92 insn = next_insn_same_func(file, insn))
93
Peter Zijlstradbf4aeb2020-03-10 18:24:59 +010094#define sym_for_each_insn(file, sym, insn) \
95 for (insn = find_insn(file, sym->sec, sym->offset); \
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050096 insn && &insn->list != &file->insn_list && \
Peter Zijlstradbf4aeb2020-03-10 18:24:59 +010097 insn->sec == sym->sec && \
98 insn->offset < sym->offset + sym->len; \
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050099 insn = list_next_entry(insn, list))
100
Peter Zijlstradbf4aeb2020-03-10 18:24:59 +0100101#define sym_for_each_insn_continue_reverse(file, sym, insn) \
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500102 for (insn = list_prev_entry(insn, list); \
103 &insn->list != &file->insn_list && \
Peter Zijlstradbf4aeb2020-03-10 18:24:59 +0100104 insn->sec == sym->sec && insn->offset >= sym->offset; \
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500105 insn = list_prev_entry(insn, list))
106
107#define sec_for_each_insn_from(file, insn) \
108 for (; insn; insn = next_insn_same_sec(file, insn))
109
Josh Poimboeufbaa414692017-06-28 10:11:07 -0500110#define sec_for_each_insn_continue(file, insn) \
111 for (insn = next_insn_same_sec(file, insn); insn; \
112 insn = next_insn_same_sec(file, insn))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500113
Josh Poimboeufa2296142020-02-10 12:32:39 -0600114static bool is_static_jump(struct instruction *insn)
115{
116 return insn->type == INSN_JUMP_CONDITIONAL ||
117 insn->type == INSN_JUMP_UNCONDITIONAL;
118}
119
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500120static bool is_sibling_call(struct instruction *insn)
121{
122 /* An indirect jump is either a sibling call or a jump to a table. */
123 if (insn->type == INSN_JUMP_DYNAMIC)
124 return list_empty(&insn->alts);
125
Josh Poimboeufa2296142020-02-10 12:32:39 -0600126 if (!is_static_jump(insn))
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500127 return false;
128
129 /* add_jump_destinations() sets insn->call_dest for sibling calls. */
130 return !!insn->call_dest;
131}
132
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500133/*
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500134 * This checks to see if the given function is a "noreturn" function.
135 *
136 * For global functions which are outside the scope of this object file, we
137 * have to keep a manual list of them.
138 *
139 * For local functions, we have to detect them manually by simply looking for
140 * the lack of a return instruction.
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500141 */
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500142static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
143 int recursion)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500144{
145 int i;
146 struct instruction *insn;
147 bool empty = true;
148
149 /*
150 * Unfortunately these have to be hard coded because the noreturn
151 * attribute isn't provided in ELF data.
152 */
153 static const char * const global_noreturns[] = {
154 "__stack_chk_fail",
155 "panic",
156 "do_exit",
157 "do_task_dead",
158 "__module_put_and_exit",
159 "complete_and_exit",
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500160 "__reiserfs_panic",
161 "lbug_with_loc",
162 "fortify_panic",
Kees Cookb394d462018-01-10 14:22:38 -0800163 "usercopy_abort",
Josh Poimboeuf684fb242018-06-19 10:47:50 -0500164 "machine_real_restart",
Josh Poimboeuf4fa5ecd2019-04-04 12:17:35 -0500165 "rewind_stack_do_exit",
Brendan Higgins33adf802019-09-23 02:02:38 -0700166 "kunit_try_catch_throw",
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500167 };
168
Josh Poimboeufc9bab222019-07-17 20:36:51 -0500169 if (!func)
170 return false;
171
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500172 if (func->bind == STB_WEAK)
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500173 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500174
175 if (func->bind == STB_GLOBAL)
176 for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
177 if (!strcmp(func->name, global_noreturns[i]))
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500178 return true;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500179
Josh Poimboeuf13810432018-05-09 22:39:15 -0500180 if (!func->len)
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500181 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500182
Josh Poimboeuf13810432018-05-09 22:39:15 -0500183 insn = find_insn(file, func->sec, func->offset);
184 if (!insn->func)
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500185 return false;
Josh Poimboeuf13810432018-05-09 22:39:15 -0500186
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +0100187 func_for_each_insn(file, func, insn) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500188 empty = false;
189
190 if (insn->type == INSN_RETURN)
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500191 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500192 }
193
194 if (empty)
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500195 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500196
197 /*
198 * A function can have a sibling call instead of a return. In that
199 * case, the function's dead-end status depends on whether the target
200 * of the sibling call returns.
201 */
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +0100202 func_for_each_insn(file, func, insn) {
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500203 if (is_sibling_call(insn)) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500204 struct instruction *dest = insn->jump_dest;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500205
206 if (!dest)
207 /* sibling call to another file */
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500208 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500209
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500210 /* local sibling call */
211 if (recursion == 5) {
212 /*
213 * Infinite recursion: two functions have
214 * sibling calls to each other. This is a very
215 * rare case. It means they aren't dead ends.
216 */
217 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500218 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500219
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500220 return __dead_end_function(file, dest->func, recursion+1);
221 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500222 }
223
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500224 return true;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500225}
226
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500227static bool dead_end_function(struct objtool_file *file, struct symbol *func)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500228{
229 return __dead_end_function(file, func, 0);
230}
231
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100232static void init_cfi_state(struct cfi_state *cfi)
Josh Poimboeufbaa414692017-06-28 10:11:07 -0500233{
234 int i;
235
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -0500236 for (i = 0; i < CFI_NUM_REGS; i++) {
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100237 cfi->regs[i].base = CFI_UNDEFINED;
238 cfi->vals[i].base = CFI_UNDEFINED;
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -0500239 }
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100240 cfi->cfa.base = CFI_UNDEFINED;
241 cfi->drap_reg = CFI_UNDEFINED;
242 cfi->drap_offset = -1;
243}
244
Peter Zijlstra932f8e92020-03-23 18:26:03 +0100245static void init_insn_state(struct insn_state *state, struct section *sec)
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100246{
247 memset(state, 0, sizeof(*state));
248 init_cfi_state(&state->cfi);
Peter Zijlstra932f8e92020-03-23 18:26:03 +0100249
250 /*
251 * We need the full vmlinux for noinstr validation, otherwise we can
252 * not correctly determine insn->call_dest->sec (external symbols do
253 * not have a section).
254 */
255 if (vmlinux && sec)
256 state->noinstr = sec->noinstr;
Josh Poimboeufbaa414692017-06-28 10:11:07 -0500257}
258
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500259/*
260 * Call the arch-specific instruction decoder for all the instructions and add
261 * them to the global instruction list.
262 */
263static int decode_instructions(struct objtool_file *file)
264{
265 struct section *sec;
266 struct symbol *func;
267 unsigned long offset;
268 struct instruction *insn;
Peter Zijlstra1e11f3f2020-03-12 09:26:29 +0100269 unsigned long nr_insns = 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500270 int ret;
271
Josh Poimboeufbaa414692017-06-28 10:11:07 -0500272 for_each_sec(file, sec) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500273
274 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
275 continue;
276
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500277 if (strcmp(sec->name, ".altinstr_replacement") &&
278 strcmp(sec->name, ".altinstr_aux") &&
279 strncmp(sec->name, ".discard.", 9))
280 sec->text = true;
281
Thomas Gleixner0cc9ac8d2020-03-25 17:18:17 +0100282 if (!strcmp(sec->name, ".noinstr.text") ||
283 !strcmp(sec->name, ".entry.text"))
Peter Zijlstrac4a33932020-03-10 18:57:41 +0100284 sec->noinstr = true;
285
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500286 for (offset = 0; offset < sec->len; offset += insn->len) {
287 insn = malloc(sizeof(*insn));
Josh Poimboeufbaa414692017-06-28 10:11:07 -0500288 if (!insn) {
289 WARN("malloc failed");
290 return -1;
291 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500292 memset(insn, 0, sizeof(*insn));
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500293 INIT_LIST_HEAD(&insn->alts);
Julien Thierry65ea47d2020-03-27 15:28:47 +0000294 INIT_LIST_HEAD(&insn->stack_ops);
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100295 init_cfi_state(&insn->cfi);
Josh Poimboeufbaa414692017-06-28 10:11:07 -0500296
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500297 insn->sec = sec;
298 insn->offset = offset;
299
300 ret = arch_decode_instruction(file->elf, sec, offset,
301 sec->len - offset,
302 &insn->len, &insn->type,
Josh Poimboeufbaa414692017-06-28 10:11:07 -0500303 &insn->immediate,
Julien Thierry65ea47d2020-03-27 15:28:47 +0000304 &insn->stack_ops);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500305 if (ret)
Kamalesh Babulalb7037982017-10-19 11:27:24 -0500306 goto err;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500307
Peter Zijlstra87ecb582020-03-16 15:47:27 +0100308 hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500309 list_add_tail(&insn->list, &file->insn_list);
Peter Zijlstra1e11f3f2020-03-12 09:26:29 +0100310 nr_insns++;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500311 }
312
313 list_for_each_entry(func, &sec->symbol_list, list) {
Josh Poimboeufe10cd8f2019-07-17 20:36:48 -0500314 if (func->type != STT_FUNC || func->alias != func)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500315 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
Peter Zijlstradbf4aeb2020-03-10 18:24:59 +0100323 sym_for_each_insn(file, func, insn)
Josh Poimboeufe10cd8f2019-07-17 20:36:48 -0500324 insn->func = func;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500325 }
326 }
327
Peter Zijlstra1e11f3f2020-03-12 09:26:29 +0100328 if (stats)
329 printf("nr_insns: %lu\n", nr_insns);
330
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500331 return 0;
Kamalesh Babulalb7037982017-10-19 11:27:24 -0500332
333err:
334 free(insn);
335 return ret;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500336}
337
Sami Tolvanen6b5dd712020-04-21 15:08:43 -0700338static struct instruction *find_last_insn(struct objtool_file *file,
339 struct section *sec)
340{
341 struct instruction *insn = NULL;
342 unsigned int offset;
343 unsigned int end = (sec->len > 10) ? sec->len - 10 : 0;
344
345 for (offset = sec->len - 1; offset >= end && !insn; offset--)
346 insn = find_insn(file, sec, offset);
347
348 return insn;
349}
350
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500351/*
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500352 * Mark "ud2" instructions and manually annotated dead ends.
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500353 */
354static int add_dead_ends(struct objtool_file *file)
355{
356 struct section *sec;
Matt Helsleyf1974222020-05-29 14:01:13 -0700357 struct reloc *reloc;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500358 struct instruction *insn;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500359
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500360 /*
361 * By default, "ud2" is a dead end unless otherwise annotated, because
362 * GCC 7 inserts it for certain divide-by-zero cases.
363 */
364 for_each_insn(file, insn)
365 if (insn->type == INSN_BUG)
366 insn->dead_end = true;
367
368 /*
369 * Check for manually annotated dead ends.
370 */
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500371 sec = find_section_by_name(file->elf, ".rela.discard.unreachable");
372 if (!sec)
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500373 goto reachable;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500374
Matt Helsleyf1974222020-05-29 14:01:13 -0700375 list_for_each_entry(reloc, &sec->reloc_list, list) {
376 if (reloc->sym->type != STT_SECTION) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500377 WARN("unexpected relocation symbol type in %s", sec->name);
378 return -1;
379 }
Matt Helsleyf1974222020-05-29 14:01:13 -0700380 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500381 if (insn)
382 insn = list_prev_entry(insn, list);
Matt Helsleyf1974222020-05-29 14:01:13 -0700383 else if (reloc->addend == reloc->sym->sec->len) {
384 insn = find_last_insn(file, reloc->sym->sec);
Sami Tolvanen6b5dd712020-04-21 15:08:43 -0700385 if (!insn) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500386 WARN("can't find unreachable insn at %s+0x%x",
Matt Helsleyf1974222020-05-29 14:01:13 -0700387 reloc->sym->sec->name, reloc->addend);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500388 return -1;
389 }
390 } else {
391 WARN("can't find unreachable insn at %s+0x%x",
Matt Helsleyf1974222020-05-29 14:01:13 -0700392 reloc->sym->sec->name, reloc->addend);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500393 return -1;
394 }
395
396 insn->dead_end = true;
397 }
398
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500399reachable:
400 /*
401 * These manually annotated reachable checks are needed for GCC 4.4,
402 * where the Linux unreachable() macro isn't supported. In that case
403 * GCC doesn't know the "ud2" is fatal, so it generates code as if it's
404 * not a dead end.
405 */
406 sec = find_section_by_name(file->elf, ".rela.discard.reachable");
407 if (!sec)
408 return 0;
409
Matt Helsleyf1974222020-05-29 14:01:13 -0700410 list_for_each_entry(reloc, &sec->reloc_list, list) {
411 if (reloc->sym->type != STT_SECTION) {
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500412 WARN("unexpected relocation symbol type in %s", sec->name);
413 return -1;
414 }
Matt Helsleyf1974222020-05-29 14:01:13 -0700415 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500416 if (insn)
417 insn = list_prev_entry(insn, list);
Matt Helsleyf1974222020-05-29 14:01:13 -0700418 else if (reloc->addend == reloc->sym->sec->len) {
419 insn = find_last_insn(file, reloc->sym->sec);
Sami Tolvanen6b5dd712020-04-21 15:08:43 -0700420 if (!insn) {
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500421 WARN("can't find reachable insn at %s+0x%x",
Matt Helsleyf1974222020-05-29 14:01:13 -0700422 reloc->sym->sec->name, reloc->addend);
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500423 return -1;
424 }
425 } else {
426 WARN("can't find reachable insn at %s+0x%x",
Matt Helsleyf1974222020-05-29 14:01:13 -0700427 reloc->sym->sec->name, reloc->addend);
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500428 return -1;
429 }
430
431 insn->dead_end = false;
432 }
433
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500434 return 0;
435}
436
Josh Poimboeuf1e7e4782020-08-18 15:57:45 +0200437static int create_static_call_sections(struct objtool_file *file)
438{
439 struct section *sec, *reloc_sec;
440 struct reloc *reloc;
441 struct static_call_site *site;
442 struct instruction *insn;
443 struct symbol *key_sym;
444 char *key_name, *tmp;
445 int idx;
446
447 sec = find_section_by_name(file->elf, ".static_call_sites");
448 if (sec) {
449 INIT_LIST_HEAD(&file->static_call_list);
450 WARN("file already has .static_call_sites section, skipping");
451 return 0;
452 }
453
454 if (list_empty(&file->static_call_list))
455 return 0;
456
457 idx = 0;
458 list_for_each_entry(insn, &file->static_call_list, static_call_node)
459 idx++;
460
461 sec = elf_create_section(file->elf, ".static_call_sites", SHF_WRITE,
462 sizeof(struct static_call_site), idx);
463 if (!sec)
464 return -1;
465
466 reloc_sec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
467 if (!reloc_sec)
468 return -1;
469
470 idx = 0;
471 list_for_each_entry(insn, &file->static_call_list, static_call_node) {
472
473 site = (struct static_call_site *)sec->data->d_buf + idx;
474 memset(site, 0, sizeof(struct static_call_site));
475
476 /* populate reloc for 'addr' */
477 reloc = malloc(sizeof(*reloc));
478 if (!reloc) {
479 perror("malloc");
480 return -1;
481 }
482 memset(reloc, 0, sizeof(*reloc));
483 reloc->sym = insn->sec->sym;
484 reloc->addend = insn->offset;
485 reloc->type = R_X86_64_PC32;
486 reloc->offset = idx * sizeof(struct static_call_site);
487 reloc->sec = reloc_sec;
488 elf_add_reloc(file->elf, reloc);
489
490 /* find key symbol */
491 key_name = strdup(insn->call_dest->name);
492 if (!key_name) {
493 perror("strdup");
494 return -1;
495 }
496 if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR,
497 STATIC_CALL_TRAMP_PREFIX_LEN)) {
498 WARN("static_call: trampoline name malformed: %s", key_name);
499 return -1;
500 }
501 tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN;
502 memcpy(tmp, STATIC_CALL_KEY_PREFIX_STR, STATIC_CALL_KEY_PREFIX_LEN);
503
504 key_sym = find_symbol_by_name(file->elf, tmp);
505 if (!key_sym) {
506 WARN("static_call: can't find static_call_key symbol: %s", tmp);
507 return -1;
508 }
509 free(key_name);
510
511 /* populate reloc for 'key' */
512 reloc = malloc(sizeof(*reloc));
513 if (!reloc) {
514 perror("malloc");
515 return -1;
516 }
517 memset(reloc, 0, sizeof(*reloc));
518 reloc->sym = key_sym;
519 reloc->addend = 0;
520 reloc->type = R_X86_64_PC32;
521 reloc->offset = idx * sizeof(struct static_call_site) + 4;
522 reloc->sec = reloc_sec;
523 elf_add_reloc(file->elf, reloc);
524
525 idx++;
526 }
527
528 if (elf_rebuild_reloc_section(file->elf, reloc_sec))
529 return -1;
530
531 return 0;
532}
533
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500534/*
535 * Warnings shouldn't be reported for ignored functions.
536 */
537static void add_ignores(struct objtool_file *file)
538{
539 struct instruction *insn;
540 struct section *sec;
541 struct symbol *func;
Matt Helsleyf1974222020-05-29 14:01:13 -0700542 struct reloc *reloc;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500543
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100544 sec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard");
545 if (!sec)
546 return;
547
Matt Helsleyf1974222020-05-29 14:01:13 -0700548 list_for_each_entry(reloc, &sec->reloc_list, list) {
549 switch (reloc->sym->type) {
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100550 case STT_FUNC:
Matt Helsleyf1974222020-05-29 14:01:13 -0700551 func = reloc->sym;
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100552 break;
553
554 case STT_SECTION:
Matt Helsleyf1974222020-05-29 14:01:13 -0700555 func = find_func_by_offset(reloc->sym->sec, reloc->addend);
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600556 if (!func)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500557 continue;
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100558 break;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500559
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100560 default:
Matt Helsleyf1974222020-05-29 14:01:13 -0700561 WARN("unexpected relocation symbol type in %s: %d", sec->name, reloc->sym->type);
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100562 continue;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500563 }
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100564
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +0100565 func_for_each_insn(file, func, insn)
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100566 insn->ignore = true;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500567 }
568}
569
570/*
Peter Zijlstraea242132019-02-25 12:50:09 +0100571 * This is a whitelist of functions that is allowed to be called with AC set.
572 * The list is meant to be minimal and only contains compiler instrumentation
573 * ABI and a few functions used to implement *_{to,from}_user() functions.
574 *
575 * These functions must not directly change AC, but may PUSHF/POPF.
576 */
577static const char *uaccess_safe_builtin[] = {
578 /* KASAN */
579 "kasan_report",
580 "check_memory_region",
581 /* KASAN out-of-line */
582 "__asan_loadN_noabort",
583 "__asan_load1_noabort",
584 "__asan_load2_noabort",
585 "__asan_load4_noabort",
586 "__asan_load8_noabort",
587 "__asan_load16_noabort",
588 "__asan_storeN_noabort",
589 "__asan_store1_noabort",
590 "__asan_store2_noabort",
591 "__asan_store4_noabort",
592 "__asan_store8_noabort",
593 "__asan_store16_noabort",
594 /* KASAN in-line */
595 "__asan_report_load_n_noabort",
596 "__asan_report_load1_noabort",
597 "__asan_report_load2_noabort",
598 "__asan_report_load4_noabort",
599 "__asan_report_load8_noabort",
600 "__asan_report_load16_noabort",
601 "__asan_report_store_n_noabort",
602 "__asan_report_store1_noabort",
603 "__asan_report_store2_noabort",
604 "__asan_report_store4_noabort",
605 "__asan_report_store8_noabort",
606 "__asan_report_store16_noabort",
Marco Elver5f5c9712019-11-14 19:02:57 +0100607 /* KCSAN */
Marco Elver99676832020-03-25 17:41:57 +0100608 "__kcsan_check_access",
Marco Elver5f5c9712019-11-14 19:02:57 +0100609 "kcsan_found_watchpoint",
610 "kcsan_setup_watchpoint",
Marco Elver99676832020-03-25 17:41:57 +0100611 "kcsan_check_scoped_accesses",
Marco Elver50a19ad2020-04-24 17:47:30 +0200612 "kcsan_disable_current",
613 "kcsan_enable_current_nowarn",
Marco Elver5f5c9712019-11-14 19:02:57 +0100614 /* KCSAN/TSAN */
615 "__tsan_func_entry",
616 "__tsan_func_exit",
617 "__tsan_read_range",
618 "__tsan_write_range",
619 "__tsan_read1",
620 "__tsan_read2",
621 "__tsan_read4",
622 "__tsan_read8",
623 "__tsan_read16",
624 "__tsan_write1",
625 "__tsan_write2",
626 "__tsan_write4",
627 "__tsan_write8",
628 "__tsan_write16",
Peter Zijlstraea242132019-02-25 12:50:09 +0100629 /* KCOV */
630 "write_comp_data",
Josh Poimboeufae033f02020-04-29 14:09:04 -0500631 "check_kcov_mode",
Peter Zijlstraea242132019-02-25 12:50:09 +0100632 "__sanitizer_cov_trace_pc",
633 "__sanitizer_cov_trace_const_cmp1",
634 "__sanitizer_cov_trace_const_cmp2",
635 "__sanitizer_cov_trace_const_cmp4",
636 "__sanitizer_cov_trace_const_cmp8",
637 "__sanitizer_cov_trace_cmp1",
638 "__sanitizer_cov_trace_cmp2",
639 "__sanitizer_cov_trace_cmp4",
640 "__sanitizer_cov_trace_cmp8",
Al Viro36b1c702020-02-16 13:07:49 -0500641 "__sanitizer_cov_trace_switch",
Peter Zijlstraea242132019-02-25 12:50:09 +0100642 /* UBSAN */
643 "ubsan_type_mismatch_common",
644 "__ubsan_handle_type_mismatch",
645 "__ubsan_handle_type_mismatch_v1",
Peter Zijlstra9a50dca2019-10-21 15:11:49 +0200646 "__ubsan_handle_shift_out_of_bounds",
Peter Zijlstraea242132019-02-25 12:50:09 +0100647 /* misc */
648 "csum_partial_copy_generic",
649 "__memcpy_mcsafe",
Josh Poimboeufa7e47f22019-07-17 20:36:46 -0500650 "mcsafe_handle_tail",
Peter Zijlstraea242132019-02-25 12:50:09 +0100651 "ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */
652 NULL
653};
654
655static void add_uaccess_safe(struct objtool_file *file)
656{
657 struct symbol *func;
658 const char **name;
659
660 if (!uaccess)
661 return;
662
663 for (name = uaccess_safe_builtin; *name; name++) {
664 func = find_symbol_by_name(file->elf, *name);
665 if (!func)
666 continue;
667
Josh Poimboeufe10cd8f2019-07-17 20:36:48 -0500668 func->uaccess_safe = true;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500669 }
670}
671
672/*
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000673 * FIXME: For now, just ignore any alternatives which add retpolines. This is
674 * a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline.
675 * But it at least allows objtool to understand the control flow *around* the
676 * retpoline.
677 */
Peter Zijlstraff05ab22019-03-18 14:33:07 +0100678static int add_ignore_alternatives(struct objtool_file *file)
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000679{
680 struct section *sec;
Matt Helsleyf1974222020-05-29 14:01:13 -0700681 struct reloc *reloc;
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000682 struct instruction *insn;
683
Peter Zijlstraff05ab22019-03-18 14:33:07 +0100684 sec = find_section_by_name(file->elf, ".rela.discard.ignore_alts");
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000685 if (!sec)
686 return 0;
687
Matt Helsleyf1974222020-05-29 14:01:13 -0700688 list_for_each_entry(reloc, &sec->reloc_list, list) {
689 if (reloc->sym->type != STT_SECTION) {
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000690 WARN("unexpected relocation symbol type in %s", sec->name);
691 return -1;
692 }
693
Matt Helsleyf1974222020-05-29 14:01:13 -0700694 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000695 if (!insn) {
Peter Zijlstraff05ab22019-03-18 14:33:07 +0100696 WARN("bad .discard.ignore_alts entry");
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000697 return -1;
698 }
699
700 insn->ignore_alts = true;
701 }
702
703 return 0;
704}
705
706/*
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500707 * Find the destination instructions for all jumps.
708 */
709static int add_jump_destinations(struct objtool_file *file)
710{
711 struct instruction *insn;
Matt Helsleyf1974222020-05-29 14:01:13 -0700712 struct reloc *reloc;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500713 struct section *dest_sec;
714 unsigned long dest_off;
715
716 for_each_insn(file, insn) {
Josh Poimboeufa2296142020-02-10 12:32:39 -0600717 if (!is_static_jump(insn))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500718 continue;
719
Josh Poimboeufe6da9562019-05-13 12:01:31 -0500720 if (insn->ignore || insn->offset == FAKE_JUMP_OFFSET)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500721 continue;
722
Matt Helsleyf1974222020-05-29 14:01:13 -0700723 reloc = find_reloc_by_dest_range(file->elf, insn->sec,
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +0100724 insn->offset, insn->len);
Matt Helsleyf1974222020-05-29 14:01:13 -0700725 if (!reloc) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500726 dest_sec = insn->sec;
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000727 dest_off = arch_jump_destination(insn);
Matt Helsleyf1974222020-05-29 14:01:13 -0700728 } else if (reloc->sym->type == STT_SECTION) {
729 dest_sec = reloc->sym->sec;
730 dest_off = arch_dest_reloc_offset(reloc->addend);
731 } else if (reloc->sym->sec->idx) {
732 dest_sec = reloc->sym->sec;
733 dest_off = reloc->sym->sym.st_value +
734 arch_dest_reloc_offset(reloc->addend);
735 } else if (strstr(reloc->sym->name, "_indirect_thunk_")) {
Josh Poimboeuf39b73532018-01-11 21:46:23 +0000736 /*
737 * Retpoline jumps are really dynamic jumps in
738 * disguise, so convert them accordingly.
739 */
Josh Poimboeufb68b9902019-07-17 20:36:57 -0500740 if (insn->type == INSN_JUMP_UNCONDITIONAL)
741 insn->type = INSN_JUMP_DYNAMIC;
742 else
743 insn->type = INSN_JUMP_DYNAMIC_CONDITIONAL;
744
Peter Zijlstrab5bc2232018-01-16 10:24:06 +0100745 insn->retpoline_safe = true;
Josh Poimboeuf39b73532018-01-11 21:46:23 +0000746 continue;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500747 } else {
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500748 /* external sibling call */
Matt Helsleyf1974222020-05-29 14:01:13 -0700749 insn->call_dest = reloc->sym;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500750 continue;
751 }
752
753 insn->jump_dest = find_insn(file, dest_sec, dest_off);
754 if (!insn->jump_dest) {
755
756 /*
757 * This is a special case where an alt instruction
758 * jumps past the end of the section. These are
759 * handled later in handle_group_alt().
760 */
761 if (!strcmp(insn->sec->name, ".altinstr_replacement"))
762 continue;
763
764 WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
765 insn->sec, insn->offset, dest_sec->name,
766 dest_off);
767 return -1;
768 }
Josh Poimboeufcd778492018-06-01 07:23:51 -0500769
770 /*
Peter Zijlstra54262aa2019-03-06 12:58:15 +0100771 * Cross-function jump.
Josh Poimboeufcd778492018-06-01 07:23:51 -0500772 */
773 if (insn->func && insn->jump_dest->func &&
Peter Zijlstra54262aa2019-03-06 12:58:15 +0100774 insn->func != insn->jump_dest->func) {
775
776 /*
777 * For GCC 8+, create parent/child links for any cold
778 * subfunctions. This is _mostly_ redundant with a
779 * similar initialization in read_symbols().
780 *
781 * If a function has aliases, we want the *first* such
782 * function in the symbol table to be the subfunction's
783 * parent. In that case we overwrite the
784 * initialization done in read_symbols().
785 *
786 * However this code can't completely replace the
787 * read_symbols() code because this doesn't detect the
788 * case where the parent function's only reference to a
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -0500789 * subfunction is through a jump table.
Peter Zijlstra54262aa2019-03-06 12:58:15 +0100790 */
791 if (!strstr(insn->func->name, ".cold.") &&
792 strstr(insn->jump_dest->func->name, ".cold.")) {
793 insn->func->cfunc = insn->jump_dest->func;
794 insn->jump_dest->func->pfunc = insn->func;
795
796 } else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
797 insn->jump_dest->offset == insn->jump_dest->func->offset) {
798
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500799 /* internal sibling call */
Peter Zijlstra54262aa2019-03-06 12:58:15 +0100800 insn->call_dest = insn->jump_dest->func;
Peter Zijlstra54262aa2019-03-06 12:58:15 +0100801 }
Josh Poimboeufcd778492018-06-01 07:23:51 -0500802 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500803 }
804
805 return 0;
806}
807
Alexandre Chartre8aa8eb22020-04-14 12:36:12 +0200808static void remove_insn_ops(struct instruction *insn)
809{
810 struct stack_op *op, *tmp;
811
812 list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
813 list_del(&op->list);
814 free(op);
815 }
816}
817
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500818/*
819 * Find the destination instructions for all calls.
820 */
821static int add_call_destinations(struct objtool_file *file)
822{
823 struct instruction *insn;
824 unsigned long dest_off;
Matt Helsleyf1974222020-05-29 14:01:13 -0700825 struct reloc *reloc;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500826
827 for_each_insn(file, insn) {
828 if (insn->type != INSN_CALL)
829 continue;
830
Matt Helsleyf1974222020-05-29 14:01:13 -0700831 reloc = find_reloc_by_dest_range(file->elf, insn->sec,
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +0100832 insn->offset, insn->len);
Matt Helsleyf1974222020-05-29 14:01:13 -0700833 if (!reloc) {
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000834 dest_off = arch_jump_destination(insn);
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600835 insn->call_dest = find_func_by_offset(insn->sec, dest_off);
836 if (!insn->call_dest)
837 insn->call_dest = find_symbol_by_offset(insn->sec, dest_off);
Josh Poimboeufa845c7c2018-01-29 22:00:39 -0600838
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600839 if (insn->ignore)
840 continue;
841
842 if (!insn->call_dest) {
Alexandre Chartre8aa8eb22020-04-14 12:36:12 +0200843 WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500844 return -1;
845 }
Josh Poimboeufa845c7c2018-01-29 22:00:39 -0600846
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600847 if (insn->func && insn->call_dest->type != STT_FUNC) {
848 WARN_FUNC("unsupported call to non-function",
849 insn->sec, insn->offset);
850 return -1;
851 }
852
Matt Helsleyf1974222020-05-29 14:01:13 -0700853 } else if (reloc->sym->type == STT_SECTION) {
854 dest_off = arch_dest_reloc_offset(reloc->addend);
855 insn->call_dest = find_func_by_offset(reloc->sym->sec,
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000856 dest_off);
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600857 if (!insn->call_dest) {
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000858 WARN_FUNC("can't find call dest symbol at %s+0x%lx",
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500859 insn->sec, insn->offset,
Matt Helsleyf1974222020-05-29 14:01:13 -0700860 reloc->sym->sec->name,
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000861 dest_off);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500862 return -1;
863 }
864 } else
Matt Helsleyf1974222020-05-29 14:01:13 -0700865 insn->call_dest = reloc->sym;
Alexandre Chartre8aa8eb22020-04-14 12:36:12 +0200866
867 /*
Peter Zijlstra0f1441b2020-06-12 16:05:26 +0200868 * Many compilers cannot disable KCOV with a function attribute
869 * so they need a little help, NOP out any KCOV calls from noinstr
870 * text.
871 */
872 if (insn->sec->noinstr &&
873 !strncmp(insn->call_dest->name, "__sanitizer_cov_", 16)) {
Peter Zijlstrad832c002020-06-18 17:55:29 +0200874 if (reloc) {
875 reloc->type = R_NONE;
876 elf_write_reloc(file->elf, reloc);
Peter Zijlstra0f1441b2020-06-12 16:05:26 +0200877 }
878
879 elf_write_insn(file->elf, insn->sec,
880 insn->offset, insn->len,
881 arch_nop_insn(insn->len));
882 insn->type = INSN_NOP;
883 }
884
885 /*
Alexandre Chartre8aa8eb22020-04-14 12:36:12 +0200886 * Whatever stack impact regular CALLs have, should be undone
887 * by the RETURN of the called function.
888 *
889 * Annotated intra-function calls retain the stack_ops but
890 * are converted to JUMP, see read_intra_function_calls().
891 */
892 remove_insn_ops(insn);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500893 }
894
895 return 0;
896}
897
898/*
899 * The .alternatives section requires some extra special care, over and above
900 * what other special sections require:
901 *
902 * 1. Because alternatives are patched in-place, we need to insert a fake jump
903 * instruction at the end so that validate_branch() skips all the original
904 * replaced instructions when validating the new instruction path.
905 *
906 * 2. An added wrinkle is that the new instruction length might be zero. In
907 * that case the old instructions are replaced with noops. We simulate that
908 * by creating a fake jump as the only new instruction.
909 *
910 * 3. In some cases, the alternative section includes an instruction which
911 * conditionally jumps to the _end_ of the entry. We have to modify these
912 * jumps' destinations to point back to .text rather than the end of the
913 * entry in .altinstr_replacement.
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500914 */
915static int handle_group_alt(struct objtool_file *file,
916 struct special_alt *special_alt,
917 struct instruction *orig_insn,
918 struct instruction **new_insn)
919{
Alexandre Chartre13fab062020-04-14 12:36:11 +0200920 static unsigned int alt_group_next_index = 1;
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600921 struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump = NULL;
Alexandre Chartre13fab062020-04-14 12:36:11 +0200922 unsigned int alt_group = alt_group_next_index++;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500923 unsigned long dest_off;
924
925 last_orig_insn = NULL;
926 insn = orig_insn;
927 sec_for_each_insn_from(file, insn) {
928 if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
929 break;
930
Alexandre Chartre13fab062020-04-14 12:36:11 +0200931 insn->alt_group = alt_group;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500932 last_orig_insn = insn;
933 }
934
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600935 if (next_insn_same_sec(file, last_orig_insn)) {
936 fake_jump = malloc(sizeof(*fake_jump));
937 if (!fake_jump) {
938 WARN("malloc failed");
939 return -1;
940 }
941 memset(fake_jump, 0, sizeof(*fake_jump));
942 INIT_LIST_HEAD(&fake_jump->alts);
Julien Thierry65ea47d2020-03-27 15:28:47 +0000943 INIT_LIST_HEAD(&fake_jump->stack_ops);
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100944 init_cfi_state(&fake_jump->cfi);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500945
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600946 fake_jump->sec = special_alt->new_sec;
Josh Poimboeufe6da9562019-05-13 12:01:31 -0500947 fake_jump->offset = FAKE_JUMP_OFFSET;
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600948 fake_jump->type = INSN_JUMP_UNCONDITIONAL;
949 fake_jump->jump_dest = list_next_entry(last_orig_insn, list);
Josh Poimboeufe6da9562019-05-13 12:01:31 -0500950 fake_jump->func = orig_insn->func;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500951 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500952
953 if (!special_alt->new_len) {
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600954 if (!fake_jump) {
955 WARN("%s: empty alternative at end of section",
956 special_alt->orig_sec->name);
957 return -1;
958 }
959
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500960 *new_insn = fake_jump;
961 return 0;
962 }
963
964 last_new_insn = NULL;
Alexandre Chartre13fab062020-04-14 12:36:11 +0200965 alt_group = alt_group_next_index++;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500966 insn = *new_insn;
967 sec_for_each_insn_from(file, insn) {
968 if (insn->offset >= special_alt->new_off + special_alt->new_len)
969 break;
970
971 last_new_insn = insn;
972
Josh Poimboeufa845c7c2018-01-29 22:00:39 -0600973 insn->ignore = orig_insn->ignore_alts;
Peter Zijlstraa4d09dd2019-02-25 10:31:24 +0100974 insn->func = orig_insn->func;
Alexandre Chartre13fab062020-04-14 12:36:11 +0200975 insn->alt_group = alt_group;
Josh Poimboeufa845c7c2018-01-29 22:00:39 -0600976
Josh Poimboeufdc419722020-02-10 12:32:40 -0600977 /*
978 * Since alternative replacement code is copy/pasted by the
979 * kernel after applying relocations, generally such code can't
980 * have relative-address relocation references to outside the
981 * .altinstr_replacement section, unless the arch's
982 * alternatives code can adjust the relative offsets
983 * accordingly.
984 *
985 * The x86 alternatives code adjusts the offsets only when it
986 * encounters a branch instruction at the very beginning of the
987 * replacement group.
988 */
989 if ((insn->offset != special_alt->new_off ||
990 (insn->type != INSN_CALL && !is_static_jump(insn))) &&
Matt Helsleyf1974222020-05-29 14:01:13 -0700991 find_reloc_by_dest_range(file->elf, insn->sec, insn->offset, insn->len)) {
Josh Poimboeufdc419722020-02-10 12:32:40 -0600992
993 WARN_FUNC("unsupported relocation in alternatives section",
994 insn->sec, insn->offset);
995 return -1;
996 }
997
Josh Poimboeufa2296142020-02-10 12:32:39 -0600998 if (!is_static_jump(insn))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500999 continue;
1000
1001 if (!insn->immediate)
1002 continue;
1003
Raphael Gaultbfb08f22020-03-27 15:28:45 +00001004 dest_off = arch_jump_destination(insn);
Josh Poimboeuf17bc3392018-01-29 22:00:40 -06001005 if (dest_off == special_alt->new_off + special_alt->new_len) {
1006 if (!fake_jump) {
1007 WARN("%s: alternative jump to end of section",
1008 special_alt->orig_sec->name);
1009 return -1;
1010 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001011 insn->jump_dest = fake_jump;
Josh Poimboeuf17bc3392018-01-29 22:00:40 -06001012 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001013
1014 if (!insn->jump_dest) {
1015 WARN_FUNC("can't find alternative jump destination",
1016 insn->sec, insn->offset);
1017 return -1;
1018 }
1019 }
1020
1021 if (!last_new_insn) {
1022 WARN_FUNC("can't find last new alternative instruction",
1023 special_alt->new_sec, special_alt->new_off);
1024 return -1;
1025 }
1026
Josh Poimboeuf17bc3392018-01-29 22:00:40 -06001027 if (fake_jump)
1028 list_add(&fake_jump->list, &last_new_insn->list);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001029
1030 return 0;
1031}
1032
1033/*
1034 * A jump table entry can either convert a nop to a jump or a jump to a nop.
1035 * If the original instruction is a jump, make the alt entry an effective nop
1036 * by just skipping the original instruction.
1037 */
1038static int handle_jump_alt(struct objtool_file *file,
1039 struct special_alt *special_alt,
1040 struct instruction *orig_insn,
1041 struct instruction **new_insn)
1042{
1043 if (orig_insn->type == INSN_NOP)
1044 return 0;
1045
1046 if (orig_insn->type != INSN_JUMP_UNCONDITIONAL) {
1047 WARN_FUNC("unsupported instruction at jump label",
1048 orig_insn->sec, orig_insn->offset);
1049 return -1;
1050 }
1051
1052 *new_insn = list_next_entry(orig_insn, list);
1053 return 0;
1054}
1055
1056/*
1057 * Read all the special sections which have alternate instructions which can be
1058 * patched in or redirected to at runtime. Each instruction having alternate
1059 * instruction(s) has them added to its insn->alts list, which will be
1060 * traversed in validate_branch().
1061 */
1062static int add_special_section_alts(struct objtool_file *file)
1063{
1064 struct list_head special_alts;
1065 struct instruction *orig_insn, *new_insn;
1066 struct special_alt *special_alt, *tmp;
1067 struct alternative *alt;
1068 int ret;
1069
1070 ret = special_get_alts(file->elf, &special_alts);
1071 if (ret)
1072 return ret;
1073
1074 list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001075
1076 orig_insn = find_insn(file, special_alt->orig_sec,
1077 special_alt->orig_off);
1078 if (!orig_insn) {
1079 WARN_FUNC("special: can't find orig instruction",
1080 special_alt->orig_sec, special_alt->orig_off);
1081 ret = -1;
1082 goto out;
1083 }
1084
1085 new_insn = NULL;
1086 if (!special_alt->group || special_alt->new_len) {
1087 new_insn = find_insn(file, special_alt->new_sec,
1088 special_alt->new_off);
1089 if (!new_insn) {
1090 WARN_FUNC("special: can't find new instruction",
1091 special_alt->new_sec,
1092 special_alt->new_off);
1093 ret = -1;
1094 goto out;
1095 }
1096 }
1097
1098 if (special_alt->group) {
Julien Thierry7170cf42020-03-27 15:28:41 +00001099 if (!special_alt->orig_len) {
1100 WARN_FUNC("empty alternative entry",
1101 orig_insn->sec, orig_insn->offset);
1102 continue;
1103 }
1104
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001105 ret = handle_group_alt(file, special_alt, orig_insn,
1106 &new_insn);
1107 if (ret)
1108 goto out;
1109 } else if (special_alt->jump_or_nop) {
1110 ret = handle_jump_alt(file, special_alt, orig_insn,
1111 &new_insn);
1112 if (ret)
1113 goto out;
1114 }
1115
Josh Poimboeuf258c7602018-01-11 21:46:24 +00001116 alt = malloc(sizeof(*alt));
1117 if (!alt) {
1118 WARN("malloc failed");
1119 ret = -1;
1120 goto out;
1121 }
1122
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001123 alt->insn = new_insn;
Peter Zijlstra764eef42019-03-01 11:19:03 +01001124 alt->skip_orig = special_alt->skip_orig;
Peter Zijlstraea242132019-02-25 12:50:09 +01001125 orig_insn->ignore_alts |= special_alt->skip_alt;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001126 list_add_tail(&alt->list, &orig_insn->alts);
1127
1128 list_del(&special_alt->list);
1129 free(special_alt);
1130 }
1131
1132out:
1133 return ret;
1134}
1135
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001136static int add_jump_table(struct objtool_file *file, struct instruction *insn,
Matt Helsleyf1974222020-05-29 14:01:13 -07001137 struct reloc *table)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001138{
Matt Helsleyf1974222020-05-29 14:01:13 -07001139 struct reloc *reloc = table;
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001140 struct instruction *dest_insn;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001141 struct alternative *alt;
Josh Poimboeuffd35c882018-05-10 17:48:49 -05001142 struct symbol *pfunc = insn->func->pfunc;
1143 unsigned int prev_offset = 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001144
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001145 /*
Matt Helsleyf1974222020-05-29 14:01:13 -07001146 * Each @reloc is a switch table relocation which points to the target
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001147 * instruction.
1148 */
Matt Helsleyf1974222020-05-29 14:01:13 -07001149 list_for_each_entry_from(reloc, &table->sec->reloc_list, list) {
Jann Hornbd98c812019-07-17 20:36:54 -05001150
1151 /* Check for the end of the table: */
Matt Helsleyf1974222020-05-29 14:01:13 -07001152 if (reloc != table && reloc->jump_table_start)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001153 break;
1154
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001155 /* Make sure the table entries are consecutive: */
Matt Helsleyf1974222020-05-29 14:01:13 -07001156 if (prev_offset && reloc->offset != prev_offset + 8)
Josh Poimboeuffd35c882018-05-10 17:48:49 -05001157 break;
1158
1159 /* Detect function pointers from contiguous objects: */
Matt Helsleyf1974222020-05-29 14:01:13 -07001160 if (reloc->sym->sec == pfunc->sec &&
1161 reloc->addend == pfunc->offset)
Josh Poimboeuffd35c882018-05-10 17:48:49 -05001162 break;
1163
Matt Helsleyf1974222020-05-29 14:01:13 -07001164 dest_insn = find_insn(file, reloc->sym->sec, reloc->addend);
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001165 if (!dest_insn)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001166 break;
1167
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001168 /* Make sure the destination is in the same function: */
Josh Poimboeufe65050b2019-07-17 20:36:55 -05001169 if (!dest_insn->func || dest_insn->func->pfunc != pfunc)
Josh Poimboeuf13810432018-05-09 22:39:15 -05001170 break;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001171
1172 alt = malloc(sizeof(*alt));
1173 if (!alt) {
1174 WARN("malloc failed");
1175 return -1;
1176 }
1177
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001178 alt->insn = dest_insn;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001179 list_add_tail(&alt->list, &insn->alts);
Matt Helsleyf1974222020-05-29 14:01:13 -07001180 prev_offset = reloc->offset;
Josh Poimboeuffd35c882018-05-10 17:48:49 -05001181 }
1182
1183 if (!prev_offset) {
1184 WARN_FUNC("can't find switch jump table",
1185 insn->sec, insn->offset);
1186 return -1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001187 }
1188
1189 return 0;
1190}
1191
1192/*
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001193 * find_jump_table() - Given a dynamic jump, find the switch jump table in
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001194 * .rodata associated with it.
1195 *
1196 * There are 3 basic patterns:
1197 *
1198 * 1. jmpq *[rodata addr](,%reg,8)
1199 *
1200 * This is the most common case by far. It jumps to an address in a simple
1201 * jump table which is stored in .rodata.
1202 *
1203 * 2. jmpq *[rodata addr](%rip)
1204 *
1205 * This is caused by a rare GCC quirk, currently only seen in three driver
1206 * functions in the kernel, only with certain obscure non-distro configs.
1207 *
1208 * As part of an optimization, GCC makes a copy of an existing switch jump
1209 * table, modifies it, and then hard-codes the jump (albeit with an indirect
1210 * jump) to use a single entry in the table. The rest of the jump table and
1211 * some of its jump targets remain as dead code.
1212 *
1213 * In such a case we can just crudely ignore all unreachable instruction
1214 * warnings for the entire object file. Ideally we would just ignore them
1215 * for the function, but that would require redesigning the code quite a
1216 * bit. And honestly that's just not worth doing: unreachable instruction
1217 * warnings are of questionable value anyway, and this is such a rare issue.
1218 *
1219 * 3. mov [rodata addr],%reg1
1220 * ... some instructions ...
1221 * jmpq *(%reg1,%reg2,8)
1222 *
1223 * This is a fairly uncommon pattern which is new for GCC 6. As of this
1224 * writing, there are 11 occurrences of it in the allmodconfig kernel.
1225 *
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001226 * As of GCC 7 there are quite a few more of these and the 'in between' code
1227 * is significant. Esp. with KASAN enabled some of the code between the mov
1228 * and jmpq uses .rodata itself, which can confuse things.
1229 *
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001230 * TODO: Once we have DWARF CFI and smarter instruction decoding logic,
1231 * ensure the same register is used in the mov and jump instructions.
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001232 *
1233 * NOTE: RETPOLINE made it harder still to decode dynamic jumps.
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001234 */
Matt Helsleyf1974222020-05-29 14:01:13 -07001235static struct reloc *find_jump_table(struct objtool_file *file,
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001236 struct symbol *func,
1237 struct instruction *insn)
1238{
Matt Helsleyf1974222020-05-29 14:01:13 -07001239 struct reloc *text_reloc, *table_reloc;
Josh Poimboeuf113d4bc2020-02-17 21:41:53 -06001240 struct instruction *dest_insn, *orig_insn = insn;
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001241 struct section *table_sec;
Josh Poimboeuf6f5ec292018-05-14 08:53:24 -05001242 unsigned long table_offset;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001243
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001244 /*
1245 * Backward search using the @first_jump_src links, these help avoid
1246 * much of the 'in between' code. Which avoids us getting confused by
1247 * it.
1248 */
Josh Poimboeuf7dec80c2018-05-18 15:10:34 -05001249 for (;
Josh Poimboeuf1119d262020-04-28 16:45:16 -05001250 insn && insn->func && insn->func->pfunc == func;
1251 insn = insn->first_jump_src ?: prev_insn_same_sym(file, insn)) {
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001252
Josh Poimboeuf7dec80c2018-05-18 15:10:34 -05001253 if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001254 break;
1255
1256 /* allow small jumps within the range */
1257 if (insn->type == INSN_JUMP_UNCONDITIONAL &&
1258 insn->jump_dest &&
1259 (insn->jump_dest->offset <= insn->offset ||
1260 insn->jump_dest->offset > orig_insn->offset))
1261 break;
1262
1263 /* look for a relocation which references .rodata */
Matt Helsleyf1974222020-05-29 14:01:13 -07001264 text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +01001265 insn->offset, insn->len);
Matt Helsleyf1974222020-05-29 14:01:13 -07001266 if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
1267 !text_reloc->sym->sec->rodata)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001268 continue;
1269
Matt Helsleyf1974222020-05-29 14:01:13 -07001270 table_offset = text_reloc->addend;
1271 table_sec = text_reloc->sym->sec;
Allan Xavier4a60aa02018-09-07 08:12:01 -05001272
Matt Helsleyf1974222020-05-29 14:01:13 -07001273 if (text_reloc->type == R_X86_64_PC32)
Josh Poimboeuf6f5ec292018-05-14 08:53:24 -05001274 table_offset += 4;
1275
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001276 /*
1277 * Make sure the .rodata address isn't associated with a
Josh Poimboeuf87b512d2019-06-27 20:50:46 -05001278 * symbol. GCC jump tables are anonymous data.
1279 *
1280 * Also support C jump tables which are in the same format as
1281 * switch jump tables. For objtool to recognize them, they
1282 * need to be placed in the C_JUMP_TABLE_SECTION section. They
1283 * have symbols associated with them.
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001284 */
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001285 if (find_symbol_containing(table_sec, table_offset) &&
1286 strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001287 continue;
1288
Josh Poimboeuf113d4bc2020-02-17 21:41:53 -06001289 /*
Matt Helsleyf1974222020-05-29 14:01:13 -07001290 * Each table entry has a reloc associated with it. The reloc
Josh Poimboeuf113d4bc2020-02-17 21:41:53 -06001291 * should reference text in the same function as the original
1292 * instruction.
1293 */
Matt Helsleyf1974222020-05-29 14:01:13 -07001294 table_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset);
1295 if (!table_reloc)
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001296 continue;
Matt Helsleyf1974222020-05-29 14:01:13 -07001297 dest_insn = find_insn(file, table_reloc->sym->sec, table_reloc->addend);
Josh Poimboeuf113d4bc2020-02-17 21:41:53 -06001298 if (!dest_insn || !dest_insn->func || dest_insn->func->pfunc != func)
1299 continue;
Josh Poimboeuf7dec80c2018-05-18 15:10:34 -05001300
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001301 /*
1302 * Use of RIP-relative switch jumps is quite rare, and
1303 * indicates a rare GCC quirk/bug which can leave dead code
1304 * behind.
1305 */
Matt Helsleyf1974222020-05-29 14:01:13 -07001306 if (text_reloc->type == R_X86_64_PC32)
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001307 file->ignore_unreachables = true;
1308
Matt Helsleyf1974222020-05-29 14:01:13 -07001309 return table_reloc;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001310 }
1311
1312 return NULL;
1313}
1314
Jann Hornbd98c812019-07-17 20:36:54 -05001315/*
1316 * First pass: Mark the head of each jump table so that in the next pass,
1317 * we know when a given jump table ends and the next one starts.
1318 */
1319static void mark_func_jump_tables(struct objtool_file *file,
1320 struct symbol *func)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001321{
Jann Hornbd98c812019-07-17 20:36:54 -05001322 struct instruction *insn, *last = NULL;
Matt Helsleyf1974222020-05-29 14:01:13 -07001323 struct reloc *reloc;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001324
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +01001325 func_for_each_insn(file, func, insn) {
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001326 if (!last)
1327 last = insn;
1328
1329 /*
1330 * Store back-pointers for unconditional forward jumps such
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001331 * that find_jump_table() can back-track using those and
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001332 * avoid some potentially confusing code.
1333 */
1334 if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
1335 insn->offset > last->offset &&
1336 insn->jump_dest->offset > insn->offset &&
1337 !insn->jump_dest->first_jump_src) {
1338
1339 insn->jump_dest->first_jump_src = insn;
1340 last = insn->jump_dest;
1341 }
1342
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001343 if (insn->type != INSN_JUMP_DYNAMIC)
1344 continue;
1345
Matt Helsleyf1974222020-05-29 14:01:13 -07001346 reloc = find_jump_table(file, func, insn);
1347 if (reloc) {
1348 reloc->jump_table_start = true;
1349 insn->jump_table = reloc;
Jann Hornbd98c812019-07-17 20:36:54 -05001350 }
1351 }
1352}
1353
1354static int add_func_jump_tables(struct objtool_file *file,
1355 struct symbol *func)
1356{
1357 struct instruction *insn;
1358 int ret;
1359
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +01001360 func_for_each_insn(file, func, insn) {
Jann Hornbd98c812019-07-17 20:36:54 -05001361 if (!insn->jump_table)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001362 continue;
1363
Jann Hornbd98c812019-07-17 20:36:54 -05001364 ret = add_jump_table(file, insn, insn->jump_table);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001365 if (ret)
1366 return ret;
1367 }
1368
1369 return 0;
1370}
1371
1372/*
1373 * For some switch statements, gcc generates a jump table in the .rodata
1374 * section which contains a list of addresses within the function to jump to.
1375 * This finds these jump tables and adds them to the insn->alts lists.
1376 */
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001377static int add_jump_table_alts(struct objtool_file *file)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001378{
1379 struct section *sec;
1380 struct symbol *func;
1381 int ret;
1382
Allan Xavier4a60aa02018-09-07 08:12:01 -05001383 if (!file->rodata)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001384 return 0;
1385
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001386 for_each_sec(file, sec) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001387 list_for_each_entry(func, &sec->symbol_list, list) {
1388 if (func->type != STT_FUNC)
1389 continue;
1390
Jann Hornbd98c812019-07-17 20:36:54 -05001391 mark_func_jump_tables(file, func);
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001392 ret = add_func_jump_tables(file, func);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001393 if (ret)
1394 return ret;
1395 }
1396 }
1397
1398 return 0;
1399}
1400
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001401static int read_unwind_hints(struct objtool_file *file)
1402{
Matt Helsleyf1974222020-05-29 14:01:13 -07001403 struct section *sec, *relocsec;
1404 struct reloc *reloc;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001405 struct unwind_hint *hint;
1406 struct instruction *insn;
1407 struct cfi_reg *cfa;
1408 int i;
1409
1410 sec = find_section_by_name(file->elf, ".discard.unwind_hints");
1411 if (!sec)
1412 return 0;
1413
Matt Helsleyf1974222020-05-29 14:01:13 -07001414 relocsec = sec->reloc;
1415 if (!relocsec) {
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001416 WARN("missing .rela.discard.unwind_hints section");
1417 return -1;
1418 }
1419
1420 if (sec->len % sizeof(struct unwind_hint)) {
1421 WARN("struct unwind_hint size mismatch");
1422 return -1;
1423 }
1424
1425 file->hints = true;
1426
1427 for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) {
1428 hint = (struct unwind_hint *)sec->data->d_buf + i;
1429
Matt Helsleyf1974222020-05-29 14:01:13 -07001430 reloc = find_reloc_by_dest(file->elf, sec, i * sizeof(*hint));
1431 if (!reloc) {
1432 WARN("can't find reloc for unwind_hints[%d]", i);
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001433 return -1;
1434 }
1435
Matt Helsleyf1974222020-05-29 14:01:13 -07001436 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001437 if (!insn) {
1438 WARN("can't find insn for unwind_hints[%d]", i);
1439 return -1;
1440 }
1441
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001442 cfa = &insn->cfi.cfa;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001443
Peter Zijlstrac536ed22020-04-01 16:54:26 +02001444 if (hint->type == UNWIND_HINT_TYPE_RET_OFFSET) {
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001445 insn->ret_offset = hint->sp_offset;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001446 continue;
1447 }
1448
1449 insn->hint = true;
1450
1451 switch (hint->sp_reg) {
1452 case ORC_REG_UNDEFINED:
1453 cfa->base = CFI_UNDEFINED;
1454 break;
1455 case ORC_REG_SP:
1456 cfa->base = CFI_SP;
1457 break;
1458 case ORC_REG_BP:
1459 cfa->base = CFI_BP;
1460 break;
1461 case ORC_REG_SP_INDIRECT:
1462 cfa->base = CFI_SP_INDIRECT;
1463 break;
1464 case ORC_REG_R10:
1465 cfa->base = CFI_R10;
1466 break;
1467 case ORC_REG_R13:
1468 cfa->base = CFI_R13;
1469 break;
1470 case ORC_REG_DI:
1471 cfa->base = CFI_DI;
1472 break;
1473 case ORC_REG_DX:
1474 cfa->base = CFI_DX;
1475 break;
1476 default:
1477 WARN_FUNC("unsupported unwind_hint sp base reg %d",
1478 insn->sec, insn->offset, hint->sp_reg);
1479 return -1;
1480 }
1481
1482 cfa->offset = hint->sp_offset;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001483 insn->cfi.type = hint->type;
1484 insn->cfi.end = hint->end;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001485 }
1486
1487 return 0;
1488}
1489
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001490static int read_retpoline_hints(struct objtool_file *file)
1491{
Josh Poimboeuf63474dc2018-03-06 17:58:15 -06001492 struct section *sec;
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001493 struct instruction *insn;
Matt Helsleyf1974222020-05-29 14:01:13 -07001494 struct reloc *reloc;
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001495
Josh Poimboeuf63474dc2018-03-06 17:58:15 -06001496 sec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe");
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001497 if (!sec)
1498 return 0;
1499
Matt Helsleyf1974222020-05-29 14:01:13 -07001500 list_for_each_entry(reloc, &sec->reloc_list, list) {
1501 if (reloc->sym->type != STT_SECTION) {
Josh Poimboeuf63474dc2018-03-06 17:58:15 -06001502 WARN("unexpected relocation symbol type in %s", sec->name);
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001503 return -1;
1504 }
1505
Matt Helsleyf1974222020-05-29 14:01:13 -07001506 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001507 if (!insn) {
Josh Poimboeuf63474dc2018-03-06 17:58:15 -06001508 WARN("bad .discard.retpoline_safe entry");
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001509 return -1;
1510 }
1511
1512 if (insn->type != INSN_JUMP_DYNAMIC &&
1513 insn->type != INSN_CALL_DYNAMIC) {
Josh Poimboeuf63474dc2018-03-06 17:58:15 -06001514 WARN_FUNC("retpoline_safe hint not an indirect jump/call",
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001515 insn->sec, insn->offset);
1516 return -1;
1517 }
1518
1519 insn->retpoline_safe = true;
1520 }
1521
1522 return 0;
1523}
1524
Peter Zijlstrac4a33932020-03-10 18:57:41 +01001525static int read_instr_hints(struct objtool_file *file)
1526{
1527 struct section *sec;
1528 struct instruction *insn;
Matt Helsleyf1974222020-05-29 14:01:13 -07001529 struct reloc *reloc;
Peter Zijlstrac4a33932020-03-10 18:57:41 +01001530
1531 sec = find_section_by_name(file->elf, ".rela.discard.instr_end");
1532 if (!sec)
1533 return 0;
1534
Matt Helsleyf1974222020-05-29 14:01:13 -07001535 list_for_each_entry(reloc, &sec->reloc_list, list) {
1536 if (reloc->sym->type != STT_SECTION) {
Peter Zijlstrac4a33932020-03-10 18:57:41 +01001537 WARN("unexpected relocation symbol type in %s", sec->name);
1538 return -1;
1539 }
1540
Matt Helsleyf1974222020-05-29 14:01:13 -07001541 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Peter Zijlstrac4a33932020-03-10 18:57:41 +01001542 if (!insn) {
1543 WARN("bad .discard.instr_end entry");
1544 return -1;
1545 }
1546
1547 insn->instr--;
1548 }
1549
1550 sec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
1551 if (!sec)
1552 return 0;
1553
Matt Helsleyf1974222020-05-29 14:01:13 -07001554 list_for_each_entry(reloc, &sec->reloc_list, list) {
1555 if (reloc->sym->type != STT_SECTION) {
Peter Zijlstrac4a33932020-03-10 18:57:41 +01001556 WARN("unexpected relocation symbol type in %s", sec->name);
1557 return -1;
1558 }
1559
Matt Helsleyf1974222020-05-29 14:01:13 -07001560 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Peter Zijlstrac4a33932020-03-10 18:57:41 +01001561 if (!insn) {
1562 WARN("bad .discard.instr_begin entry");
1563 return -1;
1564 }
1565
1566 insn->instr++;
1567 }
1568
1569 return 0;
1570}
1571
Alexandre Chartre8aa8eb22020-04-14 12:36:12 +02001572static int read_intra_function_calls(struct objtool_file *file)
1573{
1574 struct instruction *insn;
1575 struct section *sec;
Matt Helsleyf1974222020-05-29 14:01:13 -07001576 struct reloc *reloc;
Alexandre Chartre8aa8eb22020-04-14 12:36:12 +02001577
1578 sec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls");
1579 if (!sec)
1580 return 0;
1581
Matt Helsleyf1974222020-05-29 14:01:13 -07001582 list_for_each_entry(reloc, &sec->reloc_list, list) {
Alexandre Chartre8aa8eb22020-04-14 12:36:12 +02001583 unsigned long dest_off;
1584
Matt Helsleyf1974222020-05-29 14:01:13 -07001585 if (reloc->sym->type != STT_SECTION) {
Alexandre Chartre8aa8eb22020-04-14 12:36:12 +02001586 WARN("unexpected relocation symbol type in %s",
1587 sec->name);
1588 return -1;
1589 }
1590
Matt Helsleyf1974222020-05-29 14:01:13 -07001591 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Alexandre Chartre8aa8eb22020-04-14 12:36:12 +02001592 if (!insn) {
1593 WARN("bad .discard.intra_function_call entry");
1594 return -1;
1595 }
1596
1597 if (insn->type != INSN_CALL) {
1598 WARN_FUNC("intra_function_call not a direct call",
1599 insn->sec, insn->offset);
1600 return -1;
1601 }
1602
1603 /*
1604 * Treat intra-function CALLs as JMPs, but with a stack_op.
1605 * See add_call_destinations(), which strips stack_ops from
1606 * normal CALLs.
1607 */
1608 insn->type = INSN_JUMP_UNCONDITIONAL;
1609
1610 dest_off = insn->offset + insn->len + insn->immediate;
1611 insn->jump_dest = find_insn(file, insn->sec, dest_off);
1612 if (!insn->jump_dest) {
1613 WARN_FUNC("can't find call dest at %s+0x%lx",
1614 insn->sec, insn->offset,
1615 insn->sec->name, dest_off);
1616 return -1;
1617 }
1618 }
1619
1620 return 0;
1621}
1622
Josh Poimboeuf1e7e4782020-08-18 15:57:45 +02001623static int read_static_call_tramps(struct objtool_file *file)
1624{
1625 struct section *sec;
1626 struct symbol *func;
1627
1628 for_each_sec(file, sec) {
1629 list_for_each_entry(func, &sec->symbol_list, list) {
1630 if (func->bind == STB_GLOBAL &&
1631 !strncmp(func->name, STATIC_CALL_TRAMP_PREFIX_STR,
1632 strlen(STATIC_CALL_TRAMP_PREFIX_STR)))
1633 func->static_call_tramp = true;
1634 }
1635 }
1636
1637 return 0;
1638}
1639
Allan Xavier4a60aa02018-09-07 08:12:01 -05001640static void mark_rodata(struct objtool_file *file)
1641{
1642 struct section *sec;
1643 bool found = false;
1644
1645 /*
Josh Poimboeuf87b512d2019-06-27 20:50:46 -05001646 * Search for the following rodata sections, each of which can
1647 * potentially contain jump tables:
1648 *
1649 * - .rodata: can contain GCC switch tables
1650 * - .rodata.<func>: same, if -fdata-sections is being used
1651 * - .rodata..c_jump_table: contains C annotated jump tables
1652 *
1653 * .rodata.str1.* sections are ignored; they don't contain jump tables.
Allan Xavier4a60aa02018-09-07 08:12:01 -05001654 */
1655 for_each_sec(file, sec) {
Muchun Song1ee444702020-04-12 22:44:05 +08001656 if (!strncmp(sec->name, ".rodata", 7) &&
1657 !strstr(sec->name, ".str1.")) {
Allan Xavier4a60aa02018-09-07 08:12:01 -05001658 sec->rodata = true;
1659 found = true;
1660 }
1661 }
1662
1663 file->rodata = found;
1664}
1665
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001666static int decode_sections(struct objtool_file *file)
1667{
1668 int ret;
1669
Allan Xavier4a60aa02018-09-07 08:12:01 -05001670 mark_rodata(file);
1671
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001672 ret = decode_instructions(file);
1673 if (ret)
1674 return ret;
1675
1676 ret = add_dead_ends(file);
1677 if (ret)
1678 return ret;
1679
1680 add_ignores(file);
Peter Zijlstraea242132019-02-25 12:50:09 +01001681 add_uaccess_safe(file);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001682
Peter Zijlstraff05ab22019-03-18 14:33:07 +01001683 ret = add_ignore_alternatives(file);
Josh Poimboeuf258c7602018-01-11 21:46:24 +00001684 if (ret)
1685 return ret;
1686
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001687 ret = add_jump_destinations(file);
1688 if (ret)
1689 return ret;
1690
Josh Poimboeufa845c7c2018-01-29 22:00:39 -06001691 ret = add_special_section_alts(file);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001692 if (ret)
1693 return ret;
1694
Alexandre Chartre8aa8eb22020-04-14 12:36:12 +02001695 ret = read_intra_function_calls(file);
1696 if (ret)
1697 return ret;
1698
Josh Poimboeufa845c7c2018-01-29 22:00:39 -06001699 ret = add_call_destinations(file);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001700 if (ret)
1701 return ret;
1702
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001703 ret = add_jump_table_alts(file);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001704 if (ret)
1705 return ret;
1706
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001707 ret = read_unwind_hints(file);
1708 if (ret)
1709 return ret;
1710
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001711 ret = read_retpoline_hints(file);
1712 if (ret)
1713 return ret;
1714
Peter Zijlstrac4a33932020-03-10 18:57:41 +01001715 ret = read_instr_hints(file);
1716 if (ret)
1717 return ret;
1718
Josh Poimboeuf1e7e4782020-08-18 15:57:45 +02001719 ret = read_static_call_tramps(file);
1720 if (ret)
1721 return ret;
1722
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001723 return 0;
1724}
1725
1726static bool is_fentry_call(struct instruction *insn)
1727{
Alexandre Chartre87cf61f2020-04-14 12:36:10 +02001728 if (insn->type == INSN_CALL && insn->call_dest &&
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001729 insn->call_dest->type == STT_NOTYPE &&
1730 !strcmp(insn->call_dest->name, "__fentry__"))
1731 return true;
1732
1733 return false;
1734}
1735
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001736static bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001737{
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001738 u8 ret_offset = insn->ret_offset;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001739 struct cfi_state *cfi = &state->cfi;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001740 int i;
1741
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001742 if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap)
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001743 return true;
1744
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001745 if (cfi->cfa.offset != initial_func_cfi.cfa.offset + ret_offset)
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001746 return true;
1747
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001748 if (cfi->stack_size != initial_func_cfi.cfa.offset + ret_offset)
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001749 return true;
1750
Alexandre Chartrec721b3f82020-04-07 09:31:35 +02001751 /*
1752 * If there is a ret offset hint then don't check registers
1753 * because a callee-saved register might have been pushed on
1754 * the stack.
1755 */
1756 if (ret_offset)
1757 return false;
1758
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001759 for (i = 0; i < CFI_NUM_REGS; i++) {
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001760 if (cfi->regs[i].base != initial_func_cfi.regs[i].base ||
1761 cfi->regs[i].offset != initial_func_cfi.regs[i].offset)
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001762 return true;
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001763 }
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001764
1765 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001766}
1767
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001768static bool has_valid_stack_frame(struct insn_state *state)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001769{
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001770 struct cfi_state *cfi = &state->cfi;
1771
1772 if (cfi->cfa.base == CFI_BP && cfi->regs[CFI_BP].base == CFI_CFA &&
1773 cfi->regs[CFI_BP].offset == -16)
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001774 return true;
1775
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001776 if (cfi->drap && cfi->regs[CFI_BP].base == CFI_BP)
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001777 return true;
1778
1779 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001780}
1781
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001782static int update_cfi_state_regs(struct instruction *insn,
1783 struct cfi_state *cfi,
Julien Thierry65ea47d2020-03-27 15:28:47 +00001784 struct stack_op *op)
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001785{
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001786 struct cfi_reg *cfa = &cfi->cfa;
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001787
Josh Poimboeufd8dd25a2020-04-25 05:03:00 -05001788 if (cfa->base != CFI_SP && cfa->base != CFI_SP_INDIRECT)
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001789 return 0;
1790
1791 /* push */
Peter Zijlstraea242132019-02-25 12:50:09 +01001792 if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF)
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001793 cfa->offset += 8;
1794
1795 /* pop */
Peter Zijlstraea242132019-02-25 12:50:09 +01001796 if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF)
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001797 cfa->offset -= 8;
1798
1799 /* add immediate to sp */
1800 if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
1801 op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
1802 cfa->offset -= op->src.offset;
1803
1804 return 0;
1805}
1806
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001807static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001808{
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001809 if (arch_callee_saved_reg(reg) &&
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001810 cfi->regs[reg].base == CFI_UNDEFINED) {
1811 cfi->regs[reg].base = base;
1812 cfi->regs[reg].offset = offset;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001813 }
1814}
1815
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001816static void restore_reg(struct cfi_state *cfi, unsigned char reg)
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001817{
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001818 cfi->regs[reg].base = initial_func_cfi.regs[reg].base;
1819 cfi->regs[reg].offset = initial_func_cfi.regs[reg].offset;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001820}
1821
1822/*
1823 * A note about DRAP stack alignment:
1824 *
1825 * GCC has the concept of a DRAP register, which is used to help keep track of
1826 * the stack pointer when aligning the stack. r10 or r13 is used as the DRAP
1827 * register. The typical DRAP pattern is:
1828 *
1829 * 4c 8d 54 24 08 lea 0x8(%rsp),%r10
1830 * 48 83 e4 c0 and $0xffffffffffffffc0,%rsp
1831 * 41 ff 72 f8 pushq -0x8(%r10)
1832 * 55 push %rbp
1833 * 48 89 e5 mov %rsp,%rbp
1834 * (more pushes)
1835 * 41 52 push %r10
1836 * ...
1837 * 41 5a pop %r10
1838 * (more pops)
1839 * 5d pop %rbp
1840 * 49 8d 62 f8 lea -0x8(%r10),%rsp
1841 * c3 retq
1842 *
1843 * There are some variations in the epilogues, like:
1844 *
1845 * 5b pop %rbx
1846 * 41 5a pop %r10
1847 * 41 5c pop %r12
1848 * 41 5d pop %r13
1849 * 41 5e pop %r14
1850 * c9 leaveq
1851 * 49 8d 62 f8 lea -0x8(%r10),%rsp
1852 * c3 retq
1853 *
1854 * and:
1855 *
1856 * 4c 8b 55 e8 mov -0x18(%rbp),%r10
1857 * 48 8b 5d e0 mov -0x20(%rbp),%rbx
1858 * 4c 8b 65 f0 mov -0x10(%rbp),%r12
1859 * 4c 8b 6d f8 mov -0x8(%rbp),%r13
1860 * c9 leaveq
1861 * 49 8d 62 f8 lea -0x8(%r10),%rsp
1862 * c3 retq
1863 *
1864 * Sometimes r13 is used as the DRAP register, in which case it's saved and
1865 * restored beforehand:
1866 *
1867 * 41 55 push %r13
1868 * 4c 8d 6c 24 10 lea 0x10(%rsp),%r13
1869 * 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1870 * ...
1871 * 49 8d 65 f0 lea -0x10(%r13),%rsp
1872 * 41 5d pop %r13
1873 * c3 retq
1874 */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001875static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
Julien Thierry65ea47d2020-03-27 15:28:47 +00001876 struct stack_op *op)
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001877{
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001878 struct cfi_reg *cfa = &cfi->cfa;
1879 struct cfi_reg *regs = cfi->regs;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001880
1881 /* stack operations don't make sense with an undefined CFA */
1882 if (cfa->base == CFI_UNDEFINED) {
1883 if (insn->func) {
1884 WARN_FUNC("undefined stack state", insn->sec, insn->offset);
1885 return -1;
1886 }
1887 return 0;
1888 }
1889
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001890 if (cfi->type == ORC_TYPE_REGS || cfi->type == ORC_TYPE_REGS_IRET)
1891 return update_cfi_state_regs(insn, cfi, op);
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001892
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001893 switch (op->dest.type) {
1894
1895 case OP_DEST_REG:
1896 switch (op->src.type) {
1897
1898 case OP_SRC_REG:
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001899 if (op->src.reg == CFI_SP && op->dest.reg == CFI_BP &&
1900 cfa->base == CFI_SP &&
1901 regs[CFI_BP].base == CFI_CFA &&
1902 regs[CFI_BP].offset == -cfa->offset) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001903
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001904 /* mov %rsp, %rbp */
1905 cfa->base = op->dest.reg;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001906 cfi->bp_scratch = false;
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001907 }
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001908
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001909 else if (op->src.reg == CFI_SP &&
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001910 op->dest.reg == CFI_BP && cfi->drap) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001911
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001912 /* drap: mov %rsp, %rbp */
1913 regs[CFI_BP].base = CFI_BP;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001914 regs[CFI_BP].offset = -cfi->stack_size;
1915 cfi->bp_scratch = false;
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001916 }
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001917
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001918 else if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
1919
1920 /*
1921 * mov %rsp, %reg
1922 *
1923 * This is needed for the rare case where GCC
1924 * does:
1925 *
1926 * mov %rsp, %rax
1927 * ...
1928 * mov %rax, %rsp
1929 */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001930 cfi->vals[op->dest.reg].base = CFI_CFA;
1931 cfi->vals[op->dest.reg].offset = -cfi->stack_size;
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001932 }
1933
Josh Poimboeuf3c1f0582018-03-22 13:00:37 -05001934 else if (op->src.reg == CFI_BP && op->dest.reg == CFI_SP &&
1935 cfa->base == CFI_BP) {
1936
1937 /*
1938 * mov %rbp, %rsp
1939 *
1940 * Restore the original stack pointer (Clang).
1941 */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001942 cfi->stack_size = -cfi->regs[CFI_BP].offset;
Josh Poimboeuf3c1f0582018-03-22 13:00:37 -05001943 }
1944
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001945 else if (op->dest.reg == cfa->base) {
1946
1947 /* mov %reg, %rsp */
1948 if (cfa->base == CFI_SP &&
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001949 cfi->vals[op->src.reg].base == CFI_CFA) {
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001950
1951 /*
1952 * This is needed for the rare case
1953 * where GCC does something dumb like:
1954 *
1955 * lea 0x8(%rsp), %rcx
1956 * ...
1957 * mov %rcx, %rsp
1958 */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001959 cfa->offset = -cfi->vals[op->src.reg].offset;
1960 cfi->stack_size = cfa->offset;
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001961
1962 } else {
1963 cfa->base = CFI_UNDEFINED;
1964 cfa->offset = 0;
1965 }
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001966 }
1967
1968 break;
1969
1970 case OP_SRC_ADD:
1971 if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
1972
1973 /* add imm, %rsp */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001974 cfi->stack_size -= op->src.offset;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001975 if (cfa->base == CFI_SP)
1976 cfa->offset -= op->src.offset;
1977 break;
1978 }
1979
1980 if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
1981
1982 /* lea disp(%rbp), %rsp */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001983 cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001984 break;
1985 }
1986
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001987 if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05001988
1989 /* drap: lea disp(%rsp), %drap */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001990 cfi->drap_reg = op->dest.reg;
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001991
1992 /*
1993 * lea disp(%rsp), %reg
1994 *
1995 * This is needed for the rare case where GCC
1996 * does something dumb like:
1997 *
1998 * lea 0x8(%rsp), %rcx
1999 * ...
2000 * mov %rcx, %rsp
2001 */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002002 cfi->vals[op->dest.reg].base = CFI_CFA;
2003 cfi->vals[op->dest.reg].offset = \
2004 -cfi->stack_size + op->src.offset;
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05002005
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002006 break;
2007 }
2008
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002009 if (cfi->drap && op->dest.reg == CFI_SP &&
2010 op->src.reg == cfi->drap_reg) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002011
2012 /* drap: lea disp(%drap), %rsp */
2013 cfa->base = CFI_SP;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002014 cfa->offset = cfi->stack_size = -op->src.offset;
2015 cfi->drap_reg = CFI_UNDEFINED;
2016 cfi->drap = false;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002017 break;
2018 }
2019
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002020 if (op->dest.reg == cfi->cfa.base) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002021 WARN_FUNC("unsupported stack register modification",
2022 insn->sec, insn->offset);
2023 return -1;
2024 }
2025
2026 break;
2027
2028 case OP_SRC_AND:
2029 if (op->dest.reg != CFI_SP ||
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002030 (cfi->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
2031 (cfi->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002032 WARN_FUNC("unsupported stack pointer realignment",
2033 insn->sec, insn->offset);
2034 return -1;
2035 }
2036
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002037 if (cfi->drap_reg != CFI_UNDEFINED) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002038 /* drap: and imm, %rsp */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002039 cfa->base = cfi->drap_reg;
2040 cfa->offset = cfi->stack_size = 0;
2041 cfi->drap = true;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002042 }
2043
2044 /*
2045 * Older versions of GCC (4.8ish) realign the stack
2046 * without DRAP, with a frame pointer.
2047 */
2048
2049 break;
2050
2051 case OP_SRC_POP:
Peter Zijlstraea242132019-02-25 12:50:09 +01002052 case OP_SRC_POPF:
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002053 if (!cfi->drap && op->dest.reg == cfa->base) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002054
2055 /* pop %rbp */
2056 cfa->base = CFI_SP;
2057 }
2058
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002059 if (cfi->drap && cfa->base == CFI_BP_INDIRECT &&
2060 op->dest.reg == cfi->drap_reg &&
2061 cfi->drap_offset == -cfi->stack_size) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002062
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05002063 /* drap: pop %drap */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002064 cfa->base = cfi->drap_reg;
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05002065 cfa->offset = 0;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002066 cfi->drap_offset = -1;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002067
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002068 } else if (regs[op->dest.reg].offset == -cfi->stack_size) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002069
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05002070 /* pop %reg */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002071 restore_reg(cfi, op->dest.reg);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002072 }
2073
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002074 cfi->stack_size -= 8;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002075 if (cfa->base == CFI_SP)
2076 cfa->offset -= 8;
2077
2078 break;
2079
2080 case OP_SRC_REG_INDIRECT:
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002081 if (cfi->drap && op->src.reg == CFI_BP &&
2082 op->src.offset == cfi->drap_offset) {
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05002083
2084 /* drap: mov disp(%rbp), %drap */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002085 cfa->base = cfi->drap_reg;
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05002086 cfa->offset = 0;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002087 cfi->drap_offset = -1;
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05002088 }
2089
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002090 if (cfi->drap && op->src.reg == CFI_BP &&
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002091 op->src.offset == regs[op->dest.reg].offset) {
2092
2093 /* drap: mov disp(%rbp), %reg */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002094 restore_reg(cfi, op->dest.reg);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002095
2096 } else if (op->src.reg == cfa->base &&
2097 op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
2098
2099 /* mov disp(%rbp), %reg */
2100 /* mov disp(%rsp), %reg */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002101 restore_reg(cfi, op->dest.reg);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002102 }
2103
2104 break;
2105
2106 default:
2107 WARN_FUNC("unknown stack-related instruction",
2108 insn->sec, insn->offset);
2109 return -1;
2110 }
2111
2112 break;
2113
2114 case OP_DEST_PUSH:
Peter Zijlstraea242132019-02-25 12:50:09 +01002115 case OP_DEST_PUSHF:
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002116 cfi->stack_size += 8;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002117 if (cfa->base == CFI_SP)
2118 cfa->offset += 8;
2119
2120 if (op->src.type != OP_SRC_REG)
2121 break;
2122
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002123 if (cfi->drap) {
2124 if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002125
2126 /* drap: push %drap */
2127 cfa->base = CFI_BP_INDIRECT;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002128 cfa->offset = -cfi->stack_size;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002129
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05002130 /* save drap so we know when to restore it */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002131 cfi->drap_offset = -cfi->stack_size;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002132
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002133 } else if (op->src.reg == CFI_BP && cfa->base == cfi->drap_reg) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002134
2135 /* drap: push %rbp */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002136 cfi->stack_size = 0;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002137
2138 } else if (regs[op->src.reg].base == CFI_UNDEFINED) {
2139
2140 /* drap: push %reg */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002141 save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002142 }
2143
2144 } else {
2145
2146 /* push %reg */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002147 save_reg(cfi, op->src.reg, CFI_CFA, -cfi->stack_size);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002148 }
2149
2150 /* detect when asm code uses rbp as a scratch register */
Josh Poimboeuf867ac9d2017-07-24 18:34:14 -05002151 if (!no_fp && insn->func && op->src.reg == CFI_BP &&
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002152 cfa->base != CFI_BP)
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002153 cfi->bp_scratch = true;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002154 break;
2155
2156 case OP_DEST_REG_INDIRECT:
2157
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002158 if (cfi->drap) {
2159 if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002160
2161 /* drap: mov %drap, disp(%rbp) */
2162 cfa->base = CFI_BP_INDIRECT;
2163 cfa->offset = op->dest.offset;
2164
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05002165 /* save drap offset so we know when to restore it */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002166 cfi->drap_offset = op->dest.offset;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002167 }
2168
2169 else if (regs[op->src.reg].base == CFI_UNDEFINED) {
2170
2171 /* drap: mov reg, disp(%rbp) */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002172 save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002173 }
2174
2175 } else if (op->dest.reg == cfa->base) {
2176
2177 /* mov reg, disp(%rbp) */
2178 /* mov reg, disp(%rsp) */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002179 save_reg(cfi, op->src.reg, CFI_CFA,
2180 op->dest.offset - cfi->cfa.offset);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002181 }
2182
2183 break;
2184
2185 case OP_DEST_LEAVE:
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002186 if ((!cfi->drap && cfa->base != CFI_BP) ||
2187 (cfi->drap && cfa->base != cfi->drap_reg)) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002188 WARN_FUNC("leave instruction with modified stack frame",
2189 insn->sec, insn->offset);
2190 return -1;
2191 }
2192
2193 /* leave (mov %rbp, %rsp; pop %rbp) */
2194
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002195 cfi->stack_size = -cfi->regs[CFI_BP].offset - 8;
2196 restore_reg(cfi, CFI_BP);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002197
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002198 if (!cfi->drap) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002199 cfa->base = CFI_SP;
2200 cfa->offset -= 8;
2201 }
2202
2203 break;
2204
2205 case OP_DEST_MEM:
Peter Zijlstraea242132019-02-25 12:50:09 +01002206 if (op->src.type != OP_SRC_POP && op->src.type != OP_SRC_POPF) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002207 WARN_FUNC("unknown stack-related memory operation",
2208 insn->sec, insn->offset);
2209 return -1;
2210 }
2211
2212 /* pop mem */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002213 cfi->stack_size -= 8;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002214 if (cfa->base == CFI_SP)
2215 cfa->offset -= 8;
2216
2217 break;
2218
2219 default:
2220 WARN_FUNC("unknown stack-related instruction",
2221 insn->sec, insn->offset);
2222 return -1;
2223 }
2224
2225 return 0;
2226}
2227
Julien Thierry65ea47d2020-03-27 15:28:47 +00002228static int handle_insn_ops(struct instruction *insn, struct insn_state *state)
2229{
2230 struct stack_op *op;
2231
2232 list_for_each_entry(op, &insn->stack_ops, list) {
Peter Zijlstraab3852a2020-05-08 12:34:33 +02002233 struct cfi_state old_cfi = state->cfi;
Julien Thierry65ea47d2020-03-27 15:28:47 +00002234 int res;
2235
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002236 res = update_cfi_state(insn, &state->cfi, op);
Julien Thierry65ea47d2020-03-27 15:28:47 +00002237 if (res)
2238 return res;
2239
Peter Zijlstraab3852a2020-05-08 12:34:33 +02002240 if (insn->alt_group && memcmp(&state->cfi, &old_cfi, sizeof(struct cfi_state))) {
2241 WARN_FUNC("alternative modifies stack", insn->sec, insn->offset);
2242 return -1;
2243 }
2244
Julien Thierry65ea47d2020-03-27 15:28:47 +00002245 if (op->dest.type == OP_DEST_PUSHF) {
2246 if (!state->uaccess_stack) {
2247 state->uaccess_stack = 1;
2248 } else if (state->uaccess_stack >> 31) {
2249 WARN_FUNC("PUSHF stack exhausted",
2250 insn->sec, insn->offset);
2251 return 1;
2252 }
2253 state->uaccess_stack <<= 1;
2254 state->uaccess_stack |= state->uaccess;
2255 }
2256
2257 if (op->src.type == OP_SRC_POPF) {
2258 if (state->uaccess_stack) {
2259 state->uaccess = state->uaccess_stack & 1;
2260 state->uaccess_stack >>= 1;
2261 if (state->uaccess_stack == 1)
2262 state->uaccess_stack = 0;
2263 }
2264 }
2265 }
2266
2267 return 0;
2268}
2269
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002270static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002271{
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002272 struct cfi_state *cfi1 = &insn->cfi;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002273 int i;
2274
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002275 if (memcmp(&cfi1->cfa, &cfi2->cfa, sizeof(cfi1->cfa))) {
2276
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002277 WARN_FUNC("stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
2278 insn->sec, insn->offset,
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002279 cfi1->cfa.base, cfi1->cfa.offset,
2280 cfi2->cfa.base, cfi2->cfa.offset);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002281
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002282 } else if (memcmp(&cfi1->regs, &cfi2->regs, sizeof(cfi1->regs))) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002283 for (i = 0; i < CFI_NUM_REGS; i++) {
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002284 if (!memcmp(&cfi1->regs[i], &cfi2->regs[i],
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002285 sizeof(struct cfi_reg)))
2286 continue;
2287
2288 WARN_FUNC("stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
2289 insn->sec, insn->offset,
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002290 i, cfi1->regs[i].base, cfi1->regs[i].offset,
2291 i, cfi2->regs[i].base, cfi2->regs[i].offset);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002292 break;
2293 }
2294
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002295 } else if (cfi1->type != cfi2->type) {
Josh Poimboeuf627fce12017-07-11 10:33:42 -05002296
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002297 WARN_FUNC("stack state mismatch: type1=%d type2=%d",
2298 insn->sec, insn->offset, cfi1->type, cfi2->type);
2299
2300 } else if (cfi1->drap != cfi2->drap ||
2301 (cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) ||
2302 (cfi1->drap && cfi1->drap_offset != cfi2->drap_offset)) {
2303
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05002304 WARN_FUNC("stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002305 insn->sec, insn->offset,
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002306 cfi1->drap, cfi1->drap_reg, cfi1->drap_offset,
2307 cfi2->drap, cfi2->drap_reg, cfi2->drap_offset);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002308
2309 } else
2310 return true;
2311
2312 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002313}
2314
Peter Zijlstraea242132019-02-25 12:50:09 +01002315static inline bool func_uaccess_safe(struct symbol *func)
2316{
2317 if (func)
Josh Poimboeufe10cd8f2019-07-17 20:36:48 -05002318 return func->uaccess_safe;
Peter Zijlstraea242132019-02-25 12:50:09 +01002319
2320 return false;
2321}
2322
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002323static inline const char *call_dest_name(struct instruction *insn)
Peter Zijlstraea242132019-02-25 12:50:09 +01002324{
2325 if (insn->call_dest)
2326 return insn->call_dest->name;
2327
2328 return "{dynamic}";
2329}
2330
Peter Zijlstra6b643a02020-06-03 20:09:06 +02002331static inline bool noinstr_call_dest(struct symbol *func)
2332{
2333 /*
2334 * We can't deal with indirect function calls at present;
2335 * assume they're instrumented.
2336 */
2337 if (!func)
2338 return false;
2339
2340 /*
2341 * If the symbol is from a noinstr section; we good.
2342 */
2343 if (func->sec->noinstr)
2344 return true;
2345
2346 /*
2347 * The __ubsan_handle_*() calls are like WARN(), they only happen when
2348 * something 'BAD' happened. At the risk of taking the machine down,
2349 * let them proceed to get the message out.
2350 */
2351 if (!strncmp(func->name, "__ubsan_handle_", 15))
2352 return true;
2353
2354 return false;
2355}
2356
Peter Zijlstraea242132019-02-25 12:50:09 +01002357static int validate_call(struct instruction *insn, struct insn_state *state)
2358{
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002359 if (state->noinstr && state->instr <= 0 &&
Peter Zijlstra6b643a02020-06-03 20:09:06 +02002360 !noinstr_call_dest(insn->call_dest)) {
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002361 WARN_FUNC("call to %s() leaves .noinstr.text section",
2362 insn->sec, insn->offset, call_dest_name(insn));
2363 return 1;
2364 }
2365
Peter Zijlstraea242132019-02-25 12:50:09 +01002366 if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
2367 WARN_FUNC("call to %s() with UACCESS enabled",
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002368 insn->sec, insn->offset, call_dest_name(insn));
Peter Zijlstraea242132019-02-25 12:50:09 +01002369 return 1;
2370 }
2371
Peter Zijlstra2f0f9e92019-02-25 11:10:55 +01002372 if (state->df) {
2373 WARN_FUNC("call to %s() with DF set",
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002374 insn->sec, insn->offset, call_dest_name(insn));
Peter Zijlstra2f0f9e92019-02-25 11:10:55 +01002375 return 1;
2376 }
2377
Peter Zijlstraea242132019-02-25 12:50:09 +01002378 return 0;
2379}
2380
Peter Zijlstra54262aa2019-03-06 12:58:15 +01002381static int validate_sibling_call(struct instruction *insn, struct insn_state *state)
2382{
Peter Zijlstrae25eea82020-04-01 16:38:19 +02002383 if (has_modified_stack_frame(insn, state)) {
Peter Zijlstra54262aa2019-03-06 12:58:15 +01002384 WARN_FUNC("sibling call from callable instruction with modified stack frame",
2385 insn->sec, insn->offset);
2386 return 1;
2387 }
2388
Peter Zijlstraea242132019-02-25 12:50:09 +01002389 return validate_call(insn, state);
Peter Zijlstra54262aa2019-03-06 12:58:15 +01002390}
2391
Peter Zijlstraa92e92d2020-03-10 18:07:44 +01002392static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
2393{
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002394 if (state->noinstr && state->instr > 0) {
2395 WARN_FUNC("return with instrumentation enabled",
2396 insn->sec, insn->offset);
2397 return 1;
2398 }
2399
Peter Zijlstraa92e92d2020-03-10 18:07:44 +01002400 if (state->uaccess && !func_uaccess_safe(func)) {
2401 WARN_FUNC("return with UACCESS enabled",
2402 insn->sec, insn->offset);
2403 return 1;
2404 }
2405
2406 if (!state->uaccess && func_uaccess_safe(func)) {
2407 WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function",
2408 insn->sec, insn->offset);
2409 return 1;
2410 }
2411
2412 if (state->df) {
2413 WARN_FUNC("return with DF set",
2414 insn->sec, insn->offset);
2415 return 1;
2416 }
2417
Peter Zijlstrae25eea82020-04-01 16:38:19 +02002418 if (func && has_modified_stack_frame(insn, state)) {
Peter Zijlstraa92e92d2020-03-10 18:07:44 +01002419 WARN_FUNC("return with modified stack frame",
2420 insn->sec, insn->offset);
2421 return 1;
2422 }
2423
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002424 if (state->cfi.bp_scratch) {
Josh Poimboeufb2966952020-04-01 13:23:29 -05002425 WARN_FUNC("BP used as a scratch register",
2426 insn->sec, insn->offset);
Peter Zijlstraa92e92d2020-03-10 18:07:44 +01002427 return 1;
2428 }
2429
2430 return 0;
2431}
2432
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002433/*
Peter Zijlstra7117f16b2020-04-28 19:37:01 +02002434 * Alternatives should not contain any ORC entries, this in turn means they
2435 * should not contain any CFI ops, which implies all instructions should have
2436 * the same same CFI state.
2437 *
2438 * It is possible to constuct alternatives that have unreachable holes that go
2439 * unreported (because they're NOPs), such holes would result in CFI_UNDEFINED
2440 * states which then results in ORC entries, which we just said we didn't want.
2441 *
2442 * Avoid them by copying the CFI entry of the first instruction into the whole
2443 * alternative.
2444 */
2445static void fill_alternative_cfi(struct objtool_file *file, struct instruction *insn)
2446{
2447 struct instruction *first_insn = insn;
2448 int alt_group = insn->alt_group;
2449
2450 sec_for_each_insn_continue(file, insn) {
2451 if (insn->alt_group != alt_group)
2452 break;
2453 insn->cfi = first_insn->cfi;
2454 }
2455}
2456
2457/*
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002458 * Follow the branch starting at the given instruction, and recursively follow
2459 * any other branches (jumps). Meanwhile, track the frame pointer state at
2460 * each instruction and validate all the rules described in
2461 * tools/objtool/Documentation/stack-validation.txt.
2462 */
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002463static int validate_branch(struct objtool_file *file, struct symbol *func,
Peter Zijlstrab7460462020-04-02 10:15:51 +02002464 struct instruction *insn, struct insn_state state)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002465{
2466 struct alternative *alt;
Peter Zijlstrab7460462020-04-02 10:15:51 +02002467 struct instruction *next_insn;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002468 struct section *sec;
Peter Zijlstra882a0db2019-07-24 17:47:26 -05002469 u8 visited;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002470 int ret;
2471
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002472 sec = insn->sec;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002473
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002474 while (1) {
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002475 next_insn = next_insn_same_sec(file, insn);
2476
Josh Poimboeuf13810432018-05-09 22:39:15 -05002477 if (file->c_file && func && insn->func && func != insn->func->pfunc) {
Josh Poimboeufee976382017-08-11 12:24:15 -05002478 WARN("%s() falls through to next function %s()",
2479 func->name, insn->func->name);
2480 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002481 }
2482
Josh Poimboeuf48550222017-07-07 09:19:42 -05002483 if (func && insn->ignore) {
2484 WARN_FUNC("BUG: why am I validating an ignored function?",
2485 sec, insn->offset);
Josh Poimboeuf12b25722017-08-10 16:37:25 -05002486 return 1;
Josh Poimboeuf48550222017-07-07 09:19:42 -05002487 }
2488
Peter Zijlstra882a0db2019-07-24 17:47:26 -05002489 visited = 1 << state.uaccess;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002490 if (insn->visited) {
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002491 if (!insn->hint && !insn_cfi_match(insn, &state.cfi))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002492 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002493
Peter Zijlstra882a0db2019-07-24 17:47:26 -05002494 if (insn->visited & visited)
Peter Zijlstraea242132019-02-25 12:50:09 +01002495 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002496 }
2497
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002498 if (state.noinstr)
2499 state.instr += insn->instr;
2500
Peter Zijlstrac536ed22020-04-01 16:54:26 +02002501 if (insn->hint)
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002502 state.cfi = insn->cfi;
Peter Zijlstrac536ed22020-04-01 16:54:26 +02002503 else
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002504 insn->cfi = state.cfi;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002505
Peter Zijlstra882a0db2019-07-24 17:47:26 -05002506 insn->visited |= visited;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002507
Peter Zijlstra7117f16b2020-04-28 19:37:01 +02002508 if (!insn->ignore_alts && !list_empty(&insn->alts)) {
Peter Zijlstra764eef42019-03-01 11:19:03 +01002509 bool skip_orig = false;
2510
Josh Poimboeufa845c7c2018-01-29 22:00:39 -06002511 list_for_each_entry(alt, &insn->alts, list) {
Peter Zijlstra764eef42019-03-01 11:19:03 +01002512 if (alt->skip_orig)
2513 skip_orig = true;
2514
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002515 ret = validate_branch(file, func, alt->insn, state);
Peter Zijlstra7697eee2019-03-01 11:15:49 +01002516 if (ret) {
2517 if (backtrace)
2518 BT_FUNC("(alt)", insn);
2519 return ret;
2520 }
Josh Poimboeufa845c7c2018-01-29 22:00:39 -06002521 }
Peter Zijlstra764eef42019-03-01 11:19:03 +01002522
Peter Zijlstra7117f16b2020-04-28 19:37:01 +02002523 if (insn->alt_group)
2524 fill_alternative_cfi(file, insn);
2525
Peter Zijlstra764eef42019-03-01 11:19:03 +01002526 if (skip_orig)
2527 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002528 }
2529
Peter Zijlstra60041bc2020-04-24 16:16:41 +02002530 if (handle_insn_ops(insn, &state))
2531 return 1;
2532
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002533 switch (insn->type) {
2534
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002535 case INSN_RETURN:
Peter Zijlstraa92e92d2020-03-10 18:07:44 +01002536 return validate_return(func, insn, &state);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002537
2538 case INSN_CALL:
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002539 case INSN_CALL_DYNAMIC:
Peter Zijlstraea242132019-02-25 12:50:09 +01002540 ret = validate_call(insn, &state);
2541 if (ret)
2542 return ret;
2543
Josh Poimboeufc9bab222019-07-17 20:36:51 -05002544 if (!no_fp && func && !is_fentry_call(insn) &&
2545 !has_valid_stack_frame(&state)) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002546 WARN_FUNC("call without frame pointer save/setup",
2547 sec, insn->offset);
2548 return 1;
2549 }
Josh Poimboeufc9bab222019-07-17 20:36:51 -05002550
2551 if (dead_end_function(file, insn->call_dest))
2552 return 0;
2553
Josh Poimboeuf1e7e4782020-08-18 15:57:45 +02002554 if (insn->type == INSN_CALL && insn->call_dest->static_call_tramp) {
2555 list_add_tail(&insn->static_call_node,
2556 &file->static_call_list);
2557 }
2558
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002559 break;
2560
2561 case INSN_JUMP_CONDITIONAL:
2562 case INSN_JUMP_UNCONDITIONAL:
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002563 if (func && is_sibling_call(insn)) {
Peter Zijlstra54262aa2019-03-06 12:58:15 +01002564 ret = validate_sibling_call(insn, &state);
2565 if (ret)
2566 return ret;
2567
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002568 } else if (insn->jump_dest) {
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002569 ret = validate_branch(file, func,
2570 insn->jump_dest, state);
Peter Zijlstra7697eee2019-03-01 11:15:49 +01002571 if (ret) {
2572 if (backtrace)
2573 BT_FUNC("(branch)", insn);
2574 return ret;
2575 }
Josh Poimboeuf48550222017-07-07 09:19:42 -05002576 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002577
2578 if (insn->type == INSN_JUMP_UNCONDITIONAL)
2579 return 0;
2580
2581 break;
2582
2583 case INSN_JUMP_DYNAMIC:
Josh Poimboeufb68b9902019-07-17 20:36:57 -05002584 case INSN_JUMP_DYNAMIC_CONDITIONAL:
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002585 if (func && is_sibling_call(insn)) {
Peter Zijlstra54262aa2019-03-06 12:58:15 +01002586 ret = validate_sibling_call(insn, &state);
2587 if (ret)
2588 return ret;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002589 }
2590
Josh Poimboeufb68b9902019-07-17 20:36:57 -05002591 if (insn->type == INSN_JUMP_DYNAMIC)
2592 return 0;
2593
2594 break;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002595
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002596 case INSN_CONTEXT_SWITCH:
2597 if (func && (!next_insn || !next_insn->hint)) {
2598 WARN_FUNC("unsupported instruction in callable function",
2599 sec, insn->offset);
2600 return 1;
2601 }
2602 return 0;
2603
Peter Zijlstraea242132019-02-25 12:50:09 +01002604 case INSN_STAC:
2605 if (state.uaccess) {
2606 WARN_FUNC("recursive UACCESS enable", sec, insn->offset);
2607 return 1;
2608 }
2609
2610 state.uaccess = true;
2611 break;
2612
2613 case INSN_CLAC:
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002614 if (!state.uaccess && func) {
Peter Zijlstraea242132019-02-25 12:50:09 +01002615 WARN_FUNC("redundant UACCESS disable", sec, insn->offset);
2616 return 1;
2617 }
2618
2619 if (func_uaccess_safe(func) && !state.uaccess_stack) {
2620 WARN_FUNC("UACCESS-safe disables UACCESS", sec, insn->offset);
2621 return 1;
2622 }
2623
2624 state.uaccess = false;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002625 break;
2626
Peter Zijlstra2f0f9e92019-02-25 11:10:55 +01002627 case INSN_STD:
2628 if (state.df)
2629 WARN_FUNC("recursive STD", sec, insn->offset);
2630
2631 state.df = true;
2632 break;
2633
2634 case INSN_CLD:
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002635 if (!state.df && func)
Peter Zijlstra2f0f9e92019-02-25 11:10:55 +01002636 WARN_FUNC("redundant CLD", sec, insn->offset);
2637
2638 state.df = false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002639 break;
2640
2641 default:
2642 break;
2643 }
2644
2645 if (insn->dead_end)
2646 return 0;
2647
Josh Poimboeuf00d961802017-09-18 21:43:30 -05002648 if (!next_insn) {
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002649 if (state.cfi.cfa.base == CFI_UNDEFINED)
Josh Poimboeuf00d961802017-09-18 21:43:30 -05002650 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002651 WARN("%s: unexpected end of section", sec->name);
2652 return 1;
2653 }
Josh Poimboeuf00d961802017-09-18 21:43:30 -05002654
2655 insn = next_insn;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002656 }
2657
2658 return 0;
2659}
2660
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002661static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002662{
2663 struct instruction *insn;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002664 struct insn_state state;
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002665 int ret, warnings = 0;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002666
2667 if (!file->hints)
2668 return 0;
2669
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002670 init_insn_state(&state, sec);
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002671
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002672 if (sec) {
2673 insn = find_insn(file, sec, 0);
2674 if (!insn)
2675 return 0;
2676 } else {
2677 insn = list_first_entry(&file->insn_list, typeof(*insn), list);
2678 }
2679
2680 while (&insn->list != &file->insn_list && (!sec || insn->sec == sec)) {
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002681 if (insn->hint && !insn->visited) {
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002682 ret = validate_branch(file, insn->func, insn, state);
Peter Zijlstra7697eee2019-03-01 11:15:49 +01002683 if (ret && backtrace)
2684 BT_FUNC("<=== (hint)", insn);
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002685 warnings += ret;
2686 }
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002687
2688 insn = list_next_entry(insn, list);
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002689 }
2690
2691 return warnings;
2692}
2693
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01002694static int validate_retpoline(struct objtool_file *file)
2695{
2696 struct instruction *insn;
2697 int warnings = 0;
2698
2699 for_each_insn(file, insn) {
2700 if (insn->type != INSN_JUMP_DYNAMIC &&
2701 insn->type != INSN_CALL_DYNAMIC)
2702 continue;
2703
2704 if (insn->retpoline_safe)
2705 continue;
2706
Peter Zijlstraca41b972018-01-31 10:18:28 +01002707 /*
2708 * .init.text code is ran before userspace and thus doesn't
2709 * strictly need retpolines, except for modules which are
2710 * loaded late, they very much do need retpoline in their
2711 * .init.text
2712 */
2713 if (!strcmp(insn->sec->name, ".init.text") && !module)
2714 continue;
2715
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01002716 WARN_FUNC("indirect %s found in RETPOLINE build",
2717 insn->sec, insn->offset,
2718 insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call");
2719
2720 warnings++;
2721 }
2722
2723 return warnings;
2724}
2725
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002726static bool is_kasan_insn(struct instruction *insn)
2727{
2728 return (insn->type == INSN_CALL &&
2729 !strcmp(insn->call_dest->name, "__asan_handle_no_return"));
2730}
2731
2732static bool is_ubsan_insn(struct instruction *insn)
2733{
2734 return (insn->type == INSN_CALL &&
2735 !strcmp(insn->call_dest->name,
2736 "__ubsan_handle_builtin_unreachable"));
2737}
2738
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002739static bool ignore_unreachable_insn(struct instruction *insn)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002740{
2741 int i;
2742
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002743 if (insn->ignore || insn->type == INSN_NOP)
2744 return true;
2745
2746 /*
2747 * Ignore any unused exceptions. This can happen when a whitelisted
2748 * function has an exception table entry.
Josh Poimboeuf0e2bb2b2017-07-27 15:56:54 -05002749 *
2750 * Also ignore alternative replacement instructions. This can happen
2751 * when a whitelisted function uses one of the ALTERNATIVE macros.
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002752 */
Josh Poimboeuf0e2bb2b2017-07-27 15:56:54 -05002753 if (!strcmp(insn->sec->name, ".fixup") ||
2754 !strcmp(insn->sec->name, ".altinstr_replacement") ||
2755 !strcmp(insn->sec->name, ".altinstr_aux"))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002756 return true;
2757
Josh Poimboeufbd841d62020-04-01 13:23:25 -05002758 if (!insn->func)
2759 return false;
2760
2761 /*
2762 * CONFIG_UBSAN_TRAP inserts a UD2 when it sees
2763 * __builtin_unreachable(). The BUG() macro has an unreachable() after
2764 * the UD2, which causes GCC's undefined trap logic to emit another UD2
2765 * (or occasionally a JMP to UD2).
2766 */
2767 if (list_prev_entry(insn, list)->dead_end &&
2768 (insn->type == INSN_BUG ||
2769 (insn->type == INSN_JUMP_UNCONDITIONAL &&
2770 insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
2771 return true;
2772
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002773 /*
2774 * Check if this (or a subsequent) instruction is related to
2775 * CONFIG_UBSAN or CONFIG_KASAN.
2776 *
2777 * End the search at 5 instructions to avoid going into the weeds.
2778 */
2779 for (i = 0; i < 5; i++) {
2780
2781 if (is_kasan_insn(insn) || is_ubsan_insn(insn))
2782 return true;
2783
Josh Poimboeuffe24e272018-02-08 17:09:25 -06002784 if (insn->type == INSN_JUMP_UNCONDITIONAL) {
2785 if (insn->jump_dest &&
2786 insn->jump_dest->func == insn->func) {
2787 insn = insn->jump_dest;
2788 continue;
2789 }
2790
2791 break;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002792 }
2793
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002794 if (insn->offset + insn->len >= insn->func->offset + insn->func->len)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002795 break;
Josh Poimboeuffe24e272018-02-08 17:09:25 -06002796
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002797 insn = list_next_entry(insn, list);
2798 }
2799
2800 return false;
2801}
2802
Peter Zijlstra4b5e2e72020-03-23 21:17:50 +01002803static int validate_symbol(struct objtool_file *file, struct section *sec,
2804 struct symbol *sym, struct insn_state *state)
2805{
2806 struct instruction *insn;
2807 int ret;
2808
2809 if (!sym->len) {
2810 WARN("%s() is missing an ELF size annotation", sym->name);
2811 return 1;
2812 }
2813
2814 if (sym->pfunc != sym || sym->alias != sym)
2815 return 0;
2816
2817 insn = find_insn(file, sec, sym->offset);
2818 if (!insn || insn->ignore || insn->visited)
2819 return 0;
2820
2821 state->uaccess = sym->uaccess_safe;
2822
2823 ret = validate_branch(file, insn->func, insn, *state);
2824 if (ret && backtrace)
2825 BT_FUNC("<=== (sym)", insn);
2826 return ret;
2827}
2828
Peter Zijlstra350994b2020-03-23 20:57:13 +01002829static int validate_section(struct objtool_file *file, struct section *sec)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002830{
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002831 struct insn_state state;
Peter Zijlstra4b5e2e72020-03-23 21:17:50 +01002832 struct symbol *func;
2833 int warnings = 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002834
Peter Zijlstra350994b2020-03-23 20:57:13 +01002835 list_for_each_entry(func, &sec->symbol_list, list) {
2836 if (func->type != STT_FUNC)
2837 continue;
Josh Poimboeufe10cd8f2019-07-17 20:36:48 -05002838
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002839 init_insn_state(&state, sec);
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002840 state.cfi.cfa = initial_func_cfi.cfa;
2841 memcpy(&state.cfi.regs, &initial_func_cfi.regs,
Julien Thierry0699e552020-03-27 15:28:40 +00002842 CFI_NUM_REGS * sizeof(struct cfi_reg));
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002843 state.cfi.stack_size = initial_func_cfi.cfa.offset;
Julien Thierry0699e552020-03-27 15:28:40 +00002844
Peter Zijlstra4b5e2e72020-03-23 21:17:50 +01002845 warnings += validate_symbol(file, sec, func, &state);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002846 }
2847
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002848 return warnings;
2849}
2850
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002851static int validate_vmlinux_functions(struct objtool_file *file)
2852{
2853 struct section *sec;
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002854 int warnings = 0;
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002855
2856 sec = find_section_by_name(file->elf, ".noinstr.text");
Thomas Gleixner0cc9ac8d2020-03-25 17:18:17 +01002857 if (sec) {
2858 warnings += validate_section(file, sec);
2859 warnings += validate_unwind_hints(file, sec);
2860 }
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002861
Thomas Gleixner0cc9ac8d2020-03-25 17:18:17 +01002862 sec = find_section_by_name(file->elf, ".entry.text");
2863 if (sec) {
2864 warnings += validate_section(file, sec);
2865 warnings += validate_unwind_hints(file, sec);
2866 }
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002867
2868 return warnings;
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002869}
2870
Peter Zijlstra350994b2020-03-23 20:57:13 +01002871static int validate_functions(struct objtool_file *file)
2872{
2873 struct section *sec;
2874 int warnings = 0;
2875
Peter Zijlstrada837bd2020-03-23 21:11:14 +01002876 for_each_sec(file, sec) {
2877 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
2878 continue;
2879
Peter Zijlstra350994b2020-03-23 20:57:13 +01002880 warnings += validate_section(file, sec);
Peter Zijlstrada837bd2020-03-23 21:11:14 +01002881 }
Peter Zijlstra350994b2020-03-23 20:57:13 +01002882
2883 return warnings;
2884}
2885
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002886static int validate_reachable_instructions(struct objtool_file *file)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002887{
2888 struct instruction *insn;
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002889
2890 if (file->ignore_unreachables)
2891 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002892
2893 for_each_insn(file, insn) {
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002894 if (insn->visited || ignore_unreachable_insn(insn))
2895 continue;
2896
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002897 WARN_FUNC("unreachable instruction", insn->sec, insn->offset);
2898 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002899 }
2900
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002901 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002902}
2903
Josh Poimboeuf0c671812019-03-18 19:09:38 -05002904static struct objtool_file file;
2905
Peter Zijlstra43a45252018-01-16 17:16:32 +01002906int check(const char *_objname, bool orc)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002907{
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002908 int ret, warnings = 0;
2909
2910 objname = _objname;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002911
Peter Zijlstra2b10be22020-04-17 23:15:00 +02002912 file.elf = elf_open_read(objname, O_RDWR);
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002913 if (!file.elf)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002914 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002915
2916 INIT_LIST_HEAD(&file.insn_list);
2917 hash_init(file.insn_hash);
Josh Poimboeuf1e7e4782020-08-18 15:57:45 +02002918 INIT_LIST_HEAD(&file.static_call_list);
Peter Zijlstra734d0992020-06-17 18:22:31 +02002919 file.c_file = !vmlinux && find_section_by_name(file.elf, ".comment");
Josh Poimboeuf867ac9d2017-07-24 18:34:14 -05002920 file.ignore_unreachables = no_unreachable;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002921 file.hints = false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002922
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002923 arch_initial_func_cfi_state(&initial_func_cfi);
2924
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002925 ret = decode_sections(&file);
2926 if (ret < 0)
2927 goto out;
2928 warnings += ret;
2929
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002930 if (list_empty(&file.insn_list))
2931 goto out;
2932
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002933 if (vmlinux && !validate_dup) {
2934 ret = validate_vmlinux_functions(&file);
2935 if (ret < 0)
2936 goto out;
2937
2938 warnings += ret;
2939 goto out;
2940 }
2941
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01002942 if (retpoline) {
2943 ret = validate_retpoline(&file);
2944 if (ret < 0)
2945 return ret;
2946 warnings += ret;
2947 }
2948
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002949 ret = validate_functions(&file);
2950 if (ret < 0)
2951 goto out;
2952 warnings += ret;
2953
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002954 ret = validate_unwind_hints(&file, NULL);
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002955 if (ret < 0)
2956 goto out;
2957 warnings += ret;
2958
Josh Poimboeufbaa414692017-06-28 10:11:07 -05002959 if (!warnings) {
2960 ret = validate_reachable_instructions(&file);
2961 if (ret < 0)
2962 goto out;
2963 warnings += ret;
2964 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002965
Josh Poimboeuf1e7e4782020-08-18 15:57:45 +02002966 ret = create_static_call_sections(&file);
2967 if (ret < 0)
2968 goto out;
2969 warnings += ret;
2970
Josh Poimboeuf627fce12017-07-11 10:33:42 -05002971 if (orc) {
2972 ret = create_orc(&file);
2973 if (ret < 0)
2974 goto out;
2975
2976 ret = create_orc_sections(&file);
2977 if (ret < 0)
2978 goto out;
Peter Zijlstra2b10be22020-04-17 23:15:00 +02002979 }
Josh Poimboeuf627fce12017-07-11 10:33:42 -05002980
Peter Zijlstra2b10be22020-04-17 23:15:00 +02002981 if (file.elf->changed) {
Josh Poimboeuf627fce12017-07-11 10:33:42 -05002982 ret = elf_write(file.elf);
2983 if (ret < 0)
2984 goto out;
2985 }
2986
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002987out:
Josh Poimboeuf644592d2020-02-10 12:32:38 -06002988 if (ret < 0) {
2989 /*
2990 * Fatal error. The binary is corrupt or otherwise broken in
2991 * some way, or objtool itself is broken. Fail the kernel
2992 * build.
2993 */
2994 return ret;
2995 }
2996
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002997 return 0;
2998}