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