5154 lines · 117.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> */ #define _GNU_SOURCE /* memmem() */#include <fnmatch.h>#include <string.h>#include <stdlib.h>#include <inttypes.h>#include <sys/mman.h> #include <objtool/builtin.h>#include <objtool/cfi.h>#include <objtool/arch.h>#include <objtool/disas.h>#include <objtool/check.h>#include <objtool/special.h>#include <objtool/trace.h>#include <objtool/warn.h>#include <objtool/checksum.h>#include <objtool/util.h> #include <linux/objtool_types.h>#include <linux/hashtable.h>#include <linux/kernel.h>#include <linux/static_call_types.h>#include <linux/string.h> 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;static struct cfi_state force_undefined_cfi; struct disas_context *objtool_disas_ctx; size_t sym_name_max_len; 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;} struct instruction *next_insn_same_sec(struct objtool_file *file,				       struct instruction *insn){	if (insn->idx == INSN_CHUNK_MAX)		return find_insn(file, insn->sec, insn->offset + insn->len); 	insn++;	if (!insn->len)		return NULL; 	return insn;} static struct instruction *next_insn_same_func(struct objtool_file *file,					       struct instruction *insn){	struct instruction *next = next_insn_same_sec(file, insn);	struct symbol *func = insn_func(insn); 	if (!func)		return NULL; 	if (next && insn_func(next) == 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_sec(struct objtool_file *file,					      struct instruction *insn){	if (insn->idx == 0) {		if (insn->prev_len)			return find_insn(file, insn->sec, insn->offset - insn->prev_len);		return NULL;	} 	return insn - 1;} static struct instruction *prev_insn_same_sym(struct objtool_file *file,					      struct instruction *insn){	struct instruction *prev = prev_insn_same_sec(file, insn); 	if (prev && insn_func(prev) == insn_func(insn))		return prev; 	return NULL;} #define for_each_insn(file, insn)					\	for (struct section *__sec, *__fake = (struct section *)1;	\	     __fake; __fake = NULL)					\		for_each_sec(file->elf, __sec)				\			sec_for_each_insn(file, __sec, insn) #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->offset < sym->offset + sym->len;		\	     insn = next_insn_same_sec(file, insn)) #define sym_for_each_insn_continue_reverse(file, sym, insn)		\	for (insn = prev_insn_same_sec(file, insn);			\	     insn && insn->offset >= sym->offset;			\	     insn = prev_insn_same_sec(file, insn)) #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 inline struct reloc *insn_jump_table(struct instruction *insn){	if (insn->type == INSN_JUMP_DYNAMIC ||	    insn->type == INSN_CALL_DYNAMIC)		return insn->_jump_table; 	return NULL;} static inline unsigned long insn_jump_table_size(struct instruction *insn){	if (insn->type == INSN_JUMP_DYNAMIC ||	    insn->type == INSN_CALL_DYNAMIC)		return insn->_jump_table_size; 	return 0;} static bool is_jump_table_jump(struct instruction *insn){	struct alt_group *alt_group = insn->alt_group; 	if (insn_jump_table(insn))		return true; 	/* Retpoline alternative for a jump table? */	return alt_group && alt_group->orig_group &&	       insn_jump_table(alt_group->orig_group->first_insn);} static bool is_sibling_call(struct instruction *insn){	/*	 * Assume only STT_FUNC calls have jump-tables.	 */	if (insn_func(insn)) {		/* 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(insn) for sibling calls. */	return (is_static_jump(insn) && insn_call_dest(insn));} /* * Checks if a function is a Rust "noreturn" one. */static bool is_rust_noreturn(const struct symbol *func){	/*	 * If it does not start with "_R", then it is not a Rust symbol.	 */	if (strncmp(func->name, "_R", 2))		return false; 	/*	 * These are just heuristics -- we do not control the precise symbol	 * name, due to the crate disambiguators (which depend on the compiler)	 * as well as changes to the source code itself between versions (since	 * these come from the Rust standard library).	 */	return str_ends_with(func->name, "_4core3num20from_str_radix_panic")				||	       str_ends_with(func->name, "_4core3num22from_ascii_radix_panic")				||	       str_ends_with(func->name, "_4core5sliceSp15copy_from_slice17len_mismatch_fail")		||	       str_ends_with(func->name, "_4core6option13expect_failed")				||	       str_ends_with(func->name, "_4core6option13unwrap_failed")				||	       str_ends_with(func->name, "_4core6result13unwrap_failed")				||	       str_ends_with(func->name, "_4core9panicking5panic")					||	       str_ends_with(func->name, "_4core9panicking9panic_fmt")					||	       str_ends_with(func->name, "_4core9panicking14panic_explicit")				||	       str_ends_with(func->name, "_4core9panicking14panic_nounwind")				||	       str_ends_with(func->name, "_4core9panicking18panic_bounds_check")			||	       str_ends_with(func->name, "_4core9panicking18panic_nounwind_fmt")			||	       str_ends_with(func->name, "_4core9panicking19assert_failed_inner")			||	       str_ends_with(func->name, "_4core9panicking30panic_null_pointer_dereference")		||	       str_ends_with(func->name, "_4core9panicking36panic_misaligned_pointer_dereference")	||	       str_ends_with(func->name, "_7___rustc17rust_begin_unwind")				||	       strstr(func->name, "_4core9panicking13assert_failed")					||	       strstr(func->name, "_4core9panicking11panic_const24panic_const_")			||	       (strstr(func->name, "_4core5slice5index") &&		strstr(func->name, "slice_") &&		str_ends_with(func->name, "_fail"));} /* * 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; #define NORETURN(func) __stringify(func),	static const char * const global_noreturns[] = {#include "noreturns.h"	};#undef NORETURN 	if (!func)		return false; 	if (!is_local_sym(func)) {		if (is_rust_noreturn(func))			return true; 		for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)			if (!strcmp(func->name, global_noreturns[i]))				return true;	} 	if (is_weak_sym(func))		return false; 	if (!func->len)		return false; 	insn = find_insn(file, func->sec, func->offset);	if (!insn || !insn_func(insn))		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, insn_func(dest), 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 objtool_file *file, struct insn_state *state,			    struct section *sec){	memset(state, 0, sizeof(*state));	init_cfi_state(&state->cfi); 	if (opts.noinstr && sec)		state->noinstr = sec->noinstr;} static struct cfi_state *cfi_alloc(void){	struct cfi_state *cfi = calloc(1, sizeof(struct cfi_state));	if (!cfi) {		ERROR_GLIBC("calloc");		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) {		ERROR_GLIBC("mmap fail cfi_hash");		cfi_hash = NULL;	}  else if (opts.stats) {		printf("cfi_bits: %d\n", cfi_bits);	} 	return cfi_hash;} static unsigned long nr_insns;static unsigned long nr_insns_visited; /* * 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; 	for_each_sec(file->elf, sec) {		struct instruction *insns = NULL;		u8 prev_len = 0;		u8 idx = 0; 		if (!is_text_sec(sec))			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") ||		    !strcmp(sec->name, ".cpuidle.text") ||		    !strncmp(sec->name, ".text..__x86.", 13))			sec->noinstr = true; 		/*		 * .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(sec->name, ".init.text") && !opts.module)			sec->init = true; 		for (offset = 0; offset < sec_size(sec); offset += insn->len) {			if (!insns || idx == INSN_CHUNK_MAX) {				insns = calloc(INSN_CHUNK_SIZE, sizeof(*insn));				if (!insns) {					ERROR_GLIBC("calloc");					return -1;				}				idx = 0;			} else {				idx++;			}			insn = &insns[idx];			insn->idx = idx; 			INIT_LIST_HEAD(&insn->call_node);			insn->sec = sec;			insn->offset = offset;			insn->prev_len = prev_len; 			if (arch_decode_instruction(file, sec, offset, sec_size(sec) - offset, insn))				return -1; 			prev_len = insn->len; 			/*			 * By default, "ud2" is a dead end unless otherwise			 * annotated, because GCC 7 inserts it for certain			 * divide-by-zero cases.			 */			if (insn->type == INSN_BUG)				insn->dead_end = true; 			hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));			nr_insns++;		} 		sec_for_each_sym(sec, func) {			if (!is_notype_sym(func) && !is_func_sym(func))				continue; 			if (func->offset == sec_size(sec)) {				/* Heuristic: likely an "end" symbol */				if (is_notype_sym(func))					continue;				ERROR("%s(): STT_FUNC at end of section", func->name);				return -1;			} 			if (func->embedded_insn || func->alias != func)				continue; 			if (!find_insn(file, sec, func->offset)) {				ERROR("%s(): can't find starting instruction", func->name);				return -1;			}

Lines 1–500 of 5154.