github.com/prattmic/llgo-embedded@v0.0.0-20150820070356-41cfecea0e1e/third_party/gofrontend/libgo/go/debug/dwarf/line.go (about) 1 // Copyright 2012 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 // DWARF line number information. 6 7 package dwarf 8 9 import ( 10 "errors" 11 "path/filepath" 12 "sort" 13 "strconv" 14 ) 15 16 // A Line holds all the available information about the source code 17 // corresponding to a specific program counter address. 18 type Line struct { 19 Filename string // source file name 20 OpIndex int // index of operation in VLIW instruction 21 Line int // line number 22 Column int // column number 23 ISA int // instruction set code 24 Discriminator int // block discriminator 25 Stmt bool // instruction starts statement 26 Block bool // instruction starts basic block 27 EndPrologue bool // instruction ends function prologue 28 BeginEpilogue bool // instruction begins function epilogue 29 } 30 31 // LineForPc returns the line number information for a program counter 32 // address, if any. When this returns multiple Line structures in a 33 // context where only one can be used, the last one is the best. 34 func (d *Data) LineForPC(pc uint64) ([]*Line, error) { 35 for i := range d.unit { 36 u := &d.unit[i] 37 if u.pc == nil { 38 if err := d.readUnitLine(i, u); err != nil { 39 return nil, err 40 } 41 } 42 for _, ar := range u.pc { 43 if pc >= ar.low && pc < ar.high { 44 return d.findLine(u, pc) 45 } 46 } 47 } 48 return nil, nil 49 } 50 51 // readUnitLine reads in the line number information for a compilation 52 // unit. 53 func (d *Data) readUnitLine(i int, u *unit) error { 54 r := d.unitReader(i) 55 setLineOff := false 56 for { 57 e, err := r.Next() 58 if err != nil { 59 return err 60 } 61 if e == nil { 62 break 63 } 64 if r.unit != i { 65 break 66 } 67 switch e.Tag { 68 case TagCompileUnit, TagSubprogram, TagEntryPoint, TagInlinedSubroutine: 69 low, lowok := e.Val(AttrLowpc).(uint64) 70 var high uint64 71 var highok bool 72 switch v := e.Val(AttrHighpc).(type) { 73 case uint64: 74 high = v 75 highok = true 76 case int64: 77 high = low + uint64(v) 78 highok = true 79 } 80 if lowok && highok { 81 u.pc = append(u.pc, addrRange{low, high}) 82 } else if off, ok := e.Val(AttrRanges).(Offset); ok { 83 if err := d.readAddressRanges(off, low, u); err != nil { 84 return err 85 } 86 } 87 val := e.Val(AttrStmtList) 88 if val != nil { 89 if off, ok := val.(int64); ok { 90 u.lineoff = Offset(off) 91 setLineOff = true 92 } else if off, ok := val.(Offset); ok { 93 u.lineoff = off 94 setLineOff = true 95 } else { 96 return errors.New("unrecognized format for DW_ATTR_stmt_list") 97 } 98 } 99 if dir, ok := e.Val(AttrCompDir).(string); ok { 100 u.dir = dir 101 } 102 } 103 } 104 if !setLineOff { 105 u.lineoff = Offset(0) 106 u.lineoff-- 107 } 108 return nil 109 } 110 111 // readAddressRanges adds address ranges to a unit. 112 func (d *Data) readAddressRanges(off Offset, base uint64, u *unit) error { 113 b := makeBuf(d, u, "ranges", off, d.ranges[off:]) 114 var highest uint64 115 switch u.addrsize() { 116 case 1: 117 highest = 0xff 118 case 2: 119 highest = 0xffff 120 case 4: 121 highest = 0xffffffff 122 case 8: 123 highest = 0xffffffffffffffff 124 default: 125 return errors.New("unknown address size") 126 } 127 for { 128 if b.err != nil { 129 return b.err 130 } 131 low := b.addr() 132 high := b.addr() 133 if low == 0 && high == 0 { 134 return b.err 135 } else if low == highest { 136 base = high 137 } else { 138 u.pc = append(u.pc, addrRange{low + base, high + base}) 139 } 140 } 141 } 142 143 // findLine finds the line information for a PC value, given the unit 144 // containing the information. 145 func (d *Data) findLine(u *unit, pc uint64) ([]*Line, error) { 146 if u.lines == nil { 147 if err := d.parseLine(u); err != nil { 148 return nil, err 149 } 150 } 151 152 for _, ln := range u.lines { 153 if pc < ln.addrs[0].pc || pc > ln.addrs[len(ln.addrs)-1].pc { 154 continue 155 } 156 i := sort.Search(len(ln.addrs), 157 func(i int) bool { return ln.addrs[i].pc > pc }) 158 i-- 159 p := new(Line) 160 *p = ln.line 161 p.Line = ln.addrs[i].line 162 ret := []*Line{p} 163 for i++; i < len(ln.addrs) && ln.addrs[i].pc == pc; i++ { 164 p = new(Line) 165 *p = ln.line 166 p.Line = ln.addrs[i].line 167 ret = append(ret, p) 168 } 169 return ret, nil 170 } 171 172 return nil, nil 173 } 174 175 // FileLine returns the file name and line number for a program 176 // counter address, or "", 0 if unknown. 177 func (d *Data) FileLine(pc uint64) (string, int, error) { 178 r, err := d.LineForPC(pc) 179 if err != nil { 180 return "", 0, err 181 } 182 if r == nil { 183 return "", 0, nil 184 } 185 ln := r[len(r)-1] 186 return ln.Filename, ln.Line, nil 187 } 188 189 // A mapLineInfo holds the PC values and line numbers associated with 190 // a single Line structure. This representation is chosen to reduce 191 // memory usage based on typical debug info. 192 type mapLineInfo struct { 193 line Line // line.Line will be zero 194 addrs lineAddrs // sorted by PC 195 } 196 197 // A list of lines. This will be sorted by PC. 198 type lineAddrs []oneLineInfo 199 200 func (p lineAddrs) Len() int { return len(p) } 201 func (p lineAddrs) Less(i, j int) bool { return p[i].pc < p[j].pc } 202 func (p lineAddrs) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 203 204 // A oneLineInfo is a single PC and line number. 205 type oneLineInfo struct { 206 pc uint64 207 line int 208 } 209 210 // A lineHdr holds the relevant information from a line number 211 // program header. 212 type lineHdr struct { 213 version uint16 // version of line number encoding 214 minInsnLen uint8 // minimum instruction length 215 maxOpsPerInsn uint8 // maximum number of ops per instruction 216 defStmt bool // initial value of stmt register 217 lineBase int8 // line adjustment base 218 lineRange uint8 // line adjustment step 219 opBase uint8 // base of special opcode values 220 opLen []uint8 // lengths of standard opcodes 221 dirs []string // directories 222 files []string // file names 223 } 224 225 // parseLine parses the line number information for a compilation unit 226 func (d *Data) parseLine(u *unit) error { 227 if u.lineoff+1 == 0 { 228 return errors.New("unknown line offset") 229 } 230 b := makeBuf(d, u, "line", u.lineoff, d.line[u.lineoff:]) 231 len := uint64(b.uint32()) 232 dwarf64 := false 233 if len == 0xffffffff { 234 len = b.uint64() 235 dwarf64 = true 236 } 237 end := b.off + Offset(len) 238 hdr := d.parseLineHdr(u, &b, dwarf64) 239 if b.err == nil { 240 d.parseLineProgram(u, &b, hdr, end) 241 } 242 return b.err 243 } 244 245 // parseLineHdr parses a line number program header. 246 func (d *Data) parseLineHdr(u *unit, b *buf, dwarf64 bool) (hdr lineHdr) { 247 hdr.version = b.uint16() 248 if hdr.version < 2 || hdr.version > 4 { 249 b.error("unsupported DWARF version " + strconv.Itoa(int(hdr.version))) 250 return 251 } 252 253 var hlen Offset 254 if dwarf64 { 255 hlen = Offset(b.uint64()) 256 } else { 257 hlen = Offset(b.uint32()) 258 } 259 end := b.off + hlen 260 261 hdr.minInsnLen = b.uint8() 262 if hdr.version < 4 { 263 hdr.maxOpsPerInsn = 1 264 } else { 265 hdr.maxOpsPerInsn = b.uint8() 266 } 267 268 if b.uint8() == 0 { 269 hdr.defStmt = false 270 } else { 271 hdr.defStmt = true 272 } 273 hdr.lineBase = int8(b.uint8()) 274 hdr.lineRange = b.uint8() 275 hdr.opBase = b.uint8() 276 hdr.opLen = b.bytes(int(hdr.opBase - 1)) 277 278 for d := b.string(); len(d) > 0; d = b.string() { 279 hdr.dirs = append(hdr.dirs, d) 280 } 281 282 for f := b.string(); len(f) > 0; f = b.string() { 283 d := b.uint() 284 if !filepath.IsAbs(f) { 285 if d > 0 { 286 if d > uint64(len(hdr.dirs)) { 287 b.error("DWARF directory index out of range") 288 return 289 } 290 f = filepath.Join(hdr.dirs[d-1], f) 291 } else if u.dir != "" { 292 f = filepath.Join(u.dir, f) 293 } 294 } 295 b.uint() // file's last mtime 296 b.uint() // file length 297 hdr.files = append(hdr.files, f) 298 } 299 300 if end > b.off { 301 b.bytes(int(end - b.off)) 302 } 303 304 return 305 } 306 307 // parseLineProgram parses a line program, adding information to 308 // d.lineInfo as it goes. 309 func (d *Data) parseLineProgram(u *unit, b *buf, hdr lineHdr, end Offset) { 310 address := uint64(0) 311 line := 1 312 resetLineInfo := Line{ 313 Filename: "", 314 OpIndex: 0, 315 Line: 0, 316 Column: 0, 317 ISA: 0, 318 Discriminator: 0, 319 Stmt: hdr.defStmt, 320 Block: false, 321 EndPrologue: false, 322 BeginEpilogue: false, 323 } 324 if len(hdr.files) > 0 { 325 resetLineInfo.Filename = hdr.files[0] 326 } 327 lineInfo := resetLineInfo 328 329 var lines []mapLineInfo 330 331 minInsnLen := uint64(hdr.minInsnLen) 332 maxOpsPerInsn := uint64(hdr.maxOpsPerInsn) 333 lineBase := int(hdr.lineBase) 334 lineRange := hdr.lineRange 335 newLineInfo := true 336 for b.off < end && b.err == nil { 337 op := b.uint8() 338 if op >= hdr.opBase { 339 // This is a special opcode. 340 op -= hdr.opBase 341 advance := uint64(op / hdr.lineRange) 342 opIndex := uint64(lineInfo.OpIndex) 343 address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn) 344 newOpIndex := int((opIndex + advance) % maxOpsPerInsn) 345 line += lineBase + int(op%lineRange) 346 if newOpIndex != lineInfo.OpIndex { 347 lineInfo.OpIndex = newOpIndex 348 newLineInfo = true 349 } 350 lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo) 351 } else if op == LineExtendedOp { 352 c := b.uint() 353 op = b.uint8() 354 switch op { 355 case LineExtEndSequence: 356 u.lines = append(u.lines, lines...) 357 lineInfo = resetLineInfo 358 lines = nil 359 newLineInfo = true 360 case LineExtSetAddress: 361 address = b.addr() 362 case LineExtDefineFile: 363 f := b.string() 364 d := b.uint() 365 b.uint() // mtime 366 b.uint() // length 367 if d > 0 && !filepath.IsAbs(f) { 368 if d >= uint64(len(hdr.dirs)) { 369 b.error("DWARF directory index out of range") 370 return 371 } 372 f = filepath.Join(hdr.dirs[d-1], f) 373 } 374 hdr.files = append(hdr.files, f) 375 case LineExtSetDiscriminator: 376 lineInfo.Discriminator = int(b.uint()) 377 newLineInfo = true 378 default: 379 if c > 0 { 380 b.bytes(int(c) - 1) 381 } 382 } 383 } else { 384 switch op { 385 case LineCopy: 386 lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo) 387 case LineAdvancePC: 388 advance := b.uint() 389 opIndex := uint64(lineInfo.OpIndex) 390 address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn) 391 newOpIndex := int((opIndex + advance) % maxOpsPerInsn) 392 if newOpIndex != lineInfo.OpIndex { 393 lineInfo.OpIndex = newOpIndex 394 newLineInfo = true 395 } 396 case LineAdvanceLine: 397 line += int(b.int()) 398 case LineSetFile: 399 i := b.uint() 400 if i > uint64(len(hdr.files)) { 401 b.error("DWARF file number out of range") 402 return 403 } 404 lineInfo.Filename = hdr.files[i-1] 405 newLineInfo = true 406 case LineSetColumn: 407 lineInfo.Column = int(b.uint()) 408 newLineInfo = true 409 case LineNegateStmt: 410 lineInfo.Stmt = !lineInfo.Stmt 411 newLineInfo = true 412 case LineSetBasicBlock: 413 lineInfo.Block = true 414 newLineInfo = true 415 case LineConstAddPC: 416 op = 255 - hdr.opBase 417 advance := uint64(op / hdr.lineRange) 418 opIndex := uint64(lineInfo.OpIndex) 419 address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn) 420 newOpIndex := int((opIndex + advance) % maxOpsPerInsn) 421 if newOpIndex != lineInfo.OpIndex { 422 lineInfo.OpIndex = newOpIndex 423 newLineInfo = true 424 } 425 case LineFixedAdvancePC: 426 address += uint64(b.uint16()) 427 if lineInfo.OpIndex != 0 { 428 lineInfo.OpIndex = 0 429 newLineInfo = true 430 } 431 case LineSetPrologueEnd: 432 lineInfo.EndPrologue = true 433 newLineInfo = true 434 case LineSetEpilogueBegin: 435 lineInfo.BeginEpilogue = true 436 newLineInfo = true 437 case LineSetISA: 438 lineInfo.ISA = int(b.uint()) 439 newLineInfo = true 440 default: 441 if int(op) >= len(hdr.opLen) { 442 b.error("DWARF line opcode has unknown length") 443 return 444 } 445 for i := hdr.opLen[op-1]; i > 0; i-- { 446 b.int() 447 } 448 } 449 } 450 } 451 } 452 453 // addLine adds the current address and line to lines using lineInfo. 454 // If newLineInfo is true this is a new lineInfo. This returns the 455 // updated lines, lineInfo, and newLineInfo. 456 func (d *Data) addLine(lines []mapLineInfo, lineInfo Line, address uint64, line int, newLineInfo bool) ([]mapLineInfo, Line, bool) { 457 if newLineInfo { 458 if len(lines) > 0 { 459 sort.Sort(lines[len(lines)-1].addrs) 460 p := &lines[len(lines)-1] 461 if len(p.addrs) > 0 && address > p.addrs[len(p.addrs)-1].pc { 462 p.addrs = append(p.addrs, oneLineInfo{address, p.addrs[len(p.addrs)-1].line}) 463 } 464 } 465 lines = append(lines, mapLineInfo{line: lineInfo}) 466 } 467 p := &lines[len(lines)-1] 468 p.addrs = append(p.addrs, oneLineInfo{address, line}) 469 470 if lineInfo.Block || lineInfo.EndPrologue || lineInfo.BeginEpilogue || lineInfo.Discriminator != 0 { 471 lineInfo.Block = false 472 lineInfo.EndPrologue = false 473 lineInfo.BeginEpilogue = false 474 lineInfo.Discriminator = 0 475 newLineInfo = true 476 } else { 477 newLineInfo = false 478 } 479 480 return lines, lineInfo, newLineInfo 481 }