1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ld 6 7 import ( 8 "cmd/internal/obj" 9 "fmt" 10 "log" 11 ) 12 13 // funcpctab writes to dst a pc-value table mapping the code in func to the values 14 // returned by valfunc parameterized by arg. The invocation of valfunc to update the 15 // current value is, for each p, 16 // 17 // val = valfunc(func, val, p, 0, arg); 18 // record val as value at p->pc; 19 // val = valfunc(func, val, p, 1, arg); 20 // 21 // where func is the function, val is the current value, p is the instruction being 22 // considered, and arg can be used to further parameterize valfunc. 23 24 // pctofileline computes either the file number (arg == 0) 25 // or the line number (arg == 1) to use at p. 26 // Because p->lineno applies to p, phase == 0 (before p) 27 // takes care of the update. 28 29 // pctospadj computes the sp adjustment in effect. 30 // It is oldval plus any adjustment made by p itself. 31 // The adjustment by p takes effect only after p, so we 32 // apply the change during phase == 1. 33 34 // pctopcdata computes the pcdata value in effect at p. 35 // A PCDATA instruction sets the value in effect at future 36 // non-PCDATA instructions. 37 // Since PCDATA instructions have no width in the final code, 38 // it does not matter which phase we use for the update. 39 40 // iteration over encoded pcdata tables. 41 42 func getvarint(pp *[]byte) uint32 { 43 v := uint32(0) 44 p := *pp 45 for shift := 0; ; shift += 7 { 46 v |= uint32(p[0]&0x7F) << uint(shift) 47 tmp4 := p 48 p = p[1:] 49 if tmp4[0]&0x80 == 0 { 50 break 51 } 52 } 53 54 *pp = p 55 return v 56 } 57 58 func pciternext(it *Pciter) { 59 it.pc = it.nextpc 60 if it.done != 0 { 61 return 62 } 63 if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) { 64 it.done = 1 65 return 66 } 67 68 // value delta 69 v := getvarint(&it.p) 70 71 if v == 0 && it.start == 0 { 72 it.done = 1 73 return 74 } 75 76 it.start = 0 77 dv := int32(v>>1) ^ (int32(v<<31) >> 31) 78 it.value += dv 79 80 // pc delta 81 v = getvarint(&it.p) 82 83 it.nextpc = it.pc + v*it.pcscale 84 } 85 86 func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) { 87 it.d = *d 88 it.p = it.d.P 89 it.pc = 0 90 it.nextpc = 0 91 it.value = -1 92 it.start = 1 93 it.done = 0 94 it.pcscale = uint32(ctxt.Arch.Minlc) 95 pciternext(it) 96 } 97 98 // Copyright 2013 The Go Authors. All rights reserved. 99 // Use of this source code is governed by a BSD-style 100 // license that can be found in the LICENSE file. 101 102 func addvarint(d *Pcdata, val uint32) { 103 n := int32(0) 104 for v := val; v >= 0x80; v >>= 7 { 105 n++ 106 } 107 n++ 108 109 old := len(d.P) 110 for cap(d.P) < len(d.P)+int(n) { 111 d.P = append(d.P[:cap(d.P)], 0) 112 } 113 d.P = d.P[:old+int(n)] 114 115 p := d.P[old:] 116 var v uint32 117 for v = val; v >= 0x80; v >>= 7 { 118 p[0] = byte(v | 0x80) 119 p = p[1:] 120 } 121 p[0] = byte(v) 122 } 123 124 func addpctab(ftab *LSym, off int32, d *Pcdata) int32 { 125 var start int32 126 if len(d.P) > 0 { 127 start = int32(len(ftab.P)) 128 Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P))) 129 copy(ftab.P[start:], d.P) 130 } 131 return int32(setuint32(Ctxt, ftab, int64(off), uint32(start))) 132 } 133 134 func ftabaddstring(ftab *LSym, s string) int32 { 135 n := int32(len(s)) + 1 136 start := int32(len(ftab.P)) 137 Symgrow(Ctxt, ftab, int64(start)+int64(n)+1) 138 copy(ftab.P[start:], s) 139 return start 140 } 141 142 func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) { 143 var f *LSym 144 145 // Give files numbers. 146 for i := 0; i < len(files); i++ { 147 f = files[i] 148 if f.Type != obj.SFILEPATH { 149 ctxt.Nhistfile++ 150 f.Value = int64(ctxt.Nhistfile) 151 f.Type = obj.SFILEPATH 152 f.Next = ctxt.Filesyms 153 ctxt.Filesyms = f 154 } 155 } 156 157 newval := int32(-1) 158 var out Pcdata 159 160 var dv int32 161 var it Pciter 162 var oldval int32 163 var v uint32 164 var val int32 165 for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) { 166 // value delta 167 oldval = it.value 168 169 if oldval == -1 { 170 val = -1 171 } else { 172 if oldval < 0 || oldval >= int32(len(files)) { 173 log.Fatalf("bad pcdata %d", oldval) 174 } 175 val = int32(files[oldval].Value) 176 } 177 178 dv = val - newval 179 newval = val 180 v = (uint32(dv) << 1) ^ uint32(int32(dv>>31)) 181 addvarint(&out, v) 182 183 // pc delta 184 addvarint(&out, (it.nextpc-it.pc)/it.pcscale) 185 } 186 187 // terminating value delta 188 addvarint(&out, 0) 189 190 *d = out 191 } 192 193 func container(s *LSym) int { 194 // We want to generate func table entries only for the "lowest level" symbols, 195 // not containers of subsymbols. 196 if s != nil && s.Type&obj.SCONTAINER != 0 { 197 return 1 198 } 199 return 0 200 } 201 202 // pclntab initializes the pclntab symbol with 203 // runtime function and file name information. 204 205 var pclntab_zpcln Pcln 206 207 // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab. 208 var pclntabNfunc int32 209 var pclntabFiletabOffset int32 210 var pclntabPclntabOffset int32 211 var pclntabFirstFunc *LSym 212 var pclntabLastFunc *LSym 213 214 func pclntab() { 215 funcdata_bytes := int64(0) 216 ftab := Linklookup(Ctxt, "runtime.pclntab", 0) 217 ftab.Type = obj.SPCLNTAB 218 ftab.Reachable = true 219 220 // See golang.org/s/go12symtab for the format. Briefly: 221 // 8-byte header 222 // nfunc [thearch.ptrsize bytes] 223 // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] 224 // end PC [thearch.ptrsize bytes] 225 // offset to file table [4 bytes] 226 nfunc := int32(0) 227 228 // Find container symbols, mark them with SCONTAINER 229 for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { 230 if Ctxt.Cursym.Outer != nil { 231 Ctxt.Cursym.Outer.Type |= obj.SCONTAINER 232 } 233 } 234 235 for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { 236 if container(Ctxt.Cursym) == 0 { 237 nfunc++ 238 } 239 } 240 241 pclntabNfunc = nfunc 242 Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4) 243 setuint32(Ctxt, ftab, 0, 0xfffffffb) 244 setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc)) 245 setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize)) 246 setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize)) 247 pclntabPclntabOffset = int32(8 + Thearch.Ptrsize) 248 249 nfunc = 0 250 var last *LSym 251 var end int32 252 var funcstart int32 253 var i int32 254 var it Pciter 255 var off int32 256 var pcln *Pcln 257 for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { 258 last = Ctxt.Cursym 259 if container(Ctxt.Cursym) != 0 { 260 continue 261 } 262 pcln = Ctxt.Cursym.Pcln 263 if pcln == nil { 264 pcln = &pclntab_zpcln 265 } 266 267 if pclntabFirstFunc == nil { 268 pclntabFirstFunc = Ctxt.Cursym 269 } 270 271 funcstart = int32(len(ftab.P)) 272 funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) 273 274 setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym) 275 setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize)) 276 277 // fixed size of struct, checked below 278 off = funcstart 279 280 end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize) 281 if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) { 282 end += 4 283 } 284 Symgrow(Ctxt, ftab, int64(end)) 285 286 // entry uintptr 287 off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym)) 288 289 // name int32 290 off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name)))) 291 292 // args int32 293 // TODO: Move into funcinfo. 294 off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args))) 295 296 // frame int32 297 // This has been removed (it was never set quite correctly anyway). 298 // Nothing should use it. 299 // Leave an obviously incorrect value. 300 // TODO: Remove entirely. 301 off = int32(setuint32(Ctxt, ftab, int64(off), 0x1234567)) 302 303 if pcln != &pclntab_zpcln { 304 renumberfiles(Ctxt, pcln.File, &pcln.Pcfile) 305 if false { 306 // Sanity check the new numbering 307 for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) { 308 if it.value < 1 || it.value > Ctxt.Nhistfile { 309 Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, Ctxt.Nhistfile) 310 errorexit() 311 } 312 } 313 } 314 } 315 316 // pcdata 317 off = addpctab(ftab, off, &pcln.Pcsp) 318 319 off = addpctab(ftab, off, &pcln.Pcfile) 320 off = addpctab(ftab, off, &pcln.Pcline) 321 off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata))) 322 off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata))) 323 for i = 0; i < int32(pcln.Npcdata); i++ { 324 off = addpctab(ftab, off, &pcln.Pcdata[i]) 325 } 326 327 // funcdata, must be pointer-aligned and we're only int32-aligned. 328 // Missing funcdata will be 0 (nil pointer). 329 if pcln.Nfuncdata > 0 { 330 if off&int32(Thearch.Ptrsize-1) != 0 { 331 off += 4 332 } 333 for i = 0; i < int32(pcln.Nfuncdata); i++ { 334 if pcln.Funcdata[i] == nil { 335 setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize)) 336 } else { 337 // TODO: Dedup. 338 funcdata_bytes += pcln.Funcdata[i].Size 339 340 setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i]) 341 } 342 } 343 344 off += int32(pcln.Nfuncdata) * int32(Thearch.Ptrsize) 345 } 346 347 if off != end { 348 Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln.Npcdata, pcln.Nfuncdata, Thearch.Ptrsize) 349 errorexit() 350 } 351 352 nfunc++ 353 } 354 355 pclntabLastFunc = last 356 // Final entry of table is just end pc. 357 setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size) 358 359 // Start file table. 360 start := int32(len(ftab.P)) 361 362 start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) 363 pclntabFiletabOffset = start 364 setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start)) 365 366 Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4) 367 setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile)) 368 for s := Ctxt.Filesyms; s != nil; s = s.Next { 369 setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name))) 370 } 371 372 ftab.Size = int64(len(ftab.P)) 373 374 if Debug['v'] != 0 { 375 fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes)) 376 } 377 } 378 379 const ( 380 BUCKETSIZE = 256 * MINFUNC 381 SUBBUCKETS = 16 382 SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS 383 NOIDX = 0x7fffffff 384 ) 385 386 // findfunctab generates a lookup table to quickly find the containing 387 // function for a pc. See src/runtime/symtab.go:findfunc for details. 388 func findfunctab() { 389 t := Linklookup(Ctxt, "runtime.findfunctab", 0) 390 t.Type = obj.SRODATA 391 t.Reachable = true 392 t.Local = true 393 394 // find min and max address 395 min := Ctxt.Textp.Value 396 397 max := int64(0) 398 for s := Ctxt.Textp; s != nil; s = s.Next { 399 max = s.Value + s.Size 400 } 401 402 // for each subbucket, compute the minimum of all symbol indexes 403 // that map to that subbucket. 404 n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) 405 406 indexes := make([]int32, n) 407 for i := int32(0); i < n; i++ { 408 indexes[i] = NOIDX 409 } 410 idx := int32(0) 411 var e *LSym 412 var i int32 413 var p int64 414 var q int64 415 for s := Ctxt.Textp; s != nil; s = s.Next { 416 if container(s) != 0 { 417 continue 418 } 419 p = s.Value 420 e = s.Next 421 for container(e) != 0 { 422 e = e.Next 423 } 424 if e != nil { 425 q = e.Value 426 } else { 427 q = max 428 } 429 430 //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); 431 for ; p < q; p += SUBBUCKETSIZE { 432 i = int32((p - min) / SUBBUCKETSIZE) 433 if indexes[i] > idx { 434 indexes[i] = idx 435 } 436 } 437 438 i = int32((q - 1 - min) / SUBBUCKETSIZE) 439 if indexes[i] > idx { 440 indexes[i] = idx 441 } 442 idx++ 443 } 444 445 // allocate table 446 nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) 447 448 Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n)) 449 450 // fill in table 451 var base int32 452 var j int32 453 for i := int32(0); i < nbuckets; i++ { 454 base = indexes[i*SUBBUCKETS] 455 if base == NOIDX { 456 Diag("hole in findfunctab") 457 } 458 setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base)) 459 for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { 460 idx = indexes[i*SUBBUCKETS+j] 461 if idx == NOIDX { 462 Diag("hole in findfunctab") 463 } 464 if idx-base >= 256 { 465 Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) 466 } 467 468 setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) 469 } 470 } 471 } 472