summaryrefslogtreecommitdiffstats
path: root/llgo/third_party/gofrontend/libgo/go/debug
diff options
context:
space:
mode:
Diffstat (limited to 'llgo/third_party/gofrontend/libgo/go/debug')
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/buf.go11
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/class_string.go17
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/const.go43
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/entry.go275
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/line.go935
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/line_test.go232
-rwxr-xr-xllgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line-clang.elfbin0 -> 10271 bytes
-rwxr-xr-xllgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line-gcc.elfbin0 -> 10113 bytes
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line1.c9
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line1.h7
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line2.c6
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/type.go10
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/typeunit.go27
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/dwarf/unit.go67
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/elf/elf.go4
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/elf/file.go149
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/elf/file_test.go36
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/elf/testdata/go-relocation-test-clang-arm.objbin0 -> 3092 bytes
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/elf/testdata/go-relocation-test-gcc492-arm.objbin0 -> 2648 bytes
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/gosym/pclntab_test.go22
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/gosym/symtab.go2
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/macho/file.go4
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/pe/file.go13
-rw-r--r--llgo/third_party/gofrontend/libgo/go/debug/pe/file_test.go116
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
new file mode 100755
index 00000000000..b63cc781c41
--- /dev/null
+++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line-clang.elf
Binary files differ
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
new file mode 100755
index 00000000000..50500a8eecd
--- /dev/null
+++ b/llgo/third_party/gofrontend/libgo/go/debug/dwarf/testdata/line-gcc.elf
Binary files differ
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
new file mode 100644
index 00000000000..1cc7e4b1114
--- /dev/null
+++ b/llgo/third_party/gofrontend/libgo/go/debug/elf/testdata/go-relocation-test-clang-arm.obj
Binary files differ
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
new file mode 100644
index 00000000000..ed45be2c55f
--- /dev/null
+++ b/llgo/third_party/gofrontend/libgo/go/debug/elf/testdata/go-relocation-test-gcc492-arm.obj
Binary files differ
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")
+}
OpenPOWER on IntegriCloud