AllErrors = flag.Bool("e", false, "no limit on number of errors reported")
SymABIs = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble")
Newobj = flag.Bool("newobj", false, "use new object file format")
+
+ Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)")
)
var (
ctxt.Flag_dynlink = *flags.Dynlink
ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
ctxt.Flag_newobj = *flags.Newobj
+ switch *flags.Spectre {
+ default:
+ log.Printf("unknown setting -spectre=%s", *flags.Spectre)
+ os.Exit(2)
+ case "":
+ // nothing
+ case "index":
+ // known to compiler; ignore here so people can use
+ // the same list with -gcflags=-spectre=LIST and -asmflags=-spectrre=LIST
+ case "all", "ret":
+ ctxt.Retpoline = true
+ }
+
ctxt.Bso = bufio.NewWriter(os.Stdout)
defer ctxt.Bso.Flush()
// nothing
case "all":
spectreIndex = true
+ Ctxt.Retpoline = true
case "index":
spectreIndex = true
+ case "ret":
+ Ctxt.Retpoline = true
}
}
}
func span5(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
+ if ctxt.Retpoline {
+ ctxt.Diag("-spectre=ret not supported on arm")
+ ctxt.Retpoline = false // don't keep printing
+ }
+
var p *obj.Prog
var op *obj.Prog
}
func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
+ if ctxt.Retpoline {
+ ctxt.Diag("-spectre=ret not supported on arm64")
+ ctxt.Retpoline = false // don't keep printing
+ }
+
p := cursym.Func.Text
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
return
Flag_optimize bool
Flag_locationlists bool
Flag_newobj bool // use new object file format
+ Retpoline bool // emit use of retpoline stubs for indirect jmp/call
Bso *bufio.Writer
Pathname string
hashmu sync.Mutex // protects hash, funchash
var xcmp [C_NCLASS][C_NCLASS]bool
func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
+ if ctxt.Retpoline {
+ ctxt.Diag("-spectre=ret not supported on mips")
+ ctxt.Retpoline = false // don't keep printing
+ }
+
p := cursym.Func.Text
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
return
// assemble emits machine code.
// It is called at the very end of the assembly process.
func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
+ if ctxt.Retpoline {
+ ctxt.Diag("-spectre=ret not supported on riscv")
+ ctxt.Retpoline = false // don't keep printing
+ }
+
var symcode []uint32
for p := cursym.Func.Text; p != nil; p = p.Link {
switch p.As {
var xcmp [C_NCLASS][C_NCLASS]bool
func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
+ if ctxt.Retpoline {
+ ctxt.Diag("-spectre=ret not supported on s390x")
+ ctxt.Retpoline = false // don't keep printing
+ }
+
p := cursym.Func.Text
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
return
p.As = spadjop(ctxt, ASUBL, ASUBQ)
}
}
+ if ctxt.Retpoline && (p.As == obj.ACALL || p.As == obj.AJMP) && (p.To.Type == obj.TYPE_REG || p.To.Type == obj.TYPE_MEM) {
+ if p.To.Type != obj.TYPE_REG {
+ ctxt.Diag("non-retpoline-compatible: %v", p)
+ continue
+ }
+ p.To.Type = obj.TYPE_BRANCH
+ p.To.Name = obj.NAME_EXTERN
+ p.To.Sym = ctxt.Lookup("runtime.retpoline" + obj.Rconv(int(p.To.Reg)))
+ p.To.Reg = 0
+ p.To.Offset = 0
+ }
}
var count int64 // rough count of number of instructions
/* call function */ \
MOVQ f+8(FP), DX; \
PCDATA $PCDATA_StackMapIndex, $0; \
- CALL (DX); \
+ MOVQ (DX), AX; \
+ CALL AX; \
/* copy return values back */ \
MOVQ argtype+0(FP), DX; \
MOVQ argptr+16(FP), DI; \
DATA runtime·tls_g+0(SB)/8, $16
GLOBL runtime·tls_g+0(SB), NOPTR, $8
#endif
+
+// The compiler and assembler's -spectre=ret mode rewrites
+// all indirect CALL AX / JMP AX instructions to be
+// CALL retpolineAX / JMP retpolineAX.
+// See https://support.google.com/faqs/answer/7625886.
+#define RETPOLINE(reg) \
+ /* CALL setup */ BYTE $0xE8; BYTE $(2+2); BYTE $0; BYTE $0; BYTE $0; \
+ /* nospec: */ \
+ /* PAUSE */ BYTE $0xF3; BYTE $0x90; \
+ /* JMP nospec */ BYTE $0xEB; BYTE $-(2+2); \
+ /* setup: */ \
+ /* MOVQ AX, 0(SP) */ BYTE $0x48|((reg&8)>>1); BYTE $0x89; \
+ BYTE $0x04|((reg&7)<<3); BYTE $0x24; \
+ /* RET */ BYTE $0xC3
+
+TEXT runtime·retpolineAX(SB),NOSPLIT,$0; RETPOLINE(0)
+TEXT runtime·retpolineCX(SB),NOSPLIT,$0; RETPOLINE(1)
+TEXT runtime·retpolineDX(SB),NOSPLIT,$0; RETPOLINE(2)
+TEXT runtime·retpolineBX(SB),NOSPLIT,$0; RETPOLINE(3)
+/* SP is 4, can't happen / magic encodings */
+TEXT runtime·retpolineBP(SB),NOSPLIT,$0; RETPOLINE(5)
+TEXT runtime·retpolineSI(SB),NOSPLIT,$0; RETPOLINE(6)
+TEXT runtime·retpolineDI(SB),NOSPLIT,$0; RETPOLINE(7)
+TEXT runtime·retpolineR8(SB),NOSPLIT,$0; RETPOLINE(8)
+TEXT runtime·retpolineR9(SB),NOSPLIT,$0; RETPOLINE(9)
+TEXT runtime·retpolineR10(SB),NOSPLIT,$0; RETPOLINE(10)
+TEXT runtime·retpolineR11(SB),NOSPLIT,$0; RETPOLINE(11)
+TEXT runtime·retpolineR12(SB),NOSPLIT,$0; RETPOLINE(12)
+TEXT runtime·retpolineR13(SB),NOSPLIT,$0; RETPOLINE(13)
+TEXT runtime·retpolineR14(SB),NOSPLIT,$0; RETPOLINE(14)
+TEXT runtime·retpolineR15(SB),NOSPLIT,$0; RETPOLINE(15)
// Called from assembly only; declared for go vet.
func settls() // argument in DI
+
+// Retpolines, used by -spectre=ret flag in cmd/asm, cmd/compile.
+func retpolineAX()
+func retpolineCX()
+func retpolineDX()
+func retpolineBX()
+func retpolineBP()
+func retpolineSI()
+func retpolineDI()
+func retpolineR8()
+func retpolineR9()
+func retpolineR10()
+func retpolineR11()
+func retpolineR12()
+func retpolineR13()
+func retpolineR14()
+func retpolineR15()
--- /dev/null
+// +build amd64
+// asmcheck -gcflags=-spectre=ret
+
+package codegen
+
+func CallFunc(f func()) {
+ // amd64:`CALL\truntime.retpoline`
+ f()
+}
+
+func CallInterface(x interface{ M() }) {
+ // amd64:`CALL\truntime.retpoline`
+ x.M()
+}