github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/dwarf/reader/reader.go (about) 1 package reader 2 3 import ( 4 "debug/dwarf" 5 "errors" 6 "fmt" 7 8 "github.com/go-delve/delve/pkg/dwarf/godwarf" 9 "github.com/go-delve/delve/pkg/dwarf/op" 10 ) 11 12 type Reader struct { 13 *dwarf.Reader 14 depth int 15 } 16 17 // New returns a reader for the specified dwarf data. 18 func New(data *dwarf.Data) *Reader { 19 return &Reader{data.Reader(), 0} 20 } 21 22 // Seek moves the reader to an arbitrary offset. 23 func (reader *Reader) Seek(off dwarf.Offset) { 24 reader.depth = 0 25 reader.Reader.Seek(off) 26 } 27 28 // SeekToEntry moves the reader to an arbitrary entry. 29 func (reader *Reader) SeekToEntry(entry *dwarf.Entry) error { 30 reader.Seek(entry.Offset) 31 // Consume the current entry so .Next works as intended 32 _, err := reader.Next() 33 return err 34 } 35 36 // AddrFor returns the address for the named entry. 37 func (reader *Reader) AddrFor(name string, staticBase uint64, ptrSize int) (uint64, error) { 38 entry, err := reader.FindEntryNamed(name, false) 39 if err != nil { 40 return 0, err 41 } 42 instructions, ok := entry.Val(dwarf.AttrLocation).([]byte) 43 if !ok { 44 return 0, fmt.Errorf("type assertion failed") 45 } 46 addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{StaticBase: staticBase}, instructions, ptrSize, nil) 47 if err != nil { 48 return 0, err 49 } 50 return uint64(addr), nil 51 } 52 53 var ErrTypeNotFound = errors.New("no type entry found, use 'types' for a list of valid types") 54 55 // SeekToType moves the reader to the type specified by the entry, 56 // optionally resolving typedefs and pointer types. If the reader is set 57 // to a struct type the NextMemberVariable call can be used to walk all member data. 58 func (reader *Reader) SeekToType(entry *dwarf.Entry, resolveTypedefs bool, resolvePointerTypes bool) (*dwarf.Entry, error) { 59 offset, ok := entry.Val(dwarf.AttrType).(dwarf.Offset) 60 if !ok { 61 return nil, fmt.Errorf("entry does not have a type attribute") 62 } 63 64 // Seek to the first type offset 65 reader.Seek(offset) 66 67 // Walk the types to the base 68 for typeEntry, err := reader.Next(); typeEntry != nil; typeEntry, err = reader.Next() { 69 if err != nil { 70 return nil, err 71 } 72 73 if typeEntry.Tag == dwarf.TagTypedef && !resolveTypedefs { 74 return typeEntry, nil 75 } 76 77 if typeEntry.Tag == dwarf.TagPointerType && !resolvePointerTypes { 78 return typeEntry, nil 79 } 80 81 offset, ok = typeEntry.Val(dwarf.AttrType).(dwarf.Offset) 82 if !ok { 83 return typeEntry, nil 84 } 85 86 reader.Seek(offset) 87 } 88 89 return nil, ErrTypeNotFound 90 } 91 92 func (reader *Reader) NextType() (*dwarf.Entry, error) { 93 for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { 94 if err != nil { 95 return nil, err 96 } 97 98 switch entry.Tag { 99 case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType: 100 return entry, nil 101 } 102 } 103 104 return nil, nil 105 } 106 107 // SeekToTypeNamed moves the reader to the type specified by the name. 108 // If the reader is set to a struct type the NextMemberVariable call 109 // can be used to walk all member data. 110 func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) { 111 // Walk the types to the base 112 for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() { 113 if err != nil { 114 return nil, err 115 } 116 117 n, ok := entry.Val(dwarf.AttrName).(string) 118 if !ok { 119 continue 120 } 121 122 if n == name { 123 return entry, nil 124 } 125 } 126 127 return nil, ErrTypeNotFound 128 } 129 130 // FindEntryNamed finds the entry for 'name'. 131 func (reader *Reader) FindEntryNamed(name string, member bool) (*dwarf.Entry, error) { 132 depth := 1 133 for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { 134 if err != nil { 135 return nil, err 136 } 137 138 if entry.Children { 139 depth++ 140 } 141 142 if entry.Tag == 0 { 143 depth-- 144 if depth <= 0 { 145 return nil, fmt.Errorf("could not find symbol value for %s", name) 146 } 147 } 148 149 if member { 150 if entry.Tag != dwarf.TagMember { 151 continue 152 } 153 } else { 154 if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagStructType { 155 continue 156 } 157 } 158 159 n, ok := entry.Val(dwarf.AttrName).(string) 160 if !ok || n != name { 161 continue 162 } 163 return entry, nil 164 } 165 return nil, fmt.Errorf("could not find symbol value for %s", name) 166 } 167 168 func (reader *Reader) InstructionsForEntryNamed(name string, member bool) ([]byte, error) { 169 entry, err := reader.FindEntryNamed(name, member) 170 if err != nil { 171 return nil, err 172 } 173 var attr dwarf.Attr 174 if member { 175 attr = dwarf.AttrDataMemberLoc 176 } else { 177 attr = dwarf.AttrLocation 178 } 179 instr, ok := entry.Val(attr).([]byte) 180 if !ok { 181 return nil, errors.New("invalid typecast for Dwarf instructions") 182 } 183 return instr, nil 184 } 185 186 func (reader *Reader) InstructionsForEntry(entry *dwarf.Entry) ([]byte, error) { 187 if entry.Tag == dwarf.TagMember { 188 instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte) 189 if !ok { 190 return nil, fmt.Errorf("member data has no data member location attribute") 191 } 192 // clone slice to prevent stomping on the dwarf data 193 return append([]byte{}, instructions...), nil 194 } 195 196 // non-member 197 instructions, ok := entry.Val(dwarf.AttrLocation).([]byte) 198 if !ok { 199 return nil, fmt.Errorf("entry has no location attribute") 200 } 201 202 // clone slice to prevent stomping on the dwarf data 203 return append([]byte{}, instructions...), nil 204 } 205 206 // NextMemberVariable moves the reader to the next debug entry that describes a member variable and returns the entry. 207 func (reader *Reader) NextMemberVariable() (*dwarf.Entry, error) { 208 for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { 209 if err != nil { 210 return nil, err 211 } 212 213 // All member variables will be at the same depth 214 reader.SkipChildren() 215 216 // End of the current depth 217 if entry.Tag == 0 { 218 break 219 } 220 221 if entry.Tag == dwarf.TagMember { 222 return entry, nil 223 } 224 } 225 226 // No more items 227 return nil, nil 228 } 229 230 // NextPackageVariable moves the reader to the next debug entry that describes a package variable. 231 // Any TagVariable entry that is not inside a sub program entry and is marked external is considered a package variable. 232 func (reader *Reader) NextPackageVariable() (*dwarf.Entry, error) { 233 for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { 234 if err != nil { 235 return nil, err 236 } 237 238 if entry.Tag == dwarf.TagVariable { 239 ext, ok := entry.Val(dwarf.AttrExternal).(bool) 240 if ok && ext { 241 return entry, nil 242 } 243 } 244 245 // Ignore everything inside sub programs 246 if entry.Tag == dwarf.TagSubprogram { 247 reader.SkipChildren() 248 } 249 } 250 251 // No more items 252 return nil, nil 253 } 254 255 func (reader *Reader) NextCompileUnit() (*dwarf.Entry, error) { 256 for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { 257 if err != nil { 258 return nil, err 259 } 260 261 if entry.Tag == dwarf.TagCompileUnit { 262 return entry, nil 263 } 264 } 265 266 return nil, nil 267 } 268 269 // InlineStack returns the stack of inlined calls for the specified function 270 // and PC address. 271 // If pc is 0 then all inlined calls will be returned. 272 func InlineStack(root *godwarf.Tree, pc uint64) []*godwarf.Tree { 273 v := []*godwarf.Tree{} 274 for _, child := range root.Children { 275 v = inlineStackInternal(v, child, pc) 276 } 277 return v 278 } 279 280 // inlineStackInternal precalculates the inlined call stack for pc 281 // If pc == 0 then all inlined calls will be returned 282 // Otherwise an inlined call will be returned if its range, or 283 // the range of one of its child entries contains irdr.pc. 284 // The recursive calculation of range inclusion is necessary because 285 // sometimes when doing midstack inlining the Go compiler emits the toplevel 286 // inlined call with ranges that do not cover the inlining of nested inlined 287 // calls. 288 // For example if a function A calls B which calls C and both the calls to 289 // B and C are inlined the DW_AT_inlined_subroutine entry for A might have 290 // ranges that do not cover the ranges of the inlined call to C. 291 // This is probably a violation of the DWARF standard (it's unclear) but we 292 // might as well support it as best as possible anyway. 293 func inlineStackInternal(stack []*godwarf.Tree, n *godwarf.Tree, pc uint64) []*godwarf.Tree { 294 switch n.Tag { 295 case dwarf.TagSubprogram, dwarf.TagInlinedSubroutine, dwarf.TagLexDwarfBlock: 296 if pc == 0 || n.ContainsPC(pc) { 297 for _, child := range n.Children { 298 stack = inlineStackInternal(stack, child, pc) 299 } 300 if n.Tag == dwarf.TagInlinedSubroutine { 301 stack = append(stack, n) 302 } 303 } 304 } 305 return stack 306 }