diff options
Diffstat (limited to 'tools/objtool/arch/x86/decode.c')
| -rw-r--r-- | tools/objtool/arch/x86/decode.c | 180 | 
1 files changed, 55 insertions, 125 deletions
| diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index 0893436cc09f..4d6d7fc13255 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -20,6 +20,7 @@  #include <objtool/arch.h>  #include <objtool/warn.h>  #include <objtool/endianness.h> +#include <objtool/builtin.h>  #include <arch/elf.h>  static int is_x86_64(const struct elf *elf) @@ -102,12 +103,13 @@ unsigned long arch_jump_destination(struct instruction *insn)  #define rm_is_mem(reg)	(mod_is_mem() && !is_RIP() && rm_is(reg))  #define rm_is_reg(reg)	(mod_is_reg() && modrm_rm == (reg)) -int arch_decode_instruction(const struct elf *elf, const struct section *sec, +int arch_decode_instruction(struct objtool_file *file, const struct section *sec,  			    unsigned long offset, unsigned int maxlen,  			    unsigned int *len, enum insn_type *type,  			    unsigned long *immediate,  			    struct list_head *ops_list)  { +	const struct elf *elf = file->elf;  	struct insn insn;  	int x86_64, ret;  	unsigned char op1, op2, @@ -544,6 +546,36 @@ int arch_decode_instruction(const struct elf *elf, const struct section *sec,  		*type = INSN_RETURN;  		break; +	case 0xc7: /* mov imm, r/m */ +		if (!noinstr) +			break; + +		if (insn.length == 3+4+4 && !strncmp(sec->name, ".init.text", 10)) { +			struct reloc *immr, *disp; +			struct symbol *func; +			int idx; + +			immr = find_reloc_by_dest(elf, (void *)sec, offset+3); +			disp = find_reloc_by_dest(elf, (void *)sec, offset+7); + +			if (!immr || strcmp(immr->sym->name, "pv_ops")) +				break; + +			idx = (immr->addend + 8) / sizeof(void *); + +			func = disp->sym; +			if (disp->sym->type == STT_SECTION) +				func = find_symbol_by_offset(disp->sym->sec, disp->addend); +			if (!func) { +				WARN("no func for pv_ops[]"); +				return -1; +			} + +			objtool_pv_add(file, idx, func); +		} + +		break; +  	case 0xcf: /* iret */  		/*  		 * Handle sync_core(), which has an IRET to self. @@ -659,154 +691,52 @@ const char *arch_nop_insn(int len)  	return nops[len-1];  } -/* asm/alternative.h ? */ - -#define ALTINSTR_FLAG_INV	(1 << 15) -#define ALT_NOT(feat)		((feat) | ALTINSTR_FLAG_INV) - -struct alt_instr { -	s32 instr_offset;	/* original instruction */ -	s32 repl_offset;	/* offset to replacement instruction */ -	u16 cpuid;		/* cpuid bit set for replacement */ -	u8  instrlen;		/* length of original instruction */ -	u8  replacementlen;	/* length of new instruction */ -} __packed; - -static int elf_add_alternative(struct elf *elf, -			       struct instruction *orig, struct symbol *sym, -			       int cpuid, u8 orig_len, u8 repl_len) -{ -	const int size = sizeof(struct alt_instr); -	struct alt_instr *alt; -	struct section *sec; -	Elf_Scn *s; - -	sec = find_section_by_name(elf, ".altinstructions"); -	if (!sec) { -		sec = elf_create_section(elf, ".altinstructions", -					 SHF_ALLOC, 0, 0); - -		if (!sec) { -			WARN_ELF("elf_create_section"); -			return -1; -		} -	} - -	s = elf_getscn(elf->elf, sec->idx); -	if (!s) { -		WARN_ELF("elf_getscn"); -		return -1; -	} - -	sec->data = elf_newdata(s); -	if (!sec->data) { -		WARN_ELF("elf_newdata"); -		return -1; -	} - -	sec->data->d_size = size; -	sec->data->d_align = 1; - -	alt = sec->data->d_buf = malloc(size); -	if (!sec->data->d_buf) { -		perror("malloc"); -		return -1; -	} -	memset(sec->data->d_buf, 0, size); - -	if (elf_add_reloc_to_insn(elf, sec, sec->sh.sh_size, -				  R_X86_64_PC32, orig->sec, orig->offset)) { -		WARN("elf_create_reloc: alt_instr::instr_offset"); -		return -1; -	} - -	if (elf_add_reloc(elf, sec, sec->sh.sh_size + 4, -			  R_X86_64_PC32, sym, 0)) { -		WARN("elf_create_reloc: alt_instr::repl_offset"); -		return -1; -	} - -	alt->cpuid = bswap_if_needed(cpuid); -	alt->instrlen = orig_len; -	alt->replacementlen = repl_len; - -	sec->sh.sh_size += size; -	sec->changed = true; - -	return 0; -} - -#define X86_FEATURE_RETPOLINE                ( 7*32+12) +#define BYTE_RET	0xC3 -int arch_rewrite_retpolines(struct objtool_file *file) +const char *arch_ret_insn(int len)  { -	struct instruction *insn; -	struct reloc *reloc; -	struct symbol *sym; -	char name[32] = ""; - -	list_for_each_entry(insn, &file->retpoline_call_list, call_node) { - -		if (insn->type != INSN_JUMP_DYNAMIC && -		    insn->type != INSN_CALL_DYNAMIC) -			continue; - -		if (!strcmp(insn->sec->name, ".text.__x86.indirect_thunk")) -			continue; - -		reloc = insn->reloc; - -		sprintf(name, "__x86_indirect_alt_%s_%s", -			insn->type == INSN_JUMP_DYNAMIC ? "jmp" : "call", -			reloc->sym->name + 21); - -		sym = find_symbol_by_name(file->elf, name); -		if (!sym) { -			sym = elf_create_undef_symbol(file->elf, name); -			if (!sym) { -				WARN("elf_create_undef_symbol"); -				return -1; -			} -		} +	static const char ret[5][5] = { +		{ BYTE_RET }, +		{ BYTE_RET, BYTES_NOP1 }, +		{ BYTE_RET, BYTES_NOP2 }, +		{ BYTE_RET, BYTES_NOP3 }, +		{ BYTE_RET, BYTES_NOP4 }, +	}; -		if (elf_add_alternative(file->elf, insn, sym, -					ALT_NOT(X86_FEATURE_RETPOLINE), 5, 5)) { -			WARN("elf_add_alternative"); -			return -1; -		} +	if (len < 1 || len > 5) { +		WARN("invalid RET size: %d\n", len); +		return NULL;  	} -	return 0; +	return ret[len-1];  } -int arch_decode_hint_reg(struct instruction *insn, u8 sp_reg) +int arch_decode_hint_reg(u8 sp_reg, int *base)  { -	struct cfi_reg *cfa = &insn->cfi.cfa; -  	switch (sp_reg) {  	case ORC_REG_UNDEFINED: -		cfa->base = CFI_UNDEFINED; +		*base = CFI_UNDEFINED;  		break;  	case ORC_REG_SP: -		cfa->base = CFI_SP; +		*base = CFI_SP;  		break;  	case ORC_REG_BP: -		cfa->base = CFI_BP; +		*base = CFI_BP;  		break;  	case ORC_REG_SP_INDIRECT: -		cfa->base = CFI_SP_INDIRECT; +		*base = CFI_SP_INDIRECT;  		break;  	case ORC_REG_R10: -		cfa->base = CFI_R10; +		*base = CFI_R10;  		break;  	case ORC_REG_R13: -		cfa->base = CFI_R13; +		*base = CFI_R13;  		break;  	case ORC_REG_DI: -		cfa->base = CFI_DI; +		*base = CFI_DI;  		break;  	case ORC_REG_DX: -		cfa->base = CFI_DX; +		*base = CFI_DX;  		break;  	default:  		return -1; |