2019-05-19 16:51:43 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2017-06-28 18:11:05 +03:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
2021-06-24 12:41:01 +03:00
|
|
|
#include <sys/mman.h>
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2020-11-13 02:03:32 +03:00
|
|
|
#include <arch/elf.h>
|
|
|
|
#include <objtool/builtin.h>
|
|
|
|
#include <objtool/cfi.h>
|
|
|
|
#include <objtool/arch.h>
|
|
|
|
#include <objtool/check.h>
|
|
|
|
#include <objtool/special.h>
|
|
|
|
#include <objtool/warn.h>
|
|
|
|
#include <objtool/endianness.h>
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2020-09-04 18:30:27 +03:00
|
|
|
#include <linux/objtool.h>
|
2017-06-28 18:11:05 +03:00
|
|
|
#include <linux/hashtable.h>
|
|
|
|
#include <linux/kernel.h>
|
2020-08-18 16:57:45 +03:00
|
|
|
#include <linux/static_call_types.h>
|
2017-06-28 18:11:05 +03:00
|
|
|
|
|
|
|
struct alternative {
|
|
|
|
struct list_head list;
|
|
|
|
struct instruction *insn;
|
2019-03-01 13:19:03 +03:00
|
|
|
bool skip_orig;
|
2017-06-28 18:11:05 +03:00
|
|
|
};
|
|
|
|
|
2021-06-24 12:41:01 +03:00
|
|
|
static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache;
|
|
|
|
|
|
|
|
static struct cfi_init_state initial_func_cfi;
|
|
|
|
static struct cfi_state init_cfi;
|
|
|
|
static struct cfi_state func_cfi;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2017-07-11 18:33:42 +03:00
|
|
|
struct instruction *find_insn(struct objtool_file *file,
|
|
|
|
struct section *sec, unsigned long offset)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
|
|
|
struct instruction *insn;
|
|
|
|
|
2020-03-16 17:47:27 +03:00
|
|
|
hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) {
|
2017-06-28 18:11:05 +03:00
|
|
|
if (insn->sec == sec && insn->offset == offset)
|
|
|
|
return insn;
|
2020-03-16 17:47:27 +03:00
|
|
|
}
|
2017-06-28 18:11:05 +03:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct instruction *next_insn_same_sec(struct objtool_file *file,
|
|
|
|
struct instruction *insn)
|
|
|
|
{
|
|
|
|
struct instruction *next = list_next_entry(insn, list);
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
if (!next || &next->list == &file->insn_list || next->sec != insn->sec)
|
2017-06-28 18:11:05 +03:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
|
2018-05-10 06:39:15 +03:00
|
|
|
static struct instruction *next_insn_same_func(struct objtool_file *file,
|
|
|
|
struct instruction *insn)
|
|
|
|
{
|
|
|
|
struct instruction *next = list_next_entry(insn, list);
|
|
|
|
struct symbol *func = insn->func;
|
|
|
|
|
|
|
|
if (!func)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (&next->list != &file->insn_list && next->func == func)
|
|
|
|
return next;
|
|
|
|
|
|
|
|
/* Check if we're already in the subfunction: */
|
|
|
|
if (func == func->cfunc)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Move to the subfunction: */
|
|
|
|
return find_insn(file, func->cfunc->sec, func->cfunc->offset);
|
|
|
|
}
|
|
|
|
|
2020-04-29 00:45:16 +03:00
|
|
|
static struct instruction *prev_insn_same_sym(struct objtool_file *file,
|
|
|
|
struct instruction *insn)
|
|
|
|
{
|
|
|
|
struct instruction *prev = list_prev_entry(insn, list);
|
|
|
|
|
|
|
|
if (&prev->list != &file->insn_list && prev->func == insn->func)
|
|
|
|
return prev;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-03-10 20:27:24 +03:00
|
|
|
#define func_for_each_insn(file, func, insn) \
|
2018-05-10 06:39:15 +03:00
|
|
|
for (insn = find_insn(file, func->sec, func->offset); \
|
|
|
|
insn; \
|
|
|
|
insn = next_insn_same_func(file, insn))
|
|
|
|
|
2020-03-10 20:24:59 +03:00
|
|
|
#define sym_for_each_insn(file, sym, insn) \
|
|
|
|
for (insn = find_insn(file, sym->sec, sym->offset); \
|
2017-06-28 18:11:05 +03:00
|
|
|
insn && &insn->list != &file->insn_list && \
|
2020-03-10 20:24:59 +03:00
|
|
|
insn->sec == sym->sec && \
|
|
|
|
insn->offset < sym->offset + sym->len; \
|
2017-06-28 18:11:05 +03:00
|
|
|
insn = list_next_entry(insn, list))
|
|
|
|
|
2020-03-10 20:24:59 +03:00
|
|
|
#define sym_for_each_insn_continue_reverse(file, sym, insn) \
|
2017-06-28 18:11:05 +03:00
|
|
|
for (insn = list_prev_entry(insn, list); \
|
|
|
|
&insn->list != &file->insn_list && \
|
2020-03-10 20:24:59 +03:00
|
|
|
insn->sec == sym->sec && insn->offset >= sym->offset; \
|
2017-06-28 18:11:05 +03:00
|
|
|
insn = list_prev_entry(insn, list))
|
|
|
|
|
|
|
|
#define sec_for_each_insn_from(file, insn) \
|
|
|
|
for (; insn; insn = next_insn_same_sec(file, insn))
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
#define sec_for_each_insn_continue(file, insn) \
|
|
|
|
for (insn = next_insn_same_sec(file, insn); insn; \
|
|
|
|
insn = next_insn_same_sec(file, insn))
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2021-02-24 19:29:14 +03:00
|
|
|
static bool is_jump_table_jump(struct instruction *insn)
|
|
|
|
{
|
|
|
|
struct alt_group *alt_group = insn->alt_group;
|
|
|
|
|
|
|
|
if (insn->jump_table)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Retpoline alternative for a jump table? */
|
|
|
|
return alt_group && alt_group->orig_group &&
|
|
|
|
alt_group->orig_group->first_insn->jump_table;
|
|
|
|
}
|
|
|
|
|
2019-07-18 04:36:52 +03:00
|
|
|
static bool is_sibling_call(struct instruction *insn)
|
|
|
|
{
|
2021-01-22 00:29:22 +03:00
|
|
|
/*
|
|
|
|
* Assume only ELF functions can make sibling calls. This ensures
|
|
|
|
* sibling call detection consistency between vmlinux.o and individual
|
|
|
|
* objects.
|
|
|
|
*/
|
|
|
|
if (!insn->func)
|
|
|
|
return false;
|
|
|
|
|
2019-07-18 04:36:52 +03:00
|
|
|
/* An indirect jump is either a sibling call or a jump to a table. */
|
|
|
|
if (insn->type == INSN_JUMP_DYNAMIC)
|
2021-02-24 19:29:14 +03:00
|
|
|
return !is_jump_table_jump(insn);
|
2019-07-18 04:36:52 +03:00
|
|
|
|
|
|
|
/* add_jump_destinations() sets insn->call_dest for sibling calls. */
|
2021-01-22 00:29:22 +03:00
|
|
|
return (is_static_jump(insn) && insn->call_dest);
|
2019-07-18 04:36:52 +03:00
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
/*
|
|
|
|
* This checks to see if the given function is a "noreturn" function.
|
|
|
|
*
|
|
|
|
* For global functions which are outside the scope of this object file, we
|
|
|
|
* have to keep a manual list of them.
|
|
|
|
*
|
|
|
|
* For local functions, we have to detect them manually by simply looking for
|
|
|
|
* the lack of a return instruction.
|
|
|
|
*/
|
2019-07-18 04:36:50 +03:00
|
|
|
static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
|
|
|
|
int recursion)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct instruction *insn;
|
|
|
|
bool empty = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unfortunately these have to be hard coded because the noreturn
|
|
|
|
* attribute isn't provided in ELF data.
|
|
|
|
*/
|
|
|
|
static const char * const global_noreturns[] = {
|
|
|
|
"__stack_chk_fail",
|
|
|
|
"panic",
|
|
|
|
"do_exit",
|
|
|
|
"do_task_dead",
|
|
|
|
"__module_put_and_exit",
|
|
|
|
"complete_and_exit",
|
|
|
|
"__reiserfs_panic",
|
|
|
|
"lbug_with_loc",
|
|
|
|
"fortify_panic",
|
2018-01-11 01:22:38 +03:00
|
|
|
"usercopy_abort",
|
2018-06-19 18:47:50 +03:00
|
|
|
"machine_real_restart",
|
2019-04-04 20:17:35 +03:00
|
|
|
"rewind_stack_do_exit",
|
2019-09-23 12:02:38 +03:00
|
|
|
"kunit_try_catch_throw",
|
2021-01-22 00:29:25 +03:00
|
|
|
"xen_start_kernel",
|
2021-06-24 12:41:00 +03:00
|
|
|
"cpu_bringup_and_idle",
|
2017-06-28 18:11:05 +03:00
|
|
|
};
|
|
|
|
|
2019-07-18 04:36:51 +03:00
|
|
|
if (!func)
|
|
|
|
return false;
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
if (func->bind == STB_WEAK)
|
2019-07-18 04:36:50 +03:00
|
|
|
return false;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
|
|
|
if (func->bind == STB_GLOBAL)
|
|
|
|
for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
|
|
|
|
if (!strcmp(func->name, global_noreturns[i]))
|
2019-07-18 04:36:50 +03:00
|
|
|
return true;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2018-05-10 06:39:15 +03:00
|
|
|
if (!func->len)
|
2019-07-18 04:36:50 +03:00
|
|
|
return false;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2018-05-10 06:39:15 +03:00
|
|
|
insn = find_insn(file, func->sec, func->offset);
|
|
|
|
if (!insn->func)
|
2019-07-18 04:36:50 +03:00
|
|
|
return false;
|
2018-05-10 06:39:15 +03:00
|
|
|
|
2020-03-10 20:27:24 +03:00
|
|
|
func_for_each_insn(file, func, insn) {
|
2017-06-28 18:11:05 +03:00
|
|
|
empty = false;
|
|
|
|
|
|
|
|
if (insn->type == INSN_RETURN)
|
2019-07-18 04:36:50 +03:00
|
|
|
return false;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (empty)
|
2019-07-18 04:36:50 +03:00
|
|
|
return false;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* A function can have a sibling call instead of a return. In that
|
|
|
|
* case, the function's dead-end status depends on whether the target
|
|
|
|
* of the sibling call returns.
|
|
|
|
*/
|
2020-03-10 20:27:24 +03:00
|
|
|
func_for_each_insn(file, func, insn) {
|
2019-07-18 04:36:52 +03:00
|
|
|
if (is_sibling_call(insn)) {
|
2017-06-28 18:11:05 +03:00
|
|
|
struct instruction *dest = insn->jump_dest;
|
|
|
|
|
|
|
|
if (!dest)
|
|
|
|
/* sibling call to another file */
|
2019-07-18 04:36:50 +03:00
|
|
|
return false;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2019-07-18 04:36:52 +03:00
|
|
|
/* local sibling call */
|
|
|
|
if (recursion == 5) {
|
|
|
|
/*
|
|
|
|
* Infinite recursion: two functions have
|
|
|
|
* sibling calls to each other. This is a very
|
|
|
|
* rare case. It means they aren't dead ends.
|
|
|
|
*/
|
|
|
|
return false;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2019-07-18 04:36:52 +03:00
|
|
|
return __dead_end_function(file, dest->func, recursion+1);
|
|
|
|
}
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2019-07-18 04:36:50 +03:00
|
|
|
return true;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2019-07-18 04:36:50 +03:00
|
|
|
static bool dead_end_function(struct objtool_file *file, struct symbol *func)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
|
|
|
return __dead_end_function(file, func, 0);
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
static void init_cfi_state(struct cfi_state *cfi)
|
2017-06-28 18:11:07 +03:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2017-08-29 20:51:03 +03:00
|
|
|
for (i = 0; i < CFI_NUM_REGS; i++) {
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->regs[i].base = CFI_UNDEFINED;
|
|
|
|
cfi->vals[i].base = CFI_UNDEFINED;
|
2017-08-29 20:51:03 +03:00
|
|
|
}
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->cfa.base = CFI_UNDEFINED;
|
|
|
|
cfi->drap_reg = CFI_UNDEFINED;
|
|
|
|
cfi->drap_offset = -1;
|
|
|
|
}
|
|
|
|
|
2020-03-23 20:26:03 +03:00
|
|
|
static void init_insn_state(struct insn_state *state, struct section *sec)
|
2020-03-25 16:04:45 +03:00
|
|
|
{
|
|
|
|
memset(state, 0, sizeof(*state));
|
|
|
|
init_cfi_state(&state->cfi);
|
2020-03-23 20:26:03 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We need the full vmlinux for noinstr validation, otherwise we can
|
|
|
|
* not correctly determine insn->call_dest->sec (external symbols do
|
|
|
|
* not have a section).
|
|
|
|
*/
|
2020-10-01 00:36:59 +03:00
|
|
|
if (vmlinux && noinstr && sec)
|
2020-03-23 20:26:03 +03:00
|
|
|
state->noinstr = sec->noinstr;
|
2017-06-28 18:11:07 +03:00
|
|
|
}
|
|
|
|
|
2021-06-24 12:41:01 +03:00
|
|
|
static struct cfi_state *cfi_alloc(void)
|
|
|
|
{
|
|
|
|
struct cfi_state *cfi = calloc(sizeof(struct cfi_state), 1);
|
|
|
|
if (!cfi) {
|
|
|
|
WARN("calloc failed");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
nr_cfi++;
|
|
|
|
return cfi;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cfi_bits;
|
|
|
|
static struct hlist_head *cfi_hash;
|
|
|
|
|
|
|
|
static inline bool cficmp(struct cfi_state *cfi1, struct cfi_state *cfi2)
|
|
|
|
{
|
|
|
|
return memcmp((void *)cfi1 + sizeof(cfi1->hash),
|
|
|
|
(void *)cfi2 + sizeof(cfi2->hash),
|
|
|
|
sizeof(struct cfi_state) - sizeof(struct hlist_node));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline u32 cfi_key(struct cfi_state *cfi)
|
|
|
|
{
|
|
|
|
return jhash((void *)cfi + sizeof(cfi->hash),
|
|
|
|
sizeof(*cfi) - sizeof(cfi->hash), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cfi_state *cfi_hash_find_or_add(struct cfi_state *cfi)
|
|
|
|
{
|
|
|
|
struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)];
|
|
|
|
struct cfi_state *obj;
|
|
|
|
|
|
|
|
hlist_for_each_entry(obj, head, hash) {
|
|
|
|
if (!cficmp(cfi, obj)) {
|
|
|
|
nr_cfi_cache++;
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = cfi_alloc();
|
|
|
|
*obj = *cfi;
|
|
|
|
hlist_add_head(&obj->hash, head);
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cfi_hash_add(struct cfi_state *cfi)
|
|
|
|
{
|
|
|
|
struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)];
|
|
|
|
|
|
|
|
hlist_add_head(&cfi->hash, head);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *cfi_hash_alloc(unsigned long size)
|
|
|
|
{
|
|
|
|
cfi_bits = max(10, ilog2(size));
|
|
|
|
cfi_hash = mmap(NULL, sizeof(struct hlist_head) << cfi_bits,
|
|
|
|
PROT_READ|PROT_WRITE,
|
|
|
|
MAP_PRIVATE|MAP_ANON, -1, 0);
|
|
|
|
if (cfi_hash == (void *)-1L) {
|
|
|
|
WARN("mmap fail cfi_hash");
|
|
|
|
cfi_hash = NULL;
|
|
|
|
} else if (stats) {
|
|
|
|
printf("cfi_bits: %d\n", cfi_bits);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cfi_hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long nr_insns;
|
|
|
|
static unsigned long nr_insns_visited;
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
/*
|
|
|
|
* Call the arch-specific instruction decoder for all the instructions and add
|
|
|
|
* them to the global instruction list.
|
|
|
|
*/
|
|
|
|
static int decode_instructions(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct section *sec;
|
|
|
|
struct symbol *func;
|
|
|
|
unsigned long offset;
|
|
|
|
struct instruction *insn;
|
|
|
|
int ret;
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
for_each_sec(file, sec) {
|
2017-06-28 18:11:05 +03:00
|
|
|
|
|
|
|
if (!(sec->sh.sh_flags & SHF_EXECINSTR))
|
|
|
|
continue;
|
|
|
|
|
2017-07-11 18:33:42 +03:00
|
|
|
if (strcmp(sec->name, ".altinstr_replacement") &&
|
|
|
|
strcmp(sec->name, ".altinstr_aux") &&
|
|
|
|
strncmp(sec->name, ".discard.", 9))
|
|
|
|
sec->text = true;
|
|
|
|
|
2020-03-25 19:18:17 +03:00
|
|
|
if (!strcmp(sec->name, ".noinstr.text") ||
|
|
|
|
!strcmp(sec->name, ".entry.text"))
|
2020-03-10 20:57:41 +03:00
|
|
|
sec->noinstr = true;
|
|
|
|
|
2021-08-23 01:50:37 +03:00
|
|
|
for (offset = 0; offset < sec->sh.sh_size; offset += insn->len) {
|
2017-06-28 18:11:05 +03:00
|
|
|
insn = malloc(sizeof(*insn));
|
2017-06-28 18:11:07 +03:00
|
|
|
if (!insn) {
|
|
|
|
WARN("malloc failed");
|
|
|
|
return -1;
|
|
|
|
}
|
2017-06-28 18:11:05 +03:00
|
|
|
memset(insn, 0, sizeof(*insn));
|
|
|
|
INIT_LIST_HEAD(&insn->alts);
|
2020-03-27 18:28:47 +03:00
|
|
|
INIT_LIST_HEAD(&insn->stack_ops);
|
2017-06-28 18:11:07 +03:00
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
insn->sec = sec;
|
|
|
|
insn->offset = offset;
|
|
|
|
|
2021-06-24 12:41:23 +03:00
|
|
|
ret = arch_decode_instruction(file, sec, offset,
|
2021-08-23 01:50:37 +03:00
|
|
|
sec->sh.sh_size - offset,
|
2017-06-28 18:11:05 +03:00
|
|
|
&insn->len, &insn->type,
|
2017-06-28 18:11:07 +03:00
|
|
|
&insn->immediate,
|
2020-03-27 18:28:47 +03:00
|
|
|
&insn->stack_ops);
|
2017-06-28 18:11:05 +03:00
|
|
|
if (ret)
|
2017-10-19 19:27:24 +03:00
|
|
|
goto err;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2020-03-16 17:47:27 +03:00
|
|
|
hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
|
2017-06-28 18:11:05 +03:00
|
|
|
list_add_tail(&insn->list, &file->insn_list);
|
2020-03-12 11:26:29 +03:00
|
|
|
nr_insns++;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(func, &sec->symbol_list, list) {
|
2019-07-18 04:36:48 +03:00
|
|
|
if (func->type != STT_FUNC || func->alias != func)
|
2017-06-28 18:11:05 +03:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!find_insn(file, sec, func->offset)) {
|
|
|
|
WARN("%s(): can't find starting instruction",
|
|
|
|
func->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-03-10 20:24:59 +03:00
|
|
|
sym_for_each_insn(file, func, insn)
|
2019-07-18 04:36:48 +03:00
|
|
|
insn->func = func;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-12 11:26:29 +03:00
|
|
|
if (stats)
|
|
|
|
printf("nr_insns: %lu\n", nr_insns);
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
return 0;
|
2017-10-19 19:27:24 +03:00
|
|
|
|
|
|
|
err:
|
|
|
|
free(insn);
|
|
|
|
return ret;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2021-06-24 12:41:23 +03:00
|
|
|
/*
|
|
|
|
* Read the pv_ops[] .data table to find the static initialized values.
|
|
|
|
*/
|
|
|
|
static int add_pv_ops(struct objtool_file *file, const char *symname)
|
|
|
|
{
|
|
|
|
struct symbol *sym, *func;
|
|
|
|
unsigned long off, end;
|
|
|
|
struct reloc *rel;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
sym = find_symbol_by_name(file->elf, symname);
|
|
|
|
if (!sym)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
off = sym->offset;
|
|
|
|
end = off + sym->len;
|
|
|
|
for (;;) {
|
|
|
|
rel = find_reloc_by_dest_range(file->elf, sym->sec, off, end - off);
|
|
|
|
if (!rel)
|
|
|
|
break;
|
|
|
|
|
|
|
|
func = rel->sym;
|
|
|
|
if (func->type == STT_SECTION)
|
|
|
|
func = find_symbol_by_offset(rel->sym->sec, rel->addend);
|
|
|
|
|
|
|
|
idx = (rel->offset - sym->offset) / sizeof(unsigned long);
|
|
|
|
|
|
|
|
objtool_pv_add(file, idx, func);
|
|
|
|
|
|
|
|
off = rel->offset + 1;
|
|
|
|
if (off > end)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate and initialize file->pv_ops[].
|
|
|
|
*/
|
|
|
|
static int init_pv_ops(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
static const char *pv_ops_tables[] = {
|
|
|
|
"pv_ops",
|
|
|
|
"xen_cpu_ops",
|
|
|
|
"xen_irq_ops",
|
|
|
|
"xen_mmu_ops",
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
const char *pv_ops;
|
|
|
|
struct symbol *sym;
|
|
|
|
int idx, nr;
|
|
|
|
|
|
|
|
if (!noinstr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
file->pv_ops = NULL;
|
|
|
|
|
|
|
|
sym = find_symbol_by_name(file->elf, "pv_ops");
|
|
|
|
if (!sym)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nr = sym->len / sizeof(unsigned long);
|
|
|
|
file->pv_ops = calloc(sizeof(struct pv_state), nr);
|
|
|
|
if (!file->pv_ops)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (idx = 0; idx < nr; idx++)
|
|
|
|
INIT_LIST_HEAD(&file->pv_ops[idx].targets);
|
|
|
|
|
|
|
|
for (idx = 0; (pv_ops = pv_ops_tables[idx]); idx++)
|
|
|
|
add_pv_ops(file, pv_ops);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-22 01:08:43 +03:00
|
|
|
static struct instruction *find_last_insn(struct objtool_file *file,
|
|
|
|
struct section *sec)
|
|
|
|
{
|
|
|
|
struct instruction *insn = NULL;
|
|
|
|
unsigned int offset;
|
2021-08-23 01:50:37 +03:00
|
|
|
unsigned int end = (sec->sh.sh_size > 10) ? sec->sh.sh_size - 10 : 0;
|
2020-04-22 01:08:43 +03:00
|
|
|
|
2021-08-23 01:50:37 +03:00
|
|
|
for (offset = sec->sh.sh_size - 1; offset >= end && !insn; offset--)
|
2020-04-22 01:08:43 +03:00
|
|
|
insn = find_insn(file, sec, offset);
|
|
|
|
|
|
|
|
return insn;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
/*
|
objtool: Assume unannotated UD2 instructions are dead ends
Arnd reported some false positive warnings with GCC 7:
drivers/hid/wacom_wac.o: warning: objtool: wacom_bpt3_touch()+0x2a5: stack state mismatch: cfa1=7+8 cfa2=6+16
drivers/iio/adc/vf610_adc.o: warning: objtool: vf610_adc_calculate_rates() falls through to next function vf610_adc_sample_set()
drivers/pwm/pwm-hibvt.o: warning: objtool: hibvt_pwm_get_state() falls through to next function hibvt_pwm_remove()
drivers/pwm/pwm-mediatek.o: warning: objtool: mtk_pwm_config() falls through to next function mtk_pwm_enable()
drivers/spi/spi-bcm2835.o: warning: objtool: .text: unexpected end of section
drivers/spi/spi-bcm2835aux.o: warning: objtool: .text: unexpected end of section
drivers/watchdog/digicolor_wdt.o: warning: objtool: dc_wdt_get_timeleft() falls through to next function dc_wdt_restart()
When GCC 7 detects a potential divide-by-zero condition, it sometimes
inserts a UD2 instruction for the case where the divisor is zero,
instead of letting the hardware trap on the divide instruction.
Objtool doesn't consider UD2 to be fatal unless it's annotated with
unreachable(). So it considers the GCC-generated UD2 to be non-fatal,
and it tries to follow the control flow past the UD2 and gets
confused.
Previously, objtool *did* assume UD2 was always a dead end. That
changed with the following commit:
d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
The motivation behind that change was that Peter was planning on using
UD2 for __WARN(), which is *not* a dead end. However, it turns out
that some emulators rely on UD2 being fatal, so he ended up using
'ud0' instead:
9a93848fe787 ("x86/debug: Implement __WARN() using UD0")
For GCC 4.5+, it should be safe to go back to the previous assumption
that UD2 is fatal, even when it's not annotated with unreachable().
But for pre-4.5 versions of GCC, the unreachable() macro isn't
supported, so such cases of UD2 need to be explicitly annotated as
reachable.
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
Link: http://lkml.kernel.org/r/e57fa9dfede25f79487da8126ee9cdf7b856db65.1501188854.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-07-27 23:56:53 +03:00
|
|
|
* Mark "ud2" instructions and manually annotated dead ends.
|
2017-06-28 18:11:05 +03:00
|
|
|
*/
|
|
|
|
static int add_dead_ends(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct section *sec;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct reloc *reloc;
|
2017-06-28 18:11:05 +03:00
|
|
|
struct instruction *insn;
|
|
|
|
|
objtool: Assume unannotated UD2 instructions are dead ends
Arnd reported some false positive warnings with GCC 7:
drivers/hid/wacom_wac.o: warning: objtool: wacom_bpt3_touch()+0x2a5: stack state mismatch: cfa1=7+8 cfa2=6+16
drivers/iio/adc/vf610_adc.o: warning: objtool: vf610_adc_calculate_rates() falls through to next function vf610_adc_sample_set()
drivers/pwm/pwm-hibvt.o: warning: objtool: hibvt_pwm_get_state() falls through to next function hibvt_pwm_remove()
drivers/pwm/pwm-mediatek.o: warning: objtool: mtk_pwm_config() falls through to next function mtk_pwm_enable()
drivers/spi/spi-bcm2835.o: warning: objtool: .text: unexpected end of section
drivers/spi/spi-bcm2835aux.o: warning: objtool: .text: unexpected end of section
drivers/watchdog/digicolor_wdt.o: warning: objtool: dc_wdt_get_timeleft() falls through to next function dc_wdt_restart()
When GCC 7 detects a potential divide-by-zero condition, it sometimes
inserts a UD2 instruction for the case where the divisor is zero,
instead of letting the hardware trap on the divide instruction.
Objtool doesn't consider UD2 to be fatal unless it's annotated with
unreachable(). So it considers the GCC-generated UD2 to be non-fatal,
and it tries to follow the control flow past the UD2 and gets
confused.
Previously, objtool *did* assume UD2 was always a dead end. That
changed with the following commit:
d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
The motivation behind that change was that Peter was planning on using
UD2 for __WARN(), which is *not* a dead end. However, it turns out
that some emulators rely on UD2 being fatal, so he ended up using
'ud0' instead:
9a93848fe787 ("x86/debug: Implement __WARN() using UD0")
For GCC 4.5+, it should be safe to go back to the previous assumption
that UD2 is fatal, even when it's not annotated with unreachable().
But for pre-4.5 versions of GCC, the unreachable() macro isn't
supported, so such cases of UD2 need to be explicitly annotated as
reachable.
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
Link: http://lkml.kernel.org/r/e57fa9dfede25f79487da8126ee9cdf7b856db65.1501188854.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-07-27 23:56:53 +03:00
|
|
|
/*
|
|
|
|
* By default, "ud2" is a dead end unless otherwise annotated, because
|
|
|
|
* GCC 7 inserts it for certain divide-by-zero cases.
|
|
|
|
*/
|
|
|
|
for_each_insn(file, insn)
|
|
|
|
if (insn->type == INSN_BUG)
|
|
|
|
insn->dead_end = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for manually annotated dead ends.
|
|
|
|
*/
|
2017-06-28 18:11:05 +03:00
|
|
|
sec = find_section_by_name(file->elf, ".rela.discard.unreachable");
|
|
|
|
if (!sec)
|
objtool: Assume unannotated UD2 instructions are dead ends
Arnd reported some false positive warnings with GCC 7:
drivers/hid/wacom_wac.o: warning: objtool: wacom_bpt3_touch()+0x2a5: stack state mismatch: cfa1=7+8 cfa2=6+16
drivers/iio/adc/vf610_adc.o: warning: objtool: vf610_adc_calculate_rates() falls through to next function vf610_adc_sample_set()
drivers/pwm/pwm-hibvt.o: warning: objtool: hibvt_pwm_get_state() falls through to next function hibvt_pwm_remove()
drivers/pwm/pwm-mediatek.o: warning: objtool: mtk_pwm_config() falls through to next function mtk_pwm_enable()
drivers/spi/spi-bcm2835.o: warning: objtool: .text: unexpected end of section
drivers/spi/spi-bcm2835aux.o: warning: objtool: .text: unexpected end of section
drivers/watchdog/digicolor_wdt.o: warning: objtool: dc_wdt_get_timeleft() falls through to next function dc_wdt_restart()
When GCC 7 detects a potential divide-by-zero condition, it sometimes
inserts a UD2 instruction for the case where the divisor is zero,
instead of letting the hardware trap on the divide instruction.
Objtool doesn't consider UD2 to be fatal unless it's annotated with
unreachable(). So it considers the GCC-generated UD2 to be non-fatal,
and it tries to follow the control flow past the UD2 and gets
confused.
Previously, objtool *did* assume UD2 was always a dead end. That
changed with the following commit:
d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
The motivation behind that change was that Peter was planning on using
UD2 for __WARN(), which is *not* a dead end. However, it turns out
that some emulators rely on UD2 being fatal, so he ended up using
'ud0' instead:
9a93848fe787 ("x86/debug: Implement __WARN() using UD0")
For GCC 4.5+, it should be safe to go back to the previous assumption
that UD2 is fatal, even when it's not annotated with unreachable().
But for pre-4.5 versions of GCC, the unreachable() macro isn't
supported, so such cases of UD2 need to be explicitly annotated as
reachable.
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
Link: http://lkml.kernel.org/r/e57fa9dfede25f79487da8126ee9cdf7b856db65.1501188854.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-07-27 23:56:53 +03:00
|
|
|
goto reachable;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
list_for_each_entry(reloc, &sec->reloc_list, list) {
|
|
|
|
if (reloc->sym->type != STT_SECTION) {
|
2017-06-28 18:11:05 +03:00
|
|
|
WARN("unexpected relocation symbol type in %s", sec->name);
|
|
|
|
return -1;
|
|
|
|
}
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
insn = find_insn(file, reloc->sym->sec, reloc->addend);
|
2017-06-28 18:11:05 +03:00
|
|
|
if (insn)
|
|
|
|
insn = list_prev_entry(insn, list);
|
2021-08-23 01:50:37 +03:00
|
|
|
else if (reloc->addend == reloc->sym->sec->sh.sh_size) {
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
insn = find_last_insn(file, reloc->sym->sec);
|
2020-04-22 01:08:43 +03:00
|
|
|
if (!insn) {
|
2017-06-28 18:11:05 +03:00
|
|
|
WARN("can't find unreachable insn at %s+0x%x",
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
reloc->sym->sec->name, reloc->addend);
|
2017-06-28 18:11:05 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
WARN("can't find unreachable insn at %s+0x%x",
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
reloc->sym->sec->name, reloc->addend);
|
2017-06-28 18:11:05 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
insn->dead_end = true;
|
|
|
|
}
|
|
|
|
|
objtool: Assume unannotated UD2 instructions are dead ends
Arnd reported some false positive warnings with GCC 7:
drivers/hid/wacom_wac.o: warning: objtool: wacom_bpt3_touch()+0x2a5: stack state mismatch: cfa1=7+8 cfa2=6+16
drivers/iio/adc/vf610_adc.o: warning: objtool: vf610_adc_calculate_rates() falls through to next function vf610_adc_sample_set()
drivers/pwm/pwm-hibvt.o: warning: objtool: hibvt_pwm_get_state() falls through to next function hibvt_pwm_remove()
drivers/pwm/pwm-mediatek.o: warning: objtool: mtk_pwm_config() falls through to next function mtk_pwm_enable()
drivers/spi/spi-bcm2835.o: warning: objtool: .text: unexpected end of section
drivers/spi/spi-bcm2835aux.o: warning: objtool: .text: unexpected end of section
drivers/watchdog/digicolor_wdt.o: warning: objtool: dc_wdt_get_timeleft() falls through to next function dc_wdt_restart()
When GCC 7 detects a potential divide-by-zero condition, it sometimes
inserts a UD2 instruction for the case where the divisor is zero,
instead of letting the hardware trap on the divide instruction.
Objtool doesn't consider UD2 to be fatal unless it's annotated with
unreachable(). So it considers the GCC-generated UD2 to be non-fatal,
and it tries to follow the control flow past the UD2 and gets
confused.
Previously, objtool *did* assume UD2 was always a dead end. That
changed with the following commit:
d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
The motivation behind that change was that Peter was planning on using
UD2 for __WARN(), which is *not* a dead end. However, it turns out
that some emulators rely on UD2 being fatal, so he ended up using
'ud0' instead:
9a93848fe787 ("x86/debug: Implement __WARN() using UD0")
For GCC 4.5+, it should be safe to go back to the previous assumption
that UD2 is fatal, even when it's not annotated with unreachable().
But for pre-4.5 versions of GCC, the unreachable() macro isn't
supported, so such cases of UD2 need to be explicitly annotated as
reachable.
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
Link: http://lkml.kernel.org/r/e57fa9dfede25f79487da8126ee9cdf7b856db65.1501188854.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-07-27 23:56:53 +03:00
|
|
|
reachable:
|
|
|
|
/*
|
|
|
|
* These manually annotated reachable checks are needed for GCC 4.4,
|
|
|
|
* where the Linux unreachable() macro isn't supported. In that case
|
|
|
|
* GCC doesn't know the "ud2" is fatal, so it generates code as if it's
|
|
|
|
* not a dead end.
|
|
|
|
*/
|
|
|
|
sec = find_section_by_name(file->elf, ".rela.discard.reachable");
|
|
|
|
if (!sec)
|
|
|
|
return 0;
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
list_for_each_entry(reloc, &sec->reloc_list, list) {
|
|
|
|
if (reloc->sym->type != STT_SECTION) {
|
objtool: Assume unannotated UD2 instructions are dead ends
Arnd reported some false positive warnings with GCC 7:
drivers/hid/wacom_wac.o: warning: objtool: wacom_bpt3_touch()+0x2a5: stack state mismatch: cfa1=7+8 cfa2=6+16
drivers/iio/adc/vf610_adc.o: warning: objtool: vf610_adc_calculate_rates() falls through to next function vf610_adc_sample_set()
drivers/pwm/pwm-hibvt.o: warning: objtool: hibvt_pwm_get_state() falls through to next function hibvt_pwm_remove()
drivers/pwm/pwm-mediatek.o: warning: objtool: mtk_pwm_config() falls through to next function mtk_pwm_enable()
drivers/spi/spi-bcm2835.o: warning: objtool: .text: unexpected end of section
drivers/spi/spi-bcm2835aux.o: warning: objtool: .text: unexpected end of section
drivers/watchdog/digicolor_wdt.o: warning: objtool: dc_wdt_get_timeleft() falls through to next function dc_wdt_restart()
When GCC 7 detects a potential divide-by-zero condition, it sometimes
inserts a UD2 instruction for the case where the divisor is zero,
instead of letting the hardware trap on the divide instruction.
Objtool doesn't consider UD2 to be fatal unless it's annotated with
unreachable(). So it considers the GCC-generated UD2 to be non-fatal,
and it tries to follow the control flow past the UD2 and gets
confused.
Previously, objtool *did* assume UD2 was always a dead end. That
changed with the following commit:
d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
The motivation behind that change was that Peter was planning on using
UD2 for __WARN(), which is *not* a dead end. However, it turns out
that some emulators rely on UD2 being fatal, so he ended up using
'ud0' instead:
9a93848fe787 ("x86/debug: Implement __WARN() using UD0")
For GCC 4.5+, it should be safe to go back to the previous assumption
that UD2 is fatal, even when it's not annotated with unreachable().
But for pre-4.5 versions of GCC, the unreachable() macro isn't
supported, so such cases of UD2 need to be explicitly annotated as
reachable.
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
Link: http://lkml.kernel.org/r/e57fa9dfede25f79487da8126ee9cdf7b856db65.1501188854.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-07-27 23:56:53 +03:00
|
|
|
WARN("unexpected relocation symbol type in %s", sec->name);
|
|
|
|
return -1;
|
|
|
|
}
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
insn = find_insn(file, reloc->sym->sec, reloc->addend);
|
objtool: Assume unannotated UD2 instructions are dead ends
Arnd reported some false positive warnings with GCC 7:
drivers/hid/wacom_wac.o: warning: objtool: wacom_bpt3_touch()+0x2a5: stack state mismatch: cfa1=7+8 cfa2=6+16
drivers/iio/adc/vf610_adc.o: warning: objtool: vf610_adc_calculate_rates() falls through to next function vf610_adc_sample_set()
drivers/pwm/pwm-hibvt.o: warning: objtool: hibvt_pwm_get_state() falls through to next function hibvt_pwm_remove()
drivers/pwm/pwm-mediatek.o: warning: objtool: mtk_pwm_config() falls through to next function mtk_pwm_enable()
drivers/spi/spi-bcm2835.o: warning: objtool: .text: unexpected end of section
drivers/spi/spi-bcm2835aux.o: warning: objtool: .text: unexpected end of section
drivers/watchdog/digicolor_wdt.o: warning: objtool: dc_wdt_get_timeleft() falls through to next function dc_wdt_restart()
When GCC 7 detects a potential divide-by-zero condition, it sometimes
inserts a UD2 instruction for the case where the divisor is zero,
instead of letting the hardware trap on the divide instruction.
Objtool doesn't consider UD2 to be fatal unless it's annotated with
unreachable(). So it considers the GCC-generated UD2 to be non-fatal,
and it tries to follow the control flow past the UD2 and gets
confused.
Previously, objtool *did* assume UD2 was always a dead end. That
changed with the following commit:
d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
The motivation behind that change was that Peter was planning on using
UD2 for __WARN(), which is *not* a dead end. However, it turns out
that some emulators rely on UD2 being fatal, so he ended up using
'ud0' instead:
9a93848fe787 ("x86/debug: Implement __WARN() using UD0")
For GCC 4.5+, it should be safe to go back to the previous assumption
that UD2 is fatal, even when it's not annotated with unreachable().
But for pre-4.5 versions of GCC, the unreachable() macro isn't
supported, so such cases of UD2 need to be explicitly annotated as
reachable.
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
Link: http://lkml.kernel.org/r/e57fa9dfede25f79487da8126ee9cdf7b856db65.1501188854.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-07-27 23:56:53 +03:00
|
|
|
if (insn)
|
|
|
|
insn = list_prev_entry(insn, list);
|
2021-08-23 01:50:37 +03:00
|
|
|
else if (reloc->addend == reloc->sym->sec->sh.sh_size) {
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
insn = find_last_insn(file, reloc->sym->sec);
|
2020-04-22 01:08:43 +03:00
|
|
|
if (!insn) {
|
objtool: Assume unannotated UD2 instructions are dead ends
Arnd reported some false positive warnings with GCC 7:
drivers/hid/wacom_wac.o: warning: objtool: wacom_bpt3_touch()+0x2a5: stack state mismatch: cfa1=7+8 cfa2=6+16
drivers/iio/adc/vf610_adc.o: warning: objtool: vf610_adc_calculate_rates() falls through to next function vf610_adc_sample_set()
drivers/pwm/pwm-hibvt.o: warning: objtool: hibvt_pwm_get_state() falls through to next function hibvt_pwm_remove()
drivers/pwm/pwm-mediatek.o: warning: objtool: mtk_pwm_config() falls through to next function mtk_pwm_enable()
drivers/spi/spi-bcm2835.o: warning: objtool: .text: unexpected end of section
drivers/spi/spi-bcm2835aux.o: warning: objtool: .text: unexpected end of section
drivers/watchdog/digicolor_wdt.o: warning: objtool: dc_wdt_get_timeleft() falls through to next function dc_wdt_restart()
When GCC 7 detects a potential divide-by-zero condition, it sometimes
inserts a UD2 instruction for the case where the divisor is zero,
instead of letting the hardware trap on the divide instruction.
Objtool doesn't consider UD2 to be fatal unless it's annotated with
unreachable(). So it considers the GCC-generated UD2 to be non-fatal,
and it tries to follow the control flow past the UD2 and gets
confused.
Previously, objtool *did* assume UD2 was always a dead end. That
changed with the following commit:
d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
The motivation behind that change was that Peter was planning on using
UD2 for __WARN(), which is *not* a dead end. However, it turns out
that some emulators rely on UD2 being fatal, so he ended up using
'ud0' instead:
9a93848fe787 ("x86/debug: Implement __WARN() using UD0")
For GCC 4.5+, it should be safe to go back to the previous assumption
that UD2 is fatal, even when it's not annotated with unreachable().
But for pre-4.5 versions of GCC, the unreachable() macro isn't
supported, so such cases of UD2 need to be explicitly annotated as
reachable.
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
Link: http://lkml.kernel.org/r/e57fa9dfede25f79487da8126ee9cdf7b856db65.1501188854.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-07-27 23:56:53 +03:00
|
|
|
WARN("can't find reachable insn at %s+0x%x",
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
reloc->sym->sec->name, reloc->addend);
|
objtool: Assume unannotated UD2 instructions are dead ends
Arnd reported some false positive warnings with GCC 7:
drivers/hid/wacom_wac.o: warning: objtool: wacom_bpt3_touch()+0x2a5: stack state mismatch: cfa1=7+8 cfa2=6+16
drivers/iio/adc/vf610_adc.o: warning: objtool: vf610_adc_calculate_rates() falls through to next function vf610_adc_sample_set()
drivers/pwm/pwm-hibvt.o: warning: objtool: hibvt_pwm_get_state() falls through to next function hibvt_pwm_remove()
drivers/pwm/pwm-mediatek.o: warning: objtool: mtk_pwm_config() falls through to next function mtk_pwm_enable()
drivers/spi/spi-bcm2835.o: warning: objtool: .text: unexpected end of section
drivers/spi/spi-bcm2835aux.o: warning: objtool: .text: unexpected end of section
drivers/watchdog/digicolor_wdt.o: warning: objtool: dc_wdt_get_timeleft() falls through to next function dc_wdt_restart()
When GCC 7 detects a potential divide-by-zero condition, it sometimes
inserts a UD2 instruction for the case where the divisor is zero,
instead of letting the hardware trap on the divide instruction.
Objtool doesn't consider UD2 to be fatal unless it's annotated with
unreachable(). So it considers the GCC-generated UD2 to be non-fatal,
and it tries to follow the control flow past the UD2 and gets
confused.
Previously, objtool *did* assume UD2 was always a dead end. That
changed with the following commit:
d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
The motivation behind that change was that Peter was planning on using
UD2 for __WARN(), which is *not* a dead end. However, it turns out
that some emulators rely on UD2 being fatal, so he ended up using
'ud0' instead:
9a93848fe787 ("x86/debug: Implement __WARN() using UD0")
For GCC 4.5+, it should be safe to go back to the previous assumption
that UD2 is fatal, even when it's not annotated with unreachable().
But for pre-4.5 versions of GCC, the unreachable() macro isn't
supported, so such cases of UD2 need to be explicitly annotated as
reachable.
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
Link: http://lkml.kernel.org/r/e57fa9dfede25f79487da8126ee9cdf7b856db65.1501188854.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-07-27 23:56:53 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
WARN("can't find reachable insn at %s+0x%x",
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
reloc->sym->sec->name, reloc->addend);
|
objtool: Assume unannotated UD2 instructions are dead ends
Arnd reported some false positive warnings with GCC 7:
drivers/hid/wacom_wac.o: warning: objtool: wacom_bpt3_touch()+0x2a5: stack state mismatch: cfa1=7+8 cfa2=6+16
drivers/iio/adc/vf610_adc.o: warning: objtool: vf610_adc_calculate_rates() falls through to next function vf610_adc_sample_set()
drivers/pwm/pwm-hibvt.o: warning: objtool: hibvt_pwm_get_state() falls through to next function hibvt_pwm_remove()
drivers/pwm/pwm-mediatek.o: warning: objtool: mtk_pwm_config() falls through to next function mtk_pwm_enable()
drivers/spi/spi-bcm2835.o: warning: objtool: .text: unexpected end of section
drivers/spi/spi-bcm2835aux.o: warning: objtool: .text: unexpected end of section
drivers/watchdog/digicolor_wdt.o: warning: objtool: dc_wdt_get_timeleft() falls through to next function dc_wdt_restart()
When GCC 7 detects a potential divide-by-zero condition, it sometimes
inserts a UD2 instruction for the case where the divisor is zero,
instead of letting the hardware trap on the divide instruction.
Objtool doesn't consider UD2 to be fatal unless it's annotated with
unreachable(). So it considers the GCC-generated UD2 to be non-fatal,
and it tries to follow the control flow past the UD2 and gets
confused.
Previously, objtool *did* assume UD2 was always a dead end. That
changed with the following commit:
d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
The motivation behind that change was that Peter was planning on using
UD2 for __WARN(), which is *not* a dead end. However, it turns out
that some emulators rely on UD2 being fatal, so he ended up using
'ud0' instead:
9a93848fe787 ("x86/debug: Implement __WARN() using UD0")
For GCC 4.5+, it should be safe to go back to the previous assumption
that UD2 is fatal, even when it's not annotated with unreachable().
But for pre-4.5 versions of GCC, the unreachable() macro isn't
supported, so such cases of UD2 need to be explicitly annotated as
reachable.
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: d1091c7fa3d5 ("objtool: Improve detection of BUG() and other dead ends")
Link: http://lkml.kernel.org/r/e57fa9dfede25f79487da8126ee9cdf7b856db65.1501188854.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-07-27 23:56:53 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
insn->dead_end = false;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-18 16:57:45 +03:00
|
|
|
static int create_static_call_sections(struct objtool_file *file)
|
|
|
|
{
|
2021-03-26 18:12:07 +03:00
|
|
|
struct section *sec;
|
2020-08-18 16:57:45 +03:00
|
|
|
struct static_call_site *site;
|
|
|
|
struct instruction *insn;
|
|
|
|
struct symbol *key_sym;
|
|
|
|
char *key_name, *tmp;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
sec = find_section_by_name(file->elf, ".static_call_sites");
|
|
|
|
if (sec) {
|
|
|
|
INIT_LIST_HEAD(&file->static_call_list);
|
|
|
|
WARN("file already has .static_call_sites section, skipping");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (list_empty(&file->static_call_list))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
idx = 0;
|
2021-03-26 18:12:12 +03:00
|
|
|
list_for_each_entry(insn, &file->static_call_list, call_node)
|
2020-08-18 16:57:45 +03:00
|
|
|
idx++;
|
|
|
|
|
|
|
|
sec = elf_create_section(file->elf, ".static_call_sites", SHF_WRITE,
|
|
|
|
sizeof(struct static_call_site), idx);
|
|
|
|
if (!sec)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
idx = 0;
|
2021-03-26 18:12:12 +03:00
|
|
|
list_for_each_entry(insn, &file->static_call_list, call_node) {
|
2020-08-18 16:57:45 +03:00
|
|
|
|
|
|
|
site = (struct static_call_site *)sec->data->d_buf + idx;
|
|
|
|
memset(site, 0, sizeof(struct static_call_site));
|
|
|
|
|
|
|
|
/* populate reloc for 'addr' */
|
2021-03-26 18:12:07 +03:00
|
|
|
if (elf_add_reloc_to_insn(file->elf, sec,
|
|
|
|
idx * sizeof(struct static_call_site),
|
|
|
|
R_X86_64_PC32,
|
|
|
|
insn->sec, insn->offset))
|
2020-12-15 01:04:20 +03:00
|
|
|
return -1;
|
2020-08-18 16:57:45 +03:00
|
|
|
|
|
|
|
/* find key symbol */
|
|
|
|
key_name = strdup(insn->call_dest->name);
|
|
|
|
if (!key_name) {
|
|
|
|
perror("strdup");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR,
|
|
|
|
STATIC_CALL_TRAMP_PREFIX_LEN)) {
|
|
|
|
WARN("static_call: trampoline name malformed: %s", key_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN;
|
|
|
|
memcpy(tmp, STATIC_CALL_KEY_PREFIX_STR, STATIC_CALL_KEY_PREFIX_LEN);
|
|
|
|
|
|
|
|
key_sym = find_symbol_by_name(file->elf, tmp);
|
|
|
|
if (!key_sym) {
|
2021-01-28 02:18:37 +03:00
|
|
|
if (!module) {
|
|
|
|
WARN("static_call: can't find static_call_key symbol: %s", tmp);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For modules(), the key might not be exported, which
|
|
|
|
* means the module can make static calls but isn't
|
|
|
|
* allowed to change them.
|
|
|
|
*
|
|
|
|
* In that case we temporarily set the key to be the
|
|
|
|
* trampoline address. This is fixed up in
|
|
|
|
* static_call_add_module().
|
|
|
|
*/
|
|
|
|
key_sym = insn->call_dest;
|
2020-08-18 16:57:45 +03:00
|
|
|
}
|
|
|
|
free(key_name);
|
|
|
|
|
|
|
|
/* populate reloc for 'key' */
|
2021-03-26 18:12:07 +03:00
|
|
|
if (elf_add_reloc(file->elf, sec,
|
|
|
|
idx * sizeof(struct static_call_site) + 4,
|
|
|
|
R_X86_64_PC32, key_sym,
|
|
|
|
is_sibling_call(insn) * STATIC_CALL_SITE_TAIL))
|
2020-08-18 16:57:45 +03:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-10-26 15:01:36 +03:00
|
|
|
static int create_retpoline_sites_sections(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct instruction *insn;
|
|
|
|
struct section *sec;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
sec = find_section_by_name(file->elf, ".retpoline_sites");
|
|
|
|
if (sec) {
|
|
|
|
WARN("file already has .retpoline_sites, skipping");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = 0;
|
|
|
|
list_for_each_entry(insn, &file->retpoline_call_list, call_node)
|
|
|
|
idx++;
|
|
|
|
|
|
|
|
if (!idx)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
sec = elf_create_section(file->elf, ".retpoline_sites", 0,
|
|
|
|
sizeof(int), idx);
|
|
|
|
if (!sec) {
|
|
|
|
WARN("elf_create_section: .retpoline_sites");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = 0;
|
|
|
|
list_for_each_entry(insn, &file->retpoline_call_list, call_node) {
|
|
|
|
|
|
|
|
int *site = (int *)sec->data->d_buf + idx;
|
|
|
|
*site = 0;
|
|
|
|
|
|
|
|
if (elf_add_reloc_to_insn(file->elf, sec,
|
|
|
|
idx * sizeof(int),
|
|
|
|
R_X86_64_PC32,
|
|
|
|
insn->sec, insn->offset)) {
|
|
|
|
WARN("elf_add_reloc_to_insn: .retpoline_sites");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-07 01:14:09 +03:00
|
|
|
static int create_mcount_loc_sections(struct objtool_file *file)
|
|
|
|
{
|
2021-03-26 18:12:07 +03:00
|
|
|
struct section *sec;
|
2020-08-07 01:14:09 +03:00
|
|
|
unsigned long *loc;
|
|
|
|
struct instruction *insn;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
sec = find_section_by_name(file->elf, "__mcount_loc");
|
|
|
|
if (sec) {
|
|
|
|
INIT_LIST_HEAD(&file->mcount_loc_list);
|
|
|
|
WARN("file already has __mcount_loc section, skipping");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (list_empty(&file->mcount_loc_list))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
idx = 0;
|
2021-10-26 15:01:35 +03:00
|
|
|
list_for_each_entry(insn, &file->mcount_loc_list, call_node)
|
2020-08-07 01:14:09 +03:00
|
|
|
idx++;
|
|
|
|
|
|
|
|
sec = elf_create_section(file->elf, "__mcount_loc", 0, sizeof(unsigned long), idx);
|
|
|
|
if (!sec)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
idx = 0;
|
2021-10-26 15:01:35 +03:00
|
|
|
list_for_each_entry(insn, &file->mcount_loc_list, call_node) {
|
2020-08-07 01:14:09 +03:00
|
|
|
|
|
|
|
loc = (unsigned long *)sec->data->d_buf + idx;
|
|
|
|
memset(loc, 0, sizeof(unsigned long));
|
|
|
|
|
2021-03-26 18:12:07 +03:00
|
|
|
if (elf_add_reloc_to_insn(file->elf, sec,
|
|
|
|
idx * sizeof(unsigned long),
|
|
|
|
R_X86_64_64,
|
|
|
|
insn->sec, insn->offset))
|
2020-08-07 01:14:09 +03:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
/*
|
|
|
|
* Warnings shouldn't be reported for ignored functions.
|
|
|
|
*/
|
|
|
|
static void add_ignores(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct instruction *insn;
|
|
|
|
struct section *sec;
|
|
|
|
struct symbol *func;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct reloc *reloc;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2019-02-27 16:04:13 +03:00
|
|
|
sec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard");
|
|
|
|
if (!sec)
|
|
|
|
return;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
list_for_each_entry(reloc, &sec->reloc_list, list) {
|
|
|
|
switch (reloc->sym->type) {
|
2019-02-27 16:04:13 +03:00
|
|
|
case STT_FUNC:
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
func = reloc->sym;
|
2019-02-27 16:04:13 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STT_SECTION:
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
func = find_func_by_offset(reloc->sym->sec, reloc->addend);
|
2020-02-18 06:41:54 +03:00
|
|
|
if (!func)
|
2017-06-28 18:11:05 +03:00
|
|
|
continue;
|
2019-02-27 16:04:13 +03:00
|
|
|
break;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2019-02-27 16:04:13 +03:00
|
|
|
default:
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
WARN("unexpected relocation symbol type in %s: %d", sec->name, reloc->sym->type);
|
2019-02-27 16:04:13 +03:00
|
|
|
continue;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
2019-02-27 16:04:13 +03:00
|
|
|
|
2020-03-10 20:27:24 +03:00
|
|
|
func_for_each_insn(file, func, insn)
|
2019-02-27 16:04:13 +03:00
|
|
|
insn->ignore = true;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-25 14:50:09 +03:00
|
|
|
/*
|
|
|
|
* This is a whitelist of functions that is allowed to be called with AC set.
|
|
|
|
* The list is meant to be minimal and only contains compiler instrumentation
|
|
|
|
* ABI and a few functions used to implement *_{to,from}_user() functions.
|
|
|
|
*
|
|
|
|
* These functions must not directly change AC, but may PUSHF/POPF.
|
|
|
|
*/
|
|
|
|
static const char *uaccess_safe_builtin[] = {
|
|
|
|
/* KASAN */
|
|
|
|
"kasan_report",
|
2021-02-24 23:05:05 +03:00
|
|
|
"kasan_check_range",
|
2019-02-25 14:50:09 +03:00
|
|
|
/* KASAN out-of-line */
|
|
|
|
"__asan_loadN_noabort",
|
|
|
|
"__asan_load1_noabort",
|
|
|
|
"__asan_load2_noabort",
|
|
|
|
"__asan_load4_noabort",
|
|
|
|
"__asan_load8_noabort",
|
|
|
|
"__asan_load16_noabort",
|
|
|
|
"__asan_storeN_noabort",
|
|
|
|
"__asan_store1_noabort",
|
|
|
|
"__asan_store2_noabort",
|
|
|
|
"__asan_store4_noabort",
|
|
|
|
"__asan_store8_noabort",
|
|
|
|
"__asan_store16_noabort",
|
objtool: Permit __kasan_check_{read,write} under UACCESS
Building linux-next with JUMP_LABEL=n and KASAN=y, I got this objtool
warning:
arch/x86/lib/copy_mc.o: warning: objtool: copy_mc_to_user()+0x22: call to
__kasan_check_read() with UACCESS enabled
What happens here is that copy_mc_to_user() branches on a static key in a
UACCESS region:
__uaccess_begin();
if (static_branch_unlikely(©_mc_fragile_key))
ret = copy_mc_fragile(to, from, len);
ret = copy_mc_generic(to, from, len);
__uaccess_end();
and the !CONFIG_JUMP_LABEL version of static_branch_unlikely() uses
static_key_enabled(), which uses static_key_count(), which uses
atomic_read(), which calls instrument_atomic_read(), which uses
kasan_check_read(), which is __kasan_check_read().
Let's permit these KASAN helpers in UACCESS regions - static keys should
probably work under UACCESS, I think.
PeterZ adds:
It's not a matter of permitting, it's a matter of being safe and
correct. In this case it is, because it's a thin wrapper around
check_memory_region() which was already marked safe.
check_memory_region() is correct because the only thing it ends up
calling is kasa_report() and that is also marked safe because that is
annotated with user_access_save/restore() before it does anything else.
On top of that, all of KASAN is noinstr, so nothing in here will end up
in tracing and/or call schedule() before the user_access_save().
Signed-off-by: Jann Horn <jannh@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-09-29 01:49:16 +03:00
|
|
|
"__kasan_check_read",
|
|
|
|
"__kasan_check_write",
|
2019-02-25 14:50:09 +03:00
|
|
|
/* KASAN in-line */
|
|
|
|
"__asan_report_load_n_noabort",
|
|
|
|
"__asan_report_load1_noabort",
|
|
|
|
"__asan_report_load2_noabort",
|
|
|
|
"__asan_report_load4_noabort",
|
|
|
|
"__asan_report_load8_noabort",
|
|
|
|
"__asan_report_load16_noabort",
|
|
|
|
"__asan_report_store_n_noabort",
|
|
|
|
"__asan_report_store1_noabort",
|
|
|
|
"__asan_report_store2_noabort",
|
|
|
|
"__asan_report_store4_noabort",
|
|
|
|
"__asan_report_store8_noabort",
|
|
|
|
"__asan_report_store16_noabort",
|
2019-11-14 21:02:57 +03:00
|
|
|
/* KCSAN */
|
2020-03-25 19:41:57 +03:00
|
|
|
"__kcsan_check_access",
|
2019-11-14 21:02:57 +03:00
|
|
|
"kcsan_found_watchpoint",
|
|
|
|
"kcsan_setup_watchpoint",
|
2020-03-25 19:41:57 +03:00
|
|
|
"kcsan_check_scoped_accesses",
|
2020-04-24 18:47:30 +03:00
|
|
|
"kcsan_disable_current",
|
|
|
|
"kcsan_enable_current_nowarn",
|
2019-11-14 21:02:57 +03:00
|
|
|
/* KCSAN/TSAN */
|
|
|
|
"__tsan_func_entry",
|
|
|
|
"__tsan_func_exit",
|
|
|
|
"__tsan_read_range",
|
|
|
|
"__tsan_write_range",
|
|
|
|
"__tsan_read1",
|
|
|
|
"__tsan_read2",
|
|
|
|
"__tsan_read4",
|
|
|
|
"__tsan_read8",
|
|
|
|
"__tsan_read16",
|
|
|
|
"__tsan_write1",
|
|
|
|
"__tsan_write2",
|
|
|
|
"__tsan_write4",
|
|
|
|
"__tsan_write8",
|
|
|
|
"__tsan_write16",
|
2020-07-24 10:00:02 +03:00
|
|
|
"__tsan_read_write1",
|
|
|
|
"__tsan_read_write2",
|
|
|
|
"__tsan_read_write4",
|
|
|
|
"__tsan_read_write8",
|
|
|
|
"__tsan_read_write16",
|
2020-07-03 16:40:30 +03:00
|
|
|
"__tsan_atomic8_load",
|
|
|
|
"__tsan_atomic16_load",
|
|
|
|
"__tsan_atomic32_load",
|
|
|
|
"__tsan_atomic64_load",
|
|
|
|
"__tsan_atomic8_store",
|
|
|
|
"__tsan_atomic16_store",
|
|
|
|
"__tsan_atomic32_store",
|
|
|
|
"__tsan_atomic64_store",
|
|
|
|
"__tsan_atomic8_exchange",
|
|
|
|
"__tsan_atomic16_exchange",
|
|
|
|
"__tsan_atomic32_exchange",
|
|
|
|
"__tsan_atomic64_exchange",
|
|
|
|
"__tsan_atomic8_fetch_add",
|
|
|
|
"__tsan_atomic16_fetch_add",
|
|
|
|
"__tsan_atomic32_fetch_add",
|
|
|
|
"__tsan_atomic64_fetch_add",
|
|
|
|
"__tsan_atomic8_fetch_sub",
|
|
|
|
"__tsan_atomic16_fetch_sub",
|
|
|
|
"__tsan_atomic32_fetch_sub",
|
|
|
|
"__tsan_atomic64_fetch_sub",
|
|
|
|
"__tsan_atomic8_fetch_and",
|
|
|
|
"__tsan_atomic16_fetch_and",
|
|
|
|
"__tsan_atomic32_fetch_and",
|
|
|
|
"__tsan_atomic64_fetch_and",
|
|
|
|
"__tsan_atomic8_fetch_or",
|
|
|
|
"__tsan_atomic16_fetch_or",
|
|
|
|
"__tsan_atomic32_fetch_or",
|
|
|
|
"__tsan_atomic64_fetch_or",
|
|
|
|
"__tsan_atomic8_fetch_xor",
|
|
|
|
"__tsan_atomic16_fetch_xor",
|
|
|
|
"__tsan_atomic32_fetch_xor",
|
|
|
|
"__tsan_atomic64_fetch_xor",
|
|
|
|
"__tsan_atomic8_fetch_nand",
|
|
|
|
"__tsan_atomic16_fetch_nand",
|
|
|
|
"__tsan_atomic32_fetch_nand",
|
|
|
|
"__tsan_atomic64_fetch_nand",
|
|
|
|
"__tsan_atomic8_compare_exchange_strong",
|
|
|
|
"__tsan_atomic16_compare_exchange_strong",
|
|
|
|
"__tsan_atomic32_compare_exchange_strong",
|
|
|
|
"__tsan_atomic64_compare_exchange_strong",
|
|
|
|
"__tsan_atomic8_compare_exchange_weak",
|
|
|
|
"__tsan_atomic16_compare_exchange_weak",
|
|
|
|
"__tsan_atomic32_compare_exchange_weak",
|
|
|
|
"__tsan_atomic64_compare_exchange_weak",
|
|
|
|
"__tsan_atomic8_compare_exchange_val",
|
|
|
|
"__tsan_atomic16_compare_exchange_val",
|
|
|
|
"__tsan_atomic32_compare_exchange_val",
|
|
|
|
"__tsan_atomic64_compare_exchange_val",
|
|
|
|
"__tsan_atomic_thread_fence",
|
|
|
|
"__tsan_atomic_signal_fence",
|
2019-02-25 14:50:09 +03:00
|
|
|
/* KCOV */
|
|
|
|
"write_comp_data",
|
2020-04-29 22:09:04 +03:00
|
|
|
"check_kcov_mode",
|
2019-02-25 14:50:09 +03:00
|
|
|
"__sanitizer_cov_trace_pc",
|
|
|
|
"__sanitizer_cov_trace_const_cmp1",
|
|
|
|
"__sanitizer_cov_trace_const_cmp2",
|
|
|
|
"__sanitizer_cov_trace_const_cmp4",
|
|
|
|
"__sanitizer_cov_trace_const_cmp8",
|
|
|
|
"__sanitizer_cov_trace_cmp1",
|
|
|
|
"__sanitizer_cov_trace_cmp2",
|
|
|
|
"__sanitizer_cov_trace_cmp4",
|
|
|
|
"__sanitizer_cov_trace_cmp8",
|
2020-02-16 21:07:49 +03:00
|
|
|
"__sanitizer_cov_trace_switch",
|
2019-02-25 14:50:09 +03:00
|
|
|
/* UBSAN */
|
|
|
|
"ubsan_type_mismatch_common",
|
|
|
|
"__ubsan_handle_type_mismatch",
|
|
|
|
"__ubsan_handle_type_mismatch_v1",
|
2019-10-21 16:11:49 +03:00
|
|
|
"__ubsan_handle_shift_out_of_bounds",
|
2019-02-25 14:50:09 +03:00
|
|
|
/* misc */
|
|
|
|
"csum_partial_copy_generic",
|
x86, powerpc: Rename memcpy_mcsafe() to copy_mc_to_{user, kernel}()
In reaction to a proposal to introduce a memcpy_mcsafe_fast()
implementation Linus points out that memcpy_mcsafe() is poorly named
relative to communicating the scope of the interface. Specifically what
addresses are valid to pass as source, destination, and what faults /
exceptions are handled.
Of particular concern is that even though x86 might be able to handle
the semantics of copy_mc_to_user() with its common copy_user_generic()
implementation other archs likely need / want an explicit path for this
case:
On Fri, May 1, 2020 at 11:28 AM Linus Torvalds <torvalds@linux-foundation.org> wrote:
>
> On Thu, Apr 30, 2020 at 6:21 PM Dan Williams <dan.j.williams@intel.com> wrote:
> >
> > However now I see that copy_user_generic() works for the wrong reason.
> > It works because the exception on the source address due to poison
> > looks no different than a write fault on the user address to the
> > caller, it's still just a short copy. So it makes copy_to_user() work
> > for the wrong reason relative to the name.
>
> Right.
>
> And it won't work that way on other architectures. On x86, we have a
> generic function that can take faults on either side, and we use it
> for both cases (and for the "in_user" case too), but that's an
> artifact of the architecture oddity.
>
> In fact, it's probably wrong even on x86 - because it can hide bugs -
> but writing those things is painful enough that everybody prefers
> having just one function.
Replace a single top-level memcpy_mcsafe() with either
copy_mc_to_user(), or copy_mc_to_kernel().
Introduce an x86 copy_mc_fragile() name as the rename for the
low-level x86 implementation formerly named memcpy_mcsafe(). It is used
as the slow / careful backend that is supplanted by a fast
copy_mc_generic() in a follow-on patch.
One side-effect of this reorganization is that separating copy_mc_64.S
to its own file means that perf no longer needs to track dependencies
for its memcpy_64.S benchmarks.
[ bp: Massage a bit. ]
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Tony Luck <tony.luck@intel.com>
Acked-by: Michael Ellerman <mpe@ellerman.id.au>
Cc: <stable@vger.kernel.org>
Link: http://lore.kernel.org/r/CAHk-=wjSqtXAqfUJxFtWNwmguFASTgB0dz1dT3V-78Quiezqbg@mail.gmail.com
Link: https://lkml.kernel.org/r/160195561680.2163339.11574962055305783722.stgit@dwillia2-desk3.amr.corp.intel.com
2020-10-06 06:40:16 +03:00
|
|
|
"copy_mc_fragile",
|
|
|
|
"copy_mc_fragile_handle_tail",
|
2020-10-06 06:40:25 +03:00
|
|
|
"copy_mc_enhanced_fast_string",
|
2019-02-25 14:50:09 +03:00
|
|
|
"ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static void add_uaccess_safe(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct symbol *func;
|
|
|
|
const char **name;
|
|
|
|
|
|
|
|
if (!uaccess)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (name = uaccess_safe_builtin; *name; name++) {
|
|
|
|
func = find_symbol_by_name(file->elf, *name);
|
|
|
|
if (!func)
|
|
|
|
continue;
|
|
|
|
|
2019-07-18 04:36:48 +03:00
|
|
|
func->uaccess_safe = true;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-12 00:46:24 +03:00
|
|
|
/*
|
|
|
|
* FIXME: For now, just ignore any alternatives which add retpolines. This is
|
|
|
|
* a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline.
|
|
|
|
* But it at least allows objtool to understand the control flow *around* the
|
|
|
|
* retpoline.
|
|
|
|
*/
|
2019-03-18 16:33:07 +03:00
|
|
|
static int add_ignore_alternatives(struct objtool_file *file)
|
2018-01-12 00:46:24 +03:00
|
|
|
{
|
|
|
|
struct section *sec;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct reloc *reloc;
|
2018-01-12 00:46:24 +03:00
|
|
|
struct instruction *insn;
|
|
|
|
|
2019-03-18 16:33:07 +03:00
|
|
|
sec = find_section_by_name(file->elf, ".rela.discard.ignore_alts");
|
2018-01-12 00:46:24 +03:00
|
|
|
if (!sec)
|
|
|
|
return 0;
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
list_for_each_entry(reloc, &sec->reloc_list, list) {
|
|
|
|
if (reloc->sym->type != STT_SECTION) {
|
2018-01-12 00:46:24 +03:00
|
|
|
WARN("unexpected relocation symbol type in %s", sec->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
insn = find_insn(file, reloc->sym->sec, reloc->addend);
|
2018-01-12 00:46:24 +03:00
|
|
|
if (!insn) {
|
2019-03-18 16:33:07 +03:00
|
|
|
WARN("bad .discard.ignore_alts entry");
|
2018-01-12 00:46:24 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
insn->ignore_alts = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-26 18:12:04 +03:00
|
|
|
__weak bool arch_is_retpoline(struct symbol *sym)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-03-26 18:12:13 +03:00
|
|
|
#define NEGATIVE_RELOC ((void *)-1L)
|
|
|
|
|
|
|
|
static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
|
|
|
|
{
|
|
|
|
if (insn->reloc == NEGATIVE_RELOC)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!insn->reloc) {
|
2021-06-24 12:41:23 +03:00
|
|
|
if (!file)
|
|
|
|
return NULL;
|
|
|
|
|
2021-03-26 18:12:13 +03:00
|
|
|
insn->reloc = find_reloc_by_dest_range(file->elf, insn->sec,
|
|
|
|
insn->offset, insn->len);
|
|
|
|
if (!insn->reloc) {
|
|
|
|
insn->reloc = NEGATIVE_RELOC;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return insn->reloc;
|
|
|
|
}
|
|
|
|
|
2021-06-24 12:41:02 +03:00
|
|
|
static void remove_insn_ops(struct instruction *insn)
|
|
|
|
{
|
|
|
|
struct stack_op *op, *tmp;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
|
|
|
|
list_del(&op->list);
|
|
|
|
free(op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-26 15:01:34 +03:00
|
|
|
static void annotate_call_site(struct objtool_file *file,
|
|
|
|
struct instruction *insn, bool sibling)
|
2021-06-24 12:41:02 +03:00
|
|
|
{
|
|
|
|
struct reloc *reloc = insn_reloc(file, insn);
|
2021-10-26 15:01:34 +03:00
|
|
|
struct symbol *sym = insn->call_dest;
|
2021-06-24 12:41:02 +03:00
|
|
|
|
2021-10-26 15:01:34 +03:00
|
|
|
if (!sym)
|
|
|
|
sym = reloc->sym;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Alternative replacement code is just template code which is
|
|
|
|
* sometimes copied to the original instruction. For now, don't
|
|
|
|
* annotate it. (In the future we might consider annotating the
|
|
|
|
* original instruction if/when it ever makes sense to do so.)
|
|
|
|
*/
|
|
|
|
if (!strcmp(insn->sec->name, ".altinstr_replacement"))
|
2021-06-24 12:41:02 +03:00
|
|
|
return;
|
|
|
|
|
2021-10-26 15:01:34 +03:00
|
|
|
if (sym->static_call_tramp) {
|
|
|
|
list_add_tail(&insn->call_node, &file->static_call_list);
|
|
|
|
return;
|
2021-06-24 12:41:02 +03:00
|
|
|
}
|
|
|
|
|
2021-10-26 15:01:36 +03:00
|
|
|
if (sym->retpoline_thunk) {
|
|
|
|
list_add_tail(&insn->call_node, &file->retpoline_call_list);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-06-24 12:41:02 +03:00
|
|
|
/*
|
|
|
|
* Many compilers cannot disable KCOV with a function attribute
|
|
|
|
* so they need a little help, NOP out any KCOV calls from noinstr
|
|
|
|
* text.
|
|
|
|
*/
|
2021-10-26 15:01:34 +03:00
|
|
|
if (insn->sec->noinstr && sym->kcov) {
|
2021-06-24 12:41:02 +03:00
|
|
|
if (reloc) {
|
|
|
|
reloc->type = R_NONE;
|
|
|
|
elf_write_reloc(file->elf, reloc);
|
|
|
|
}
|
|
|
|
|
|
|
|
elf_write_insn(file->elf, insn->sec,
|
|
|
|
insn->offset, insn->len,
|
|
|
|
sibling ? arch_ret_insn(insn->len)
|
|
|
|
: arch_nop_insn(insn->len));
|
|
|
|
|
|
|
|
insn->type = sibling ? INSN_RETURN : INSN_NOP;
|
2021-10-26 15:01:34 +03:00
|
|
|
return;
|
2021-06-24 12:41:02 +03:00
|
|
|
}
|
|
|
|
|
2021-10-26 15:01:34 +03:00
|
|
|
if (mcount && sym->fentry) {
|
2021-06-24 12:41:02 +03:00
|
|
|
if (sibling)
|
|
|
|
WARN_FUNC("Tail call to __fentry__ !?!?", insn->sec, insn->offset);
|
|
|
|
|
|
|
|
if (reloc) {
|
|
|
|
reloc->type = R_NONE;
|
|
|
|
elf_write_reloc(file->elf, reloc);
|
|
|
|
}
|
|
|
|
|
|
|
|
elf_write_insn(file->elf, insn->sec,
|
|
|
|
insn->offset, insn->len,
|
|
|
|
arch_nop_insn(insn->len));
|
|
|
|
|
|
|
|
insn->type = INSN_NOP;
|
|
|
|
|
2021-10-26 15:01:35 +03:00
|
|
|
list_add_tail(&insn->call_node, &file->mcount_loc_list);
|
2021-10-26 15:01:34 +03:00
|
|
|
return;
|
2021-06-24 12:41:02 +03:00
|
|
|
}
|
2021-10-26 15:01:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void add_call_dest(struct objtool_file *file, struct instruction *insn,
|
|
|
|
struct symbol *dest, bool sibling)
|
|
|
|
{
|
|
|
|
insn->call_dest = dest;
|
|
|
|
if (!dest)
|
|
|
|
return;
|
2021-06-24 12:41:02 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Whatever stack impact regular CALLs have, should be undone
|
|
|
|
* by the RETURN of the called function.
|
|
|
|
*
|
|
|
|
* Annotated intra-function calls retain the stack_ops but
|
|
|
|
* are converted to JUMP, see read_intra_function_calls().
|
|
|
|
*/
|
|
|
|
remove_insn_ops(insn);
|
2021-10-26 15:01:34 +03:00
|
|
|
|
|
|
|
annotate_call_site(file, insn, sibling);
|
2021-06-24 12:41:02 +03:00
|
|
|
}
|
|
|
|
|
2021-10-26 15:01:36 +03:00
|
|
|
static void add_retpoline_call(struct objtool_file *file, struct instruction *insn)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Retpoline calls/jumps are really dynamic calls/jumps in disguise,
|
|
|
|
* so convert them accordingly.
|
|
|
|
*/
|
|
|
|
switch (insn->type) {
|
|
|
|
case INSN_CALL:
|
|
|
|
insn->type = INSN_CALL_DYNAMIC;
|
|
|
|
break;
|
|
|
|
case INSN_JUMP_UNCONDITIONAL:
|
|
|
|
insn->type = INSN_JUMP_DYNAMIC;
|
|
|
|
break;
|
|
|
|
case INSN_JUMP_CONDITIONAL:
|
|
|
|
insn->type = INSN_JUMP_DYNAMIC_CONDITIONAL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
insn->retpoline_safe = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Whatever stack impact regular CALLs have, should be undone
|
|
|
|
* by the RETURN of the called function.
|
|
|
|
*
|
|
|
|
* Annotated intra-function calls retain the stack_ops but
|
|
|
|
* are converted to JUMP, see read_intra_function_calls().
|
|
|
|
*/
|
|
|
|
remove_insn_ops(insn);
|
|
|
|
|
|
|
|
annotate_call_site(file, insn, false);
|
|
|
|
}
|
2017-06-28 18:11:05 +03:00
|
|
|
/*
|
|
|
|
* Find the destination instructions for all jumps.
|
|
|
|
*/
|
|
|
|
static int add_jump_destinations(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct instruction *insn;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct reloc *reloc;
|
2017-06-28 18:11:05 +03:00
|
|
|
struct section *dest_sec;
|
|
|
|
unsigned long dest_off;
|
|
|
|
|
|
|
|
for_each_insn(file, insn) {
|
2020-02-10 21:32:39 +03:00
|
|
|
if (!is_static_jump(insn))
|
2017-06-28 18:11:05 +03:00
|
|
|
continue;
|
|
|
|
|
2021-03-26 18:12:13 +03:00
|
|
|
reloc = insn_reloc(file, insn);
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
if (!reloc) {
|
2017-06-28 18:11:05 +03:00
|
|
|
dest_sec = insn->sec;
|
2020-03-27 18:28:45 +03:00
|
|
|
dest_off = arch_jump_destination(insn);
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
} else if (reloc->sym->type == STT_SECTION) {
|
|
|
|
dest_sec = reloc->sym->sec;
|
|
|
|
dest_off = arch_dest_reloc_offset(reloc->addend);
|
2021-10-26 15:01:33 +03:00
|
|
|
} else if (reloc->sym->retpoline_thunk) {
|
2021-10-26 15:01:36 +03:00
|
|
|
add_retpoline_call(file, insn);
|
2018-01-12 00:46:23 +03:00
|
|
|
continue;
|
2021-01-22 00:29:22 +03:00
|
|
|
} else if (insn->func) {
|
|
|
|
/* internal or external sibling call (with reloc) */
|
2021-06-24 12:41:02 +03:00
|
|
|
add_call_dest(file, insn, reloc->sym, true);
|
2017-06-28 18:11:05 +03:00
|
|
|
continue;
|
2021-01-22 00:29:22 +03:00
|
|
|
} else if (reloc->sym->sec->idx) {
|
|
|
|
dest_sec = reloc->sym->sec;
|
|
|
|
dest_off = reloc->sym->sym.st_value +
|
|
|
|
arch_dest_reloc_offset(reloc->addend);
|
|
|
|
} else {
|
|
|
|
/* non-func asm code jumping to another file */
|
|
|
|
continue;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
insn->jump_dest = find_insn(file, dest_sec, dest_off);
|
|
|
|
if (!insn->jump_dest) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a special case where an alt instruction
|
|
|
|
* jumps past the end of the section. These are
|
|
|
|
* handled later in handle_group_alt().
|
|
|
|
*/
|
|
|
|
if (!strcmp(insn->sec->name, ".altinstr_replacement"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
|
|
|
|
insn->sec, insn->offset, dest_sec->name,
|
|
|
|
dest_off);
|
|
|
|
return -1;
|
|
|
|
}
|
2018-06-01 15:23:51 +03:00
|
|
|
|
|
|
|
/*
|
2019-03-06 14:58:15 +03:00
|
|
|
* Cross-function jump.
|
2018-06-01 15:23:51 +03:00
|
|
|
*/
|
|
|
|
if (insn->func && insn->jump_dest->func &&
|
2019-03-06 14:58:15 +03:00
|
|
|
insn->func != insn->jump_dest->func) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For GCC 8+, create parent/child links for any cold
|
|
|
|
* subfunctions. This is _mostly_ redundant with a
|
|
|
|
* similar initialization in read_symbols().
|
|
|
|
*
|
|
|
|
* If a function has aliases, we want the *first* such
|
|
|
|
* function in the symbol table to be the subfunction's
|
|
|
|
* parent. In that case we overwrite the
|
|
|
|
* initialization done in read_symbols().
|
|
|
|
*
|
|
|
|
* However this code can't completely replace the
|
|
|
|
* read_symbols() code because this doesn't detect the
|
|
|
|
* case where the parent function's only reference to a
|
2019-07-18 04:36:53 +03:00
|
|
|
* subfunction is through a jump table.
|
2019-03-06 14:58:15 +03:00
|
|
|
*/
|
2021-01-22 00:29:19 +03:00
|
|
|
if (!strstr(insn->func->name, ".cold") &&
|
|
|
|
strstr(insn->jump_dest->func->name, ".cold")) {
|
2019-03-06 14:58:15 +03:00
|
|
|
insn->func->cfunc = insn->jump_dest->func;
|
|
|
|
insn->jump_dest->func->pfunc = insn->func;
|
|
|
|
|
|
|
|
} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
|
|
|
|
insn->jump_dest->offset == insn->jump_dest->func->offset) {
|
2021-01-22 00:29:22 +03:00
|
|
|
/* internal sibling call (without reloc) */
|
2021-06-24 12:41:02 +03:00
|
|
|
add_call_dest(file, insn, insn->jump_dest->func, true);
|
2019-03-06 14:58:15 +03:00
|
|
|
}
|
2018-06-01 15:23:51 +03:00
|
|
|
}
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-15 10:53:18 +03:00
|
|
|
static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
|
|
|
|
{
|
|
|
|
struct symbol *call_dest;
|
|
|
|
|
|
|
|
call_dest = find_func_by_offset(sec, offset);
|
|
|
|
if (!call_dest)
|
|
|
|
call_dest = find_symbol_by_offset(sec, offset);
|
|
|
|
|
|
|
|
return call_dest;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
/*
|
|
|
|
* Find the destination instructions for all calls.
|
|
|
|
*/
|
|
|
|
static int add_call_destinations(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct instruction *insn;
|
|
|
|
unsigned long dest_off;
|
2021-06-24 12:41:02 +03:00
|
|
|
struct symbol *dest;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct reloc *reloc;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
|
|
|
for_each_insn(file, insn) {
|
|
|
|
if (insn->type != INSN_CALL)
|
|
|
|
continue;
|
|
|
|
|
2021-03-26 18:12:13 +03:00
|
|
|
reloc = insn_reloc(file, insn);
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
if (!reloc) {
|
2020-03-27 18:28:45 +03:00
|
|
|
dest_off = arch_jump_destination(insn);
|
2021-06-24 12:41:02 +03:00
|
|
|
dest = find_call_destination(insn->sec, dest_off);
|
|
|
|
|
|
|
|
add_call_dest(file, insn, dest, false);
|
2018-01-30 07:00:39 +03:00
|
|
|
|
2020-02-18 06:41:54 +03:00
|
|
|
if (insn->ignore)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!insn->call_dest) {
|
2020-04-14 13:36:12 +03:00
|
|
|
WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset);
|
2017-06-28 18:11:05 +03:00
|
|
|
return -1;
|
|
|
|
}
|
2018-01-30 07:00:39 +03:00
|
|
|
|
2020-02-18 06:41:54 +03:00
|
|
|
if (insn->func && insn->call_dest->type != STT_FUNC) {
|
|
|
|
WARN_FUNC("unsupported call to non-function",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
} else if (reloc->sym->type == STT_SECTION) {
|
|
|
|
dest_off = arch_dest_reloc_offset(reloc->addend);
|
2021-06-24 12:41:02 +03:00
|
|
|
dest = find_call_destination(reloc->sym->sec, dest_off);
|
|
|
|
if (!dest) {
|
2020-03-27 18:28:45 +03:00
|
|
|
WARN_FUNC("can't find call dest symbol at %s+0x%lx",
|
2017-06-28 18:11:05 +03:00
|
|
|
insn->sec, insn->offset,
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
reloc->sym->sec->name,
|
2020-03-27 18:28:45 +03:00
|
|
|
dest_off);
|
2017-06-28 18:11:05 +03:00
|
|
|
return -1;
|
|
|
|
}
|
2021-03-26 18:12:03 +03:00
|
|
|
|
2021-06-24 12:41:02 +03:00
|
|
|
add_call_dest(file, insn, dest, false);
|
|
|
|
|
2021-10-26 15:01:33 +03:00
|
|
|
} else if (reloc->sym->retpoline_thunk) {
|
2021-10-26 15:01:36 +03:00
|
|
|
add_retpoline_call(file, insn);
|
2021-03-26 18:12:03 +03:00
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
} else
|
2021-06-24 12:41:02 +03:00
|
|
|
add_call_dest(file, insn, reloc->sym, false);
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
* The .alternatives section requires some extra special care over and above
|
|
|
|
* other special sections because alternatives are patched in place.
|
2017-06-28 18:11:05 +03:00
|
|
|
*/
|
|
|
|
static int handle_group_alt(struct objtool_file *file,
|
|
|
|
struct special_alt *special_alt,
|
|
|
|
struct instruction *orig_insn,
|
|
|
|
struct instruction **new_insn)
|
|
|
|
{
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
struct instruction *last_orig_insn, *last_new_insn = NULL, *insn, *nop = NULL;
|
2020-12-18 23:19:32 +03:00
|
|
|
struct alt_group *orig_alt_group, *new_alt_group;
|
2017-06-28 18:11:05 +03:00
|
|
|
unsigned long dest_off;
|
|
|
|
|
2020-12-18 23:19:32 +03:00
|
|
|
|
|
|
|
orig_alt_group = malloc(sizeof(*orig_alt_group));
|
|
|
|
if (!orig_alt_group) {
|
|
|
|
WARN("malloc failed");
|
|
|
|
return -1;
|
|
|
|
}
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
orig_alt_group->cfi = calloc(special_alt->orig_len,
|
|
|
|
sizeof(struct cfi_state *));
|
|
|
|
if (!orig_alt_group->cfi) {
|
|
|
|
WARN("calloc failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
last_orig_insn = NULL;
|
|
|
|
insn = orig_insn;
|
|
|
|
sec_for_each_insn_from(file, insn) {
|
|
|
|
if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
|
|
|
|
break;
|
|
|
|
|
2020-12-18 23:19:32 +03:00
|
|
|
insn->alt_group = orig_alt_group;
|
2017-06-28 18:11:05 +03:00
|
|
|
last_orig_insn = insn;
|
|
|
|
}
|
2020-12-18 23:19:32 +03:00
|
|
|
orig_alt_group->orig_group = NULL;
|
|
|
|
orig_alt_group->first_insn = orig_insn;
|
|
|
|
orig_alt_group->last_insn = last_orig_insn;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2018-01-30 07:00:40 +03:00
|
|
|
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
new_alt_group = malloc(sizeof(*new_alt_group));
|
|
|
|
if (!new_alt_group) {
|
|
|
|
WARN("malloc failed");
|
|
|
|
return -1;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
if (special_alt->new_len < special_alt->orig_len) {
|
|
|
|
/*
|
|
|
|
* Insert a fake nop at the end to make the replacement
|
|
|
|
* alt_group the same size as the original. This is needed to
|
|
|
|
* allow propagate_alt_cfi() to do its magic. When the last
|
|
|
|
* instruction affects the stack, the instruction after it (the
|
|
|
|
* nop) will propagate the new state to the shared CFI array.
|
|
|
|
*/
|
|
|
|
nop = malloc(sizeof(*nop));
|
|
|
|
if (!nop) {
|
|
|
|
WARN("malloc failed");
|
2018-01-30 07:00:40 +03:00
|
|
|
return -1;
|
|
|
|
}
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
memset(nop, 0, sizeof(*nop));
|
|
|
|
INIT_LIST_HEAD(&nop->alts);
|
|
|
|
INIT_LIST_HEAD(&nop->stack_ops);
|
|
|
|
|
|
|
|
nop->sec = special_alt->new_sec;
|
|
|
|
nop->offset = special_alt->new_off + special_alt->new_len;
|
|
|
|
nop->len = special_alt->orig_len - special_alt->new_len;
|
|
|
|
nop->type = INSN_NOP;
|
|
|
|
nop->func = orig_insn->func;
|
|
|
|
nop->alt_group = new_alt_group;
|
|
|
|
nop->ignore = orig_insn->ignore_alts;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
2018-01-30 07:00:40 +03:00
|
|
|
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
if (!special_alt->new_len) {
|
|
|
|
*new_insn = nop;
|
|
|
|
goto end;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
insn = *new_insn;
|
|
|
|
sec_for_each_insn_from(file, insn) {
|
2020-09-04 18:30:23 +03:00
|
|
|
struct reloc *alt_reloc;
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
if (insn->offset >= special_alt->new_off + special_alt->new_len)
|
|
|
|
break;
|
|
|
|
|
|
|
|
last_new_insn = insn;
|
|
|
|
|
2018-01-30 07:00:39 +03:00
|
|
|
insn->ignore = orig_insn->ignore_alts;
|
2019-02-25 12:31:24 +03:00
|
|
|
insn->func = orig_insn->func;
|
2020-12-18 23:19:32 +03:00
|
|
|
insn->alt_group = new_alt_group;
|
2018-01-30 07:00:39 +03:00
|
|
|
|
2020-02-10 21:32:40 +03:00
|
|
|
/*
|
|
|
|
* Since alternative replacement code is copy/pasted by the
|
|
|
|
* kernel after applying relocations, generally such code can't
|
|
|
|
* have relative-address relocation references to outside the
|
|
|
|
* .altinstr_replacement section, unless the arch's
|
|
|
|
* alternatives code can adjust the relative offsets
|
|
|
|
* accordingly.
|
|
|
|
*/
|
2021-03-26 18:12:13 +03:00
|
|
|
alt_reloc = insn_reloc(file, insn);
|
2020-09-04 18:30:23 +03:00
|
|
|
if (alt_reloc &&
|
|
|
|
!arch_support_alt_relocation(special_alt, insn, alt_reloc)) {
|
2020-02-10 21:32:40 +03:00
|
|
|
|
|
|
|
WARN_FUNC("unsupported relocation in alternatives section",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-02-10 21:32:39 +03:00
|
|
|
if (!is_static_jump(insn))
|
2017-06-28 18:11:05 +03:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!insn->immediate)
|
|
|
|
continue;
|
|
|
|
|
2020-03-27 18:28:45 +03:00
|
|
|
dest_off = arch_jump_destination(insn);
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
if (dest_off == special_alt->new_off + special_alt->new_len)
|
|
|
|
insn->jump_dest = next_insn_same_sec(file, last_orig_insn);
|
2017-06-28 18:11:05 +03:00
|
|
|
|
|
|
|
if (!insn->jump_dest) {
|
|
|
|
WARN_FUNC("can't find alternative jump destination",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!last_new_insn) {
|
|
|
|
WARN_FUNC("can't find last new alternative instruction",
|
|
|
|
special_alt->new_sec, special_alt->new_off);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
if (nop)
|
|
|
|
list_add(&nop->list, &last_new_insn->list);
|
|
|
|
end:
|
2020-12-18 23:19:32 +03:00
|
|
|
new_alt_group->orig_group = orig_alt_group;
|
|
|
|
new_alt_group->first_insn = *new_insn;
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
new_alt_group->last_insn = nop ? : last_new_insn;
|
|
|
|
new_alt_group->cfi = orig_alt_group->cfi;
|
2017-06-28 18:11:05 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A jump table entry can either convert a nop to a jump or a jump to a nop.
|
|
|
|
* If the original instruction is a jump, make the alt entry an effective nop
|
|
|
|
* by just skipping the original instruction.
|
|
|
|
*/
|
|
|
|
static int handle_jump_alt(struct objtool_file *file,
|
|
|
|
struct special_alt *special_alt,
|
|
|
|
struct instruction *orig_insn,
|
|
|
|
struct instruction **new_insn)
|
|
|
|
{
|
2021-05-13 17:15:50 +03:00
|
|
|
if (orig_insn->type != INSN_JUMP_UNCONDITIONAL &&
|
|
|
|
orig_insn->type != INSN_NOP) {
|
2021-05-06 22:34:04 +03:00
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
WARN_FUNC("unsupported instruction at jump label",
|
|
|
|
orig_insn->sec, orig_insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-05-06 22:34:03 +03:00
|
|
|
if (special_alt->key_addend & 2) {
|
|
|
|
struct reloc *reloc = insn_reloc(file, orig_insn);
|
|
|
|
|
|
|
|
if (reloc) {
|
|
|
|
reloc->type = R_NONE;
|
|
|
|
elf_write_reloc(file->elf, reloc);
|
|
|
|
}
|
|
|
|
elf_write_insn(file->elf, orig_insn->sec,
|
|
|
|
orig_insn->offset, orig_insn->len,
|
|
|
|
arch_nop_insn(orig_insn->len));
|
|
|
|
orig_insn->type = INSN_NOP;
|
2021-05-13 17:15:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (orig_insn->type == INSN_NOP) {
|
|
|
|
if (orig_insn->len == 2)
|
|
|
|
file->jl_nop_short++;
|
|
|
|
else
|
|
|
|
file->jl_nop_long++;
|
|
|
|
|
|
|
|
return 0;
|
2021-05-06 22:34:03 +03:00
|
|
|
}
|
|
|
|
|
2021-05-06 22:34:04 +03:00
|
|
|
if (orig_insn->len == 2)
|
|
|
|
file->jl_short++;
|
|
|
|
else
|
|
|
|
file->jl_long++;
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
*new_insn = list_next_entry(orig_insn, list);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read all the special sections which have alternate instructions which can be
|
|
|
|
* patched in or redirected to at runtime. Each instruction having alternate
|
|
|
|
* instruction(s) has them added to its insn->alts list, which will be
|
|
|
|
* traversed in validate_branch().
|
|
|
|
*/
|
|
|
|
static int add_special_section_alts(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct list_head special_alts;
|
|
|
|
struct instruction *orig_insn, *new_insn;
|
|
|
|
struct special_alt *special_alt, *tmp;
|
|
|
|
struct alternative *alt;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = special_get_alts(file->elf, &special_alts);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
|
|
|
|
|
|
|
|
orig_insn = find_insn(file, special_alt->orig_sec,
|
|
|
|
special_alt->orig_off);
|
|
|
|
if (!orig_insn) {
|
|
|
|
WARN_FUNC("special: can't find orig instruction",
|
|
|
|
special_alt->orig_sec, special_alt->orig_off);
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_insn = NULL;
|
|
|
|
if (!special_alt->group || special_alt->new_len) {
|
|
|
|
new_insn = find_insn(file, special_alt->new_sec,
|
|
|
|
special_alt->new_off);
|
|
|
|
if (!new_insn) {
|
|
|
|
WARN_FUNC("special: can't find new instruction",
|
|
|
|
special_alt->new_sec,
|
|
|
|
special_alt->new_off);
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (special_alt->group) {
|
2020-03-27 18:28:41 +03:00
|
|
|
if (!special_alt->orig_len) {
|
|
|
|
WARN_FUNC("empty alternative entry",
|
|
|
|
orig_insn->sec, orig_insn->offset);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
ret = handle_group_alt(file, special_alt, orig_insn,
|
|
|
|
&new_insn);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
} else if (special_alt->jump_or_nop) {
|
|
|
|
ret = handle_jump_alt(file, special_alt, orig_insn,
|
|
|
|
&new_insn);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-01-12 00:46:24 +03:00
|
|
|
alt = malloc(sizeof(*alt));
|
|
|
|
if (!alt) {
|
|
|
|
WARN("malloc failed");
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
alt->insn = new_insn;
|
2019-03-01 13:19:03 +03:00
|
|
|
alt->skip_orig = special_alt->skip_orig;
|
2019-02-25 14:50:09 +03:00
|
|
|
orig_insn->ignore_alts |= special_alt->skip_alt;
|
2017-06-28 18:11:05 +03:00
|
|
|
list_add_tail(&alt->list, &orig_insn->alts);
|
|
|
|
|
|
|
|
list_del(&special_alt->list);
|
|
|
|
free(special_alt);
|
|
|
|
}
|
|
|
|
|
2021-05-06 22:34:04 +03:00
|
|
|
if (stats) {
|
|
|
|
printf("jl\\\tNOP\tJMP\n");
|
|
|
|
printf("short:\t%ld\t%ld\n", file->jl_nop_short, file->jl_short);
|
|
|
|
printf("long:\t%ld\t%ld\n", file->jl_nop_long, file->jl_long);
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-07-18 04:36:53 +03:00
|
|
|
static int add_jump_table(struct objtool_file *file, struct instruction *insn,
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct reloc *table)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct reloc *reloc = table;
|
2019-07-18 04:36:53 +03:00
|
|
|
struct instruction *dest_insn;
|
2017-06-28 18:11:05 +03:00
|
|
|
struct alternative *alt;
|
2018-05-11 01:48:49 +03:00
|
|
|
struct symbol *pfunc = insn->func->pfunc;
|
|
|
|
unsigned int prev_offset = 0;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2019-07-18 04:36:53 +03:00
|
|
|
/*
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
* Each @reloc is a switch table relocation which points to the target
|
2019-07-18 04:36:53 +03:00
|
|
|
* instruction.
|
|
|
|
*/
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
list_for_each_entry_from(reloc, &table->sec->reloc_list, list) {
|
objtool: Support repeated uses of the same C jump table
This fixes objtool for both a GCC issue and a Clang issue:
1) GCC issue:
kernel/bpf/core.o: warning: objtool: ___bpf_prog_run()+0x8d5: sibling call from callable instruction with modified stack frame
With CONFIG_RETPOLINE=n, GCC is doing the following optimization in
___bpf_prog_run().
Before:
select_insn:
jmp *jumptable(,%rax,8)
...
ALU64_ADD_X:
...
jmp select_insn
ALU_ADD_X:
...
jmp select_insn
After:
select_insn:
jmp *jumptable(, %rax, 8)
...
ALU64_ADD_X:
...
jmp *jumptable(, %rax, 8)
ALU_ADD_X:
...
jmp *jumptable(, %rax, 8)
This confuses objtool. It has never seen multiple indirect jump
sites which use the same jump table.
For GCC switch tables, the only way of detecting the size of a table
is by continuing to scan for more tables. The size of the previous
table can only be determined after another switch table is found, or
when the scan reaches the end of the function.
That logic was reused for C jump tables, and was based on the
assumption that each jump table only has a single jump site. The
above optimization breaks that assumption.
2) Clang issue:
drivers/usb/misc/sisusbvga/sisusb.o: warning: objtool: sisusb_write_mem_bulk()+0x588: can't find switch jump table
With clang 9, code can be generated where a function contains two
indirect jump instructions which use the same switch table.
The fix is the same for both issues: split the jump table parsing into
two passes.
In the first pass, locate the heads of all switch tables for the
function and mark their locations.
In the second pass, parse the switch tables and add them.
Fixes: e55a73251da3 ("bpf: Fix ORC unwinding in non-JIT BPF code")
Reported-by: Randy Dunlap <rdunlap@infradead.org>
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/e995befaada9d4d8b2cf788ff3f566ba900d2b4d.1563413318.git.jpoimboe@redhat.com
Co-developed-by: Josh Poimboeuf <jpoimboe@redhat.com>
2019-07-18 04:36:54 +03:00
|
|
|
|
|
|
|
/* Check for the end of the table: */
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
if (reloc != table && reloc->jump_table_start)
|
2017-06-28 18:11:05 +03:00
|
|
|
break;
|
|
|
|
|
2019-07-18 04:36:53 +03:00
|
|
|
/* Make sure the table entries are consecutive: */
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
if (prev_offset && reloc->offset != prev_offset + 8)
|
2018-05-11 01:48:49 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Detect function pointers from contiguous objects: */
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
if (reloc->sym->sec == pfunc->sec &&
|
|
|
|
reloc->addend == pfunc->offset)
|
2018-05-11 01:48:49 +03:00
|
|
|
break;
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
dest_insn = find_insn(file, reloc->sym->sec, reloc->addend);
|
2019-07-18 04:36:53 +03:00
|
|
|
if (!dest_insn)
|
2017-06-28 18:11:05 +03:00
|
|
|
break;
|
|
|
|
|
2019-07-18 04:36:53 +03:00
|
|
|
/* Make sure the destination is in the same function: */
|
objtool: Fix seg fault on bad switch table entry
In one rare case, Clang generated the following code:
5ca: 83 e0 21 and $0x21,%eax
5cd: b9 04 00 00 00 mov $0x4,%ecx
5d2: ff 24 c5 00 00 00 00 jmpq *0x0(,%rax,8)
5d5: R_X86_64_32S .rodata+0x38
which uses the corresponding jump table relocations:
000000000038 000200000001 R_X86_64_64 0000000000000000 .text + 834
000000000040 000200000001 R_X86_64_64 0000000000000000 .text + 5d9
000000000048 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000050 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000058 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000060 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000068 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000070 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000078 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000080 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000088 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000090 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000098 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000a0 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000a8 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000b0 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000b8 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000c0 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000c8 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000d0 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000d8 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000e0 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000e8 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000f0 000200000001 R_X86_64_64 0000000000000000 .text + b96
0000000000f8 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000100 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000108 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000110 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000118 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000120 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000128 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000130 000200000001 R_X86_64_64 0000000000000000 .text + b96
000000000138 000200000001 R_X86_64_64 0000000000000000 .text + 82f
000000000140 000200000001 R_X86_64_64 0000000000000000 .text + 828
Since %eax was masked with 0x21, only the first two and the last two
entries are possible.
Objtool doesn't actually emulate all the code, so it isn't smart enough
to know that all the middle entries aren't reachable. They point to the
NOP padding area after the end of the function, so objtool seg faulted
when it tried to dereference a NULL insn->func.
After this fix, objtool still gives an "unreachable" error because it
stops reading the jump table when it encounters the bad addresses:
/home/jpoimboe/objtool-tests/adm1275.o: warning: objtool: adm1275_probe()+0x828: unreachable instruction
While the above code is technically correct, it's very wasteful of
memory -- it uses 34 jump table entries when only 4 are needed. It's
also not possible for objtool to validate this type of switch table
because the unused entries point outside the function and objtool has no
way of determining if that's intentional. Hopefully the Clang folks can
fix it.
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/a9db88eec4f1ca089e040989846961748238b6d8.1563413318.git.jpoimboe@redhat.com
2019-07-18 04:36:55 +03:00
|
|
|
if (!dest_insn->func || dest_insn->func->pfunc != pfunc)
|
2018-05-10 06:39:15 +03:00
|
|
|
break;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
|
|
|
alt = malloc(sizeof(*alt));
|
|
|
|
if (!alt) {
|
|
|
|
WARN("malloc failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-07-18 04:36:53 +03:00
|
|
|
alt->insn = dest_insn;
|
2017-06-28 18:11:05 +03:00
|
|
|
list_add_tail(&alt->list, &insn->alts);
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
prev_offset = reloc->offset;
|
2018-05-11 01:48:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!prev_offset) {
|
|
|
|
WARN_FUNC("can't find switch jump table",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return -1;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2020-09-04 18:30:24 +03:00
|
|
|
* find_jump_table() - Given a dynamic jump, find the switch jump table
|
|
|
|
* associated with it.
|
2017-06-28 18:11:05 +03:00
|
|
|
*/
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
static struct reloc *find_jump_table(struct objtool_file *file,
|
2017-06-28 18:11:05 +03:00
|
|
|
struct symbol *func,
|
|
|
|
struct instruction *insn)
|
|
|
|
{
|
2020-09-04 18:30:24 +03:00
|
|
|
struct reloc *table_reloc;
|
2020-02-18 06:41:53 +03:00
|
|
|
struct instruction *dest_insn, *orig_insn = insn;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2018-02-08 16:02:32 +03:00
|
|
|
/*
|
|
|
|
* Backward search using the @first_jump_src links, these help avoid
|
|
|
|
* much of the 'in between' code. Which avoids us getting confused by
|
|
|
|
* it.
|
|
|
|
*/
|
2018-05-18 23:10:34 +03:00
|
|
|
for (;
|
2020-04-29 00:45:16 +03:00
|
|
|
insn && insn->func && insn->func->pfunc == func;
|
|
|
|
insn = insn->first_jump_src ?: prev_insn_same_sym(file, insn)) {
|
2018-02-08 16:02:32 +03:00
|
|
|
|
2018-05-18 23:10:34 +03:00
|
|
|
if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
|
2017-06-28 18:11:05 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* allow small jumps within the range */
|
|
|
|
if (insn->type == INSN_JUMP_UNCONDITIONAL &&
|
|
|
|
insn->jump_dest &&
|
|
|
|
(insn->jump_dest->offset <= insn->offset ||
|
|
|
|
insn->jump_dest->offset > orig_insn->offset))
|
|
|
|
break;
|
|
|
|
|
2020-09-04 18:30:24 +03:00
|
|
|
table_reloc = arch_find_switch_table(file, insn);
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
if (!table_reloc)
|
2019-07-18 04:36:53 +03:00
|
|
|
continue;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
dest_insn = find_insn(file, table_reloc->sym->sec, table_reloc->addend);
|
2020-02-18 06:41:53 +03:00
|
|
|
if (!dest_insn || !dest_insn->func || dest_insn->func->pfunc != func)
|
|
|
|
continue;
|
2018-05-18 23:10:34 +03:00
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
return table_reloc;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
objtool: Support repeated uses of the same C jump table
This fixes objtool for both a GCC issue and a Clang issue:
1) GCC issue:
kernel/bpf/core.o: warning: objtool: ___bpf_prog_run()+0x8d5: sibling call from callable instruction with modified stack frame
With CONFIG_RETPOLINE=n, GCC is doing the following optimization in
___bpf_prog_run().
Before:
select_insn:
jmp *jumptable(,%rax,8)
...
ALU64_ADD_X:
...
jmp select_insn
ALU_ADD_X:
...
jmp select_insn
After:
select_insn:
jmp *jumptable(, %rax, 8)
...
ALU64_ADD_X:
...
jmp *jumptable(, %rax, 8)
ALU_ADD_X:
...
jmp *jumptable(, %rax, 8)
This confuses objtool. It has never seen multiple indirect jump
sites which use the same jump table.
For GCC switch tables, the only way of detecting the size of a table
is by continuing to scan for more tables. The size of the previous
table can only be determined after another switch table is found, or
when the scan reaches the end of the function.
That logic was reused for C jump tables, and was based on the
assumption that each jump table only has a single jump site. The
above optimization breaks that assumption.
2) Clang issue:
drivers/usb/misc/sisusbvga/sisusb.o: warning: objtool: sisusb_write_mem_bulk()+0x588: can't find switch jump table
With clang 9, code can be generated where a function contains two
indirect jump instructions which use the same switch table.
The fix is the same for both issues: split the jump table parsing into
two passes.
In the first pass, locate the heads of all switch tables for the
function and mark their locations.
In the second pass, parse the switch tables and add them.
Fixes: e55a73251da3 ("bpf: Fix ORC unwinding in non-JIT BPF code")
Reported-by: Randy Dunlap <rdunlap@infradead.org>
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/e995befaada9d4d8b2cf788ff3f566ba900d2b4d.1563413318.git.jpoimboe@redhat.com
Co-developed-by: Josh Poimboeuf <jpoimboe@redhat.com>
2019-07-18 04:36:54 +03:00
|
|
|
/*
|
|
|
|
* First pass: Mark the head of each jump table so that in the next pass,
|
|
|
|
* we know when a given jump table ends and the next one starts.
|
|
|
|
*/
|
|
|
|
static void mark_func_jump_tables(struct objtool_file *file,
|
|
|
|
struct symbol *func)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
objtool: Support repeated uses of the same C jump table
This fixes objtool for both a GCC issue and a Clang issue:
1) GCC issue:
kernel/bpf/core.o: warning: objtool: ___bpf_prog_run()+0x8d5: sibling call from callable instruction with modified stack frame
With CONFIG_RETPOLINE=n, GCC is doing the following optimization in
___bpf_prog_run().
Before:
select_insn:
jmp *jumptable(,%rax,8)
...
ALU64_ADD_X:
...
jmp select_insn
ALU_ADD_X:
...
jmp select_insn
After:
select_insn:
jmp *jumptable(, %rax, 8)
...
ALU64_ADD_X:
...
jmp *jumptable(, %rax, 8)
ALU_ADD_X:
...
jmp *jumptable(, %rax, 8)
This confuses objtool. It has never seen multiple indirect jump
sites which use the same jump table.
For GCC switch tables, the only way of detecting the size of a table
is by continuing to scan for more tables. The size of the previous
table can only be determined after another switch table is found, or
when the scan reaches the end of the function.
That logic was reused for C jump tables, and was based on the
assumption that each jump table only has a single jump site. The
above optimization breaks that assumption.
2) Clang issue:
drivers/usb/misc/sisusbvga/sisusb.o: warning: objtool: sisusb_write_mem_bulk()+0x588: can't find switch jump table
With clang 9, code can be generated where a function contains two
indirect jump instructions which use the same switch table.
The fix is the same for both issues: split the jump table parsing into
two passes.
In the first pass, locate the heads of all switch tables for the
function and mark their locations.
In the second pass, parse the switch tables and add them.
Fixes: e55a73251da3 ("bpf: Fix ORC unwinding in non-JIT BPF code")
Reported-by: Randy Dunlap <rdunlap@infradead.org>
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/e995befaada9d4d8b2cf788ff3f566ba900d2b4d.1563413318.git.jpoimboe@redhat.com
Co-developed-by: Josh Poimboeuf <jpoimboe@redhat.com>
2019-07-18 04:36:54 +03:00
|
|
|
struct instruction *insn, *last = NULL;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct reloc *reloc;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2020-03-10 20:27:24 +03:00
|
|
|
func_for_each_insn(file, func, insn) {
|
2018-02-08 16:02:32 +03:00
|
|
|
if (!last)
|
|
|
|
last = insn;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Store back-pointers for unconditional forward jumps such
|
2019-07-18 04:36:53 +03:00
|
|
|
* that find_jump_table() can back-track using those and
|
2018-02-08 16:02:32 +03:00
|
|
|
* avoid some potentially confusing code.
|
|
|
|
*/
|
|
|
|
if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
|
|
|
|
insn->offset > last->offset &&
|
|
|
|
insn->jump_dest->offset > insn->offset &&
|
|
|
|
!insn->jump_dest->first_jump_src) {
|
|
|
|
|
|
|
|
insn->jump_dest->first_jump_src = insn;
|
|
|
|
last = insn->jump_dest;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
if (insn->type != INSN_JUMP_DYNAMIC)
|
|
|
|
continue;
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
reloc = find_jump_table(file, func, insn);
|
|
|
|
if (reloc) {
|
|
|
|
reloc->jump_table_start = true;
|
|
|
|
insn->jump_table = reloc;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
}
|
objtool: Support repeated uses of the same C jump table
This fixes objtool for both a GCC issue and a Clang issue:
1) GCC issue:
kernel/bpf/core.o: warning: objtool: ___bpf_prog_run()+0x8d5: sibling call from callable instruction with modified stack frame
With CONFIG_RETPOLINE=n, GCC is doing the following optimization in
___bpf_prog_run().
Before:
select_insn:
jmp *jumptable(,%rax,8)
...
ALU64_ADD_X:
...
jmp select_insn
ALU_ADD_X:
...
jmp select_insn
After:
select_insn:
jmp *jumptable(, %rax, 8)
...
ALU64_ADD_X:
...
jmp *jumptable(, %rax, 8)
ALU_ADD_X:
...
jmp *jumptable(, %rax, 8)
This confuses objtool. It has never seen multiple indirect jump
sites which use the same jump table.
For GCC switch tables, the only way of detecting the size of a table
is by continuing to scan for more tables. The size of the previous
table can only be determined after another switch table is found, or
when the scan reaches the end of the function.
That logic was reused for C jump tables, and was based on the
assumption that each jump table only has a single jump site. The
above optimization breaks that assumption.
2) Clang issue:
drivers/usb/misc/sisusbvga/sisusb.o: warning: objtool: sisusb_write_mem_bulk()+0x588: can't find switch jump table
With clang 9, code can be generated where a function contains two
indirect jump instructions which use the same switch table.
The fix is the same for both issues: split the jump table parsing into
two passes.
In the first pass, locate the heads of all switch tables for the
function and mark their locations.
In the second pass, parse the switch tables and add them.
Fixes: e55a73251da3 ("bpf: Fix ORC unwinding in non-JIT BPF code")
Reported-by: Randy Dunlap <rdunlap@infradead.org>
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/e995befaada9d4d8b2cf788ff3f566ba900d2b4d.1563413318.git.jpoimboe@redhat.com
Co-developed-by: Josh Poimboeuf <jpoimboe@redhat.com>
2019-07-18 04:36:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int add_func_jump_tables(struct objtool_file *file,
|
|
|
|
struct symbol *func)
|
|
|
|
{
|
|
|
|
struct instruction *insn;
|
|
|
|
int ret;
|
|
|
|
|
2020-03-10 20:27:24 +03:00
|
|
|
func_for_each_insn(file, func, insn) {
|
objtool: Support repeated uses of the same C jump table
This fixes objtool for both a GCC issue and a Clang issue:
1) GCC issue:
kernel/bpf/core.o: warning: objtool: ___bpf_prog_run()+0x8d5: sibling call from callable instruction with modified stack frame
With CONFIG_RETPOLINE=n, GCC is doing the following optimization in
___bpf_prog_run().
Before:
select_insn:
jmp *jumptable(,%rax,8)
...
ALU64_ADD_X:
...
jmp select_insn
ALU_ADD_X:
...
jmp select_insn
After:
select_insn:
jmp *jumptable(, %rax, 8)
...
ALU64_ADD_X:
...
jmp *jumptable(, %rax, 8)
ALU_ADD_X:
...
jmp *jumptable(, %rax, 8)
This confuses objtool. It has never seen multiple indirect jump
sites which use the same jump table.
For GCC switch tables, the only way of detecting the size of a table
is by continuing to scan for more tables. The size of the previous
table can only be determined after another switch table is found, or
when the scan reaches the end of the function.
That logic was reused for C jump tables, and was based on the
assumption that each jump table only has a single jump site. The
above optimization breaks that assumption.
2) Clang issue:
drivers/usb/misc/sisusbvga/sisusb.o: warning: objtool: sisusb_write_mem_bulk()+0x588: can't find switch jump table
With clang 9, code can be generated where a function contains two
indirect jump instructions which use the same switch table.
The fix is the same for both issues: split the jump table parsing into
two passes.
In the first pass, locate the heads of all switch tables for the
function and mark their locations.
In the second pass, parse the switch tables and add them.
Fixes: e55a73251da3 ("bpf: Fix ORC unwinding in non-JIT BPF code")
Reported-by: Randy Dunlap <rdunlap@infradead.org>
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/e995befaada9d4d8b2cf788ff3f566ba900d2b4d.1563413318.git.jpoimboe@redhat.com
Co-developed-by: Josh Poimboeuf <jpoimboe@redhat.com>
2019-07-18 04:36:54 +03:00
|
|
|
if (!insn->jump_table)
|
|
|
|
continue;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
objtool: Support repeated uses of the same C jump table
This fixes objtool for both a GCC issue and a Clang issue:
1) GCC issue:
kernel/bpf/core.o: warning: objtool: ___bpf_prog_run()+0x8d5: sibling call from callable instruction with modified stack frame
With CONFIG_RETPOLINE=n, GCC is doing the following optimization in
___bpf_prog_run().
Before:
select_insn:
jmp *jumptable(,%rax,8)
...
ALU64_ADD_X:
...
jmp select_insn
ALU_ADD_X:
...
jmp select_insn
After:
select_insn:
jmp *jumptable(, %rax, 8)
...
ALU64_ADD_X:
...
jmp *jumptable(, %rax, 8)
ALU_ADD_X:
...
jmp *jumptable(, %rax, 8)
This confuses objtool. It has never seen multiple indirect jump
sites which use the same jump table.
For GCC switch tables, the only way of detecting the size of a table
is by continuing to scan for more tables. The size of the previous
table can only be determined after another switch table is found, or
when the scan reaches the end of the function.
That logic was reused for C jump tables, and was based on the
assumption that each jump table only has a single jump site. The
above optimization breaks that assumption.
2) Clang issue:
drivers/usb/misc/sisusbvga/sisusb.o: warning: objtool: sisusb_write_mem_bulk()+0x588: can't find switch jump table
With clang 9, code can be generated where a function contains two
indirect jump instructions which use the same switch table.
The fix is the same for both issues: split the jump table parsing into
two passes.
In the first pass, locate the heads of all switch tables for the
function and mark their locations.
In the second pass, parse the switch tables and add them.
Fixes: e55a73251da3 ("bpf: Fix ORC unwinding in non-JIT BPF code")
Reported-by: Randy Dunlap <rdunlap@infradead.org>
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/e995befaada9d4d8b2cf788ff3f566ba900d2b4d.1563413318.git.jpoimboe@redhat.com
Co-developed-by: Josh Poimboeuf <jpoimboe@redhat.com>
2019-07-18 04:36:54 +03:00
|
|
|
ret = add_jump_table(file, insn, insn->jump_table);
|
2017-06-28 18:11:05 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For some switch statements, gcc generates a jump table in the .rodata
|
|
|
|
* section which contains a list of addresses within the function to jump to.
|
|
|
|
* This finds these jump tables and adds them to the insn->alts lists.
|
|
|
|
*/
|
2019-07-18 04:36:53 +03:00
|
|
|
static int add_jump_table_alts(struct objtool_file *file)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
|
|
|
struct section *sec;
|
|
|
|
struct symbol *func;
|
|
|
|
int ret;
|
|
|
|
|
2018-09-07 16:12:01 +03:00
|
|
|
if (!file->rodata)
|
2017-06-28 18:11:05 +03:00
|
|
|
return 0;
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
for_each_sec(file, sec) {
|
2017-06-28 18:11:05 +03:00
|
|
|
list_for_each_entry(func, &sec->symbol_list, list) {
|
|
|
|
if (func->type != STT_FUNC)
|
|
|
|
continue;
|
|
|
|
|
objtool: Support repeated uses of the same C jump table
This fixes objtool for both a GCC issue and a Clang issue:
1) GCC issue:
kernel/bpf/core.o: warning: objtool: ___bpf_prog_run()+0x8d5: sibling call from callable instruction with modified stack frame
With CONFIG_RETPOLINE=n, GCC is doing the following optimization in
___bpf_prog_run().
Before:
select_insn:
jmp *jumptable(,%rax,8)
...
ALU64_ADD_X:
...
jmp select_insn
ALU_ADD_X:
...
jmp select_insn
After:
select_insn:
jmp *jumptable(, %rax, 8)
...
ALU64_ADD_X:
...
jmp *jumptable(, %rax, 8)
ALU_ADD_X:
...
jmp *jumptable(, %rax, 8)
This confuses objtool. It has never seen multiple indirect jump
sites which use the same jump table.
For GCC switch tables, the only way of detecting the size of a table
is by continuing to scan for more tables. The size of the previous
table can only be determined after another switch table is found, or
when the scan reaches the end of the function.
That logic was reused for C jump tables, and was based on the
assumption that each jump table only has a single jump site. The
above optimization breaks that assumption.
2) Clang issue:
drivers/usb/misc/sisusbvga/sisusb.o: warning: objtool: sisusb_write_mem_bulk()+0x588: can't find switch jump table
With clang 9, code can be generated where a function contains two
indirect jump instructions which use the same switch table.
The fix is the same for both issues: split the jump table parsing into
two passes.
In the first pass, locate the heads of all switch tables for the
function and mark their locations.
In the second pass, parse the switch tables and add them.
Fixes: e55a73251da3 ("bpf: Fix ORC unwinding in non-JIT BPF code")
Reported-by: Randy Dunlap <rdunlap@infradead.org>
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/e995befaada9d4d8b2cf788ff3f566ba900d2b4d.1563413318.git.jpoimboe@redhat.com
Co-developed-by: Josh Poimboeuf <jpoimboe@redhat.com>
2019-07-18 04:36:54 +03:00
|
|
|
mark_func_jump_tables(file, func);
|
2019-07-18 04:36:53 +03:00
|
|
|
ret = add_func_jump_tables(file, func);
|
2017-06-28 18:11:05 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-22 00:29:24 +03:00
|
|
|
static void set_func_state(struct cfi_state *state)
|
|
|
|
{
|
|
|
|
state->cfa = initial_func_cfi.cfa;
|
|
|
|
memcpy(&state->regs, &initial_func_cfi.regs,
|
|
|
|
CFI_NUM_REGS * sizeof(struct cfi_reg));
|
|
|
|
state->stack_size = initial_func_cfi.cfa.offset;
|
|
|
|
}
|
|
|
|
|
2017-07-11 18:33:43 +03:00
|
|
|
static int read_unwind_hints(struct objtool_file *file)
|
|
|
|
{
|
2021-06-24 12:41:01 +03:00
|
|
|
struct cfi_state cfi = init_cfi;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct section *sec, *relocsec;
|
2017-07-11 18:33:43 +03:00
|
|
|
struct unwind_hint *hint;
|
|
|
|
struct instruction *insn;
|
2021-06-24 12:41:01 +03:00
|
|
|
struct reloc *reloc;
|
2017-07-11 18:33:43 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
sec = find_section_by_name(file->elf, ".discard.unwind_hints");
|
|
|
|
if (!sec)
|
|
|
|
return 0;
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
relocsec = sec->reloc;
|
|
|
|
if (!relocsec) {
|
2017-07-11 18:33:43 +03:00
|
|
|
WARN("missing .rela.discard.unwind_hints section");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-08-23 01:50:37 +03:00
|
|
|
if (sec->sh.sh_size % sizeof(struct unwind_hint)) {
|
2017-07-11 18:33:43 +03:00
|
|
|
WARN("struct unwind_hint size mismatch");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
file->hints = true;
|
|
|
|
|
2021-08-23 01:50:37 +03:00
|
|
|
for (i = 0; i < sec->sh.sh_size / sizeof(struct unwind_hint); i++) {
|
2017-07-11 18:33:43 +03:00
|
|
|
hint = (struct unwind_hint *)sec->data->d_buf + i;
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
reloc = find_reloc_by_dest(file->elf, sec, i * sizeof(*hint));
|
|
|
|
if (!reloc) {
|
|
|
|
WARN("can't find reloc for unwind_hints[%d]", i);
|
2017-07-11 18:33:43 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
insn = find_insn(file, reloc->sym->sec, reloc->addend);
|
2017-07-11 18:33:43 +03:00
|
|
|
if (!insn) {
|
|
|
|
WARN("can't find insn for unwind_hints[%d]", i);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-01-22 00:29:24 +03:00
|
|
|
insn->hint = true;
|
2017-07-11 18:33:43 +03:00
|
|
|
|
2021-01-22 00:29:24 +03:00
|
|
|
if (hint->type == UNWIND_HINT_TYPE_FUNC) {
|
2021-06-24 12:41:01 +03:00
|
|
|
insn->cfi = &func_cfi;
|
2017-07-11 18:33:43 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-06-24 12:41:01 +03:00
|
|
|
if (insn->cfi)
|
|
|
|
cfi = *(insn->cfi);
|
|
|
|
|
|
|
|
if (arch_decode_hint_reg(hint->sp_reg, &cfi.cfa.base)) {
|
2017-07-11 18:33:43 +03:00
|
|
|
WARN_FUNC("unsupported unwind_hint sp base reg %d",
|
|
|
|
insn->sec, insn->offset, hint->sp_reg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-06-24 12:41:01 +03:00
|
|
|
cfi.cfa.offset = bswap_if_needed(hint->sp_offset);
|
|
|
|
cfi.type = hint->type;
|
|
|
|
cfi.end = hint->end;
|
|
|
|
|
|
|
|
insn->cfi = cfi_hash_find_or_add(&cfi);
|
2017-07-11 18:33:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-01-16 12:24:06 +03:00
|
|
|
static int read_retpoline_hints(struct objtool_file *file)
|
|
|
|
{
|
2018-03-07 02:58:15 +03:00
|
|
|
struct section *sec;
|
2018-01-16 12:24:06 +03:00
|
|
|
struct instruction *insn;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct reloc *reloc;
|
2018-01-16 12:24:06 +03:00
|
|
|
|
2018-03-07 02:58:15 +03:00
|
|
|
sec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe");
|
2018-01-16 12:24:06 +03:00
|
|
|
if (!sec)
|
|
|
|
return 0;
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
list_for_each_entry(reloc, &sec->reloc_list, list) {
|
|
|
|
if (reloc->sym->type != STT_SECTION) {
|
2018-03-07 02:58:15 +03:00
|
|
|
WARN("unexpected relocation symbol type in %s", sec->name);
|
2018-01-16 12:24:06 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
insn = find_insn(file, reloc->sym->sec, reloc->addend);
|
2018-01-16 12:24:06 +03:00
|
|
|
if (!insn) {
|
2018-03-07 02:58:15 +03:00
|
|
|
WARN("bad .discard.retpoline_safe entry");
|
2018-01-16 12:24:06 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (insn->type != INSN_JUMP_DYNAMIC &&
|
|
|
|
insn->type != INSN_CALL_DYNAMIC) {
|
2018-03-07 02:58:15 +03:00
|
|
|
WARN_FUNC("retpoline_safe hint not an indirect jump/call",
|
2018-01-16 12:24:06 +03:00
|
|
|
insn->sec, insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
insn->retpoline_safe = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-10 20:57:41 +03:00
|
|
|
static int read_instr_hints(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct section *sec;
|
|
|
|
struct instruction *insn;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct reloc *reloc;
|
2020-03-10 20:57:41 +03:00
|
|
|
|
|
|
|
sec = find_section_by_name(file->elf, ".rela.discard.instr_end");
|
|
|
|
if (!sec)
|
|
|
|
return 0;
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
list_for_each_entry(reloc, &sec->reloc_list, list) {
|
|
|
|
if (reloc->sym->type != STT_SECTION) {
|
2020-03-10 20:57:41 +03:00
|
|
|
WARN("unexpected relocation symbol type in %s", sec->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
insn = find_insn(file, reloc->sym->sec, reloc->addend);
|
2020-03-10 20:57:41 +03:00
|
|
|
if (!insn) {
|
|
|
|
WARN("bad .discard.instr_end entry");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
insn->instr--;
|
|
|
|
}
|
|
|
|
|
|
|
|
sec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
|
|
|
|
if (!sec)
|
|
|
|
return 0;
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
list_for_each_entry(reloc, &sec->reloc_list, list) {
|
|
|
|
if (reloc->sym->type != STT_SECTION) {
|
2020-03-10 20:57:41 +03:00
|
|
|
WARN("unexpected relocation symbol type in %s", sec->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
insn = find_insn(file, reloc->sym->sec, reloc->addend);
|
2020-03-10 20:57:41 +03:00
|
|
|
if (!insn) {
|
|
|
|
WARN("bad .discard.instr_begin entry");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
insn->instr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-14 13:36:12 +03:00
|
|
|
static int read_intra_function_calls(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct instruction *insn;
|
|
|
|
struct section *sec;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
struct reloc *reloc;
|
2020-04-14 13:36:12 +03:00
|
|
|
|
|
|
|
sec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls");
|
|
|
|
if (!sec)
|
|
|
|
return 0;
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
list_for_each_entry(reloc, &sec->reloc_list, list) {
|
2020-04-14 13:36:12 +03:00
|
|
|
unsigned long dest_off;
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
if (reloc->sym->type != STT_SECTION) {
|
2020-04-14 13:36:12 +03:00
|
|
|
WARN("unexpected relocation symbol type in %s",
|
|
|
|
sec->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-30 00:01:13 +03:00
|
|
|
insn = find_insn(file, reloc->sym->sec, reloc->addend);
|
2020-04-14 13:36:12 +03:00
|
|
|
if (!insn) {
|
|
|
|
WARN("bad .discard.intra_function_call entry");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (insn->type != INSN_CALL) {
|
|
|
|
WARN_FUNC("intra_function_call not a direct call",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Treat intra-function CALLs as JMPs, but with a stack_op.
|
|
|
|
* See add_call_destinations(), which strips stack_ops from
|
|
|
|
* normal CALLs.
|
|
|
|
*/
|
|
|
|
insn->type = INSN_JUMP_UNCONDITIONAL;
|
|
|
|
|
|
|
|
dest_off = insn->offset + insn->len + insn->immediate;
|
|
|
|
insn->jump_dest = find_insn(file, insn->sec, dest_off);
|
|
|
|
if (!insn->jump_dest) {
|
|
|
|
WARN_FUNC("can't find call dest at %s+0x%lx",
|
|
|
|
insn->sec, insn->offset,
|
|
|
|
insn->sec->name, dest_off);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-10-26 15:01:33 +03:00
|
|
|
static int classify_symbols(struct objtool_file *file)
|
2020-08-18 16:57:45 +03:00
|
|
|
{
|
|
|
|
struct section *sec;
|
|
|
|
struct symbol *func;
|
|
|
|
|
|
|
|
for_each_sec(file, sec) {
|
|
|
|
list_for_each_entry(func, &sec->symbol_list, list) {
|
2021-10-26 15:01:33 +03:00
|
|
|
if (func->bind != STB_GLOBAL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!strncmp(func->name, STATIC_CALL_TRAMP_PREFIX_STR,
|
2020-08-18 16:57:45 +03:00
|
|
|
strlen(STATIC_CALL_TRAMP_PREFIX_STR)))
|
|
|
|
func->static_call_tramp = true;
|
2021-10-26 15:01:33 +03:00
|
|
|
|
|
|
|
if (arch_is_retpoline(func))
|
|
|
|
func->retpoline_thunk = true;
|
|
|
|
|
|
|
|
if (!strcmp(func->name, "__fentry__"))
|
|
|
|
func->fentry = true;
|
|
|
|
|
|
|
|
if (!strncmp(func->name, "__sanitizer_cov_", 16))
|
|
|
|
func->kcov = true;
|
2020-08-18 16:57:45 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-07 16:12:01 +03:00
|
|
|
static void mark_rodata(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct section *sec;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
/*
|
2019-06-28 04:50:46 +03:00
|
|
|
* Search for the following rodata sections, each of which can
|
|
|
|
* potentially contain jump tables:
|
|
|
|
*
|
|
|
|
* - .rodata: can contain GCC switch tables
|
|
|
|
* - .rodata.<func>: same, if -fdata-sections is being used
|
|
|
|
* - .rodata..c_jump_table: contains C annotated jump tables
|
|
|
|
*
|
|
|
|
* .rodata.str1.* sections are ignored; they don't contain jump tables.
|
2018-09-07 16:12:01 +03:00
|
|
|
*/
|
|
|
|
for_each_sec(file, sec) {
|
2020-04-12 17:44:05 +03:00
|
|
|
if (!strncmp(sec->name, ".rodata", 7) &&
|
|
|
|
!strstr(sec->name, ".str1.")) {
|
2018-09-07 16:12:01 +03:00
|
|
|
sec->rodata = true;
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
file->rodata = found;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
static int decode_sections(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2018-09-07 16:12:01 +03:00
|
|
|
mark_rodata(file);
|
|
|
|
|
2021-06-24 12:41:23 +03:00
|
|
|
ret = init_pv_ops(file);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
ret = decode_instructions(file);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = add_dead_ends(file);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
add_ignores(file);
|
2019-02-25 14:50:09 +03:00
|
|
|
add_uaccess_safe(file);
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2019-03-18 16:33:07 +03:00
|
|
|
ret = add_ignore_alternatives(file);
|
2018-01-12 00:46:24 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2021-03-26 18:12:05 +03:00
|
|
|
/*
|
|
|
|
* Must be before add_{jump_call}_destination.
|
|
|
|
*/
|
2021-10-26 15:01:33 +03:00
|
|
|
ret = classify_symbols(file);
|
2020-08-18 16:57:49 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2021-03-26 18:12:12 +03:00
|
|
|
/*
|
|
|
|
* Must be before add_special_section_alts() as that depends on
|
|
|
|
* jump_dest being set.
|
|
|
|
*/
|
2017-06-28 18:11:05 +03:00
|
|
|
ret = add_jump_destinations(file);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2018-01-30 07:00:39 +03:00
|
|
|
ret = add_special_section_alts(file);
|
2017-06-28 18:11:05 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2021-03-26 18:12:05 +03:00
|
|
|
/*
|
|
|
|
* Must be before add_call_destination(); it changes INSN_CALL to
|
|
|
|
* INSN_JUMP.
|
|
|
|
*/
|
2020-04-14 13:36:12 +03:00
|
|
|
ret = read_intra_function_calls(file);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2018-01-30 07:00:39 +03:00
|
|
|
ret = add_call_destinations(file);
|
2017-06-28 18:11:05 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2019-07-18 04:36:53 +03:00
|
|
|
ret = add_jump_table_alts(file);
|
2017-06-28 18:11:05 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-07-11 18:33:43 +03:00
|
|
|
ret = read_unwind_hints(file);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2018-01-16 12:24:06 +03:00
|
|
|
ret = read_retpoline_hints(file);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-03-10 20:57:41 +03:00
|
|
|
ret = read_instr_hints(file);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_fentry_call(struct instruction *insn)
|
|
|
|
{
|
2021-10-26 15:01:33 +03:00
|
|
|
if (insn->type == INSN_CALL &&
|
|
|
|
insn->call_dest &&
|
|
|
|
insn->call_dest->fentry)
|
2017-06-28 18:11:05 +03:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-04-01 17:38:19 +03:00
|
|
|
static bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
2020-03-25 16:04:45 +03:00
|
|
|
struct cfi_state *cfi = &state->cfi;
|
2017-06-28 18:11:07 +03:00
|
|
|
int i;
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap)
|
2020-04-01 17:38:19 +03:00
|
|
|
return true;
|
|
|
|
|
2021-01-22 00:29:24 +03:00
|
|
|
if (cfi->cfa.offset != initial_func_cfi.cfa.offset)
|
2017-06-28 18:11:07 +03:00
|
|
|
return true;
|
|
|
|
|
2021-01-22 00:29:24 +03:00
|
|
|
if (cfi->stack_size != initial_func_cfi.cfa.offset)
|
2020-04-01 17:38:19 +03:00
|
|
|
return true;
|
|
|
|
|
|
|
|
for (i = 0; i < CFI_NUM_REGS; i++) {
|
2020-03-25 16:04:45 +03:00
|
|
|
if (cfi->regs[i].base != initial_func_cfi.regs[i].base ||
|
|
|
|
cfi->regs[i].offset != initial_func_cfi.regs[i].offset)
|
2017-06-28 18:11:07 +03:00
|
|
|
return true;
|
2020-04-01 17:38:19 +03:00
|
|
|
}
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-10-14 10:38:00 +03:00
|
|
|
static bool check_reg_frame_pos(const struct cfi_reg *reg,
|
|
|
|
int expected_offset)
|
|
|
|
{
|
|
|
|
return reg->base == CFI_CFA &&
|
|
|
|
reg->offset == expected_offset;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
static bool has_valid_stack_frame(struct insn_state *state)
|
|
|
|
{
|
2020-03-25 16:04:45 +03:00
|
|
|
struct cfi_state *cfi = &state->cfi;
|
|
|
|
|
2020-10-14 10:38:00 +03:00
|
|
|
if (cfi->cfa.base == CFI_BP &&
|
|
|
|
check_reg_frame_pos(&cfi->regs[CFI_BP], -cfi->cfa.offset) &&
|
|
|
|
check_reg_frame_pos(&cfi->regs[CFI_RA], -cfi->cfa.offset + 8))
|
2017-06-28 18:11:07 +03:00
|
|
|
return true;
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (cfi->drap && cfi->regs[CFI_BP].base == CFI_BP)
|
2017-06-28 18:11:07 +03:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
static int update_cfi_state_regs(struct instruction *insn,
|
|
|
|
struct cfi_state *cfi,
|
2020-03-27 18:28:47 +03:00
|
|
|
struct stack_op *op)
|
2017-07-11 18:33:42 +03:00
|
|
|
{
|
2020-03-25 16:04:45 +03:00
|
|
|
struct cfi_reg *cfa = &cfi->cfa;
|
2017-07-11 18:33:42 +03:00
|
|
|
|
2020-04-25 13:03:00 +03:00
|
|
|
if (cfa->base != CFI_SP && cfa->base != CFI_SP_INDIRECT)
|
2017-07-11 18:33:42 +03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* push */
|
2019-02-25 14:50:09 +03:00
|
|
|
if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF)
|
2017-07-11 18:33:42 +03:00
|
|
|
cfa->offset += 8;
|
|
|
|
|
|
|
|
/* pop */
|
2019-02-25 14:50:09 +03:00
|
|
|
if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF)
|
2017-07-11 18:33:42 +03:00
|
|
|
cfa->offset -= 8;
|
|
|
|
|
|
|
|
/* add immediate to sp */
|
|
|
|
if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
|
|
|
|
op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
|
|
|
|
cfa->offset -= op->src.offset;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
objtool: Track DRAP separately from callee-saved registers
When GCC realigns a function's stack, it sometimes uses %r13 as the DRAP
register, like:
push %r13
lea 0x10(%rsp), %r13
and $0xfffffffffffffff0, %rsp
pushq -0x8(%r13)
push %rbp
mov %rsp, %rbp
push %r13
...
mov -0x8(%rbp),%r13
leaveq
lea -0x10(%r13), %rsp
pop %r13
retq
Since %r13 was pushed onto the stack twice, its two stack locations need
to be stored separately. The first push of %r13 is its original value,
and the second push of %r13 is the caller's stack frame address.
Since %r13 is a callee-saved register, we need to track the stack
location of its original value separately from the DRAP register.
This fixes the following false positive warning:
lib/ubsan.o: warning: objtool: val_to_string.constprop.7()+0x97: leave instruction with modified stack frame
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0")
Link: http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-08-11 00:37:26 +03:00
|
|
|
if (arch_callee_saved_reg(reg) &&
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->regs[reg].base == CFI_UNDEFINED) {
|
|
|
|
cfi->regs[reg].base = base;
|
|
|
|
cfi->regs[reg].offset = offset;
|
2017-06-28 18:11:07 +03:00
|
|
|
}
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
static void restore_reg(struct cfi_state *cfi, unsigned char reg)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->regs[reg].base = initial_func_cfi.regs[reg].base;
|
|
|
|
cfi->regs[reg].offset = initial_func_cfi.regs[reg].offset;
|
2017-06-28 18:11:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A note about DRAP stack alignment:
|
|
|
|
*
|
|
|
|
* GCC has the concept of a DRAP register, which is used to help keep track of
|
|
|
|
* the stack pointer when aligning the stack. r10 or r13 is used as the DRAP
|
|
|
|
* register. The typical DRAP pattern is:
|
|
|
|
*
|
|
|
|
* 4c 8d 54 24 08 lea 0x8(%rsp),%r10
|
|
|
|
* 48 83 e4 c0 and $0xffffffffffffffc0,%rsp
|
|
|
|
* 41 ff 72 f8 pushq -0x8(%r10)
|
|
|
|
* 55 push %rbp
|
|
|
|
* 48 89 e5 mov %rsp,%rbp
|
|
|
|
* (more pushes)
|
|
|
|
* 41 52 push %r10
|
|
|
|
* ...
|
|
|
|
* 41 5a pop %r10
|
|
|
|
* (more pops)
|
|
|
|
* 5d pop %rbp
|
|
|
|
* 49 8d 62 f8 lea -0x8(%r10),%rsp
|
|
|
|
* c3 retq
|
|
|
|
*
|
|
|
|
* There are some variations in the epilogues, like:
|
|
|
|
*
|
|
|
|
* 5b pop %rbx
|
|
|
|
* 41 5a pop %r10
|
|
|
|
* 41 5c pop %r12
|
|
|
|
* 41 5d pop %r13
|
|
|
|
* 41 5e pop %r14
|
|
|
|
* c9 leaveq
|
|
|
|
* 49 8d 62 f8 lea -0x8(%r10),%rsp
|
|
|
|
* c3 retq
|
|
|
|
*
|
|
|
|
* and:
|
|
|
|
*
|
|
|
|
* 4c 8b 55 e8 mov -0x18(%rbp),%r10
|
|
|
|
* 48 8b 5d e0 mov -0x20(%rbp),%rbx
|
|
|
|
* 4c 8b 65 f0 mov -0x10(%rbp),%r12
|
|
|
|
* 4c 8b 6d f8 mov -0x8(%rbp),%r13
|
|
|
|
* c9 leaveq
|
|
|
|
* 49 8d 62 f8 lea -0x8(%r10),%rsp
|
|
|
|
* c3 retq
|
|
|
|
*
|
|
|
|
* Sometimes r13 is used as the DRAP register, in which case it's saved and
|
|
|
|
* restored beforehand:
|
|
|
|
*
|
|
|
|
* 41 55 push %r13
|
|
|
|
* 4c 8d 6c 24 10 lea 0x10(%rsp),%r13
|
|
|
|
* 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
|
|
|
|
* ...
|
|
|
|
* 49 8d 65 f0 lea -0x10(%r13),%rsp
|
|
|
|
* 41 5d pop %r13
|
|
|
|
* c3 retq
|
|
|
|
*/
|
2021-02-11 15:03:28 +03:00
|
|
|
static int update_cfi_state(struct instruction *insn,
|
|
|
|
struct instruction *next_insn,
|
|
|
|
struct cfi_state *cfi, struct stack_op *op)
|
2017-06-28 18:11:07 +03:00
|
|
|
{
|
2020-03-25 16:04:45 +03:00
|
|
|
struct cfi_reg *cfa = &cfi->cfa;
|
|
|
|
struct cfi_reg *regs = cfi->regs;
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
/* stack operations don't make sense with an undefined CFA */
|
|
|
|
if (cfa->base == CFI_UNDEFINED) {
|
|
|
|
if (insn->func) {
|
|
|
|
WARN_FUNC("undefined stack state", insn->sec, insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-04 18:30:27 +03:00
|
|
|
if (cfi->type == UNWIND_HINT_TYPE_REGS ||
|
|
|
|
cfi->type == UNWIND_HINT_TYPE_REGS_PARTIAL)
|
2020-03-25 16:04:45 +03:00
|
|
|
return update_cfi_state_regs(insn, cfi, op);
|
2017-07-11 18:33:42 +03:00
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
switch (op->dest.type) {
|
|
|
|
|
|
|
|
case OP_DEST_REG:
|
|
|
|
switch (op->src.type) {
|
|
|
|
|
|
|
|
case OP_SRC_REG:
|
2017-09-21 00:24:32 +03:00
|
|
|
if (op->src.reg == CFI_SP && op->dest.reg == CFI_BP &&
|
|
|
|
cfa->base == CFI_SP &&
|
2020-10-14 10:38:00 +03:00
|
|
|
check_reg_frame_pos(®s[CFI_BP], -cfa->offset)) {
|
2017-09-21 00:24:32 +03:00
|
|
|
|
|
|
|
/* mov %rsp, %rbp */
|
|
|
|
cfa->base = op->dest.reg;
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->bp_scratch = false;
|
2017-09-21 00:24:32 +03:00
|
|
|
}
|
2017-08-29 20:51:03 +03:00
|
|
|
|
2017-09-21 00:24:32 +03:00
|
|
|
else if (op->src.reg == CFI_SP &&
|
2020-03-25 16:04:45 +03:00
|
|
|
op->dest.reg == CFI_BP && cfi->drap) {
|
2017-08-29 20:51:03 +03:00
|
|
|
|
2017-09-21 00:24:32 +03:00
|
|
|
/* drap: mov %rsp, %rbp */
|
|
|
|
regs[CFI_BP].base = CFI_BP;
|
2020-03-25 16:04:45 +03:00
|
|
|
regs[CFI_BP].offset = -cfi->stack_size;
|
|
|
|
cfi->bp_scratch = false;
|
2017-09-21 00:24:32 +03:00
|
|
|
}
|
2017-08-29 20:51:03 +03:00
|
|
|
|
2017-09-21 00:24:32 +03:00
|
|
|
else if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* mov %rsp, %reg
|
|
|
|
*
|
|
|
|
* This is needed for the rare case where GCC
|
|
|
|
* does:
|
|
|
|
*
|
|
|
|
* mov %rsp, %rax
|
|
|
|
* ...
|
|
|
|
* mov %rax, %rsp
|
|
|
|
*/
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->vals[op->dest.reg].base = CFI_CFA;
|
|
|
|
cfi->vals[op->dest.reg].offset = -cfi->stack_size;
|
2017-08-29 20:51:03 +03:00
|
|
|
}
|
|
|
|
|
2018-03-22 21:00:37 +03:00
|
|
|
else if (op->src.reg == CFI_BP && op->dest.reg == CFI_SP &&
|
2021-02-09 23:41:13 +03:00
|
|
|
(cfa->base == CFI_BP || cfa->base == cfi->drap_reg)) {
|
2018-03-22 21:00:37 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* mov %rbp, %rsp
|
|
|
|
*
|
|
|
|
* Restore the original stack pointer (Clang).
|
|
|
|
*/
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->stack_size = -cfi->regs[CFI_BP].offset;
|
2018-03-22 21:00:37 +03:00
|
|
|
}
|
|
|
|
|
2017-08-29 20:51:03 +03:00
|
|
|
else if (op->dest.reg == cfa->base) {
|
|
|
|
|
|
|
|
/* mov %reg, %rsp */
|
|
|
|
if (cfa->base == CFI_SP &&
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->vals[op->src.reg].base == CFI_CFA) {
|
2017-08-29 20:51:03 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is needed for the rare case
|
|
|
|
* where GCC does something dumb like:
|
|
|
|
*
|
|
|
|
* lea 0x8(%rsp), %rcx
|
|
|
|
* ...
|
|
|
|
* mov %rcx, %rsp
|
|
|
|
*/
|
2020-03-25 16:04:45 +03:00
|
|
|
cfa->offset = -cfi->vals[op->src.reg].offset;
|
|
|
|
cfi->stack_size = cfa->offset;
|
2017-08-29 20:51:03 +03:00
|
|
|
|
2021-02-03 14:02:17 +03:00
|
|
|
} else if (cfa->base == CFI_SP &&
|
|
|
|
cfi->vals[op->src.reg].base == CFI_SP_INDIRECT &&
|
|
|
|
cfi->vals[op->src.reg].offset == cfa->offset) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stack swizzle:
|
|
|
|
*
|
|
|
|
* 1: mov %rsp, (%[tos])
|
|
|
|
* 2: mov %[tos], %rsp
|
|
|
|
* ...
|
|
|
|
* 3: pop %rsp
|
|
|
|
*
|
|
|
|
* Where:
|
|
|
|
*
|
|
|
|
* 1 - places a pointer to the previous
|
|
|
|
* stack at the Top-of-Stack of the
|
|
|
|
* new stack.
|
|
|
|
*
|
|
|
|
* 2 - switches to the new stack.
|
|
|
|
*
|
|
|
|
* 3 - pops the Top-of-Stack to restore
|
|
|
|
* the original stack.
|
|
|
|
*
|
|
|
|
* Note: we set base to SP_INDIRECT
|
|
|
|
* here and preserve offset. Therefore
|
|
|
|
* when the unwinder reaches ToS it
|
|
|
|
* will dereference SP and then add the
|
|
|
|
* offset to find the next frame, IOW:
|
|
|
|
* (%rsp) + offset.
|
|
|
|
*/
|
|
|
|
cfa->base = CFI_SP_INDIRECT;
|
|
|
|
|
2017-08-29 20:51:03 +03:00
|
|
|
} else {
|
|
|
|
cfa->base = CFI_UNDEFINED;
|
|
|
|
cfa->offset = 0;
|
|
|
|
}
|
2017-06-28 18:11:07 +03:00
|
|
|
}
|
|
|
|
|
2021-02-18 19:14:10 +03:00
|
|
|
else if (op->dest.reg == CFI_SP &&
|
|
|
|
cfi->vals[op->src.reg].base == CFI_SP_INDIRECT &&
|
|
|
|
cfi->vals[op->src.reg].offset == cfa->offset) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The same stack swizzle case 2) as above. But
|
|
|
|
* because we can't change cfa->base, case 3)
|
|
|
|
* will become a regular POP. Pretend we're a
|
|
|
|
* PUSH so things don't go unbalanced.
|
|
|
|
*/
|
|
|
|
cfi->stack_size += 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_SRC_ADD:
|
|
|
|
if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
|
|
|
|
|
|
|
|
/* add imm, %rsp */
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->stack_size -= op->src.offset;
|
2017-06-28 18:11:07 +03:00
|
|
|
if (cfa->base == CFI_SP)
|
|
|
|
cfa->offset -= op->src.offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
|
|
|
|
|
|
|
|
/* lea disp(%rbp), %rsp */
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset);
|
2017-06-28 18:11:07 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-10-14 10:38:01 +03:00
|
|
|
if (!cfi->drap && op->src.reg == CFI_SP &&
|
|
|
|
op->dest.reg == CFI_BP && cfa->base == CFI_SP &&
|
|
|
|
check_reg_frame_pos(®s[CFI_BP], -cfa->offset + op->src.offset)) {
|
|
|
|
|
|
|
|
/* lea disp(%rsp), %rbp */
|
|
|
|
cfa->base = CFI_BP;
|
|
|
|
cfa->offset -= op->src.offset;
|
|
|
|
cfi->bp_scratch = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-08-29 20:51:03 +03:00
|
|
|
if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
/* drap: lea disp(%rsp), %drap */
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->drap_reg = op->dest.reg;
|
2017-08-29 20:51:03 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* lea disp(%rsp), %reg
|
|
|
|
*
|
|
|
|
* This is needed for the rare case where GCC
|
|
|
|
* does something dumb like:
|
|
|
|
*
|
|
|
|
* lea 0x8(%rsp), %rcx
|
|
|
|
* ...
|
|
|
|
* mov %rcx, %rsp
|
|
|
|
*/
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->vals[op->dest.reg].base = CFI_CFA;
|
|
|
|
cfi->vals[op->dest.reg].offset = \
|
|
|
|
-cfi->stack_size + op->src.offset;
|
2017-08-29 20:51:03 +03:00
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (cfi->drap && op->dest.reg == CFI_SP &&
|
|
|
|
op->src.reg == cfi->drap_reg) {
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
/* drap: lea disp(%drap), %rsp */
|
|
|
|
cfa->base = CFI_SP;
|
2020-03-25 16:04:45 +03:00
|
|
|
cfa->offset = cfi->stack_size = -op->src.offset;
|
|
|
|
cfi->drap_reg = CFI_UNDEFINED;
|
|
|
|
cfi->drap = false;
|
2017-06-28 18:11:07 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-02-11 15:03:28 +03:00
|
|
|
if (op->dest.reg == cfi->cfa.base && !(next_insn && next_insn->hint)) {
|
2017-06-28 18:11:07 +03:00
|
|
|
WARN_FUNC("unsupported stack register modification",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_SRC_AND:
|
|
|
|
if (op->dest.reg != CFI_SP ||
|
2020-03-25 16:04:45 +03:00
|
|
|
(cfi->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
|
|
|
|
(cfi->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
|
2017-06-28 18:11:07 +03:00
|
|
|
WARN_FUNC("unsupported stack pointer realignment",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (cfi->drap_reg != CFI_UNDEFINED) {
|
2017-06-28 18:11:07 +03:00
|
|
|
/* drap: and imm, %rsp */
|
2020-03-25 16:04:45 +03:00
|
|
|
cfa->base = cfi->drap_reg;
|
|
|
|
cfa->offset = cfi->stack_size = 0;
|
|
|
|
cfi->drap = true;
|
2017-06-28 18:11:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Older versions of GCC (4.8ish) realign the stack
|
|
|
|
* without DRAP, with a frame pointer.
|
|
|
|
*/
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_SRC_POP:
|
2019-02-25 14:50:09 +03:00
|
|
|
case OP_SRC_POPF:
|
2021-02-03 14:02:17 +03:00
|
|
|
if (op->dest.reg == CFI_SP && cfa->base == CFI_SP_INDIRECT) {
|
|
|
|
|
|
|
|
/* pop %rsp; # restore from a stack swizzle */
|
|
|
|
cfa->base = CFI_SP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (!cfi->drap && op->dest.reg == cfa->base) {
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
/* pop %rbp */
|
|
|
|
cfa->base = CFI_SP;
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (cfi->drap && cfa->base == CFI_BP_INDIRECT &&
|
|
|
|
op->dest.reg == cfi->drap_reg &&
|
|
|
|
cfi->drap_offset == -cfi->stack_size) {
|
2017-06-28 18:11:07 +03:00
|
|
|
|
objtool: Track DRAP separately from callee-saved registers
When GCC realigns a function's stack, it sometimes uses %r13 as the DRAP
register, like:
push %r13
lea 0x10(%rsp), %r13
and $0xfffffffffffffff0, %rsp
pushq -0x8(%r13)
push %rbp
mov %rsp, %rbp
push %r13
...
mov -0x8(%rbp),%r13
leaveq
lea -0x10(%r13), %rsp
pop %r13
retq
Since %r13 was pushed onto the stack twice, its two stack locations need
to be stored separately. The first push of %r13 is its original value,
and the second push of %r13 is the caller's stack frame address.
Since %r13 is a callee-saved register, we need to track the stack
location of its original value separately from the DRAP register.
This fixes the following false positive warning:
lib/ubsan.o: warning: objtool: val_to_string.constprop.7()+0x97: leave instruction with modified stack frame
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0")
Link: http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-08-11 00:37:26 +03:00
|
|
|
/* drap: pop %drap */
|
2020-03-25 16:04:45 +03:00
|
|
|
cfa->base = cfi->drap_reg;
|
objtool: Track DRAP separately from callee-saved registers
When GCC realigns a function's stack, it sometimes uses %r13 as the DRAP
register, like:
push %r13
lea 0x10(%rsp), %r13
and $0xfffffffffffffff0, %rsp
pushq -0x8(%r13)
push %rbp
mov %rsp, %rbp
push %r13
...
mov -0x8(%rbp),%r13
leaveq
lea -0x10(%r13), %rsp
pop %r13
retq
Since %r13 was pushed onto the stack twice, its two stack locations need
to be stored separately. The first push of %r13 is its original value,
and the second push of %r13 is the caller's stack frame address.
Since %r13 is a callee-saved register, we need to track the stack
location of its original value separately from the DRAP register.
This fixes the following false positive warning:
lib/ubsan.o: warning: objtool: val_to_string.constprop.7()+0x97: leave instruction with modified stack frame
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0")
Link: http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-08-11 00:37:26 +03:00
|
|
|
cfa->offset = 0;
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->drap_offset = -1;
|
2017-06-28 18:11:07 +03:00
|
|
|
|
2021-02-09 23:41:13 +03:00
|
|
|
} else if (cfi->stack_size == -regs[op->dest.reg].offset) {
|
2017-06-28 18:11:07 +03:00
|
|
|
|
objtool: Track DRAP separately from callee-saved registers
When GCC realigns a function's stack, it sometimes uses %r13 as the DRAP
register, like:
push %r13
lea 0x10(%rsp), %r13
and $0xfffffffffffffff0, %rsp
pushq -0x8(%r13)
push %rbp
mov %rsp, %rbp
push %r13
...
mov -0x8(%rbp),%r13
leaveq
lea -0x10(%r13), %rsp
pop %r13
retq
Since %r13 was pushed onto the stack twice, its two stack locations need
to be stored separately. The first push of %r13 is its original value,
and the second push of %r13 is the caller's stack frame address.
Since %r13 is a callee-saved register, we need to track the stack
location of its original value separately from the DRAP register.
This fixes the following false positive warning:
lib/ubsan.o: warning: objtool: val_to_string.constprop.7()+0x97: leave instruction with modified stack frame
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0")
Link: http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-08-11 00:37:26 +03:00
|
|
|
/* pop %reg */
|
2020-03-25 16:04:45 +03:00
|
|
|
restore_reg(cfi, op->dest.reg);
|
2017-06-28 18:11:07 +03:00
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->stack_size -= 8;
|
2017-06-28 18:11:07 +03:00
|
|
|
if (cfa->base == CFI_SP)
|
|
|
|
cfa->offset -= 8;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_SRC_REG_INDIRECT:
|
2020-10-14 10:38:02 +03:00
|
|
|
if (!cfi->drap && op->dest.reg == cfa->base &&
|
|
|
|
op->dest.reg == CFI_BP) {
|
|
|
|
|
|
|
|
/* mov disp(%rsp), %rbp */
|
|
|
|
cfa->base = CFI_SP;
|
|
|
|
cfa->offset = cfi->stack_size;
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (cfi->drap && op->src.reg == CFI_BP &&
|
|
|
|
op->src.offset == cfi->drap_offset) {
|
objtool: Track DRAP separately from callee-saved registers
When GCC realigns a function's stack, it sometimes uses %r13 as the DRAP
register, like:
push %r13
lea 0x10(%rsp), %r13
and $0xfffffffffffffff0, %rsp
pushq -0x8(%r13)
push %rbp
mov %rsp, %rbp
push %r13
...
mov -0x8(%rbp),%r13
leaveq
lea -0x10(%r13), %rsp
pop %r13
retq
Since %r13 was pushed onto the stack twice, its two stack locations need
to be stored separately. The first push of %r13 is its original value,
and the second push of %r13 is the caller's stack frame address.
Since %r13 is a callee-saved register, we need to track the stack
location of its original value separately from the DRAP register.
This fixes the following false positive warning:
lib/ubsan.o: warning: objtool: val_to_string.constprop.7()+0x97: leave instruction with modified stack frame
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0")
Link: http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-08-11 00:37:26 +03:00
|
|
|
|
|
|
|
/* drap: mov disp(%rbp), %drap */
|
2020-03-25 16:04:45 +03:00
|
|
|
cfa->base = cfi->drap_reg;
|
objtool: Track DRAP separately from callee-saved registers
When GCC realigns a function's stack, it sometimes uses %r13 as the DRAP
register, like:
push %r13
lea 0x10(%rsp), %r13
and $0xfffffffffffffff0, %rsp
pushq -0x8(%r13)
push %rbp
mov %rsp, %rbp
push %r13
...
mov -0x8(%rbp),%r13
leaveq
lea -0x10(%r13), %rsp
pop %r13
retq
Since %r13 was pushed onto the stack twice, its two stack locations need
to be stored separately. The first push of %r13 is its original value,
and the second push of %r13 is the caller's stack frame address.
Since %r13 is a callee-saved register, we need to track the stack
location of its original value separately from the DRAP register.
This fixes the following false positive warning:
lib/ubsan.o: warning: objtool: val_to_string.constprop.7()+0x97: leave instruction with modified stack frame
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0")
Link: http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-08-11 00:37:26 +03:00
|
|
|
cfa->offset = 0;
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->drap_offset = -1;
|
objtool: Track DRAP separately from callee-saved registers
When GCC realigns a function's stack, it sometimes uses %r13 as the DRAP
register, like:
push %r13
lea 0x10(%rsp), %r13
and $0xfffffffffffffff0, %rsp
pushq -0x8(%r13)
push %rbp
mov %rsp, %rbp
push %r13
...
mov -0x8(%rbp),%r13
leaveq
lea -0x10(%r13), %rsp
pop %r13
retq
Since %r13 was pushed onto the stack twice, its two stack locations need
to be stored separately. The first push of %r13 is its original value,
and the second push of %r13 is the caller's stack frame address.
Since %r13 is a callee-saved register, we need to track the stack
location of its original value separately from the DRAP register.
This fixes the following false positive warning:
lib/ubsan.o: warning: objtool: val_to_string.constprop.7()+0x97: leave instruction with modified stack frame
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0")
Link: http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-08-11 00:37:26 +03:00
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (cfi->drap && op->src.reg == CFI_BP &&
|
2017-06-28 18:11:07 +03:00
|
|
|
op->src.offset == regs[op->dest.reg].offset) {
|
|
|
|
|
|
|
|
/* drap: mov disp(%rbp), %reg */
|
2020-03-25 16:04:45 +03:00
|
|
|
restore_reg(cfi, op->dest.reg);
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
} else if (op->src.reg == cfa->base &&
|
|
|
|
op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
|
|
|
|
|
|
|
|
/* mov disp(%rbp), %reg */
|
|
|
|
/* mov disp(%rsp), %reg */
|
2020-03-25 16:04:45 +03:00
|
|
|
restore_reg(cfi, op->dest.reg);
|
2020-10-14 10:38:02 +03:00
|
|
|
|
|
|
|
} else if (op->src.reg == CFI_SP &&
|
|
|
|
op->src.offset == regs[op->dest.reg].offset + cfi->stack_size) {
|
|
|
|
|
|
|
|
/* mov disp(%rsp), %reg */
|
|
|
|
restore_reg(cfi, op->dest.reg);
|
2017-06-28 18:11:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
WARN_FUNC("unknown stack-related instruction",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_DEST_PUSH:
|
2019-02-25 14:50:09 +03:00
|
|
|
case OP_DEST_PUSHF:
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->stack_size += 8;
|
2017-06-28 18:11:07 +03:00
|
|
|
if (cfa->base == CFI_SP)
|
|
|
|
cfa->offset += 8;
|
|
|
|
|
|
|
|
if (op->src.type != OP_SRC_REG)
|
|
|
|
break;
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (cfi->drap) {
|
|
|
|
if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
/* drap: push %drap */
|
|
|
|
cfa->base = CFI_BP_INDIRECT;
|
2020-03-25 16:04:45 +03:00
|
|
|
cfa->offset = -cfi->stack_size;
|
2017-06-28 18:11:07 +03:00
|
|
|
|
objtool: Track DRAP separately from callee-saved registers
When GCC realigns a function's stack, it sometimes uses %r13 as the DRAP
register, like:
push %r13
lea 0x10(%rsp), %r13
and $0xfffffffffffffff0, %rsp
pushq -0x8(%r13)
push %rbp
mov %rsp, %rbp
push %r13
...
mov -0x8(%rbp),%r13
leaveq
lea -0x10(%r13), %rsp
pop %r13
retq
Since %r13 was pushed onto the stack twice, its two stack locations need
to be stored separately. The first push of %r13 is its original value,
and the second push of %r13 is the caller's stack frame address.
Since %r13 is a callee-saved register, we need to track the stack
location of its original value separately from the DRAP register.
This fixes the following false positive warning:
lib/ubsan.o: warning: objtool: val_to_string.constprop.7()+0x97: leave instruction with modified stack frame
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0")
Link: http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-08-11 00:37:26 +03:00
|
|
|
/* save drap so we know when to restore it */
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->drap_offset = -cfi->stack_size;
|
2017-06-28 18:11:07 +03:00
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
} else if (op->src.reg == CFI_BP && cfa->base == cfi->drap_reg) {
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
/* drap: push %rbp */
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->stack_size = 0;
|
2017-06-28 18:11:07 +03:00
|
|
|
|
2020-09-15 10:53:16 +03:00
|
|
|
} else {
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
/* drap: push %reg */
|
2020-03-25 16:04:45 +03:00
|
|
|
save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
|
2017-06-28 18:11:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* push %reg */
|
2020-03-25 16:04:45 +03:00
|
|
|
save_reg(cfi, op->src.reg, CFI_CFA, -cfi->stack_size);
|
2017-06-28 18:11:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* detect when asm code uses rbp as a scratch register */
|
2017-07-25 02:34:14 +03:00
|
|
|
if (!no_fp && insn->func && op->src.reg == CFI_BP &&
|
2017-06-28 18:11:07 +03:00
|
|
|
cfa->base != CFI_BP)
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->bp_scratch = true;
|
2017-06-28 18:11:07 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_DEST_REG_INDIRECT:
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (cfi->drap) {
|
|
|
|
if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
/* drap: mov %drap, disp(%rbp) */
|
|
|
|
cfa->base = CFI_BP_INDIRECT;
|
|
|
|
cfa->offset = op->dest.offset;
|
|
|
|
|
objtool: Track DRAP separately from callee-saved registers
When GCC realigns a function's stack, it sometimes uses %r13 as the DRAP
register, like:
push %r13
lea 0x10(%rsp), %r13
and $0xfffffffffffffff0, %rsp
pushq -0x8(%r13)
push %rbp
mov %rsp, %rbp
push %r13
...
mov -0x8(%rbp),%r13
leaveq
lea -0x10(%r13), %rsp
pop %r13
retq
Since %r13 was pushed onto the stack twice, its two stack locations need
to be stored separately. The first push of %r13 is its original value,
and the second push of %r13 is the caller's stack frame address.
Since %r13 is a callee-saved register, we need to track the stack
location of its original value separately from the DRAP register.
This fixes the following false positive warning:
lib/ubsan.o: warning: objtool: val_to_string.constprop.7()+0x97: leave instruction with modified stack frame
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0")
Link: http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-08-11 00:37:26 +03:00
|
|
|
/* save drap offset so we know when to restore it */
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->drap_offset = op->dest.offset;
|
2020-09-15 10:53:16 +03:00
|
|
|
} else {
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
/* drap: mov reg, disp(%rbp) */
|
2020-03-25 16:04:45 +03:00
|
|
|
save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
|
2017-06-28 18:11:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
} else if (op->dest.reg == cfa->base) {
|
|
|
|
|
|
|
|
/* mov reg, disp(%rbp) */
|
|
|
|
/* mov reg, disp(%rsp) */
|
2020-03-25 16:04:45 +03:00
|
|
|
save_reg(cfi, op->src.reg, CFI_CFA,
|
|
|
|
op->dest.offset - cfi->cfa.offset);
|
2020-10-14 10:38:02 +03:00
|
|
|
|
|
|
|
} else if (op->dest.reg == CFI_SP) {
|
|
|
|
|
|
|
|
/* mov reg, disp(%rsp) */
|
|
|
|
save_reg(cfi, op->src.reg, CFI_CFA,
|
|
|
|
op->dest.offset - cfi->stack_size);
|
2021-02-03 14:02:17 +03:00
|
|
|
|
|
|
|
} else if (op->src.reg == CFI_SP && op->dest.offset == 0) {
|
|
|
|
|
|
|
|
/* mov %rsp, (%reg); # setup a stack swizzle. */
|
|
|
|
cfi->vals[op->dest.reg].base = CFI_SP_INDIRECT;
|
|
|
|
cfi->vals[op->dest.reg].offset = cfa->offset;
|
2017-06-28 18:11:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_DEST_MEM:
|
2019-02-25 14:50:09 +03:00
|
|
|
if (op->src.type != OP_SRC_POP && op->src.type != OP_SRC_POPF) {
|
2017-06-28 18:11:07 +03:00
|
|
|
WARN_FUNC("unknown stack-related memory operation",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pop mem */
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi->stack_size -= 8;
|
2017-06-28 18:11:07 +03:00
|
|
|
if (cfa->base == CFI_SP)
|
|
|
|
cfa->offset -= 8;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
WARN_FUNC("unknown stack-related instruction",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
/*
|
|
|
|
* The stack layouts of alternatives instructions can sometimes diverge when
|
|
|
|
* they have stack modifications. That's fine as long as the potential stack
|
|
|
|
* layouts don't conflict at any given potential instruction boundary.
|
|
|
|
*
|
|
|
|
* Flatten the CFIs of the different alternative code streams (both original
|
|
|
|
* and replacement) into a single shared CFI array which can be used to detect
|
|
|
|
* conflicts and nicely feed a linear array of ORC entries to the unwinder.
|
|
|
|
*/
|
|
|
|
static int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn)
|
2020-03-27 18:28:47 +03:00
|
|
|
{
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
struct cfi_state **alt_cfi;
|
|
|
|
int group_off;
|
2020-03-27 18:28:47 +03:00
|
|
|
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
if (!insn->alt_group)
|
|
|
|
return 0;
|
2020-03-27 18:28:47 +03:00
|
|
|
|
2021-06-24 12:41:01 +03:00
|
|
|
if (!insn->cfi) {
|
|
|
|
WARN("CFI missing");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
alt_cfi = insn->alt_group->cfi;
|
|
|
|
group_off = insn->offset - insn->alt_group->first_insn->offset;
|
2020-03-27 18:28:47 +03:00
|
|
|
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
if (!alt_cfi[group_off]) {
|
2021-06-24 12:41:01 +03:00
|
|
|
alt_cfi[group_off] = insn->cfi;
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
} else {
|
2021-06-24 12:41:01 +03:00
|
|
|
if (cficmp(alt_cfi[group_off], insn->cfi)) {
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
WARN_FUNC("stack layout conflict in alternatives",
|
|
|
|
insn->sec, insn->offset);
|
2020-05-08 13:34:33 +03:00
|
|
|
return -1;
|
|
|
|
}
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-11 15:03:28 +03:00
|
|
|
static int handle_insn_ops(struct instruction *insn,
|
|
|
|
struct instruction *next_insn,
|
|
|
|
struct insn_state *state)
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
{
|
|
|
|
struct stack_op *op;
|
|
|
|
|
|
|
|
list_for_each_entry(op, &insn->stack_ops, list) {
|
|
|
|
|
2021-02-11 15:03:28 +03:00
|
|
|
if (update_cfi_state(insn, next_insn, &state->cfi, op))
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
return 1;
|
2020-05-08 13:34:33 +03:00
|
|
|
|
2021-03-08 17:46:04 +03:00
|
|
|
if (!insn->alt_group)
|
|
|
|
continue;
|
|
|
|
|
2020-03-27 18:28:47 +03:00
|
|
|
if (op->dest.type == OP_DEST_PUSHF) {
|
|
|
|
if (!state->uaccess_stack) {
|
|
|
|
state->uaccess_stack = 1;
|
|
|
|
} else if (state->uaccess_stack >> 31) {
|
|
|
|
WARN_FUNC("PUSHF stack exhausted",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
state->uaccess_stack <<= 1;
|
|
|
|
state->uaccess_stack |= state->uaccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op->src.type == OP_SRC_POPF) {
|
|
|
|
if (state->uaccess_stack) {
|
|
|
|
state->uaccess = state->uaccess_stack & 1;
|
|
|
|
state->uaccess_stack >>= 1;
|
|
|
|
if (state->uaccess_stack == 1)
|
|
|
|
state->uaccess_stack = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
|
2017-06-28 18:11:07 +03:00
|
|
|
{
|
2021-06-24 12:41:01 +03:00
|
|
|
struct cfi_state *cfi1 = insn->cfi;
|
2017-06-28 18:11:07 +03:00
|
|
|
int i;
|
|
|
|
|
2021-06-24 12:41:01 +03:00
|
|
|
if (!cfi1) {
|
|
|
|
WARN("CFI missing");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (memcmp(&cfi1->cfa, &cfi2->cfa, sizeof(cfi1->cfa))) {
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
WARN_FUNC("stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
|
|
|
|
insn->sec, insn->offset,
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi1->cfa.base, cfi1->cfa.offset,
|
|
|
|
cfi2->cfa.base, cfi2->cfa.offset);
|
2017-06-28 18:11:07 +03:00
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
} else if (memcmp(&cfi1->regs, &cfi2->regs, sizeof(cfi1->regs))) {
|
2017-06-28 18:11:07 +03:00
|
|
|
for (i = 0; i < CFI_NUM_REGS; i++) {
|
2020-03-25 16:04:45 +03:00
|
|
|
if (!memcmp(&cfi1->regs[i], &cfi2->regs[i],
|
2017-06-28 18:11:07 +03:00
|
|
|
sizeof(struct cfi_reg)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
WARN_FUNC("stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
|
|
|
|
insn->sec, insn->offset,
|
2020-03-25 16:04:45 +03:00
|
|
|
i, cfi1->regs[i].base, cfi1->regs[i].offset,
|
|
|
|
i, cfi2->regs[i].base, cfi2->regs[i].offset);
|
2017-06-28 18:11:07 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
} else if (cfi1->type != cfi2->type) {
|
|
|
|
|
2017-07-11 18:33:42 +03:00
|
|
|
WARN_FUNC("stack state mismatch: type1=%d type2=%d",
|
2020-03-25 16:04:45 +03:00
|
|
|
insn->sec, insn->offset, cfi1->type, cfi2->type);
|
|
|
|
|
|
|
|
} else if (cfi1->drap != cfi2->drap ||
|
|
|
|
(cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) ||
|
|
|
|
(cfi1->drap && cfi1->drap_offset != cfi2->drap_offset)) {
|
2017-07-11 18:33:42 +03:00
|
|
|
|
objtool: Track DRAP separately from callee-saved registers
When GCC realigns a function's stack, it sometimes uses %r13 as the DRAP
register, like:
push %r13
lea 0x10(%rsp), %r13
and $0xfffffffffffffff0, %rsp
pushq -0x8(%r13)
push %rbp
mov %rsp, %rbp
push %r13
...
mov -0x8(%rbp),%r13
leaveq
lea -0x10(%r13), %rsp
pop %r13
retq
Since %r13 was pushed onto the stack twice, its two stack locations need
to be stored separately. The first push of %r13 is its original value,
and the second push of %r13 is the caller's stack frame address.
Since %r13 is a callee-saved register, we need to track the stack
location of its original value separately from the DRAP register.
This fixes the following false positive warning:
lib/ubsan.o: warning: objtool: val_to_string.constprop.7()+0x97: leave instruction with modified stack frame
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: baa41469a7b9 ("objtool: Implement stack validation 2.0")
Link: http://lkml.kernel.org/r/3da23a6d4c5b3c1e21fc2ccc21a73941b97ff20a.1502401017.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-08-11 00:37:26 +03:00
|
|
|
WARN_FUNC("stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
|
2017-06-28 18:11:07 +03:00
|
|
|
insn->sec, insn->offset,
|
2020-03-25 16:04:45 +03:00
|
|
|
cfi1->drap, cfi1->drap_reg, cfi1->drap_offset,
|
|
|
|
cfi2->drap, cfi2->drap_reg, cfi2->drap_offset);
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
} else
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2019-02-25 14:50:09 +03:00
|
|
|
static inline bool func_uaccess_safe(struct symbol *func)
|
|
|
|
{
|
|
|
|
if (func)
|
2019-07-18 04:36:48 +03:00
|
|
|
return func->uaccess_safe;
|
2019-02-25 14:50:09 +03:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-18 04:36:52 +03:00
|
|
|
static inline const char *call_dest_name(struct instruction *insn)
|
2019-02-25 14:50:09 +03:00
|
|
|
{
|
2021-06-24 12:41:23 +03:00
|
|
|
static char pvname[16];
|
|
|
|
struct reloc *rel;
|
|
|
|
int idx;
|
|
|
|
|
2019-02-25 14:50:09 +03:00
|
|
|
if (insn->call_dest)
|
|
|
|
return insn->call_dest->name;
|
|
|
|
|
2021-06-24 12:41:23 +03:00
|
|
|
rel = insn_reloc(NULL, insn);
|
|
|
|
if (rel && !strcmp(rel->sym->name, "pv_ops")) {
|
|
|
|
idx = (rel->addend / sizeof(void *));
|
|
|
|
snprintf(pvname, sizeof(pvname), "pv_ops[%d]", idx);
|
|
|
|
return pvname;
|
|
|
|
}
|
|
|
|
|
2019-02-25 14:50:09 +03:00
|
|
|
return "{dynamic}";
|
|
|
|
}
|
|
|
|
|
2021-06-24 12:41:23 +03:00
|
|
|
static bool pv_call_dest(struct objtool_file *file, struct instruction *insn)
|
|
|
|
{
|
|
|
|
struct symbol *target;
|
|
|
|
struct reloc *rel;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
rel = insn_reloc(file, insn);
|
|
|
|
if (!rel || strcmp(rel->sym->name, "pv_ops"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
idx = (arch_dest_reloc_offset(rel->addend) / sizeof(void *));
|
|
|
|
|
|
|
|
if (file->pv_ops[idx].clean)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
file->pv_ops[idx].clean = true;
|
|
|
|
|
|
|
|
list_for_each_entry(target, &file->pv_ops[idx].targets, pv_target) {
|
|
|
|
if (!target->sec->noinstr) {
|
|
|
|
WARN("pv_ops[%d]: %s", idx, target->name);
|
|
|
|
file->pv_ops[idx].clean = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return file->pv_ops[idx].clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool noinstr_call_dest(struct objtool_file *file,
|
|
|
|
struct instruction *insn,
|
|
|
|
struct symbol *func)
|
2020-06-03 21:09:06 +03:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We can't deal with indirect function calls at present;
|
|
|
|
* assume they're instrumented.
|
|
|
|
*/
|
2021-06-24 12:41:23 +03:00
|
|
|
if (!func) {
|
|
|
|
if (file->pv_ops)
|
|
|
|
return pv_call_dest(file, insn);
|
|
|
|
|
2020-06-03 21:09:06 +03:00
|
|
|
return false;
|
2021-06-24 12:41:23 +03:00
|
|
|
}
|
2020-06-03 21:09:06 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the symbol is from a noinstr section; we good.
|
|
|
|
*/
|
|
|
|
if (func->sec->noinstr)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The __ubsan_handle_*() calls are like WARN(), they only happen when
|
|
|
|
* something 'BAD' happened. At the risk of taking the machine down,
|
|
|
|
* let them proceed to get the message out.
|
|
|
|
*/
|
|
|
|
if (!strncmp(func->name, "__ubsan_handle_", 15))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-06-24 12:41:23 +03:00
|
|
|
static int validate_call(struct objtool_file *file,
|
|
|
|
struct instruction *insn,
|
|
|
|
struct insn_state *state)
|
2019-02-25 14:50:09 +03:00
|
|
|
{
|
2020-03-10 20:57:41 +03:00
|
|
|
if (state->noinstr && state->instr <= 0 &&
|
2021-06-24 12:41:23 +03:00
|
|
|
!noinstr_call_dest(file, insn, insn->call_dest)) {
|
2020-03-10 20:57:41 +03:00
|
|
|
WARN_FUNC("call to %s() leaves .noinstr.text section",
|
|
|
|
insn->sec, insn->offset, call_dest_name(insn));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-02-25 14:50:09 +03:00
|
|
|
if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
|
|
|
|
WARN_FUNC("call to %s() with UACCESS enabled",
|
2019-07-18 04:36:52 +03:00
|
|
|
insn->sec, insn->offset, call_dest_name(insn));
|
2019-02-25 14:50:09 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-02-25 13:10:55 +03:00
|
|
|
if (state->df) {
|
|
|
|
WARN_FUNC("call to %s() with DF set",
|
2019-07-18 04:36:52 +03:00
|
|
|
insn->sec, insn->offset, call_dest_name(insn));
|
2019-02-25 13:10:55 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-02-25 14:50:09 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-06-24 12:41:23 +03:00
|
|
|
static int validate_sibling_call(struct objtool_file *file,
|
|
|
|
struct instruction *insn,
|
|
|
|
struct insn_state *state)
|
2019-03-06 14:58:15 +03:00
|
|
|
{
|
2020-04-01 17:38:19 +03:00
|
|
|
if (has_modified_stack_frame(insn, state)) {
|
2019-03-06 14:58:15 +03:00
|
|
|
WARN_FUNC("sibling call from callable instruction with modified stack frame",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-06-24 12:41:23 +03:00
|
|
|
return validate_call(file, insn, state);
|
2019-03-06 14:58:15 +03:00
|
|
|
}
|
|
|
|
|
2020-03-10 20:07:44 +03:00
|
|
|
static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
|
|
|
|
{
|
2020-03-10 20:57:41 +03:00
|
|
|
if (state->noinstr && state->instr > 0) {
|
|
|
|
WARN_FUNC("return with instrumentation enabled",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-03-10 20:07:44 +03:00
|
|
|
if (state->uaccess && !func_uaccess_safe(func)) {
|
|
|
|
WARN_FUNC("return with UACCESS enabled",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!state->uaccess && func_uaccess_safe(func)) {
|
|
|
|
WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->df) {
|
|
|
|
WARN_FUNC("return with DF set",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-04-01 17:38:19 +03:00
|
|
|
if (func && has_modified_stack_frame(insn, state)) {
|
2020-03-10 20:07:44 +03:00
|
|
|
WARN_FUNC("return with modified stack frame",
|
|
|
|
insn->sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:04:45 +03:00
|
|
|
if (state->cfi.bp_scratch) {
|
2020-04-01 21:23:29 +03:00
|
|
|
WARN_FUNC("BP used as a scratch register",
|
|
|
|
insn->sec, insn->offset);
|
2020-03-10 20:07:44 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
static struct instruction *next_insn_to_validate(struct objtool_file *file,
|
|
|
|
struct instruction *insn)
|
2020-04-28 20:37:01 +03:00
|
|
|
{
|
2020-12-18 23:19:32 +03:00
|
|
|
struct alt_group *alt_group = insn->alt_group;
|
2020-04-28 20:37:01 +03:00
|
|
|
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
/*
|
|
|
|
* Simulate the fact that alternatives are patched in-place. When the
|
|
|
|
* end of a replacement alt_group is reached, redirect objtool flow to
|
|
|
|
* the end of the original alt_group.
|
|
|
|
*/
|
|
|
|
if (alt_group && insn == alt_group->last_insn && alt_group->orig_group)
|
|
|
|
return next_insn_same_sec(file, alt_group->orig_group->last_insn);
|
|
|
|
|
|
|
|
return next_insn_same_sec(file, insn);
|
2020-04-28 20:37:01 +03:00
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
/*
|
|
|
|
* Follow the branch starting at the given instruction, and recursively follow
|
|
|
|
* any other branches (jumps). Meanwhile, track the frame pointer state at
|
|
|
|
* each instruction and validate all the rules described in
|
|
|
|
* tools/objtool/Documentation/stack-validation.txt.
|
|
|
|
*/
|
2019-07-18 04:36:47 +03:00
|
|
|
static int validate_branch(struct objtool_file *file, struct symbol *func,
|
2020-04-02 11:15:51 +03:00
|
|
|
struct instruction *insn, struct insn_state state)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
|
|
|
struct alternative *alt;
|
2021-06-24 12:41:01 +03:00
|
|
|
struct instruction *next_insn, *prev_insn = NULL;
|
2017-06-28 18:11:05 +03:00
|
|
|
struct section *sec;
|
2019-07-25 01:47:26 +03:00
|
|
|
u8 visited;
|
2017-06-28 18:11:05 +03:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
sec = insn->sec;
|
|
|
|
|
|
|
|
while (1) {
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
next_insn = next_insn_to_validate(file, insn);
|
2017-07-11 18:33:43 +03:00
|
|
|
|
2018-05-10 06:39:15 +03:00
|
|
|
if (file->c_file && func && insn->func && func != insn->func->pfunc) {
|
2017-08-11 20:24:15 +03:00
|
|
|
WARN("%s() falls through to next function %s()",
|
|
|
|
func->name, insn->func->name);
|
|
|
|
return 1;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2017-07-07 17:19:42 +03:00
|
|
|
if (func && insn->ignore) {
|
|
|
|
WARN_FUNC("BUG: why am I validating an ignored function?",
|
|
|
|
sec, insn->offset);
|
2017-08-11 00:37:25 +03:00
|
|
|
return 1;
|
2017-07-07 17:19:42 +03:00
|
|
|
}
|
|
|
|
|
2019-07-25 01:47:26 +03:00
|
|
|
visited = 1 << state.uaccess;
|
2017-06-28 18:11:05 +03:00
|
|
|
if (insn->visited) {
|
2020-03-25 16:04:45 +03:00
|
|
|
if (!insn->hint && !insn_cfi_match(insn, &state.cfi))
|
2017-06-28 18:11:05 +03:00
|
|
|
return 1;
|
|
|
|
|
2019-07-25 01:47:26 +03:00
|
|
|
if (insn->visited & visited)
|
2019-02-25 14:50:09 +03:00
|
|
|
return 0;
|
2021-06-24 12:41:01 +03:00
|
|
|
} else {
|
|
|
|
nr_insns_visited++;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2020-03-10 20:57:41 +03:00
|
|
|
if (state.noinstr)
|
|
|
|
state.instr += insn->instr;
|
|
|
|
|
2021-06-24 12:41:01 +03:00
|
|
|
if (insn->hint) {
|
|
|
|
state.cfi = *insn->cfi;
|
|
|
|
} else {
|
|
|
|
/* XXX track if we actually changed state.cfi */
|
|
|
|
|
|
|
|
if (prev_insn && !cficmp(prev_insn->cfi, &state.cfi)) {
|
|
|
|
insn->cfi = prev_insn->cfi;
|
|
|
|
nr_cfi_reused++;
|
|
|
|
} else {
|
|
|
|
insn->cfi = cfi_hash_find_or_add(&state.cfi);
|
|
|
|
}
|
|
|
|
}
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2019-07-25 01:47:26 +03:00
|
|
|
insn->visited |= visited;
|
2017-06-28 18:11:07 +03:00
|
|
|
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 23:26:21 +03:00
|
|
|
if (propagate_alt_cfi(file, insn))
|
|
|
|
return 1;
|
|
|
|
|
2020-04-28 20:37:01 +03:00
|
|
|
if (!insn->ignore_alts && !list_empty(&insn->alts)) {
|
2019-03-01 13:19:03 +03:00
|
|
|
bool skip_orig = false;
|
|
|
|
|
2018-01-30 07:00:39 +03:00
|
|
|
list_for_each_entry(alt, &insn->alts, list) {
|
2019-03-01 13:19:03 +03:00
|
|
|
if (alt->skip_orig)
|
|
|
|
skip_orig = true;
|
|
|
|
|
2019-07-18 04:36:47 +03:00
|
|
|
ret = validate_branch(file, func, alt->insn, state);
|
objtool: Add --backtrace support
For when you want to know the path that reached your fail state:
$ ./objtool check --no-fp --backtrace arch/x86/lib/usercopy_64.o
arch/x86/lib/usercopy_64.o: warning: objtool: .altinstr_replacement+0x3: UACCESS disable without MEMOPs: __clear_user()
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x3a: (alt)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x2e: (branch)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x18: (branch)
arch/x86/lib/usercopy_64.o: warning: objtool: .altinstr_replacement+0xffffffffffffffff: (branch)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x5: (alt)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x0: <=== (func)
0000000000000000 <__clear_user>:
0: e8 00 00 00 00 callq 5 <__clear_user+0x5>
1: R_X86_64_PLT32 __fentry__-0x4
5: 90 nop
6: 90 nop
7: 90 nop
8: 48 89 f0 mov %rsi,%rax
b: 48 c1 ee 03 shr $0x3,%rsi
f: 83 e0 07 and $0x7,%eax
12: 48 89 f1 mov %rsi,%rcx
15: 48 85 c9 test %rcx,%rcx
18: 74 0f je 29 <__clear_user+0x29>
1a: 48 c7 07 00 00 00 00 movq $0x0,(%rdi)
21: 48 83 c7 08 add $0x8,%rdi
25: ff c9 dec %ecx
27: 75 f1 jne 1a <__clear_user+0x1a>
29: 48 89 c1 mov %rax,%rcx
2c: 85 c9 test %ecx,%ecx
2e: 74 0a je 3a <__clear_user+0x3a>
30: c6 07 00 movb $0x0,(%rdi)
33: 48 ff c7 inc %rdi
36: ff c9 dec %ecx
38: 75 f6 jne 30 <__clear_user+0x30>
3a: 90 nop
3b: 90 nop
3c: 90 nop
3d: 48 89 c8 mov %rcx,%rax
40: c3 retq
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2019-03-01 13:15:49 +03:00
|
|
|
if (ret) {
|
|
|
|
if (backtrace)
|
|
|
|
BT_FUNC("(alt)", insn);
|
|
|
|
return ret;
|
|
|
|
}
|
2018-01-30 07:00:39 +03:00
|
|
|
}
|
2019-03-01 13:19:03 +03:00
|
|
|
|
|
|
|
if (skip_orig)
|
|
|
|
return 0;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2021-02-11 15:03:28 +03:00
|
|
|
if (handle_insn_ops(insn, next_insn, &state))
|
2020-04-24 17:16:41 +03:00
|
|
|
return 1;
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
switch (insn->type) {
|
|
|
|
|
|
|
|
case INSN_RETURN:
|
2020-03-10 20:07:44 +03:00
|
|
|
return validate_return(func, insn, &state);
|
2017-06-28 18:11:05 +03:00
|
|
|
|
|
|
|
case INSN_CALL:
|
2019-02-25 14:50:09 +03:00
|
|
|
case INSN_CALL_DYNAMIC:
|
2021-06-24 12:41:23 +03:00
|
|
|
ret = validate_call(file, insn, &state);
|
2019-02-25 14:50:09 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2019-07-18 04:36:51 +03:00
|
|
|
if (!no_fp && func && !is_fentry_call(insn) &&
|
|
|
|
!has_valid_stack_frame(&state)) {
|
2017-06-28 18:11:05 +03:00
|
|
|
WARN_FUNC("call without frame pointer save/setup",
|
|
|
|
sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
2019-07-18 04:36:51 +03:00
|
|
|
|
|
|
|
if (dead_end_function(file, insn->call_dest))
|
|
|
|
return 0;
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case INSN_JUMP_CONDITIONAL:
|
|
|
|
case INSN_JUMP_UNCONDITIONAL:
|
2021-01-22 00:29:22 +03:00
|
|
|
if (is_sibling_call(insn)) {
|
2021-06-24 12:41:23 +03:00
|
|
|
ret = validate_sibling_call(file, insn, &state);
|
2017-06-28 18:11:05 +03:00
|
|
|
if (ret)
|
2019-03-06 14:58:15 +03:00
|
|
|
return ret;
|
2017-07-07 17:19:42 +03:00
|
|
|
|
2019-07-18 04:36:52 +03:00
|
|
|
} else if (insn->jump_dest) {
|
2019-07-18 04:36:47 +03:00
|
|
|
ret = validate_branch(file, func,
|
|
|
|
insn->jump_dest, state);
|
objtool: Add --backtrace support
For when you want to know the path that reached your fail state:
$ ./objtool check --no-fp --backtrace arch/x86/lib/usercopy_64.o
arch/x86/lib/usercopy_64.o: warning: objtool: .altinstr_replacement+0x3: UACCESS disable without MEMOPs: __clear_user()
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x3a: (alt)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x2e: (branch)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x18: (branch)
arch/x86/lib/usercopy_64.o: warning: objtool: .altinstr_replacement+0xffffffffffffffff: (branch)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x5: (alt)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x0: <=== (func)
0000000000000000 <__clear_user>:
0: e8 00 00 00 00 callq 5 <__clear_user+0x5>
1: R_X86_64_PLT32 __fentry__-0x4
5: 90 nop
6: 90 nop
7: 90 nop
8: 48 89 f0 mov %rsi,%rax
b: 48 c1 ee 03 shr $0x3,%rsi
f: 83 e0 07 and $0x7,%eax
12: 48 89 f1 mov %rsi,%rcx
15: 48 85 c9 test %rcx,%rcx
18: 74 0f je 29 <__clear_user+0x29>
1a: 48 c7 07 00 00 00 00 movq $0x0,(%rdi)
21: 48 83 c7 08 add $0x8,%rdi
25: ff c9 dec %ecx
27: 75 f1 jne 1a <__clear_user+0x1a>
29: 48 89 c1 mov %rax,%rcx
2c: 85 c9 test %ecx,%ecx
2e: 74 0a je 3a <__clear_user+0x3a>
30: c6 07 00 movb $0x0,(%rdi)
33: 48 ff c7 inc %rdi
36: ff c9 dec %ecx
38: 75 f6 jne 30 <__clear_user+0x30>
3a: 90 nop
3b: 90 nop
3c: 90 nop
3d: 48 89 c8 mov %rcx,%rax
40: c3 retq
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2019-03-01 13:15:49 +03:00
|
|
|
if (ret) {
|
|
|
|
if (backtrace)
|
|
|
|
BT_FUNC("(branch)", insn);
|
|
|
|
return ret;
|
|
|
|
}
|
2017-07-07 17:19:42 +03:00
|
|
|
}
|
2017-06-28 18:11:05 +03:00
|
|
|
|
|
|
|
if (insn->type == INSN_JUMP_UNCONDITIONAL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case INSN_JUMP_DYNAMIC:
|
2019-07-18 04:36:57 +03:00
|
|
|
case INSN_JUMP_DYNAMIC_CONDITIONAL:
|
2021-01-22 00:29:22 +03:00
|
|
|
if (is_sibling_call(insn)) {
|
2021-06-24 12:41:23 +03:00
|
|
|
ret = validate_sibling_call(file, insn, &state);
|
2019-03-06 14:58:15 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2019-07-18 04:36:57 +03:00
|
|
|
if (insn->type == INSN_JUMP_DYNAMIC)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
break;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2017-07-11 18:33:43 +03:00
|
|
|
case INSN_CONTEXT_SWITCH:
|
|
|
|
if (func && (!next_insn || !next_insn->hint)) {
|
|
|
|
WARN_FUNC("unsupported instruction in callable function",
|
|
|
|
sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
2019-02-25 14:50:09 +03:00
|
|
|
case INSN_STAC:
|
|
|
|
if (state.uaccess) {
|
|
|
|
WARN_FUNC("recursive UACCESS enable", sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
state.uaccess = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case INSN_CLAC:
|
2019-07-18 04:36:47 +03:00
|
|
|
if (!state.uaccess && func) {
|
2019-02-25 14:50:09 +03:00
|
|
|
WARN_FUNC("redundant UACCESS disable", sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (func_uaccess_safe(func) && !state.uaccess_stack) {
|
|
|
|
WARN_FUNC("UACCESS-safe disables UACCESS", sec, insn->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
state.uaccess = false;
|
2017-06-28 18:11:07 +03:00
|
|
|
break;
|
|
|
|
|
2019-02-25 13:10:55 +03:00
|
|
|
case INSN_STD:
|
2021-01-22 00:29:17 +03:00
|
|
|
if (state.df) {
|
2019-02-25 13:10:55 +03:00
|
|
|
WARN_FUNC("recursive STD", sec, insn->offset);
|
2021-01-22 00:29:17 +03:00
|
|
|
return 1;
|
|
|
|
}
|
2019-02-25 13:10:55 +03:00
|
|
|
|
|
|
|
state.df = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case INSN_CLD:
|
2021-01-22 00:29:17 +03:00
|
|
|
if (!state.df && func) {
|
2019-02-25 13:10:55 +03:00
|
|
|
WARN_FUNC("redundant CLD", sec, insn->offset);
|
2021-01-22 00:29:17 +03:00
|
|
|
return 1;
|
|
|
|
}
|
2019-02-25 13:10:55 +03:00
|
|
|
|
|
|
|
state.df = false;
|
2017-06-28 18:11:07 +03:00
|
|
|
break;
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (insn->dead_end)
|
|
|
|
return 0;
|
|
|
|
|
2017-09-19 05:43:30 +03:00
|
|
|
if (!next_insn) {
|
2020-03-25 16:04:45 +03:00
|
|
|
if (state.cfi.cfa.base == CFI_UNDEFINED)
|
2017-09-19 05:43:30 +03:00
|
|
|
return 0;
|
2017-06-28 18:11:05 +03:00
|
|
|
WARN("%s: unexpected end of section", sec->name);
|
|
|
|
return 1;
|
|
|
|
}
|
2017-09-19 05:43:30 +03:00
|
|
|
|
2021-06-24 12:41:01 +03:00
|
|
|
prev_insn = insn;
|
2017-09-19 05:43:30 +03:00
|
|
|
insn = next_insn;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-23 20:26:03 +03:00
|
|
|
static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
|
2017-07-11 18:33:43 +03:00
|
|
|
{
|
|
|
|
struct instruction *insn;
|
|
|
|
struct insn_state state;
|
2020-03-23 20:26:03 +03:00
|
|
|
int ret, warnings = 0;
|
2017-07-11 18:33:43 +03:00
|
|
|
|
|
|
|
if (!file->hints)
|
|
|
|
return 0;
|
|
|
|
|
2020-03-23 20:26:03 +03:00
|
|
|
init_insn_state(&state, sec);
|
2017-07-11 18:33:43 +03:00
|
|
|
|
2020-03-23 20:26:03 +03:00
|
|
|
if (sec) {
|
|
|
|
insn = find_insn(file, sec, 0);
|
|
|
|
if (!insn)
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
insn = list_first_entry(&file->insn_list, typeof(*insn), list);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (&insn->list != &file->insn_list && (!sec || insn->sec == sec)) {
|
2021-09-14 17:41:23 +03:00
|
|
|
if (insn->hint && !insn->visited && !insn->ignore) {
|
2019-07-18 04:36:47 +03:00
|
|
|
ret = validate_branch(file, insn->func, insn, state);
|
objtool: Add --backtrace support
For when you want to know the path that reached your fail state:
$ ./objtool check --no-fp --backtrace arch/x86/lib/usercopy_64.o
arch/x86/lib/usercopy_64.o: warning: objtool: .altinstr_replacement+0x3: UACCESS disable without MEMOPs: __clear_user()
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x3a: (alt)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x2e: (branch)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x18: (branch)
arch/x86/lib/usercopy_64.o: warning: objtool: .altinstr_replacement+0xffffffffffffffff: (branch)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x5: (alt)
arch/x86/lib/usercopy_64.o: warning: objtool: __clear_user()+0x0: <=== (func)
0000000000000000 <__clear_user>:
0: e8 00 00 00 00 callq 5 <__clear_user+0x5>
1: R_X86_64_PLT32 __fentry__-0x4
5: 90 nop
6: 90 nop
7: 90 nop
8: 48 89 f0 mov %rsi,%rax
b: 48 c1 ee 03 shr $0x3,%rsi
f: 83 e0 07 and $0x7,%eax
12: 48 89 f1 mov %rsi,%rcx
15: 48 85 c9 test %rcx,%rcx
18: 74 0f je 29 <__clear_user+0x29>
1a: 48 c7 07 00 00 00 00 movq $0x0,(%rdi)
21: 48 83 c7 08 add $0x8,%rdi
25: ff c9 dec %ecx
27: 75 f1 jne 1a <__clear_user+0x1a>
29: 48 89 c1 mov %rax,%rcx
2c: 85 c9 test %ecx,%ecx
2e: 74 0a je 3a <__clear_user+0x3a>
30: c6 07 00 movb $0x0,(%rdi)
33: 48 ff c7 inc %rdi
36: ff c9 dec %ecx
38: 75 f6 jne 30 <__clear_user+0x30>
3a: 90 nop
3b: 90 nop
3c: 90 nop
3d: 48 89 c8 mov %rcx,%rax
40: c3 retq
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2019-03-01 13:15:49 +03:00
|
|
|
if (ret && backtrace)
|
|
|
|
BT_FUNC("<=== (hint)", insn);
|
2017-07-11 18:33:43 +03:00
|
|
|
warnings += ret;
|
|
|
|
}
|
2020-03-23 20:26:03 +03:00
|
|
|
|
|
|
|
insn = list_next_entry(insn, list);
|
2017-07-11 18:33:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return warnings;
|
|
|
|
}
|
|
|
|
|
2018-01-16 12:24:06 +03:00
|
|
|
static int validate_retpoline(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct instruction *insn;
|
|
|
|
int warnings = 0;
|
|
|
|
|
|
|
|
for_each_insn(file, insn) {
|
|
|
|
if (insn->type != INSN_JUMP_DYNAMIC &&
|
|
|
|
insn->type != INSN_CALL_DYNAMIC)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (insn->retpoline_safe)
|
|
|
|
continue;
|
|
|
|
|
2018-01-31 12:18:28 +03:00
|
|
|
/*
|
|
|
|
* .init.text code is ran before userspace and thus doesn't
|
|
|
|
* strictly need retpolines, except for modules which are
|
|
|
|
* loaded late, they very much do need retpoline in their
|
|
|
|
* .init.text
|
|
|
|
*/
|
|
|
|
if (!strcmp(insn->sec->name, ".init.text") && !module)
|
|
|
|
continue;
|
|
|
|
|
2018-01-16 12:24:06 +03:00
|
|
|
WARN_FUNC("indirect %s found in RETPOLINE build",
|
|
|
|
insn->sec, insn->offset,
|
|
|
|
insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call");
|
|
|
|
|
|
|
|
warnings++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return warnings;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
static bool is_kasan_insn(struct instruction *insn)
|
|
|
|
{
|
|
|
|
return (insn->type == INSN_CALL &&
|
|
|
|
!strcmp(insn->call_dest->name, "__asan_handle_no_return"));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_ubsan_insn(struct instruction *insn)
|
|
|
|
{
|
|
|
|
return (insn->type == INSN_CALL &&
|
|
|
|
!strcmp(insn->call_dest->name,
|
|
|
|
"__ubsan_handle_builtin_unreachable"));
|
|
|
|
}
|
|
|
|
|
2020-09-19 09:41:18 +03:00
|
|
|
static bool ignore_unreachable_insn(struct objtool_file *file, struct instruction *insn)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
|
|
|
int i;
|
2020-09-19 09:41:18 +03:00
|
|
|
struct instruction *prev_insn;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
if (insn->ignore || insn->type == INSN_NOP)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignore any unused exceptions. This can happen when a whitelisted
|
|
|
|
* function has an exception table entry.
|
2017-07-27 23:56:54 +03:00
|
|
|
*
|
|
|
|
* Also ignore alternative replacement instructions. This can happen
|
|
|
|
* when a whitelisted function uses one of the ALTERNATIVE macros.
|
2017-06-28 18:11:07 +03:00
|
|
|
*/
|
2017-07-27 23:56:54 +03:00
|
|
|
if (!strcmp(insn->sec->name, ".fixup") ||
|
|
|
|
!strcmp(insn->sec->name, ".altinstr_replacement") ||
|
|
|
|
!strcmp(insn->sec->name, ".altinstr_aux"))
|
2017-06-28 18:11:05 +03:00
|
|
|
return true;
|
|
|
|
|
2020-04-01 21:23:25 +03:00
|
|
|
if (!insn->func)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CONFIG_UBSAN_TRAP inserts a UD2 when it sees
|
|
|
|
* __builtin_unreachable(). The BUG() macro has an unreachable() after
|
|
|
|
* the UD2, which causes GCC's undefined trap logic to emit another UD2
|
|
|
|
* (or occasionally a JMP to UD2).
|
2020-09-19 09:41:18 +03:00
|
|
|
*
|
|
|
|
* It may also insert a UD2 after calling a __noreturn function.
|
2020-04-01 21:23:25 +03:00
|
|
|
*/
|
2020-09-19 09:41:18 +03:00
|
|
|
prev_insn = list_prev_entry(insn, list);
|
|
|
|
if ((prev_insn->dead_end || dead_end_function(file, prev_insn->call_dest)) &&
|
2020-04-01 21:23:25 +03:00
|
|
|
(insn->type == INSN_BUG ||
|
|
|
|
(insn->type == INSN_JUMP_UNCONDITIONAL &&
|
|
|
|
insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
|
|
|
|
return true;
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
/*
|
|
|
|
* Check if this (or a subsequent) instruction is related to
|
|
|
|
* CONFIG_UBSAN or CONFIG_KASAN.
|
|
|
|
*
|
|
|
|
* End the search at 5 instructions to avoid going into the weeds.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
|
|
|
|
|
|
if (is_kasan_insn(insn) || is_ubsan_insn(insn))
|
|
|
|
return true;
|
|
|
|
|
2018-02-09 02:09:25 +03:00
|
|
|
if (insn->type == INSN_JUMP_UNCONDITIONAL) {
|
|
|
|
if (insn->jump_dest &&
|
|
|
|
insn->jump_dest->func == insn->func) {
|
|
|
|
insn = insn->jump_dest;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
if (insn->offset + insn->len >= insn->func->offset + insn->func->len)
|
2017-06-28 18:11:05 +03:00
|
|
|
break;
|
2018-02-09 02:09:25 +03:00
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
insn = list_next_entry(insn, list);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-23 23:17:50 +03:00
|
|
|
static int validate_symbol(struct objtool_file *file, struct section *sec,
|
|
|
|
struct symbol *sym, struct insn_state *state)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
|
|
|
struct instruction *insn;
|
2020-03-23 23:17:50 +03:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!sym->len) {
|
|
|
|
WARN("%s() is missing an ELF size annotation", sym->name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sym->pfunc != sym || sym->alias != sym)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
insn = find_insn(file, sec, sym->offset);
|
|
|
|
if (!insn || insn->ignore || insn->visited)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
state->uaccess = sym->uaccess_safe;
|
|
|
|
|
|
|
|
ret = validate_branch(file, insn->func, insn, *state);
|
|
|
|
if (ret && backtrace)
|
|
|
|
BT_FUNC("<=== (sym)", insn);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int validate_section(struct objtool_file *file, struct section *sec)
|
|
|
|
{
|
2017-06-28 18:11:07 +03:00
|
|
|
struct insn_state state;
|
2020-03-23 23:17:50 +03:00
|
|
|
struct symbol *func;
|
|
|
|
int warnings = 0;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
2020-03-23 22:57:13 +03:00
|
|
|
list_for_each_entry(func, &sec->symbol_list, list) {
|
|
|
|
if (func->type != STT_FUNC)
|
|
|
|
continue;
|
2019-07-18 04:36:48 +03:00
|
|
|
|
2020-03-23 20:26:03 +03:00
|
|
|
init_insn_state(&state, sec);
|
2021-01-22 00:29:24 +03:00
|
|
|
set_func_state(&state.cfi);
|
2020-03-27 18:28:40 +03:00
|
|
|
|
2020-03-23 23:17:50 +03:00
|
|
|
warnings += validate_symbol(file, sec, func, &state);
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return warnings;
|
|
|
|
}
|
|
|
|
|
2020-03-10 20:57:41 +03:00
|
|
|
static int validate_vmlinux_functions(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct section *sec;
|
2020-03-23 20:26:03 +03:00
|
|
|
int warnings = 0;
|
2020-03-10 20:57:41 +03:00
|
|
|
|
|
|
|
sec = find_section_by_name(file->elf, ".noinstr.text");
|
2020-03-25 19:18:17 +03:00
|
|
|
if (sec) {
|
|
|
|
warnings += validate_section(file, sec);
|
|
|
|
warnings += validate_unwind_hints(file, sec);
|
|
|
|
}
|
2020-03-10 20:57:41 +03:00
|
|
|
|
2020-03-25 19:18:17 +03:00
|
|
|
sec = find_section_by_name(file->elf, ".entry.text");
|
|
|
|
if (sec) {
|
|
|
|
warnings += validate_section(file, sec);
|
|
|
|
warnings += validate_unwind_hints(file, sec);
|
|
|
|
}
|
2020-03-23 20:26:03 +03:00
|
|
|
|
|
|
|
return warnings;
|
2020-03-10 20:57:41 +03:00
|
|
|
}
|
|
|
|
|
2020-03-23 22:57:13 +03:00
|
|
|
static int validate_functions(struct objtool_file *file)
|
|
|
|
{
|
|
|
|
struct section *sec;
|
|
|
|
int warnings = 0;
|
|
|
|
|
2020-03-23 23:11:14 +03:00
|
|
|
for_each_sec(file, sec) {
|
|
|
|
if (!(sec->sh.sh_flags & SHF_EXECINSTR))
|
|
|
|
continue;
|
|
|
|
|
2020-03-23 22:57:13 +03:00
|
|
|
warnings += validate_section(file, sec);
|
2020-03-23 23:11:14 +03:00
|
|
|
}
|
2020-03-23 22:57:13 +03:00
|
|
|
|
|
|
|
return warnings;
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
static int validate_reachable_instructions(struct objtool_file *file)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
|
|
|
struct instruction *insn;
|
2017-06-28 18:11:07 +03:00
|
|
|
|
|
|
|
if (file->ignore_unreachables)
|
|
|
|
return 0;
|
2017-06-28 18:11:05 +03:00
|
|
|
|
|
|
|
for_each_insn(file, insn) {
|
2020-09-19 09:41:18 +03:00
|
|
|
if (insn->visited || ignore_unreachable_insn(file, insn))
|
2017-06-28 18:11:07 +03:00
|
|
|
continue;
|
|
|
|
|
|
|
|
WARN_FUNC("unreachable instruction", insn->sec, insn->offset);
|
|
|
|
return 1;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
return 0;
|
2017-06-28 18:11:05 +03:00
|
|
|
}
|
|
|
|
|
2020-08-25 15:47:40 +03:00
|
|
|
int check(struct objtool_file *file)
|
2017-06-28 18:11:05 +03:00
|
|
|
{
|
|
|
|
int ret, warnings = 0;
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
arch_initial_func_cfi_state(&initial_func_cfi);
|
2021-06-24 12:41:01 +03:00
|
|
|
init_cfi_state(&init_cfi);
|
|
|
|
init_cfi_state(&func_cfi);
|
|
|
|
set_func_state(&func_cfi);
|
|
|
|
|
|
|
|
if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3)))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
cfi_hash_add(&init_cfi);
|
|
|
|
cfi_hash_add(&func_cfi);
|
2017-06-28 18:11:07 +03:00
|
|
|
|
2020-08-25 15:47:39 +03:00
|
|
|
ret = decode_sections(file);
|
2017-06-28 18:11:05 +03:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2021-06-24 12:41:01 +03:00
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
warnings += ret;
|
|
|
|
|
2020-08-25 15:47:39 +03:00
|
|
|
if (list_empty(&file->insn_list))
|
2017-06-28 18:11:05 +03:00
|
|
|
goto out;
|
|
|
|
|
2020-03-10 20:57:41 +03:00
|
|
|
if (vmlinux && !validate_dup) {
|
2020-08-25 15:47:39 +03:00
|
|
|
ret = validate_vmlinux_functions(file);
|
2020-03-10 20:57:41 +03:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
warnings += ret;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-01-16 12:24:06 +03:00
|
|
|
if (retpoline) {
|
2020-08-25 15:47:39 +03:00
|
|
|
ret = validate_retpoline(file);
|
2018-01-16 12:24:06 +03:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
warnings += ret;
|
|
|
|
}
|
|
|
|
|
2020-08-25 15:47:39 +03:00
|
|
|
ret = validate_functions(file);
|
2017-06-28 18:11:05 +03:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
warnings += ret;
|
|
|
|
|
2020-08-25 15:47:39 +03:00
|
|
|
ret = validate_unwind_hints(file, NULL);
|
2017-07-11 18:33:43 +03:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
warnings += ret;
|
|
|
|
|
2017-06-28 18:11:07 +03:00
|
|
|
if (!warnings) {
|
2020-08-25 15:47:39 +03:00
|
|
|
ret = validate_reachable_instructions(file);
|
2017-06-28 18:11:07 +03:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
warnings += ret;
|
|
|
|
}
|
|
|
|
|
2020-08-25 15:47:39 +03:00
|
|
|
ret = create_static_call_sections(file);
|
2020-08-18 16:57:45 +03:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
warnings += ret;
|
|
|
|
|
2021-10-26 15:01:36 +03:00
|
|
|
if (retpoline) {
|
|
|
|
ret = create_retpoline_sites_sections(file);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
warnings += ret;
|
|
|
|
}
|
|
|
|
|
2020-08-07 01:14:09 +03:00
|
|
|
if (mcount) {
|
|
|
|
ret = create_mcount_loc_sections(file);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
warnings += ret;
|
|
|
|
}
|
|
|
|
|
2021-06-24 12:41:01 +03:00
|
|
|
if (stats) {
|
|
|
|
printf("nr_insns_visited: %ld\n", nr_insns_visited);
|
|
|
|
printf("nr_cfi: %ld\n", nr_cfi);
|
|
|
|
printf("nr_cfi_reused: %ld\n", nr_cfi_reused);
|
|
|
|
printf("nr_cfi_cache: %ld\n", nr_cfi_cache);
|
|
|
|
}
|
|
|
|
|
2017-06-28 18:11:05 +03:00
|
|
|
out:
|
2021-01-15 01:32:42 +03:00
|
|
|
/*
|
|
|
|
* For now, don't fail the kernel build on fatal warnings. These
|
|
|
|
* errors are still fairly common due to the growing matrix of
|
|
|
|
* supported toolchains and their recent pace of change.
|
|
|
|
*/
|
2017-06-28 18:11:05 +03:00
|
|
|
return 0;
|
|
|
|
}
|