diff mbox

libgo patch committed: Support DWARF versions 3 and 4

Message ID mcrpqcmnuy0.fsf@dhcp-172-18-216-180.mtv.corp.google.com
State New
Headers show

Commit Message

Ian Lance Taylor March 9, 2012, 6:35 a.m. UTC
This patch to libgo extends the DWARF reader to handle DWARF versions 3
and 4.  It also includes some patches to the line number reading code
based on tests using DWARF version 4.  Bootstrapped and ran Go testsuite
on x86_64-unknown-linux-gnu.  Committed to mainline.

Ian
diff mbox

Patch

diff -r 976886593e1f libgo/go/debug/dwarf/buf.go
--- a/libgo/go/debug/dwarf/buf.go	Thu Mar 08 21:32:43 2012 -0800
+++ b/libgo/go/debug/dwarf/buf.go	Thu Mar 08 22:32:23 2012 -0800
@@ -14,16 +14,16 @@ 
 // Data buffer being decoded.
 type buf struct {
 	dwarf    *Data
+	u        *unit
 	order    binary.ByteOrder
 	name     string
 	off      Offset
 	data     []byte
-	addrsize int
 	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 @@ 
 
 // 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 -r 976886593e1f libgo/go/debug/dwarf/const.go
--- a/libgo/go/debug/dwarf/const.go	Thu Mar 08 21:32:43 2012 -0800
+++ b/libgo/go/debug/dwarf/const.go	Thu Mar 08 22:32:23 2012 -0800
@@ -207,6 +207,11 @@ 
 	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.
diff -r 976886593e1f libgo/go/debug/dwarf/entry.go
--- a/libgo/go/debug/dwarf/entry.go	Thu Mar 08 21:32:43 2012 -0800
+++ b/libgo/go/debug/dwarf/entry.go	Thu Mar 08 22:32:23 2012 -0800
@@ -40,7 +40,7 @@ 
 	} 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 @@ 
 		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 @@ 
 			val = Offset(b.uint64()) + ubase
 		case formRefUdata:
 			val = Offset(b.uint()) + ubase
+		case formRefSig8:
+			val = b.uint64()
 
 		// string
 		case formString:
@@ -208,7 +234,7 @@ 
 			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 {
@@ -251,7 +277,7 @@ 
 	r := &Reader{d: d}
 	r.unit = i
 	u := &d.unit[i]
-	r.b = makeBuf(d, "info", u.off, u.data, u.addrsize)
+	r.b = makeBuf(d, u, "info", u.off, u.data)
 	return r
 }
 
@@ -267,7 +293,7 @@ 
 		}
 		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
 	}
 
@@ -278,7 +304,7 @@ 
 		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
 		}
 	}
@@ -290,7 +316,7 @@ 
 	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 -r 976886593e1f libgo/go/debug/dwarf/line.go
--- a/libgo/go/debug/dwarf/line.go	Thu Mar 08 21:32:43 2012 -0800
+++ b/libgo/go/debug/dwarf/line.go	Thu Mar 08 22:32:23 2012 -0800
@@ -74,9 +74,17 @@ 
 				// TODO: Handle AttrRanges and .debug_ranges.
 				_ = f
 			}
-			if off, ok := e.Val(AttrStmtList).(int64); ok {
-				u.lineoff = Offset(off)
-				setLineOff = true
+			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
@@ -177,15 +185,15 @@ 
 	if u.lineoff+1 == 0 {
 		return errors.New("unknown line offset")
 	}
-	b := makeBuf(d, "line", u.lineoff, d.line, u.addrsize)
+	b := makeBuf(d, u, "line", u.lineoff, d.line[u.lineoff:])
 	len := uint64(b.uint32())
-	offSize := 4
+	dwarf64 := false
 	if len == 0xffffffff {
 		len = b.uint64()
-		offSize = 8
+		dwarf64 = true
 	}
 	end := b.off + Offset(len)
-	hdr := d.parseLineHdr(u, &b, offSize)
+	hdr := d.parseLineHdr(u, &b, dwarf64)
 	if b.err == nil {
 		d.parseLineProgram(u, &b, hdr, end)
 	}
@@ -193,14 +201,20 @@ 
 }
 
 // parseLineHdr parses a line number program header.
-func (d *Data) parseLineHdr(u *unit, b *buf, offSize int) (hdr lineHdr) {
+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
 	}
 
-	b.bytes(offSize) // header length
+	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 {
@@ -241,6 +255,10 @@ 
 		hdr.files = append(hdr.files, f)
 	}
 
+	if end > b.off {
+		b.bytes(int(end - b.off))
+	}
+
 	return
 }
 
@@ -296,6 +314,7 @@ 
 				u.lines = append(u.lines, lines...)
 				lineInfo = resetLineInfo
 				lines = nil
+				newLineInfo = true
 			case LineExtSetAddress:
 				address = b.addr()
 			case LineExtDefineFile:
diff -r 976886593e1f libgo/go/debug/dwarf/open.go
--- a/libgo/go/debug/dwarf/open.go	Thu Mar 08 21:32:43 2012 -0800
+++ b/libgo/go/debug/dwarf/open.go	Thu Mar 08 22:32:23 2012 -0800
@@ -24,7 +24,6 @@ 
 
 	// parsed data
 	abbrevCache map[uint32]abbrevTable
-	addrsize    int
 	order       binary.ByteOrder
 	typeCache   map[Offset]Type
 	unit        []unit
diff -r 976886593e1f libgo/go/debug/dwarf/type.go
--- a/libgo/go/debug/dwarf/type.go	Thu Mar 08 21:32:43 2012 -0800
+++ b/libgo/go/debug/dwarf/type.go	Thu Mar 08 22:32:23 2012 -0800
@@ -435,7 +435,7 @@ 
 					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
diff -r 976886593e1f libgo/go/debug/dwarf/unit.go
--- a/libgo/go/debug/dwarf/unit.go	Thu Mar 08 21:32:43 2012 -0800
+++ b/libgo/go/debug/dwarf/unit.go	Thu Mar 08 22:32:23 2012 -0800
@@ -16,6 +16,8 @@ 
 	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
@@ -30,9 +32,18 @@ 
 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 {
@@ -40,13 +51,18 @@ 
 	}
 
 	// 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
 		}
@@ -57,6 +73,7 @@ 
 			}
 			break
 		}
+		u.version = int(vers)
 		u.atable = atable
 		u.addrsize = int(b.uint8())
 		u.off = b.off