summaryrefslogtreecommitdiffstats
path: root/src/debug/dwarf
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:25:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:25:22 +0000
commitf6ad4dcef54c5ce997a4bad5a6d86de229015700 (patch)
tree7cfa4e31ace5c2bd95c72b154d15af494b2bcbef /src/debug/dwarf
parentInitial commit. (diff)
downloadgolang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.tar.xz
golang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.zip
Adding upstream version 1.22.1.upstream/1.22.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/debug/dwarf')
-rw-r--r--src/debug/dwarf/attr_string.go265
-rw-r--r--src/debug/dwarf/buf.go205
-rw-r--r--src/debug/dwarf/class_string.go42
-rw-r--r--src/debug/dwarf/const.go475
-rw-r--r--src/debug/dwarf/dwarf5ranges_test.go41
-rw-r--r--src/debug/dwarf/entry.go1221
-rw-r--r--src/debug/dwarf/entry_test.go459
-rw-r--r--src/debug/dwarf/export_test.go7
-rw-r--r--src/debug/dwarf/line.go852
-rw-r--r--src/debug/dwarf/line_test.go462
-rw-r--r--src/debug/dwarf/open.go140
-rw-r--r--src/debug/dwarf/tag_string.go119
-rw-r--r--src/debug/dwarf/testdata/bitfields.c17
-rw-r--r--src/debug/dwarf/testdata/bitfields.elf4bin0 -> 2464 bytes
-rw-r--r--src/debug/dwarf/testdata/cppunsuptypes.cc34
-rw-r--r--src/debug/dwarf/testdata/cppunsuptypes.elfbin0 -> 3920 bytes
-rw-r--r--src/debug/dwarf/testdata/cycle.c7
-rw-r--r--src/debug/dwarf/testdata/cycle.elfbin0 -> 2624 bytes
-rw-r--r--src/debug/dwarf/testdata/debug_rnglistsbin0 -> 23 bytes
-rw-r--r--src/debug/dwarf/testdata/line-clang-dwarf5.elfbin0 -> 18384 bytes
-rw-r--r--src/debug/dwarf/testdata/line-clang.elfbin0 -> 10271 bytes
-rw-r--r--src/debug/dwarf/testdata/line-gcc-dwarf5.elfbin0 -> 18040 bytes
-rw-r--r--src/debug/dwarf/testdata/line-gcc-win.binbin0 -> 133202 bytes
-rw-r--r--src/debug/dwarf/testdata/line-gcc-zstd.elfbin0 -> 17168 bytes
-rw-r--r--src/debug/dwarf/testdata/line-gcc.elfbin0 -> 10113 bytes
-rw-r--r--src/debug/dwarf/testdata/line1.c9
-rw-r--r--src/debug/dwarf/testdata/line1.h7
-rw-r--r--src/debug/dwarf/testdata/line2.c6
-rw-r--r--src/debug/dwarf/testdata/ranges.c25
-rw-r--r--src/debug/dwarf/testdata/ranges.elfbin0 -> 10348 bytes
-rw-r--r--src/debug/dwarf/testdata/rnglistx.c19
-rw-r--r--src/debug/dwarf/testdata/rnglistx.elfbin0 -> 11024 bytes
-rw-r--r--src/debug/dwarf/testdata/split.c5
-rw-r--r--src/debug/dwarf/testdata/split.elfbin0 -> 9509 bytes
-rw-r--r--src/debug/dwarf/testdata/typedef.c86
-rw-r--r--src/debug/dwarf/testdata/typedef.elfbin0 -> 12448 bytes
-rw-r--r--src/debug/dwarf/testdata/typedef.elf4bin0 -> 9496 bytes
-rw-r--r--src/debug/dwarf/testdata/typedef.elf5bin0 -> 6016 bytes
-rw-r--r--src/debug/dwarf/testdata/typedef.machobin0 -> 5024 bytes
-rw-r--r--src/debug/dwarf/testdata/typedef.macho4bin0 -> 6220 bytes
-rw-r--r--src/debug/dwarf/type.go870
-rw-r--r--src/debug/dwarf/type_test.go335
-rw-r--r--src/debug/dwarf/typeunit.go160
-rw-r--r--src/debug/dwarf/unit.go137
44 files changed, 6005 insertions, 0 deletions
diff --git a/src/debug/dwarf/attr_string.go b/src/debug/dwarf/attr_string.go
new file mode 100644
index 0000000..8a4fff8
--- /dev/null
+++ b/src/debug/dwarf/attr_string.go
@@ -0,0 +1,265 @@
+// Code generated by "stringer -type Attr -trimprefix=Attr"; DO NOT EDIT.
+
+package dwarf
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[AttrSibling-1]
+ _ = x[AttrLocation-2]
+ _ = x[AttrName-3]
+ _ = x[AttrOrdering-9]
+ _ = x[AttrByteSize-11]
+ _ = x[AttrBitOffset-12]
+ _ = x[AttrBitSize-13]
+ _ = x[AttrStmtList-16]
+ _ = x[AttrLowpc-17]
+ _ = x[AttrHighpc-18]
+ _ = x[AttrLanguage-19]
+ _ = x[AttrDiscr-21]
+ _ = x[AttrDiscrValue-22]
+ _ = x[AttrVisibility-23]
+ _ = x[AttrImport-24]
+ _ = x[AttrStringLength-25]
+ _ = x[AttrCommonRef-26]
+ _ = x[AttrCompDir-27]
+ _ = x[AttrConstValue-28]
+ _ = x[AttrContainingType-29]
+ _ = x[AttrDefaultValue-30]
+ _ = x[AttrInline-32]
+ _ = x[AttrIsOptional-33]
+ _ = x[AttrLowerBound-34]
+ _ = x[AttrProducer-37]
+ _ = x[AttrPrototyped-39]
+ _ = x[AttrReturnAddr-42]
+ _ = x[AttrStartScope-44]
+ _ = x[AttrStrideSize-46]
+ _ = x[AttrUpperBound-47]
+ _ = x[AttrAbstractOrigin-49]
+ _ = x[AttrAccessibility-50]
+ _ = x[AttrAddrClass-51]
+ _ = x[AttrArtificial-52]
+ _ = x[AttrBaseTypes-53]
+ _ = x[AttrCalling-54]
+ _ = x[AttrCount-55]
+ _ = x[AttrDataMemberLoc-56]
+ _ = x[AttrDeclColumn-57]
+ _ = x[AttrDeclFile-58]
+ _ = x[AttrDeclLine-59]
+ _ = x[AttrDeclaration-60]
+ _ = x[AttrDiscrList-61]
+ _ = x[AttrEncoding-62]
+ _ = x[AttrExternal-63]
+ _ = x[AttrFrameBase-64]
+ _ = x[AttrFriend-65]
+ _ = x[AttrIdentifierCase-66]
+ _ = x[AttrMacroInfo-67]
+ _ = x[AttrNamelistItem-68]
+ _ = x[AttrPriority-69]
+ _ = x[AttrSegment-70]
+ _ = x[AttrSpecification-71]
+ _ = x[AttrStaticLink-72]
+ _ = x[AttrType-73]
+ _ = x[AttrUseLocation-74]
+ _ = x[AttrVarParam-75]
+ _ = x[AttrVirtuality-76]
+ _ = x[AttrVtableElemLoc-77]
+ _ = x[AttrAllocated-78]
+ _ = x[AttrAssociated-79]
+ _ = x[AttrDataLocation-80]
+ _ = x[AttrStride-81]
+ _ = x[AttrEntrypc-82]
+ _ = x[AttrUseUTF8-83]
+ _ = x[AttrExtension-84]
+ _ = x[AttrRanges-85]
+ _ = x[AttrTrampoline-86]
+ _ = x[AttrCallColumn-87]
+ _ = x[AttrCallFile-88]
+ _ = x[AttrCallLine-89]
+ _ = x[AttrDescription-90]
+ _ = x[AttrBinaryScale-91]
+ _ = x[AttrDecimalScale-92]
+ _ = x[AttrSmall-93]
+ _ = x[AttrDecimalSign-94]
+ _ = x[AttrDigitCount-95]
+ _ = x[AttrPictureString-96]
+ _ = x[AttrMutable-97]
+ _ = x[AttrThreadsScaled-98]
+ _ = x[AttrExplicit-99]
+ _ = x[AttrObjectPointer-100]
+ _ = x[AttrEndianity-101]
+ _ = x[AttrElemental-102]
+ _ = x[AttrPure-103]
+ _ = x[AttrRecursive-104]
+ _ = x[AttrSignature-105]
+ _ = x[AttrMainSubprogram-106]
+ _ = x[AttrDataBitOffset-107]
+ _ = x[AttrConstExpr-108]
+ _ = x[AttrEnumClass-109]
+ _ = x[AttrLinkageName-110]
+ _ = x[AttrStringLengthBitSize-111]
+ _ = x[AttrStringLengthByteSize-112]
+ _ = x[AttrRank-113]
+ _ = x[AttrStrOffsetsBase-114]
+ _ = x[AttrAddrBase-115]
+ _ = x[AttrRnglistsBase-116]
+ _ = x[AttrDwoName-118]
+ _ = x[AttrReference-119]
+ _ = x[AttrRvalueReference-120]
+ _ = x[AttrMacros-121]
+ _ = x[AttrCallAllCalls-122]
+ _ = x[AttrCallAllSourceCalls-123]
+ _ = x[AttrCallAllTailCalls-124]
+ _ = x[AttrCallReturnPC-125]
+ _ = x[AttrCallValue-126]
+ _ = x[AttrCallOrigin-127]
+ _ = x[AttrCallParameter-128]
+ _ = x[AttrCallPC-129]
+ _ = x[AttrCallTailCall-130]
+ _ = x[AttrCallTarget-131]
+ _ = x[AttrCallTargetClobbered-132]
+ _ = x[AttrCallDataLocation-133]
+ _ = x[AttrCallDataValue-134]
+ _ = x[AttrNoreturn-135]
+ _ = x[AttrAlignment-136]
+ _ = x[AttrExportSymbols-137]
+ _ = x[AttrDeleted-138]
+ _ = x[AttrDefaulted-139]
+ _ = x[AttrLoclistsBase-140]
+}
+
+const _Attr_name = "SiblingLocationNameOrderingByteSizeBitOffsetBitSizeStmtListLowpcHighpcLanguageDiscrDiscrValueVisibilityImportStringLengthCommonRefCompDirConstValueContainingTypeDefaultValueInlineIsOptionalLowerBoundProducerPrototypedReturnAddrStartScopeStrideSizeUpperBoundAbstractOriginAccessibilityAddrClassArtificialBaseTypesCallingCountDataMemberLocDeclColumnDeclFileDeclLineDeclarationDiscrListEncodingExternalFrameBaseFriendIdentifierCaseMacroInfoNamelistItemPrioritySegmentSpecificationStaticLinkTypeUseLocationVarParamVirtualityVtableElemLocAllocatedAssociatedDataLocationStrideEntrypcUseUTF8ExtensionRangesTrampolineCallColumnCallFileCallLineDescriptionBinaryScaleDecimalScaleSmallDecimalSignDigitCountPictureStringMutableThreadsScaledExplicitObjectPointerEndianityElementalPureRecursiveSignatureMainSubprogramDataBitOffsetConstExprEnumClassLinkageNameStringLengthBitSizeStringLengthByteSizeRankStrOffsetsBaseAddrBaseRnglistsBaseDwoNameReferenceRvalueReferenceMacrosCallAllCallsCallAllSourceCallsCallAllTailCallsCallReturnPCCallValueCallOriginCallParameterCallPCCallTailCallCallTargetCallTargetClobberedCallDataLocationCallDataValueNoreturnAlignmentExportSymbolsDeletedDefaultedLoclistsBase"
+
+var _Attr_map = map[Attr]string{
+ 1: _Attr_name[0:7],
+ 2: _Attr_name[7:15],
+ 3: _Attr_name[15:19],
+ 9: _Attr_name[19:27],
+ 11: _Attr_name[27:35],
+ 12: _Attr_name[35:44],
+ 13: _Attr_name[44:51],
+ 16: _Attr_name[51:59],
+ 17: _Attr_name[59:64],
+ 18: _Attr_name[64:70],
+ 19: _Attr_name[70:78],
+ 21: _Attr_name[78:83],
+ 22: _Attr_name[83:93],
+ 23: _Attr_name[93:103],
+ 24: _Attr_name[103:109],
+ 25: _Attr_name[109:121],
+ 26: _Attr_name[121:130],
+ 27: _Attr_name[130:137],
+ 28: _Attr_name[137:147],
+ 29: _Attr_name[147:161],
+ 30: _Attr_name[161:173],
+ 32: _Attr_name[173:179],
+ 33: _Attr_name[179:189],
+ 34: _Attr_name[189:199],
+ 37: _Attr_name[199:207],
+ 39: _Attr_name[207:217],
+ 42: _Attr_name[217:227],
+ 44: _Attr_name[227:237],
+ 46: _Attr_name[237:247],
+ 47: _Attr_name[247:257],
+ 49: _Attr_name[257:271],
+ 50: _Attr_name[271:284],
+ 51: _Attr_name[284:293],
+ 52: _Attr_name[293:303],
+ 53: _Attr_name[303:312],
+ 54: _Attr_name[312:319],
+ 55: _Attr_name[319:324],
+ 56: _Attr_name[324:337],
+ 57: _Attr_name[337:347],
+ 58: _Attr_name[347:355],
+ 59: _Attr_name[355:363],
+ 60: _Attr_name[363:374],
+ 61: _Attr_name[374:383],
+ 62: _Attr_name[383:391],
+ 63: _Attr_name[391:399],
+ 64: _Attr_name[399:408],
+ 65: _Attr_name[408:414],
+ 66: _Attr_name[414:428],
+ 67: _Attr_name[428:437],
+ 68: _Attr_name[437:449],
+ 69: _Attr_name[449:457],
+ 70: _Attr_name[457:464],
+ 71: _Attr_name[464:477],
+ 72: _Attr_name[477:487],
+ 73: _Attr_name[487:491],
+ 74: _Attr_name[491:502],
+ 75: _Attr_name[502:510],
+ 76: _Attr_name[510:520],
+ 77: _Attr_name[520:533],
+ 78: _Attr_name[533:542],
+ 79: _Attr_name[542:552],
+ 80: _Attr_name[552:564],
+ 81: _Attr_name[564:570],
+ 82: _Attr_name[570:577],
+ 83: _Attr_name[577:584],
+ 84: _Attr_name[584:593],
+ 85: _Attr_name[593:599],
+ 86: _Attr_name[599:609],
+ 87: _Attr_name[609:619],
+ 88: _Attr_name[619:627],
+ 89: _Attr_name[627:635],
+ 90: _Attr_name[635:646],
+ 91: _Attr_name[646:657],
+ 92: _Attr_name[657:669],
+ 93: _Attr_name[669:674],
+ 94: _Attr_name[674:685],
+ 95: _Attr_name[685:695],
+ 96: _Attr_name[695:708],
+ 97: _Attr_name[708:715],
+ 98: _Attr_name[715:728],
+ 99: _Attr_name[728:736],
+ 100: _Attr_name[736:749],
+ 101: _Attr_name[749:758],
+ 102: _Attr_name[758:767],
+ 103: _Attr_name[767:771],
+ 104: _Attr_name[771:780],
+ 105: _Attr_name[780:789],
+ 106: _Attr_name[789:803],
+ 107: _Attr_name[803:816],
+ 108: _Attr_name[816:825],
+ 109: _Attr_name[825:834],
+ 110: _Attr_name[834:845],
+ 111: _Attr_name[845:864],
+ 112: _Attr_name[864:884],
+ 113: _Attr_name[884:888],
+ 114: _Attr_name[888:902],
+ 115: _Attr_name[902:910],
+ 116: _Attr_name[910:922],
+ 118: _Attr_name[922:929],
+ 119: _Attr_name[929:938],
+ 120: _Attr_name[938:953],
+ 121: _Attr_name[953:959],
+ 122: _Attr_name[959:971],
+ 123: _Attr_name[971:989],
+ 124: _Attr_name[989:1005],
+ 125: _Attr_name[1005:1017],
+ 126: _Attr_name[1017:1026],
+ 127: _Attr_name[1026:1036],
+ 128: _Attr_name[1036:1049],
+ 129: _Attr_name[1049:1055],
+ 130: _Attr_name[1055:1067],
+ 131: _Attr_name[1067:1077],
+ 132: _Attr_name[1077:1096],
+ 133: _Attr_name[1096:1112],
+ 134: _Attr_name[1112:1125],
+ 135: _Attr_name[1125:1133],
+ 136: _Attr_name[1133:1142],
+ 137: _Attr_name[1142:1155],
+ 138: _Attr_name[1155:1162],
+ 139: _Attr_name[1162:1171],
+ 140: _Attr_name[1171:1183],
+}
+
+func (i Attr) String() string {
+ if str, ok := _Attr_map[i]; ok {
+ return str
+ }
+ return "Attr(" + strconv.FormatInt(int64(i), 10) + ")"
+}
diff --git a/src/debug/dwarf/buf.go b/src/debug/dwarf/buf.go
new file mode 100644
index 0000000..7ac53ef
--- /dev/null
+++ b/src/debug/dwarf/buf.go
@@ -0,0 +1,205 @@
+// Copyright 2009 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.
+
+// Buffered reading and decoding of DWARF data streams.
+
+package dwarf
+
+import (
+ "bytes"
+ "encoding/binary"
+ "strconv"
+)
+
+// Data buffer being decoded.
+type buf struct {
+ dwarf *Data
+ order binary.ByteOrder
+ format dataFormat
+ name string
+ off Offset
+ data []byte
+ err error
+}
+
+// Data format, other than byte order. This affects the handling of
+// certain field formats.
+type dataFormat interface {
+ // DWARF version number. Zero means unknown.
+ version() int
+
+ // 64-bit DWARF format?
+ dwarf64() (dwarf64 bool, isKnown bool)
+
+ // Size of an address, in bytes. Zero means unknown.
+ addrsize() int
+}
+
+// Some parts of DWARF have no data format, e.g., abbrevs.
+type unknownFormat struct{}
+
+func (u unknownFormat) version() int {
+ return 0
+}
+
+func (u unknownFormat) dwarf64() (bool, bool) {
+ return false, false
+}
+
+func (u unknownFormat) addrsize() int {
+ return 0
+}
+
+func makeBuf(d *Data, format dataFormat, name string, off Offset, data []byte) buf {
+ return buf{d, d.order, format, name, off, data, nil}
+}
+
+func (b *buf) uint8() uint8 {
+ if len(b.data) < 1 {
+ b.error("underflow")
+ return 0
+ }
+ val := b.data[0]
+ b.data = b.data[1:]
+ b.off++
+ return val
+}
+
+func (b *buf) bytes(n int) []byte {
+ if n < 0 || len(b.data) < n {
+ b.error("underflow")
+ return nil
+ }
+ data := b.data[0:n]
+ b.data = b.data[n:]
+ b.off += Offset(n)
+ return data
+}
+
+func (b *buf) skip(n int) { b.bytes(n) }
+
+func (b *buf) string() string {
+ i := bytes.IndexByte(b.data, 0)
+ if i < 0 {
+ b.error("underflow")
+ return ""
+ }
+
+ s := string(b.data[0:i])
+ b.data = b.data[i+1:]
+ b.off += Offset(i + 1)
+ return s
+}
+
+func (b *buf) uint16() uint16 {
+ a := b.bytes(2)
+ if a == nil {
+ return 0
+ }
+ return b.order.Uint16(a)
+}
+
+func (b *buf) uint24() uint32 {
+ a := b.bytes(3)
+ if a == nil {
+ return 0
+ }
+ if b.dwarf.bigEndian {
+ return uint32(a[2]) | uint32(a[1])<<8 | uint32(a[0])<<16
+ } else {
+ return uint32(a[0]) | uint32(a[1])<<8 | uint32(a[2])<<16
+ }
+}
+
+func (b *buf) uint32() uint32 {
+ a := b.bytes(4)
+ if a == nil {
+ return 0
+ }
+ return b.order.Uint32(a)
+}
+
+func (b *buf) uint64() uint64 {
+ a := b.bytes(8)
+ if a == nil {
+ return 0
+ }
+ return b.order.Uint64(a)
+}
+
+// Read a varint, which is 7 bits per byte, little endian.
+// the 0x80 bit means read another byte.
+func (b *buf) varint() (c uint64, bits uint) {
+ for i := 0; i < len(b.data); i++ {
+ byte := b.data[i]
+ c |= uint64(byte&0x7F) << bits
+ bits += 7
+ if byte&0x80 == 0 {
+ b.off += Offset(i + 1)
+ b.data = b.data[i+1:]
+ return c, bits
+ }
+ }
+ return 0, 0
+}
+
+// Unsigned int is just a varint.
+func (b *buf) uint() uint64 {
+ x, _ := b.varint()
+ return x
+}
+
+// Signed int is a sign-extended varint.
+func (b *buf) int() int64 {
+ ux, bits := b.varint()
+ x := int64(ux)
+ if x&(1<<(bits-1)) != 0 {
+ x |= -1 << bits
+ }
+ return x
+}
+
+// Address-sized uint.
+func (b *buf) addr() uint64 {
+ switch b.format.addrsize() {
+ case 1:
+ return uint64(b.uint8())
+ case 2:
+ return uint64(b.uint16())
+ case 4:
+ return uint64(b.uint32())
+ case 8:
+ return b.uint64()
+ }
+ b.error("unknown address size")
+ 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
+ b.err = DecodeError{b.name, b.off, s}
+ }
+}
+
+type DecodeError struct {
+ Name string
+ Offset Offset
+ Err string
+}
+
+func (e DecodeError) Error() string {
+ return "decoding dwarf section " + e.Name + " at offset 0x" + strconv.FormatInt(int64(e.Offset), 16) + ": " + e.Err
+}
diff --git a/src/debug/dwarf/class_string.go b/src/debug/dwarf/class_string.go
new file mode 100644
index 0000000..163bed7
--- /dev/null
+++ b/src/debug/dwarf/class_string.go
@@ -0,0 +1,42 @@
+// Code generated by "stringer -type=Class"; DO NOT EDIT.
+
+package dwarf
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[ClassUnknown-0]
+ _ = x[ClassAddress-1]
+ _ = x[ClassBlock-2]
+ _ = x[ClassConstant-3]
+ _ = x[ClassExprLoc-4]
+ _ = x[ClassFlag-5]
+ _ = x[ClassLinePtr-6]
+ _ = x[ClassLocListPtr-7]
+ _ = x[ClassMacPtr-8]
+ _ = x[ClassRangeListPtr-9]
+ _ = x[ClassReference-10]
+ _ = x[ClassReferenceSig-11]
+ _ = x[ClassString-12]
+ _ = x[ClassReferenceAlt-13]
+ _ = x[ClassStringAlt-14]
+ _ = x[ClassAddrPtr-15]
+ _ = x[ClassLocList-16]
+ _ = x[ClassRngList-17]
+ _ = x[ClassRngListsPtr-18]
+ _ = x[ClassStrOffsetsPtr-19]
+}
+
+const _Class_name = "ClassUnknownClassAddressClassBlockClassConstantClassExprLocClassFlagClassLinePtrClassLocListPtrClassMacPtrClassRangeListPtrClassReferenceClassReferenceSigClassStringClassReferenceAltClassStringAltClassAddrPtrClassLocListClassRngListClassRngListsPtrClassStrOffsetsPtr"
+
+var _Class_index = [...]uint16{0, 12, 24, 34, 47, 59, 68, 80, 95, 106, 123, 137, 154, 165, 182, 196, 208, 220, 232, 248, 266}
+
+func (i Class) String() string {
+ if i < 0 || i >= Class(len(_Class_index)-1) {
+ return "Class(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _Class_name[_Class_index[i]:_Class_index[i+1]]
+}
diff --git a/src/debug/dwarf/const.go b/src/debug/dwarf/const.go
new file mode 100644
index 0000000..ea52460
--- /dev/null
+++ b/src/debug/dwarf/const.go
@@ -0,0 +1,475 @@
+// Copyright 2009 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.
+
+// Constants
+
+package dwarf
+
+//go:generate stringer -type Attr -trimprefix=Attr
+
+// An Attr identifies the attribute type in a DWARF [Entry.Field].
+type Attr uint32
+
+const (
+ AttrSibling Attr = 0x01
+ AttrLocation Attr = 0x02
+ AttrName Attr = 0x03
+ AttrOrdering Attr = 0x09
+ AttrByteSize Attr = 0x0B
+ AttrBitOffset Attr = 0x0C
+ AttrBitSize Attr = 0x0D
+ AttrStmtList Attr = 0x10
+ AttrLowpc Attr = 0x11
+ AttrHighpc Attr = 0x12
+ AttrLanguage Attr = 0x13
+ AttrDiscr Attr = 0x15
+ AttrDiscrValue Attr = 0x16
+ AttrVisibility Attr = 0x17
+ AttrImport Attr = 0x18
+ AttrStringLength Attr = 0x19
+ AttrCommonRef Attr = 0x1A
+ AttrCompDir Attr = 0x1B
+ AttrConstValue Attr = 0x1C
+ AttrContainingType Attr = 0x1D
+ AttrDefaultValue Attr = 0x1E
+ AttrInline Attr = 0x20
+ AttrIsOptional Attr = 0x21
+ AttrLowerBound Attr = 0x22
+ AttrProducer Attr = 0x25
+ AttrPrototyped Attr = 0x27
+ AttrReturnAddr Attr = 0x2A
+ AttrStartScope Attr = 0x2C
+ AttrStrideSize Attr = 0x2E
+ AttrUpperBound Attr = 0x2F
+ AttrAbstractOrigin Attr = 0x31
+ AttrAccessibility Attr = 0x32
+ AttrAddrClass Attr = 0x33
+ AttrArtificial Attr = 0x34
+ AttrBaseTypes Attr = 0x35
+ AttrCalling Attr = 0x36
+ AttrCount Attr = 0x37
+ AttrDataMemberLoc Attr = 0x38
+ AttrDeclColumn Attr = 0x39
+ AttrDeclFile Attr = 0x3A
+ AttrDeclLine Attr = 0x3B
+ AttrDeclaration Attr = 0x3C
+ AttrDiscrList Attr = 0x3D
+ AttrEncoding Attr = 0x3E
+ AttrExternal Attr = 0x3F
+ AttrFrameBase Attr = 0x40
+ AttrFriend Attr = 0x41
+ AttrIdentifierCase Attr = 0x42
+ AttrMacroInfo Attr = 0x43
+ AttrNamelistItem Attr = 0x44
+ AttrPriority Attr = 0x45
+ AttrSegment Attr = 0x46
+ AttrSpecification Attr = 0x47
+ AttrStaticLink Attr = 0x48
+ AttrType Attr = 0x49
+ AttrUseLocation Attr = 0x4A
+ AttrVarParam Attr = 0x4B
+ AttrVirtuality Attr = 0x4C
+ AttrVtableElemLoc Attr = 0x4D
+ // The following are new in DWARF 3.
+ AttrAllocated Attr = 0x4E
+ AttrAssociated Attr = 0x4F
+ AttrDataLocation Attr = 0x50
+ AttrStride Attr = 0x51
+ AttrEntrypc Attr = 0x52
+ AttrUseUTF8 Attr = 0x53
+ AttrExtension Attr = 0x54
+ AttrRanges Attr = 0x55
+ AttrTrampoline Attr = 0x56
+ AttrCallColumn Attr = 0x57
+ AttrCallFile Attr = 0x58
+ AttrCallLine Attr = 0x59
+ AttrDescription Attr = 0x5A
+ AttrBinaryScale Attr = 0x5B
+ AttrDecimalScale Attr = 0x5C
+ AttrSmall Attr = 0x5D
+ AttrDecimalSign Attr = 0x5E
+ AttrDigitCount Attr = 0x5F
+ AttrPictureString Attr = 0x60
+ AttrMutable Attr = 0x61
+ AttrThreadsScaled Attr = 0x62
+ AttrExplicit Attr = 0x63
+ AttrObjectPointer Attr = 0x64
+ AttrEndianity Attr = 0x65
+ AttrElemental Attr = 0x66
+ AttrPure Attr = 0x67
+ AttrRecursive Attr = 0x68
+ // The following are new in DWARF 4.
+ AttrSignature Attr = 0x69
+ AttrMainSubprogram Attr = 0x6A
+ AttrDataBitOffset Attr = 0x6B
+ AttrConstExpr Attr = 0x6C
+ AttrEnumClass Attr = 0x6D
+ AttrLinkageName Attr = 0x6E
+ // The following are new in DWARF 5.
+ AttrStringLengthBitSize Attr = 0x6F
+ AttrStringLengthByteSize Attr = 0x70
+ AttrRank Attr = 0x71
+ AttrStrOffsetsBase Attr = 0x72
+ AttrAddrBase Attr = 0x73
+ AttrRnglistsBase Attr = 0x74
+ AttrDwoName Attr = 0x76
+ AttrReference Attr = 0x77
+ AttrRvalueReference Attr = 0x78
+ AttrMacros Attr = 0x79
+ AttrCallAllCalls Attr = 0x7A
+ AttrCallAllSourceCalls Attr = 0x7B
+ AttrCallAllTailCalls Attr = 0x7C
+ AttrCallReturnPC Attr = 0x7D
+ AttrCallValue Attr = 0x7E
+ AttrCallOrigin Attr = 0x7F
+ AttrCallParameter Attr = 0x80
+ AttrCallPC Attr = 0x81
+ AttrCallTailCall Attr = 0x82
+ AttrCallTarget Attr = 0x83
+ AttrCallTargetClobbered Attr = 0x84
+ AttrCallDataLocation Attr = 0x85
+ AttrCallDataValue Attr = 0x86
+ AttrNoreturn Attr = 0x87
+ AttrAlignment Attr = 0x88
+ AttrExportSymbols Attr = 0x89
+ AttrDeleted Attr = 0x8A
+ AttrDefaulted Attr = 0x8B
+ AttrLoclistsBase Attr = 0x8C
+)
+
+func (a Attr) GoString() string {
+ if str, ok := _Attr_map[a]; ok {
+ return "dwarf.Attr" + str
+ }
+ return "dwarf." + a.String()
+}
+
+// A format is a DWARF data encoding format.
+type format uint32
+
+const (
+ // value formats
+ formAddr format = 0x01
+ formDwarfBlock2 format = 0x03
+ formDwarfBlock4 format = 0x04
+ formData2 format = 0x05
+ formData4 format = 0x06
+ formData8 format = 0x07
+ formString format = 0x08
+ formDwarfBlock format = 0x09
+ formDwarfBlock1 format = 0x0A
+ formData1 format = 0x0B
+ formFlag format = 0x0C
+ formSdata format = 0x0D
+ formStrp format = 0x0E
+ formUdata format = 0x0F
+ formRefAddr format = 0x10
+ formRef1 format = 0x11
+ formRef2 format = 0x12
+ formRef4 format = 0x13
+ formRef8 format = 0x14
+ formRefUdata format = 0x15
+ formIndirect format = 0x16
+ // The following are new in DWARF 4.
+ formSecOffset format = 0x17
+ formExprloc format = 0x18
+ formFlagPresent format = 0x19
+ formRefSig8 format = 0x20
+ // The following are new in DWARF 5.
+ formStrx format = 0x1A
+ formAddrx format = 0x1B
+ formRefSup4 format = 0x1C
+ formStrpSup format = 0x1D
+ formData16 format = 0x1E
+ formLineStrp format = 0x1F
+ formImplicitConst format = 0x21
+ formLoclistx format = 0x22
+ formRnglistx format = 0x23
+ formRefSup8 format = 0x24
+ formStrx1 format = 0x25
+ formStrx2 format = 0x26
+ formStrx3 format = 0x27
+ formStrx4 format = 0x28
+ formAddrx1 format = 0x29
+ formAddrx2 format = 0x2A
+ formAddrx3 format = 0x2B
+ formAddrx4 format = 0x2C
+ // Extensions for multi-file compression (.dwz)
+ // http://www.dwarfstd.org/ShowIssue.php?issue=120604.1
+ formGnuRefAlt format = 0x1f20
+ formGnuStrpAlt format = 0x1f21
+)
+
+//go:generate stringer -type Tag -trimprefix=Tag
+
+// A Tag is the classification (the type) of an [Entry].
+type Tag uint32
+
+const (
+ TagArrayType Tag = 0x01
+ TagClassType Tag = 0x02
+ TagEntryPoint Tag = 0x03
+ TagEnumerationType Tag = 0x04
+ TagFormalParameter Tag = 0x05
+ TagImportedDeclaration Tag = 0x08
+ TagLabel Tag = 0x0A
+ TagLexDwarfBlock Tag = 0x0B
+ TagMember Tag = 0x0D
+ TagPointerType Tag = 0x0F
+ TagReferenceType Tag = 0x10
+ TagCompileUnit Tag = 0x11
+ TagStringType Tag = 0x12
+ TagStructType Tag = 0x13
+ TagSubroutineType Tag = 0x15
+ TagTypedef Tag = 0x16
+ TagUnionType Tag = 0x17
+ TagUnspecifiedParameters Tag = 0x18
+ TagVariant Tag = 0x19
+ TagCommonDwarfBlock Tag = 0x1A
+ TagCommonInclusion Tag = 0x1B
+ TagInheritance Tag = 0x1C
+ TagInlinedSubroutine Tag = 0x1D
+ TagModule Tag = 0x1E
+ TagPtrToMemberType Tag = 0x1F
+ TagSetType Tag = 0x20
+ TagSubrangeType Tag = 0x21
+ TagWithStmt Tag = 0x22
+ TagAccessDeclaration Tag = 0x23
+ TagBaseType Tag = 0x24
+ TagCatchDwarfBlock Tag = 0x25
+ TagConstType Tag = 0x26
+ TagConstant Tag = 0x27
+ TagEnumerator Tag = 0x28
+ TagFileType Tag = 0x29
+ TagFriend Tag = 0x2A
+ TagNamelist Tag = 0x2B
+ TagNamelistItem Tag = 0x2C
+ TagPackedType Tag = 0x2D
+ TagSubprogram Tag = 0x2E
+ TagTemplateTypeParameter Tag = 0x2F
+ TagTemplateValueParameter Tag = 0x30
+ TagThrownType Tag = 0x31
+ TagTryDwarfBlock Tag = 0x32
+ TagVariantPart Tag = 0x33
+ TagVariable Tag = 0x34
+ TagVolatileType Tag = 0x35
+ // The following are new in DWARF 3.
+ TagDwarfProcedure Tag = 0x36
+ TagRestrictType Tag = 0x37
+ TagInterfaceType Tag = 0x38
+ TagNamespace Tag = 0x39
+ TagImportedModule Tag = 0x3A
+ TagUnspecifiedType Tag = 0x3B
+ TagPartialUnit Tag = 0x3C
+ TagImportedUnit Tag = 0x3D
+ TagMutableType Tag = 0x3E // Later removed from DWARF.
+ TagCondition Tag = 0x3F
+ TagSharedType Tag = 0x40
+ // The following are new in DWARF 4.
+ TagTypeUnit Tag = 0x41
+ TagRvalueReferenceType Tag = 0x42
+ TagTemplateAlias Tag = 0x43
+ // The following are new in DWARF 5.
+ TagCoarrayType Tag = 0x44
+ TagGenericSubrange Tag = 0x45
+ TagDynamicType Tag = 0x46
+ TagAtomicType Tag = 0x47
+ TagCallSite Tag = 0x48
+ TagCallSiteParameter Tag = 0x49
+ TagSkeletonUnit Tag = 0x4A
+ TagImmutableType Tag = 0x4B
+)
+
+func (t Tag) GoString() string {
+ if t <= TagTemplateAlias {
+ return "dwarf.Tag" + t.String()
+ }
+ return "dwarf." + t.String()
+}
+
+// Location expression operators.
+// The debug info encodes value locations like 8(R3)
+// as a sequence of these op codes.
+// This package does not implement full expressions;
+// the opPlusUconst operator is expected by the type parser.
+const (
+ opAddr = 0x03 /* 1 op, const addr */
+ opDeref = 0x06
+ opConst1u = 0x08 /* 1 op, 1 byte const */
+ opConst1s = 0x09 /* " signed */
+ opConst2u = 0x0A /* 1 op, 2 byte const */
+ opConst2s = 0x0B /* " signed */
+ opConst4u = 0x0C /* 1 op, 4 byte const */
+ opConst4s = 0x0D /* " signed */
+ opConst8u = 0x0E /* 1 op, 8 byte const */
+ opConst8s = 0x0F /* " signed */
+ opConstu = 0x10 /* 1 op, LEB128 const */
+ opConsts = 0x11 /* " signed */
+ opDup = 0x12
+ opDrop = 0x13
+ opOver = 0x14
+ opPick = 0x15 /* 1 op, 1 byte stack index */
+ opSwap = 0x16
+ opRot = 0x17
+ opXderef = 0x18
+ opAbs = 0x19
+ opAnd = 0x1A
+ opDiv = 0x1B
+ opMinus = 0x1C
+ opMod = 0x1D
+ opMul = 0x1E
+ opNeg = 0x1F
+ opNot = 0x20
+ opOr = 0x21
+ opPlus = 0x22
+ opPlusUconst = 0x23 /* 1 op, ULEB128 addend */
+ opShl = 0x24
+ opShr = 0x25
+ opShra = 0x26
+ opXor = 0x27
+ opSkip = 0x2F /* 1 op, signed 2-byte constant */
+ opBra = 0x28 /* 1 op, signed 2-byte constant */
+ opEq = 0x29
+ opGe = 0x2A
+ opGt = 0x2B
+ opLe = 0x2C
+ opLt = 0x2D
+ opNe = 0x2E
+ opLit0 = 0x30
+ /* OpLitN = OpLit0 + N for N = 0..31 */
+ opReg0 = 0x50
+ /* OpRegN = OpReg0 + N for N = 0..31 */
+ opBreg0 = 0x70 /* 1 op, signed LEB128 constant */
+ /* OpBregN = OpBreg0 + N for N = 0..31 */
+ opRegx = 0x90 /* 1 op, ULEB128 register */
+ opFbreg = 0x91 /* 1 op, SLEB128 offset */
+ opBregx = 0x92 /* 2 op, ULEB128 reg; SLEB128 off */
+ opPiece = 0x93 /* 1 op, ULEB128 size of piece */
+ opDerefSize = 0x94 /* 1-byte size of data retrieved */
+ opXderefSize = 0x95 /* 1-byte size of data retrieved */
+ opNop = 0x96
+ // The following are new in DWARF 3.
+ opPushObjAddr = 0x97
+ opCall2 = 0x98 /* 2-byte offset of DIE */
+ opCall4 = 0x99 /* 4-byte offset of DIE */
+ opCallRef = 0x9A /* 4- or 8- byte offset of DIE */
+ opFormTLSAddress = 0x9B
+ opCallFrameCFA = 0x9C
+ opBitPiece = 0x9D
+ // The following are new in DWARF 4.
+ opImplicitValue = 0x9E
+ opStackValue = 0x9F
+ // The following a new in DWARF 5.
+ opImplicitPointer = 0xA0
+ opAddrx = 0xA1
+ opConstx = 0xA2
+ opEntryValue = 0xA3
+ opConstType = 0xA4
+ opRegvalType = 0xA5
+ opDerefType = 0xA6
+ opXderefType = 0xA7
+ opConvert = 0xA8
+ opReinterpret = 0xA9
+ /* 0xE0-0xFF reserved for user-specific */
+)
+
+// Basic type encodings -- the value for AttrEncoding in a TagBaseType Entry.
+const (
+ encAddress = 0x01
+ encBoolean = 0x02
+ encComplexFloat = 0x03
+ encFloat = 0x04
+ encSigned = 0x05
+ encSignedChar = 0x06
+ encUnsigned = 0x07
+ encUnsignedChar = 0x08
+ // The following are new in DWARF 3.
+ encImaginaryFloat = 0x09
+ encPackedDecimal = 0x0A
+ encNumericString = 0x0B
+ encEdited = 0x0C
+ encSignedFixed = 0x0D
+ encUnsignedFixed = 0x0E
+ encDecimalFloat = 0x0F
+ // The following are new in DWARF 4.
+ encUTF = 0x10
+ // The following are new in DWARF 5.
+ encUCS = 0x11
+ encASCII = 0x12
+)
+
+// Statement program standard opcode encodings.
+const (
+ 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
+)
+
+// Statement program extended opcode encodings.
+const (
+ lneEndSequence = 1
+ lneSetAddress = 2
+ lneDefineFile = 3
+
+ // DWARF 4
+ lneSetDiscriminator = 4
+)
+
+// Line table directory and file name entry formats.
+// These are new in DWARF 5.
+const (
+ lnctPath = 0x01
+ lnctDirectoryIndex = 0x02
+ lnctTimestamp = 0x03
+ lnctSize = 0x04
+ lnctMD5 = 0x05
+)
+
+// Location list entry codes.
+// These are new in DWARF 5.
+const (
+ lleEndOfList = 0x00
+ lleBaseAddressx = 0x01
+ lleStartxEndx = 0x02
+ lleStartxLength = 0x03
+ lleOffsetPair = 0x04
+ lleDefaultLocation = 0x05
+ lleBaseAddress = 0x06
+ lleStartEnd = 0x07
+ lleStartLength = 0x08
+)
+
+// Unit header unit type encodings.
+// These are new in DWARF 5.
+const (
+ utCompile = 0x01
+ utType = 0x02
+ utPartial = 0x03
+ utSkeleton = 0x04
+ utSplitCompile = 0x05
+ utSplitType = 0x06
+)
+
+// Opcodes for DWARFv5 debug_rnglists section.
+const (
+ rleEndOfList = 0x0
+ rleBaseAddressx = 0x1
+ rleStartxEndx = 0x2
+ rleStartxLength = 0x3
+ rleOffsetPair = 0x4
+ rleBaseAddress = 0x5
+ rleStartEnd = 0x6
+ rleStartLength = 0x7
+)
diff --git a/src/debug/dwarf/dwarf5ranges_test.go b/src/debug/dwarf/dwarf5ranges_test.go
new file mode 100644
index 0000000..8bc50bc
--- /dev/null
+++ b/src/debug/dwarf/dwarf5ranges_test.go
@@ -0,0 +1,41 @@
+// Copyright 2020 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
+
+import (
+ "encoding/binary"
+ "os"
+ "reflect"
+ "testing"
+)
+
+func TestDwarf5Ranges(t *testing.T) {
+ rngLists, err := os.ReadFile("testdata/debug_rnglists")
+ if err != nil {
+ t.Fatalf("could not read test data: %v", err)
+ }
+
+ d := &Data{}
+ d.order = binary.LittleEndian
+ if err := d.AddSection(".debug_rnglists", rngLists); err != nil {
+ t.Fatal(err)
+ }
+ u := &unit{
+ asize: 8,
+ vers: 5,
+ is64: true,
+ }
+ ret, err := d.dwarf5Ranges(u, nil, 0x5fbd, 0xc, [][2]uint64{})
+ if err != nil {
+ t.Fatalf("could not read rnglist: %v", err)
+ }
+ t.Logf("%#v", ret)
+
+ tgt := [][2]uint64{{0x0000000000006712, 0x000000000000679f}, {0x00000000000067af}, {0x00000000000067b3}}
+
+ if reflect.DeepEqual(ret, tgt) {
+ t.Errorf("expected %#v got %#x", tgt, ret)
+ }
+}
diff --git a/src/debug/dwarf/entry.go b/src/debug/dwarf/entry.go
new file mode 100644
index 0000000..4541d74
--- /dev/null
+++ b/src/debug/dwarf/entry.go
@@ -0,0 +1,1221 @@
+// Copyright 2009 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 debug information entry parser.
+// An entry is a sequence of data items of a given format.
+// The first word in the entry is an index into what DWARF
+// calls the ``abbreviation table.'' An abbreviation is really
+// just a type descriptor: it's an array of attribute tag/value format pairs.
+
+package dwarf
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "strconv"
+)
+
+// a single entry's description: a sequence of attributes
+type abbrev struct {
+ tag Tag
+ children bool
+ field []afield
+}
+
+type afield struct {
+ attr Attr
+ fmt format
+ class Class
+ val int64 // for formImplicitConst
+}
+
+// a map from entry format ids to their descriptions
+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 uint64, vers int) (abbrevTable, error) {
+ if m, ok := d.abbrevCache[off]; ok {
+ return m, nil
+ }
+
+ data := d.abbrev
+ if off > uint64(len(data)) {
+ data = nil
+ } else {
+ data = data[off:]
+ }
+ b := makeBuf(d, unknownFormat{}, "abbrev", 0, data)
+
+ // Error handling is simplified by the buf getters
+ // returning an endless stream of 0s after an error.
+ m := make(abbrevTable)
+ for {
+ // Table ends with id == 0.
+ id := uint32(b.uint())
+ if id == 0 {
+ break
+ }
+
+ // Walk over attributes, counting.
+ n := 0
+ b1 := b // Read from copy of b.
+ b1.uint()
+ b1.uint8()
+ for {
+ tag := b1.uint()
+ fmt := b1.uint()
+ if tag == 0 && fmt == 0 {
+ break
+ }
+ if format(fmt) == formImplicitConst {
+ b1.int()
+ }
+ n++
+ }
+ if b1.err != nil {
+ return nil, b1.err
+ }
+
+ // Walk over attributes again, this time writing them down.
+ var a abbrev
+ a.tag = Tag(b.uint())
+ a.children = b.uint8() != 0
+ a.field = make([]afield, n)
+ 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)
+ if a.field[i].fmt == formImplicitConst {
+ a.field[i].val = b.int()
+ }
+ }
+ b.uint()
+ b.uint()
+
+ m[id] = a
+ }
+ if b.err != nil {
+ return nil, b.err
+ }
+ d.abbrevCache[off] = m
+ 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,
+ // The following are new in DWARF 5.
+ AttrStrOffsetsBase: ClassStrOffsetsPtr,
+ AttrAddrBase: ClassAddrPtr,
+ AttrRnglistsBase: ClassRngListsPtr,
+ AttrLoclistsBase: ClassLocListPtr,
+}
+
+// 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 formIndirect:
+ return ClassUnknown
+
+ case formAddr, formAddrx, formAddrx1, formAddrx2, formAddrx3, formAddrx4:
+ 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, formData16, formImplicitConst:
+ // 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, formRefSup4, formRefSup8:
+ return ClassReference
+
+ case formRefSig8:
+ return ClassReferenceSig
+
+ case formString, formStrp, formStrx, formStrpSup, formLineStrp, formStrx1, formStrx2, formStrx3, formStrx4:
+ 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
+ }
+ return ClassUnknown
+
+ case formExprloc:
+ return ClassExprLoc
+
+ case formGnuRefAlt:
+ return ClassReferenceAlt
+
+ case formGnuStrpAlt:
+ return ClassStringAlt
+
+ case formLoclistx:
+ return ClassLocList
+
+ case formRnglistx:
+ return ClassRngList
+ }
+}
+
+// An entry is a sequence of attribute/value pairs.
+type Entry struct {
+ Offset Offset // offset of Entry in DWARF info
+ Tag Tag // tag (kind of Entry)
+ Children bool // whether Entry is followed by children
+ Field []Field
+}
+
+// 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
+//
+// For unrecognized or vendor-defined attributes, [Class] may be
+// [ClassUnknown].
+type Field struct {
+ Attr Attr
+ Val any
+ Class Class
+}
+
+// A Class is the DWARF 4 class of an attribute 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 (
+ // ClassUnknown represents values of unknown DWARF class.
+ ClassUnknown Class = iota
+
+ // ClassAddress represents values of type uint64 that are
+ // addresses on the target machine.
+ ClassAddress
+
+ // 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
+
+ // ClassRangeListPtr 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
+
+ // ClassAddrPtr represents values that are an int64 offset
+ // into the "addr" section.
+ ClassAddrPtr
+
+ // ClassLocList represents values that are an int64 offset
+ // into the "loclists" section.
+ ClassLocList
+
+ // ClassRngList represents values that are a uint64 offset
+ // from the base of the "rnglists" section.
+ ClassRngList
+
+ // ClassRngListsPtr represents values that are an int64 offset
+ // into the "rnglists" section. These are used as the base for
+ // ClassRngList values.
+ ClassRngListsPtr
+
+ // ClassStrOffsetsPtr represents values that are an int64
+ // offset into the "str_offsets" section.
+ ClassStrOffsetsPtr
+)
+
+//go:generate stringer -type=Class
+
+func (i Class) GoString() string {
+ return "dwarf." + i.String()
+}
+
+// Val returns the value associated with attribute [Attr] in [Entry],
+// or nil if there is no such attribute.
+//
+// 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)
+func (e *Entry) Val(a Attr) any {
+ 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 &e.Field[i]
+ }
+ }
+ return nil
+}
+
+// An Offset represents the location of an [Entry] within the DWARF info.
+// (See [Reader.Seek].)
+type Offset uint32
+
+// Entry reads a single entry from buf, decoding
+// according to the given abbreviation table.
+func (b *buf) entry(cu *Entry, atab abbrevTable, ubase Offset, vers int) *Entry {
+ off := b.off
+ id := uint32(b.uint())
+ if id == 0 {
+ return &Entry{}
+ }
+ a, ok := atab[id]
+ if !ok {
+ b.error("unknown abbreviation table index")
+ return nil
+ }
+ e := &Entry{
+ Offset: off,
+ Tag: a.tag,
+ Children: a.children,
+ Field: make([]Field, len(a.field)),
+ }
+
+ // If we are currently parsing the compilation unit,
+ // we can't evaluate Addrx or Strx until we've seen the
+ // relevant base entry.
+ type delayed struct {
+ idx int
+ off uint64
+ fmt format
+ }
+ var delay []delayed
+
+ resolveStrx := func(strBase, off uint64) string {
+ off += strBase
+ if uint64(int(off)) != off {
+ b.error("DW_FORM_strx offset out of range")
+ }
+
+ b1 := makeBuf(b.dwarf, b.format, "str_offsets", 0, b.dwarf.strOffsets)
+ b1.skip(int(off))
+ is64, _ := b.format.dwarf64()
+ if is64 {
+ off = b1.uint64()
+ } else {
+ off = uint64(b1.uint32())
+ }
+ if b1.err != nil {
+ b.err = b1.err
+ return ""
+ }
+ if uint64(int(off)) != off {
+ b.error("DW_FORM_strx indirect offset out of range")
+ }
+ b1 = makeBuf(b.dwarf, b.format, "str", 0, b.dwarf.str)
+ b1.skip(int(off))
+ val := b1.string()
+ if b1.err != nil {
+ b.err = b1.err
+ }
+ return val
+ }
+
+ resolveRnglistx := func(rnglistsBase, off uint64) uint64 {
+ is64, _ := b.format.dwarf64()
+ if is64 {
+ off *= 8
+ } else {
+ off *= 4
+ }
+ off += rnglistsBase
+ if uint64(int(off)) != off {
+ b.error("DW_FORM_rnglistx offset out of range")
+ }
+
+ b1 := makeBuf(b.dwarf, b.format, "rnglists", 0, b.dwarf.rngLists)
+ b1.skip(int(off))
+ if is64 {
+ off = b1.uint64()
+ } else {
+ off = uint64(b1.uint32())
+ }
+ if b1.err != nil {
+ b.err = b1.err
+ return 0
+ }
+ if uint64(int(off)) != off {
+ b.error("DW_FORM_rnglistx indirect offset out of range")
+ }
+ return rnglistsBase + off
+ }
+
+ 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())
+ e.Field[i].Class = formToClass(fmt, a.field[i].attr, vers, b)
+ }
+ var val any
+ switch fmt {
+ default:
+ b.error("unknown entry attr format 0x" + strconv.FormatInt(int64(fmt), 16))
+
+ // address
+ case formAddr:
+ val = b.addr()
+ case formAddrx, formAddrx1, formAddrx2, formAddrx3, formAddrx4:
+ var off uint64
+ switch fmt {
+ case formAddrx:
+ off = b.uint()
+ case formAddrx1:
+ off = uint64(b.uint8())
+ case formAddrx2:
+ off = uint64(b.uint16())
+ case formAddrx3:
+ off = uint64(b.uint24())
+ case formAddrx4:
+ off = uint64(b.uint32())
+ }
+ if b.dwarf.addr == nil {
+ b.error("DW_FORM_addrx with no .debug_addr section")
+ }
+ if b.err != nil {
+ return nil
+ }
+
+ // We have to adjust by the offset of the
+ // compilation unit. This won't work if the
+ // program uses Reader.Seek to skip over the
+ // unit. Not much we can do about that.
+ var addrBase int64
+ if cu != nil {
+ addrBase, _ = cu.Val(AttrAddrBase).(int64)
+ } else if a.tag == TagCompileUnit {
+ delay = append(delay, delayed{i, off, formAddrx})
+ break
+ }
+
+ var err error
+ val, err = b.dwarf.debugAddr(b.format, uint64(addrBase), off)
+ if err != nil {
+ if b.err == nil {
+ b.err = err
+ }
+ return nil
+ }
+
+ // block
+ case formDwarfBlock1:
+ val = b.bytes(int(b.uint8()))
+ case formDwarfBlock2:
+ val = b.bytes(int(b.uint16()))
+ case formDwarfBlock4:
+ val = b.bytes(int(b.uint32()))
+ case formDwarfBlock:
+ val = b.bytes(int(b.uint()))
+
+ // constant
+ case formData1:
+ val = int64(b.uint8())
+ case formData2:
+ val = int64(b.uint16())
+ case formData4:
+ val = int64(b.uint32())
+ case formData8:
+ val = int64(b.uint64())
+ case formData16:
+ val = b.bytes(16)
+ case formSdata:
+ val = int64(b.int())
+ case formUdata:
+ val = int64(b.uint())
+ case formImplicitConst:
+ val = a.field[i].val
+
+ // flag
+ case formFlag:
+ val = b.uint8() == 1
+ // New in DWARF 4.
+ case formFlagPresent:
+ // The attribute is implicitly indicated as present, and no value is
+ // encoded in the debugging information entry itself.
+ val = true
+
+ // reference to other entry
+ case formRefAddr:
+ vers := b.format.version()
+ if vers == 0 {
+ b.error("unknown version for DW_FORM_ref_addr")
+ } else if vers == 2 {
+ val = Offset(b.addr())
+ } else {
+ is64, known := b.format.dwarf64()
+ if !known {
+ b.error("unknown size for DW_FORM_ref_addr")
+ } else if is64 {
+ val = Offset(b.uint64())
+ } else {
+ val = Offset(b.uint32())
+ }
+ }
+ case formRef1:
+ val = Offset(b.uint8()) + ubase
+ case formRef2:
+ val = Offset(b.uint16()) + ubase
+ case formRef4:
+ val = Offset(b.uint32()) + ubase
+ case formRef8:
+ val = Offset(b.uint64()) + ubase
+ case formRefUdata:
+ val = Offset(b.uint()) + ubase
+
+ // string
+ case formString:
+ val = b.string()
+ case formStrp, formLineStrp:
+ var off uint64 // offset into .debug_str
+ is64, known := b.format.dwarf64()
+ if !known {
+ b.error("unknown size for DW_FORM_strp/line_strp")
+ } else if is64 {
+ off = b.uint64()
+ } else {
+ off = uint64(b.uint32())
+ }
+ if uint64(int(off)) != off {
+ b.error("DW_FORM_strp/line_strp offset out of range")
+ }
+ if b.err != nil {
+ return nil
+ }
+ var b1 buf
+ if fmt == formStrp {
+ b1 = makeBuf(b.dwarf, b.format, "str", 0, b.dwarf.str)
+ } else {
+ if len(b.dwarf.lineStr) == 0 {
+ b.error("DW_FORM_line_strp with no .debug_line_str section")
+ return nil
+ }
+ b1 = makeBuf(b.dwarf, b.format, "line_str", 0, b.dwarf.lineStr)
+ }
+ b1.skip(int(off))
+ val = b1.string()
+ if b1.err != nil {
+ b.err = b1.err
+ return nil
+ }
+ case formStrx, formStrx1, formStrx2, formStrx3, formStrx4:
+ var off uint64
+ switch fmt {
+ case formStrx:
+ off = b.uint()
+ case formStrx1:
+ off = uint64(b.uint8())
+ case formStrx2:
+ off = uint64(b.uint16())
+ case formStrx3:
+ off = uint64(b.uint24())
+ case formStrx4:
+ off = uint64(b.uint32())
+ }
+ if len(b.dwarf.strOffsets) == 0 {
+ b.error("DW_FORM_strx with no .debug_str_offsets section")
+ }
+ is64, known := b.format.dwarf64()
+ if !known {
+ b.error("unknown offset size for DW_FORM_strx")
+ }
+ if b.err != nil {
+ return nil
+ }
+ if is64 {
+ off *= 8
+ } else {
+ off *= 4
+ }
+
+ // We have to adjust by the offset of the
+ // compilation unit. This won't work if the
+ // program uses Reader.Seek to skip over the
+ // unit. Not much we can do about that.
+ var strBase int64
+ if cu != nil {
+ strBase, _ = cu.Val(AttrStrOffsetsBase).(int64)
+ } else if a.tag == TagCompileUnit {
+ delay = append(delay, delayed{i, off, formStrx})
+ break
+ }
+
+ val = resolveStrx(uint64(strBase), off)
+
+ case formStrpSup:
+ is64, known := b.format.dwarf64()
+ if !known {
+ b.error("unknown size for DW_FORM_strp_sup")
+ } else if is64 {
+ val = b.uint64()
+ } else {
+ val = b.uint32()
+ }
+
+ // lineptr, loclistptr, macptr, rangelistptr
+ // New in DWARF 4, but clang can generate them with -gdwarf-2.
+ // Section reference, replacing use of formData4 and formData8.
+ case formSecOffset, formGnuRefAlt, formGnuStrpAlt:
+ is64, known := b.format.dwarf64()
+ if !known {
+ b.error("unknown size for form 0x" + strconv.FormatInt(int64(fmt), 16))
+ } else if is64 {
+ val = int64(b.uint64())
+ } else {
+ val = int64(b.uint32())
+ }
+
+ // exprloc
+ // New in DWARF 4.
+ case formExprloc:
+ val = b.bytes(int(b.uint()))
+
+ // reference
+ // New in DWARF 4.
+ case formRefSig8:
+ // 64-bit type signature.
+ val = b.uint64()
+ case formRefSup4:
+ val = b.uint32()
+ case formRefSup8:
+ val = b.uint64()
+
+ // loclist
+ case formLoclistx:
+ val = b.uint()
+
+ // rnglist
+ case formRnglistx:
+ off := b.uint()
+
+ // We have to adjust by the rnglists_base of
+ // the compilation unit. This won't work if
+ // the program uses Reader.Seek to skip over
+ // the unit. Not much we can do about that.
+ var rnglistsBase int64
+ if cu != nil {
+ rnglistsBase, _ = cu.Val(AttrRnglistsBase).(int64)
+ } else if a.tag == TagCompileUnit {
+ delay = append(delay, delayed{i, off, formRnglistx})
+ break
+ }
+
+ val = resolveRnglistx(uint64(rnglistsBase), off)
+ }
+
+ e.Field[i].Val = val
+ }
+ if b.err != nil {
+ return nil
+ }
+
+ for _, del := range delay {
+ switch del.fmt {
+ case formAddrx:
+ addrBase, _ := e.Val(AttrAddrBase).(int64)
+ val, err := b.dwarf.debugAddr(b.format, uint64(addrBase), del.off)
+ if err != nil {
+ b.err = err
+ return nil
+ }
+ e.Field[del.idx].Val = val
+ case formStrx:
+ strBase, _ := e.Val(AttrStrOffsetsBase).(int64)
+ e.Field[del.idx].Val = resolveStrx(uint64(strBase), del.off)
+ if b.err != nil {
+ return nil
+ }
+ case formRnglistx:
+ rnglistsBase, _ := e.Val(AttrRnglistsBase).(int64)
+ e.Field[del.idx].Val = resolveRnglistx(uint64(rnglistsBase), del.off)
+ if b.err != nil {
+ return nil
+ }
+ }
+ }
+
+ return e
+}
+
+// A Reader allows reading [Entry] structures from a DWARF “info” section.
+// The [Entry] structures are arranged in a tree. The [Reader.Next] function
+// return successive entries from a pre-order traversal of the tree.
+// If an entry has children, its Children field will be true, and the children
+// follow, terminated by an [Entry] with [Tag] 0.
+type Reader struct {
+ b buf
+ d *Data
+ err error
+ unit int
+ lastUnit bool // set if last entry returned by Next is TagCompileUnit/TagPartialUnit
+ lastChildren bool // .Children of last entry returned by Next
+ lastSibling Offset // .Val(AttrSibling) of last entry returned by Next
+ cu *Entry // current compilation unit
+}
+
+// Reader returns a new Reader for [Data].
+// The reader is positioned at byte offset 0 in the DWARF “info” section.
+func (d *Data) Reader() *Reader {
+ r := &Reader{d: d}
+ r.Seek(0)
+ 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
+}
+
+// ByteOrder returns the byte order in the current compilation unit.
+func (r *Reader) ByteOrder() binary.ByteOrder {
+ return r.b.order
+}
+
+// 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) {
+ d := r.d
+ r.err = nil
+ r.lastChildren = false
+ if off == 0 {
+ if len(d.unit) == 0 {
+ return
+ }
+ u := &d.unit[0]
+ r.unit = 0
+ r.b = makeBuf(r.d, u, "info", u.off, u.data)
+ r.cu = nil
+ return
+ }
+
+ i := d.offsetToUnit(off)
+ if i == -1 {
+ r.err = errors.New("offset out of range")
+ return
+ }
+ if i != r.unit {
+ r.cu = nil
+ }
+ 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.
+func (r *Reader) maybeNextUnit() {
+ for len(r.b.data) == 0 && r.unit+1 < len(r.d.unit) {
+ r.nextUnit()
+ }
+}
+
+// nextUnit advances to the next unit.
+func (r *Reader) nextUnit() {
+ r.unit++
+ u := &r.d.unit[r.unit]
+ r.b = makeBuf(r.d, u, "info", u.off, u.data)
+ r.cu = nil
+}
+
+// Next reads the next entry from the encoded entry stream.
+// It returns nil, nil when it reaches the end of the section.
+// It returns an error if the current offset is invalid or the data at the
+// offset cannot be decoded as a valid [Entry].
+func (r *Reader) Next() (*Entry, error) {
+ if r.err != nil {
+ return nil, r.err
+ }
+ r.maybeNextUnit()
+ if len(r.b.data) == 0 {
+ return nil, nil
+ }
+ u := &r.d.unit[r.unit]
+ e := r.b.entry(r.cu, u.atable, u.base, u.vers)
+ if r.b.err != nil {
+ r.err = r.b.err
+ return nil, r.err
+ }
+ r.lastUnit = false
+ if e != nil {
+ r.lastChildren = e.Children
+ if r.lastChildren {
+ r.lastSibling, _ = e.Val(AttrSibling).(Offset)
+ }
+ if e.Tag == TagCompileUnit || e.Tag == TagPartialUnit {
+ r.lastUnit = true
+ r.cu = e
+ }
+ } else {
+ r.lastChildren = false
+ }
+ return e, nil
+}
+
+// SkipChildren skips over the child entries associated with
+// the last [Entry] returned by [Reader.Next]. If that [Entry] did not have
+// children or [Reader.Next] has not been called, SkipChildren is a no-op.
+func (r *Reader) SkipChildren() {
+ if r.err != nil || !r.lastChildren {
+ return
+ }
+
+ // If the last entry had a sibling attribute,
+ // that attribute gives the offset of the next
+ // sibling, so we can avoid decoding the
+ // child subtrees.
+ if r.lastSibling >= r.b.off {
+ r.Seek(r.lastSibling)
+ return
+ }
+
+ if r.lastUnit && r.unit+1 < len(r.d.unit) {
+ r.nextUnit()
+ return
+ }
+
+ for {
+ e, err := r.Next()
+ if err != nil || e == nil || e.Tag == 0 {
+ break
+ }
+ if e.Children {
+ r.SkipChildren()
+ }
+ }
+}
+
+// clone returns a copy of the reader. This is used by the typeReader
+// interface.
+func (r *Reader) clone() typeReader {
+ return r.d.Reader()
+}
+
+// offset returns the current buffer offset. This is used by the
+// typeReader interface.
+func (r *Reader) offset() Offset {
+ return r.b.off
+}
+
+// SeekPC returns the [Entry] for the compilation unit that includes pc,
+// and positions the reader to read the children of that unit. If pc
+// is not covered by any unit, SeekPC returns [ErrUnknownPC] and the
+// position of the reader is undefined.
+//
+// Because compilation units can describe multiple regions of the
+// executable, in the worst case SeekPC must search through all the
+// ranges in all the compilation units. Each call to SeekPC starts the
+// search at the compilation unit of the last call, so in general
+// looking up a series of PCs will be faster if they are sorted. If
+// the caller wishes to do repeated fast PC lookups, it should build
+// an appropriate index using the Ranges method.
+func (r *Reader) SeekPC(pc uint64) (*Entry, error) {
+ unit := r.unit
+ for i := 0; i < len(r.d.unit); i++ {
+ if unit >= len(r.d.unit) {
+ unit = 0
+ }
+ r.err = nil
+ r.lastChildren = false
+ r.unit = unit
+ r.cu = nil
+ u := &r.d.unit[unit]
+ r.b = makeBuf(r.d, u, "info", u.off, u.data)
+ e, err := r.Next()
+ if err != nil {
+ return nil, err
+ }
+ if e == nil || e.Tag == 0 {
+ return nil, ErrUnknownPC
+ }
+ ranges, err := r.d.Ranges(e)
+ if err != nil {
+ return nil, err
+ }
+ for _, pcs := range ranges {
+ if pcs[0] <= pc && pc < pcs[1] {
+ return e, nil
+ }
+ }
+ unit++
+ }
+ return nil, ErrUnknownPC
+}
+
+// Ranges returns the PC ranges covered by e, a slice of [low,high) pairs.
+// Only some entry types, such as [TagCompileUnit] or [TagSubprogram], have PC
+// ranges; for others, this will return nil with no error.
+func (d *Data) Ranges(e *Entry) ([][2]uint64, error) {
+ var ret [][2]uint64
+
+ low, lowOK := e.Val(AttrLowpc).(uint64)
+
+ var high uint64
+ var highOK bool
+ highField := e.AttrField(AttrHighpc)
+ if highField != nil {
+ switch highField.Class {
+ case ClassAddress:
+ high, highOK = highField.Val.(uint64)
+ case ClassConstant:
+ off, ok := highField.Val.(int64)
+ if ok {
+ high = low + uint64(off)
+ highOK = true
+ }
+ }
+ }
+
+ if lowOK && highOK {
+ ret = append(ret, [2]uint64{low, high})
+ }
+
+ var u *unit
+ if uidx := d.offsetToUnit(e.Offset); uidx >= 0 && uidx < len(d.unit) {
+ u = &d.unit[uidx]
+ }
+
+ if u != nil && u.vers >= 5 && d.rngLists != nil {
+ // DWARF version 5 and later
+ field := e.AttrField(AttrRanges)
+ if field == nil {
+ return ret, nil
+ }
+ switch field.Class {
+ case ClassRangeListPtr:
+ ranges, rangesOK := field.Val.(int64)
+ if !rangesOK {
+ return ret, nil
+ }
+ cu, base, err := d.baseAddressForEntry(e)
+ if err != nil {
+ return nil, err
+ }
+ return d.dwarf5Ranges(u, cu, base, ranges, ret)
+
+ case ClassRngList:
+ rnglist, ok := field.Val.(uint64)
+ if !ok {
+ return ret, nil
+ }
+ cu, base, err := d.baseAddressForEntry(e)
+ if err != nil {
+ return nil, err
+ }
+ return d.dwarf5Ranges(u, cu, base, int64(rnglist), ret)
+
+ default:
+ return ret, nil
+ }
+ }
+
+ // DWARF version 2 through 4
+ ranges, rangesOK := e.Val(AttrRanges).(int64)
+ if rangesOK && d.ranges != nil {
+ _, base, err := d.baseAddressForEntry(e)
+ if err != nil {
+ return nil, err
+ }
+ return d.dwarf2Ranges(u, base, ranges, ret)
+ }
+
+ return ret, nil
+}
+
+// baseAddressForEntry returns the initial base address to be used when
+// looking up the range list of entry e.
+// DWARF specifies that this should be the lowpc attribute of the enclosing
+// compilation unit, however comments in gdb/dwarf2read.c say that some
+// versions of GCC use the entrypc attribute, so we check that too.
+func (d *Data) baseAddressForEntry(e *Entry) (*Entry, uint64, error) {
+ var cu *Entry
+ if e.Tag == TagCompileUnit {
+ cu = e
+ } else {
+ i := d.offsetToUnit(e.Offset)
+ if i == -1 {
+ return nil, 0, errors.New("no unit for entry")
+ }
+ u := &d.unit[i]
+ b := makeBuf(d, u, "info", u.off, u.data)
+ cu = b.entry(nil, u.atable, u.base, u.vers)
+ if b.err != nil {
+ return nil, 0, b.err
+ }
+ }
+
+ if cuEntry, cuEntryOK := cu.Val(AttrEntrypc).(uint64); cuEntryOK {
+ return cu, cuEntry, nil
+ } else if cuLow, cuLowOK := cu.Val(AttrLowpc).(uint64); cuLowOK {
+ return cu, cuLow, nil
+ }
+
+ return cu, 0, nil
+}
+
+func (d *Data) dwarf2Ranges(u *unit, base uint64, ranges int64, ret [][2]uint64) ([][2]uint64, error) {
+ if ranges < 0 || ranges > int64(len(d.ranges)) {
+ return nil, fmt.Errorf("invalid range offset %d (max %d)", ranges, len(d.ranges))
+ }
+ buf := makeBuf(d, u, "ranges", Offset(ranges), d.ranges[ranges:])
+ for len(buf.data) > 0 {
+ low := buf.addr()
+ high := buf.addr()
+
+ if low == 0 && high == 0 {
+ break
+ }
+
+ if low == ^uint64(0)>>uint((8-u.addrsize())*8) {
+ base = high
+ } else {
+ ret = append(ret, [2]uint64{base + low, base + high})
+ }
+ }
+
+ return ret, nil
+}
+
+// dwarf5Ranges interprets a debug_rnglists sequence, see DWARFv5 section
+// 2.17.3 (page 53).
+func (d *Data) dwarf5Ranges(u *unit, cu *Entry, base uint64, ranges int64, ret [][2]uint64) ([][2]uint64, error) {
+ if ranges < 0 || ranges > int64(len(d.rngLists)) {
+ return nil, fmt.Errorf("invalid rnglist offset %d (max %d)", ranges, len(d.ranges))
+ }
+ var addrBase int64
+ if cu != nil {
+ addrBase, _ = cu.Val(AttrAddrBase).(int64)
+ }
+
+ buf := makeBuf(d, u, "rnglists", 0, d.rngLists)
+ buf.skip(int(ranges))
+ for {
+ opcode := buf.uint8()
+ switch opcode {
+ case rleEndOfList:
+ if buf.err != nil {
+ return nil, buf.err
+ }
+ return ret, nil
+
+ case rleBaseAddressx:
+ baseIdx := buf.uint()
+ var err error
+ base, err = d.debugAddr(u, uint64(addrBase), baseIdx)
+ if err != nil {
+ return nil, err
+ }
+
+ case rleStartxEndx:
+ startIdx := buf.uint()
+ endIdx := buf.uint()
+
+ start, err := d.debugAddr(u, uint64(addrBase), startIdx)
+ if err != nil {
+ return nil, err
+ }
+ end, err := d.debugAddr(u, uint64(addrBase), endIdx)
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, [2]uint64{start, end})
+
+ case rleStartxLength:
+ startIdx := buf.uint()
+ len := buf.uint()
+ start, err := d.debugAddr(u, uint64(addrBase), startIdx)
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, [2]uint64{start, start + len})
+
+ case rleOffsetPair:
+ off1 := buf.uint()
+ off2 := buf.uint()
+ ret = append(ret, [2]uint64{base + off1, base + off2})
+
+ case rleBaseAddress:
+ base = buf.addr()
+
+ case rleStartEnd:
+ start := buf.addr()
+ end := buf.addr()
+ ret = append(ret, [2]uint64{start, end})
+
+ case rleStartLength:
+ start := buf.addr()
+ len := buf.uint()
+ ret = append(ret, [2]uint64{start, start + len})
+ }
+ }
+}
+
+// debugAddr returns the address at idx in debug_addr
+func (d *Data) debugAddr(format dataFormat, addrBase, idx uint64) (uint64, error) {
+ off := idx*uint64(format.addrsize()) + addrBase
+
+ if uint64(int(off)) != off {
+ return 0, errors.New("offset out of range")
+ }
+
+ b := makeBuf(d, format, "addr", 0, d.addr)
+ b.skip(int(off))
+ val := b.addr()
+ if b.err != nil {
+ return 0, b.err
+ }
+ return val, nil
+}
diff --git a/src/debug/dwarf/entry_test.go b/src/debug/dwarf/entry_test.go
new file mode 100644
index 0000000..1ce1c98
--- /dev/null
+++ b/src/debug/dwarf/entry_test.go
@@ -0,0 +1,459 @@
+// Copyright 2009 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"
+ "encoding/binary"
+ "path/filepath"
+ "reflect"
+ "testing"
+)
+
+func TestSplit(t *testing.T) {
+ // debug/dwarf doesn't (currently) support split DWARF, but
+ // the attributes that pointed to the split DWARF used to
+ // cause loading the DWARF data to fail entirely (issue
+ // #12592). Test that we can at least read the DWARF data.
+ d := elfData(t, "testdata/split.elf")
+ r := d.Reader()
+ e, err := r.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if e.Tag != TagCompileUnit {
+ t.Fatalf("bad tag: have %s, want %s", e.Tag, TagCompileUnit)
+ }
+ // Check that we were able to parse the unknown section offset
+ // field, even if we can't figure out its DWARF class.
+ const AttrGNUAddrBase Attr = 0x2133
+ f := e.AttrField(AttrGNUAddrBase)
+ if _, ok := f.Val.(int64); !ok {
+ t.Fatalf("bad attribute value type: have %T, want int64", f.Val)
+ }
+ if f.Class != ClassUnknown {
+ t.Fatalf("bad class: have %s, want %s", f.Class, ClassUnknown)
+ }
+}
+
+// wantRange maps from a PC to the ranges of the compilation unit
+// containing that PC.
+type wantRange struct {
+ pc uint64
+ ranges [][2]uint64
+}
+
+func TestReaderSeek(t *testing.T) {
+ want := []wantRange{
+ {0x40059d, [][2]uint64{{0x40059d, 0x400601}}},
+ {0x400600, [][2]uint64{{0x40059d, 0x400601}}},
+ {0x400601, [][2]uint64{{0x400601, 0x400611}}},
+ {0x4005f0, [][2]uint64{{0x40059d, 0x400601}}}, // loop test
+ {0x10, nil},
+ {0x400611, nil},
+ }
+ testRanges(t, "testdata/line-gcc.elf", want)
+
+ want = []wantRange{
+ {0x401122, [][2]uint64{{0x401122, 0x401166}}},
+ {0x401165, [][2]uint64{{0x401122, 0x401166}}},
+ {0x401166, [][2]uint64{{0x401166, 0x401179}}},
+ }
+ testRanges(t, "testdata/line-gcc-dwarf5.elf", want)
+
+ want = []wantRange{
+ {0x401130, [][2]uint64{{0x401130, 0x40117e}}},
+ {0x40117d, [][2]uint64{{0x401130, 0x40117e}}},
+ {0x40117e, nil},
+ }
+ testRanges(t, "testdata/line-clang-dwarf5.elf", want)
+
+ want = []wantRange{
+ {0x401126, [][2]uint64{{0x401126, 0x40116a}}},
+ {0x40116a, [][2]uint64{{0x40116a, 0x401180}}},
+ }
+ testRanges(t, "testdata/line-gcc-zstd.elf", want)
+}
+
+func TestRangesSection(t *testing.T) {
+ want := []wantRange{
+ {0x400500, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
+ {0x400400, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
+ {0x400548, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
+ {0x400407, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
+ {0x400408, nil},
+ {0x400449, nil},
+ {0x4003ff, nil},
+ }
+ testRanges(t, "testdata/ranges.elf", want)
+}
+
+func TestRangesRnglistx(t *testing.T) {
+ want := []wantRange{
+ {0x401000, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}},
+ {0x40101c, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}},
+ {0x40101d, nil},
+ {0x40101f, nil},
+ {0x401020, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}},
+ {0x40102b, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}},
+ {0x40102c, nil},
+ }
+ testRanges(t, "testdata/rnglistx.elf", want)
+}
+
+func testRanges(t *testing.T, name string, want []wantRange) {
+ d := elfData(t, name)
+ r := d.Reader()
+ for _, w := range want {
+ entry, err := r.SeekPC(w.pc)
+ if err != nil {
+ if w.ranges != nil {
+ t.Errorf("%s: missing Entry for %#x", name, w.pc)
+ }
+ if err != ErrUnknownPC {
+ t.Errorf("%s: expected ErrUnknownPC for %#x, got %v", name, w.pc, err)
+ }
+ continue
+ }
+
+ ranges, err := d.Ranges(entry)
+ if err != nil {
+ t.Errorf("%s: %v", name, err)
+ continue
+ }
+ if !reflect.DeepEqual(ranges, w.ranges) {
+ t.Errorf("%s: for %#x got %x, expected %x", name, w.pc, ranges, w.ranges)
+ }
+ }
+}
+
+func TestReaderRanges(t *testing.T) {
+ type subprograms []struct {
+ name string
+ ranges [][2]uint64
+ }
+ tests := []struct {
+ filename string
+ subprograms subprograms
+ }{
+ {
+ "testdata/line-gcc.elf",
+ subprograms{
+ {"f1", [][2]uint64{{0x40059d, 0x4005e7}}},
+ {"main", [][2]uint64{{0x4005e7, 0x400601}}},
+ {"f2", [][2]uint64{{0x400601, 0x400611}}},
+ },
+ },
+ {
+ "testdata/line-gcc-dwarf5.elf",
+ subprograms{
+ {"main", [][2]uint64{{0x401147, 0x401166}}},
+ {"f1", [][2]uint64{{0x401122, 0x401147}}},
+ {"f2", [][2]uint64{{0x401166, 0x401179}}},
+ },
+ },
+ {
+ "testdata/line-clang-dwarf5.elf",
+ subprograms{
+ {"main", [][2]uint64{{0x401130, 0x401144}}},
+ {"f1", [][2]uint64{{0x401150, 0x40117e}}},
+ {"f2", [][2]uint64{{0x401180, 0x401197}}},
+ },
+ },
+ {
+ "testdata/line-gcc-zstd.elf",
+ subprograms{
+ {"f2", nil},
+ {"main", [][2]uint64{{0x40114b, 0x40116a}}},
+ {"f1", [][2]uint64{{0x401126, 0x40114b}}},
+ {"f2", [][2]uint64{{0x40116a, 0x401180}}},
+ },
+ },
+ }
+
+ for _, test := range tests {
+ d := elfData(t, test.filename)
+ subprograms := test.subprograms
+
+ r := d.Reader()
+ i := 0
+ for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() {
+ if entry.Tag != TagSubprogram {
+ continue
+ }
+
+ if i > len(subprograms) {
+ t.Fatalf("%s: too many subprograms (expected at most %d)", test.filename, i)
+ }
+
+ if got := entry.Val(AttrName).(string); got != subprograms[i].name {
+ t.Errorf("%s: subprogram %d name is %s, expected %s", test.filename, i, got, subprograms[i].name)
+ }
+ ranges, err := d.Ranges(entry)
+ if err != nil {
+ t.Errorf("%s: subprogram %d: %v", test.filename, i, err)
+ continue
+ }
+ if !reflect.DeepEqual(ranges, subprograms[i].ranges) {
+ t.Errorf("%s: subprogram %d ranges are %x, expected %x", test.filename, i, ranges, subprograms[i].ranges)
+ }
+ i++
+ }
+
+ if i < len(subprograms) {
+ t.Errorf("%s: saw only %d subprograms, expected %d", test.filename, i, len(subprograms))
+ }
+ }
+}
+
+func Test64Bit(t *testing.T) {
+ // I don't know how to generate a 64-bit DWARF debug
+ // compilation unit except by using XCOFF, so this is
+ // hand-written.
+ tests := []struct {
+ name string
+ info []byte
+ addrSize int
+ byteOrder binary.ByteOrder
+ }{
+ {
+ "32-bit little",
+ []byte{0x30, 0, 0, 0, // comp unit length
+ 4, 0, // DWARF version 4
+ 0, 0, 0, 0, // abbrev offset
+ 8, // address size
+ 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ 8, binary.LittleEndian,
+ },
+ {
+ "64-bit little",
+ []byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF
+ 0x30, 0, 0, 0, 0, 0, 0, 0, // comp unit length
+ 4, 0, // DWARF version 4
+ 0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset
+ 8, // address size
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ 8, binary.LittleEndian,
+ },
+ {
+ "64-bit big",
+ []byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF
+ 0, 0, 0, 0, 0, 0, 0, 0x30, // comp unit length
+ 0, 4, // DWARF version 4
+ 0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset
+ 8, // address size
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ 8, binary.BigEndian,
+ },
+ }
+
+ for _, test := range tests {
+ data, err := New(nil, nil, nil, test.info, nil, nil, nil, nil)
+ if err != nil {
+ t.Errorf("%s: %v", test.name, err)
+ }
+
+ r := data.Reader()
+ if r.AddressSize() != test.addrSize {
+ t.Errorf("%s: got address size %d, want %d", test.name, r.AddressSize(), test.addrSize)
+ }
+ if r.ByteOrder() != test.byteOrder {
+ t.Errorf("%s: got byte order %s, want %s", test.name, r.ByteOrder(), test.byteOrder)
+ }
+ }
+}
+
+func TestUnitIteration(t *testing.T) {
+ // Iterate over all ELF test files we have and ensure that
+ // we get the same set of compilation units skipping (method 0)
+ // and not skipping (method 1) CU children.
+ files, err := filepath.Glob(filepath.Join("testdata", "*.elf"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, file := range files {
+ t.Run(file, func(t *testing.T) {
+ d := elfData(t, file)
+ var units [2][]any
+ for method := range units {
+ for r := d.Reader(); ; {
+ ent, err := r.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if ent == nil {
+ break
+ }
+ if ent.Tag == TagCompileUnit {
+ units[method] = append(units[method], ent.Val(AttrName))
+ }
+ if method == 0 {
+ if ent.Tag != TagCompileUnit {
+ t.Fatalf("found unexpected tag %v on top level", ent.Tag)
+ }
+ r.SkipChildren()
+ }
+ }
+ }
+ t.Logf("skipping CUs: %v", units[0])
+ t.Logf("not-skipping CUs: %v", units[1])
+ if !reflect.DeepEqual(units[0], units[1]) {
+ t.Fatal("set of CUs differ")
+ }
+ })
+ }
+}
+
+func TestIssue51758(t *testing.T) {
+ abbrev := []byte{0x21, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5c,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x22, 0x5c,
+ 0x6e, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x3a, 0x20,
+ 0x5c, 0x22, 0x5c, 0x5c, 0x30, 0x30, 0x35, 0x5c, 0x5c, 0x30, 0x30,
+ 0x30, 0x5c, 0x5c, 0x30, 0x30, 0x30, 0x5c, 0x5c, 0x30, 0x30, 0x30,
+ 0x5c, 0x5c, 0x30, 0x30, 0x34, 0x5c, 0x5c, 0x30, 0x30, 0x30, 0x5c,
+ 0x5c, 0x30, 0x30, 0x30, 0x2d, 0x5c, 0x5c, 0x30, 0x30, 0x30, 0x5c,
+ 0x22, 0x5c, 0x6e, 0x20, 0x20, 0x7d, 0x5c, 0x6e, 0x7d, 0x5c, 0x6e,
+ 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65,
+ 0x3a, 0x20, 0x22, 0x21, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x33, 0x37, 0x37, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69,
+ 0x6e, 0x66, 0x6f, 0x3a, 0x20, 0x22, 0x5c, 0x30, 0x30, 0x35, 0x5c,
+ 0x30, 0x30, 0x30, 0x5c, 0x30, 0x30, 0x30, 0x5c, 0x30, 0x30, 0x30,
+ 0x5c, 0x30, 0x30, 0x34, 0x5c, 0x30, 0x30, 0x30, 0x5c, 0x30, 0x30,
+ 0x30, 0x2d, 0x5c, 0x30, 0x30, 0x30, 0x22, 0x0a, 0x20, 0x20, 0x7d,
+ 0x0a, 0x7d, 0x0a, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x7b, 0x0a, 0x7d,
+ 0x0a, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x7b, 0x0a, 0x7d, 0x0a, 0x6c,
+ 0x69, 0x73, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x4e, 0x65, 0x77,
+ 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x62, 0x72,
+ 0x65, 0x76, 0x3a, 0x20, 0x22, 0x5c, 0x30, 0x30, 0x35, 0x5c, 0x30,
+ 0x30, 0x30, 0x5c, 0x30, 0x30, 0x30, 0x5c, 0x30, 0x30, 0x30, 0x5c,
+ 0x30, 0x30, 0x34, 0x5c, 0x30, 0x30, 0x30, 0x5c, 0x30, 0x30, 0x30,
+ 0x2d, 0x5c, 0x30, 0x30, 0x30, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x7b,
+ 0x5c, 0x6e, 0x20, 0x20, 0x4e, 0x65, 0x77, 0x20, 0x7b, 0x5c, 0x6e,
+ 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x62, 0x72, 0x65, 0x76, 0x3a,
+ 0x20, 0x5c, 0x22, 0x21, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x22, 0x5c, 0x6e, 0x20, 0x20, 0x20,
+ 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x3a, 0x20, 0x5c, 0x22, 0x5c, 0x5c,
+ 0x30, 0x30, 0x35, 0x5c, 0x5c, 0x30, 0x30, 0x30, 0x5c, 0x5c, 0x30,
+ 0x30, 0x30, 0x5c, 0x5c, 0x30, 0x30, 0x30, 0x5c, 0x5c, 0x30, 0x30,
+ 0x34, 0x5c, 0x5c, 0x30, 0x30, 0x30, 0x5c, 0x5c, 0x30, 0x30, 0x30,
+ 0x2d, 0x5c, 0x5c, 0x30, 0x30, 0x30, 0x5c, 0x22, 0x5c, 0x6e, 0x20,
+ 0x20, 0x7d, 0x5c, 0x6e, 0x7d, 0x5c, 0x6e, 0x22, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x22, 0x21,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33,
+ 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c,
+ 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37,
+ 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37,
+ 0x37, 0x5c, 0x33, 0x37, 0x37, 0x5c, 0x33, 0x37, 0x37, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff}
+ aranges := []byte{0x2c}
+ frame := []byte{}
+ info := []byte{0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2d, 0x0, 0x5,
+ 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2d, 0x0}
+
+ // The input above is malformed; the goal here it just to make sure
+ // that we don't get a panic or other bad behavior while trying to
+ // construct a dwarf.Data object from the input. For good measure,
+ // test to make sure we can handle the case where the input is
+ // truncated as well.
+ for i := 0; i <= len(info); i++ {
+ truncated := info[:i]
+ dw, err := New(abbrev, aranges, frame, truncated, nil, nil, nil, nil)
+ if err == nil {
+ t.Errorf("expected error")
+ } else {
+ if dw != nil {
+ t.Errorf("got non-nil dw, wanted nil")
+ }
+ }
+ }
+}
+
+func TestIssue52045(t *testing.T) {
+ var abbrev, aranges, frame, line, pubnames, ranges, str []byte
+ info := []byte{0x7, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
+
+ // A hand-crafted input corresponding to a minimal-size
+ // .debug_info (header only, no DIEs) and an empty abbrev table.
+ data0, _ := New(abbrev, aranges, frame, info, line, pubnames, ranges, str)
+ reader0 := data0.Reader()
+ entry0, _ := reader0.SeekPC(0x0)
+ // main goal is to make sure we can get here without crashing
+ if entry0 != nil {
+ t.Errorf("got non-nil entry0, wanted nil")
+ }
+}
diff --git a/src/debug/dwarf/export_test.go b/src/debug/dwarf/export_test.go
new file mode 100644
index 0000000..b8a25ff
--- /dev/null
+++ b/src/debug/dwarf/export_test.go
@@ -0,0 +1,7 @@
+// Copyright 2017 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
+
+var PathJoin = pathJoin
diff --git a/src/debug/dwarf/line.go b/src/debug/dwarf/line.go
new file mode 100644
index 0000000..3a02c8e
--- /dev/null
+++ b/src/debug/dwarf/line.go
@@ -0,0 +1,852 @@
+// 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.
+
+package dwarf
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "path"
+ "strings"
+)
+
+// 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 the [LineEntry.EndSequence] field set.
+type LineReader struct {
+ buf buf
+
+ // Original .debug_line section data. Used by Seek.
+ section []byte
+
+ str []byte // .debug_str
+ lineStr []byte // .debug_line_str
+
+ // Header information
+ version uint16
+ addrsize int
+ segmentSelectorSize int
+ 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
+}
+
+// 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
+ }
+ if off < 0 || 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,
+ str: d.str,
+ lineStr: d.lineStr,
+ }
+
+ // Read the header.
+ if err := r.readHeader(compDir); err != nil {
+ return nil, err
+ }
+
+ // Initialize line reader state.
+ r.Reset()
+
+ return &r, nil
+}
+
+// readHeader reads the line number program header from r.buf and sets
+// all of the header fields in r.
+func (r *LineReader) readHeader(compDir string) 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 > 5) {
+ // 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)}
+ }
+ if r.version >= 5 {
+ r.addrsize = int(buf.uint8())
+ r.segmentSelectorSize = int(buf.uint8())
+ } else {
+ r.addrsize = buf.format.addrsize()
+ r.segmentSelectorSize = 0
+ }
+ var headerLength Offset
+ if dwarf64 {
+ headerLength = Offset(buf.uint64())
+ } else {
+ headerLength = Offset(buf.uint32())
+ }
+ programOffset := buf.off + headerLength
+ if programOffset > r.endOffset {
+ return DecodeError{"line", hdrOffset, fmt.Sprintf("malformed line table: program offset %d exceeds end offset %d", programOffset, r.endOffset)}
+ }
+ r.programOffset = programOffset
+ 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 r.version < 5 {
+ // Read include directories table.
+ r.directories = []string{compDir}
+ for {
+ directory := buf.string()
+ if buf.err != nil {
+ return buf.err
+ }
+ if len(directory) == 0 {
+ break
+ }
+ if !pathIsAbs(directory) {
+ // Relative paths are implicitly relative to
+ // the compilation directory.
+ directory = pathJoin(compDir, directory)
+ }
+ r.directories = append(r.directories, directory)
+ }
+
+ // 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
+ }
+ }
+ } else {
+ dirFormat := r.readLNCTFormat()
+ c := buf.uint()
+ r.directories = make([]string, c)
+ for i := range r.directories {
+ dir, _, _, err := r.readLNCT(dirFormat, dwarf64)
+ if err != nil {
+ return err
+ }
+ r.directories[i] = dir
+ }
+ fileFormat := r.readLNCTFormat()
+ c = buf.uint()
+ r.fileEntries = make([]*LineFile, c)
+ for i := range r.fileEntries {
+ name, mtime, size, err := r.readLNCT(fileFormat, dwarf64)
+ if err != nil {
+ return err
+ }
+ r.fileEntries[i] = &LineFile{name, mtime, int(size)}
+ }
+ }
+
+ r.initialFileEntries = len(r.fileEntries)
+
+ return buf.err
+}
+
+// lnctForm is a pair of an LNCT code and a form. This represents an
+// entry in the directory name or file name description in the DWARF 5
+// line number program header.
+type lnctForm struct {
+ lnct int
+ form format
+}
+
+// readLNCTFormat reads an LNCT format description.
+func (r *LineReader) readLNCTFormat() []lnctForm {
+ c := r.buf.uint8()
+ ret := make([]lnctForm, c)
+ for i := range ret {
+ ret[i].lnct = int(r.buf.uint())
+ ret[i].form = format(r.buf.uint())
+ }
+ return ret
+}
+
+// readLNCT reads a sequence of LNCT entries and returns path information.
+func (r *LineReader) readLNCT(s []lnctForm, dwarf64 bool) (path string, mtime uint64, size uint64, err error) {
+ var dir string
+ for _, lf := range s {
+ var str string
+ var val uint64
+ switch lf.form {
+ case formString:
+ str = r.buf.string()
+ case formStrp, formLineStrp:
+ var off uint64
+ if dwarf64 {
+ off = r.buf.uint64()
+ } else {
+ off = uint64(r.buf.uint32())
+ }
+ if uint64(int(off)) != off {
+ return "", 0, 0, DecodeError{"line", r.buf.off, "strp/line_strp offset out of range"}
+ }
+ var b1 buf
+ if lf.form == formStrp {
+ b1 = makeBuf(r.buf.dwarf, r.buf.format, "str", 0, r.str)
+ } else {
+ b1 = makeBuf(r.buf.dwarf, r.buf.format, "line_str", 0, r.lineStr)
+ }
+ b1.skip(int(off))
+ str = b1.string()
+ if b1.err != nil {
+ return "", 0, 0, DecodeError{"line", r.buf.off, b1.err.Error()}
+ }
+ case formStrpSup:
+ // Supplemental sections not yet supported.
+ if dwarf64 {
+ r.buf.uint64()
+ } else {
+ r.buf.uint32()
+ }
+ case formStrx:
+ // .debug_line.dwo sections not yet supported.
+ r.buf.uint()
+ case formStrx1:
+ r.buf.uint8()
+ case formStrx2:
+ r.buf.uint16()
+ case formStrx3:
+ r.buf.uint24()
+ case formStrx4:
+ r.buf.uint32()
+ case formData1:
+ val = uint64(r.buf.uint8())
+ case formData2:
+ val = uint64(r.buf.uint16())
+ case formData4:
+ val = uint64(r.buf.uint32())
+ case formData8:
+ val = r.buf.uint64()
+ case formData16:
+ r.buf.bytes(16)
+ case formDwarfBlock:
+ r.buf.bytes(int(r.buf.uint()))
+ case formUdata:
+ val = r.buf.uint()
+ }
+
+ switch lf.lnct {
+ case lnctPath:
+ path = str
+ case lnctDirectoryIndex:
+ if val >= uint64(len(r.directories)) {
+ return "", 0, 0, DecodeError{"line", r.buf.off, "directory index out of range"}
+ }
+ dir = r.directories[val]
+ case lnctTimestamp:
+ mtime = val
+ case lnctSize:
+ size = val
+ case lnctMD5:
+ // Ignored.
+ }
+ }
+
+ if dir != "" && path != "" {
+ path = pathJoin(dir, path)
+ }
+
+ return path, mtime, size, nil
+}
+
+// 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
+ }
+ if len(name) == 0 {
+ return true, nil
+ }
+ off := r.buf.off
+ dirIndex := int(r.buf.uint())
+ if !pathIsAbs(name) {
+ if dirIndex >= len(r.directories) {
+ return false, DecodeError{"line", off, "directory index too large"}
+ }
+ name = pathJoin(r.directories[dirIndex], name)
+ }
+ mtime := r.buf.uint()
+ length := int(r.buf.uint())
+
+ // If this is a dynamically added path and the cursor was
+ // backed up, we may have already added this entry. Avoid
+ // updating existing line table entries in this case. This
+ // avoids an allocation and potential racy access to the slice
+ // backing store if the user called Files.
+ if len(r.fileEntries) < cap(r.fileEntries) {
+ fe := r.fileEntries[:len(r.fileEntries)+1]
+ if fe[len(fe)-1] != nil {
+ // We already processed this addition.
+ r.fileEntries = fe
+ return false, nil
+ }
+ }
+ r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length})
+ return false, nil
+}
+
+// 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
+ }
+}
+
+// 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
+ }
+ emit := r.step(entry)
+ if r.buf.err != nil {
+ return r.buf.err
+ }
+ if emit {
+ return 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.
+}
+
+// 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 + adjustedOpcode%r.lineRange
+ r.state.Line += lineDelta
+ goto emit
+ }
+
+ switch opcode {
+ case 0:
+ // Extended opcode [DWARF2 6.2.5.3]
+ length := Offset(r.buf.uint())
+ startOff := r.buf.off
+ opcode := r.buf.uint8()
+
+ switch opcode {
+ case lneEndSequence:
+ r.state.EndSequence = true
+ *entry = r.state
+ r.resetState()
+
+ case lneSetAddress:
+ switch r.addrsize {
+ case 1:
+ r.state.Address = uint64(r.buf.uint8())
+ case 2:
+ r.state.Address = uint64(r.buf.uint16())
+ case 4:
+ r.state.Address = uint64(r.buf.uint32())
+ case 8:
+ r.state.Address = r.buf.uint64()
+ default:
+ r.buf.error("unknown address size")
+ }
+
+ 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()
+
+ case lneSetDiscriminator:
+ // [DWARF4 6.2.5.3]
+ r.state.Discriminator = int(r.buf.uint())
+ }
+
+ r.buf.skip(int(startOff + length - r.buf.off))
+
+ if opcode == lneEndSequence {
+ return true
+ }
+
+ // Standard opcodes [DWARF2 6.2.5.2]
+ case lnsCopy:
+ goto emit
+
+ case lnsAdvancePC:
+ r.advancePC(int(r.buf.uint()))
+
+ case lnsAdvanceLine:
+ r.state.Line += int(r.buf.int())
+
+ case lnsSetFile:
+ r.fileIndex = int(r.buf.uint())
+ r.updateFile()
+
+ 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()
+ }
+ }
+ return false
+
+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 [LineReader.Tell].
+//
+// The argument pos must have been returned by a call to [LineReader.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]
+
+ // Reset file entries list.
+ r.fileEntries = r.fileEntries[:r.initialFileEntries]
+
+ // Reset line number program state.
+ r.resetState()
+}
+
+// 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,
+ File: nil,
+ Line: 1,
+ Column: 0,
+ IsStmt: r.defaultIsStmt,
+ BasicBlock: false,
+ PrologueEnd: false,
+ EpilogueBegin: false,
+ ISA: 0,
+ Discriminator: 0,
+ }
+ r.fileIndex = 1
+ r.updateFile()
+}
+
+// Files returns the file name table of this compilation unit as of
+// the current position in the line table. The file name table may be
+// referenced from attributes in this compilation unit such as
+// [AttrDeclFile].
+//
+// Entry 0 is always nil, since file index 0 represents "no file".
+//
+// The file name table of a compilation unit is not fixed. Files
+// returns the file table as of the current position in the line
+// table. This may contain more entries than the file table at an
+// earlier position in the line table, though existing entries never
+// change.
+func (r *LineReader) Files() []*LineFile {
+ return r.fileEntries
+}
+
+// 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
+ }
+ 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
+ }
+ }
+
+ // 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
+ }
+ 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
+ }
+}
+
+// pathIsAbs reports whether path is an absolute path (or "full path
+// name" in DWARF parlance). This is in "whatever form makes sense for
+// the host system", so this accepts both UNIX-style and DOS-style
+// absolute paths. We avoid the filepath package because we want this
+// to behave the same regardless of our host system and because we
+// don't know what system the paths came from.
+func pathIsAbs(path string) bool {
+ _, path = splitDrive(path)
+ return len(path) > 0 && (path[0] == '/' || path[0] == '\\')
+}
+
+// pathJoin joins dirname and filename. filename must be relative.
+// DWARF paths can be UNIX-style or DOS-style, so this handles both.
+func pathJoin(dirname, filename string) string {
+ if len(dirname) == 0 {
+ return filename
+ }
+ // dirname should be absolute, which means we can determine
+ // whether it's a DOS path reasonably reliably by looking for
+ // a drive letter or UNC path.
+ drive, dirname := splitDrive(dirname)
+ if drive == "" {
+ // UNIX-style path.
+ return path.Join(dirname, filename)
+ }
+ // DOS-style path.
+ drive2, filename := splitDrive(filename)
+ if drive2 != "" {
+ if !strings.EqualFold(drive, drive2) {
+ // Different drives. There's not much we can
+ // do here, so just ignore the directory.
+ return drive2 + filename
+ }
+ // Drives are the same. Ignore drive on filename.
+ }
+ if !(strings.HasSuffix(dirname, "/") || strings.HasSuffix(dirname, `\`)) && dirname != "" {
+ sep := `\`
+ if strings.HasPrefix(dirname, "/") {
+ sep = `/`
+ }
+ dirname += sep
+ }
+ return drive + dirname + filename
+}
+
+// splitDrive splits the DOS drive letter or UNC share point from
+// path, if any. path == drive + rest
+func splitDrive(path string) (drive, rest string) {
+ if len(path) >= 2 && path[1] == ':' {
+ if c := path[0]; 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' {
+ return path[:2], path[2:]
+ }
+ }
+ if len(path) > 3 && (path[0] == '\\' || path[0] == '/') && (path[1] == '\\' || path[1] == '/') {
+ // Normalize the path so we can search for just \ below.
+ npath := strings.Replace(path, "/", `\`, -1)
+ // Get the host part, which must be non-empty.
+ slash1 := strings.IndexByte(npath[2:], '\\') + 2
+ if slash1 > 2 {
+ // Get the mount-point part, which must be non-empty.
+ slash2 := strings.IndexByte(npath[slash1+1:], '\\') + slash1 + 1
+ if slash2 > slash1 {
+ return path[:slash2], path[slash2:]
+ }
+ }
+ }
+ return "", path
+}
diff --git a/src/debug/dwarf/line_test.go b/src/debug/dwarf/line_test.go
new file mode 100644
index 0000000..e947d99
--- /dev/null
+++ b/src/debug/dwarf/line_test.go
@@ -0,0 +1,462 @@
+// 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.
+
+package dwarf_test
+
+import (
+ . "debug/dwarf"
+ "io"
+ "strings"
+ "testing"
+)
+
+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"}
+)
+
+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},
+
+ {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},
+ }
+ files := [][]*LineFile{{nil, file1H, file1C}, {nil, file2C}}
+
+ testLineTable(t, want, files, elfData(t, "testdata/line-gcc.elf"))
+}
+
+func TestLineELFGCCZstd(t *testing.T) {
+ // Generated by:
+ // # gcc --version | head -n1
+ // gcc (Debian 12.2.0-10) 12.2.0
+ // # gcc -g -no-pie -Wl,--compress-debug-sections=zstd line*.c
+
+ zfile1H := &LineFile{Name: "/home/iant/go/src/debug/dwarf/testdata/line1.h"}
+ zfile1C := &LineFile{Name: "/home/iant/go/src/debug/dwarf/testdata/line1.c"}
+ zfile2C := &LineFile{Name: "/home/iant/go/src/debug/dwarf/testdata/line2.c"}
+
+ // Line table based on readelf --debug-dump=rawline,decodedline
+ want := []LineEntry{
+ {Address: 0x401126, File: zfile1H, Line: 2, Column: 1, IsStmt: true},
+ {Address: 0x40112a, File: zfile1H, Line: 5, Column: 8, IsStmt: true},
+ {Address: 0x401131, File: zfile1H, Line: 5, Column: 2, IsStmt: true},
+ {Address: 0x401133, File: zfile1H, Line: 6, Column: 10, IsStmt: true, Discriminator: 3},
+ {Address: 0x40113d, File: zfile1H, Line: 5, Column: 22, IsStmt: true, Discriminator: 3},
+ {Address: 0x401141, File: zfile1H, Line: 5, Column: 15, IsStmt: true, Discriminator: 1},
+ {Address: 0x401147, File: zfile1H, Line: 7, Column: 1, IsStmt: true},
+ {Address: 0x40114b, File: zfile1C, Line: 6, Column: 1, IsStmt: true},
+ {Address: 0x40114f, File: zfile1C, Line: 7, Column: 2, IsStmt: true},
+ {Address: 0x401159, File: zfile1C, Line: 8, Column: 2, IsStmt: true},
+ {Address: 0x401168, File: zfile1C, Line: 9, Column: 1, IsStmt: true},
+ {Address: 0x40116a, EndSequence: true},
+
+ {Address: 0x40116a, File: zfile2C, Line: 4, Column: 1, IsStmt: true},
+ {Address: 0x40116e, File: zfile2C, Line: 5, Column: 2, IsStmt: true},
+ {Address: 0x40117d, File: zfile2C, Line: 6, Column: 1, IsStmt: true},
+ {Address: 0x401180, EndSequence: true},
+ }
+ files := [][]*LineFile{
+ {zfile1C, zfile1H, zfile1C},
+ {zfile2C, zfile2C},
+ }
+
+ testLineTable(t, want, files, elfData(t, "testdata/line-gcc-zstd.elf"))
+}
+
+func TestLineGCCWindows(t *testing.T) {
+ // Generated by:
+ // > gcc --version
+ // gcc (tdm64-1) 4.9.2
+ // > gcc -g -o line-gcc-win.bin line1.c C:\workdir\go\src\debug\dwarf\testdata\line2.c
+
+ toWindows := func(lf *LineFile) *LineFile {
+ lf2 := *lf
+ lf2.Name = strings.Replace(lf2.Name, "/home/austin/go.dev/", "C:\\workdir\\go\\", -1)
+ lf2.Name = strings.Replace(lf2.Name, "/", "\\", -1)
+ return &lf2
+ }
+ file1C := toWindows(file1C)
+ file1H := toWindows(file1H)
+ file2C := toWindows(file2C)
+
+ // Line table based on objdump --dwarf=rawline,decodedline
+ want := []LineEntry{
+ {Address: 0x401530, File: file1H, Line: 2, IsStmt: true},
+ {Address: 0x401538, File: file1H, Line: 5, IsStmt: true},
+ {Address: 0x401541, File: file1H, Line: 6, IsStmt: true, Discriminator: 3},
+ {Address: 0x40154b, File: file1H, Line: 5, IsStmt: true, Discriminator: 3},
+ {Address: 0x40154f, File: file1H, Line: 5, IsStmt: false, Discriminator: 1},
+ {Address: 0x401555, File: file1H, Line: 7, IsStmt: true},
+ {Address: 0x40155b, File: file1C, Line: 6, IsStmt: true},
+ {Address: 0x401563, File: file1C, Line: 6, IsStmt: true},
+ {Address: 0x401568, File: file1C, Line: 7, IsStmt: true},
+ {Address: 0x40156d, File: file1C, Line: 8, IsStmt: true},
+ {Address: 0x401572, File: file1C, Line: 9, IsStmt: true},
+ {Address: 0x401578, EndSequence: true},
+
+ {Address: 0x401580, File: file2C, Line: 4, IsStmt: true},
+ {Address: 0x401588, File: file2C, Line: 5, IsStmt: true},
+ {Address: 0x401595, File: file2C, Line: 6, IsStmt: true},
+ {Address: 0x40159b, EndSequence: true},
+ }
+ files := [][]*LineFile{{nil, file1H, file1C}, {nil, file2C}}
+
+ testLineTable(t, want, files, peData(t, "testdata/line-gcc-win.bin"))
+}
+
+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},
+ }
+ files := [][]*LineFile{{nil, file1C, file1H}, {nil, file2C}}
+
+ testLineTable(t, want, files, elfData(t, "testdata/line-clang.elf"))
+}
+
+func TestLineRnglists(t *testing.T) {
+ // Test a newer file, generated by clang.
+ file := &LineFile{Name: "/usr/local/google/home/iant/foo.c"}
+ want := []LineEntry{
+ {Address: 0x401020, File: file, Line: 12, IsStmt: true},
+ {Address: 0x401020, File: file, Line: 13, Column: 12, IsStmt: true, PrologueEnd: true},
+ {Address: 0x401022, File: file, Line: 13, Column: 7},
+ {Address: 0x401024, File: file, Line: 17, Column: 1, IsStmt: true},
+ {Address: 0x401027, File: file, Line: 16, Column: 10, IsStmt: true},
+ {Address: 0x40102c, EndSequence: true},
+ {Address: 0x401000, File: file, Line: 2, IsStmt: true},
+ {Address: 0x401000, File: file, Line: 6, Column: 17, IsStmt: true, PrologueEnd: true},
+ {Address: 0x401002, File: file, Line: 6, Column: 3},
+ {Address: 0x401019, File: file, Line: 9, Column: 3, IsStmt: true},
+ {Address: 0x40101a, File: file, Line: 0, Column: 3},
+ {Address: 0x40101c, File: file, Line: 9, Column: 3},
+ {Address: 0x40101d, EndSequence: true},
+ }
+ files := [][]*LineFile{{file}}
+
+ testLineTable(t, want, files, elfData(t, "testdata/rnglistx.elf"))
+}
+
+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 testLineTable(t *testing.T, want []LineEntry, files [][]*LineFile, d *Data) {
+ // Get line table from d.
+ var got []LineEntry
+ dr := d.Reader()
+ for {
+ ent, err := dr.Next()
+ if err != nil {
+ t.Fatal("dr.Next:", err)
+ } else if ent == nil {
+ break
+ }
+
+ if ent.Tag != TagCompileUnit {
+ dr.SkipChildren()
+ continue
+ }
+
+ // Ignore system compilation units (this happens in
+ // the Windows binary). We'll still decode the line
+ // table, but won't check it.
+ name := ent.Val(AttrName).(string)
+ ignore := strings.HasPrefix(name, "C:/crossdev/") || strings.HasPrefix(name, "../../")
+
+ // Decode CU's line table.
+ lr, err := d.LineReader(ent)
+ if err != nil {
+ t.Fatal("d.LineReader:", err)
+ } else if lr == nil {
+ continue
+ }
+
+ for {
+ var line LineEntry
+ err := lr.Next(&line)
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ t.Fatal("lr.Next:", err)
+ }
+ // Ignore sources from the Windows build environment.
+ if ignore {
+ continue
+ }
+ got = append(got, line)
+ }
+
+ // Check file table.
+ if !ignore {
+ if !compareFiles(files[0], lr.Files()) {
+ t.Log("File tables do not match. Got:")
+ dumpFiles(t, lr.Files())
+ t.Log("Want:")
+ dumpFiles(t, files[0])
+ t.Fail()
+ }
+ files = files[1:]
+ }
+ }
+
+ // Compare line tables.
+ if !compareLines(t, got, want) {
+ t.Log("Line tables do not match. Got:")
+ dumpLines(t, got)
+ t.Log("Want:")
+ dumpLines(t, want)
+ t.FailNow()
+ }
+}
+
+func compareFiles(a, b []*LineFile) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i := range a {
+ if a[i] == nil && b[i] == nil {
+ continue
+ }
+ if a[i] != nil && b[i] != nil && a[i].Name == b[i].Name {
+ continue
+ }
+ return false
+ }
+ return true
+}
+
+func dumpFiles(t *testing.T, files []*LineFile) {
+ for i, f := range files {
+ name := "<nil>"
+ if f != nil {
+ name = f.Name
+ }
+ t.Logf(" %d %s", i, name)
+ }
+}
+
+func compareLines(t *testing.T, a, b []LineEntry) bool {
+ t.Helper()
+ if len(a) != len(b) {
+ t.Errorf("len(a) == %d, len(b) == %d", 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 {
+ t.Errorf("%d: name %v != name %v", i, al.File.Name, bl.File.Name)
+ return false
+ }
+ al.File = nil
+ bl.File = nil
+ if al != bl {
+ t.Errorf("%d: %#v != %#v", i, 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)
+ }
+}
+
+type joinTest struct {
+ dirname, filename string
+ path string
+}
+
+var joinTests = []joinTest{
+ {"a", "b", "a/b"},
+ {"a", "", "a"},
+ {"", "b", "b"},
+ {"/a", "b", "/a/b"},
+ {"/a/", "b", "/a/b"},
+
+ {`C:\Windows\`, `System32`, `C:\Windows\System32`},
+ {`C:\Windows\`, ``, `C:\Windows\`},
+ {`C:\`, `Windows`, `C:\Windows`},
+ {`C:\Windows\`, `C:System32`, `C:\Windows\System32`},
+ {`C:\Windows`, `a/b`, `C:\Windows\a/b`},
+ {`\\host\share\`, `foo`, `\\host\share\foo`},
+ {`\\host\share\`, `foo\bar`, `\\host\share\foo\bar`},
+ {`//host/share/`, `foo/bar`, `//host/share/foo/bar`},
+
+ // Note: the Go compiler currently emits DWARF line table paths
+ // with '/' instead of '\' (see issues #19784, #36495). These
+ // tests are to cover cases that might come up for Windows Go
+ // binaries.
+ {`c:/workdir/go/src/x`, `y.go`, `c:/workdir/go/src/x/y.go`},
+ {`d:/some/thing/`, `b.go`, `d:/some/thing/b.go`},
+ {`e:\blah\`, `foo.c`, `e:\blah\foo.c`},
+
+ // The following are "best effort". We shouldn't see relative
+ // base directories in DWARF, but these test that pathJoin
+ // doesn't fail miserably if it sees one.
+ {`C:`, `a`, `C:a`},
+ {`C:`, `a\b`, `C:a\b`},
+ {`C:.`, `a`, `C:.\a`},
+ {`C:a`, `b`, `C:a\b`},
+}
+
+func TestPathJoin(t *testing.T) {
+ for _, test := range joinTests {
+ got := PathJoin(test.dirname, test.filename)
+ if test.path != got {
+ t.Errorf("pathJoin(%q, %q) = %q, want %q", test.dirname, test.filename, got, test.path)
+ }
+ }
+}
+
+func TestPathLineReaderMalformed(t *testing.T) {
+ // This test case drawn from issue #52354. What's happening
+ // here is that the stmtList attribute in the compilation
+ // unit is malformed (negative).
+ var aranges, frame, pubnames, ranges, str []byte
+ abbrev := []byte{0x10, 0x20, 0x20, 0x20, 0x21, 0x20, 0x10, 0x21, 0x61,
+ 0x0, 0x0, 0xff, 0x20, 0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}
+ info := []byte{0x0, 0x0, 0x0, 0x9, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0,
+ 0x20, 0x10, 0x10}
+ line := []byte{0x20}
+ Data0, err := New(abbrev, aranges, frame, info, line, pubnames, ranges, str)
+ if err != nil {
+ t.Fatalf("error unexpected: %v", err)
+ }
+ Reader0 := Data0.Reader()
+ Entry0, err := Reader0.Next()
+ if err != nil {
+ t.Fatalf("error unexpected: %v", err)
+ }
+ LineReader0, err := Data0.LineReader(Entry0)
+ if err == nil {
+ t.Fatalf("expected error")
+ }
+ if LineReader0 != nil {
+ t.Fatalf("expected nil line reader")
+ }
+}
diff --git a/src/debug/dwarf/open.go b/src/debug/dwarf/open.go
new file mode 100644
index 0000000..0901341
--- /dev/null
+++ b/src/debug/dwarf/open.go
@@ -0,0 +1,140 @@
+// Copyright 2009 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 provides access to DWARF debugging information loaded from
+executable files, as defined in the DWARF 2.0 Standard at
+http://dwarfstd.org/doc/dwarf-2.0.0.pdf.
+
+# Security
+
+This package is not designed to be hardened against adversarial inputs, and is
+outside the scope of https://go.dev/security/policy. In particular, only basic
+validation is done when parsing object files. As such, care should be taken when
+parsing untrusted inputs, as parsing malformed files may consume significant
+resources, or cause panics.
+*/
+package dwarf
+
+import (
+ "encoding/binary"
+ "errors"
+)
+
+// Data represents the DWARF debugging information
+// loaded from an executable file (for example, an ELF or Mach-O executable).
+type Data struct {
+ // raw data
+ abbrev []byte
+ aranges []byte
+ frame []byte
+ info []byte
+ line []byte
+ pubnames []byte
+ ranges []byte
+ str []byte
+
+ // New sections added in DWARF 5.
+ addr []byte
+ lineStr []byte
+ strOffsets []byte
+ rngLists []byte
+
+ // parsed data
+ abbrevCache map[uint64]abbrevTable
+ bigEndian bool
+ order binary.ByteOrder
+ typeCache map[Offset]Type
+ typeSigs map[uint64]*typeUnit
+ unit []unit
+}
+
+var errSegmentSelector = errors.New("non-zero segment_selector size not supported")
+
+// New returns a new [Data] object initialized from the given parameters.
+// 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
+// the ".debug_abbrev" section.
+func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Data, error) {
+ d := &Data{
+ abbrev: abbrev,
+ aranges: aranges,
+ frame: frame,
+ info: info,
+ line: line,
+ pubnames: pubnames,
+ ranges: ranges,
+ str: str,
+ abbrevCache: make(map[uint64]abbrevTable),
+ typeCache: make(map[Offset]Type),
+ typeSigs: make(map[uint64]*typeUnit),
+ }
+
+ // Sniff .debug_info to figure out byte order.
+ // 32-bit DWARF: 4 byte length, 2 byte version.
+ // 64-bit DWARf: 4 bytes of 0xff, 8 byte length, 2 byte version.
+ if len(d.info) < 6 {
+ return nil, DecodeError{"info", Offset(len(d.info)), "too short"}
+ }
+ offset := 4
+ if d.info[0] == 0xff && d.info[1] == 0xff && d.info[2] == 0xff && d.info[3] == 0xff {
+ if len(d.info) < 14 {
+ return nil, DecodeError{"info", Offset(len(d.info)), "too short"}
+ }
+ offset = 12
+ }
+ // Fetch the version, a tiny 16-bit number (1, 2, 3, 4, 5).
+ x, y := d.info[offset], d.info[offset+1]
+ switch {
+ case x == 0 && y == 0:
+ return nil, DecodeError{"info", 4, "unsupported version 0"}
+ case x == 0:
+ d.bigEndian = true
+ d.order = binary.BigEndian
+ case y == 0:
+ d.bigEndian = false
+ d.order = binary.LittleEndian
+ default:
+ return nil, DecodeError{"info", 4, "cannot determine byte order"}
+ }
+
+ u, err := d.parseUnits()
+ if err != nil {
+ return nil, err
+ }
+ d.unit = u
+ return d, nil
+}
+
+// AddTypes will add one .debug_types section to the DWARF data. A
+// typical object with DWARF version 4 debug info will have multiple
+// .debug_types sections. The name is used for error reporting only,
+// and serves to distinguish one .debug_types section from another.
+func (d *Data) AddTypes(name string, types []byte) error {
+ return d.parseTypes(name, types)
+}
+
+// AddSection adds another DWARF section by name. The name should be a
+// DWARF section name such as ".debug_addr", ".debug_str_offsets", and
+// so forth. This approach is used for new DWARF sections added in
+// DWARF 5 and later.
+func (d *Data) AddSection(name string, contents []byte) error {
+ var err error
+ switch name {
+ case ".debug_addr":
+ d.addr = contents
+ case ".debug_line_str":
+ d.lineStr = contents
+ case ".debug_str_offsets":
+ d.strOffsets = contents
+ case ".debug_rnglists":
+ d.rngLists = contents
+ }
+ // Just ignore names that we don't yet support.
+ return err
+}
diff --git a/src/debug/dwarf/tag_string.go b/src/debug/dwarf/tag_string.go
new file mode 100644
index 0000000..b79ea17
--- /dev/null
+++ b/src/debug/dwarf/tag_string.go
@@ -0,0 +1,119 @@
+// Code generated by "stringer -type Tag -trimprefix=Tag"; DO NOT EDIT.
+
+package dwarf
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[TagArrayType-1]
+ _ = x[TagClassType-2]
+ _ = x[TagEntryPoint-3]
+ _ = x[TagEnumerationType-4]
+ _ = x[TagFormalParameter-5]
+ _ = x[TagImportedDeclaration-8]
+ _ = x[TagLabel-10]
+ _ = x[TagLexDwarfBlock-11]
+ _ = x[TagMember-13]
+ _ = x[TagPointerType-15]
+ _ = x[TagReferenceType-16]
+ _ = x[TagCompileUnit-17]
+ _ = x[TagStringType-18]
+ _ = x[TagStructType-19]
+ _ = x[TagSubroutineType-21]
+ _ = x[TagTypedef-22]
+ _ = x[TagUnionType-23]
+ _ = x[TagUnspecifiedParameters-24]
+ _ = x[TagVariant-25]
+ _ = x[TagCommonDwarfBlock-26]
+ _ = x[TagCommonInclusion-27]
+ _ = x[TagInheritance-28]
+ _ = x[TagInlinedSubroutine-29]
+ _ = x[TagModule-30]
+ _ = x[TagPtrToMemberType-31]
+ _ = x[TagSetType-32]
+ _ = x[TagSubrangeType-33]
+ _ = x[TagWithStmt-34]
+ _ = x[TagAccessDeclaration-35]
+ _ = x[TagBaseType-36]
+ _ = x[TagCatchDwarfBlock-37]
+ _ = x[TagConstType-38]
+ _ = x[TagConstant-39]
+ _ = x[TagEnumerator-40]
+ _ = x[TagFileType-41]
+ _ = x[TagFriend-42]
+ _ = x[TagNamelist-43]
+ _ = x[TagNamelistItem-44]
+ _ = x[TagPackedType-45]
+ _ = x[TagSubprogram-46]
+ _ = x[TagTemplateTypeParameter-47]
+ _ = x[TagTemplateValueParameter-48]
+ _ = x[TagThrownType-49]
+ _ = x[TagTryDwarfBlock-50]
+ _ = x[TagVariantPart-51]
+ _ = x[TagVariable-52]
+ _ = x[TagVolatileType-53]
+ _ = x[TagDwarfProcedure-54]
+ _ = x[TagRestrictType-55]
+ _ = x[TagInterfaceType-56]
+ _ = x[TagNamespace-57]
+ _ = x[TagImportedModule-58]
+ _ = x[TagUnspecifiedType-59]
+ _ = x[TagPartialUnit-60]
+ _ = x[TagImportedUnit-61]
+ _ = x[TagMutableType-62]
+ _ = x[TagCondition-63]
+ _ = x[TagSharedType-64]
+ _ = x[TagTypeUnit-65]
+ _ = x[TagRvalueReferenceType-66]
+ _ = x[TagTemplateAlias-67]
+ _ = x[TagCoarrayType-68]
+ _ = x[TagGenericSubrange-69]
+ _ = x[TagDynamicType-70]
+ _ = x[TagAtomicType-71]
+ _ = x[TagCallSite-72]
+ _ = x[TagCallSiteParameter-73]
+ _ = x[TagSkeletonUnit-74]
+ _ = x[TagImmutableType-75]
+}
+
+const (
+ _Tag_name_0 = "ArrayTypeClassTypeEntryPointEnumerationTypeFormalParameter"
+ _Tag_name_1 = "ImportedDeclaration"
+ _Tag_name_2 = "LabelLexDwarfBlock"
+ _Tag_name_3 = "Member"
+ _Tag_name_4 = "PointerTypeReferenceTypeCompileUnitStringTypeStructType"
+ _Tag_name_5 = "SubroutineTypeTypedefUnionTypeUnspecifiedParametersVariantCommonDwarfBlockCommonInclusionInheritanceInlinedSubroutineModulePtrToMemberTypeSetTypeSubrangeTypeWithStmtAccessDeclarationBaseTypeCatchDwarfBlockConstTypeConstantEnumeratorFileTypeFriendNamelistNamelistItemPackedTypeSubprogramTemplateTypeParameterTemplateValueParameterThrownTypeTryDwarfBlockVariantPartVariableVolatileTypeDwarfProcedureRestrictTypeInterfaceTypeNamespaceImportedModuleUnspecifiedTypePartialUnitImportedUnitMutableTypeConditionSharedTypeTypeUnitRvalueReferenceTypeTemplateAliasCoarrayTypeGenericSubrangeDynamicTypeAtomicTypeCallSiteCallSiteParameterSkeletonUnitImmutableType"
+)
+
+var (
+ _Tag_index_0 = [...]uint8{0, 9, 18, 28, 43, 58}
+ _Tag_index_2 = [...]uint8{0, 5, 18}
+ _Tag_index_4 = [...]uint8{0, 11, 24, 35, 45, 55}
+ _Tag_index_5 = [...]uint16{0, 14, 21, 30, 51, 58, 74, 89, 100, 117, 123, 138, 145, 157, 165, 182, 190, 205, 214, 222, 232, 240, 246, 254, 266, 276, 286, 307, 329, 339, 352, 363, 371, 383, 397, 409, 422, 431, 445, 460, 471, 483, 494, 503, 513, 521, 540, 553, 564, 579, 590, 600, 608, 625, 637, 650}
+)
+
+func (i Tag) String() string {
+ switch {
+ case 1 <= i && i <= 5:
+ i -= 1
+ return _Tag_name_0[_Tag_index_0[i]:_Tag_index_0[i+1]]
+ case i == 8:
+ return _Tag_name_1
+ case 10 <= i && i <= 11:
+ i -= 10
+ return _Tag_name_2[_Tag_index_2[i]:_Tag_index_2[i+1]]
+ case i == 13:
+ return _Tag_name_3
+ case 15 <= i && i <= 19:
+ i -= 15
+ return _Tag_name_4[_Tag_index_4[i]:_Tag_index_4[i+1]]
+ case 21 <= i && i <= 75:
+ i -= 21
+ return _Tag_name_5[_Tag_index_5[i]:_Tag_index_5[i+1]]
+ default:
+ return "Tag(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+}
diff --git a/src/debug/dwarf/testdata/bitfields.c b/src/debug/dwarf/testdata/bitfields.c
new file mode 100644
index 0000000..0583333
--- /dev/null
+++ b/src/debug/dwarf/testdata/bitfields.c
@@ -0,0 +1,17 @@
+// Copyright 2022 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.
+
+/*
+Linux ELF:
+gcc -gdwarf-4 -m64 -c bitfields.c -o bitfields.elf4
+*/
+
+typedef struct another_struct {
+ unsigned short quix;
+ int xyz[0];
+ unsigned x:1;
+ long long array[40];
+} t_another_struct;
+t_another_struct q2;
+
diff --git a/src/debug/dwarf/testdata/bitfields.elf4 b/src/debug/dwarf/testdata/bitfields.elf4
new file mode 100644
index 0000000..2e06e68
--- /dev/null
+++ b/src/debug/dwarf/testdata/bitfields.elf4
Binary files differ
diff --git a/src/debug/dwarf/testdata/cppunsuptypes.cc b/src/debug/dwarf/testdata/cppunsuptypes.cc
new file mode 100644
index 0000000..e9281c7
--- /dev/null
+++ b/src/debug/dwarf/testdata/cppunsuptypes.cc
@@ -0,0 +1,34 @@
+// Copyright 2019 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.
+
+// cppunsuptypes.elf built with g++ 7.3
+// g++ -g -c -o cppunsuptypes.elf cppunsuptypes.cc
+
+int i = 3;
+double d = 3;
+
+// anonymous reference type
+int &culprit = i;
+
+// named reference type
+typedef double &dref;
+dref dr = d;
+
+// incorporated into another type
+typedef struct {
+ dref q;
+ int &r;
+} hasrefs;
+
+hasrefs hr = { d, i };
+
+// This code is intended to trigger a DWARF "pointer to member" type DIE
+struct CS { int dm; };
+
+int foo()
+{
+ int CS::* pdm = &CS::dm;
+ CS cs = {42};
+ return cs.*pdm;
+}
diff --git a/src/debug/dwarf/testdata/cppunsuptypes.elf b/src/debug/dwarf/testdata/cppunsuptypes.elf
new file mode 100644
index 0000000..e955512
--- /dev/null
+++ b/src/debug/dwarf/testdata/cppunsuptypes.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/cycle.c b/src/debug/dwarf/testdata/cycle.c
new file mode 100644
index 0000000..a0b53df
--- /dev/null
+++ b/src/debug/dwarf/testdata/cycle.c
@@ -0,0 +1,7 @@
+typedef struct aaa *AAA;
+typedef AAA BBB;
+struct aaa { BBB val; };
+
+AAA x(void) {
+ return (AAA)0;
+}
diff --git a/src/debug/dwarf/testdata/cycle.elf b/src/debug/dwarf/testdata/cycle.elf
new file mode 100644
index 0000000..e0b66ca
--- /dev/null
+++ b/src/debug/dwarf/testdata/cycle.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/debug_rnglists b/src/debug/dwarf/testdata/debug_rnglists
new file mode 100644
index 0000000..985ec6c
--- /dev/null
+++ b/src/debug/dwarf/testdata/debug_rnglists
Binary files differ
diff --git a/src/debug/dwarf/testdata/line-clang-dwarf5.elf b/src/debug/dwarf/testdata/line-clang-dwarf5.elf
new file mode 100644
index 0000000..7b80c9c
--- /dev/null
+++ b/src/debug/dwarf/testdata/line-clang-dwarf5.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/line-clang.elf b/src/debug/dwarf/testdata/line-clang.elf
new file mode 100644
index 0000000..b63cc78
--- /dev/null
+++ b/src/debug/dwarf/testdata/line-clang.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/line-gcc-dwarf5.elf b/src/debug/dwarf/testdata/line-gcc-dwarf5.elf
new file mode 100644
index 0000000..34ce17c
--- /dev/null
+++ b/src/debug/dwarf/testdata/line-gcc-dwarf5.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/line-gcc-win.bin b/src/debug/dwarf/testdata/line-gcc-win.bin
new file mode 100644
index 0000000..583ad44
--- /dev/null
+++ b/src/debug/dwarf/testdata/line-gcc-win.bin
Binary files differ
diff --git a/src/debug/dwarf/testdata/line-gcc-zstd.elf b/src/debug/dwarf/testdata/line-gcc-zstd.elf
new file mode 100644
index 0000000..45cbe72
--- /dev/null
+++ b/src/debug/dwarf/testdata/line-gcc-zstd.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/line-gcc.elf b/src/debug/dwarf/testdata/line-gcc.elf
new file mode 100644
index 0000000..50500a8
--- /dev/null
+++ b/src/debug/dwarf/testdata/line-gcc.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/line1.c b/src/debug/dwarf/testdata/line1.c
new file mode 100644
index 0000000..f358647
--- /dev/null
+++ b/src/debug/dwarf/testdata/line1.c
@@ -0,0 +1,9 @@
+#include "line1.h"
+
+void f2();
+
+int main()
+{
+ f1();
+ f2();
+}
diff --git a/src/debug/dwarf/testdata/line1.h b/src/debug/dwarf/testdata/line1.h
new file mode 100644
index 0000000..974d4c8
--- /dev/null
+++ b/src/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/src/debug/dwarf/testdata/line2.c b/src/debug/dwarf/testdata/line2.c
new file mode 100644
index 0000000..38d8998
--- /dev/null
+++ b/src/debug/dwarf/testdata/line2.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+void f2()
+{
+ printf("hello\n");
+}
diff --git a/src/debug/dwarf/testdata/ranges.c b/src/debug/dwarf/testdata/ranges.c
new file mode 100644
index 0000000..2f208e5
--- /dev/null
+++ b/src/debug/dwarf/testdata/ranges.c
@@ -0,0 +1,25 @@
+// gcc -g -O2 -freorder-blocks-and-partition
+
+const char *arr[10000];
+const char *hot = "hot";
+const char *cold = "cold";
+
+__attribute__((noinline))
+void fn(int path) {
+ int i;
+
+ if (path) {
+ for (i = 0; i < sizeof arr / sizeof arr[0]; i++) {
+ arr[i] = hot;
+ }
+ } else {
+ for (i = 0; i < sizeof arr / sizeof arr[0]; i++) {
+ arr[i] = cold;
+ }
+ }
+}
+
+int main(int argc, char *argv[]) {
+ fn(argc);
+ return 0;
+}
diff --git a/src/debug/dwarf/testdata/ranges.elf b/src/debug/dwarf/testdata/ranges.elf
new file mode 100644
index 0000000..7f54138
--- /dev/null
+++ b/src/debug/dwarf/testdata/ranges.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/rnglistx.c b/src/debug/dwarf/testdata/rnglistx.c
new file mode 100644
index 0000000..8770435
--- /dev/null
+++ b/src/debug/dwarf/testdata/rnglistx.c
@@ -0,0 +1,19 @@
+// clang -gdwarf-5 -O2 -nostdlib
+
+__attribute__((noinline, cold))
+static int sum(int i) {
+ int j, s;
+
+ s = 0;
+ for (j = 0; j < i; j++) {
+ s += j * i;
+ }
+ return s;
+}
+
+int main(int argc, char** argv) {
+ if (argc == 0) {
+ return 0;
+ }
+ return sum(argc);
+}
diff --git a/src/debug/dwarf/testdata/rnglistx.elf b/src/debug/dwarf/testdata/rnglistx.elf
new file mode 100644
index 0000000..c2d7f55
--- /dev/null
+++ b/src/debug/dwarf/testdata/rnglistx.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/split.c b/src/debug/dwarf/testdata/split.c
new file mode 100644
index 0000000..0ef3427
--- /dev/null
+++ b/src/debug/dwarf/testdata/split.c
@@ -0,0 +1,5 @@
+// gcc -gsplit-dwarf split.c -o split.elf
+
+int main()
+{
+}
diff --git a/src/debug/dwarf/testdata/split.elf b/src/debug/dwarf/testdata/split.elf
new file mode 100644
index 0000000..99ee2c2
--- /dev/null
+++ b/src/debug/dwarf/testdata/split.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/typedef.c b/src/debug/dwarf/testdata/typedef.c
new file mode 100644
index 0000000..3e7e008
--- /dev/null
+++ b/src/debug/dwarf/testdata/typedef.c
@@ -0,0 +1,86 @@
+// Copyright 2009 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.
+
+/*
+Linux ELF:
+gcc -gdwarf-2 -m64 -c typedef.c && gcc -gdwarf-2 -m64 -o typedef.elf typedef.o
+
+OS X Mach-O:
+gcc -gdwarf-2 -m64 -c typedef.c -o typedef.macho
+gcc -gdwarf-4 -m64 -c typedef.c -o typedef.macho4
+*/
+#include <complex.h>
+
+typedef volatile int* t_ptr_volatile_int;
+typedef const char *t_ptr_const_char;
+typedef long t_long;
+typedef unsigned short t_ushort;
+typedef int t_func_int_of_float_double(float, double);
+typedef int (*t_ptr_func_int_of_float_double)(float, double);
+typedef int (*t_ptr_func_int_of_float_complex)(float complex);
+typedef int (*t_ptr_func_int_of_double_complex)(double complex);
+typedef int (*t_ptr_func_int_of_long_double_complex)(long double complex);
+typedef int *t_func_ptr_int_of_char_schar_uchar(char, signed char, unsigned char);
+typedef void t_func_void_of_char(char);
+typedef void t_func_void_of_void(void);
+typedef void t_func_void_of_ptr_char_dots(char*, ...);
+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;
+ int y : 4;
+ long long array[40];
+} t_my_union;
+typedef enum my_enum {
+ e1 = 1,
+ e2 = 2,
+ e3 = -5,
+ e4 = 1000000000000000LL,
+} t_my_enum;
+
+typedef struct list t_my_list;
+struct list {
+ short val;
+ t_my_list *next;
+};
+
+typedef struct tree {
+ struct tree *left, *right;
+ unsigned long long val;
+} t_my_tree;
+
+t_ptr_volatile_int *a2;
+t_ptr_const_char **a3a;
+t_long *a4;
+t_ushort *a5;
+t_func_int_of_float_double *a6;
+t_ptr_func_int_of_float_double *a7;
+t_func_ptr_int_of_char_schar_uchar *a8;
+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_struct1 *a12a;
+t_my_union *a12b;
+t_my_enum *a13;
+t_my_list *a14;
+t_my_tree *a15;
+t_ptr_func_int_of_float_complex *a16;
+t_ptr_func_int_of_double_complex *a17;
+t_ptr_func_int_of_long_double_complex *a18;
+
+int main()
+{
+ return 0;
+}
diff --git a/src/debug/dwarf/testdata/typedef.elf b/src/debug/dwarf/testdata/typedef.elf
new file mode 100644
index 0000000..b2062d2
--- /dev/null
+++ b/src/debug/dwarf/testdata/typedef.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/typedef.elf4 b/src/debug/dwarf/testdata/typedef.elf4
new file mode 100644
index 0000000..3d5a5a1
--- /dev/null
+++ b/src/debug/dwarf/testdata/typedef.elf4
Binary files differ
diff --git a/src/debug/dwarf/testdata/typedef.elf5 b/src/debug/dwarf/testdata/typedef.elf5
new file mode 100644
index 0000000..aec48f6
--- /dev/null
+++ b/src/debug/dwarf/testdata/typedef.elf5
Binary files differ
diff --git a/src/debug/dwarf/testdata/typedef.macho b/src/debug/dwarf/testdata/typedef.macho
new file mode 100644
index 0000000..f75afcc
--- /dev/null
+++ b/src/debug/dwarf/testdata/typedef.macho
Binary files differ
diff --git a/src/debug/dwarf/testdata/typedef.macho4 b/src/debug/dwarf/testdata/typedef.macho4
new file mode 100644
index 0000000..093ff37
--- /dev/null
+++ b/src/debug/dwarf/testdata/typedef.macho4
Binary files differ
diff --git a/src/debug/dwarf/type.go b/src/debug/dwarf/type.go
new file mode 100644
index 0000000..627d3a1
--- /dev/null
+++ b/src/debug/dwarf/type.go
@@ -0,0 +1,870 @@
+// Copyright 2009 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 type information structures.
+// The format is heavily biased toward C, but for simplicity
+// the String methods use a pseudo-Go syntax.
+
+package dwarf
+
+import "strconv"
+
+// A Type conventionally represents a pointer to any of the
+// specific Type structures ([CharType], [StructType], etc.).
+type Type interface {
+ Common() *CommonType
+ String() string
+ Size() int64
+}
+
+// A CommonType holds fields common to multiple types.
+// If a field is not known or not applicable for a given type,
+// the zero value is used.
+type CommonType struct {
+ ByteSize int64 // size of value of this type, in bytes
+ Name string // name that can be used to refer to type
+}
+
+func (c *CommonType) Common() *CommonType { return c }
+
+func (c *CommonType) Size() int64 { return c.ByteSize }
+
+// Basic types
+
+// A BasicType holds fields common to all basic types.
+//
+// See the documentation for [StructField] for more info on the interpretation of
+// the BitSize/BitOffset/DataBitOffset fields.
+type BasicType struct {
+ CommonType
+ BitSize int64
+ BitOffset int64
+ DataBitOffset int64
+}
+
+func (b *BasicType) Basic() *BasicType { return b }
+
+func (t *BasicType) String() string {
+ if t.Name != "" {
+ return t.Name
+ }
+ return "?"
+}
+
+// A CharType represents a signed character type.
+type CharType struct {
+ BasicType
+}
+
+// A UcharType represents an unsigned character type.
+type UcharType struct {
+ BasicType
+}
+
+// An IntType represents a signed integer type.
+type IntType struct {
+ BasicType
+}
+
+// A UintType represents an unsigned integer type.
+type UintType struct {
+ BasicType
+}
+
+// A FloatType represents a floating point type.
+type FloatType struct {
+ BasicType
+}
+
+// A ComplexType represents a complex floating point type.
+type ComplexType struct {
+ BasicType
+}
+
+// A BoolType represents a boolean type.
+type BoolType struct {
+ BasicType
+}
+
+// An AddrType represents a machine address type.
+type AddrType struct {
+ BasicType
+}
+
+// An UnspecifiedType represents an implicit, unknown, ambiguous or nonexistent type.
+type UnspecifiedType struct {
+ BasicType
+}
+
+// qualifiers
+
+// A QualType represents a type that has the C/C++ "const", "restrict", or "volatile" qualifier.
+type QualType struct {
+ CommonType
+ Qual string
+ Type Type
+}
+
+func (t *QualType) String() string { return t.Qual + " " + t.Type.String() }
+
+func (t *QualType) Size() int64 { return t.Type.Size() }
+
+// An ArrayType represents a fixed size array type.
+type ArrayType struct {
+ CommonType
+ Type Type
+ StrideBitSize int64 // if > 0, number of bits to hold each element
+ Count int64 // if == -1, an incomplete array, like char x[].
+}
+
+func (t *ArrayType) String() string {
+ return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String()
+}
+
+func (t *ArrayType) Size() int64 {
+ if t.Count == -1 {
+ return 0
+ }
+ return t.Count * t.Type.Size()
+}
+
+// A VoidType represents the C void type.
+type VoidType struct {
+ CommonType
+}
+
+func (t *VoidType) String() string { return "void" }
+
+// A PtrType represents a pointer type.
+type PtrType struct {
+ CommonType
+ Type Type
+}
+
+func (t *PtrType) String() string { return "*" + t.Type.String() }
+
+// A StructType represents a struct, union, or C++ class type.
+type StructType struct {
+ CommonType
+ StructName string
+ Kind string // "struct", "union", or "class".
+ Field []*StructField
+ Incomplete bool // if true, struct, union, class is declared but not defined
+}
+
+// A StructField represents a field in a struct, union, or C++ class type.
+//
+// # Bit Fields
+//
+// The BitSize, BitOffset, and DataBitOffset fields describe the bit
+// size and offset of data members declared as bit fields in C/C++
+// struct/union/class types.
+//
+// BitSize is the number of bits in the bit field.
+//
+// DataBitOffset, if non-zero, is the number of bits from the start of
+// the enclosing entity (e.g. containing struct/class/union) to the
+// start of the bit field. This corresponds to the DW_AT_data_bit_offset
+// DWARF attribute that was introduced in DWARF 4.
+//
+// BitOffset, if non-zero, is the number of bits between the most
+// significant bit of the storage unit holding the bit field to the
+// most significant bit of the bit field. Here "storage unit" is the
+// type name before the bit field (for a field "unsigned x:17", the
+// storage unit is "unsigned"). BitOffset values can vary depending on
+// the endianness of the system. BitOffset corresponds to the
+// DW_AT_bit_offset DWARF attribute that was deprecated in DWARF 4 and
+// removed in DWARF 5.
+//
+// At most one of DataBitOffset and BitOffset will be non-zero;
+// DataBitOffset/BitOffset will only be non-zero if BitSize is
+// non-zero. Whether a C compiler uses one or the other
+// will depend on compiler vintage and command line options.
+//
+// Here is an example of C/C++ bit field use, along with what to
+// expect in terms of DWARF bit offset info. Consider this code:
+//
+// struct S {
+// int q;
+// int j:5;
+// int k:6;
+// int m:5;
+// int n:8;
+// } s;
+//
+// For the code above, one would expect to see the following for
+// DW_AT_bit_offset values (using GCC 8):
+//
+// Little | Big
+// Endian | Endian
+// |
+// "j": 27 | 0
+// "k": 21 | 5
+// "m": 16 | 11
+// "n": 8 | 16
+//
+// Note that in the above the offsets are purely with respect to the
+// containing storage unit for j/k/m/n -- these values won't vary based
+// on the size of prior data members in the containing struct.
+//
+// If the compiler emits DW_AT_data_bit_offset, the expected values
+// would be:
+//
+// "j": 32
+// "k": 37
+// "m": 43
+// "n": 48
+//
+// Here the value 32 for "j" reflects the fact that the bit field is
+// preceded by other data members (recall that DW_AT_data_bit_offset
+// values are relative to the start of the containing struct). Hence
+// DW_AT_data_bit_offset values can be quite large for structs with
+// many fields.
+//
+// DWARF also allow for the possibility of base types that have
+// non-zero bit size and bit offset, so this information is also
+// captured for base types, but it is worth noting that it is not
+// possible to trigger this behavior using mainstream languages.
+type StructField struct {
+ Name string
+ Type Type
+ ByteOffset int64
+ ByteSize int64 // usually zero; use Type.Size() for normal fields
+ BitOffset int64
+ DataBitOffset int64
+ BitSize int64 // zero if not a bit field
+}
+
+func (t *StructType) String() string {
+ if t.StructName != "" {
+ return t.Kind + " " + t.StructName
+ }
+ return t.Defn()
+}
+
+func (f *StructField) bitOffset() int64 {
+ if f.BitOffset != 0 {
+ return f.BitOffset
+ }
+ return f.DataBitOffset
+}
+
+func (t *StructType) Defn() string {
+ s := t.Kind
+ if t.StructName != "" {
+ s += " " + t.StructName
+ }
+ if t.Incomplete {
+ s += " /*incomplete*/"
+ return s
+ }
+ s += " {"
+ for i, f := range t.Field {
+ if i > 0 {
+ s += "; "
+ }
+ s += f.Name + " " + f.Type.String()
+ s += "@" + strconv.FormatInt(f.ByteOffset, 10)
+ if f.BitSize > 0 {
+ s += " : " + strconv.FormatInt(f.BitSize, 10)
+ s += "@" + strconv.FormatInt(f.bitOffset(), 10)
+ }
+ }
+ s += "}"
+ return s
+}
+
+// An EnumType represents an enumerated type.
+// The only indication of its native integer type is its ByteSize
+// (inside [CommonType]).
+type EnumType struct {
+ CommonType
+ EnumName string
+ Val []*EnumValue
+}
+
+// An EnumValue represents a single enumeration value.
+type EnumValue struct {
+ Name string
+ Val int64
+}
+
+func (t *EnumType) String() string {
+ s := "enum"
+ if t.EnumName != "" {
+ s += " " + t.EnumName
+ }
+ s += " {"
+ for i, v := range t.Val {
+ if i > 0 {
+ s += "; "
+ }
+ s += v.Name + "=" + strconv.FormatInt(v.Val, 10)
+ }
+ s += "}"
+ return s
+}
+
+// A FuncType represents a function type.
+type FuncType struct {
+ CommonType
+ ReturnType Type
+ ParamType []Type
+}
+
+func (t *FuncType) String() string {
+ s := "func("
+ for i, t := range t.ParamType {
+ if i > 0 {
+ s += ", "
+ }
+ s += t.String()
+ }
+ s += ")"
+ if t.ReturnType != nil {
+ s += " " + t.ReturnType.String()
+ }
+ return s
+}
+
+// A DotDotDotType represents the variadic ... function parameter.
+type DotDotDotType struct {
+ CommonType
+}
+
+func (t *DotDotDotType) String() string { return "..." }
+
+// A TypedefType represents a named type.
+type TypedefType struct {
+ CommonType
+ Type Type
+}
+
+func (t *TypedefType) String() string { return t.Name }
+
+func (t *TypedefType) Size() int64 { return t.Type.Size() }
+
+// An UnsupportedType is a placeholder returned in situations where we
+// encounter a type that isn't supported.
+type UnsupportedType struct {
+ CommonType
+ Tag Tag
+}
+
+func (t *UnsupportedType) String() string {
+ if t.Name != "" {
+ return t.Name
+ }
+ return t.Name + "(unsupported type " + t.Tag.String() + ")"
+}
+
+// typeReader is used to read from either the info section or the
+// types section.
+type typeReader interface {
+ Seek(Offset)
+ 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.
+func (d *Data) Type(off Offset) (Type, error) {
+ return d.readType("info", d.Reader(), off, d.typeCache, nil)
+}
+
+type typeFixer struct {
+ typedefs []*TypedefType
+ arraytypes []*Type
+}
+
+func (tf *typeFixer) recordArrayType(t *Type) {
+ if t == nil {
+ return
+ }
+ _, ok := (*t).(*ArrayType)
+ if ok {
+ tf.arraytypes = append(tf.arraytypes, t)
+ }
+}
+
+func (tf *typeFixer) apply() {
+ for _, t := range tf.typedefs {
+ t.Common().ByteSize = t.Type.Size()
+ }
+ for _, t := range tf.arraytypes {
+ zeroArray(t)
+ }
+}
+
+// readType reads a type from r at off of name. It adds types to the
+// type cache, appends new typedef types to typedefs, and computes the
+// sizes of types. Callers should pass nil for typedefs; this is used
+// for internal recursion.
+func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Offset]Type, fixups *typeFixer) (Type, error) {
+ if t, ok := typeCache[off]; ok {
+ return t, nil
+ }
+ r.Seek(off)
+ e, err := r.Next()
+ if err != nil {
+ return nil, err
+ }
+ addressSize := r.AddressSize()
+ if e == nil || e.Offset != off {
+ return nil, DecodeError{name, off, "no type at offset"}
+ }
+
+ // If this is the root of the recursion, prepare to resolve
+ // typedef sizes and perform other fixups once the recursion is
+ // done. This must be done after the type graph is constructed
+ // because it may need to resolve cycles in a different order than
+ // readType encounters them.
+ if fixups == nil {
+ var fixer typeFixer
+ defer func() {
+ fixer.apply()
+ }()
+ fixups = &fixer
+ }
+
+ // Parse type from Entry.
+ // Must always set typeCache[off] before calling
+ // d.readType recursively, to handle circular types correctly.
+ var typ Type
+
+ nextDepth := 0
+
+ // Get next child; set err if error happens.
+ next := func() *Entry {
+ if !e.Children {
+ return nil
+ }
+ // Only return direct children.
+ // Skip over composite entries that happen to be nested
+ // inside this one. Most DWARF generators wouldn't generate
+ // such a thing, but clang does.
+ // See golang.org/issue/6472.
+ for {
+ kid, err1 := r.Next()
+ if err1 != nil {
+ err = err1
+ return nil
+ }
+ if kid == nil {
+ err = DecodeError{name, r.offset(), "unexpected end of DWARF entries"}
+ return nil
+ }
+ if kid.Tag == 0 {
+ if nextDepth > 0 {
+ nextDepth--
+ continue
+ }
+ return nil
+ }
+ if kid.Children {
+ nextDepth++
+ }
+ if nextDepth > 0 {
+ continue
+ }
+ return kid
+ }
+ }
+
+ // Get Type referred to by Entry's AttrType field.
+ // Set err if error happens. Not having a type is an error.
+ typeOf := func(e *Entry) Type {
+ tval := e.Val(AttrType)
+ var t Type
+ switch toff := tval.(type) {
+ case Offset:
+ if t, err = d.readType(name, r.clone(), toff, typeCache, fixups); err != nil {
+ return nil
+ }
+ case uint64:
+ if t, err = d.sigToType(toff); err != nil {
+ return nil
+ }
+ default:
+ // It appears that no Type means "void".
+ return new(VoidType)
+ }
+ return t
+ }
+
+ switch e.Tag {
+ case TagArrayType:
+ // Multi-dimensional array. (DWARF v2 §5.4)
+ // Attributes:
+ // AttrType:subtype [required]
+ // AttrStrideSize: size in bits of each element of the array
+ // AttrByteSize: size of entire array
+ // Children:
+ // TagSubrangeType or TagEnumerationType giving one dimension.
+ // dimensions are in left to right order.
+ t := new(ArrayType)
+ typ = t
+ typeCache[off] = t
+ if t.Type = typeOf(e); err != nil {
+ goto Error
+ }
+ t.StrideBitSize, _ = e.Val(AttrStrideSize).(int64)
+
+ // Accumulate dimensions,
+ var dims []int64
+ for kid := next(); kid != nil; kid = next() {
+ // TODO(rsc): Can also be TagEnumerationType
+ // but haven't seen that in the wild yet.
+ switch kid.Tag {
+ case TagSubrangeType:
+ count, ok := kid.Val(AttrCount).(int64)
+ if !ok {
+ // Old binaries may have an upper bound instead.
+ count, ok = kid.Val(AttrUpperBound).(int64)
+ if ok {
+ count++ // Length is one more than upper bound.
+ } else if len(dims) == 0 {
+ count = -1 // As in x[].
+ }
+ }
+ dims = append(dims, count)
+ case TagEnumerationType:
+ err = DecodeError{name, kid.Offset, "cannot handle enumeration type as array bound"}
+ goto Error
+ }
+ }
+ if len(dims) == 0 {
+ // LLVM generates this for x[].
+ dims = []int64{-1}
+ }
+
+ t.Count = dims[0]
+ for i := len(dims) - 1; i >= 1; i-- {
+ t.Type = &ArrayType{Type: t.Type, Count: dims[i]}
+ }
+
+ case TagBaseType:
+ // Basic type. (DWARF v2 §5.1)
+ // Attributes:
+ // AttrName: name of base type in programming language of the compilation unit [required]
+ // AttrEncoding: encoding value for type (encFloat etc) [required]
+ // AttrByteSize: size of type in bytes [required]
+ // AttrBitOffset: bit offset of value within containing storage unit
+ // AttrDataBitOffset: bit offset of value within containing storage unit
+ // AttrBitSize: size in bits
+ //
+ // For most languages BitOffset/DataBitOffset/BitSize will not be present
+ // for base types.
+ name, _ := e.Val(AttrName).(string)
+ enc, ok := e.Val(AttrEncoding).(int64)
+ if !ok {
+ err = DecodeError{name, e.Offset, "missing encoding attribute for " + name}
+ goto Error
+ }
+ switch enc {
+ default:
+ err = DecodeError{name, e.Offset, "unrecognized encoding attribute value"}
+ goto Error
+
+ case encAddress:
+ typ = new(AddrType)
+ case encBoolean:
+ typ = new(BoolType)
+ case encComplexFloat:
+ typ = new(ComplexType)
+ if name == "complex" {
+ // clang writes out 'complex' instead of 'complex float' or 'complex double'.
+ // clang also writes out a byte size that we can use to distinguish.
+ // See issue 8694.
+ switch byteSize, _ := e.Val(AttrByteSize).(int64); byteSize {
+ case 8:
+ name = "complex float"
+ case 16:
+ name = "complex double"
+ }
+ }
+ case encFloat:
+ typ = new(FloatType)
+ case encSigned:
+ typ = new(IntType)
+ case encUnsigned:
+ typ = new(UintType)
+ case encSignedChar:
+ typ = new(CharType)
+ case encUnsignedChar:
+ typ = new(UcharType)
+ }
+ typeCache[off] = typ
+ t := typ.(interface {
+ Basic() *BasicType
+ }).Basic()
+ t.Name = name
+ t.BitSize, _ = e.Val(AttrBitSize).(int64)
+ haveBitOffset := false
+ haveDataBitOffset := false
+ t.BitOffset, haveBitOffset = e.Val(AttrBitOffset).(int64)
+ t.DataBitOffset, haveDataBitOffset = e.Val(AttrDataBitOffset).(int64)
+ if haveBitOffset && haveDataBitOffset {
+ err = DecodeError{name, e.Offset, "duplicate bit offset attributes"}
+ goto Error
+ }
+
+ case TagClassType, TagStructType, TagUnionType:
+ // Structure, union, or class type. (DWARF v2 §5.5)
+ // Attributes:
+ // AttrName: name of struct, union, or class
+ // AttrByteSize: byte size [required]
+ // AttrDeclaration: if true, struct/union/class is incomplete
+ // Children:
+ // TagMember to describe one member.
+ // AttrName: name of member [required]
+ // AttrType: type of member [required]
+ // AttrByteSize: size in bytes
+ // AttrBitOffset: bit offset within bytes for bit fields
+ // AttrDataBitOffset: field bit offset relative to struct start
+ // AttrBitSize: bit size for bit fields
+ // AttrDataMemberLoc: location within struct [required for struct, class]
+ // There is much more to handle C++, all ignored for now.
+ t := new(StructType)
+ typ = t
+ typeCache[off] = t
+ switch e.Tag {
+ case TagClassType:
+ t.Kind = "class"
+ case TagStructType:
+ t.Kind = "struct"
+ case TagUnionType:
+ t.Kind = "union"
+ }
+ t.StructName, _ = e.Val(AttrName).(string)
+ t.Incomplete = e.Val(AttrDeclaration) != nil
+ t.Field = make([]*StructField, 0, 8)
+ var lastFieldType *Type
+ var lastFieldBitSize int64
+ var lastFieldByteOffset int64
+ for kid := next(); kid != nil; kid = next() {
+ if kid.Tag != TagMember {
+ continue
+ }
+ f := new(StructField)
+ if f.Type = typeOf(kid); err != nil {
+ goto Error
+ }
+ switch loc := kid.Val(AttrDataMemberLoc).(type) {
+ case []byte:
+ // TODO: Should have original compilation
+ // unit here, not unknownFormat.
+ b := makeBuf(d, unknownFormat{}, "location", 0, loc)
+ if b.uint8() != opPlusUconst {
+ err = DecodeError{name, kid.Offset, "unexpected opcode"}
+ goto Error
+ }
+ f.ByteOffset = int64(b.uint())
+ if b.err != nil {
+ err = b.err
+ goto Error
+ }
+ case int64:
+ f.ByteOffset = loc
+ }
+
+ f.Name, _ = kid.Val(AttrName).(string)
+ f.ByteSize, _ = kid.Val(AttrByteSize).(int64)
+ haveBitOffset := false
+ haveDataBitOffset := false
+ f.BitOffset, haveBitOffset = kid.Val(AttrBitOffset).(int64)
+ f.DataBitOffset, haveDataBitOffset = kid.Val(AttrDataBitOffset).(int64)
+ if haveBitOffset && haveDataBitOffset {
+ err = DecodeError{name, e.Offset, "duplicate bit offset attributes"}
+ goto Error
+ }
+ f.BitSize, _ = kid.Val(AttrBitSize).(int64)
+ t.Field = append(t.Field, f)
+
+ if lastFieldBitSize == 0 && lastFieldByteOffset == f.ByteOffset && t.Kind != "union" {
+ // Last field was zero width. Fix array length.
+ // (DWARF writes out 0-length arrays as if they were 1-length arrays.)
+ fixups.recordArrayType(lastFieldType)
+ }
+ lastFieldType = &f.Type
+ lastFieldByteOffset = f.ByteOffset
+ lastFieldBitSize = f.BitSize
+ }
+ if t.Kind != "union" {
+ b, ok := e.Val(AttrByteSize).(int64)
+ if ok && b == lastFieldByteOffset {
+ // Final field must be zero width. Fix array length.
+ fixups.recordArrayType(lastFieldType)
+ }
+ }
+
+ case TagConstType, TagVolatileType, TagRestrictType:
+ // Type modifier (DWARF v2 §5.2)
+ // Attributes:
+ // AttrType: subtype
+ t := new(QualType)
+ typ = t
+ typeCache[off] = t
+ if t.Type = typeOf(e); err != nil {
+ goto Error
+ }
+ switch e.Tag {
+ case TagConstType:
+ t.Qual = "const"
+ case TagRestrictType:
+ t.Qual = "restrict"
+ case TagVolatileType:
+ t.Qual = "volatile"
+ }
+
+ case TagEnumerationType:
+ // Enumeration type (DWARF v2 §5.6)
+ // Attributes:
+ // AttrName: enum name if any
+ // AttrByteSize: bytes required to represent largest value
+ // Children:
+ // TagEnumerator:
+ // AttrName: name of constant
+ // AttrConstValue: value of constant
+ t := new(EnumType)
+ typ = t
+ typeCache[off] = t
+ t.EnumName, _ = e.Val(AttrName).(string)
+ t.Val = make([]*EnumValue, 0, 8)
+ for kid := next(); kid != nil; kid = next() {
+ if kid.Tag == TagEnumerator {
+ f := new(EnumValue)
+ f.Name, _ = kid.Val(AttrName).(string)
+ f.Val, _ = kid.Val(AttrConstValue).(int64)
+ n := len(t.Val)
+ if n >= cap(t.Val) {
+ val := make([]*EnumValue, n, n*2)
+ copy(val, t.Val)
+ t.Val = val
+ }
+ t.Val = t.Val[0 : n+1]
+ t.Val[n] = f
+ }
+ }
+
+ case TagPointerType:
+ // Type modifier (DWARF v2 §5.2)
+ // Attributes:
+ // AttrType: subtype [not required! void* has no AttrType]
+ // AttrAddrClass: address class [ignored]
+ t := new(PtrType)
+ typ = t
+ typeCache[off] = t
+ if e.Val(AttrType) == nil {
+ t.Type = &VoidType{}
+ break
+ }
+ t.Type = typeOf(e)
+
+ case TagSubroutineType:
+ // Subroutine type. (DWARF v2 §5.7)
+ // Attributes:
+ // AttrType: type of return value if any
+ // AttrName: possible name of type [ignored]
+ // AttrPrototyped: whether used ANSI C prototype [ignored]
+ // Children:
+ // TagFormalParameter: typed parameter
+ // AttrType: type of parameter
+ // TagUnspecifiedParameter: final ...
+ t := new(FuncType)
+ typ = t
+ typeCache[off] = t
+ if t.ReturnType = typeOf(e); err != nil {
+ goto Error
+ }
+ t.ParamType = make([]Type, 0, 8)
+ for kid := next(); kid != nil; kid = next() {
+ var tkid Type
+ switch kid.Tag {
+ default:
+ continue
+ case TagFormalParameter:
+ if tkid = typeOf(kid); err != nil {
+ goto Error
+ }
+ case TagUnspecifiedParameters:
+ tkid = &DotDotDotType{}
+ }
+ t.ParamType = append(t.ParamType, tkid)
+ }
+
+ case TagTypedef:
+ // Typedef (DWARF v2 §5.3)
+ // Attributes:
+ // AttrName: name [required]
+ // AttrType: type definition [required]
+ t := new(TypedefType)
+ typ = t
+ typeCache[off] = t
+ t.Name, _ = e.Val(AttrName).(string)
+ t.Type = typeOf(e)
+
+ case TagUnspecifiedType:
+ // Unspecified type (DWARF v3 §5.2)
+ // Attributes:
+ // AttrName: name
+ t := new(UnspecifiedType)
+ typ = t
+ typeCache[off] = t
+ t.Name, _ = e.Val(AttrName).(string)
+
+ default:
+ // This is some other type DIE that we're currently not
+ // equipped to handle. Return an abstract "unsupported type"
+ // object in such cases.
+ t := new(UnsupportedType)
+ typ = t
+ typeCache[off] = t
+ t.Tag = e.Tag
+ t.Name, _ = e.Val(AttrName).(string)
+ }
+
+ if err != nil {
+ goto Error
+ }
+
+ {
+ b, ok := e.Val(AttrByteSize).(int64)
+ if !ok {
+ b = -1
+ switch t := typ.(type) {
+ case *TypedefType:
+ // Record that we need to resolve this
+ // type's size once the type graph is
+ // constructed.
+ fixups.typedefs = append(fixups.typedefs, t)
+ case *PtrType:
+ b = int64(addressSize)
+ }
+ }
+ typ.Common().ByteSize = b
+ }
+ return typ, nil
+
+Error:
+ // If the parse fails, take the type out of the cache
+ // so that the next call with this offset doesn't hit
+ // the cache and return success.
+ delete(typeCache, off)
+ return nil, err
+}
+
+func zeroArray(t *Type) {
+ at := (*t).(*ArrayType)
+ if at.Type.Size() == 0 {
+ return
+ }
+ // Make a copy to avoid invalidating typeCache.
+ tt := *at
+ tt.Count = 0
+ *t = &tt
+}
diff --git a/src/debug/dwarf/type_test.go b/src/debug/dwarf/type_test.go
new file mode 100644
index 0000000..5858ef5
--- /dev/null
+++ b/src/debug/dwarf/type_test.go
@@ -0,0 +1,335 @@
+// Copyright 2009 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"
+ "debug/elf"
+ "debug/macho"
+ "debug/pe"
+ "fmt"
+ "strconv"
+ "testing"
+)
+
+var typedefTests = map[string]string{
+ "t_ptr_volatile_int": "*volatile int",
+ "t_ptr_const_char": "*const char",
+ "t_long": "long int",
+ "t_ushort": "short unsigned int",
+ "t_func_int_of_float_double": "func(float, double) int",
+ "t_ptr_func_int_of_float_double": "*func(float, double) int",
+ "t_ptr_func_int_of_float_complex": "*func(complex float) int",
+ "t_ptr_func_int_of_double_complex": "*func(complex double) int",
+ "t_ptr_func_int_of_long_double_complex": "*func(complex long double) int",
+ "t_func_ptr_int_of_char_schar_uchar": "func(char, signed char, unsigned char) *int",
+ "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; 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 {
+ t.Fatal(err)
+ }
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatal(err)
+ }
+ return d
+}
+
+func machoData(t *testing.T, name string) *Data {
+ f, err := macho.Open(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatal(err)
+ }
+ return d
+}
+
+func peData(t *testing.T, name string) *Data {
+ f, err := pe.Open(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatal(err)
+ }
+ return d
+}
+
+func TestTypedefsELF(t *testing.T) {
+ testTypedefs(t, elfData(t, "testdata/typedef.elf"), "elf", typedefTests)
+}
+
+func TestTypedefsMachO(t *testing.T) {
+ testTypedefs(t, machoData(t, "testdata/typedef.macho"), "macho", typedefTests)
+}
+
+func TestTypedefsELFDwarf4(t *testing.T) {
+ testTypedefs(t, elfData(t, "testdata/typedef.elf4"), "elf", typedefTests)
+}
+
+func testTypedefs(t *testing.T, d *Data, kind string, testcases map[string]string) {
+ r := d.Reader()
+ seen := make(map[string]bool)
+ for {
+ e, err := r.Next()
+ if err != nil {
+ t.Fatal("r.Next:", err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag == TagTypedef {
+ typ, err := d.Type(e.Offset)
+ if err != nil {
+ t.Fatal("d.Type:", err)
+ }
+ t1 := typ.(*TypedefType)
+ var typstr string
+ if ts, ok := t1.Type.(*StructType); ok {
+ typstr = ts.Defn()
+ } else {
+ typstr = t1.Type.String()
+ }
+
+ if want, ok := testcases[t1.Name]; ok {
+ if seen[t1.Name] {
+ t.Errorf("multiple definitions for %s", t1.Name)
+ }
+ seen[t1.Name] = true
+ if typstr != want && (kind != "macho" || typstr != machoBug[want]) {
+ t.Errorf("%s:\n\thave %s\n\twant %s", t1.Name, typstr, want)
+ }
+ }
+ }
+ if e.Tag != TagCompileUnit {
+ r.SkipChildren()
+ }
+ }
+
+ for k := range testcases {
+ if !seen[k] {
+ t.Errorf("missing %s", k)
+ }
+ }
+}
+
+func TestTypedefCycle(t *testing.T) {
+ // See issue #13039: reading a typedef cycle starting from a
+ // different place than the size needed to be computed from
+ // used to crash.
+ //
+ // cycle.elf built with GCC 4.8.4:
+ // gcc -g -c -o cycle.elf cycle.c
+ d := elfData(t, "testdata/cycle.elf")
+ r := d.Reader()
+ offsets := []Offset{}
+ for {
+ e, err := r.Next()
+ if err != nil {
+ t.Fatal("r.Next:", err)
+ }
+ if e == nil {
+ break
+ }
+ switch e.Tag {
+ case TagBaseType, TagTypedef, TagPointerType, TagStructType:
+ offsets = append(offsets, e.Offset)
+ }
+ }
+
+ // Parse each type with a fresh type cache.
+ for _, offset := range offsets {
+ d := elfData(t, "testdata/cycle.elf")
+ _, err := d.Type(offset)
+ if err != nil {
+ t.Fatalf("d.Type(0x%x): %s", offset, err)
+ }
+ }
+}
+
+var unsupportedTypeTests = []string{
+ // varname:typename:string:size
+ "culprit::(unsupported type ReferenceType):8",
+ "pdm::(unsupported type PtrToMemberType):-1",
+}
+
+func TestUnsupportedTypes(t *testing.T) {
+ // Issue 29601:
+ // When reading DWARF from C++ load modules, we can encounter
+ // oddball type DIEs. These will be returned as "UnsupportedType"
+ // objects; check to make sure this works properly.
+ d := elfData(t, "testdata/cppunsuptypes.elf")
+ r := d.Reader()
+ seen := make(map[string]bool)
+ for {
+ e, err := r.Next()
+ if err != nil {
+ t.Fatal("r.Next:", err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag == TagVariable {
+ vname, _ := e.Val(AttrName).(string)
+ tAttr := e.Val(AttrType)
+ typOff, ok := tAttr.(Offset)
+ if !ok {
+ t.Errorf("variable at offset %v has no type", e.Offset)
+ continue
+ }
+ typ, err := d.Type(typOff)
+ if err != nil {
+ t.Errorf("err in type decode: %v\n", err)
+ continue
+ }
+ unsup, isok := typ.(*UnsupportedType)
+ if !isok {
+ continue
+ }
+ tag := vname + ":" + unsup.Name + ":" + unsup.String() +
+ ":" + strconv.FormatInt(unsup.Size(), 10)
+ seen[tag] = true
+ }
+ }
+ dumpseen := false
+ for _, v := range unsupportedTypeTests {
+ if !seen[v] {
+ t.Errorf("missing %s", v)
+ dumpseen = true
+ }
+ }
+ if dumpseen {
+ for k := range seen {
+ fmt.Printf("seen: %s\n", k)
+ }
+ }
+}
+
+var expectedBitOffsets1 = map[string]string{
+ "x": "S:1 DBO:32",
+ "y": "S:4 DBO:33",
+}
+
+var expectedBitOffsets2 = map[string]string{
+ "x": "S:1 BO:7",
+ "y": "S:4 BO:27",
+}
+
+func TestBitOffsetsELF(t *testing.T) {
+ f := "testdata/typedef.elf"
+ testBitOffsets(t, elfData(t, f), f, expectedBitOffsets2)
+}
+
+func TestBitOffsetsMachO(t *testing.T) {
+ f := "testdata/typedef.macho"
+ testBitOffsets(t, machoData(t, f), f, expectedBitOffsets2)
+}
+
+func TestBitOffsetsMachO4(t *testing.T) {
+ f := "testdata/typedef.macho4"
+ testBitOffsets(t, machoData(t, f), f, expectedBitOffsets1)
+}
+
+func TestBitOffsetsELFDwarf4(t *testing.T) {
+ f := "testdata/typedef.elf4"
+ testBitOffsets(t, elfData(t, f), f, expectedBitOffsets1)
+}
+
+func TestBitOffsetsELFDwarf5(t *testing.T) {
+ f := "testdata/typedef.elf5"
+ testBitOffsets(t, elfData(t, f), f, expectedBitOffsets1)
+}
+
+func testBitOffsets(t *testing.T, d *Data, tag string, expectedBitOffsets map[string]string) {
+ r := d.Reader()
+ for {
+ e, err := r.Next()
+ if err != nil {
+ t.Fatal("r.Next:", err)
+ }
+ if e == nil {
+ break
+ }
+
+ if e.Tag == TagStructType {
+ typ, err := d.Type(e.Offset)
+ if err != nil {
+ t.Fatal("d.Type:", err)
+ }
+
+ t1 := typ.(*StructType)
+
+ bitInfoDump := func(f *StructField) string {
+ res := fmt.Sprintf("S:%d", f.BitSize)
+ if f.BitOffset != 0 {
+ res += fmt.Sprintf(" BO:%d", f.BitOffset)
+ }
+ if f.DataBitOffset != 0 {
+ res += fmt.Sprintf(" DBO:%d", f.DataBitOffset)
+ }
+ return res
+ }
+
+ for _, field := range t1.Field {
+ // We're only testing for bitfields
+ if field.BitSize == 0 {
+ continue
+ }
+ got := bitInfoDump(field)
+ want := expectedBitOffsets[field.Name]
+ if got != want {
+ t.Errorf("%s: field %s in %s: got info %q want %q", tag, field.Name, t1.StructName, got, want)
+ }
+ }
+ }
+ if e.Tag != TagCompileUnit {
+ r.SkipChildren()
+ }
+ }
+}
+
+var bitfieldTests = map[string]string{
+ "t_another_struct": "struct another_struct {quix short unsigned int@0; xyz [0]int@4; x unsigned int@4 : 1@31; array [40]long long int@8}",
+}
+
+// TestBitFieldZeroArrayIssue50685 checks to make sure that the DWARF
+// type reading code doesn't get confused by the presence of a
+// specifically-sized bitfield member immediately following a field
+// whose type is a zero-length array. Prior to the fix for issue
+// 50685, we would get this type for the case in testdata/bitfields.c:
+//
+// another_struct {quix short unsigned int@0; xyz [-1]int@4; x unsigned int@4 : 1@31; array [40]long long int@8}
+//
+// Note the "-1" for the xyz field, which should be zero.
+func TestBitFieldZeroArrayIssue50685(t *testing.T) {
+ f := "testdata/bitfields.elf4"
+ testTypedefs(t, elfData(t, f), "elf", bitfieldTests)
+}
diff --git a/src/debug/dwarf/typeunit.go b/src/debug/dwarf/typeunit.go
new file mode 100644
index 0000000..8ecf876
--- /dev/null
+++ b/src/debug/dwarf/typeunit.go
@@ -0,0 +1,160 @@
+// 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
+
+import (
+ "fmt"
+ "strconv"
+)
+
+// Parse the type units stored in a DWARF4 .debug_types section. Each
+// type unit defines a single primary type and an 8-byte signature.
+// Other sections may then use formRefSig8 to refer to the type.
+
+// The typeUnit format is a single type with a signature. It holds
+// the same data as a compilation unit.
+type typeUnit struct {
+ unit
+ toff Offset // Offset to signature type within data.
+ name string // Name of .debug_type section.
+ cache Type // Cache the type, nil to start.
+}
+
+// Parse a .debug_types section.
+func (d *Data) parseTypes(name string, types []byte) error {
+ b := makeBuf(d, unknownFormat{}, name, 0, types)
+ for len(b.data) > 0 {
+ base := b.off
+ n, dwarf64 := b.unitLength()
+ if n != Offset(uint32(n)) {
+ b.error("type unit length overflow")
+ return b.err
+ }
+ hdroff := b.off
+ vers := int(b.uint16())
+ if vers != 4 {
+ b.error("unsupported DWARF version " + strconv.Itoa(vers))
+ return b.err
+ }
+ var ao uint64
+ if !dwarf64 {
+ ao = uint64(b.uint32())
+ } else {
+ ao = b.uint64()
+ }
+ atable, err := d.parseAbbrev(ao, vers)
+ if err != nil {
+ return err
+ }
+ asize := b.uint8()
+ sig := b.uint64()
+
+ var toff uint32
+ if !dwarf64 {
+ toff = b.uint32()
+ } else {
+ to64 := b.uint64()
+ if to64 != uint64(uint32(to64)) {
+ b.error("type unit type offset overflow")
+ return b.err
+ }
+ toff = uint32(to64)
+ }
+
+ boff := b.off
+ d.typeSigs[sig] = &typeUnit{
+ unit: unit{
+ base: base,
+ off: boff,
+ data: b.bytes(int(n - (b.off - hdroff))),
+ atable: atable,
+ asize: int(asize),
+ vers: vers,
+ is64: dwarf64,
+ },
+ toff: Offset(toff),
+ name: name,
+ }
+ if b.err != nil {
+ return b.err
+ }
+ }
+ return nil
+}
+
+// Return the type for a type signature.
+func (d *Data) sigToType(sig uint64) (Type, error) {
+ tu := d.typeSigs[sig]
+ if tu == nil {
+ return nil, fmt.Errorf("no type unit with signature %v", sig)
+ }
+ if tu.cache != nil {
+ return tu.cache, nil
+ }
+
+ b := makeBuf(d, tu, tu.name, tu.off, tu.data)
+ r := &typeUnitReader{d: d, tu: tu, b: b}
+ t, err := d.readType(tu.name, r, tu.toff, make(map[Offset]Type), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ tu.cache = t
+ return t, nil
+}
+
+// typeUnitReader is a typeReader for a tagTypeUnit.
+type typeUnitReader struct {
+ d *Data
+ tu *typeUnit
+ b buf
+ err error
+}
+
+// Seek to a new position in the type unit.
+func (tur *typeUnitReader) Seek(off Offset) {
+ tur.err = nil
+ doff := off - tur.tu.off
+ if doff < 0 || doff >= Offset(len(tur.tu.data)) {
+ tur.err = fmt.Errorf("%s: offset %d out of range; max %d", tur.tu.name, doff, len(tur.tu.data))
+ return
+ }
+ 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 {
+ return nil, tur.err
+ }
+ if len(tur.tu.data) == 0 {
+ return nil, nil
+ }
+ e := tur.b.entry(nil, tur.tu.atable, tur.tu.base, tur.tu.vers)
+ if tur.b.err != nil {
+ tur.err = tur.b.err
+ return nil, tur.err
+ }
+ return e, nil
+}
+
+// clone returns a new reader for the type unit.
+func (tur *typeUnitReader) clone() typeReader {
+ return &typeUnitReader{
+ d: tur.d,
+ tu: tur.tu,
+ b: makeBuf(tur.d, tur.tu, tur.tu.name, tur.tu.off, tur.tu.data),
+ }
+}
+
+// offset returns the current offset.
+func (tur *typeUnitReader) offset() Offset {
+ return tur.b.off
+}
diff --git a/src/debug/dwarf/unit.go b/src/debug/dwarf/unit.go
new file mode 100644
index 0000000..8b810d0
--- /dev/null
+++ b/src/debug/dwarf/unit.go
@@ -0,0 +1,137 @@
+// Copyright 2009 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
+
+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
+ data []byte
+ atable abbrevTable
+ asize int
+ vers int
+ utype uint8 // DWARF 5 unit type
+ is64 bool // True for 64-bit DWARF format
+}
+
+// Implement the dataFormat interface.
+
+func (u *unit) version() int {
+ return u.vers
+}
+
+func (u *unit) dwarf64() (bool, bool) {
+ return u.is64, true
+}
+
+func (u *unit) addrsize() int {
+ return u.asize
+}
+
+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.unitLength()
+ if len != Offset(uint32(len)) {
+ b.error("unit length overflow")
+ break
+ }
+ b.skip(int(len))
+ if len > 0 {
+ nunit++
+ }
+ }
+ if b.err != nil {
+ return nil, b.err
+ }
+
+ // Again, this time writing them down.
+ b = makeBuf(d, unknownFormat{}, "info", 0, d.info)
+ units := make([]unit, nunit)
+ for i := range units {
+ u := &units[i]
+ u.base = b.off
+ var n Offset
+ if b.err != nil {
+ return nil, b.err
+ }
+ for n == 0 {
+ n, u.is64 = b.unitLength()
+ }
+ dataOff := b.off
+ vers := b.uint16()
+ if vers < 2 || vers > 5 {
+ b.error("unsupported DWARF version " + strconv.Itoa(int(vers)))
+ break
+ }
+ u.vers = int(vers)
+ if vers >= 5 {
+ u.utype = b.uint8()
+ u.asize = int(b.uint8())
+ }
+ var abbrevOff uint64
+ if u.is64 {
+ abbrevOff = b.uint64()
+ } else {
+ abbrevOff = uint64(b.uint32())
+ }
+ atable, err := d.parseAbbrev(abbrevOff, u.vers)
+ if err != nil {
+ if b.err == nil {
+ b.err = err
+ }
+ break
+ }
+ u.atable = atable
+ if vers < 5 {
+ u.asize = int(b.uint8())
+ }
+
+ switch u.utype {
+ case utSkeleton, utSplitCompile:
+ b.uint64() // unit ID
+ case utType, utSplitType:
+ b.uint64() // type signature
+ if u.is64 { // type offset
+ b.uint64()
+ } else {
+ b.uint32()
+ }
+ }
+
+ u.off = b.off
+ u.data = b.bytes(int(n - (b.off - dataOff)))
+ }
+ if b.err != nil {
+ return nil, b.err
+ }
+ 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
+}