github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/cover/backend/elf.go (about) 1 // Copyright 2020 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 backend 5 6 import ( 7 "debug/elf" 8 "encoding/binary" 9 "fmt" 10 "io" 11 "path/filepath" 12 "strings" 13 14 "github.com/google/syzkaller/pkg/log" 15 "github.com/google/syzkaller/pkg/mgrconfig" 16 "github.com/google/syzkaller/pkg/vminfo" 17 "github.com/google/syzkaller/sys/targets" 18 ) 19 20 func makeELF(target *targets.Target, kernelDirs *mgrconfig.KernelDirs, splitBuildDelimiters, moduleObj []string, 21 hostModules []*vminfo.KernelModule) (*Impl, error) { 22 return makeDWARF(&dwarfParams{ 23 target: target, 24 kernelDirs: kernelDirs, 25 splitBuildDelimiters: splitBuildDelimiters, 26 moduleObj: moduleObj, 27 hostModules: hostModules, 28 readSymbols: elfReadSymbols, 29 readTextData: elfReadTextData, 30 readModuleCoverPoints: elfReadModuleCoverPoints, 31 readTextRanges: elfReadTextRanges, 32 getCompilerVersion: elfGetCompilerVersion, 33 }) 34 } 35 36 const ( 37 TraceCbNone int = iota 38 TraceCbPc 39 TraceCbCmp 40 ) 41 42 // Normally, -fsanitize-coverage=trace-pc inserts calls to __sanitizer_cov_trace_pc() at the 43 // beginning of every basic block. -fsanitize-coverage=trace-cmp adds calls to other functions, 44 // like __sanitizer_cov_trace_cmp1() or __sanitizer_cov_trace_const_cmp4(). 45 // 46 // On ARM64 there can be additional symbol names inserted by the linker. By default, BL instruction 47 // can only target addresses within the +/-128M range from PC. To target farther addresses, the 48 // ARM64 linker inserts so-called veneers that act as trampolines for functions. We count calls to 49 // such veneers as normal calls to __sanitizer_cov_trace_XXX. 50 func getTraceCallbackType(name string) int { 51 if name == "__sanitizer_cov_trace_pc" || name == "____sanitizer_cov_trace_pc_veneer" { 52 return TraceCbPc 53 } 54 if strings.HasPrefix(name, "__sanitizer_cov_trace_") || 55 (strings.HasPrefix(name, "____sanitizer_cov_trace_") && strings.HasSuffix(name, "_veneer")) { 56 return TraceCbCmp 57 } 58 return TraceCbNone 59 } 60 61 func elfReadSymbols(module *vminfo.KernelModule, info *symbolInfo) ([]*Symbol, error) { 62 file, err := elf.Open(module.Path) 63 if err != nil { 64 return nil, err 65 } 66 defer file.Close() 67 text := file.Section(".text") 68 if text == nil { 69 return nil, fmt.Errorf("no .text section in the object file") 70 } 71 allSymbols, err := file.Symbols() 72 if err != nil { 73 return nil, fmt.Errorf("failed to read ELF symbols: %w", err) 74 } 75 info.textAddr = text.Addr 76 var symbols []*Symbol 77 for i, symb := range allSymbols { 78 if symb.Info&0xf != uint8(elf.STT_FUNC) && symb.Info&0xf != uint8(elf.STT_NOTYPE) { 79 // Only save STT_FUNC, STT_NONE otherwise some symb range inside another symb range. 80 continue 81 } 82 text := symb.Value >= text.Addr && symb.Value+symb.Size <= text.Addr+text.Size 83 if text && symb.Size != 0 { 84 start := symb.Value 85 if module.Name != "" { 86 start += module.Addr 87 } 88 symbols = append(symbols, &Symbol{ 89 Module: module, 90 ObjectUnit: ObjectUnit{ 91 Name: symb.Name, 92 }, 93 Start: start, 94 End: start + symb.Size, 95 }) 96 } 97 switch getTraceCallbackType(symb.Name) { 98 case TraceCbPc: 99 info.tracePCIdx[i] = true 100 if text { 101 info.tracePC[symb.Value] = true 102 } 103 case TraceCbCmp: 104 info.traceCmpIdx[i] = true 105 if text { 106 info.traceCmp[symb.Value] = true 107 } 108 } 109 } 110 return symbols, nil 111 } 112 113 func elfReadTextRanges(module *vminfo.KernelModule) ([]pcRange, []*CompileUnit, error) { 114 file, err := elf.Open(module.Path) 115 if err != nil { 116 return nil, nil, err 117 } 118 defer file.Close() 119 text := file.Section(".text") 120 if text == nil { 121 return nil, nil, fmt.Errorf("no .text section in the object file") 122 } 123 kaslr := file.Section(".rela.text") != nil 124 debugInfo, err := file.DWARF() 125 if err != nil { 126 if module.Name != "" { 127 log.Logf(0, "ignoring module %v without DEBUG_INFO", module.Name) 128 return nil, nil, nil 129 } 130 return nil, nil, fmt.Errorf("failed to parse DWARF: %w (set CONFIG_DEBUG_INFO=y on linux)", err) 131 } 132 133 var pcFix pcFixFn 134 if kaslr { 135 pcFix = func(r [2]uint64) ([2]uint64, bool) { 136 if r[0] >= r[1] || r[0] < text.Addr || r[1] > text.Addr+text.Size { 137 // Linux kernel binaries with CONFIG_RANDOMIZE_BASE=y are strange. 138 // .text starts at 0xffffffff81000000 and symbols point there as 139 // well, but PC ranges point to addresses around 0. 140 // So try to add text offset and retry the check. 141 // It's unclear if we also need some offset on top of text.Addr, 142 // it gives approximately correct addresses, but not necessary 143 // precisely correct addresses. 144 r[0] += text.Addr 145 r[1] += text.Addr 146 if r[0] >= r[1] || r[0] < text.Addr || r[1] > text.Addr+text.Size { 147 return r, true 148 } 149 } 150 return r, false 151 } 152 } 153 154 return readTextRanges(debugInfo, module, pcFix) 155 } 156 157 func elfReadTextData(module *vminfo.KernelModule) ([]byte, error) { 158 file, err := elf.Open(module.Path) 159 if err != nil { 160 return nil, err 161 } 162 defer file.Close() 163 text := file.Section(".text") 164 if text == nil { 165 return nil, fmt.Errorf("no .text section in the object file") 166 } 167 return text.Data() 168 } 169 170 func elfReadModuleCoverPoints(target *targets.Target, module *vminfo.KernelModule, info *symbolInfo) ([2][]uint64, 171 error) { 172 var pcs [2][]uint64 173 file, err := elf.Open(module.Path) 174 if err != nil { 175 return pcs, err 176 } 177 defer file.Close() 178 callRelocType := arches[target.Arch].callRelocType 179 relaOffset := arches[target.Arch].relaOffset 180 for _, s := range file.Sections { 181 if s.Type != elf.SHT_RELA { // nolint: misspell 182 continue 183 } 184 rel := new(elf.Rela64) 185 for r := s.Open(); ; { 186 if err := binary.Read(r, binary.LittleEndian, rel); err != nil { 187 if err == io.EOF { 188 break 189 } 190 return pcs, err 191 } 192 if (rel.Info & 0xffffffff) != callRelocType { 193 continue 194 } 195 pc := module.Addr + rel.Off - relaOffset 196 index := int(elf.R_SYM64(rel.Info)) - 1 197 if info.tracePCIdx[index] { 198 pcs[0] = append(pcs[0], pc) 199 } else if info.traceCmpIdx[index] { 200 pcs[1] = append(pcs[1], pc) 201 } 202 } 203 } 204 return pcs, nil 205 } 206 207 func elfGetCompilerVersion(path string) string { 208 file, err := elf.Open(path) 209 if err != nil { 210 return "" 211 } 212 defer file.Close() 213 sec := file.Section(".comment") 214 if sec == nil { 215 return "" 216 } 217 data, err := sec.Data() 218 if err != nil { 219 return "" 220 } 221 return string(data[:]) 222 } 223 224 func elfReadTextSecRange(module *vminfo.KernelModule) (*SecRange, error) { 225 text, err := elfReadTextSec(module) 226 if err != nil { 227 return nil, err 228 } 229 r := &SecRange{ 230 Start: text.Addr, 231 End: text.Addr + text.Size, 232 } 233 return r, nil 234 } 235 236 func elfReadTextSec(module *vminfo.KernelModule) (*elf.Section, error) { 237 file, err := elf.Open(module.Path) 238 if err != nil { 239 return nil, err 240 } 241 defer file.Close() 242 text := file.Section(".text") 243 if text == nil { 244 return nil, fmt.Errorf("no .text section in the object file") 245 } 246 return text, nil 247 } 248 249 func getLinuxPCBase(cfg *mgrconfig.Config) (uint64, error) { 250 bin := filepath.Join(cfg.KernelObj, cfg.SysTarget.KernelObject) 251 file, err := elf.Open(bin) 252 if err != nil { 253 return 0, err 254 } 255 defer file.Close() 256 allSymbols, err := file.Symbols() 257 if err != nil { 258 return 0, err 259 } 260 for _, sym := range allSymbols { 261 if sym.Name == "_stext" { 262 return sym.Value, nil 263 } 264 } 265 return 0, fmt.Errorf("no _stext symbol") 266 }