github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/syz-check/dwarf.go (about) 1 // Copyright 2019 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package main 5 6 import ( 7 "debug/dwarf" 8 "debug/elf" 9 "fmt" 10 "runtime" 11 "strings" 12 ) 13 14 func parseKernelObject(obj string) (map[string]*dwarf.StructType, error) { 15 file, err := elf.Open(obj) 16 if err != nil { 17 return nil, err 18 } 19 var sections []*elf.Section 20 for _, sec := range file.Sections { 21 // We don't need these for our purposes and dropping them speeds up parsing a lot. 22 // nolint:misspell 23 if sec.Name == ".debug_line" || strings.HasPrefix(sec.Name, ".rela.") { 24 continue 25 } 26 sections = append(sections, sec) 27 } 28 file.Sections = sections 29 debugInfo, err := file.DWARF() 30 if err != nil { 31 return nil, err 32 } 33 // DWARF parsing in Go is slow, so we parallelize it as much as possible. 34 // First stage extracts top-level compilation units and sends them over unitc. 35 // Next parallel stage consumes units, extracts struct offsets and sends them over offsetc. 36 // Next parallel stage consumes offsets, extracts struct types and sends them over structc. 37 // Last stage consumes structs, deduplicates them and builds the resulting map. 38 numProcs := runtime.GOMAXPROCS(0) 39 numTypes := numProcs / 8 40 if numTypes == 0 { 41 numTypes = 1 42 } 43 buffer := 100 * numProcs 44 unitc := make(chan Unit, buffer) 45 offsetc := make(chan []dwarf.Offset, buffer) 46 structc := make(chan map[string]*dwarf.StructType, buffer) 47 errc := make(chan error) 48 49 go extractCompilationUnits(debugInfo, unitc, errc) 50 51 uniterrc := make(chan error, numProcs) 52 for p := 0; p < numProcs; p++ { 53 go extractOffsets(debugInfo, unitc, offsetc, uniterrc) 54 } 55 go func() { 56 var err error 57 for p := 0; p < numProcs; p++ { 58 if err1 := <-uniterrc; err1 != nil { 59 err = err1 60 } 61 } 62 close(offsetc) 63 errc <- err 64 }() 65 66 structerrc := make(chan error, numTypes) 67 for p := 0; p < numTypes; p++ { 68 // Only parallel extraction of types races with each other, 69 // so we can reuse debugInfo for one of the goroutines. 70 debugInfo1 := debugInfo 71 if p != 0 { 72 debugInfo1 = nil 73 } 74 go extractStructs(file, debugInfo1, offsetc, structc, structerrc) 75 } 76 go func() { 77 var err error 78 for p := 0; p < numTypes; p++ { 79 if err1 := <-structerrc; err1 != nil { 80 err = err1 81 } 82 } 83 close(structc) 84 errc <- err 85 }() 86 87 result := make(map[string]*dwarf.StructType) 88 go func() { 89 for structs := range structc { 90 for name, str := range structs { 91 result[name] = str 92 } 93 } 94 errc <- nil 95 }() 96 97 for i := 0; i < 4; i++ { 98 if err := <-errc; err != nil { 99 return nil, err 100 } 101 } 102 return result, nil 103 } 104 105 type Unit struct { 106 start dwarf.Offset 107 end dwarf.Offset 108 } 109 110 func extractCompilationUnits(debugInfo *dwarf.Data, unitc chan Unit, errc chan error) { 111 defer close(unitc) 112 const sentinel = ^dwarf.Offset(0) 113 prev := sentinel 114 for r := debugInfo.Reader(); ; { 115 ent, err := r.Next() 116 if err != nil { 117 errc <- err 118 return 119 } 120 if ent == nil { 121 if prev != sentinel { 122 unitc <- Unit{prev, sentinel} 123 } 124 errc <- nil 125 break 126 } 127 if ent.Tag != dwarf.TagCompileUnit { 128 errc <- fmt.Errorf("found unexpected tag %v on top level", ent.Tag) 129 return 130 } 131 if prev != sentinel { 132 unitc <- Unit{prev, ent.Offset} 133 } 134 prev = ent.Offset 135 r.SkipChildren() 136 } 137 } 138 139 func extractOffsets(debugInfo *dwarf.Data, unitc chan Unit, offsetc chan []dwarf.Offset, errc chan error) { 140 r := debugInfo.Reader() 141 var offsets []dwarf.Offset 142 for unit := range unitc { 143 r.Seek(unit.start) 144 for { 145 ent, err := r.Next() 146 if err != nil { 147 errc <- err 148 return 149 } 150 if ent == nil || ent.Offset >= unit.end { 151 break 152 } 153 if ent.Tag == dwarf.TagStructType || ent.Tag == dwarf.TagTypedef { 154 offsets = append(offsets, ent.Offset) 155 } 156 if ent.Tag != dwarf.TagCompileUnit { 157 r.SkipChildren() 158 } 159 } 160 offsetc <- offsets 161 offsets = make([]dwarf.Offset, 0, len(offsets)) 162 } 163 errc <- nil 164 } 165 166 func extractStructs(file *elf.File, debugInfo *dwarf.Data, offsetc chan []dwarf.Offset, 167 structc chan map[string]*dwarf.StructType, errc chan error) { 168 if debugInfo == nil { 169 var err error 170 debugInfo, err = file.DWARF() 171 if err != nil { 172 errc <- err 173 return 174 } 175 } 176 var structs map[string]*dwarf.StructType 177 appendStruct := func(str *dwarf.StructType, name string) { 178 if name == "" || str.ByteSize <= 0 { 179 return 180 } 181 if structs == nil { 182 structs = make(map[string]*dwarf.StructType) 183 } 184 structs[name] = str 185 } 186 for offsets := range offsetc { 187 for _, off := range offsets { 188 typ1, err := debugInfo.Type(off) 189 if err != nil { 190 errc <- err 191 return 192 } 193 switch typ := typ1.(type) { 194 case *dwarf.StructType: 195 appendStruct(typ, typ.StructName) 196 case *dwarf.TypedefType: 197 if str, ok := typ.Type.(*dwarf.StructType); ok { 198 appendStruct(str, typ.Name) 199 } 200 default: 201 errc <- fmt.Errorf("got not struct/typedef") 202 return 203 } 204 } 205 structc <- structs 206 structs = nil 207 } 208 errc <- nil 209 }