Skip to content

Commit 455168d

Browse files
committed
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 <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Keith Randall <[email protected]> Run-TryBot: Keith Randall <[email protected]>
1 parent 70308d1 commit 455168d

File tree

2 files changed

+214
-32
lines changed

2 files changed

+214
-32
lines changed

src/cmd/asm/internal/asm/testdata/amd64dynlinkerror.s

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,100 @@ one:
7272
TEXT ·a13(SB), 0, $0-0
7373
MULXQ runtime·writeBarrier(SB), AX, CX
7474
RET
75+
76+
// Various special cases in the use-R15-after-global-access-when-dynlinking check.
77+
// See issue 58632.
78+
TEXT ·a14(SB), 0, $0-0
79+
CMPL runtime·writeBarrier(SB), $0
80+
MULXQ R15, AX, BX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
81+
RET
82+
TEXT ·a15(SB), 0, $0-0
83+
CMPL runtime·writeBarrier(SB), $0
84+
MULXQ AX, R15, BX
85+
ADDQ $1, R15
86+
RET
87+
TEXT ·a16(SB), 0, $0-0
88+
CMPL runtime·writeBarrier(SB), $0
89+
MULXQ AX, BX, R15
90+
ADDQ $1, R15
91+
RET
92+
TEXT ·a17(SB), 0, $0-0
93+
CMPL runtime·writeBarrier(SB), $0
94+
MOVQ (R15), AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
95+
RET
96+
TEXT ·a18(SB), 0, $0-0
97+
CMPL runtime·writeBarrier(SB), $0
98+
MOVQ (CX)(R15*1), AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
99+
RET
100+
TEXT ·a19(SB), 0, $0-0
101+
CMPL runtime·writeBarrier(SB), $0
102+
MOVQ AX, (R15) // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
103+
RET
104+
TEXT ·a20(SB), 0, $0-0
105+
CMPL runtime·writeBarrier(SB), $0
106+
MOVQ AX, (CX)(R15*1) // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
107+
RET
108+
TEXT ·a21(SB), 0, $0-0
109+
CMPL runtime·writeBarrier(SB), $0
110+
MOVBLSX AX, R15
111+
ADDQ $1, R15
112+
RET
113+
TEXT ·a22(SB), 0, $0-0
114+
CMPL runtime·writeBarrier(SB), $0
115+
PMOVMSKB X0, R15
116+
ADDQ $1, R15
117+
RET
118+
TEXT ·a23(SB), 0, $0-0
119+
CMPL runtime·writeBarrier(SB), $0
120+
LEAQ (AX)(CX*1), R15
121+
RET
122+
TEXT ·a24(SB), 0, $0-0
123+
CMPL runtime·writeBarrier(SB), $0
124+
LEAQ (R15)(AX*1), AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
125+
RET
126+
TEXT ·a25(SB), 0, $0-0
127+
CMPL runtime·writeBarrier(SB), $0
128+
LEAQ (AX)(R15*1), AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
129+
RET
130+
TEXT ·a26(SB), 0, $0-0
131+
CMPL runtime·writeBarrier(SB), $0
132+
IMUL3Q $33, AX, R15
133+
ADDQ $1, R15
134+
RET
135+
TEXT ·a27(SB), 0, $0-0
136+
CMPL runtime·writeBarrier(SB), $0
137+
IMUL3Q $33, R15, AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
138+
RET
139+
TEXT ·a28(SB), 0, $0-0
140+
CMPL runtime·writeBarrier(SB), $0
141+
PEXTRD $0, X0, R15
142+
ADDQ $1, R15
143+
RET
144+
TEXT ·a29(SB), 0, $0-0
145+
CMPL runtime·writeBarrier(SB), $0
146+
VPEXTRD $0, X0, R15
147+
ADDQ $1, R15
148+
RET
149+
TEXT ·a30(SB), 0, $0-0
150+
CMPL runtime·writeBarrier(SB), $0
151+
BSFQ R15, AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
152+
RET
153+
TEXT ·a31(SB), 0, $0-0
154+
CMPL runtime·writeBarrier(SB), $0
155+
BSFQ AX, R15
156+
ADDQ $1, R15
157+
RET
158+
TEXT ·a32(SB), 0, $0-0
159+
CMPL runtime·writeBarrier(SB), $0
160+
POPCNTL R15, AX // ERROR "when dynamic linking, R15 is clobbered by a global variable access and is used here"
161+
RET
162+
TEXT ·a33(SB), 0, $0-0
163+
CMPL runtime·writeBarrier(SB), $0
164+
POPCNTL AX, R15
165+
ADDQ $1, R15
166+
RET
167+
TEXT ·a34(SB), 0, $0-0
168+
CMPL runtime·writeBarrier(SB), $0
169+
SHLXQ AX, CX, R15
170+
ADDQ $1, R15
171+
RET

src/cmd/internal/obj/x86/obj6.go

Lines changed: 117 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,29 +1254,6 @@ func progMentionsR15(p *obj.Prog) bool {
12541254
return addrMentionsR15(&p.From) || addrMentionsR15(&p.To) || isR15(p.Reg) || addrMentionsR15(p.GetFrom3())
12551255
}
12561256

1257-
// progOverwritesR15 reports whether p writes to R15 and does not depend on
1258-
// the previous value of R15.
1259-
func progOverwritesR15(p *obj.Prog) bool {
1260-
if !(p.To.Type == obj.TYPE_REG && isR15(p.To.Reg)) {
1261-
// Not writing to R15.
1262-
return false
1263-
}
1264-
if (p.As == AXORL || p.As == AXORQ) && p.From.Type == obj.TYPE_REG && isR15(p.From.Reg) {
1265-
// These look like uses of R15, but aren't, so we must detect these
1266-
// before the use check below.
1267-
return true
1268-
}
1269-
if addrMentionsR15(&p.From) || isR15(p.Reg) || addrMentionsR15(p.GetFrom3()) {
1270-
// use before overwrite
1271-
return false
1272-
}
1273-
if p.As == AMOVL || p.As == AMOVQ || p.As == APOPQ {
1274-
return true
1275-
// TODO: MOVB might be ok if we only ever use R15B.
1276-
}
1277-
return false
1278-
}
1279-
12801257
func addrUsesGlobal(a *obj.Addr) bool {
12811258
if a == nil {
12821259
return false
@@ -1296,6 +1273,114 @@ func progUsesGlobal(p *obj.Prog) bool {
12961273
return addrUsesGlobal(&p.From) || addrUsesGlobal(&p.To) || addrUsesGlobal(p.GetFrom3())
12971274
}
12981275

1276+
type rwMask int
1277+
1278+
const (
1279+
readFrom rwMask = 1 << iota
1280+
readTo
1281+
readReg
1282+
readFrom3
1283+
writeFrom
1284+
writeTo
1285+
writeReg
1286+
writeFrom3
1287+
)
1288+
1289+
// progRW returns a mask describing the effects of the instruction p.
1290+
// Note: this isn't exhaustively accurate. It is only currently used for detecting
1291+
// reads/writes to R15, so SSE register behavior isn't fully correct, and
1292+
// other weird cases (e.g. writes to DX by CLD) also aren't captured.
1293+
func progRW(p *obj.Prog) rwMask {
1294+
var m rwMask
1295+
// Default for most instructions
1296+
if p.From.Type != obj.TYPE_NONE {
1297+
m |= readFrom
1298+
}
1299+
if p.To.Type != obj.TYPE_NONE {
1300+
// Most x86 instructions update the To value
1301+
m |= readTo | writeTo
1302+
}
1303+
if p.Reg != 0 {
1304+
m |= readReg
1305+
}
1306+
if p.GetFrom3() != nil {
1307+
m |= readFrom3
1308+
}
1309+
1310+
// Lots of exceptions to the above defaults.
1311+
name := p.As.String()
1312+
if strings.HasPrefix(name, "MOV") || strings.HasPrefix(name, "PMOV") {
1313+
// MOV instructions don't read To.
1314+
m &^= readTo
1315+
}
1316+
switch p.As {
1317+
case APOPW, APOPL, APOPQ,
1318+
ALEAL, ALEAQ,
1319+
AIMUL3W, AIMUL3L, AIMUL3Q,
1320+
APEXTRB, APEXTRW, APEXTRD, APEXTRQ, AVPEXTRB, AVPEXTRW, AVPEXTRD, AVPEXTRQ, AEXTRACTPS,
1321+
ABSFW, ABSFL, ABSFQ, ABSRW, ABSRL, ABSRQ, APOPCNTW, APOPCNTL, APOPCNTQ, ALZCNTW, ALZCNTL, ALZCNTQ,
1322+
ASHLXL, ASHLXQ, ASHRXL, ASHRXQ, ASARXL, ASARXQ:
1323+
// These instructions are pure writes to To. They don't use its old value.
1324+
m &^= readTo
1325+
case AXORL, AXORQ:
1326+
// Register-clearing idiom doesn't read previous value.
1327+
if p.From.Type == obj.TYPE_REG && p.To.Type == obj.TYPE_REG && p.From.Reg == p.To.Reg {
1328+
m &^= readFrom | readTo
1329+
}
1330+
case AMULXL, AMULXQ:
1331+
// These are write-only to both To and From3.
1332+
m &^= readTo | readFrom3
1333+
m |= writeFrom3
1334+
}
1335+
return m
1336+
}
1337+
1338+
// progReadsR15 reports whether p reads the register R15.
1339+
func progReadsR15(p *obj.Prog) bool {
1340+
m := progRW(p)
1341+
if m&readFrom != 0 && p.From.Type == obj.TYPE_REG && isR15(p.From.Reg) {
1342+
return true
1343+
}
1344+
if m&readTo != 0 && p.To.Type == obj.TYPE_REG && isR15(p.To.Reg) {
1345+
return true
1346+
}
1347+
if m&readReg != 0 && isR15(p.Reg) {
1348+
return true
1349+
}
1350+
if m&readFrom3 != 0 && p.GetFrom3().Type == obj.TYPE_REG && isR15(p.GetFrom3().Reg) {
1351+
return true
1352+
}
1353+
// reads of the index registers
1354+
if p.From.Type == obj.TYPE_MEM && (isR15(p.From.Reg) || isR15(p.From.Index)) {
1355+
return true
1356+
}
1357+
if p.To.Type == obj.TYPE_MEM && (isR15(p.To.Reg) || isR15(p.To.Index)) {
1358+
return true
1359+
}
1360+
if f3 := p.GetFrom3(); f3 != nil && f3.Type == obj.TYPE_MEM && (isR15(f3.Reg) || isR15(f3.Index)) {
1361+
return true
1362+
}
1363+
return false
1364+
}
1365+
1366+
// progWritesR15 reports whether p writes the register R15.
1367+
func progWritesR15(p *obj.Prog) bool {
1368+
m := progRW(p)
1369+
if m&writeFrom != 0 && p.From.Type == obj.TYPE_REG && isR15(p.From.Reg) {
1370+
return true
1371+
}
1372+
if m&writeTo != 0 && p.To.Type == obj.TYPE_REG && isR15(p.To.Reg) {
1373+
return true
1374+
}
1375+
if m&writeReg != 0 && isR15(p.Reg) {
1376+
return true
1377+
}
1378+
if m&writeFrom3 != 0 && p.GetFrom3().Type == obj.TYPE_REG && isR15(p.GetFrom3().Reg) {
1379+
return true
1380+
}
1381+
return false
1382+
}
1383+
12991384
func errorCheck(ctxt *obj.Link, s *obj.LSym) {
13001385
// When dynamic linking, R15 is used to access globals. Reject code that
13011386
// uses R15 after a global variable access.
@@ -1320,22 +1405,22 @@ func errorCheck(ctxt *obj.Link, s *obj.LSym) {
13201405
for len(work) > 0 {
13211406
p := work[len(work)-1]
13221407
work = work[:len(work)-1]
1408+
if progReadsR15(p) {
1409+
pos := ctxt.PosTable.Pos(p.Pos)
1410+
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)
1411+
break // only report one error
1412+
}
1413+
if progWritesR15(p) {
1414+
// R15 is overwritten by this instruction. Its value is not junk any more.
1415+
continue
1416+
}
13231417
if q := p.To.Target(); q != nil && q.Mark&markBit == 0 {
13241418
q.Mark |= markBit
13251419
work = append(work, q)
13261420
}
13271421
if p.As == obj.AJMP || p.As == obj.ARET {
13281422
continue // no fallthrough
13291423
}
1330-
if progMentionsR15(p) {
1331-
if progOverwritesR15(p) {
1332-
// R15 is overwritten by this instruction. Its value is not junk any more.
1333-
continue
1334-
}
1335-
pos := ctxt.PosTable.Pos(p.Pos)
1336-
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)
1337-
break // only report one error
1338-
}
13391424
if q := p.Link; q != nil && q.Mark&markBit == 0 {
13401425
q.Mark |= markBit
13411426
work = append(work, q)

0 commit comments

Comments
 (0)