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