diff options
author | Cary Coutant <ccoutant@google.com> | 2012-03-09 11:50:43 -0800 |
---|---|---|
committer | Cary Coutant <ccoutant@google.com> | 2012-03-09 11:50:43 -0800 |
commit | 4ed2fc3a6bbaaf1f2ea4d1c077dc7fa0867cd005 (patch) | |
tree | 7774dcb772ccc005c2fd8f8831dc45b90a8f6060 /libgo/go/debug/dwarf | |
parent | 2400d467a903f32c96df9f9fcb0de7be8a655afa (diff) | |
parent | f9c681635d1850543f547fdbb6efd5a010aca015 (diff) | |
download | gcc-4ed2fc3a6bbaaf1f2ea4d1c077dc7fa0867cd005.tar.gz |
Merge branch 'master' into google-debugfission
Diffstat (limited to 'libgo/go/debug/dwarf')
-rw-r--r-- | libgo/go/debug/dwarf/buf.go | 38 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/const.go | 32 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/entry.go | 47 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/line.go | 435 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/line_test.go | 53 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/open.go | 6 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/testdata/typedef.c | 8 | ||||
-rwxr-xr-x | libgo/go/debug/dwarf/testdata/typedef.elf | bin | 10837 -> 12448 bytes | |||
-rw-r--r-- | libgo/go/debug/dwarf/testdata/typedef.macho | bin | 5256 -> 5024 bytes | |||
-rw-r--r-- | libgo/go/debug/dwarf/type.go | 38 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/type_test.go | 19 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/unit.go | 35 |
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 Binary files differindex 44df8da9bc7..b2062d2c4bb 100755 --- a/libgo/go/debug/dwarf/testdata/typedef.elf +++ b/libgo/go/debug/dwarf/testdata/typedef.elf diff --git a/libgo/go/debug/dwarf/testdata/typedef.macho b/libgo/go/debug/dwarf/testdata/typedef.macho Binary files differindex 41019c1e146..f75afcccbfc 100644 --- a/libgo/go/debug/dwarf/testdata/typedef.macho +++ b/libgo/go/debug/dwarf/testdata/typedef.macho 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 |