1 // Copyright 2015 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 dwarf 6 7 import ( 8 "errors" 9 "fmt" 10 "io" 11 "path" 12 ) 13 14 // A LineReader reads a sequence of LineEntry structures from a DWARF 15 // "line" section for a single compilation unit. LineEntries occur in 16 // order of increasing PC and each LineEntry gives metadata for the 17 // instructions from that LineEntry's PC to just before the next 18 // LineEntry's PC. The last entry will have its EndSequence field set. 19 type LineReader struct { 20 buf buf 21 22 // Original .debug_line section data. Used by Seek. 23 section []byte 24 25 // Header information 26 version uint16 27 minInstructionLength int 28 maxOpsPerInstruction int 29 defaultIsStmt bool 30 lineBase int 31 lineRange int 32 opcodeBase int 33 opcodeLengths []int 34 directories []string 35 fileEntries []*LineFile 36 37 programOffset Offset // section offset of line number program 38 endOffset Offset // section offset of byte following program 39 40 initialFileEntries int // initial length of fileEntries 41 42 // Current line number program state machine registers 43 state LineEntry // public state 44 fileIndex int // private state 45 } 46 47 // A LineEntry is a row in a DWARF line table. 48 type LineEntry struct { 49 // Address is the program-counter value of a machine 50 // instruction generated by the compiler. This LineEntry 51 // applies to each instruction from Address to just before the 52 // Address of the next LineEntry. 53 Address uint64 54 55 // OpIndex is the index of an operation within a VLIW 56 // instruction. The index of the first operation is 0. For 57 // non-VLIW architectures, it will always be 0. Address and 58 // OpIndex together form an operation pointer that can 59 // reference any individual operation within the instruction 60 // stream. 61 OpIndex int 62 63 // File is the source file corresponding to these 64 // instructions. 65 File *LineFile 66 67 // Line is the source code line number corresponding to these 68 // instructions. Lines are numbered beginning at 1. It may be 69 // 0 if these instructions cannot be attributed to any source 70 // line. 71 Line int 72 73 // Column is the column number within the source line of these 74 // instructions. Columns are numbered beginning at 1. It may 75 // be 0 to indicate the "left edge" of the line. 76 Column int 77 78 // IsStmt indicates that Address is a recommended breakpoint 79 // location, such as the beginning of a line, statement, or a 80 // distinct subpart of a statement. 81 IsStmt bool 82 83 // BasicBlock indicates that Address is the beginning of a 84 // basic block. 85 BasicBlock bool 86 87 // PrologueEnd indicates that Address is one (of possibly 88 // many) PCs where execution should be suspended for a 89 // breakpoint on entry to the containing function. 90 // 91 // Added in DWARF 3. 92 PrologueEnd bool 93 94 // EpilogueBegin indicates that Address is one (of possibly 95 // many) PCs where execution should be suspended for a 96 // breakpoint on exit from this function. 97 // 98 // Added in DWARF 3. 99 EpilogueBegin bool 100 101 // ISA is the instruction set architecture for these 102 // instructions. Possible ISA values should be defined by the 103 // applicable ABI specification. 104 // 105 // Added in DWARF 3. 106 ISA int 107 108 // Discriminator is an arbitrary integer indicating the block 109 // to which these instructions belong. It serves to 110 // distinguish among multiple blocks that may all have with 111 // the same source file, line, and column. Where only one 112 // block exists for a given source position, it should be 0. 113 // 114 // Added in DWARF 3. 115 Discriminator int 116 117 // EndSequence indicates that Address is the first byte after 118 // the end of a sequence of target machine instructions. If it 119 // is set, only this and the Address field are meaningful. A 120 // line number table may contain information for multiple 121 // potentially disjoint instruction sequences. The last entry 122 // in a line table should always have EndSequence set. 123 EndSequence bool 124 } 125 126 // A LineFile is a source file referenced by a DWARF line table entry. 127 type LineFile struct { 128 Name string 129 Mtime uint64 // Implementation defined modification time, or 0 if unknown 130 Length int // File length, or 0 if unknown 131 } 132 133 // LineReader returns a new reader for the line table of compilation 134 // unit cu, which must be an Entry with tag TagCompileUnit. 135 // 136 // If this compilation unit has no line table, it returns nil, nil. 137 func (d *Data) LineReader(cu *Entry) (*LineReader, error) { 138 if d.line == nil { 139 // No line tables available. 140 return nil, nil 141 } 142 143 // Get line table information from cu. 144 off, ok := cu.Val(AttrStmtList).(int64) 145 if !ok { 146 // cu has no line table. 147 return nil, nil 148 } 149 if off > int64(len(d.line)) { 150 return nil, errors.New("AttrStmtList value out of range") 151 } 152 // AttrCompDir is optional if all file names are absolute. Use 153 // the empty string if it's not present. 154 compDir, _ := cu.Val(AttrCompDir).(string) 155 156 // Create the LineReader. 157 u := &d.unit[d.offsetToUnit(cu.Offset)] 158 buf := makeBuf(d, u, "line", Offset(off), d.line[off:]) 159 // The compilation directory is implicitly directories[0]. 160 r := LineReader{buf: buf, section: d.line, directories: []string{compDir}} 161 162 // Read the header. 163 if err := r.readHeader(); err != nil { 164 return nil, err 165 } 166 167 // Initialize line reader state. 168 r.Reset() 169 170 return &r, nil 171 } 172 173 // readHeader reads the line number program header from r.buf and sets 174 // all of the header fields in r. 175 func (r *LineReader) readHeader() error { 176 buf := &r.buf 177 178 // Read basic header fields [DWARF2 6.2.4]. 179 hdrOffset := buf.off 180 unitLength, dwarf64 := buf.unitLength() 181 r.endOffset = buf.off + unitLength 182 if r.endOffset > buf.off+Offset(len(buf.data)) { 183 return DecodeError{"line", hdrOffset, fmt.Sprintf("line table end %d exceeds section size %d", r.endOffset, buf.off+Offset(len(buf.data)))} 184 } 185 r.version = buf.uint16() 186 if buf.err == nil && (r.version < 2 || r.version > 4) { 187 // DWARF goes to all this effort to make new opcodes 188 // backward-compatible, and then adds fields right in 189 // the middle of the header in new versions, so we're 190 // picky about only supporting known line table 191 // versions. 192 return DecodeError{"line", hdrOffset, fmt.Sprintf("unknown line table version %d", r.version)} 193 } 194 var headerLength Offset 195 if dwarf64 { 196 headerLength = Offset(buf.uint64()) 197 } else { 198 headerLength = Offset(buf.uint32()) 199 } 200 r.programOffset = buf.off + headerLength 201 r.minInstructionLength = int(buf.uint8()) 202 if r.version >= 4 { 203 // [DWARF4 6.2.4] 204 r.maxOpsPerInstruction = int(buf.uint8()) 205 } else { 206 r.maxOpsPerInstruction = 1 207 } 208 r.defaultIsStmt = buf.uint8() != 0 209 r.lineBase = int(int8(buf.uint8())) 210 r.lineRange = int(buf.uint8()) 211 212 // Validate header. 213 if buf.err != nil { 214 return buf.err 215 } 216 if r.maxOpsPerInstruction == 0 { 217 return DecodeError{"line", hdrOffset, "invalid maximum operations per instruction: 0"} 218 } 219 if r.lineRange == 0 { 220 return DecodeError{"line", hdrOffset, "invalid line range: 0"} 221 } 222 223 // Read standard opcode length table. This table starts with opcode 1. 224 r.opcodeBase = int(buf.uint8()) 225 r.opcodeLengths = make([]int, r.opcodeBase) 226 for i := 1; i < r.opcodeBase; i++ { 227 r.opcodeLengths[i] = int(buf.uint8()) 228 } 229 230 // Validate opcode lengths. 231 if buf.err != nil { 232 return buf.err 233 } 234 for i, length := range r.opcodeLengths { 235 if known, ok := knownOpcodeLengths[i]; ok && known != length { 236 return DecodeError{"line", hdrOffset, fmt.Sprintf("opcode %d expected to have length %d, but has length %d", i, known, length)} 237 } 238 } 239 240 // Read include directories table. The caller already set 241 // directories[0] to the compilation directory. 242 for { 243 directory := buf.string() 244 if buf.err != nil { 245 return buf.err 246 } 247 if len(directory) == 0 { 248 break 249 } 250 if !path.IsAbs(directory) { 251 // Relative paths are implicitly relative to 252 // the compilation directory. 253 directory = path.Join(r.directories[0], directory) 254 } 255 r.directories = append(r.directories, directory) 256 } 257 258 // Read file name list. File numbering starts with 1, so leave 259 // the first entry nil. 260 r.fileEntries = make([]*LineFile, 1) 261 for { 262 if done, err := r.readFileEntry(); err != nil { 263 return err 264 } else if done { 265 break 266 } 267 } 268 r.initialFileEntries = len(r.fileEntries) 269 270 return buf.err 271 } 272 273 // readFileEntry reads a file entry from either the header or a 274 // DW_LNE_define_file extended opcode and adds it to r.fileEntries. A 275 // true return value indicates that there are no more entries to read. 276 func (r *LineReader) readFileEntry() (bool, error) { 277 name := r.buf.string() 278 if r.buf.err != nil { 279 return false, r.buf.err 280 } 281 if len(name) == 0 { 282 return true, nil 283 } 284 off := r.buf.off 285 dirIndex := int(r.buf.uint()) 286 if !path.IsAbs(name) { 287 if dirIndex >= len(r.directories) { 288 return false, DecodeError{"line", off, "directory index too large"} 289 } 290 name = path.Join(r.directories[dirIndex], name) 291 } 292 mtime := r.buf.uint() 293 length := int(r.buf.uint()) 294 295 r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length}) 296 return false, nil 297 } 298 299 // updateFile updates r.state.File after r.fileIndex has 300 // changed or r.fileEntries has changed. 301 func (r *LineReader) updateFile() { 302 if r.fileIndex < len(r.fileEntries) { 303 r.state.File = r.fileEntries[r.fileIndex] 304 } else { 305 r.state.File = nil 306 } 307 } 308 309 // Next sets *entry to the next row in this line table and moves to 310 // the next row. If there are no more entries and the line table is 311 // properly terminated, it returns io.EOF. 312 // 313 // Rows are always in order of increasing entry.Address, but 314 // entry.Line may go forward or backward. 315 func (r *LineReader) Next(entry *LineEntry) error { 316 if r.buf.err != nil { 317 return r.buf.err 318 } 319 320 // Execute opcodes until we reach an opcode that emits a line 321 // table entry. 322 for { 323 if len(r.buf.data) == 0 { 324 return io.EOF 325 } 326 emit := r.step(entry) 327 if r.buf.err != nil { 328 return r.buf.err 329 } 330 if emit { 331 return nil 332 } 333 } 334 } 335 336 // knownOpcodeLengths gives the opcode lengths (in varint arguments) 337 // of known standard opcodes. 338 var knownOpcodeLengths = map[int]int{ 339 lnsCopy: 0, 340 lnsAdvancePC: 1, 341 lnsAdvanceLine: 1, 342 lnsSetFile: 1, 343 lnsNegateStmt: 0, 344 lnsSetBasicBlock: 0, 345 lnsConstAddPC: 0, 346 lnsSetPrologueEnd: 0, 347 lnsSetEpilogueBegin: 0, 348 lnsSetISA: 1, 349 // lnsFixedAdvancePC takes a uint8 rather than a varint; it's 350 // unclear what length the header is supposed to claim, so 351 // ignore it. 352 } 353 354 // step processes the next opcode and updates r.state. If the opcode 355 // emits a row in the line table, this updates *entry and returns 356 // true. 357 func (r *LineReader) step(entry *LineEntry) bool { 358 opcode := int(r.buf.uint8()) 359 360 if opcode >= r.opcodeBase { 361 // Special opcode [DWARF2 6.2.5.1, DWARF4 6.2.5.1] 362 adjustedOpcode := opcode - r.opcodeBase 363 r.advancePC(adjustedOpcode / r.lineRange) 364 lineDelta := r.lineBase + int(adjustedOpcode)%r.lineRange 365 r.state.Line += lineDelta 366 goto emit 367 } 368 369 switch opcode { 370 case 0: 371 // Extended opcode [DWARF2 6.2.5.3] 372 length := Offset(r.buf.uint()) 373 startOff := r.buf.off 374 opcode := r.buf.uint8() 375 376 switch opcode { 377 case lneEndSequence: 378 r.state.EndSequence = true 379 *entry = r.state 380 r.resetState() 381 382 case lneSetAddress: 383 r.state.Address = r.buf.addr() 384 385 case lneDefineFile: 386 if done, err := r.readFileEntry(); err != nil { 387 r.buf.err = err 388 return false 389 } else if done { 390 r.buf.err = DecodeError{"line", startOff, "malformed DW_LNE_define_file operation"} 391 return false 392 } 393 r.updateFile() 394 395 case lneSetDiscriminator: 396 // [DWARF4 6.2.5.3] 397 r.state.Discriminator = int(r.buf.uint()) 398 } 399 400 r.buf.skip(int(startOff + length - r.buf.off)) 401 402 if opcode == lneEndSequence { 403 return true 404 } 405 406 // Standard opcodes [DWARF2 6.2.5.2] 407 case lnsCopy: 408 goto emit 409 410 case lnsAdvancePC: 411 r.advancePC(int(r.buf.uint())) 412 413 case lnsAdvanceLine: 414 r.state.Line += int(r.buf.int()) 415 416 case lnsSetFile: 417 r.fileIndex = int(r.buf.uint()) 418 r.updateFile() 419 420 case lnsSetColumn: 421 r.state.Column = int(r.buf.uint()) 422 423 case lnsNegateStmt: 424 r.state.IsStmt = !r.state.IsStmt 425 426 case lnsSetBasicBlock: 427 r.state.BasicBlock = true 428 429 case lnsConstAddPC: 430 r.advancePC((255 - r.opcodeBase) / r.lineRange) 431 432 case lnsFixedAdvancePC: 433 r.state.Address += uint64(r.buf.uint16()) 434 435 // DWARF3 standard opcodes [DWARF3 6.2.5.2] 436 case lnsSetPrologueEnd: 437 r.state.PrologueEnd = true 438 439 case lnsSetEpilogueBegin: 440 r.state.EpilogueBegin = true 441 442 case lnsSetISA: 443 r.state.ISA = int(r.buf.uint()) 444 445 default: 446 // Unhandled standard opcode. Skip the number of 447 // arguments that the prologue says this opcode has. 448 for i := 0; i < r.opcodeLengths[opcode]; i++ { 449 r.buf.uint() 450 } 451 } 452 return false 453 454 emit: 455 *entry = r.state 456 r.state.BasicBlock = false 457 r.state.PrologueEnd = false 458 r.state.EpilogueBegin = false 459 r.state.Discriminator = 0 460 return true 461 } 462 463 // advancePC advances "operation pointer" (the combination of Address 464 // and OpIndex) in r.state by opAdvance steps. 465 func (r *LineReader) advancePC(opAdvance int) { 466 opIndex := r.state.OpIndex + opAdvance 467 r.state.Address += uint64(r.minInstructionLength * (opIndex / r.maxOpsPerInstruction)) 468 r.state.OpIndex = opIndex % r.maxOpsPerInstruction 469 } 470 471 // A LineReaderPos represents a position in a line table. 472 type LineReaderPos struct { 473 // off is the current offset in the DWARF line section. 474 off Offset 475 // numFileEntries is the length of fileEntries. 476 numFileEntries int 477 // state and fileIndex are the statement machine state at 478 // offset off. 479 state LineEntry 480 fileIndex int 481 } 482 483 // Tell returns the current position in the line table. 484 func (r *LineReader) Tell() LineReaderPos { 485 return LineReaderPos{r.buf.off, len(r.fileEntries), r.state, r.fileIndex} 486 } 487 488 // Seek restores the line table reader to a position returned by Tell. 489 // 490 // The argument pos must have been returned by a call to Tell on this 491 // line table. 492 func (r *LineReader) Seek(pos LineReaderPos) { 493 r.buf.off = pos.off 494 r.buf.data = r.section[r.buf.off:r.endOffset] 495 r.fileEntries = r.fileEntries[:pos.numFileEntries] 496 r.state = pos.state 497 r.fileIndex = pos.fileIndex 498 } 499 500 // Reset repositions the line table reader at the beginning of the 501 // line table. 502 func (r *LineReader) Reset() { 503 // Reset buffer to the line number program offset. 504 r.buf.off = r.programOffset 505 r.buf.data = r.section[r.buf.off:r.endOffset] 506 507 // Reset file entries list. 508 r.fileEntries = r.fileEntries[:r.initialFileEntries] 509 510 // Reset line number program state. 511 r.resetState() 512 } 513 514 // resetState resets r.state to its default values 515 func (r *LineReader) resetState() { 516 // Reset the state machine registers to the defaults given in 517 // [DWARF4 6.2.2]. 518 r.state = LineEntry{ 519 Address: 0, 520 OpIndex: 0, 521 File: nil, 522 Line: 1, 523 Column: 0, 524 IsStmt: r.defaultIsStmt, 525 BasicBlock: false, 526 PrologueEnd: false, 527 EpilogueBegin: false, 528 ISA: 0, 529 Discriminator: 0, 530 } 531 r.fileIndex = 1 532 r.updateFile() 533 } 534 535 // ErrUnknownPC is the error returned by LineReader.ScanPC when the 536 // seek PC is not covered by any entry in the line table. 537 var ErrUnknownPC = errors.New("ErrUnknownPC") 538 539 // SeekPC sets *entry to the LineEntry that includes pc and positions 540 // the reader on the next entry in the line table. If necessary, this 541 // will seek backwards to find pc. 542 // 543 // If pc is not covered by any entry in this line table, SeekPC 544 // returns ErrUnknownPC. In this case, *entry and the final seek 545 // position are unspecified. 546 // 547 // Note that DWARF line tables only permit sequential, forward scans. 548 // Hence, in the worst case, this takes time linear in the size of the 549 // line table. If the caller wishes to do repeated fast PC lookups, it 550 // should build an appropriate index of the line table. 551 func (r *LineReader) SeekPC(pc uint64, entry *LineEntry) error { 552 if err := r.Next(entry); err != nil { 553 return err 554 } 555 if entry.Address > pc { 556 // We're too far. Start at the beginning of the table. 557 r.Reset() 558 if err := r.Next(entry); err != nil { 559 return err 560 } 561 if entry.Address > pc { 562 // The whole table starts after pc. 563 r.Reset() 564 return ErrUnknownPC 565 } 566 } 567 568 // Scan until we pass pc, then back up one. 569 for { 570 var next LineEntry 571 pos := r.Tell() 572 if err := r.Next(&next); err != nil { 573 if err == io.EOF { 574 return ErrUnknownPC 575 } 576 return err 577 } 578 if next.Address > pc { 579 if entry.EndSequence { 580 // pc is in a hole in the table. 581 return ErrUnknownPC 582 } 583 // entry is the desired entry. Back up the 584 // cursor to "next" and return success. 585 r.Seek(pos) 586 return nil 587 } 588 *entry = next 589 } 590 } 591