1
2
3
4
5 package ld
6
7 import (
8 "cmd/internal/obj"
9 "cmd/internal/objabi"
10 "cmd/link/internal/loader"
11 "fmt"
12 "internal/buildcfg"
13 "sort"
14 "strings"
15 )
16
17 type stackCheck struct {
18 ctxt *Link
19 ldr *loader.Loader
20 morestack loader.Sym
21 callSize int
22
23
24
25 height map[loader.Sym]int16
26
27
28
29
30 graph map[loader.Sym][]stackCheckEdge
31 }
32
33 type stackCheckEdge struct {
34 growth int
35 target loader.Sym
36 }
37
38
39
40
41 const stackCheckCycle int16 = 1<<15 - 1
42
43
44
45 const stackCheckIndirect loader.Sym = ^loader.Sym(0)
46
47
48
49
50
51
52
53
54 func (ctxt *Link) doStackCheck() {
55 sc := newStackCheck(ctxt, false)
56
57
58
59
60
61
62
63
64 limit := objabi.StackNosplit(*flagRace) - sc.callSize
65 if buildcfg.GOARCH == "arm64" {
66
67 limit -= 8
68 }
69
70
71
72
73
74
75
76
77 var failed []loader.Sym
78 for _, s := range ctxt.Textp {
79 if sc.check(s) > limit {
80 failed = append(failed, s)
81 }
82 }
83
84 if len(failed) > 0 {
85
86
87
88
89 sc = newStackCheck(ctxt, true)
90 for _, s := range failed {
91 sc.check(s)
92 }
93
94
95
96 roots := sc.findRoots()
97
98
99
100
101
102
103 for _, root := range roots {
104 ctxt.Errorf(root, "nosplit stack over %d byte limit", limit)
105 chain := []stackCheckChain{{stackCheckEdge{0, root}, false}}
106 sc.report(root, limit, &chain)
107 }
108 }
109 }
110
111 func newStackCheck(ctxt *Link, graph bool) *stackCheck {
112 sc := &stackCheck{
113 ctxt: ctxt,
114 ldr: ctxt.loader,
115 morestack: ctxt.loader.Lookup("runtime.morestack", 0),
116 height: make(map[loader.Sym]int16, len(ctxt.Textp)),
117 }
118
119
120 if !ctxt.Arch.HasLR {
121 sc.callSize = ctxt.Arch.RegSize
122 }
123
124 if graph {
125
126 sc.graph = make(map[loader.Sym][]stackCheckEdge)
127 }
128
129 return sc
130 }
131
132 func (sc *stackCheck) symName(sym loader.Sym) string {
133 switch sym {
134 case stackCheckIndirect:
135 return "indirect"
136 case 0:
137 return "leaf"
138 }
139 return fmt.Sprintf("%s<%d>", sc.ldr.SymName(sym), sc.ldr.SymVersion(sym))
140 }
141
142
143
144 func (sc *stackCheck) check(sym loader.Sym) int {
145 if h, ok := sc.height[sym]; ok {
146
147 return int(h)
148 }
149
150 sc.height[sym] = stackCheckCycle
151
152 h, edges := sc.computeHeight(sym, *flagDebugNosplit || sc.graph != nil)
153 if h > int(stackCheckCycle) {
154 h = int(stackCheckCycle)
155 }
156 sc.height[sym] = int16(h)
157 if sc.graph != nil {
158 sc.graph[sym] = edges
159 }
160
161 if *flagDebugNosplit {
162 for _, edge := range edges {
163 fmt.Printf("nosplit: %s +%d", sc.symName(sym), edge.growth)
164 if edge.target == 0 {
165
166 fmt.Printf("\n")
167 } else {
168 fmt.Printf(" -> %s\n", sc.symName(edge.target))
169 }
170 }
171 }
172
173 return h
174 }
175
176
177
178
179
180
181 func (sc *stackCheck) computeHeight(sym loader.Sym, graph bool) (int, []stackCheckEdge) {
182 ldr := sc.ldr
183
184
185 if sym == sc.morestack {
186
187
188
189
190
191 return 0, nil
192 }
193 if sym == stackCheckIndirect {
194
195
196
197 return sc.callSize, []stackCheckEdge{{sc.callSize, sc.morestack}}
198 }
199
200
201
202
203 if ldr.AttrExternal(sym) {
204 return 0, nil
205 }
206 if info := ldr.FuncInfo(sym); !info.Valid() {
207 return 0, nil
208 }
209
210
211
212 var edges []stackCheckEdge
213 maxHeight := 0
214 ctxt := sc.ctxt
215
216
217
218 addEdge := func(growth int, target loader.Sym) {
219 if graph {
220 edges = append(edges, stackCheckEdge{growth, target})
221 }
222 height := growth
223 if target != 0 {
224 height += sc.check(target)
225 }
226 if height > maxHeight {
227 maxHeight = height
228 }
229 }
230
231 if !ldr.IsNoSplit(sym) {
232
233
234
235 addEdge(sc.callSize, sc.morestack)
236 return maxHeight, edges
237 }
238
239
240
241
242
243
244 maxLocalHeight := 0
245 relocs, ri := ldr.Relocs(sym), 0
246 pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
247 for pcsp.Init(ldr.Data(ldr.Pcsp(sym))); !pcsp.Done; pcsp.Next() {
248
249 height := int(pcsp.Value)
250 if height > maxLocalHeight {
251 maxLocalHeight = height
252 }
253
254
255 for ; ri < relocs.Count(); ri++ {
256 r := relocs.At(ri)
257 if uint32(r.Off()) >= pcsp.NextPC {
258 break
259 }
260 t := r.Type()
261 if t.IsDirectCall() || t == objabi.R_CALLIND {
262 growth := height + sc.callSize
263 var target loader.Sym
264 if t == objabi.R_CALLIND {
265 target = stackCheckIndirect
266 } else {
267 target = r.Sym()
268 }
269 addEdge(growth, target)
270 }
271 }
272 }
273 if maxLocalHeight > maxHeight {
274
275
276
277
278 addEdge(maxLocalHeight, 0)
279 }
280
281 return maxHeight, edges
282 }
283
284 func (sc *stackCheck) findRoots() []loader.Sym {
285
286 nodes := make(map[loader.Sym]struct{})
287 for k := range sc.graph {
288 nodes[k] = struct{}{}
289 }
290
291
292
293
294
295
296 var walk func(origin, sym loader.Sym) (cycle bool, lowest loader.Sym)
297 walk = func(origin, sym loader.Sym) (cycle bool, lowest loader.Sym) {
298 if _, ok := nodes[sym]; !ok {
299
300 return false, 0
301 }
302 delete(nodes, sym)
303
304 if origin == sym {
305
306
307
308
309 return true, sym
310 }
311
312
313 for _, out := range sc.graph[sym] {
314 if c, l := walk(origin, out.target); c {
315 cycle = true
316 if lowest == 0 {
317
318
319
320 lowest = sym
321 }
322 if l < lowest {
323 lowest = l
324 }
325 }
326 }
327 return
328 }
329 for k := range nodes {
330
331 for _, out := range sc.graph[k] {
332 if cycle, lowest := walk(k, out.target); cycle {
333
334
335
336 nodes[lowest] = struct{}{}
337 }
338 }
339 }
340
341
342
343 var roots []loader.Sym
344 for k := range nodes {
345 roots = append(roots, k)
346 }
347 sort.Slice(roots, func(i, j int) bool {
348 h1, h2 := sc.height[roots[i]], sc.height[roots[j]]
349 if h1 != h2 {
350 return h1 > h2
351 }
352
353 return roots[i] < roots[j]
354 })
355 return roots
356 }
357
358 type stackCheckChain struct {
359 stackCheckEdge
360 printed bool
361 }
362
363 func (sc *stackCheck) report(sym loader.Sym, depth int, chain *[]stackCheckChain) {
364
365
366
367 edges, ok := sc.graph[sym]
368 isCycle := !(ok || sym == 0)
369 delete(sc.graph, sym)
370 for _, out := range edges {
371 *chain = append(*chain, stackCheckChain{out, false})
372 sc.report(out.target, depth-out.growth, chain)
373 *chain = (*chain)[:len(*chain)-1]
374 }
375 sc.graph[sym] = edges
376
377
378
379
380
381
382
383
384
385 if len(edges) == 0 && (depth < 0 || isCycle) {
386 var indent string
387 for i := range *chain {
388 ent := &(*chain)[i]
389 if ent.printed {
390
391
392 continue
393 }
394 ent.printed = true
395
396 if i == 0 {
397
398
399 fmt.Printf("%s\n", sc.symName(ent.target))
400 continue
401 }
402
403 indent = strings.Repeat(" ", i)
404 fmt.Print(indent)
405
406 fmt.Printf("grows %d bytes", ent.growth)
407 if ent.target == 0 {
408
409 } else {
410 fmt.Printf(", calls %s", sc.symName(ent.target))
411 }
412 fmt.Printf("\n")
413 }
414
415 if isCycle {
416 fmt.Printf("%sinfinite cycle\n", indent)
417 } else {
418 fmt.Printf("%s%d bytes over limit\n", indent, -depth)
419 }
420 }
421 }
422
View as plain text