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  }