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
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
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.
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)
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)