x/debug: handle some more cases for struct member offset encoding in the DWARF parser.

These cases were used in older compiler versions.  We also check that we
consumed all the opcodes in the buffer, otherwise we probably didn't
interpret it correctly.

Change-Id: If918dbdcb824a5d4e5add1c9333b34aae9d6d0b4
Reviewed-on: https://go-review.googlesource.com/19212
Reviewed-by: Dave Day <djd@golang.org>
diff --git a/dwarf/buf.go b/dwarf/buf.go
index a98d283..73fcc9a 100644
--- a/dwarf/buf.go
+++ b/dwarf/buf.go
@@ -8,6 +8,7 @@
 
 import (
 	"encoding/binary"
+	"fmt"
 	"strconv"
 )
 
@@ -173,6 +174,17 @@
 	return 0
 }
 
+// assertEmpty checks that everything has been read from b.
+func (b *buf) assertEmpty() {
+	if len(b.data) == 0 {
+		return
+	}
+	if len(b.data) > 5 {
+		b.error(fmt.Sprintf("unexpected extra data: %x...", b.data[0:5]))
+	}
+	b.error(fmt.Sprintf("unexpected extra data: %x", b.data))
+}
+
 func (b *buf) error(s string) {
 	if b.err == nil {
 		b.data = nil
diff --git a/dwarf/type.go b/dwarf/type.go
index a2471b6..dfe9d6a 100644
--- a/dwarf/type.go
+++ b/dwarf/type.go
@@ -9,6 +9,7 @@
 package dwarf
 
 import (
+	"fmt"
 	"reflect"
 	"strconv"
 )
@@ -606,12 +607,30 @@
 				case []byte:
 					// TODO: Should have original compilation
 					// unit here, not unknownFormat.
+					if len(loc) == 0 {
+						// Empty exprloc. f.ByteOffset=0.
+						break
+					}
 					b := makeBuf(d, unknownFormat{}, "location", 0, loc)
-					if x := b.uint8(); x != opPlusUconst {
-						err = DecodeError{name, kid.Offset, "unexpected opcode 0x" + strconv.FormatUint(uint64(x), 16)}
+					op := b.uint8()
+					switch op {
+					case opPlusUconst:
+						// Handle opcode sequence [DW_OP_plus_uconst <uleb128>]
+						f.ByteOffset = int64(b.uint())
+						b.assertEmpty()
+					case opConsts:
+						// Handle opcode sequence [DW_OP_consts <sleb128> DW_OP_plus]
+						f.ByteOffset = b.int()
+						op = b.uint8()
+						if op != opPlus {
+							err = DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op)}
+							goto Error
+						}
+						b.assertEmpty()
+					default:
+						err = DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op)}
 						goto Error
 					}
-					f.ByteOffset = int64(b.uint())
 					if b.err != nil {
 						err = b.err
 						goto Error