summaryrefslogtreecommitdiff
path: root/libgo/go/debug/dwarf
diff options
context:
space:
mode:
authorCary Coutant <ccoutant@google.com>2012-03-09 11:50:43 -0800
committerCary Coutant <ccoutant@google.com>2012-03-09 11:50:43 -0800
commit4ed2fc3a6bbaaf1f2ea4d1c077dc7fa0867cd005 (patch)
tree7774dcb772ccc005c2fd8f8831dc45b90a8f6060 /libgo/go/debug/dwarf
parent2400d467a903f32c96df9f9fcb0de7be8a655afa (diff)
parentf9c681635d1850543f547fdbb6efd5a010aca015 (diff)
downloadgcc-4ed2fc3a6bbaaf1f2ea4d1c077dc7fa0867cd005.tar.gz
Merge branch 'master' into google-debugfission
Diffstat (limited to 'libgo/go/debug/dwarf')
-rw-r--r--libgo/go/debug/dwarf/buf.go38
-rw-r--r--libgo/go/debug/dwarf/const.go32
-rw-r--r--libgo/go/debug/dwarf/entry.go47
-rw-r--r--libgo/go/debug/dwarf/line.go435
-rw-r--r--libgo/go/debug/dwarf/line_test.go53
-rw-r--r--libgo/go/debug/dwarf/open.go6
-rw-r--r--libgo/go/debug/dwarf/testdata/typedef.c8
-rwxr-xr-xlibgo/go/debug/dwarf/testdata/typedef.elfbin10837 -> 12448 bytes
-rw-r--r--libgo/go/debug/dwarf/testdata/typedef.machobin5256 -> 5024 bytes
-rw-r--r--libgo/go/debug/dwarf/type.go38
-rw-r--r--libgo/go/debug/dwarf/type_test.go19
-rw-r--r--libgo/go/debug/dwarf/unit.go35
12 files changed, 672 insertions, 39 deletions
diff --git a/libgo/go/debug/dwarf/buf.go b/libgo/go/debug/dwarf/buf.go
index 6dc28d2568c..08e37be4b38 100644
--- a/libgo/go/debug/dwarf/buf.go
+++ b/libgo/go/debug/dwarf/buf.go
@@ -13,17 +13,17 @@ import (
// Data buffer being decoded.
type buf struct {
- dwarf *Data
- order binary.ByteOrder
- name string
- off Offset
- data []byte
- addrsize int
- err error
+ dwarf *Data
+ u *unit
+ order binary.ByteOrder
+ name string
+ off Offset
+ data []byte
+ err error
}
-func makeBuf(d *Data, name string, off Offset, data []byte, addrsize int) buf {
- return buf{d, d.order, name, off, data, addrsize, nil}
+func makeBuf(d *Data, u *unit, name string, off Offset, data []byte) buf {
+ return buf{d, u, d.order, name, off, data, nil}
}
func (b *buf) uint8() uint8 {
@@ -121,15 +121,17 @@ func (b *buf) int() int64 {
// Address-sized uint.
func (b *buf) addr() uint64 {
- switch b.addrsize {
- case 1:
- return uint64(b.uint8())
- case 2:
- return uint64(b.uint16())
- case 4:
- return uint64(b.uint32())
- case 8:
- return uint64(b.uint64())
+ if b.u != nil {
+ switch b.u.addrsize {
+ case 1:
+ return uint64(b.uint8())
+ case 2:
+ return uint64(b.uint16())
+ case 4:
+ return uint64(b.uint32())
+ case 8:
+ return uint64(b.uint64())
+ }
}
b.error("unknown address size")
return 0
diff --git a/libgo/go/debug/dwarf/const.go b/libgo/go/debug/dwarf/const.go
index 918b153d078..ebe9a71a0c5 100644
--- a/libgo/go/debug/dwarf/const.go
+++ b/libgo/go/debug/dwarf/const.go
@@ -207,6 +207,11 @@ const (
formRef8 format = 0x14
formRefUdata format = 0x15
formIndirect format = 0x16
+ // following are defined in DWARF 4
+ formSecOffset format = 0x17
+ formExprLoc format = 0x18
+ formFlagPresent format = 0x19
+ formRefSig8 format = 0x20
)
// A Tag is the classification (the type) of an Entry.
@@ -431,3 +436,30 @@ const (
encUnsignedChar = 0x08
encImaginaryFloat = 0x09
)
+
+// Line number opcodes.
+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
+)
+
+// Line number extended opcodes.
+const (
+ LineExtEndSequence = 1
+ LineExtSetAddress = 2
+ LineExtDefineFile = 3
+ // next 1 is DWARF 4
+ LineExtSetDiscriminator = 4
+)
diff --git a/libgo/go/debug/dwarf/entry.go b/libgo/go/debug/dwarf/entry.go
index 2885d8fa26c..4761d74cd79 100644
--- a/libgo/go/debug/dwarf/entry.go
+++ b/libgo/go/debug/dwarf/entry.go
@@ -40,7 +40,7 @@ func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) {
} else {
data = data[off:]
}
- b := makeBuf(d, "abbrev", 0, data, 0)
+ b := makeBuf(d, nil, "abbrev", 0, data)
// Error handling is simplified by the buf getters
// returning an endless stream of 0s after an error.
@@ -182,13 +182,37 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
case formUdata:
val = int64(b.uint())
+ // exprloc
+ case formExprLoc:
+ val = b.bytes(int(b.uint()))
+
// flag
case formFlag:
val = b.uint8() == 1
+ case formFlagPresent:
+ val = true
+
+ // lineptr, loclistptr, macptr, rangelistptr
+ case formSecOffset:
+ if b.u == nil {
+ b.error("unknown size for DW_FORM_sec_offset")
+ } else if b.u.dwarf64 {
+ val = Offset(b.uint64())
+ } else {
+ val = Offset(b.uint32())
+ }
// reference to other entry
case formRefAddr:
- val = Offset(b.addr())
+ if b.u == nil {
+ b.error("unknown version for DW_FORM_ref_addr")
+ } else if b.u.version == 2 {
+ val = Offset(b.addr())
+ } else if b.u.dwarf64 {
+ val = Offset(b.uint64())
+ } else {
+ val = Offset(b.uint32())
+ }
case formRef1:
val = Offset(b.uint8()) + ubase
case formRef2:
@@ -199,6 +223,8 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
val = Offset(b.uint64()) + ubase
case formRefUdata:
val = Offset(b.uint()) + ubase
+ case formRefSig8:
+ val = b.uint64()
// string
case formString:
@@ -208,7 +234,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
if b.err != nil {
return nil
}
- b1 := makeBuf(b.dwarf, "str", 0, b.dwarf.str, 0)
+ b1 := makeBuf(b.dwarf, b.u, "str", 0, b.dwarf.str)
b1.skip(int(off))
val = b1.string()
if b1.err != nil {
@@ -246,6 +272,15 @@ 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
+}
+
// Seek positions the Reader at offset off in the encoded entry stream.
// Offset 0 can be used to denote the first entry.
func (r *Reader) Seek(off Offset) {
@@ -258,7 +293,7 @@ func (r *Reader) Seek(off Offset) {
}
u := &d.unit[0]
r.unit = 0
- r.b = makeBuf(r.d, "info", u.off, u.data, u.addrsize)
+ r.b = makeBuf(r.d, u, "info", u.off, u.data)
return
}
@@ -269,7 +304,7 @@ func (r *Reader) Seek(off Offset) {
u = &d.unit[i]
if u.off <= off && off < u.off+Offset(len(u.data)) {
r.unit = i
- r.b = makeBuf(r.d, "info", off, u.data[off-u.off:], u.addrsize)
+ r.b = makeBuf(r.d, u, "info", off, u.data[off-u.off:])
return
}
}
@@ -281,7 +316,7 @@ func (r *Reader) maybeNextUnit() {
for len(r.b.data) == 0 && r.unit+1 < len(r.d.unit) {
r.unit++
u := &r.d.unit[r.unit]
- r.b = makeBuf(r.d, "info", u.off, u.data, u.addrsize)
+ r.b = makeBuf(r.d, u, "info", u.off, u.data)
}
}
diff --git a/libgo/go/debug/dwarf/line.go b/libgo/go/debug/dwarf/line.go
new file mode 100644
index 00000000000..f9c77cc87e4
--- /dev/null
+++ b/libgo/go/debug/dwarf/line.go
@@ -0,0 +1,435 @@
+// Copyright 2012 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"
+)
+
+// 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
+}
+
+// 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)
+ }
+ }
+ }
+ return nil, 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
+ }
+ if e == nil {
+ break
+ }
+ if r.unit != i {
+ break
+ }
+ switch e.Tag {
+ case TagCompileUnit, TagSubprogram, TagEntryPoint, TagInlinedSubroutine:
+ low, lowok := e.Val(AttrLowpc).(uint64)
+ high, highok := e.Val(AttrHighpc).(uint64)
+ if lowok && highok {
+ u.pc = append(u.pc, addrRange{low, high})
+ } else if f, ok := e.Val(AttrRanges).(Offset); ok {
+ // TODO: Handle AttrRanges and .debug_ranges.
+ _ = f
+ }
+ 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 !setLineOff {
+ u.lineoff = Offset(0)
+ u.lineoff--
+ }
+ return 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
+ }
+ }
+
+ for _, ln := range u.lines {
+ if pc < ln.addrs[0].pc || pc > ln.addrs[len(ln.addrs)-1].pc {
+ continue
+ }
+ 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)
+ }
+ return ret, nil
+ }
+
+ return nil, nil
+}
+
+// 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
+ }
+ 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
+}
+
+// A list of lines. This will be sorted by PC.
+type lineAddrs []oneLineInfo
+
+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] }
+
+// A oneLineInfo is a single PC and line number.
+type oneLineInfo struct {
+ pc uint64
+ line int
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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
+ }
+
+ var hlen Offset
+ if dwarf64 {
+ hlen = Offset(b.uint64())
+ } else {
+ hlen = Offset(b.uint32())
+ }
+ end := b.off + hlen
+
+ hdr.minInsnLen = b.uint8()
+ if hdr.version < 4 {
+ hdr.maxOpsPerInsn = 1
+ } else {
+ hdr.maxOpsPerInsn = b.uint8()
+ }
+
+ 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))
+
+ for d := b.string(); len(d) > 0; d = b.string() {
+ hdr.dirs = append(hdr.dirs, d)
+ }
+
+ 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)
+ }
+ }
+ b.uint() // file's last mtime
+ b.uint() // file length
+ hdr.files = append(hdr.files, f)
+ }
+
+ if end > b.off {
+ b.bytes(int(end - b.off))
+ }
+
+ return
+}
+
+// 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: "",
+ OpIndex: 0,
+ Line: 0,
+ Column: 0,
+ ISA: 0,
+ Discriminator: 0,
+ Stmt: hdr.defStmt,
+ Block: false,
+ EndPrologue: false,
+ BeginEpilogue: false,
+ }
+ if len(hdr.files) > 0 {
+ resetLineInfo.Filename = hdr.files[0]
+ }
+ 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]
+ 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()
+ }
+ }
+ }
+ }
+}
+
+// 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)
+ }
+ 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
+ }
+
+ return lines, lineInfo, newLineInfo
+}
diff --git a/libgo/go/debug/dwarf/line_test.go b/libgo/go/debug/dwarf/line_test.go
new file mode 100644
index 00000000000..2476a6faf53
--- /dev/null
+++ b/libgo/go/debug/dwarf/line_test.go
@@ -0,0 +1,53 @@
+// Copyright 2012 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.
+
+package dwarf_test
+
+import (
+ . "debug/dwarf"
+ "path/filepath"
+ "testing"
+)
+
+type lineTest struct {
+ pc uint64
+ file string
+ line int
+}
+
+var elfLineTests = [...]lineTest{
+ {0x4004c4, "typedef.c", 83},
+ {0x4004c8, "typedef.c", 84},
+ {0x4004ca, "typedef.c", 84},
+ {0x4003e0, "", 0},
+}
+
+var machoLineTests = [...]lineTest{
+ {0x0, "typedef.c", 83},
+}
+
+func TestLineElf(t *testing.T) {
+ testLine(t, elfData(t, "testdata/typedef.elf"), elfLineTests[:], "elf")
+}
+
+func TestLineMachO(t *testing.T) {
+ testLine(t, machoData(t, "testdata/typedef.macho"), machoLineTests[:], "macho")
+}
+
+func testLine(t *testing.T, d *Data, tests []lineTest, kind string) {
+ for _, v := range tests {
+ file, line, err := d.FileLine(v.pc)
+ if err != nil {
+ t.Errorf("%s: %v", kind, err)
+ continue
+ }
+ if file != "" {
+ file = filepath.Base(file)
+ }
+ 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)
+ }
+ }
+}
diff --git a/libgo/go/debug/dwarf/open.go b/libgo/go/debug/dwarf/open.go
index 9543297e189..75798925296 100644
--- a/libgo/go/debug/dwarf/open.go
+++ b/libgo/go/debug/dwarf/open.go
@@ -24,15 +24,15 @@ type Data struct {
// parsed data
abbrevCache map[uint32]abbrevTable
- addrsize int
order binary.ByteOrder
typeCache map[Offset]Type
unit []unit
}
// New returns a new Data object initialized from the given parameters.
-// Clients should typically use [TODO(rsc): method to be named later] instead of calling
-// New directly.
+// Rather than calling this function directly, clients should typically use
+// the DWARF method of the File type of the appropriate package debug/elf,
+// debug/macho, or debug/pe.
//
// The []byte arguments are the data from the corresponding debug section
// in the object file; for example, for an ELF object, abbrev is the contents of
diff --git a/libgo/go/debug/dwarf/testdata/typedef.c b/libgo/go/debug/dwarf/testdata/typedef.c
index 664d021ced5..f05f01564ff 100644
--- a/libgo/go/debug/dwarf/testdata/typedef.c
+++ b/libgo/go/debug/dwarf/testdata/typedef.c
@@ -28,8 +28,13 @@ typedef struct my_struct {
volatile int vi;
char x : 1;
int y : 4;
+ int z[0];
long long array[40];
+ int zz[0];
} t_my_struct;
+typedef struct my_struct1 {
+ int zz [1];
+} t_my_struct1;
typedef union my_union {
volatile int vi;
char x : 1;
@@ -65,7 +70,8 @@ t_func_void_of_char *a9;
t_func_void_of_void *a10;
t_func_void_of_ptr_char_dots *a11;
t_my_struct *a12;
-t_my_union *a12a;
+t_my_struct1 *a12a;
+t_my_union *a12b;
t_my_enum *a13;
t_my_list *a14;
t_my_tree *a15;
diff --git a/libgo/go/debug/dwarf/testdata/typedef.elf b/libgo/go/debug/dwarf/testdata/typedef.elf
index 44df8da9bc7..b2062d2c4bb 100755
--- a/libgo/go/debug/dwarf/testdata/typedef.elf
+++ b/libgo/go/debug/dwarf/testdata/typedef.elf
Binary files differ
diff --git a/libgo/go/debug/dwarf/testdata/typedef.macho b/libgo/go/debug/dwarf/testdata/typedef.macho
index 41019c1e146..f75afcccbfc 100644
--- a/libgo/go/debug/dwarf/testdata/typedef.macho
+++ b/libgo/go/debug/dwarf/testdata/typedef.macho
Binary files differ
diff --git a/libgo/go/debug/dwarf/type.go b/libgo/go/debug/dwarf/type.go
index 9be66658fe9..2ef8ca01bf8 100644
--- a/libgo/go/debug/dwarf/type.go
+++ b/libgo/go/debug/dwarf/type.go
@@ -426,6 +426,8 @@ func (d *Data) Type(off Offset) (Type, error) {
t.StructName, _ = e.Val(AttrName).(string)
t.Incomplete = e.Val(AttrDeclaration) != nil
t.Field = make([]*StructField, 0, 8)
+ var lastFieldType Type
+ var lastFieldBitOffset int64
for kid := next(); kid != nil; kid = next() {
if kid.Tag == TagMember {
f := new(StructField)
@@ -433,7 +435,7 @@ func (d *Data) Type(off Offset) (Type, error) {
goto Error
}
if loc, ok := kid.Val(AttrDataMemberLoc).([]byte); ok {
- b := makeBuf(d, "location", 0, loc, d.addrsize)
+ b := makeBuf(d, nil, "location", 0, loc)
if b.uint8() != opPlusUconst {
err = DecodeError{"info", kid.Offset, "unexpected opcode"}
goto Error
@@ -444,11 +446,32 @@ func (d *Data) Type(off Offset) (Type, error) {
goto Error
}
}
+
+ haveBitOffset := false
f.Name, _ = kid.Val(AttrName).(string)
f.ByteSize, _ = kid.Val(AttrByteSize).(int64)
- f.BitOffset, _ = kid.Val(AttrBitOffset).(int64)
+ f.BitOffset, haveBitOffset = kid.Val(AttrBitOffset).(int64)
f.BitSize, _ = kid.Val(AttrBitSize).(int64)
t.Field = append(t.Field, f)
+
+ bito := f.BitOffset
+ if !haveBitOffset {
+ bito = f.ByteOffset * 8
+ }
+ if bito == lastFieldBitOffset && t.Kind != "union" {
+ // Last field was zero width. Fix array length.
+ // (DWARF writes out 0-length arrays as if they were 1-length arrays.)
+ zeroArray(lastFieldType)
+ }
+ lastFieldType = f.Type
+ lastFieldBitOffset = bito
+ }
+ }
+ if t.Kind != "union" {
+ b, ok := e.Val(AttrByteSize).(int64)
+ if ok && b*8 == lastFieldBitOffset {
+ // Final field must be zero width. Fix array length.
+ zeroArray(lastFieldType)
}
}
@@ -579,3 +602,14 @@ Error:
delete(d.typeCache, off)
return nil, err
}
+
+func zeroArray(t Type) {
+ for {
+ at, ok := t.(*ArrayType)
+ if !ok {
+ break
+ }
+ at.Count = 0
+ t = at.Type
+ }
+}
diff --git a/libgo/go/debug/dwarf/type_test.go b/libgo/go/debug/dwarf/type_test.go
index b9470a4fcb4..b5b255f6f4a 100644
--- a/libgo/go/debug/dwarf/type_test.go
+++ b/libgo/go/debug/dwarf/type_test.go
@@ -25,13 +25,22 @@ var typedefTests = map[string]string{
"t_func_void_of_char": "func(char) void",
"t_func_void_of_void": "func() void",
"t_func_void_of_ptr_char_dots": "func(*char, ...) void",
- "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; array [40]long long int@8}",
+ "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; z [0]int@8; array [40]long long int@8; zz [0]int@328}",
+ "t_my_struct1": "struct my_struct1 {zz [1]int@0}",
"t_my_union": "union my_union {vi volatile int@0; x char@0 : 1@7; y int@0 : 4@28; array [40]long long int@0}",
"t_my_enum": "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}",
"t_my_list": "struct list {val short int@0; next *t_my_list@8}",
"t_my_tree": "struct tree {left *struct tree@0; right *struct tree@8; val long long unsigned int@16}",
}
+// As Apple converts gcc to a clang-based front end
+// they keep breaking the DWARF output. This map lists the
+// conversion from real answer to Apple answer.
+var machoBug = map[string]string{
+ "func(*char, ...) void": "func(*char) void",
+ "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}": "enum my_enum {e1=1; e2=2; e3=-5; e4=-1530494976}",
+}
+
func elfData(t *testing.T, name string) *Data {
f, err := elf.Open(name)
if err != nil {
@@ -58,13 +67,13 @@ func machoData(t *testing.T, name string) *Data {
return d
}
-func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf")) }
+func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf"), "elf") }
func TestTypedefsMachO(t *testing.T) {
- testTypedefs(t, machoData(t, "testdata/typedef.macho"))
+ testTypedefs(t, machoData(t, "testdata/typedef.macho"), "macho")
}
-func testTypedefs(t *testing.T, d *Data) {
+func testTypedefs(t *testing.T, d *Data, kind string) {
r := d.Reader()
seen := make(map[string]bool)
for {
@@ -93,7 +102,7 @@ func testTypedefs(t *testing.T, d *Data) {
t.Errorf("multiple definitions for %s", t1.Name)
}
seen[t1.Name] = true
- if typstr != want {
+ if typstr != want && (kind != "macho" || typstr != machoBug[want]) {
t.Errorf("%s:\n\thave %s\n\twant %s", t1.Name, typstr, want)
}
}
diff --git a/libgo/go/debug/dwarf/unit.go b/libgo/go/debug/dwarf/unit.go
index c10d75dbdc9..b1903208715 100644
--- a/libgo/go/debug/dwarf/unit.go
+++ b/libgo/go/debug/dwarf/unit.go
@@ -12,17 +12,38 @@ import "strconv"
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
addrsize int
+ version int
+ dwarf64 bool // True for 64-bit DWARF format
+ dir string
+ pc []addrRange // PC ranges in this compilation unit
+ lines []mapLineInfo // PC -> line mapping
+}
+
+// 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, "info", 0, d.info, 0)
+ b := makeBuf(d, nil, "info", 0, d.info)
for len(b.data) > 0 {
- b.skip(int(b.uint32()))
+ len := b.uint32()
+ if len == 0xffffffff {
+ len64 := b.uint64()
+ if len64 != uint64(int(len64)) {
+ b.error("unit length overflow")
+ break
+ }
+ len = uint32(len64)
+ }
+ b.skip(int(len))
nunit++
}
if b.err != nil {
@@ -30,13 +51,18 @@ func (d *Data) parseUnits() ([]unit, error) {
}
// Again, this time writing them down.
- b = makeBuf(d, "info", 0, d.info, 0)
+ b = makeBuf(d, nil, "info", 0, d.info)
units := make([]unit, nunit)
for i := range units {
u := &units[i]
u.base = b.off
n := b.uint32()
- if vers := b.uint16(); vers != 2 {
+ if n == 0xffffffff {
+ u.dwarf64 = true
+ n = uint32(b.uint64())
+ }
+ vers := b.uint16()
+ if vers < 2 || vers > 4 {
b.error("unsupported DWARF version " + strconv.Itoa(int(vers)))
break
}
@@ -47,6 +73,7 @@ func (d *Data) parseUnits() ([]unit, error) {
}
break
}
+ u.version = int(vers)
u.atable = atable
u.addrsize = int(b.uint8())
u.off = b.off