github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/cmd/newlink/pclntab.go (about) 1 // Copyright 2014 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 // Generation of runtime function information (pclntab). 6 7 package main 8 9 import ( 10 "cmd/internal/goobj" 11 "cmd/internal/obj" 12 "encoding/binary" 13 "os" 14 "sort" 15 ) 16 17 var zerofunc goobj.Func 18 19 // pclntab collects the runtime function data for each function that will 20 // be listed in the binary and builds a single table describing all functions. 21 // This table is used at run time for stack traces and to look up PC-specific 22 // information during garbage collection. The symbol created is named 23 // "pclntab" for historical reasons; the scope of the table has grown to 24 // include more than just PC/line number correspondences. 25 // The table format is documented at https://golang.org/s/go12symtab. 26 func (p *Prog) pclntab() { 27 // Count number of functions going into the binary, 28 // so that we can size the initial index correctly. 29 nfunc := 0 30 for _, sym := range p.SymOrder { 31 if sym.Kind != goobj.STEXT { 32 continue 33 } 34 nfunc++ 35 } 36 37 // Table header. 38 buf := new(SymBuffer) 39 buf.Init(p) 40 buf.SetSize(8 + p.ptrsize) 41 off := 0 42 off = buf.Uint32(off, 0xfffffffb) 43 off = buf.Uint8(off, 0) 44 off = buf.Uint8(off, 0) 45 off = buf.Uint8(off, uint8(p.pcquantum)) 46 off = buf.Uint8(off, uint8(p.ptrsize)) 47 off = buf.Uint(off, uint64(nfunc), p.ptrsize) 48 indexOff := off 49 off += (nfunc*2 + 1) * p.ptrsize // function index, to be filled in 50 off += 4 // file table start offset, to be filled in 51 buf.SetSize(off) 52 53 // One-file cache for reading PCData tables from package files. 54 // TODO(rsc): Better I/O strategy. 55 var ( 56 file *os.File 57 fname string 58 ) 59 60 // Files gives the file numbering for source file names recorded 61 // in the binary. 62 files := make(map[string]int) 63 64 // Build the table, build the index, and build the file name numbering. 65 // The loop here must visit functions in the same order that they will 66 // be stored in the binary, or else binary search over the index will fail. 67 // The runtime checks that the index is sorted properly at program start time. 68 var lastSym *Sym 69 for _, sym := range p.SymOrder { 70 if sym.Kind != goobj.STEXT { 71 continue 72 } 73 lastSym = sym 74 75 // Treat no recorded function information same as all zeros. 76 f := sym.Func 77 if f == nil { 78 f = &zerofunc 79 } 80 81 // Open package file if needed, for reading PC data. 82 if fname != sym.Package.File { 83 if file != nil { 84 file.Close() 85 } 86 var err error 87 file, err = os.Open(sym.Package.File) 88 if err != nil { 89 p.errorf("%v: %v", sym, err) 90 return 91 } 92 fname = sym.Package.File 93 } 94 95 // off is the offset of the table entry where we're going to write 96 // the encoded form of Func. 97 // indexOff is the current position in the table index; 98 // we add an entry in the index pointing at off. 99 off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1) 100 indexOff = buf.Addr(indexOff, sym.SymID, 0) 101 indexOff = buf.Uint(indexOff, uint64(off), p.ptrsize) 102 103 // The Func encoding starts with a header giving offsets 104 // to data blobs, and then the data blobs themselves. 105 // end gives the current write position for the data blobs. 106 end := off + p.ptrsize + 3*4 + 5*4 + len(f.PCData)*4 + len(f.FuncData)*p.ptrsize 107 if len(f.FuncData) > 0 { 108 end += -end & (p.ptrsize - 1) 109 } 110 buf.SetSize(end) 111 112 // entry uintptr 113 // name int32 114 // args int32 115 // frame int32 116 // 117 // The frame recorded in the object file is 118 // the frame size used in an assembly listing, which does 119 // not include the caller PC on the stack. 120 // The frame size we want to list here is the delta from 121 // this function's SP to its caller's SP, which does include 122 // the caller PC. Add p.ptrsize to f.Frame to adjust. 123 // TODO(rsc): Record the same frame size in the object file. 124 off = buf.Addr(off, sym.SymID, 0) 125 off = buf.Uint32(off, uint32(addString(buf, sym.Name))) 126 off = buf.Uint32(off, uint32(f.Args)) 127 off = buf.Uint32(off, uint32(f.Frame+p.ptrsize)) 128 129 // pcdata 130 off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCSP))) 131 off = buf.Uint32(off, uint32(addPCFileTable(p, buf, file, f.PCFile, sym, files))) 132 off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCLine))) 133 off = buf.Uint32(off, uint32(len(f.PCData))) 134 off = buf.Uint32(off, uint32(len(f.FuncData))) 135 for _, pcdata := range f.PCData { 136 off = buf.Uint32(off, uint32(addPCTable(p, buf, file, pcdata))) 137 } 138 139 // funcdata 140 if len(f.FuncData) > 0 { 141 off += -off & (p.ptrsize - 1) // must be pointer-aligned 142 for _, funcdata := range f.FuncData { 143 if funcdata.Sym.Name == "" { 144 off = buf.Uint(off, uint64(funcdata.Offset), p.ptrsize) 145 } else { 146 off = buf.Addr(off, funcdata.Sym, funcdata.Offset) 147 } 148 } 149 } 150 151 if off != end { 152 p.errorf("internal error: invalid math in pclntab: off=%#x end=%#x", off, end) 153 break 154 } 155 } 156 if file != nil { 157 file.Close() 158 } 159 160 // Final entry of index is end PC of last function. 161 indexOff = buf.Addr(indexOff, lastSym.SymID, int64(lastSym.Size)) 162 163 // Start file table. 164 // Function index is immediately followed by offset to file table. 165 off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1) 166 buf.Uint32(indexOff, uint32(off)) 167 168 // File table is an array of uint32s. 169 // The first entry gives 1+n, the size of the array. 170 // The following n entries hold offsets to string data. 171 // File number n uses the string pointed at by entry n. 172 // File number 0 is invalid. 173 buf.SetSize(off + (1+len(files))*4) 174 buf.Uint32(off, uint32(1+len(files))) 175 var filestr []string 176 for file := range files { 177 filestr = append(filestr, file) 178 } 179 sort.Strings(filestr) 180 for _, file := range filestr { 181 id := files[file] 182 buf.Uint32(off+4*id, uint32(addString(buf, file))) 183 } 184 185 pclntab := &Sym{ 186 Sym: &goobj.Sym{ 187 SymID: goobj.SymID{Name: "runtime.pclntab"}, 188 Kind: goobj.SPCLNTAB, 189 Size: buf.Size(), 190 Reloc: buf.Reloc(), 191 }, 192 Bytes: buf.Bytes(), 193 } 194 p.addSym(pclntab) 195 } 196 197 // addString appends the string s to the buffer b. 198 // It returns the offset of the beginning of the string in the buffer. 199 func addString(b *SymBuffer, s string) int { 200 off := b.Size() 201 b.SetSize(off + len(s) + 1) 202 copy(b.data[off:], s) 203 return off 204 } 205 206 // addPCTable appends the PC-data table stored in the file f at the location loc 207 // to the symbol buffer b. It returns the offset of the beginning of the table 208 // in the buffer. 209 func addPCTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data) int { 210 if loc.Size == 0 { 211 return 0 212 } 213 off := b.Size() 214 b.SetSize(off + int(loc.Size)) 215 _, err := f.ReadAt(b.data[off:off+int(loc.Size)], loc.Offset) 216 if err != nil { 217 p.errorf("%v", err) 218 } 219 return off 220 } 221 222 // addPCFileTable is like addPCTable, but it renumbers the file names referred to by the table 223 // to use the global numbering maintained in the files map. It adds new files to the 224 // map as necessary. 225 func addPCFileTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data, sym *Sym, files map[string]int) int { 226 if loc.Size == 0 { 227 return 0 228 } 229 off := b.Size() 230 231 src := make([]byte, loc.Size) 232 _, err := f.ReadAt(src, loc.Offset) 233 if err != nil { 234 p.errorf("%v", err) 235 return 0 236 } 237 238 filenum := make([]int, len(sym.Func.File)) 239 for i, name := range sym.Func.File { 240 num := files[name] 241 if num == 0 { 242 num = len(files) + 1 243 files[name] = num 244 } 245 filenum[i] = num 246 } 247 248 var dst []byte 249 newval := int32(-1) 250 var it PCIter 251 for it.Init(p, src); !it.Done; it.Next() { 252 // value delta 253 oldval := it.Value 254 val := oldval 255 if oldval != -1 { 256 if oldval < 0 || int(oldval) >= len(filenum) { 257 p.errorf("%s: corrupt pc-file table", sym) 258 break 259 } 260 val = int32(filenum[oldval]) 261 } 262 dv := val - newval 263 newval = val 264 uv := uint32(dv<<1) ^ uint32(dv>>31) 265 dst = appendVarint(dst, uv) 266 267 // pc delta 268 dst = appendVarint(dst, it.NextPC-it.PC) 269 } 270 if it.Corrupt { 271 p.errorf("%s: corrupt pc-file table", sym) 272 } 273 274 // terminating value delta 275 dst = appendVarint(dst, 0) 276 277 b.SetSize(off + len(dst)) 278 copy(b.data[off:], dst) 279 return off 280 } 281 282 // A SymBuffer is a buffer for preparing the data image of a 283 // linker-generated symbol. 284 type SymBuffer struct { 285 data []byte 286 reloc []goobj.Reloc 287 order binary.ByteOrder 288 ptrsize int 289 } 290 291 // Init initializes the buffer for writing. 292 func (b *SymBuffer) Init(p *Prog) { 293 b.data = nil 294 b.reloc = nil 295 b.order = p.byteorder 296 b.ptrsize = p.ptrsize 297 } 298 299 // Bytes returns the buffer data. 300 func (b *SymBuffer) Bytes() []byte { 301 return b.data 302 } 303 304 // SetSize sets the buffer's data size to n bytes. 305 func (b *SymBuffer) SetSize(n int) { 306 for cap(b.data) < n { 307 b.data = append(b.data[:cap(b.data)], 0) 308 } 309 b.data = b.data[:n] 310 } 311 312 // Size returns the buffer's data size. 313 func (b *SymBuffer) Size() int { 314 return len(b.data) 315 } 316 317 // Reloc returns the buffered relocations. 318 func (b *SymBuffer) Reloc() []goobj.Reloc { 319 return b.reloc 320 } 321 322 // Uint8 sets the uint8 at offset off to v. 323 // It returns the offset just beyond v. 324 func (b *SymBuffer) Uint8(off int, v uint8) int { 325 b.data[off] = v 326 return off + 1 327 } 328 329 // Uint16 sets the uint16 at offset off to v. 330 // It returns the offset just beyond v. 331 func (b *SymBuffer) Uint16(off int, v uint16) int { 332 b.order.PutUint16(b.data[off:], v) 333 return off + 2 334 } 335 336 // Uint32 sets the uint32 at offset off to v. 337 // It returns the offset just beyond v. 338 func (b *SymBuffer) Uint32(off int, v uint32) int { 339 b.order.PutUint32(b.data[off:], v) 340 return off + 4 341 } 342 343 // Uint64 sets the uint64 at offset off to v. 344 // It returns the offset just beyond v. 345 func (b *SymBuffer) Uint64(off int, v uint64) int { 346 b.order.PutUint64(b.data[off:], v) 347 return off + 8 348 } 349 350 // Uint sets the size-byte unsigned integer at offset off to v. 351 // It returns the offset just beyond v. 352 func (b *SymBuffer) Uint(off int, v uint64, size int) int { 353 switch size { 354 case 1: 355 return b.Uint8(off, uint8(v)) 356 case 2: 357 return b.Uint16(off, uint16(v)) 358 case 4: 359 return b.Uint32(off, uint32(v)) 360 case 8: 361 return b.Uint64(off, v) 362 } 363 panic("invalid use of SymBuffer.SetUint") 364 } 365 366 // Addr sets the pointer-sized address at offset off to refer 367 // to symoff bytes past the start of sym. It returns the offset 368 // just beyond the address. 369 func (b *SymBuffer) Addr(off int, sym goobj.SymID, symoff int64) int { 370 b.reloc = append(b.reloc, goobj.Reloc{ 371 Offset: off, 372 Size: b.ptrsize, 373 Sym: sym, 374 Add: int(symoff), 375 Type: obj.R_ADDR, 376 }) 377 return off + b.ptrsize 378 } 379 380 // A PCIter implements iteration over PC-data tables. 381 // 382 // var it PCIter 383 // for it.Init(p, data); !it.Done; it.Next() { 384 // it.Value holds from it.PC up to (but not including) it.NextPC 385 // } 386 // if it.Corrupt { 387 // data was malformed 388 // } 389 // 390 type PCIter struct { 391 PC uint32 392 NextPC uint32 393 Value int32 394 Done bool 395 Corrupt bool 396 p []byte 397 start bool 398 pcquantum uint32 399 } 400 401 // Init initializes the iteration. 402 // On return, if it.Done is true, the iteration is over. 403 // Otherwise it.Value applies in the pc range [it.PC, it.NextPC). 404 func (it *PCIter) Init(p *Prog, buf []byte) { 405 it.p = buf 406 it.PC = 0 407 it.NextPC = 0 408 it.Value = -1 409 it.start = true 410 it.pcquantum = uint32(p.pcquantum) 411 it.Done = false 412 it.Next() 413 } 414 415 // Next steps forward one entry in the table. 416 // On return, if it.Done is true, the iteration is over. 417 // Otherwise it.Value applies in the pc range [it.PC, it.NextPC). 418 func (it *PCIter) Next() { 419 it.PC = it.NextPC 420 if it.Done { 421 return 422 } 423 if len(it.p) == 0 { 424 it.Done = true 425 return 426 } 427 428 // value delta 429 uv, p, ok := decodeVarint(it.p) 430 if !ok { 431 it.Done = true 432 it.Corrupt = true 433 return 434 } 435 it.p = p 436 if uv == 0 && !it.start { 437 it.Done = true 438 return 439 } 440 it.start = false 441 sv := int32(uv>>1) ^ int32(uv<<31)>>31 442 it.Value += sv 443 444 // pc delta 445 uv, it.p, ok = decodeVarint(it.p) 446 if !ok { 447 it.Done = true 448 it.Corrupt = true 449 return 450 } 451 it.NextPC = it.PC + uv*it.pcquantum 452 } 453 454 // decodeVarint decodes an unsigned varint from p, 455 // reporting the value, the remainder of the data, and 456 // whether the decoding was successful. 457 func decodeVarint(p []byte) (v uint32, rest []byte, ok bool) { 458 for shift := uint(0); ; shift += 7 { 459 if len(p) == 0 { 460 return 461 } 462 c := uint32(p[0]) 463 p = p[1:] 464 v |= (c & 0x7F) << shift 465 if c&0x80 == 0 { 466 break 467 } 468 } 469 return v, p, true 470 } 471 472 // appendVarint appends an unsigned varint encoding of v to p 473 // and returns the resulting slice. 474 func appendVarint(p []byte, v uint32) []byte { 475 for ; v >= 0x80; v >>= 7 { 476 p = append(p, byte(v)|0x80) 477 } 478 p = append(p, byte(v)) 479 return p 480 }