diff options
Diffstat (limited to 'llgo/third_party/gofrontend/libgo/go/debug')
24 files changed, 1330 insertions, 655 deletions
diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/buf.go b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/buf.go index 53c46eb4b81..2ade0bd76ad 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/buf.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/buf.go @@ -163,6 +163,17 @@ func (b *buf) addr() uint64 { return 0 } +func (b *buf) unitLength() (length Offset, dwarf64 bool) { + length = Offset(b.uint32()) + if length == 0xffffffff { + dwarf64 = true + length = Offset(b.uint64()) + } else if length >= 0xfffffff0 { + b.error("unit length has reserved value") + } + return +} + func (b *buf) error(s string) { if b.err == nil { b.data = nil diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/class_string.go b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/class_string.go new file mode 100644 index 00000000000..0b1206b9f3d --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/class_string.go @@ -0,0 +1,17 @@ +// generated by stringer -type=Class; DO NOT EDIT + +package dwarf + +import "fmt" + +const _Class_name = "ClassAddressClassBlockClassConstantClassExprLocClassFlagClassLinePtrClassLocListPtrClassMacPtrClassRangeListPtrClassReferenceClassReferenceSigClassStringClassReferenceAltClassStringAlt" + +var _Class_index = [...]uint8{0, 12, 22, 35, 47, 56, 68, 83, 94, 111, 125, 142, 153, 170, 184} + +func (i Class) String() string { + i -= 1 + if i < 0 || i+1 >= Class(len(_Class_index)) { + return fmt.Sprintf("Class(%d)", i+1) + } + return _Class_name[_Class_index[i]:_Class_index[i+1]] +} diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/const.go b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/const.go index 6cc6bc937a5..2170db1e32d 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/const.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/const.go @@ -453,29 +453,30 @@ const ( encImaginaryFloat = 0x09 ) -// Line number opcodes. +// Statement program standard opcode encodings. const ( - LineExtendedOp = 0 - LineCopy = 1 - LineAdvancePC = 2 - LineAdvanceLine = 3 - LineSetFile = 4 - LineSetColumn = 5 - LineNegateStmt = 6 - LineSetBasicBlock = 7 - LineConstAddPC = 8 - LineFixedAdvancePC = 9 - // next 3 are DWARF 3 - LineSetPrologueEnd = 10 - LineSetEpilogueBegin = 11 - LineSetISA = 12 + lnsCopy = 1 + lnsAdvancePC = 2 + lnsAdvanceLine = 3 + lnsSetFile = 4 + lnsSetColumn = 5 + lnsNegateStmt = 6 + lnsSetBasicBlock = 7 + lnsConstAddPC = 8 + lnsFixedAdvancePC = 9 + + // DWARF 3 + lnsSetPrologueEnd = 10 + lnsSetEpilogueBegin = 11 + lnsSetISA = 12 ) -// Line number extended opcodes. +// Statement program extended opcode encodings. const ( - LineExtEndSequence = 1 - LineExtSetAddress = 2 - LineExtDefineFile = 3 - // next 1 is DWARF 4 - LineExtSetDiscriminator = 4 + lneEndSequence = 1 + lneSetAddress = 2 + lneDefineFile = 3 + + // DWARF 4 + lneSetDiscriminator = 4 ) diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/entry.go b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/entry.go index b6ba8c0d1c3..d607e5b4a38 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/entry.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/entry.go @@ -23,8 +23,9 @@ type abbrev struct { } type afield struct { - attr Attr - fmt format + attr Attr + fmt format + class Class } // a map from entry format ids to their descriptions @@ -32,7 +33,7 @@ type abbrevTable map[uint32]abbrev // ParseAbbrev returns the abbreviation table that starts at byte off // in the .debug_abbrev section. -func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) { +func (d *Data) parseAbbrev(off uint32, vers int) (abbrevTable, error) { if m, ok := d.abbrevCache[off]; ok { return m, nil } @@ -80,6 +81,7 @@ func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) { for i := range a.field { a.field[i].attr = Attr(b.uint()) a.field[i].fmt = format(b.uint()) + a.field[i].class = formToClass(a.field[i].fmt, a.field[i].attr, vers, &b) } b.uint() b.uint() @@ -93,6 +95,118 @@ func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) { return m, nil } +// attrIsExprloc indicates attributes that allow exprloc values that +// are encoded as block values in DWARF 2 and 3. See DWARF 4, Figure +// 20. +var attrIsExprloc = map[Attr]bool{ + AttrLocation: true, + AttrByteSize: true, + AttrBitOffset: true, + AttrBitSize: true, + AttrStringLength: true, + AttrLowerBound: true, + AttrReturnAddr: true, + AttrStrideSize: true, + AttrUpperBound: true, + AttrCount: true, + AttrDataMemberLoc: true, + AttrFrameBase: true, + AttrSegment: true, + AttrStaticLink: true, + AttrUseLocation: true, + AttrVtableElemLoc: true, + AttrAllocated: true, + AttrAssociated: true, + AttrDataLocation: true, + AttrStride: true, +} + +// attrPtrClass indicates the *ptr class of attributes that have +// encoding formSecOffset in DWARF 4 or formData* in DWARF 2 and 3. +var attrPtrClass = map[Attr]Class{ + AttrLocation: ClassLocListPtr, + AttrStmtList: ClassLinePtr, + AttrStringLength: ClassLocListPtr, + AttrReturnAddr: ClassLocListPtr, + AttrStartScope: ClassRangeListPtr, + AttrDataMemberLoc: ClassLocListPtr, + AttrFrameBase: ClassLocListPtr, + AttrMacroInfo: ClassMacPtr, + AttrSegment: ClassLocListPtr, + AttrStaticLink: ClassLocListPtr, + AttrUseLocation: ClassLocListPtr, + AttrVtableElemLoc: ClassLocListPtr, + AttrRanges: ClassRangeListPtr, +} + +// formToClass returns the DWARF 4 Class for the given form. If the +// DWARF version is less then 4, it will disambiguate some forms +// depending on the attribute. +func formToClass(form format, attr Attr, vers int, b *buf) Class { + switch form { + default: + b.error("cannot determine class of unknown attribute form") + return 0 + + case formAddr: + return ClassAddress + + case formDwarfBlock1, formDwarfBlock2, formDwarfBlock4, formDwarfBlock: + // In DWARF 2 and 3, ClassExprLoc was encoded as a + // block. DWARF 4 distinguishes ClassBlock and + // ClassExprLoc, but there are no attributes that can + // be both, so we also promote ClassBlock values in + // DWARF 4 that should be ClassExprLoc in case + // producers get this wrong. + if attrIsExprloc[attr] { + return ClassExprLoc + } + return ClassBlock + + case formData1, formData2, formData4, formData8, formSdata, formUdata: + // In DWARF 2 and 3, ClassPtr was encoded as a + // constant. Unlike ClassExprLoc/ClassBlock, some + // DWARF 4 attributes need to distinguish Class*Ptr + // from ClassConstant, so we only do this promotion + // for versions 2 and 3. + if class, ok := attrPtrClass[attr]; vers < 4 && ok { + return class + } + return ClassConstant + + case formFlag, formFlagPresent: + return ClassFlag + + case formRefAddr, formRef1, formRef2, formRef4, formRef8, formRefUdata: + return ClassReference + + case formRefSig8: + return ClassReferenceSig + + case formString, formStrp: + return ClassString + + case formSecOffset: + // DWARF 4 defines four *ptr classes, but doesn't + // distinguish them in the encoding. Disambiguate + // these classes using the attribute. + if class, ok := attrPtrClass[attr]; ok { + return class + } + b.error("cannot determine class of unknown attribute with formSecOffset") + return 0 + + case formExprloc: + return ClassExprLoc + + case formGnuRefAlt: + return ClassReferenceAlt + + case formGnuStrpAlt: + return ClassStringAlt + } +} + // An entry is a sequence of attribute/value pairs. type Entry struct { Offset Offset // offset of Entry in DWARF info @@ -102,9 +216,115 @@ type Entry struct { } // A Field is a single attribute/value pair in an Entry. +// +// A value can be one of several "attribute classes" defined by DWARF. +// The Go types corresponding to each class are: +// +// DWARF class Go type Class +// ----------- ------- ----- +// address uint64 ClassAddress +// block []byte ClassBlock +// constant int64 ClassConstant +// flag bool ClassFlag +// reference +// to info dwarf.Offset ClassReference +// to type unit uint64 ClassReferenceSig +// string string ClassString +// exprloc []byte ClassExprLoc +// lineptr int64 ClassLinePtr +// loclistptr int64 ClassLocListPtr +// macptr int64 ClassMacPtr +// rangelistptr int64 ClassRangeListPtr type Field struct { - Attr Attr - Val interface{} + Attr Attr + Val interface{} + Class Class +} + +// A Class is the DWARF 4 class of an attibute value. +// +// In general, a given attribute's value may take on one of several +// possible classes defined by DWARF, each of which leads to a +// slightly different interpretation of the attribute. +// +// DWARF version 4 distinguishes attribute value classes more finely +// than previous versions of DWARF. The reader will disambiguate +// coarser classes from earlier versions of DWARF into the appropriate +// DWARF 4 class. For example, DWARF 2 uses "constant" for constants +// as well as all types of section offsets, but the reader will +// canonicalize attributes in DWARF 2 files that refer to section +// offsets to one of the Class*Ptr classes, even though these classes +// were only defined in DWARF 3. +type Class int + +const ( + // ClassAddress represents values of type uint64 that are + // addresses on the target machine. + ClassAddress Class = 1 + iota + + // ClassBlock represents values of type []byte whose + // interpretation depends on the attribute. + ClassBlock + + // ClassConstant represents values of type int64 that are + // constants. The interpretation of this constant depends on + // the attribute. + ClassConstant + + // ClassExprLoc represents values of type []byte that contain + // an encoded DWARF expression or location description. + ClassExprLoc + + // ClassFlag represents values of type bool. + ClassFlag + + // ClassLinePtr represents values that are an int64 offset + // into the "line" section. + ClassLinePtr + + // ClassLocListPtr represents values that are an int64 offset + // into the "loclist" section. + ClassLocListPtr + + // ClassMacPtr represents values that are an int64 offset into + // the "mac" section. + ClassMacPtr + + // ClassMacPtr represents values that are an int64 offset into + // the "rangelist" section. + ClassRangeListPtr + + // ClassReference represents values that are an Offset offset + // of an Entry in the info section (for use with Reader.Seek). + // The DWARF specification combines ClassReference and + // ClassReferenceSig into class "reference". + ClassReference + + // ClassReferenceSig represents values that are a uint64 type + // signature referencing a type Entry. + ClassReferenceSig + + // ClassString represents values that are strings. If the + // compilation unit specifies the AttrUseUTF8 flag (strongly + // recommended), the string value will be encoded in UTF-8. + // Otherwise, the encoding is unspecified. + ClassString + + // ClassReferenceAlt represents values of type int64 that are + // an offset into the DWARF "info" section of an alternate + // object file. + ClassReferenceAlt + + // ClassStringAlt represents values of type int64 that are an + // offset into the DWARF string section of an alternate object + // file. + ClassStringAlt +) + +//go:generate stringer -type=Class + +func (i Class) GoString() string { + return "dwarf." + i.String() } // Val returns the value associated with attribute Attr in Entry, @@ -112,12 +332,21 @@ type Field struct { // // A common idiom is to merge the check for nil return with // the check that the value has the expected dynamic type, as in: -// v, ok := e.Val(AttrSibling).(int64); +// v, ok := e.Val(AttrSibling).(int64) // func (e *Entry) Val(a Attr) interface{} { - for _, f := range e.Field { + if f := e.AttrField(a); f != nil { + return f.Val + } + return nil +} + +// AttrField returns the Field associated with attribute Attr in +// Entry, or nil if there is no such attribute. +func (e *Entry) AttrField(a Attr) *Field { + for i, f := range e.Field { if f.Attr == a { - return f.Val + return &e.Field[i] } } return nil @@ -148,6 +377,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { } for i := range e.Field { e.Field[i].Attr = a.field[i].attr + e.Field[i].Class = a.field[i].class fmt := a.field[i].fmt if fmt == formIndirect { fmt = format(b.uint()) @@ -292,13 +522,10 @@ func (d *Data) Reader() *Reader { return r } -// unitReader returns a new reader starting at a specific unit. -func (d *Data) unitReader(i int) *Reader { - r := &Reader{d: d} - r.unit = i - u := &d.unit[i] - r.b = makeBuf(d, u, "info", u.off, u.data) - return r +// AddressSize returns the size in bytes of addresses in the current compilation +// unit. +func (r *Reader) AddressSize() int { + return r.d.unit[r.unit].asize } // Seek positions the Reader at offset off in the encoded entry stream. @@ -317,18 +544,14 @@ func (r *Reader) Seek(off Offset) { return } - // TODO(rsc): binary search (maybe a new package) - var i int - var u *unit - for i = range d.unit { - u = &d.unit[i] - if u.off <= off && off < u.off+Offset(len(u.data)) { - r.unit = i - r.b = makeBuf(r.d, u, "info", off, u.data[off-u.off:]) - return - } + i := d.offsetToUnit(off) + if i == -1 { + r.err = errors.New("offset out of range") + return } - r.err = errors.New("offset out of range") + u := &d.unit[i] + r.unit = i + r.b = makeBuf(r.d, u, "info", off, u.data[off-u.off:]) } // maybeNextUnit advances to the next unit if this one is finished. diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/line.go b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/line.go index c463c3b0d8f..ca64bbd7f3b 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/line.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/line.go @@ -1,481 +1,590 @@ -// Copyright 2012 The Go Authors. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// DWARF line number information. - package dwarf import ( "errors" - "path/filepath" - "sort" - "strconv" + "fmt" + "io" + "path" ) -// A Line holds all the available information about the source code -// corresponding to a specific program counter address. -type Line struct { - Filename string // source file name - OpIndex int // index of operation in VLIW instruction - Line int // line number - Column int // column number - ISA int // instruction set code - Discriminator int // block discriminator - Stmt bool // instruction starts statement - Block bool // instruction starts basic block - EndPrologue bool // instruction ends function prologue - BeginEpilogue bool // instruction begins function epilogue +// A LineReader reads a sequence of LineEntry structures from a DWARF +// "line" section for a single compilation unit. LineEntries occur in +// order of increasing PC and each LineEntry gives metadata for the +// instructions from that LineEntry's PC to just before the next +// LineEntry's PC. The last entry will have its EndSequence field set. +type LineReader struct { + buf buf + + // Original .debug_line section data. Used by Seek. + section []byte + + // Header information + version uint16 + minInstructionLength int + maxOpsPerInstruction int + defaultIsStmt bool + lineBase int + lineRange int + opcodeBase int + opcodeLengths []int + directories []string + fileEntries []*LineFile + + programOffset Offset // section offset of line number program + endOffset Offset // section offset of byte following program + + initialFileEntries int // initial length of fileEntries + + // Current line number program state machine registers + state LineEntry // public state + fileIndex int // private state } -// LineForPc returns the line number information for a program counter -// address, if any. When this returns multiple Line structures in a -// context where only one can be used, the last one is the best. -func (d *Data) LineForPC(pc uint64) ([]*Line, error) { - for i := range d.unit { - u := &d.unit[i] - if u.pc == nil { - if err := d.readUnitLine(i, u); err != nil { - return nil, err - } - } - for _, ar := range u.pc { - if pc >= ar.low && pc < ar.high { - return d.findLine(u, pc) - } - } +// A LineEntry is a row in a DWARF line table. +type LineEntry struct { + // Address is the program-counter value of a machine + // instruction generated by the compiler. This LineEntry + // applies to each instruction from Address to just before the + // Address of the next LineEntry. + Address uint64 + + // OpIndex is the index of an operation within a VLIW + // instruction. The index of the first operation is 0. For + // non-VLIW architectures, it will always be 0. Address and + // OpIndex together form an operation pointer that can + // reference any individual operation within the instruction + // stream. + OpIndex int + + // File is the source file corresponding to these + // instructions. + File *LineFile + + // Line is the source code line number corresponding to these + // instructions. Lines are numbered beginning at 1. It may be + // 0 if these instructions cannot be attributed to any source + // line. + Line int + + // Column is the column number within the source line of these + // instructions. Columns are numbered beginning at 1. It may + // be 0 to indicate the "left edge" of the line. + Column int + + // IsStmt indicates that Address is a recommended breakpoint + // location, such as the beginning of a line, statement, or a + // distinct subpart of a statement. + IsStmt bool + + // BasicBlock indicates that Address is the beginning of a + // basic block. + BasicBlock bool + + // PrologueEnd indicates that Address is one (of possibly + // many) PCs where execution should be suspended for a + // breakpoint on entry to the containing function. + // + // Added in DWARF 3. + PrologueEnd bool + + // EpilogueBegin indicates that Address is one (of possibly + // many) PCs where execution should be suspended for a + // breakpoint on exit from this function. + // + // Added in DWARF 3. + EpilogueBegin bool + + // ISA is the instruction set architecture for these + // instructions. Possible ISA values should be defined by the + // applicable ABI specification. + // + // Added in DWARF 3. + ISA int + + // Discriminator is an arbitrary integer indicating the block + // to which these instructions belong. It serves to + // distinguish among multiple blocks that may all have with + // the same source file, line, and column. Where only one + // block exists for a given source position, it should be 0. + // + // Added in DWARF 3. + Discriminator int + + // EndSequence indicates that Address is the first byte after + // the end of a sequence of target machine instructions. If it + // is set, only this and the Address field are meaningful. A + // line number table may contain information for multiple + // potentially disjoint instruction sequences. The last entry + // in a line table should always have EndSequence set. + EndSequence bool +} + +// A LineFile is a source file referenced by a DWARF line table entry. +type LineFile struct { + Name string + Mtime uint64 // Implementation defined modification time, or 0 if unknown + Length int // File length, or 0 if unknown +} + +// LineReader returns a new reader for the line table of compilation +// unit cu, which must be an Entry with tag TagCompileUnit. +// +// If this compilation unit has no line table, it returns nil, nil. +func (d *Data) LineReader(cu *Entry) (*LineReader, error) { + if d.line == nil { + // No line tables available. + return nil, nil + } + + // Get line table information from cu. + off, ok := cu.Val(AttrStmtList).(int64) + if !ok { + // cu has no line table. + return nil, nil } - return nil, nil + if off > int64(len(d.line)) { + return nil, errors.New("AttrStmtList value out of range") + } + // AttrCompDir is optional if all file names are absolute. Use + // the empty string if it's not present. + compDir, _ := cu.Val(AttrCompDir).(string) + + // Create the LineReader. + u := &d.unit[d.offsetToUnit(cu.Offset)] + buf := makeBuf(d, u, "line", Offset(off), d.line[off:]) + // The compilation directory is implicitly directories[0]. + r := LineReader{buf: buf, section: d.line, directories: []string{compDir}} + + // Read the header. + if err := r.readHeader(); err != nil { + return nil, err + } + + // Initialize line reader state. + r.Reset() + + return &r, nil } -// readUnitLine reads in the line number information for a compilation -// unit. -func (d *Data) readUnitLine(i int, u *unit) error { - r := d.unitReader(i) - setLineOff := false - for { - e, err := r.Next() - if err != nil { - return err +// readHeader reads the line number program header from r.buf and sets +// all of the header fields in r. +func (r *LineReader) readHeader() error { + buf := &r.buf + + // Read basic header fields [DWARF2 6.2.4]. + hdrOffset := buf.off + unitLength, dwarf64 := buf.unitLength() + r.endOffset = buf.off + unitLength + if r.endOffset > buf.off+Offset(len(buf.data)) { + return DecodeError{"line", hdrOffset, fmt.Sprintf("line table end %d exceeds section size %d", r.endOffset, buf.off+Offset(len(buf.data)))} + } + r.version = buf.uint16() + if buf.err == nil && (r.version < 2 || r.version > 4) { + // DWARF goes to all this effort to make new opcodes + // backward-compatible, and then adds fields right in + // the middle of the header in new versions, so we're + // picky about only supporting known line table + // versions. + return DecodeError{"line", hdrOffset, fmt.Sprintf("unknown line table version %d", r.version)} + } + var headerLength Offset + if dwarf64 { + headerLength = Offset(buf.uint64()) + } else { + headerLength = Offset(buf.uint32()) + } + r.programOffset = buf.off + headerLength + r.minInstructionLength = int(buf.uint8()) + if r.version >= 4 { + // [DWARF4 6.2.4] + r.maxOpsPerInstruction = int(buf.uint8()) + } else { + r.maxOpsPerInstruction = 1 + } + r.defaultIsStmt = buf.uint8() != 0 + r.lineBase = int(int8(buf.uint8())) + r.lineRange = int(buf.uint8()) + + // Validate header. + if buf.err != nil { + return buf.err + } + if r.maxOpsPerInstruction == 0 { + return DecodeError{"line", hdrOffset, "invalid maximum operations per instruction: 0"} + } + if r.lineRange == 0 { + return DecodeError{"line", hdrOffset, "invalid line range: 0"} + } + + // Read standard opcode length table. This table starts with opcode 1. + r.opcodeBase = int(buf.uint8()) + r.opcodeLengths = make([]int, r.opcodeBase) + for i := 1; i < r.opcodeBase; i++ { + r.opcodeLengths[i] = int(buf.uint8()) + } + + // Validate opcode lengths. + if buf.err != nil { + return buf.err + } + for i, length := range r.opcodeLengths { + if known, ok := knownOpcodeLengths[i]; ok && known != length { + return DecodeError{"line", hdrOffset, fmt.Sprintf("opcode %d expected to have length %d, but has length %d", i, known, length)} } - if e == nil { - break + } + + // Read include directories table. The caller already set + // directories[0] to the compilation directory. + for { + directory := buf.string() + if buf.err != nil { + return buf.err } - if r.unit != i { + if len(directory) == 0 { break } - switch e.Tag { - case TagCompileUnit, TagSubprogram, TagEntryPoint, TagInlinedSubroutine: - low, lowok := e.Val(AttrLowpc).(uint64) - var high uint64 - var highok bool - switch v := e.Val(AttrHighpc).(type) { - case uint64: - high = v - highok = true - case int64: - high = low + uint64(v) - highok = true - } - if lowok && highok { - u.pc = append(u.pc, addrRange{low, high}) - } else if off, ok := e.Val(AttrRanges).(Offset); ok { - if err := d.readAddressRanges(off, low, u); err != nil { - return err - } - } - val := e.Val(AttrStmtList) - if val != nil { - if off, ok := val.(int64); ok { - u.lineoff = Offset(off) - setLineOff = true - } else if off, ok := val.(Offset); ok { - u.lineoff = off - setLineOff = true - } else { - return errors.New("unrecognized format for DW_ATTR_stmt_list") - } - } - if dir, ok := e.Val(AttrCompDir).(string); ok { - u.dir = dir - } + if !path.IsAbs(directory) { + // Relative paths are implicitly relative to + // the compilation directory. + directory = path.Join(r.directories[0], directory) } + r.directories = append(r.directories, directory) } - if !setLineOff { - u.lineoff = Offset(0) - u.lineoff-- + + // Read file name list. File numbering starts with 1, so leave + // the first entry nil. + r.fileEntries = make([]*LineFile, 1) + for { + if done, err := r.readFileEntry(); err != nil { + return err + } else if done { + break + } } - return nil + r.initialFileEntries = len(r.fileEntries) + + return buf.err } -// readAddressRanges adds address ranges to a unit. -func (d *Data) readAddressRanges(off Offset, base uint64, u *unit) error { - b := makeBuf(d, u, "ranges", off, d.ranges[off:]) - var highest uint64 - switch u.addrsize() { - case 1: - highest = 0xff - case 2: - highest = 0xffff - case 4: - highest = 0xffffffff - case 8: - highest = 0xffffffffffffffff - default: - return errors.New("unknown address size") +// readFileEntry reads a file entry from either the header or a +// DW_LNE_define_file extended opcode and adds it to r.fileEntries. A +// true return value indicates that there are no more entries to read. +func (r *LineReader) readFileEntry() (bool, error) { + name := r.buf.string() + if r.buf.err != nil { + return false, r.buf.err } - for { - if b.err != nil { - return b.err - } - low := b.addr() - high := b.addr() - if low == 0 && high == 0 { - return b.err - } else if low == highest { - base = high - } else { - u.pc = append(u.pc, addrRange{low + base, high + base}) + if len(name) == 0 { + return true, nil + } + off := r.buf.off + dirIndex := int(r.buf.uint()) + if !path.IsAbs(name) { + if dirIndex >= len(r.directories) { + return false, DecodeError{"line", off, "directory index too large"} } + name = path.Join(r.directories[dirIndex], name) } + mtime := r.buf.uint() + length := int(r.buf.uint()) + + r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length}) + return false, nil } -// findLine finds the line information for a PC value, given the unit -// containing the information. -func (d *Data) findLine(u *unit, pc uint64) ([]*Line, error) { - if u.lines == nil { - if err := d.parseLine(u); err != nil { - return nil, err - } +// updateFile updates r.state.File after r.fileIndex has +// changed or r.fileEntries has changed. +func (r *LineReader) updateFile() { + if r.fileIndex < len(r.fileEntries) { + r.state.File = r.fileEntries[r.fileIndex] + } else { + r.state.File = nil } +} - for _, ln := range u.lines { - if pc < ln.addrs[0].pc || pc > ln.addrs[len(ln.addrs)-1].pc { - continue +// Next sets *entry to the next row in this line table and moves to +// the next row. If there are no more entries and the line table is +// properly terminated, it returns io.EOF. +// +// Rows are always in order of increasing entry.Address, but +// entry.Line may go forward or backward. +func (r *LineReader) Next(entry *LineEntry) error { + if r.buf.err != nil { + return r.buf.err + } + + // Execute opcodes until we reach an opcode that emits a line + // table entry. + for { + if len(r.buf.data) == 0 { + return io.EOF } - i := sort.Search(len(ln.addrs), - func(i int) bool { return ln.addrs[i].pc > pc }) - i-- - p := new(Line) - *p = ln.line - p.Line = ln.addrs[i].line - ret := []*Line{p} - for i++; i < len(ln.addrs) && ln.addrs[i].pc == pc; i++ { - p = new(Line) - *p = ln.line - p.Line = ln.addrs[i].line - ret = append(ret, p) + emit := r.step(entry) + if r.buf.err != nil { + return r.buf.err + } + if emit { + return nil } - return ret, nil } +} - return nil, nil +// knownOpcodeLengths gives the opcode lengths (in varint arguments) +// of known standard opcodes. +var knownOpcodeLengths = map[int]int{ + lnsCopy: 0, + lnsAdvancePC: 1, + lnsAdvanceLine: 1, + lnsSetFile: 1, + lnsNegateStmt: 0, + lnsSetBasicBlock: 0, + lnsConstAddPC: 0, + lnsSetPrologueEnd: 0, + lnsSetEpilogueBegin: 0, + lnsSetISA: 1, + // lnsFixedAdvancePC takes a uint8 rather than a varint; it's + // unclear what length the header is supposed to claim, so + // ignore it. } -// FileLine returns the file name and line number for a program -// counter address, or "", 0 if unknown. -func (d *Data) FileLine(pc uint64) (string, int, error) { - r, err := d.LineForPC(pc) - if err != nil { - return "", 0, err - } - if r == nil { - return "", 0, nil +// step processes the next opcode and updates r.state. If the opcode +// emits a row in the line table, this updates *entry and returns +// true. +func (r *LineReader) step(entry *LineEntry) bool { + opcode := int(r.buf.uint8()) + + if opcode >= r.opcodeBase { + // Special opcode [DWARF2 6.2.5.1, DWARF4 6.2.5.1] + adjustedOpcode := opcode - r.opcodeBase + r.advancePC(adjustedOpcode / r.lineRange) + lineDelta := r.lineBase + int(adjustedOpcode)%r.lineRange + r.state.Line += lineDelta + goto emit } - ln := r[len(r)-1] - return ln.Filename, ln.Line, nil -} -// A mapLineInfo holds the PC values and line numbers associated with -// a single Line structure. This representation is chosen to reduce -// memory usage based on typical debug info. -type mapLineInfo struct { - line Line // line.Line will be zero - addrs lineAddrs // sorted by PC -} + switch opcode { + case 0: + // Extended opcode [DWARF2 6.2.5.3] + length := Offset(r.buf.uint()) + startOff := r.buf.off + opcode := r.buf.uint8() -// A list of lines. This will be sorted by PC. -type lineAddrs []oneLineInfo + switch opcode { + case lneEndSequence: + r.state.EndSequence = true + *entry = r.state + r.resetState() -func (p lineAddrs) Len() int { return len(p) } -func (p lineAddrs) Less(i, j int) bool { return p[i].pc < p[j].pc } -func (p lineAddrs) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + case lneSetAddress: + r.state.Address = r.buf.addr() -// A oneLineInfo is a single PC and line number. -type oneLineInfo struct { - pc uint64 - line int -} + case lneDefineFile: + if done, err := r.readFileEntry(); err != nil { + r.buf.err = err + return false + } else if done { + r.buf.err = DecodeError{"line", startOff, "malformed DW_LNE_define_file operation"} + return false + } + r.updateFile() -// A lineHdr holds the relevant information from a line number -// program header. -type lineHdr struct { - version uint16 // version of line number encoding - minInsnLen uint8 // minimum instruction length - maxOpsPerInsn uint8 // maximum number of ops per instruction - defStmt bool // initial value of stmt register - lineBase int8 // line adjustment base - lineRange uint8 // line adjustment step - opBase uint8 // base of special opcode values - opLen []uint8 // lengths of standard opcodes - dirs []string // directories - files []string // file names -} + case lneSetDiscriminator: + // [DWARF4 6.2.5.3] + r.state.Discriminator = int(r.buf.uint()) + } -// parseLine parses the line number information for a compilation unit -func (d *Data) parseLine(u *unit) error { - if u.lineoff+1 == 0 { - return errors.New("unknown line offset") - } - b := makeBuf(d, u, "line", u.lineoff, d.line[u.lineoff:]) - len := uint64(b.uint32()) - dwarf64 := false - if len == 0xffffffff { - len = b.uint64() - dwarf64 = true - } - end := b.off + Offset(len) - hdr := d.parseLineHdr(u, &b, dwarf64) - if b.err == nil { - d.parseLineProgram(u, &b, hdr, end) - } - return b.err -} + r.buf.skip(int(startOff + length - r.buf.off)) -// parseLineHdr parses a line number program header. -func (d *Data) parseLineHdr(u *unit, b *buf, dwarf64 bool) (hdr lineHdr) { - hdr.version = b.uint16() - if hdr.version < 2 || hdr.version > 4 { - b.error("unsupported DWARF version " + strconv.Itoa(int(hdr.version))) - return - } + if opcode == lneEndSequence { + return true + } - var hlen Offset - if dwarf64 { - hlen = Offset(b.uint64()) - } else { - hlen = Offset(b.uint32()) - } - end := b.off + hlen + // Standard opcodes [DWARF2 6.2.5.2] + case lnsCopy: + goto emit - hdr.minInsnLen = b.uint8() - if hdr.version < 4 { - hdr.maxOpsPerInsn = 1 - } else { - hdr.maxOpsPerInsn = b.uint8() - } + case lnsAdvancePC: + r.advancePC(int(r.buf.uint())) - if b.uint8() == 0 { - hdr.defStmt = false - } else { - hdr.defStmt = true - } - hdr.lineBase = int8(b.uint8()) - hdr.lineRange = b.uint8() - hdr.opBase = b.uint8() - hdr.opLen = b.bytes(int(hdr.opBase - 1)) + case lnsAdvanceLine: + r.state.Line += int(r.buf.int()) - for d := b.string(); len(d) > 0; d = b.string() { - hdr.dirs = append(hdr.dirs, d) - } + case lnsSetFile: + r.fileIndex = int(r.buf.uint()) + r.updateFile() - for f := b.string(); len(f) > 0; f = b.string() { - d := b.uint() - if !filepath.IsAbs(f) { - if d > 0 { - if d > uint64(len(hdr.dirs)) { - b.error("DWARF directory index out of range") - return - } - f = filepath.Join(hdr.dirs[d-1], f) - } else if u.dir != "" { - f = filepath.Join(u.dir, f) - } + case lnsSetColumn: + r.state.Column = int(r.buf.uint()) + + case lnsNegateStmt: + r.state.IsStmt = !r.state.IsStmt + + case lnsSetBasicBlock: + r.state.BasicBlock = true + + case lnsConstAddPC: + r.advancePC((255 - r.opcodeBase) / r.lineRange) + + case lnsFixedAdvancePC: + r.state.Address += uint64(r.buf.uint16()) + + // DWARF3 standard opcodes [DWARF3 6.2.5.2] + case lnsSetPrologueEnd: + r.state.PrologueEnd = true + + case lnsSetEpilogueBegin: + r.state.EpilogueBegin = true + + case lnsSetISA: + r.state.ISA = int(r.buf.uint()) + + default: + // Unhandled standard opcode. Skip the number of + // arguments that the prologue says this opcode has. + for i := 0; i < r.opcodeLengths[opcode]; i++ { + r.buf.uint() } - b.uint() // file's last mtime - b.uint() // file length - hdr.files = append(hdr.files, f) } + return false - if end > b.off { - b.bytes(int(end - b.off)) - } +emit: + *entry = r.state + r.state.BasicBlock = false + r.state.PrologueEnd = false + r.state.EpilogueBegin = false + r.state.Discriminator = 0 + return true +} + +// advancePC advances "operation pointer" (the combination of Address +// and OpIndex) in r.state by opAdvance steps. +func (r *LineReader) advancePC(opAdvance int) { + opIndex := r.state.OpIndex + opAdvance + r.state.Address += uint64(r.minInstructionLength * (opIndex / r.maxOpsPerInstruction)) + r.state.OpIndex = opIndex % r.maxOpsPerInstruction +} + +// A LineReaderPos represents a position in a line table. +type LineReaderPos struct { + // off is the current offset in the DWARF line section. + off Offset + // numFileEntries is the length of fileEntries. + numFileEntries int + // state and fileIndex are the statement machine state at + // offset off. + state LineEntry + fileIndex int +} + +// Tell returns the current position in the line table. +func (r *LineReader) Tell() LineReaderPos { + return LineReaderPos{r.buf.off, len(r.fileEntries), r.state, r.fileIndex} +} + +// Seek restores the line table reader to a position returned by Tell. +// +// The argument pos must have been returned by a call to Tell on this +// line table. +func (r *LineReader) Seek(pos LineReaderPos) { + r.buf.off = pos.off + r.buf.data = r.section[r.buf.off:r.endOffset] + r.fileEntries = r.fileEntries[:pos.numFileEntries] + r.state = pos.state + r.fileIndex = pos.fileIndex +} + +// Reset repositions the line table reader at the beginning of the +// line table. +func (r *LineReader) Reset() { + // Reset buffer to the line number program offset. + r.buf.off = r.programOffset + r.buf.data = r.section[r.buf.off:r.endOffset] - return + // Reset file entries list. + r.fileEntries = r.fileEntries[:r.initialFileEntries] + + // Reset line number program state. + r.resetState() } -// parseLineProgram parses a line program, adding information to -// d.lineInfo as it goes. -func (d *Data) parseLineProgram(u *unit, b *buf, hdr lineHdr, end Offset) { - address := uint64(0) - line := 1 - resetLineInfo := Line{ - Filename: "", +// resetState resets r.state to its default values +func (r *LineReader) resetState() { + // Reset the state machine registers to the defaults given in + // [DWARF4 6.2.2]. + r.state = LineEntry{ + Address: 0, OpIndex: 0, - Line: 0, + File: nil, + Line: 1, Column: 0, + IsStmt: r.defaultIsStmt, + BasicBlock: false, + PrologueEnd: false, + EpilogueBegin: false, ISA: 0, Discriminator: 0, - Stmt: hdr.defStmt, - Block: false, - EndPrologue: false, - BeginEpilogue: false, } - if len(hdr.files) > 0 { - resetLineInfo.Filename = hdr.files[0] + r.fileIndex = 1 + r.updateFile() +} + +// ErrUnknownPC is the error returned by LineReader.ScanPC when the +// seek PC is not covered by any entry in the line table. +var ErrUnknownPC = errors.New("ErrUnknownPC") + +// SeekPC sets *entry to the LineEntry that includes pc and positions +// the reader on the next entry in the line table. If necessary, this +// will seek backwards to find pc. +// +// If pc is not covered by any entry in this line table, SeekPC +// returns ErrUnknownPC. In this case, *entry and the final seek +// position are unspecified. +// +// Note that DWARF line tables only permit sequential, forward scans. +// Hence, in the worst case, this takes time linear in the size of the +// line table. If the caller wishes to do repeated fast PC lookups, it +// should build an appropriate index of the line table. +func (r *LineReader) SeekPC(pc uint64, entry *LineEntry) error { + if err := r.Next(entry); err != nil { + return err } - lineInfo := resetLineInfo - - var lines []mapLineInfo - - minInsnLen := uint64(hdr.minInsnLen) - maxOpsPerInsn := uint64(hdr.maxOpsPerInsn) - lineBase := int(hdr.lineBase) - lineRange := hdr.lineRange - newLineInfo := true - for b.off < end && b.err == nil { - op := b.uint8() - if op >= hdr.opBase { - // This is a special opcode. - op -= hdr.opBase - advance := uint64(op / hdr.lineRange) - opIndex := uint64(lineInfo.OpIndex) - address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn) - newOpIndex := int((opIndex + advance) % maxOpsPerInsn) - line += lineBase + int(op%lineRange) - if newOpIndex != lineInfo.OpIndex { - lineInfo.OpIndex = newOpIndex - newLineInfo = true - } - lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo) - } else if op == LineExtendedOp { - c := b.uint() - op = b.uint8() - switch op { - case LineExtEndSequence: - u.lines = append(u.lines, lines...) - lineInfo = resetLineInfo - lines = nil - newLineInfo = true - case LineExtSetAddress: - address = b.addr() - case LineExtDefineFile: - f := b.string() - d := b.uint() - b.uint() // mtime - b.uint() // length - if d > 0 && !filepath.IsAbs(f) { - if d >= uint64(len(hdr.dirs)) { - b.error("DWARF directory index out of range") - return - } - f = filepath.Join(hdr.dirs[d-1], f) - } - hdr.files = append(hdr.files, f) - case LineExtSetDiscriminator: - lineInfo.Discriminator = int(b.uint()) - newLineInfo = true - default: - if c > 0 { - b.bytes(int(c) - 1) - } - } - } else { - switch op { - case LineCopy: - lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo) - case LineAdvancePC: - advance := b.uint() - opIndex := uint64(lineInfo.OpIndex) - address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn) - newOpIndex := int((opIndex + advance) % maxOpsPerInsn) - if newOpIndex != lineInfo.OpIndex { - lineInfo.OpIndex = newOpIndex - newLineInfo = true - } - case LineAdvanceLine: - line += int(b.int()) - case LineSetFile: - i := b.uint() - if i > uint64(len(hdr.files)) { - b.error("DWARF file number out of range") - return - } - lineInfo.Filename = hdr.files[i-1] - newLineInfo = true - case LineSetColumn: - lineInfo.Column = int(b.uint()) - newLineInfo = true - case LineNegateStmt: - lineInfo.Stmt = !lineInfo.Stmt - newLineInfo = true - case LineSetBasicBlock: - lineInfo.Block = true - newLineInfo = true - case LineConstAddPC: - op = 255 - hdr.opBase - advance := uint64(op / hdr.lineRange) - opIndex := uint64(lineInfo.OpIndex) - address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn) - newOpIndex := int((opIndex + advance) % maxOpsPerInsn) - if newOpIndex != lineInfo.OpIndex { - lineInfo.OpIndex = newOpIndex - newLineInfo = true - } - case LineFixedAdvancePC: - address += uint64(b.uint16()) - if lineInfo.OpIndex != 0 { - lineInfo.OpIndex = 0 - newLineInfo = true - } - case LineSetPrologueEnd: - lineInfo.EndPrologue = true - newLineInfo = true - case LineSetEpilogueBegin: - lineInfo.BeginEpilogue = true - newLineInfo = true - case LineSetISA: - lineInfo.ISA = int(b.uint()) - newLineInfo = true - default: - if int(op) >= len(hdr.opLen) { - b.error("DWARF line opcode has unknown length") - return - } - for i := hdr.opLen[op-1]; i > 0; i-- { - b.int() - } - } + if entry.Address > pc { + // We're too far. Start at the beginning of the table. + r.Reset() + if err := r.Next(entry); err != nil { + return err + } + if entry.Address > pc { + // The whole table starts after pc. + r.Reset() + return ErrUnknownPC } } -} -// addLine adds the current address and line to lines using lineInfo. -// If newLineInfo is true this is a new lineInfo. This returns the -// updated lines, lineInfo, and newLineInfo. -func (d *Data) addLine(lines []mapLineInfo, lineInfo Line, address uint64, line int, newLineInfo bool) ([]mapLineInfo, Line, bool) { - if newLineInfo { - if len(lines) > 0 { - sort.Sort(lines[len(lines)-1].addrs) - p := &lines[len(lines)-1] - if len(p.addrs) > 0 && address > p.addrs[len(p.addrs)-1].pc { - p.addrs = append(p.addrs, oneLineInfo{address, p.addrs[len(p.addrs)-1].line}) + // Scan until we pass pc, then back up one. + for { + var next LineEntry + pos := r.Tell() + if err := r.Next(&next); err != nil { + if err == io.EOF { + return ErrUnknownPC } + return err } - lines = append(lines, mapLineInfo{line: lineInfo}) - } - p := &lines[len(lines)-1] - p.addrs = append(p.addrs, oneLineInfo{address, line}) - - if lineInfo.Block || lineInfo.EndPrologue || lineInfo.BeginEpilogue || lineInfo.Discriminator != 0 { - lineInfo.Block = false - lineInfo.EndPrologue = false - lineInfo.BeginEpilogue = false - lineInfo.Discriminator = 0 - newLineInfo = true - } else { - newLineInfo = false + if next.Address > pc { + if entry.EndSequence { + // pc is in a hole in the table. + return ErrUnknownPC + } + // entry is the desired entry. Back up the + // cursor to "next" and return success. + r.Seek(pos) + return nil + } + *entry = next } - - return lines, lineInfo, newLineInfo } diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/line_test.go b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/line_test.go index 2476a6faf53..4104b5d49b8 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/line_test.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/line_test.go @@ -1,4 +1,4 @@ -// Copyright 2012 The Go Authors. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -6,48 +6,224 @@ package dwarf_test import ( . "debug/dwarf" - "path/filepath" + "io" "testing" ) -type lineTest struct { - pc uint64 - file string - line int -} +var ( + file1C = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line1.c"} + file1H = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line1.h"} + file2C = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line2.c"} +) -var elfLineTests = [...]lineTest{ - {0x4004c4, "typedef.c", 83}, - {0x4004c8, "typedef.c", 84}, - {0x4004ca, "typedef.c", 84}, - {0x4003e0, "", 0}, -} +func TestLineELFGCC(t *testing.T) { + // Generated by: + // # gcc --version | head -n1 + // gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 + // # gcc -g -o line-gcc.elf line*.c + + // Line table based on readelf --debug-dump=rawline,decodedline + want := []LineEntry{ + {Address: 0x40059d, File: file1H, Line: 2, IsStmt: true}, + {Address: 0x4005a5, File: file1H, Line: 2, IsStmt: true}, + {Address: 0x4005b4, File: file1H, Line: 5, IsStmt: true}, + {Address: 0x4005bd, File: file1H, Line: 6, IsStmt: true, Discriminator: 2}, + {Address: 0x4005c7, File: file1H, Line: 5, IsStmt: true, Discriminator: 2}, + {Address: 0x4005cb, File: file1H, Line: 5, IsStmt: false, Discriminator: 1}, + {Address: 0x4005d1, File: file1H, Line: 7, IsStmt: true}, + {Address: 0x4005e7, File: file1C, Line: 6, IsStmt: true}, + {Address: 0x4005eb, File: file1C, Line: 7, IsStmt: true}, + {Address: 0x4005f5, File: file1C, Line: 8, IsStmt: true}, + {Address: 0x4005ff, File: file1C, Line: 9, IsStmt: true}, + {Address: 0x400601, EndSequence: true}, -var machoLineTests = [...]lineTest{ - {0x0, "typedef.c", 83}, + {Address: 0x400601, File: file2C, Line: 4, IsStmt: true}, + {Address: 0x400605, File: file2C, Line: 5, IsStmt: true}, + {Address: 0x40060f, File: file2C, Line: 6, IsStmt: true}, + {Address: 0x400611, EndSequence: true}, + } + + testLineTable(t, want, elfData(t, "testdata/line-gcc.elf")) } -func TestLineElf(t *testing.T) { - testLine(t, elfData(t, "testdata/typedef.elf"), elfLineTests[:], "elf") +func TestLineELFClang(t *testing.T) { + // Generated by: + // # clang --version | head -n1 + // Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4) + // # clang -g -o line-clang.elf line*. + + want := []LineEntry{ + {Address: 0x400530, File: file1C, Line: 6, IsStmt: true}, + {Address: 0x400534, File: file1C, Line: 7, IsStmt: true, PrologueEnd: true}, + {Address: 0x400539, File: file1C, Line: 8, IsStmt: true}, + {Address: 0x400545, File: file1C, Line: 9, IsStmt: true}, + {Address: 0x400550, File: file1H, Line: 2, IsStmt: true}, + {Address: 0x400554, File: file1H, Line: 5, IsStmt: true, PrologueEnd: true}, + {Address: 0x400568, File: file1H, Line: 6, IsStmt: true}, + {Address: 0x400571, File: file1H, Line: 5, IsStmt: true}, + {Address: 0x400581, File: file1H, Line: 7, IsStmt: true}, + {Address: 0x400583, EndSequence: true}, + + {Address: 0x400590, File: file2C, Line: 4, IsStmt: true}, + {Address: 0x4005a0, File: file2C, Line: 5, IsStmt: true, PrologueEnd: true}, + {Address: 0x4005a7, File: file2C, Line: 6, IsStmt: true}, + {Address: 0x4005b0, EndSequence: true}, + } + + testLineTable(t, want, elfData(t, "testdata/line-clang.elf")) } -func TestLineMachO(t *testing.T) { - testLine(t, machoData(t, "testdata/typedef.macho"), machoLineTests[:], "macho") +func TestLineSeek(t *testing.T) { + d := elfData(t, "testdata/line-gcc.elf") + + // Get the line table for the first CU. + cu, err := d.Reader().Next() + if err != nil { + t.Fatal("d.Reader().Next:", err) + } + lr, err := d.LineReader(cu) + if err != nil { + t.Fatal("d.LineReader:", err) + } + + // Read entries forward. + var line LineEntry + var posTable []LineReaderPos + var table []LineEntry + for { + posTable = append(posTable, lr.Tell()) + + err := lr.Next(&line) + if err != nil { + if err == io.EOF { + break + } + t.Fatal("lr.Next:", err) + } + table = append(table, line) + } + + // Test that Reset returns to the first line. + lr.Reset() + if err := lr.Next(&line); err != nil { + t.Fatal("lr.Next after Reset failed:", err) + } else if line != table[0] { + t.Fatal("lr.Next after Reset returned", line, "instead of", table[0]) + } + + // Check that entries match when seeking backward. + for i := len(posTable) - 1; i >= 0; i-- { + lr.Seek(posTable[i]) + err := lr.Next(&line) + if i == len(posTable)-1 { + if err != io.EOF { + t.Fatal("expected io.EOF after seek to end, got", err) + } + } else if err != nil { + t.Fatal("lr.Next after seek to", posTable[i], "failed:", err) + } else if line != table[i] { + t.Fatal("lr.Next after seek to", posTable[i], "returned", line, "instead of", table[i]) + } + } + + // Check that seeking to a PC returns the right line. + if err := lr.SeekPC(table[0].Address-1, &line); err != ErrUnknownPC { + t.Fatalf("lr.SeekPC to %#x returned %v instead of ErrUnknownPC", table[0].Address-1, err) + } + for i, testLine := range table { + if testLine.EndSequence { + if err := lr.SeekPC(testLine.Address, &line); err != ErrUnknownPC { + t.Fatalf("lr.SeekPC to %#x returned %v instead of ErrUnknownPC", testLine.Address, err) + } + continue + } + + nextPC := table[i+1].Address + for pc := testLine.Address; pc < nextPC; pc++ { + if err := lr.SeekPC(pc, &line); err != nil { + t.Fatalf("lr.SeekPC to %#x failed: %v", pc, err) + } else if line != testLine { + t.Fatalf("lr.SeekPC to %#x returned %v instead of %v", pc, line, testLine) + } + } + } } -func testLine(t *testing.T, d *Data, tests []lineTest, kind string) { - for _, v := range tests { - file, line, err := d.FileLine(v.pc) +func testLineTable(t *testing.T, want []LineEntry, d *Data) { + // Get line table from d. + var got []LineEntry + dr := d.Reader() + for { + ent, err := dr.Next() if err != nil { - t.Errorf("%s: %v", kind, err) + t.Fatal("dr.Next:", err) + } else if ent == nil { + break + } + + if ent.Tag != TagCompileUnit { + dr.SkipChildren() continue } - if file != "" { - file = filepath.Base(file) + + // Decode CU's line table. + lr, err := d.LineReader(ent) + if err != nil { + t.Fatal("d.LineReader:", err) + } else if lr == nil { + continue } - if file != v.file || line != v.line { - t.Errorf("%s: for %d have %q:%d want %q:%d", - kind, v.pc, file, line, v.file, v.line) + + for { + var line LineEntry + err := lr.Next(&line) + if err != nil { + if err == io.EOF { + break + } + t.Fatal("lr.Next:", err) + } + got = append(got, line) } } + + // Compare line tables. + if !compareLines(got, want) { + t.Log("Line tables do not match. Got:") + dumpLines(t, got) + t.Log("Want:") + dumpLines(t, want) + t.FailNow() + } +} + +func compareLines(a, b []LineEntry) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + al, bl := a[i], b[i] + // If both are EndSequence, then the only other valid + // field is Address. Otherwise, test equality of all + // fields. + if al.EndSequence && bl.EndSequence && al.Address == bl.Address { + continue + } + if al.File.Name != bl.File.Name { + return false + } + al.File = nil + bl.File = nil + if al != bl { + return false + } + } + return true +} + +func dumpLines(t *testing.T, lines []LineEntry) { + for _, l := range lines { + t.Logf(" %+v File:%+v", l, l.File) + } } diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line-clang.elf b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line-clang.elf Binary files differnew file mode 100755 index 00000000000..b63cc781c41 --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line-clang.elf diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line-gcc.elf b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line-gcc.elf Binary files differnew file mode 100755 index 00000000000..50500a8eecd --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line-gcc.elf diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line1.c b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line1.c new file mode 100644 index 00000000000..f35864776ca --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line1.c @@ -0,0 +1,9 @@ +#include "line1.h" + +void f2(); + +int main() +{ + f1(); + f2(); +} diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line1.h b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line1.h new file mode 100644 index 00000000000..974d4c88177 --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line1.h @@ -0,0 +1,7 @@ +static void f1() +{ + char buf[10]; + int i; + for(i = 0; i < 10; i++) + buf[i] = 1; +} diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line2.c b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line2.c new file mode 100644 index 00000000000..38d89983cbb --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line2.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +void f2() +{ + printf("hello\n"); +} diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/type.go b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/type.go index 6986b19e722..a5daa1d0bb1 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/type.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/type.go @@ -268,6 +268,9 @@ type typeReader interface { Next() (*Entry, error) clone() typeReader offset() Offset + // AddressSize returns the size in bytes of addresses in the current + // compilation unit. + AddressSize() int } // Type reads the type at off in the DWARF ``info'' section. @@ -286,6 +289,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off if err != nil { return nil, err } + addressSize := r.AddressSize() if e == nil || e.Offset != off { return nil, DecodeError{name, off, "no type at offset"} } @@ -668,6 +672,12 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off b, ok := e.Val(AttrByteSize).(int64) if !ok { b = -1 + switch t := typ.(type) { + case *TypedefType: + b = t.Type.Size() + case *PtrType: + b = int64(addressSize) + } } typ.Common().ByteSize = b } diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/typeunit.go b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/typeunit.go index 3fd1c9973e5..9cfb4a8b256 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/typeunit.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/typeunit.go @@ -27,21 +27,15 @@ func (d *Data) parseTypes(name string, types []byte) error { b := makeBuf(d, unknownFormat{}, name, 0, types) for len(b.data) > 0 { base := b.off - dwarf64 := false - n := b.uint32() - if n == 0xffffffff { - n64 := b.uint64() - if n64 != uint64(uint32(n64)) { - b.error("type unit length overflow") - return b.err - } - n = uint32(n64) - dwarf64 = true + n, dwarf64 := b.unitLength() + if n != Offset(uint32(n)) { + b.error("type unit length overflow") + return b.err } hdroff := b.off - vers := b.uint16() + vers := int(b.uint16()) if vers != 4 { - b.error("unsupported DWARF version " + strconv.Itoa(int(vers))) + b.error("unsupported DWARF version " + strconv.Itoa(vers)) return b.err } var ao uint32 @@ -55,7 +49,7 @@ func (d *Data) parseTypes(name string, types []byte) error { } ao = uint32(ao64) } - atable, err := d.parseAbbrev(ao) + atable, err := d.parseAbbrev(ao, vers) if err != nil { return err } @@ -79,7 +73,7 @@ func (d *Data) parseTypes(name string, types []byte) error { unit: unit{ base: base, off: boff, - data: b.bytes(int(Offset(n) - (b.off - hdroff))), + data: b.bytes(int(n - (b.off - hdroff))), atable: atable, asize: int(asize), vers: int(vers), @@ -135,6 +129,11 @@ func (tur *typeUnitReader) Seek(off Offset) { tur.b = makeBuf(tur.d, tur.tu, tur.tu.name, off, tur.tu.data[doff:]) } +// AddressSize returns the size in bytes of addresses in the current type unit. +func (tur *typeUnitReader) AddressSize() int { + return tur.tu.unit.asize +} + // Next reads the next Entry from the type unit. func (tur *typeUnitReader) Next() (*Entry, error) { if tur.err != nil { diff --git a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/unit.go b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/unit.go index be6093519d5..ceb6cdbff32 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/dwarf/unit.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/unit.go @@ -4,23 +4,22 @@ package dwarf -import "strconv" +import ( + "sort" + "strconv" +) // DWARF debug info is split into a sequence of compilation units. // Each unit has its own abbreviation table and address size. type unit struct { - base Offset // byte offset of header within the aggregate info - off Offset // byte offset of data within the aggregate info - lineoff Offset // byte offset of data within the line info - data []byte - atable abbrevTable - asize int - vers int - is64 bool // True for 64-bit DWARF format - dir string - pc []addrRange // PC ranges in this compilation unit - lines []mapLineInfo // PC -> line mapping + base Offset // byte offset of header within the aggregate info + off Offset // byte offset of data within the aggregate info + data []byte + atable abbrevTable + asize int + vers int + is64 bool // True for 64-bit DWARF format } // Implement the dataFormat interface. @@ -37,25 +36,15 @@ func (u *unit) addrsize() int { return u.asize } -// A range is an address range. -type addrRange struct { - low uint64 - high uint64 -} - func (d *Data) parseUnits() ([]unit, error) { // Count units. nunit := 0 b := makeBuf(d, unknownFormat{}, "info", 0, d.info) for len(b.data) > 0 { - len := b.uint32() - if len == 0xffffffff { - len64 := b.uint64() - if len64 != uint64(uint32(len64)) { - b.error("unit length overflow") - break - } - len = uint32(len64) + len, _ := b.unitLength() + if len != Offset(uint32(len)) { + b.error("unit length overflow") + break } b.skip(int(len)) nunit++ @@ -70,18 +59,15 @@ func (d *Data) parseUnits() ([]unit, error) { for i := range units { u := &units[i] u.base = b.off - n := b.uint32() - if n == 0xffffffff { - u.is64 = true - n = uint32(b.uint64()) - } + var n Offset + n, u.is64 = b.unitLength() vers := b.uint16() if vers != 2 && vers != 3 && vers != 4 { b.error("unsupported DWARF version " + strconv.Itoa(int(vers))) break } u.vers = int(vers) - atable, err := d.parseAbbrev(b.uint32()) + atable, err := d.parseAbbrev(b.uint32(), u.vers) if err != nil { if b.err == nil { b.err = err @@ -98,3 +84,20 @@ func (d *Data) parseUnits() ([]unit, error) { } return units, nil } + +// offsetToUnit returns the index of the unit containing offset off. +// It returns -1 if no unit contains this offset. +func (d *Data) offsetToUnit(off Offset) int { + // Find the unit after off + next := sort.Search(len(d.unit), func(i int) bool { + return d.unit[i].off > off + }) + if next == 0 { + return -1 + } + u := &d.unit[next-1] + if u.off <= off && off < u.off+Offset(len(u.data)) { + return next - 1 + } + return -1 +} diff --git a/llgo/third_party/gofrontend/libgo/go/debug/elf/elf.go b/llgo/third_party/gofrontend/libgo/go/debug/elf/elf.go index a9466bbdcd6..c97ccaa4c1c 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/elf/elf.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/elf/elf.go @@ -312,7 +312,7 @@ const ( SHN_HIOS SectionIndex = 0xff3f /* Last operating system-specific. */ SHN_ABS SectionIndex = 0xfff1 /* Absolute values. */ SHN_COMMON SectionIndex = 0xfff2 /* Common data. */ - SHN_XINDEX SectionIndex = 0xffff /* Escape -- index stored elsewhere. */ + SHN_XINDEX SectionIndex = 0xffff /* Escape; index stored elsewhere. */ SHN_HIRESERVE SectionIndex = 0xffff /* Last of reserved range. */ ) @@ -1414,7 +1414,7 @@ var rppcStrings = []intName{ func (i R_PPC) String() string { return stringName(uint32(i), rppcStrings, false) } func (i R_PPC) GoString() string { return stringName(uint32(i), rppcStrings, true) } -// Relocation types for PowerPC 64. +// Relocation types for 64-bit PowerPC or Power Architecture processors. type R_PPC64 int const ( diff --git a/llgo/third_party/gofrontend/libgo/go/debug/elf/file.go b/llgo/third_party/gofrontend/libgo/go/debug/elf/file.go index 0135f7f8dbf..370c5e6437c 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/elf/file.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/elf/file.go @@ -13,6 +13,7 @@ import ( "fmt" "io" "os" + "strings" ) // TODO: error reporting detail @@ -523,26 +524,24 @@ func (f *File) Section(name string) *Section { // applyRelocations applies relocations to dst. rels is a relocations section // in RELA format. func (f *File) applyRelocations(dst []byte, rels []byte) error { - if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 { + switch { + case f.Class == ELFCLASS64 && f.Machine == EM_X86_64: return f.applyRelocationsAMD64(dst, rels) - } - if f.Class == ELFCLASS32 && f.Machine == EM_386 { + case f.Class == ELFCLASS32 && f.Machine == EM_386: return f.applyRelocations386(dst, rels) - } - if f.Class == ELFCLASS64 && f.Machine == EM_AARCH64 { + case f.Class == ELFCLASS32 && f.Machine == EM_ARM: + return f.applyRelocationsARM(dst, rels) + case f.Class == ELFCLASS64 && f.Machine == EM_AARCH64: return f.applyRelocationsARM64(dst, rels) - } - if f.Class == ELFCLASS32 && f.Machine == EM_PPC { + case f.Class == ELFCLASS32 && f.Machine == EM_PPC: return f.applyRelocationsPPC(dst, rels) - } - if f.Class == ELFCLASS64 && f.Machine == EM_PPC64 { + case f.Class == ELFCLASS64 && f.Machine == EM_PPC64: return f.applyRelocationsPPC64(dst, rels) - } - if f.Class == ELFCLASS64 && f.Machine == EM_S390 { + case f.Class == ELFCLASS64 && f.Machine == EM_S390: return f.applyRelocationsS390x(dst, rels) + default: + return errors.New("applyRelocations: not implemented") } - - return errors.New("not implemented") } func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error { @@ -631,6 +630,44 @@ func (f *File) applyRelocations386(dst []byte, rels []byte) error { return nil } +func (f *File) applyRelocationsARM(dst []byte, rels []byte) error { + // 8 is the size of Rel32. + if len(rels)%8 != 0 { + return errors.New("length of relocation section is not a multiple of 8") + } + + symbols, _, err := f.getSymbols(SHT_SYMTAB) + if err != nil { + return err + } + + b := bytes.NewReader(rels) + var rel Rel32 + + for b.Len() > 0 { + binary.Read(b, f.ByteOrder, &rel) + symNo := rel.Info >> 8 + t := R_ARM(rel.Info & 0xff) + + if symNo == 0 || symNo > uint32(len(symbols)) { + continue + } + sym := &symbols[symNo-1] + + switch t { + case R_ARM_ABS32: + if rel.Off+4 >= uint32(len(dst)) { + continue + } + val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4]) + val += uint32(sym.Value) + f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val) + } + } + + return nil +} + func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error { // 24 is the size of Rela64. if len(rels)%24 != 0 { @@ -807,53 +844,52 @@ func (f *File) applyRelocationsS390x(dst []byte, rels []byte) error { } func (f *File) DWARF() (*dwarf.Data, error) { - // There are many other DWARF sections, but these - // are the required ones, and the debug/dwarf package - // does not use the others, so don't bother loading them. - var names = [...]string{"abbrev", "info", "line", "ranges", "str"} - var dat [len(names)][]byte - for i, name := range names { - name = ".debug_" + name - s := f.Section(name) - if s == nil { - continue - } + // sectionData gets the data for s, checks its size, and + // applies any applicable relations. + sectionData := func(i int, s *Section) ([]byte, error) { b, err := s.Data() if err != nil && uint64(len(b)) < s.Size { return nil, err } - dat[i] = b - } - // If there's a relocation table for .debug_info, we have to process it - // now otherwise the data in .debug_info is invalid for x86-64 objects. - rela := f.Section(".rela.debug_info") - if rela != nil && rela.Type == SHT_RELA && (f.Machine == EM_X86_64 || f.Machine == EM_AARCH64 || f.Machine == EM_PPC || f.Machine == EM_PPC64 || f.Machine == EM_S390) { - data, err := rela.Data() - if err != nil { - return nil, err - } - err = f.applyRelocations(dat[1], data) - if err != nil { - return nil, err + for _, r := range f.Sections { + if r.Type != SHT_RELA && r.Type != SHT_REL { + continue + } + if int(r.Info) != i { + continue + } + rd, err := r.Data() + if err != nil { + return nil, err + } + err = f.applyRelocations(b, rd) + if err != nil { + return nil, err + } } + return b, nil } - // When using clang we need to process relocations even for 386. - rel := f.Section(".rel.debug_info") - if rel != nil && rel.Type == SHT_REL && f.Machine == EM_386 { - data, err := rel.Data() - if err != nil { - return nil, err + // There are many other DWARF sections, but these + // are the ones the debug/dwarf package uses. + // Don't bother loading others. + var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil} + for i, s := range f.Sections { + if !strings.HasPrefix(s.Name, ".debug_") { + continue + } + if _, ok := dat[s.Name[7:]]; !ok { + continue } - err = f.applyRelocations(dat[1], data) + b, err := sectionData(i, s) if err != nil { return nil, err } + dat[s.Name[7:]] = b } - abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4] - d, err := dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) + d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"]) if err != nil { return nil, err } @@ -861,28 +897,11 @@ func (f *File) DWARF() (*dwarf.Data, error) { // Look for DWARF4 .debug_types sections. for i, s := range f.Sections { if s.Name == ".debug_types" { - b, err := s.Data() - if err != nil && uint64(len(b)) < s.Size { + b, err := sectionData(i, s) + if err != nil { return nil, err } - for _, r := range f.Sections { - if r.Type != SHT_RELA && r.Type != SHT_REL { - continue - } - if int(r.Info) != i { - continue - } - rd, err := r.Data() - if err != nil { - return nil, err - } - err = f.applyRelocations(b, rd) - if err != nil { - return nil, err - } - } - err = d.AddTypes(fmt.Sprintf("types-%d", i), b) if err != nil { return nil, err diff --git a/llgo/third_party/gofrontend/libgo/go/debug/elf/file_test.go b/llgo/third_party/gofrontend/libgo/go/debug/elf/file_test.go index 56e2604d9d3..1ad43146ac8 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/elf/file_test.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/elf/file_test.go @@ -245,50 +245,62 @@ var relocationTests = []relocationTest{ { "testdata/go-relocation-test-gcc441-x86-64.obj", []relocationTestEntry{ - {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}}, }, }, { "testdata/go-relocation-test-gcc441-x86.obj", []relocationTestEntry{ - {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "t.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}}, }, }, { "testdata/go-relocation-test-gcc424-x86-64.obj", []relocationTestEntry{ - {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}}, }, }, { - "testdata/go-relocation-test-gcc5-ppc.obj", + "testdata/go-relocation-test-gcc482-aarch64.obj", []relocationTestEntry{ - {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C11 5.0.0 20150116 (experimental) -Asystem=linux -Asystem=unix -Asystem=posix -g"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(12)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc5-ppc.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: int64(0x44)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -g -fstack-protector", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrHighpc, Val: int64(0x24), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}}, }, }, { - "testdata/go-relocation-test-gcc482-ppc64le.obj", + "testdata/go-relocation-test-gcc492-arm.obj", []relocationTestEntry{ - {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -Asystem=linux -Asystem=unix -Asystem=posix -msecure-plt -mtune=power8 -mcpu=power7 -gdwarf-2 -fstack-protector"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482-ppc64le.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x24)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.9.2 20141224 (prerelease) -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -mtls-dialect=gnu -g", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc492.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrCompDir, Val: "/root/go/src/debug/elf/testdata", Class: dwarf.ClassString}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrHighpc, Val: int64(0x28), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}}, }, }, { - "testdata/go-relocation-test-gcc482-aarch64.obj", + "testdata/go-relocation-test-clang-arm.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "Debian clang version 3.5.0-10 (tags/RELEASE_350/final) (based on LLVM 3.5.0)", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant}, dwarf.Field{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0x0), Class: dwarf.ClassLinePtr}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: int64(48), Class: dwarf.ClassConstant}}}}, + }, + }, + { + "testdata/go-relocation-test-gcc5-ppc.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C11 5.0.0 20150116 (experimental) -Asystem=linux -Asystem=unix -Asystem=posix -g", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc5-ppc.c", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: int64(0x44), Class: dwarf.ClassConstant}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}}, + }, + }, + { + "testdata/go-relocation-test-gcc482-ppc64le.obj", []relocationTestEntry{ - {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -g -fstack-protector"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: int64(0x24)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -Asystem=linux -Asystem=unix -Asystem=posix -msecure-plt -mtune=power8 -mcpu=power7 -gdwarf-2 -fstack-protector", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482-ppc64le.c", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x24), Class: dwarf.ClassAddress}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}}, }, }, { "testdata/go-relocation-test-clang-x86.obj", []relocationTestEntry{ - {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "clang version google3-trunk (trunk r209387)"}, {Attr: dwarf.AttrLanguage, Val: int64(12)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-clang.c"}, {Attr: dwarf.AttrStmtList, Val: int64(0)}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}}}}, + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "clang version google3-trunk (trunk r209387)", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "go-relocation-test-clang.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}, {Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}}}}, }, }, { "testdata/gcc-amd64-openbsd-debug-with-rela.obj", []relocationTestEntry{ - {203, &dwarf.Entry{Offset: 0xc62, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_interval"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(236)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x0}}}}}, - {204, &dwarf.Entry{Offset: 0xc70, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_value"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(237)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x10}}}}}, + {203, &dwarf.Entry{Offset: 0xc62, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_interval", Class: dwarf.ClassString}, {Attr: dwarf.AttrDeclFile, Val: int64(7), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrDeclLine, Val: int64(236), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f), Class: dwarf.ClassReference}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x0}, Class: dwarf.ClassExprLoc}}}}, + {204, &dwarf.Entry{Offset: 0xc70, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_value", Class: dwarf.ClassString}, {Attr: dwarf.AttrDeclFile, Val: int64(7), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrDeclLine, Val: int64(237), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f), Class: dwarf.ClassReference}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x10}, Class: dwarf.ClassExprLoc}}}}, }, }, } diff --git a/llgo/third_party/gofrontend/libgo/go/debug/elf/testdata/go-relocation-test-clang-arm.obj b/llgo/third_party/gofrontend/libgo/go/debug/elf/testdata/go-relocation-test-clang-arm.obj Binary files differnew file mode 100644 index 00000000000..1cc7e4b1114 --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/debug/elf/testdata/go-relocation-test-clang-arm.obj diff --git a/llgo/third_party/gofrontend/libgo/go/debug/elf/testdata/go-relocation-test-gcc492-arm.obj b/llgo/third_party/gofrontend/libgo/go/debug/elf/testdata/go-relocation-test-gcc492-arm.obj Binary files differnew file mode 100644 index 00000000000..ed45be2c55f --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/debug/elf/testdata/go-relocation-test-gcc492-arm.obj diff --git a/llgo/third_party/gofrontend/libgo/go/debug/gosym/pclntab_test.go b/llgo/third_party/gofrontend/libgo/go/debug/gosym/pclntab_test.go index 35502e8c395..53f3e952d62 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/gosym/pclntab_test.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/gosym/pclntab_test.go @@ -6,7 +6,6 @@ package gosym import ( "debug/elf" - "fmt" "io/ioutil" "os" "os/exec" @@ -30,10 +29,6 @@ func dotest(self bool) bool { if self && runtime.GOOS != "linux" { return false } - // Command below expects "sh", so Unix. - if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { - return false - } if pclinetestBinary != "" { return true } @@ -49,9 +44,14 @@ func dotest(self bool) bool { // the resulting binary looks like it was built from pclinetest.s, // but we have renamed it to keep it away from the go tool. pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest") - command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6", - pclinetestBinary, pclinetestBinary, pclinetestBinary) - cmd := exec.Command("sh", "-c", command) + cmd := exec.Command("go", "tool", "asm", "-o", pclinetestBinary+".o", "pclinetest.asm") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + panic(err) + } + cmd = exec.Command("go", "tool", "link", "-H", "linux", "-E", "main", + "-o", pclinetestBinary, pclinetestBinary+".o") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { @@ -84,7 +84,11 @@ func crack(file string, t *testing.T) (*elf.File, *Table) { } func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) { - symdat, err := f.Section(".gosymtab").Data() + s := f.Section(".gosymtab") + if s == nil { + t.Skip("no .gosymtab section") + } + symdat, err := s.Data() if err != nil { f.Close() t.Fatalf("reading %s gosymtab: %v", file, err) diff --git a/llgo/third_party/gofrontend/libgo/go/debug/gosym/symtab.go b/llgo/third_party/gofrontend/libgo/go/debug/gosym/symtab.go index ee18499d111..46f07833442 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/gosym/symtab.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/gosym/symtab.go @@ -30,7 +30,7 @@ type Sym struct { Type byte Name string GoType uint64 - // If this symbol if a function symbol, the corresponding Func + // If this symbol is a function symbol, the corresponding Func Func *Func } diff --git a/llgo/third_party/gofrontend/libgo/go/debug/macho/file.go b/llgo/third_party/gofrontend/libgo/go/debug/macho/file.go index da13c510064..a7599aa5b2b 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/macho/file.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/macho/file.go @@ -472,8 +472,8 @@ func (f *File) Section(name string) *Section { // DWARF returns the DWARF debug information for the Mach-O file. func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these - // are the required ones, and the debug/dwarf package - // does not use the others, so don't bother loading them. + // are the ones the debug/dwarf package uses. + // Don't bother loading others. var names = [...]string{"abbrev", "info", "line", "str"} var dat [len(names)][]byte for i, name := range names { diff --git a/llgo/third_party/gofrontend/libgo/go/debug/pe/file.go b/llgo/third_party/gofrontend/libgo/go/debug/pe/file.go index 759e5674fd6..3df4ae73681 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/pe/file.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/pe/file.go @@ -296,9 +296,9 @@ func (f *File) Section(name string) *Section { func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these - // are the required ones, and the debug/dwarf package - // does not use the others, so don't bother loading them. - var names = [...]string{"abbrev", "info", "str"} + // are the ones the debug/dwarf package uses. + // Don't bother loading others. + var names = [...]string{"abbrev", "info", "line", "str"} var dat [len(names)][]byte for i, name := range names { name = ".debug_" + name @@ -310,11 +310,14 @@ func (f *File) DWARF() (*dwarf.Data, error) { if err != nil && uint32(len(b)) < s.Size { return nil, err } + if 0 < s.VirtualSize && s.VirtualSize < s.Size { + b = b[:s.VirtualSize] + } dat[i] = b } - abbrev, info, str := dat[0], dat[1], dat[2] - return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) + abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3] + return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str) } // ImportedSymbols returns the names of all symbols diff --git a/llgo/third_party/gofrontend/libgo/go/debug/pe/file_test.go b/llgo/third_party/gofrontend/libgo/go/debug/pe/file_test.go index 0d73969bca9..316a569ede9 100644 --- a/llgo/third_party/gofrontend/libgo/go/debug/pe/file_test.go +++ b/llgo/third_party/gofrontend/libgo/go/debug/pe/file_test.go @@ -5,24 +5,30 @@ package pe import ( + "debug/dwarf" + "io/ioutil" + "os" + "os/exec" + "path/filepath" "reflect" + "runtime" "testing" ) type fileTest struct { - file string - hdr FileHeader - opthdr interface{} - sections []*SectionHeader - symbols []*Symbol + file string + hdr FileHeader + opthdr interface{} + sections []*SectionHeader + symbols []*Symbol + hasNoDwarfInfo bool } var fileTests = []fileTest{ { - "testdata/gcc-386-mingw-obj", - FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104}, - nil, - []*SectionHeader{ + file: "testdata/gcc-386-mingw-obj", + hdr: FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104}, + sections: []*SectionHeader{ {".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020}, {".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264}, {".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328}, @@ -36,7 +42,7 @@ var fileTests = []fileTest{ {".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832}, {".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832}, }, - []*Symbol{ + symbols: []*Symbol{ {".file", 0x0, -2, 0x0, 0x67}, {"_main", 0x0, 1, 0x20, 0x2}, {".text", 0x0, 1, 0x0, 0x3}, @@ -56,9 +62,9 @@ var fileTests = []fileTest{ }, }, { - "testdata/gcc-386-mingw-exec", - FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107}, - &OptionalHeader32{ + file: "testdata/gcc-386-mingw-exec", + hdr: FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107}, + opthdr: &OptionalHeader32{ 0x10b, 0x2, 0x38, 0xe00, 0x1a00, 0x200, 0x1160, 0x1000, 0x2000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x10000, 0x400, 0x14abb, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10, [16]DataDirectory{ {0x0, 0x0}, @@ -79,7 +85,7 @@ var fileTests = []fileTest{ {0x0, 0x0}, }, }, - []*SectionHeader{ + sections: []*SectionHeader{ {".text", 0xcd8, 0x1000, 0xe00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500060}, {".data", 0x10, 0x2000, 0x200, 0x1200, 0x0, 0x0, 0x0, 0x0, 0xc0300040}, {".rdata", 0x120, 0x3000, 0x200, 0x1400, 0x0, 0x0, 0x0, 0x0, 0x40300040}, @@ -96,13 +102,11 @@ var fileTests = []fileTest{ {".debug_frame", 0x34, 0xe000, 0x200, 0x3800, 0x0, 0x0, 0x0, 0x0, 0x42300000}, {".debug_loc", 0x38, 0xf000, 0x200, 0x3a00, 0x0, 0x0, 0x0, 0x0, 0x42100000}, }, - []*Symbol{}, }, { - "testdata/gcc-amd64-mingw-obj", - FileHeader{0x8664, 0x6, 0x0, 0x198, 0x12, 0x0, 0x4}, - nil, - []*SectionHeader{ + file: "testdata/gcc-amd64-mingw-obj", + hdr: FileHeader{0x8664, 0x6, 0x0, 0x198, 0x12, 0x0, 0x4}, + sections: []*SectionHeader{ {".text", 0x0, 0x0, 0x30, 0x104, 0x15c, 0x0, 0x3, 0x0, 0x60500020}, {".data", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500040}, {".bss", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500080}, @@ -110,7 +114,7 @@ var fileTests = []fileTest{ {".xdata", 0x0, 0x0, 0xc, 0x144, 0x0, 0x0, 0x0, 0x0, 0x40300040}, {".pdata", 0x0, 0x0, 0xc, 0x150, 0x17a, 0x0, 0x3, 0x0, 0x40300040}, }, - []*Symbol{ + symbols: []*Symbol{ {".file", 0x0, -2, 0x0, 0x67}, {"main", 0x0, 1, 0x20, 0x2}, {".text", 0x0, 1, 0x0, 0x3}, @@ -122,11 +126,12 @@ var fileTests = []fileTest{ {"__main", 0x0, 0, 0x20, 0x2}, {"puts", 0x0, 0, 0x20, 0x2}, }, + hasNoDwarfInfo: true, }, { - "testdata/gcc-amd64-mingw-exec", - FileHeader{0x8664, 0x11, 0x53e4364f, 0x39600, 0x6fc, 0xf0, 0x27}, - &OptionalHeader64{ + file: "testdata/gcc-amd64-mingw-exec", + hdr: FileHeader{0x8664, 0x11, 0x53e4364f, 0x39600, 0x6fc, 0xf0, 0x27}, + opthdr: &OptionalHeader64{ 0x20b, 0x2, 0x16, 0x6a00, 0x2400, 0x1600, 0x14e0, 0x1000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x0, 0x0, 0x5, 0x2, 0x0, 0x45000, 0x600, 0x46f19, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10, [16]DataDirectory{ {0x0, 0x0}, @@ -146,7 +151,7 @@ var fileTests = []fileTest{ {0x0, 0x0}, {0x0, 0x0}, }}, - []*SectionHeader{ + sections: []*SectionHeader{ {".text", 0x6860, 0x1000, 0x6a00, 0x600, 0x0, 0x0, 0x0, 0x0, 0x60500020}, {".data", 0xe0, 0x8000, 0x200, 0x7000, 0x0, 0x0, 0x0, 0x0, 0xc0500040}, {".rdata", 0x6b0, 0x9000, 0x800, 0x7200, 0x0, 0x0, 0x0, 0x0, 0x40600040}, @@ -165,7 +170,6 @@ var fileTests = []fileTest{ {".debug_loc", 0x13240, 0x30000, 0x13400, 0x25600, 0x0, 0x0, 0x0, 0x0, 0x42100040}, {".debug_ranges", 0xa70, 0x44000, 0xc00, 0x38a00, 0x0, 0x0, 0x0, 0x0, 0x42100040}, }, - []*Symbol{}, }, } @@ -231,6 +235,12 @@ func TestOpen(t *testing.T) { t.Errorf("open %s, symbol %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) } } + if !tt.hasNoDwarfInfo { + _, err = f.DWARF() + if err != nil { + t.Errorf("fetching %s dwarf details failed: %v", tt.file, err) + } + } } } @@ -241,3 +251,59 @@ func TestOpenFailure(t *testing.T) { t.Errorf("open %s: succeeded unexpectedly", filename) } } + +func TestDWARF(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("skipping windows only test") + } + + tmpdir, err := ioutil.TempDir("", "TestDWARF") + if err != nil { + t.Fatal("TempDir failed: ", err) + } + defer os.RemoveAll(tmpdir) + + prog := ` +package main +func main() { +} +` + src := filepath.Join(tmpdir, "a.go") + exe := filepath.Join(tmpdir, "a.exe") + err = ioutil.WriteFile(src, []byte(prog), 0644) + output, err := exec.Command("go", "build", "-o", exe, src).CombinedOutput() + if err != nil { + t.Fatalf("building test executable failed: %s %s", err, output) + } + + f, err := Open(exe) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatal(err) + } + + // look for main.main + r := d.Reader() + for { + e, err := r.Next() + if err != nil { + t.Fatal("r.Next:", err) + } + if e == nil { + break + } + if e.Tag == dwarf.TagSubprogram { + for _, f := range e.Field { + if f.Attr == dwarf.AttrName && e.Val(dwarf.AttrName) == "main.main" { + return + } + } + } + } + t.Fatal("main.main not found") +} |

