3170 lines · 74.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> */ #include <string.h>#include <stdlib.h> #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> #include <linux/objtool.h>#include <linux/hashtable.h>#include <linux/kernel.h>#include <linux/static_call_types.h> struct alternative {	struct list_head list;	struct instruction *insn;	bool skip_orig;}; struct cfi_init_state initial_func_cfi; struct instruction *find_insn(struct objtool_file *file,			      struct section *sec, unsigned long offset){	struct instruction *insn; 	hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) {		if (insn->sec == sec && insn->offset == offset)			return insn;	} 	return NULL;} static struct instruction *next_insn_same_sec(struct objtool_file *file,					      struct instruction *insn){	struct instruction *next = list_next_entry(insn, list); 	if (!next || &next->list == &file->insn_list || next->sec != insn->sec)		return NULL; 	return next;} 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);} 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;} #define func_for_each_insn(file, func, insn)				\	for (insn = find_insn(file, func->sec, func->offset);		\	     insn;							\	     insn = next_insn_same_func(file, insn)) #define sym_for_each_insn(file, sym, insn)				\	for (insn = find_insn(file, sym->sec, sym->offset);		\	     insn && &insn->list != &file->insn_list &&			\		insn->sec == sym->sec &&				\		insn->offset < sym->offset + sym->len;			\	     insn = list_next_entry(insn, list)) #define sym_for_each_insn_continue_reverse(file, sym, insn)		\	for (insn = list_prev_entry(insn, list);			\	     &insn->list != &file->insn_list &&				\		insn->sec == sym->sec && insn->offset >= sym->offset;	\	     insn = list_prev_entry(insn, list)) #define sec_for_each_insn_from(file, insn)				\	for (; insn; insn = next_insn_same_sec(file, insn)) #define sec_for_each_insn_continue(file, insn)				\	for (insn = next_insn_same_sec(file, insn); insn;		\	     insn = next_insn_same_sec(file, insn)) 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;} static bool is_sibling_call(struct instruction *insn){	/*	 * 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; 	/* An indirect jump is either a sibling call or a jump to a table. */	if (insn->type == INSN_JUMP_DYNAMIC)		return !is_jump_table_jump(insn); 	/* add_jump_destinations() sets insn->call_dest for sibling calls. */	return (is_static_jump(insn) && insn->call_dest);} /* * 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. */static bool __dead_end_function(struct objtool_file *file, struct symbol *func,				int recursion){	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",		"usercopy_abort",		"machine_real_restart",		"rewind_stack_do_exit",		"kunit_try_catch_throw",		"xen_start_kernel",	}; 	if (!func)		return false; 	if (func->bind == STB_WEAK)		return false; 	if (func->bind == STB_GLOBAL)		for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)			if (!strcmp(func->name, global_noreturns[i]))				return true; 	if (!func->len)		return false; 	insn = find_insn(file, func->sec, func->offset);	if (!insn->func)		return false; 	func_for_each_insn(file, func, insn) {		empty = false; 		if (insn->type == INSN_RETURN)			return false;	} 	if (empty)		return false; 	/*	 * 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.	 */	func_for_each_insn(file, func, insn) {		if (is_sibling_call(insn)) {			struct instruction *dest = insn->jump_dest; 			if (!dest)				/* sibling call to another file */				return false; 			/* 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;			} 			return __dead_end_function(file, dest->func, recursion+1);		}	} 	return true;} static bool dead_end_function(struct objtool_file *file, struct symbol *func){	return __dead_end_function(file, func, 0);} static void init_cfi_state(struct cfi_state *cfi){	int i; 	for (i = 0; i < CFI_NUM_REGS; i++) {		cfi->regs[i].base = CFI_UNDEFINED;		cfi->vals[i].base = CFI_UNDEFINED;	}	cfi->cfa.base = CFI_UNDEFINED;	cfi->drap_reg = CFI_UNDEFINED;	cfi->drap_offset = -1;} static void init_insn_state(struct insn_state *state, struct section *sec){	memset(state, 0, sizeof(*state));	init_cfi_state(&state->cfi); 	/*	 * 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).	 */	if (vmlinux && noinstr && sec)		state->noinstr = sec->noinstr;} /* * 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;	unsigned long nr_insns = 0;	int ret; 	for_each_sec(file, sec) { 		if (!(sec->sh.sh_flags & SHF_EXECINSTR))			continue; 		if (strcmp(sec->name, ".altinstr_replacement") &&		    strcmp(sec->name, ".altinstr_aux") &&		    strncmp(sec->name, ".discard.", 9))			sec->text = true; 		if (!strcmp(sec->name, ".noinstr.text") ||		    !strcmp(sec->name, ".entry.text"))			sec->noinstr = true; 		for (offset = 0; offset < sec->len; offset += insn->len) {			insn = malloc(sizeof(*insn));			if (!insn) {				WARN("malloc failed");				return -1;			}			memset(insn, 0, sizeof(*insn));			INIT_LIST_HEAD(&insn->alts);			INIT_LIST_HEAD(&insn->stack_ops);			init_cfi_state(&insn->cfi); 			insn->sec = sec;			insn->offset = offset; 			ret = arch_decode_instruction(file->elf, sec, offset,						      sec->len - offset,						      &insn->len, &insn->type,						      &insn->immediate,						      &insn->stack_ops);			if (ret)				goto err; 			hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));			list_add_tail(&insn->list, &file->insn_list);			nr_insns++;		} 		list_for_each_entry(func, &sec->symbol_list, list) {			if (func->type != STT_FUNC || func->alias != func)				continue; 			if (!find_insn(file, sec, func->offset)) {				WARN("%s(): can't find starting instruction",				     func->name);				return -1;			} 			sym_for_each_insn(file, func, insn)				insn->func = func;		}	} 	if (stats)		printf("nr_insns: %lu\n", nr_insns); 	return 0; err:	free(insn);	return ret;} static struct instruction *find_last_insn(struct objtool_file *file,					  struct section *sec){	struct instruction *insn = NULL;	unsigned int offset;	unsigned int end = (sec->len > 10) ? sec->len - 10 : 0; 	for (offset = sec->len - 1; offset >= end && !insn; offset--)		insn = find_insn(file, sec, offset); 	return insn;} /* * Mark "ud2" instructions and manually annotated dead ends. */static int add_dead_ends(struct objtool_file *file){	struct section *sec;	struct reloc *reloc;	struct instruction *insn; 	/*	 * 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.	 */	sec = find_section_by_name(file->elf, ".rela.discard.unreachable");	if (!sec)		goto reachable; 	list_for_each_entry(reloc, &sec->reloc_list, list) {		if (reloc->sym->type != STT_SECTION) {			WARN("unexpected relocation symbol type in %s", sec->name);			return -1;		}		insn = find_insn(file, reloc->sym->sec, reloc->addend);		if (insn)			insn = list_prev_entry(insn, list);		else if (reloc->addend == reloc->sym->sec->len) {			insn = find_last_insn(file, reloc->sym->sec);			if (!insn) {				WARN("can't find unreachable insn at %s+0x%x",				     reloc->sym->sec->name, reloc->addend);				return -1;			}		} else {			WARN("can't find unreachable insn at %s+0x%x",			     reloc->sym->sec->name, reloc->addend);			return -1;		} 		insn->dead_end = true;	} 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; 	list_for_each_entry(reloc, &sec->reloc_list, list) {		if (reloc->sym->type != STT_SECTION) {			WARN("unexpected relocation symbol type in %s", sec->name);			return -1;		}		insn = find_insn(file, reloc->sym->sec, reloc->addend);		if (insn)			insn = list_prev_entry(insn, list);		else if (reloc->addend == reloc->sym->sec->len) {			insn = find_last_insn(file, reloc->sym->sec);			if (!insn) {				WARN("can't find reachable insn at %s+0x%x",				     reloc->sym->sec->name, reloc->addend);				return -1;			}		} else {			WARN("can't find reachable insn at %s+0x%x",			     reloc->sym->sec->name, reloc->addend);			return -1;		} 		insn->dead_end = false;	} 	return 0;} static int create_static_call_sections(struct objtool_file *file){	struct section *sec;	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;	list_for_each_entry(insn, &file->static_call_list, call_node)		idx++; 	sec = elf_create_section(file->elf, ".static_call_sites", SHF_WRITE,				 sizeof(struct static_call_site), idx);	if (!sec)		return -1; 	idx = 0;	list_for_each_entry(insn, &file->static_call_list, call_node) { 		site = (struct static_call_site *)sec->data->d_buf + idx;		memset(site, 0, sizeof(struct static_call_site)); 		/* populate reloc for 'addr' */		if (elf_add_reloc_to_insn(file->elf, sec,					  idx * sizeof(struct static_call_site),					  R_X86_64_PC32,					  insn->sec, insn->offset))			return -1; 		/* 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); 

Lines 1–500 of 3170.