|
Jiri Slaby |
a2e715 |
From: Peter Zijlstra <peterz@infradead.org>
|
|
Jiri Slaby |
a2e715 |
Date: Wed, 8 Feb 2023 18:10:52 +0100
|
|
Jiri Slaby |
a2e715 |
Subject: x86/alternative: Support relocations in alternatives
|
|
Jiri Slaby |
a2e715 |
Git-commit: 270a69c4485d7d07516d058bcc0473c90ee22185
|
|
Jiri Slaby |
a2e715 |
Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git#master
|
|
Jiri Slaby |
a2e715 |
Patch-mainline: Queued in subsystem maintainer repository
|
|
Jiri Slaby |
a2e715 |
References: bsc#1206578
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
A little while ago someone (Kirill) ran into the whole 'alternatives don't
|
|
Jiri Slaby |
a2e715 |
do relocations nonsense' again and I got annoyed enough to actually look
|
|
Jiri Slaby |
a2e715 |
at the code.
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
Since the whole alternative machinery already fully decodes the
|
|
Jiri Slaby |
a2e715 |
instructions it is simple enough to adjust immediates and displacement
|
|
Jiri Slaby |
a2e715 |
when needed. Specifically, the immediates for IP modifying instructions
|
|
Jiri Slaby |
a2e715 |
(JMP, CALL, Jcc) and the displacement for RIP-relative instructions.
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
[ bp: Massage comment some more and get rid of third loop in
|
|
Jiri Slaby |
a2e715 |
apply_relocation(). ]
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
[js] declare 'next, i' out of 'for' loop.
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
|
|
Jiri Slaby |
a2e715 |
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
|
|
Jiri Slaby |
a2e715 |
Link: https://lore.kernel.org/r/20230208171431.313857925@infradead.org
|
|
Jiri Slaby |
a2e715 |
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
|
|
Jiri Slaby |
a2e715 |
---
|
|
Jiri Slaby |
a2e715 |
arch/x86/kernel/alternative.c | 261 +++++++++++++++++++++++++--------------
|
|
Jiri Slaby |
a2e715 |
tools/objtool/arch/x86/special.c | 8 -
|
|
Jiri Slaby |
a2e715 |
2 files changed, 173 insertions(+), 96 deletions(-)
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
--- a/arch/x86/kernel/alternative.c
|
|
Jiri Slaby |
a2e715 |
+++ b/arch/x86/kernel/alternative.c
|
|
Jiri Slaby |
a2e715 |
@@ -133,71 +133,6 @@ extern s32 __smp_locks[], __smp_locks_en
|
|
Jiri Slaby |
a2e715 |
void text_poke_early(void *addr, const void *opcode, size_t len);
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
/*
|
|
Jiri Slaby |
a2e715 |
- * Are we looking at a near JMP with a 1 or 4-byte displacement.
|
|
Jiri Slaby |
a2e715 |
- */
|
|
Jiri Slaby |
a2e715 |
-static inline bool is_jmp(const u8 opcode)
|
|
Jiri Slaby |
a2e715 |
-{
|
|
Jiri Slaby |
a2e715 |
- return opcode == 0xeb || opcode == 0xe9;
|
|
Jiri Slaby |
a2e715 |
-}
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
-static void __init_or_module
|
|
Jiri Slaby |
a2e715 |
-recompute_jump(struct alt_instr *a, u8 *orig_insn, u8 *repl_insn, u8 *insn_buff)
|
|
Jiri Slaby |
a2e715 |
-{
|
|
Jiri Slaby |
a2e715 |
- u8 *next_rip, *tgt_rip;
|
|
Jiri Slaby |
a2e715 |
- s32 n_dspl, o_dspl;
|
|
Jiri Slaby |
a2e715 |
- int repl_len;
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
- if (a->replacementlen != 5)
|
|
Jiri Slaby |
a2e715 |
- return;
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
- o_dspl = *(s32 *)(insn_buff + 1);
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
- /* next_rip of the replacement JMP */
|
|
Jiri Slaby |
a2e715 |
- next_rip = repl_insn + a->replacementlen;
|
|
Jiri Slaby |
a2e715 |
- /* target rip of the replacement JMP */
|
|
Jiri Slaby |
a2e715 |
- tgt_rip = next_rip + o_dspl;
|
|
Jiri Slaby |
a2e715 |
- n_dspl = tgt_rip - orig_insn;
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
- DPRINTK(ALT, "target RIP: %px, new_displ: 0x%x", tgt_rip, n_dspl);
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
- if (tgt_rip - orig_insn >= 0) {
|
|
Jiri Slaby |
a2e715 |
- if (n_dspl - 2 <= 127)
|
|
Jiri Slaby |
a2e715 |
- goto two_byte_jmp;
|
|
Jiri Slaby |
a2e715 |
- else
|
|
Jiri Slaby |
a2e715 |
- goto five_byte_jmp;
|
|
Jiri Slaby |
a2e715 |
- /* negative offset */
|
|
Jiri Slaby |
a2e715 |
- } else {
|
|
Jiri Slaby |
a2e715 |
- if (((n_dspl - 2) & 0xff) == (n_dspl - 2))
|
|
Jiri Slaby |
a2e715 |
- goto two_byte_jmp;
|
|
Jiri Slaby |
a2e715 |
- else
|
|
Jiri Slaby |
a2e715 |
- goto five_byte_jmp;
|
|
Jiri Slaby |
a2e715 |
- }
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
-two_byte_jmp:
|
|
Jiri Slaby |
a2e715 |
- n_dspl -= 2;
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
- insn_buff[0] = 0xeb;
|
|
Jiri Slaby |
a2e715 |
- insn_buff[1] = (s8)n_dspl;
|
|
Jiri Slaby |
a2e715 |
- add_nops(insn_buff + 2, 3);
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
- repl_len = 2;
|
|
Jiri Slaby |
a2e715 |
- goto done;
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
-five_byte_jmp:
|
|
Jiri Slaby |
a2e715 |
- n_dspl -= 5;
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
- insn_buff[0] = 0xe9;
|
|
Jiri Slaby |
a2e715 |
- *(s32 *)&insn_buff[1] = n_dspl;
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
- repl_len = 5;
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
-done:
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
- DPRINTK(ALT, "final displ: 0x%08x, JMP 0x%lx",
|
|
Jiri Slaby |
a2e715 |
- n_dspl, (unsigned long)orig_insn + n_dspl + repl_len);
|
|
Jiri Slaby |
a2e715 |
-}
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
-/*
|
|
Jiri Slaby |
a2e715 |
* optimize_nops_range() - Optimize a sequence of single byte NOPs (0x90)
|
|
Jiri Slaby |
a2e715 |
*
|
|
Jiri Slaby |
a2e715 |
* @instr: instruction byte stream
|
|
Jiri Slaby |
a2e715 |
@@ -264,6 +199,140 @@ static void __init_or_module noinline op
|
|
Jiri Slaby |
a2e715 |
}
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
/*
|
|
Jiri Slaby |
a2e715 |
+ * In this context, "source" is where the instructions are placed in the
|
|
Jiri Slaby |
a2e715 |
+ * section .altinstr_replacement, for example during kernel build by the
|
|
Jiri Slaby |
a2e715 |
+ * toolchain.
|
|
Jiri Slaby |
a2e715 |
+ * "Destination" is where the instructions are being patched in by this
|
|
Jiri Slaby |
a2e715 |
+ * machinery.
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * The source offset is:
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * src_imm = target - src_next_ip (1)
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * and the target offset is:
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * dst_imm = target - dst_next_ip (2)
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * so rework (1) as an expression for target like:
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * target = src_imm + src_next_ip (1a)
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * and substitute in (2) to get:
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * dst_imm = (src_imm + src_next_ip) - dst_next_ip (3)
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * Now, since the instruction stream is 'identical' at src and dst (it
|
|
Jiri Slaby |
a2e715 |
+ * is being copied after all) it can be stated that:
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * src_next_ip = src + ip_offset
|
|
Jiri Slaby |
a2e715 |
+ * dst_next_ip = dst + ip_offset (4)
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * Substitute (4) in (3) and observe ip_offset being cancelled out to
|
|
Jiri Slaby |
a2e715 |
+ * obtain:
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * dst_imm = src_imm + (src + ip_offset) - (dst + ip_offset)
|
|
Jiri Slaby |
a2e715 |
+ * = src_imm + src - dst + ip_offset - ip_offset
|
|
Jiri Slaby |
a2e715 |
+ * = src_imm + src - dst (5)
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * IOW, only the relative displacement of the code block matters.
|
|
Jiri Slaby |
a2e715 |
+ */
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+#define apply_reloc_n(n_, p_, d_) \
|
|
Jiri Slaby |
a2e715 |
+ do { \
|
|
Jiri Slaby |
a2e715 |
+ s32 v = *(s##n_ *)(p_); \
|
|
Jiri Slaby |
a2e715 |
+ v += (d_); \
|
|
Jiri Slaby |
a2e715 |
+ BUG_ON((v >> 31) != (v >> (n_-1))); \
|
|
Jiri Slaby |
a2e715 |
+ *(s##n_ *)(p_) = (s##n_)v; \
|
|
Jiri Slaby |
a2e715 |
+ } while (0)
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+static __always_inline
|
|
Jiri Slaby |
a2e715 |
+void apply_reloc(int n, void *ptr, uintptr_t diff)
|
|
Jiri Slaby |
a2e715 |
+{
|
|
Jiri Slaby |
a2e715 |
+ switch (n) {
|
|
Jiri Slaby |
a2e715 |
+ case 1: apply_reloc_n(8, ptr, diff); break;
|
|
Jiri Slaby |
a2e715 |
+ case 2: apply_reloc_n(16, ptr, diff); break;
|
|
Jiri Slaby |
a2e715 |
+ case 4: apply_reloc_n(32, ptr, diff); break;
|
|
Jiri Slaby |
a2e715 |
+ default: BUG();
|
|
Jiri Slaby |
a2e715 |
+ }
|
|
Jiri Slaby |
a2e715 |
+}
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+static __always_inline
|
|
Jiri Slaby |
a2e715 |
+bool need_reloc(unsigned long offset, u8 *src, size_t src_len)
|
|
Jiri Slaby |
a2e715 |
+{
|
|
Jiri Slaby |
a2e715 |
+ u8 *target = src + offset;
|
|
Jiri Slaby |
a2e715 |
+ /*
|
|
Jiri Slaby |
a2e715 |
+ * If the target is inside the patched block, it's relative to the
|
|
Jiri Slaby |
a2e715 |
+ * block itself and does not need relocation.
|
|
Jiri Slaby |
a2e715 |
+ */
|
|
Jiri Slaby |
a2e715 |
+ return (target < src || target > src + src_len);
|
|
Jiri Slaby |
a2e715 |
+}
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+static void __init_or_module noinline
|
|
Jiri Slaby |
a2e715 |
+apply_relocation(u8 *buf, size_t len, u8 *dest, u8 *src, size_t src_len)
|
|
Jiri Slaby |
a2e715 |
+{
|
|
Jiri Slaby |
a2e715 |
+ int next, i = 0;
|
|
Jiri Slaby |
a2e715 |
+ for (; i < len; i = next) {
|
|
Jiri Slaby |
a2e715 |
+ struct insn insn;
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+ if (WARN_ON_ONCE(insn_decode_kernel(&insn, &buf[i])))
|
|
Jiri Slaby |
a2e715 |
+ return;
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+ next = i + insn.length;
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+ switch (insn.opcode.bytes[0]) {
|
|
Jiri Slaby |
a2e715 |
+ case 0x0f:
|
|
Jiri Slaby |
a2e715 |
+ if (insn.opcode.bytes[1] < 0x80 ||
|
|
Jiri Slaby |
a2e715 |
+ insn.opcode.bytes[1] > 0x8f)
|
|
Jiri Slaby |
a2e715 |
+ break;
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+ fallthrough; /* Jcc.d32 */
|
|
Jiri Slaby |
a2e715 |
+ case 0x70 ... 0x7f: /* Jcc.d8 */
|
|
Jiri Slaby |
a2e715 |
+ case JMP8_INSN_OPCODE:
|
|
Jiri Slaby |
a2e715 |
+ case JMP32_INSN_OPCODE:
|
|
Jiri Slaby |
a2e715 |
+ case CALL_INSN_OPCODE:
|
|
Jiri Slaby |
a2e715 |
+ if (need_reloc(next + insn.immediate.value, src, src_len)) {
|
|
Jiri Slaby |
a2e715 |
+ apply_reloc(insn.immediate.nbytes,
|
|
Jiri Slaby |
a2e715 |
+ buf + i + insn_offset_immediate(&insn),
|
|
Jiri Slaby |
a2e715 |
+ src - dest);
|
|
Jiri Slaby |
a2e715 |
+ }
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+ /*
|
|
Jiri Slaby |
a2e715 |
+ * Where possible, convert JMP.d32 into JMP.d8.
|
|
Jiri Slaby |
a2e715 |
+ */
|
|
Jiri Slaby |
a2e715 |
+ if (insn.opcode.bytes[0] == JMP32_INSN_OPCODE) {
|
|
Jiri Slaby |
a2e715 |
+ s32 imm = insn.immediate.value;
|
|
Jiri Slaby |
a2e715 |
+ imm += src - dest;
|
|
Jiri Slaby |
a2e715 |
+ imm += JMP32_INSN_SIZE - JMP8_INSN_SIZE;
|
|
Jiri Slaby |
a2e715 |
+ if ((imm >> 31) == (imm >> 7)) {
|
|
Jiri Slaby |
a2e715 |
+ buf[i+0] = JMP8_INSN_OPCODE;
|
|
Jiri Slaby |
a2e715 |
+ buf[i+1] = (s8)imm;
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+ memset(&buf[i+2], INT3_INSN_OPCODE, insn.length - 2);
|
|
Jiri Slaby |
a2e715 |
+ }
|
|
Jiri Slaby |
a2e715 |
+ }
|
|
Jiri Slaby |
a2e715 |
+ break;
|
|
Jiri Slaby |
a2e715 |
+ }
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+ if (insn_rip_relative(&insn)) {
|
|
Jiri Slaby |
a2e715 |
+ if (need_reloc(next + insn.displacement.value, src, src_len)) {
|
|
Jiri Slaby |
a2e715 |
+ apply_reloc(insn.displacement.nbytes,
|
|
Jiri Slaby |
a2e715 |
+ buf + i + insn_offset_displacement(&insn),
|
|
Jiri Slaby |
a2e715 |
+ src - dest);
|
|
Jiri Slaby |
a2e715 |
+ }
|
|
Jiri Slaby |
a2e715 |
+ }
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+ /*
|
|
Jiri Slaby |
a2e715 |
+ * See if this and any potentially following NOPs can be
|
|
Jiri Slaby |
a2e715 |
+ * optimized.
|
|
Jiri Slaby |
a2e715 |
+ */
|
|
Jiri Slaby |
a2e715 |
+ if (insn.length == 1 && insn.opcode.bytes[0] == 0x90)
|
|
Jiri Slaby |
a2e715 |
+ next = i + optimize_nops_range(buf, len, i);
|
|
Jiri Slaby |
a2e715 |
+ }
|
|
Jiri Slaby |
a2e715 |
+}
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+/*
|
|
Jiri Slaby |
a2e715 |
* Replace instructions with better alternatives for this CPU type. This runs
|
|
Jiri Slaby |
a2e715 |
* before SMP is initialized to avoid SMP problems with self modifying code.
|
|
Jiri Slaby |
a2e715 |
* This implies that asymmetric systems where APs have less capabilities than
|
|
Jiri Slaby |
a2e715 |
@@ -306,8 +374,10 @@ void __init_or_module noinline apply_alt
|
|
Jiri Slaby |
a2e715 |
* - feature not present but ALTINSTR_FLAG_INV is set to mean,
|
|
Jiri Slaby |
a2e715 |
* patch if feature is *NOT* present.
|
|
Jiri Slaby |
a2e715 |
*/
|
|
Jiri Slaby |
a2e715 |
- if (!boot_cpu_has(feature) == !(a->cpuid & ALTINSTR_FLAG_INV))
|
|
Jiri Slaby |
a2e715 |
- goto next;
|
|
Jiri Slaby |
a2e715 |
+ if (!boot_cpu_has(feature) == !(a->cpuid & ALTINSTR_FLAG_INV)) {
|
|
Jiri Slaby |
a2e715 |
+ optimize_nops(instr, a->instrlen);
|
|
Jiri Slaby |
a2e715 |
+ continue;
|
|
Jiri Slaby |
a2e715 |
+ }
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
DPRINTK(ALT, "feat: %s%d*32+%d, old: (%pS (%px) len: %d), repl: (%px, len: %d)",
|
|
Jiri Slaby |
a2e715 |
(a->cpuid & ALTINSTR_FLAG_INV) ? "!" : "",
|
|
Jiri Slaby |
a2e715 |
@@ -316,37 +386,19 @@ void __init_or_module noinline apply_alt
|
|
Jiri Slaby |
a2e715 |
instr, instr, a->instrlen,
|
|
Jiri Slaby |
a2e715 |
replacement, a->replacementlen);
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
- DUMP_BYTES(ALT, instr, a->instrlen, "%px: old_insn: ", instr);
|
|
Jiri Slaby |
a2e715 |
- DUMP_BYTES(ALT, replacement, a->replacementlen, "%px: rpl_insn: ", replacement);
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
memcpy(insn_buff, replacement, a->replacementlen);
|
|
Jiri Slaby |
a2e715 |
insn_buff_sz = a->replacementlen;
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
- /*
|
|
Jiri Slaby |
a2e715 |
- * 0xe8 is a relative jump; fix the offset.
|
|
Jiri Slaby |
a2e715 |
- *
|
|
Jiri Slaby |
a2e715 |
- * Instruction length is checked before the opcode to avoid
|
|
Jiri Slaby |
a2e715 |
- * accessing uninitialized bytes for zero-length replacements.
|
|
Jiri Slaby |
a2e715 |
- */
|
|
Jiri Slaby |
a2e715 |
- if (a->replacementlen == 5 && *insn_buff == 0xe8) {
|
|
Jiri Slaby |
a2e715 |
- *(s32 *)(insn_buff + 1) += replacement - instr;
|
|
Jiri Slaby |
a2e715 |
- DPRINTK(ALT, "Fix CALL offset: 0x%x, CALL 0x%lx",
|
|
Jiri Slaby |
a2e715 |
- *(s32 *)(insn_buff + 1),
|
|
Jiri Slaby |
a2e715 |
- (unsigned long)instr + *(s32 *)(insn_buff + 1) + 5);
|
|
Jiri Slaby |
a2e715 |
- }
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
- if (a->replacementlen && is_jmp(replacement[0]))
|
|
Jiri Slaby |
a2e715 |
- recompute_jump(a, instr, replacement, insn_buff);
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
for (; insn_buff_sz < a->instrlen; insn_buff_sz++)
|
|
Jiri Slaby |
a2e715 |
insn_buff[insn_buff_sz] = 0x90;
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
+ apply_relocation(insn_buff, a->instrlen, instr, replacement, a->replacementlen);
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+ DUMP_BYTES(ALT, instr, a->instrlen, "%px: old_insn: ", instr);
|
|
Jiri Slaby |
a2e715 |
+ DUMP_BYTES(ALT, replacement, a->replacementlen, "%px: rpl_insn: ", replacement);
|
|
Jiri Slaby |
a2e715 |
DUMP_BYTES(ALT, insn_buff, insn_buff_sz, "%px: final_insn: ", instr);
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
text_poke_early(instr, insn_buff, insn_buff_sz);
|
|
Jiri Slaby |
a2e715 |
-
|
|
Jiri Slaby |
a2e715 |
-next:
|
|
Jiri Slaby |
a2e715 |
- optimize_nops(instr, a->instrlen);
|
|
Jiri Slaby |
a2e715 |
}
|
|
Jiri Slaby |
a2e715 |
}
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
@@ -851,6 +903,35 @@ static void __init int3_selftest(void)
|
|
Jiri Slaby |
a2e715 |
unregister_die_notifier(&int3_exception_nb);
|
|
Jiri Slaby |
a2e715 |
}
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
+static __initdata int __alt_reloc_selftest_addr;
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+__visible noinline void __init __alt_reloc_selftest(void *arg)
|
|
Jiri Slaby |
a2e715 |
+{
|
|
Jiri Slaby |
a2e715 |
+ WARN_ON(arg != &__alt_reloc_selftest_addr);
|
|
Jiri Slaby |
a2e715 |
+}
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+static noinline void __init alt_reloc_selftest(void)
|
|
Jiri Slaby |
a2e715 |
+{
|
|
Jiri Slaby |
a2e715 |
+ /*
|
|
Jiri Slaby |
a2e715 |
+ * Tests apply_relocation().
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * This has a relative immediate (CALL) in a place other than the first
|
|
Jiri Slaby |
a2e715 |
+ * instruction and additionally on x86_64 we get a RIP-relative LEA:
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * lea 0x0(%rip),%rdi # 5d0: R_X86_64_PC32 .init.data+0x5566c
|
|
Jiri Slaby |
a2e715 |
+ * call +0 # 5d5: R_X86_64_PLT32 __alt_reloc_selftest-0x4
|
|
Jiri Slaby |
a2e715 |
+ *
|
|
Jiri Slaby |
a2e715 |
+ * Getting this wrong will either crash and burn or tickle the WARN
|
|
Jiri Slaby |
a2e715 |
+ * above.
|
|
Jiri Slaby |
a2e715 |
+ */
|
|
Jiri Slaby |
a2e715 |
+ asm_inline volatile (
|
|
Jiri Slaby |
a2e715 |
+ ALTERNATIVE("", "lea %[mem], %%" _ASM_ARG1 "; call __alt_reloc_selftest;", X86_FEATURE_ALWAYS)
|
|
Jiri Slaby |
a2e715 |
+ : /* output */
|
|
Jiri Slaby |
a2e715 |
+ : [mem] "m" (__alt_reloc_selftest_addr)
|
|
Jiri Slaby |
a2e715 |
+ : _ASM_ARG1
|
|
Jiri Slaby |
a2e715 |
+ );
|
|
Jiri Slaby |
a2e715 |
+}
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
void __init alternative_instructions(void)
|
|
Jiri Slaby |
a2e715 |
{
|
|
Jiri Slaby |
a2e715 |
int3_selftest();
|
|
Jiri Slaby |
a2e715 |
@@ -927,6 +1008,8 @@ void __init alternative_instructions(voi
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
restart_nmi();
|
|
Jiri Slaby |
a2e715 |
alternatives_patched = 1;
|
|
Jiri Slaby |
a2e715 |
+
|
|
Jiri Slaby |
a2e715 |
+ alt_reloc_selftest();
|
|
Jiri Slaby |
a2e715 |
}
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
/**
|
|
Jiri Slaby |
a2e715 |
--- a/tools/objtool/arch/x86/special.c
|
|
Jiri Slaby |
a2e715 |
+++ b/tools/objtool/arch/x86/special.c
|
|
Jiri Slaby |
a2e715 |
@@ -42,13 +42,7 @@ bool arch_support_alt_relocation(struct
|
|
Jiri Slaby |
a2e715 |
struct instruction *insn,
|
|
Jiri Slaby |
a2e715 |
struct reloc *reloc)
|
|
Jiri Slaby |
a2e715 |
{
|
|
Jiri Slaby |
a2e715 |
- /*
|
|
Jiri Slaby |
a2e715 |
- * The x86 alternatives code adjusts the offsets only when it
|
|
Jiri Slaby |
a2e715 |
- * encounters a branch instruction at the very beginning of the
|
|
Jiri Slaby |
a2e715 |
- * replacement group.
|
|
Jiri Slaby |
a2e715 |
- */
|
|
Jiri Slaby |
a2e715 |
- return insn->offset == special_alt->new_off &&
|
|
Jiri Slaby |
a2e715 |
- (insn->type == INSN_CALL || is_jump(insn));
|
|
Jiri Slaby |
a2e715 |
+ return true;
|
|
Jiri Slaby |
a2e715 |
}
|
|
Jiri Slaby |
a2e715 |
|
|
Jiri Slaby |
a2e715 |
/*
|