summaryrefslogtreecommitdiff
path: root/tools/objtool/arch/x86
diff options
context:
space:
mode:
Diffstat (limited to 'tools/objtool/arch/x86')
-rw-r--r--tools/objtool/arch/x86/decode.c85
-rw-r--r--tools/objtool/arch/x86/orc.c6
-rw-r--r--tools/objtool/arch/x86/special.c63
3 files changed, 95 insertions, 59 deletions
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 3a1d80a7878d..98c4713c1b09 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -36,7 +36,7 @@ static int is_x86_64(const struct elf *elf)
case EM_386:
return 0;
default:
- WARN("unexpected ELF machine type %d", elf->ehdr.e_machine);
+ ERROR("unexpected ELF machine type %d", elf->ehdr.e_machine);
return -1;
}
}
@@ -125,8 +125,14 @@ bool arch_pc_relative_reloc(struct reloc *reloc)
#define is_RIP() ((modrm_rm & 7) == CFI_BP && modrm_mod == 0)
#define have_SIB() ((modrm_rm & 7) == CFI_SP && mod_is_mem())
+/*
+ * Check the ModRM register. If there is a SIB byte then check with
+ * the SIB base register. But if the SIB base is 5 (i.e. CFI_BP) and
+ * ModRM mod is 0 then there is no base register.
+ */
#define rm_is(reg) (have_SIB() ? \
- sib_base == (reg) && sib_index == CFI_SP : \
+ sib_base == (reg) && sib_index == CFI_SP && \
+ (sib_base != CFI_BP || modrm_mod != 0) : \
modrm_rm == (reg))
#define rm_is_mem(reg) (mod_is_mem() && !is_RIP() && rm_is(reg))
@@ -167,7 +173,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
ret = insn_decode(&ins, sec->data->d_buf + offset, maxlen,
x86_64 ? INSN_MODE_64 : INSN_MODE_32);
if (ret < 0) {
- WARN("can't decode instruction at %s:0x%lx", sec->name, offset);
+ ERROR("can't decode instruction at %s:0x%lx", sec->name, offset);
return -1;
}
@@ -183,6 +189,15 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
op2 = ins.opcode.bytes[1];
op3 = ins.opcode.bytes[2];
+ /*
+ * XXX hack, decoder is buggered and thinks 0xea is 7 bytes long.
+ */
+ if (op1 == 0xea) {
+ insn->len = 1;
+ insn->type = INSN_BUG;
+ return 0;
+ }
+
if (ins.rex_prefix.nbytes) {
rex = ins.rex_prefix.bytes[0];
rex_w = X86_REX_W(rex) >> 3;
@@ -315,7 +330,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
break;
default:
- /* WARN ? */
+ /* ERROR ? */
break;
}
@@ -450,10 +465,6 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
if (!rex_w)
break;
- /* skip RIP relative displacement */
- if (is_RIP())
- break;
-
/* skip nontrivial SIB */
if (have_SIB()) {
modrm_rm = sib_base;
@@ -461,6 +472,12 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
break;
}
+ /* lea disp(%rip), %dst */
+ if (is_RIP()) {
+ insn->type = INSN_LEA_RIP;
+ break;
+ }
+
/* lea disp(%src), %dst */
ADD_OP(op) {
op->src.offset = ins.displacement.value;
@@ -514,7 +531,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
case INAT_PFX_REPNE:
if (modrm == 0xca)
/* eretu/erets */
- insn->type = INSN_CONTEXT_SWITCH;
+ insn->type = INSN_SYSRET;
break;
default:
if (modrm == 0xca)
@@ -527,11 +544,15 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
insn->type = INSN_JUMP_CONDITIONAL;
- } else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
- op2 == 0x35) {
+ } else if (op2 == 0x05 || op2 == 0x34) {
+
+ /* syscall, sysenter */
+ insn->type = INSN_SYSCALL;
+
+ } else if (op2 == 0x07 || op2 == 0x35) {
- /* sysenter, sysret */
- insn->type = INSN_CONTEXT_SWITCH;
+ /* sysret, sysexit */
+ insn->type = INSN_SYSRET;
} else if (op2 == 0x0b || op2 == 0xb9) {
@@ -553,8 +574,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
if (ins.prefixes.nbytes == 1 &&
ins.prefixes.bytes[0] == 0xf2) {
/* ENQCMD cannot be used in the kernel. */
- WARN("ENQCMD instruction at %s:%lx", sec->name,
- offset);
+ WARN("ENQCMD instruction at %s:%lx", sec->name, offset);
}
} else if (op2 == 0xa0 || op2 == 0xa8) {
@@ -638,7 +658,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
if (disp->sym->type == STT_SECTION)
func = find_symbol_by_offset(disp->sym->sec, reloc_addend(disp));
if (!func) {
- WARN("no func for pv_ops[]");
+ ERROR("no func for pv_ops[]");
return -1;
}
@@ -669,7 +689,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
case 0xca: /* retf */
case 0xcb: /* retf */
- insn->type = INSN_CONTEXT_SWITCH;
+ insn->type = INSN_SYSRET;
break;
case 0xe0: /* loopne */
@@ -714,7 +734,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
} else if (modrm_reg == 5) {
/* jmpf */
- insn->type = INSN_CONTEXT_SWITCH;
+ insn->type = INSN_SYSRET;
} else if (modrm_reg == 6) {
@@ -731,7 +751,10 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
break;
}
- insn->immediate = ins.immediate.nbytes ? ins.immediate.value : 0;
+ if (ins.immediate.nbytes)
+ insn->immediate = ins.immediate.value;
+ else if (ins.displacement.nbytes)
+ insn->immediate = ins.displacement.value;
return 0;
}
@@ -765,7 +788,7 @@ const char *arch_nop_insn(int len)
};
if (len < 1 || len > 5) {
- WARN("invalid NOP size: %d\n", len);
+ ERROR("invalid NOP size: %d\n", len);
return NULL;
}
@@ -785,7 +808,7 @@ const char *arch_ret_insn(int len)
};
if (len < 1 || len > 5) {
- WARN("invalid RET size: %d\n", len);
+ ERROR("invalid RET size: %d\n", len);
return NULL;
}
@@ -828,16 +851,32 @@ int arch_decode_hint_reg(u8 sp_reg, int *base)
bool arch_is_retpoline(struct symbol *sym)
{
- return !strncmp(sym->name, "__x86_indirect_", 15);
+ return !strncmp(sym->name, "__x86_indirect_", 15) ||
+ !strncmp(sym->name, "__pi___x86_indirect_", 20);
}
bool arch_is_rethunk(struct symbol *sym)
{
- return !strcmp(sym->name, "__x86_return_thunk");
+ return !strcmp(sym->name, "__x86_return_thunk") ||
+ !strcmp(sym->name, "__pi___x86_return_thunk");
}
bool arch_is_embedded_insn(struct symbol *sym)
{
return !strcmp(sym->name, "retbleed_return_thunk") ||
+ !strcmp(sym->name, "srso_alias_safe_ret") ||
!strcmp(sym->name, "srso_safe_ret");
}
+
+unsigned int arch_reloc_size(struct reloc *reloc)
+{
+ switch (reloc_type(reloc)) {
+ case R_X86_64_32:
+ case R_X86_64_32S:
+ case R_X86_64_PC32:
+ case R_X86_64_PLT32:
+ return 4;
+ default:
+ return 8;
+ }
+}
diff --git a/tools/objtool/arch/x86/orc.c b/tools/objtool/arch/x86/orc.c
index b6cd943e87f9..7176b9ec5b05 100644
--- a/tools/objtool/arch/x86/orc.c
+++ b/tools/objtool/arch/x86/orc.c
@@ -40,7 +40,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
orc->type = ORC_TYPE_REGS_PARTIAL;
break;
default:
- WARN_INSN(insn, "unknown unwind hint type %d", cfi->type);
+ ERROR_INSN(insn, "unknown unwind hint type %d", cfi->type);
return -1;
}
@@ -72,7 +72,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
orc->sp_reg = ORC_REG_DX;
break;
default:
- WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base);
+ ERROR_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base);
return -1;
}
@@ -87,7 +87,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
orc->bp_reg = ORC_REG_BP;
break;
default:
- WARN_INSN(insn, "unknown BP base reg %d", bp->base);
+ ERROR_INSN(insn, "unknown BP base reg %d", bp->base);
return -1;
}
diff --git a/tools/objtool/arch/x86/special.c b/tools/objtool/arch/x86/special.c
index 4134d27c696b..06ca4a2659a4 100644
--- a/tools/objtool/arch/x86/special.c
+++ b/tools/objtool/arch/x86/special.c
@@ -3,39 +3,32 @@
#include <objtool/special.h>
#include <objtool/builtin.h>
+#include <objtool/warn.h>
-#define X86_FEATURE_POPCNT (4 * 32 + 23)
-#define X86_FEATURE_SMAP (9 * 32 + 20)
-
-void arch_handle_alternative(unsigned short feature, struct special_alt *alt)
+void arch_handle_alternative(struct special_alt *alt)
{
- switch (feature) {
- case X86_FEATURE_SMAP:
- /*
- * If UACCESS validation is enabled; force that alternative;
- * otherwise force it the other way.
- *
- * What we want to avoid is having both the original and the
- * alternative code flow at the same time, in that case we can
- * find paths that see the STAC but take the NOP instead of
- * CLAC and the other way around.
- */
- if (opts.uaccess)
- alt->skip_orig = true;
- else
- alt->skip_alt = true;
- break;
- case X86_FEATURE_POPCNT:
- /*
- * It has been requested that we don't validate the !POPCNT
- * feature path which is a "very very small percentage of
- * machines".
- */
- alt->skip_orig = true;
- break;
- default:
- break;
- }
+ static struct special_alt *group, *prev;
+
+ /*
+ * Recompute orig_len for nested ALTERNATIVE()s.
+ */
+ if (group && group->orig_sec == alt->orig_sec &&
+ group->orig_off == alt->orig_off) {
+
+ struct special_alt *iter = group;
+ for (;;) {
+ unsigned int len = max(iter->orig_len, alt->orig_len);
+ iter->orig_len = alt->orig_len = len;
+
+ if (iter == prev)
+ break;
+
+ iter = list_next_entry(iter, list);
+ }
+
+ } else group = alt;
+
+ prev = alt;
}
bool arch_support_alt_relocation(struct special_alt *special_alt,
@@ -86,7 +79,8 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
* NOTE: MITIGATION_RETPOLINE made it harder still to decode dynamic jumps.
*/
struct reloc *arch_find_switch_table(struct objtool_file *file,
- struct instruction *insn)
+ struct instruction *insn,
+ unsigned long *table_size)
{
struct reloc *text_reloc, *rodata_reloc;
struct section *table_sec;
@@ -132,8 +126,11 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
* indicates a rare GCC quirk/bug which can leave dead
* code behind.
*/
- if (reloc_type(text_reloc) == R_X86_64_PC32)
+ if (!file->ignore_unreachables && reloc_type(text_reloc) == R_X86_64_PC32) {
+ WARN_INSN(insn, "ignoring unreachables due to jump table quirk");
file->ignore_unreachables = true;
+ }
+ *table_size = 0;
return rodata_reloc;
}