Skip to content

Commit 9bc0bb5

Browse files
Peter ZijlstraIngo Molnar
authored andcommitted
objtool/x86: Rewrite retpoline thunk calls
When the compiler emits: "CALL __x86_indirect_thunk_\reg" for an indirect call, have objtool rewrite it to: ALTERNATIVE "call __x86_indirect_thunk_\reg", "call *%reg", ALT_NOT(X86_FEATURE_RETPOLINE) Additionally, in order to not emit endless identical .altinst_replacement chunks, use a global symbol for them, see __x86_indirect_alt_*. This also avoids objtool from having to do code generation. Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Reviewed-by: Miroslav Benes <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 50e7b4a commit 9bc0bb5

File tree

3 files changed

+167
-3
lines changed

3 files changed

+167
-3
lines changed

arch/x86/include/asm/asm-prototypes.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,19 @@ extern void cmpxchg8b_emu(void);
1919

2020
#ifdef CONFIG_RETPOLINE
2121

22-
#define DECL_INDIRECT_THUNK(reg) \
22+
#undef GEN
23+
#define GEN(reg) \
2324
extern asmlinkage void __x86_indirect_thunk_ ## reg (void);
25+
#include <asm/GEN-for-each-reg.h>
26+
27+
#undef GEN
28+
#define GEN(reg) \
29+
extern asmlinkage void __x86_indirect_alt_call_ ## reg (void);
30+
#include <asm/GEN-for-each-reg.h>
2431

2532
#undef GEN
26-
#define GEN(reg) DECL_INDIRECT_THUNK(reg)
33+
#define GEN(reg) \
34+
extern asmlinkage void __x86_indirect_alt_jmp_ ## reg (void);
2735
#include <asm/GEN-for-each-reg.h>
2836

2937
#endif /* CONFIG_RETPOLINE */

arch/x86/lib/retpoline.S

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include <asm/unwind_hints.h>
1111
#include <asm/frame.h>
1212

13+
.section .text.__x86.indirect_thunk
14+
1315
.macro RETPOLINE reg
1416
ANNOTATE_INTRA_FUNCTION_CALL
1517
call .Ldo_rop_\@
@@ -25,9 +27,9 @@
2527
.endm
2628

2729
.macro THUNK reg
28-
.section .text.__x86.indirect_thunk
2930

3031
.align 32
32+
3133
SYM_FUNC_START(__x86_indirect_thunk_\reg)
3234

3335
ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; jmp *%\reg), \
@@ -38,6 +40,32 @@ SYM_FUNC_END(__x86_indirect_thunk_\reg)
3840

3941
.endm
4042

43+
/*
44+
* This generates .altinstr_replacement symbols for use by objtool. They,
45+
* however, must not actually live in .altinstr_replacement since that will be
46+
* discarded after init, but module alternatives will also reference these
47+
* symbols.
48+
*
49+
* Their names matches the "__x86_indirect_" prefix to mark them as retpolines.
50+
*/
51+
.macro ALT_THUNK reg
52+
53+
.align 1
54+
55+
SYM_FUNC_START_NOALIGN(__x86_indirect_alt_call_\reg)
56+
ANNOTATE_RETPOLINE_SAFE
57+
1: call *%\reg
58+
2: .skip 5-(2b-1b), 0x90
59+
SYM_FUNC_END(__x86_indirect_alt_call_\reg)
60+
61+
SYM_FUNC_START_NOALIGN(__x86_indirect_alt_jmp_\reg)
62+
ANNOTATE_RETPOLINE_SAFE
63+
1: jmp *%\reg
64+
2: .skip 5-(2b-1b), 0x90
65+
SYM_FUNC_END(__x86_indirect_alt_jmp_\reg)
66+
67+
.endm
68+
4169
/*
4270
* Despite being an assembler file we can't just use .irp here
4371
* because __KSYM_DEPS__ only uses the C preprocessor and would
@@ -61,3 +89,14 @@ SYM_FUNC_END(__x86_indirect_thunk_\reg)
6189
#define GEN(reg) EXPORT_THUNK(reg)
6290
#include <asm/GEN-for-each-reg.h>
6391

92+
#undef GEN
93+
#define GEN(reg) ALT_THUNK reg
94+
#include <asm/GEN-for-each-reg.h>
95+
96+
#undef GEN
97+
#define GEN(reg) __EXPORT_THUNK(__x86_indirect_alt_call_ ## reg)
98+
#include <asm/GEN-for-each-reg.h>
99+
100+
#undef GEN
101+
#define GEN(reg) __EXPORT_THUNK(__x86_indirect_alt_jmp_ ## reg)
102+
#include <asm/GEN-for-each-reg.h>

tools/objtool/arch/x86/decode.c

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <objtool/elf.h>
2020
#include <objtool/arch.h>
2121
#include <objtool/warn.h>
22+
#include <arch/elf.h>
2223

2324
static unsigned char op_to_cfi_reg[][2] = {
2425
{CFI_AX, CFI_R8},
@@ -613,6 +614,122 @@ const char *arch_nop_insn(int len)
613614
return nops[len-1];
614615
}
615616

617+
/* asm/alternative.h ? */
618+
619+
#define ALTINSTR_FLAG_INV (1 << 15)
620+
#define ALT_NOT(feat) ((feat) | ALTINSTR_FLAG_INV)
621+
622+
struct alt_instr {
623+
s32 instr_offset; /* original instruction */
624+
s32 repl_offset; /* offset to replacement instruction */
625+
u16 cpuid; /* cpuid bit set for replacement */
626+
u8 instrlen; /* length of original instruction */
627+
u8 replacementlen; /* length of new instruction */
628+
} __packed;
629+
630+
static int elf_add_alternative(struct elf *elf,
631+
struct instruction *orig, struct symbol *sym,
632+
int cpuid, u8 orig_len, u8 repl_len)
633+
{
634+
const int size = sizeof(struct alt_instr);
635+
struct alt_instr *alt;
636+
struct section *sec;
637+
Elf_Scn *s;
638+
639+
sec = find_section_by_name(elf, ".altinstructions");
640+
if (!sec) {
641+
sec = elf_create_section(elf, ".altinstructions",
642+
SHF_WRITE, size, 0);
643+
644+
if (!sec) {
645+
WARN_ELF("elf_create_section");
646+
return -1;
647+
}
648+
}
649+
650+
s = elf_getscn(elf->elf, sec->idx);
651+
if (!s) {
652+
WARN_ELF("elf_getscn");
653+
return -1;
654+
}
655+
656+
sec->data = elf_newdata(s);
657+
if (!sec->data) {
658+
WARN_ELF("elf_newdata");
659+
return -1;
660+
}
661+
662+
sec->data->d_size = size;
663+
sec->data->d_align = 1;
664+
665+
alt = sec->data->d_buf = malloc(size);
666+
if (!sec->data->d_buf) {
667+
perror("malloc");
668+
return -1;
669+
}
670+
memset(sec->data->d_buf, 0, size);
671+
672+
if (elf_add_reloc_to_insn(elf, sec, sec->sh.sh_size,
673+
R_X86_64_PC32, orig->sec, orig->offset)) {
674+
WARN("elf_create_reloc: alt_instr::instr_offset");
675+
return -1;
676+
}
677+
678+
if (elf_add_reloc(elf, sec, sec->sh.sh_size + 4,
679+
R_X86_64_PC32, sym, 0)) {
680+
WARN("elf_create_reloc: alt_instr::repl_offset");
681+
return -1;
682+
}
683+
684+
alt->cpuid = cpuid;
685+
alt->instrlen = orig_len;
686+
alt->replacementlen = repl_len;
687+
688+
sec->sh.sh_size += size;
689+
sec->changed = true;
690+
691+
return 0;
692+
}
693+
694+
#define X86_FEATURE_RETPOLINE ( 7*32+12)
695+
696+
int arch_rewrite_retpolines(struct objtool_file *file)
697+
{
698+
struct instruction *insn;
699+
struct reloc *reloc;
700+
struct symbol *sym;
701+
char name[32] = "";
702+
703+
list_for_each_entry(insn, &file->retpoline_call_list, call_node) {
704+
705+
if (!strcmp(insn->sec->name, ".text.__x86.indirect_thunk"))
706+
continue;
707+
708+
reloc = insn->reloc;
709+
710+
sprintf(name, "__x86_indirect_alt_%s_%s",
711+
insn->type == INSN_JUMP_DYNAMIC ? "jmp" : "call",
712+
reloc->sym->name + 21);
713+
714+
sym = find_symbol_by_name(file->elf, name);
715+
if (!sym) {
716+
sym = elf_create_undef_symbol(file->elf, name);
717+
if (!sym) {
718+
WARN("elf_create_undef_symbol");
719+
return -1;
720+
}
721+
}
722+
723+
if (elf_add_alternative(file->elf, insn, sym,
724+
ALT_NOT(X86_FEATURE_RETPOLINE), 5, 5)) {
725+
WARN("elf_add_alternative");
726+
return -1;
727+
}
728+
}
729+
730+
return 0;
731+
}
732+
616733
int arch_decode_hint_reg(struct instruction *insn, u8 sp_reg)
617734
{
618735
struct cfi_reg *cfa = &insn->cfi.cfa;

0 commit comments

Comments
 (0)