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