mirror of https://github.com/golang/go.git
cmd/asm: improve detector for incorrect R15 usage when dynamic linking
Fixes #58632 Change-Id: Idb19af2ac693ea5920da57c1808f1bc02702929d Reviewed-on: https://go-review.googlesource.com/c/go/+/476295 Reviewed-by: Cherry Mui <cherryyz@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Keith Randall <khr@google.com> Run-TryBot: Keith Randall <khr@golang.org>
This commit is contained in:
parent
70308d1375
commit
455168d113
|
|
@ -72,3 +72,100 @@ one:
|
|||
TEXT ·a13(SB), 0, $0-0
|
||||
MULXQ runtime·writeBarrier(SB), AX, CX
|
||||
RET
|
||||
|
||||
// Various special cases in the use-R15-after-global-access-when-dynlinking check.
|
||||
// See issue 58632.
|
||||
TEXT ·a14(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
MULXQ R15, AX, BX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
|
||||
RET
|
||||
TEXT ·a15(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
MULXQ AX, R15, BX
|
||||
ADDQ $1, R15
|
||||
RET
|
||||
TEXT ·a16(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
MULXQ AX, BX, R15
|
||||
ADDQ $1, R15
|
||||
RET
|
||||
TEXT ·a17(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
MOVQ (R15), AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
|
||||
RET
|
||||
TEXT ·a18(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
MOVQ (CX)(R15*1), AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
|
||||
RET
|
||||
TEXT ·a19(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
MOVQ AX, (R15) // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
|
||||
RET
|
||||
TEXT ·a20(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
MOVQ AX, (CX)(R15*1) // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
|
||||
RET
|
||||
TEXT ·a21(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
MOVBLSX AX, R15
|
||||
ADDQ $1, R15
|
||||
RET
|
||||
TEXT ·a22(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
PMOVMSKB X0, R15
|
||||
ADDQ $1, R15
|
||||
RET
|
||||
TEXT ·a23(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
LEAQ (AX)(CX*1), R15
|
||||
RET
|
||||
TEXT ·a24(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
LEAQ (R15)(AX*1), AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
|
||||
RET
|
||||
TEXT ·a25(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
LEAQ (AX)(R15*1), AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
|
||||
RET
|
||||
TEXT ·a26(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
IMUL3Q $33, AX, R15
|
||||
ADDQ $1, R15
|
||||
RET
|
||||
TEXT ·a27(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
IMUL3Q $33, R15, AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
|
||||
RET
|
||||
TEXT ·a28(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
PEXTRD $0, X0, R15
|
||||
ADDQ $1, R15
|
||||
RET
|
||||
TEXT ·a29(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
VPEXTRD $0, X0, R15
|
||||
ADDQ $1, R15
|
||||
RET
|
||||
TEXT ·a30(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
BSFQ R15, AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
|
||||
RET
|
||||
TEXT ·a31(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
BSFQ AX, R15
|
||||
ADDQ $1, R15
|
||||
RET
|
||||
TEXT ·a32(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
POPCNTL R15, AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
|
||||
RET
|
||||
TEXT ·a33(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
POPCNTL AX, R15
|
||||
ADDQ $1, R15
|
||||
RET
|
||||
TEXT ·a34(SB), 0, $0-0
|
||||
CMPL runtime·writeBarrier(SB), $0
|
||||
SHLXQ AX, CX, R15
|
||||
ADDQ $1, R15
|
||||
RET
|
||||
|
|
|
|||
|
|
@ -1254,29 +1254,6 @@ func progMentionsR15(p *obj.Prog) bool {
|
|||
return addrMentionsR15(&p.From) || addrMentionsR15(&p.To) || isR15(p.Reg) || addrMentionsR15(p.GetFrom3())
|
||||
}
|
||||
|
||||
// progOverwritesR15 reports whether p writes to R15 and does not depend on
|
||||
// the previous value of R15.
|
||||
func progOverwritesR15(p *obj.Prog) bool {
|
||||
if !(p.To.Type == obj.TYPE_REG && isR15(p.To.Reg)) {
|
||||
// Not writing to R15.
|
||||
return false
|
||||
}
|
||||
if (p.As == AXORL || p.As == AXORQ) && p.From.Type == obj.TYPE_REG && isR15(p.From.Reg) {
|
||||
// These look like uses of R15, but aren't, so we must detect these
|
||||
// before the use check below.
|
||||
return true
|
||||
}
|
||||
if addrMentionsR15(&p.From) || isR15(p.Reg) || addrMentionsR15(p.GetFrom3()) {
|
||||
// use before overwrite
|
||||
return false
|
||||
}
|
||||
if p.As == AMOVL || p.As == AMOVQ || p.As == APOPQ {
|
||||
return true
|
||||
// TODO: MOVB might be ok if we only ever use R15B.
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func addrUsesGlobal(a *obj.Addr) bool {
|
||||
if a == nil {
|
||||
return false
|
||||
|
|
@ -1296,6 +1273,114 @@ func progUsesGlobal(p *obj.Prog) bool {
|
|||
return addrUsesGlobal(&p.From) || addrUsesGlobal(&p.To) || addrUsesGlobal(p.GetFrom3())
|
||||
}
|
||||
|
||||
type rwMask int
|
||||
|
||||
const (
|
||||
readFrom rwMask = 1 << iota
|
||||
readTo
|
||||
readReg
|
||||
readFrom3
|
||||
writeFrom
|
||||
writeTo
|
||||
writeReg
|
||||
writeFrom3
|
||||
)
|
||||
|
||||
// progRW returns a mask describing the effects of the instruction p.
|
||||
// Note: this isn't exhaustively accurate. It is only currently used for detecting
|
||||
// reads/writes to R15, so SSE register behavior isn't fully correct, and
|
||||
// other weird cases (e.g. writes to DX by CLD) also aren't captured.
|
||||
func progRW(p *obj.Prog) rwMask {
|
||||
var m rwMask
|
||||
// Default for most instructions
|
||||
if p.From.Type != obj.TYPE_NONE {
|
||||
m |= readFrom
|
||||
}
|
||||
if p.To.Type != obj.TYPE_NONE {
|
||||
// Most x86 instructions update the To value
|
||||
m |= readTo | writeTo
|
||||
}
|
||||
if p.Reg != 0 {
|
||||
m |= readReg
|
||||
}
|
||||
if p.GetFrom3() != nil {
|
||||
m |= readFrom3
|
||||
}
|
||||
|
||||
// Lots of exceptions to the above defaults.
|
||||
name := p.As.String()
|
||||
if strings.HasPrefix(name, "MOV") || strings.HasPrefix(name, "PMOV") {
|
||||
// MOV instructions don't read To.
|
||||
m &^= readTo
|
||||
}
|
||||
switch p.As {
|
||||
case APOPW, APOPL, APOPQ,
|
||||
ALEAL, ALEAQ,
|
||||
AIMUL3W, AIMUL3L, AIMUL3Q,
|
||||
APEXTRB, APEXTRW, APEXTRD, APEXTRQ, AVPEXTRB, AVPEXTRW, AVPEXTRD, AVPEXTRQ, AEXTRACTPS,
|
||||
ABSFW, ABSFL, ABSFQ, ABSRW, ABSRL, ABSRQ, APOPCNTW, APOPCNTL, APOPCNTQ, ALZCNTW, ALZCNTL, ALZCNTQ,
|
||||
ASHLXL, ASHLXQ, ASHRXL, ASHRXQ, ASARXL, ASARXQ:
|
||||
// These instructions are pure writes to To. They don't use its old value.
|
||||
m &^= readTo
|
||||
case AXORL, AXORQ:
|
||||
// Register-clearing idiom doesn't read previous value.
|
||||
if p.From.Type == obj.TYPE_REG && p.To.Type == obj.TYPE_REG && p.From.Reg == p.To.Reg {
|
||||
m &^= readFrom | readTo
|
||||
}
|
||||
case AMULXL, AMULXQ:
|
||||
// These are write-only to both To and From3.
|
||||
m &^= readTo | readFrom3
|
||||
m |= writeFrom3
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// progReadsR15 reports whether p reads the register R15.
|
||||
func progReadsR15(p *obj.Prog) bool {
|
||||
m := progRW(p)
|
||||
if m&readFrom != 0 && p.From.Type == obj.TYPE_REG && isR15(p.From.Reg) {
|
||||
return true
|
||||
}
|
||||
if m&readTo != 0 && p.To.Type == obj.TYPE_REG && isR15(p.To.Reg) {
|
||||
return true
|
||||
}
|
||||
if m&readReg != 0 && isR15(p.Reg) {
|
||||
return true
|
||||
}
|
||||
if m&readFrom3 != 0 && p.GetFrom3().Type == obj.TYPE_REG && isR15(p.GetFrom3().Reg) {
|
||||
return true
|
||||
}
|
||||
// reads of the index registers
|
||||
if p.From.Type == obj.TYPE_MEM && (isR15(p.From.Reg) || isR15(p.From.Index)) {
|
||||
return true
|
||||
}
|
||||
if p.To.Type == obj.TYPE_MEM && (isR15(p.To.Reg) || isR15(p.To.Index)) {
|
||||
return true
|
||||
}
|
||||
if f3 := p.GetFrom3(); f3 != nil && f3.Type == obj.TYPE_MEM && (isR15(f3.Reg) || isR15(f3.Index)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// progWritesR15 reports whether p writes the register R15.
|
||||
func progWritesR15(p *obj.Prog) bool {
|
||||
m := progRW(p)
|
||||
if m&writeFrom != 0 && p.From.Type == obj.TYPE_REG && isR15(p.From.Reg) {
|
||||
return true
|
||||
}
|
||||
if m&writeTo != 0 && p.To.Type == obj.TYPE_REG && isR15(p.To.Reg) {
|
||||
return true
|
||||
}
|
||||
if m&writeReg != 0 && isR15(p.Reg) {
|
||||
return true
|
||||
}
|
||||
if m&writeFrom3 != 0 && p.GetFrom3().Type == obj.TYPE_REG && isR15(p.GetFrom3().Reg) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func errorCheck(ctxt *obj.Link, s *obj.LSym) {
|
||||
// When dynamic linking, R15 is used to access globals. Reject code that
|
||||
// uses R15 after a global variable access.
|
||||
|
|
@ -1320,6 +1405,15 @@ func errorCheck(ctxt *obj.Link, s *obj.LSym) {
|
|||
for len(work) > 0 {
|
||||
p := work[len(work)-1]
|
||||
work = work[:len(work)-1]
|
||||
if progReadsR15(p) {
|
||||
pos := ctxt.PosTable.Pos(p.Pos)
|
||||
ctxt.Diag("%s:%s: when dynamic linking, R15 is clobbered by a global variable access and is used here: %v", path.Base(pos.Filename()), pos.LineNumber(), p)
|
||||
break // only report one error
|
||||
}
|
||||
if progWritesR15(p) {
|
||||
// R15 is overwritten by this instruction. Its value is not junk any more.
|
||||
continue
|
||||
}
|
||||
if q := p.To.Target(); q != nil && q.Mark&markBit == 0 {
|
||||
q.Mark |= markBit
|
||||
work = append(work, q)
|
||||
|
|
@ -1327,15 +1421,6 @@ func errorCheck(ctxt *obj.Link, s *obj.LSym) {
|
|||
if p.As == obj.AJMP || p.As == obj.ARET {
|
||||
continue // no fallthrough
|
||||
}
|
||||
if progMentionsR15(p) {
|
||||
if progOverwritesR15(p) {
|
||||
// R15 is overwritten by this instruction. Its value is not junk any more.
|
||||
continue
|
||||
}
|
||||
pos := ctxt.PosTable.Pos(p.Pos)
|
||||
ctxt.Diag("%s:%s: when dynamic linking, R15 is clobbered by a global variable access and is used here: %v", path.Base(pos.Filename()), pos.LineNumber(), p)
|
||||
break // only report one error
|
||||
}
|
||||
if q := p.Link; q != nil && q.Mark&markBit == 0 {
|
||||
q.Mark |= markBit
|
||||
work = append(work, q)
|
||||
|
|
|
|||
Loading…
Reference in New Issue