github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/cover/backend/mach-o.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/macho" 8 "fmt" 9 "path/filepath" 10 "sort" 11 "strings" 12 13 "github.com/google/syzkaller/sys/targets" 14 ) 15 16 func makeMachO(target *targets.Target, objDir, srcDir, buildDir string, 17 moduleObj []string, hostModules []KernelModule) (*Impl, error) { 18 return makeDWARF(&dwarfParams{ 19 target: target, 20 objDir: objDir, 21 srcDir: srcDir, 22 buildDir: buildDir, 23 moduleObj: moduleObj, 24 hostModules: hostModules, 25 readSymbols: machoReadSymbols, 26 readTextData: machoReadTextData, 27 readModuleCoverPoints: machoReadModuleCoverPoints, 28 readTextRanges: machoReadTextRanges, 29 }) 30 } 31 32 func machoReadSymbols(module *Module, info *symbolInfo) ([]*Symbol, error) { 33 file, err := macho.Open(module.Path) 34 if err != nil { 35 return nil, err 36 } 37 text := file.Section("__text") 38 if text == nil { 39 return nil, fmt.Errorf("no __text section in the object file") 40 } 41 if file.Symtab == nil { 42 return nil, fmt.Errorf("failed to read Mach-O symbols") 43 } 44 info.textAddr = text.Addr 45 46 // We don't get symbol lengths or symbol ends in Mach-O symbols. So we 47 // guesstimate them by taking the next symbols beginning -1. That only 48 // works after we have sorted them. 49 sort.Slice(file.Symtab.Syms, func(i, j int) bool { 50 return file.Symtab.Syms[i].Value < file.Symtab.Syms[j].Value 51 }) 52 53 var symbols []*Symbol 54 for i, symb := range file.Symtab.Syms { 55 // Mach-Os doesn't contain the Symbol size like in ELF 56 symbEnd := text.Addr + text.Size 57 if i < len(file.Symtab.Syms)-1 { 58 symbEnd = file.Symtab.Syms[i+1].Value 59 } 60 61 text := symb.Value >= text.Addr && symbEnd <= text.Addr+text.Size 62 if text { 63 symbStart := symb.Value + module.Addr 64 symbols = append(symbols, &Symbol{ 65 Module: module, 66 ObjectUnit: ObjectUnit{ 67 Name: symb.Name, 68 }, 69 Start: symbStart, 70 End: symbEnd, 71 }) 72 } 73 if strings.HasPrefix(symb.Name, "___sanitizer_cov_trace_") { 74 if symb.Name == "___sanitizer_cov_trace_pc_guard" { 75 info.tracePCIdx[i] = true 76 if text { 77 info.tracePC[symb.Value] = true 78 } 79 } else { 80 info.traceCmpIdx[i] = true 81 if text { 82 info.traceCmp[symb.Value] = true 83 } 84 } 85 } 86 } 87 return symbols, nil 88 } 89 90 func machoReadTextRanges(module *Module) ([]pcRange, []*CompileUnit, error) { 91 dir, kernel := filepath.Split(module.Path) 92 dSYMPath := filepath.Join(dir, fmt.Sprintf( 93 "%[1]s.dSYM/Contents/Resources/DWARF/%[1]s", kernel)) 94 dSYM, err := macho.Open(dSYMPath) 95 if err != nil { 96 return nil, nil, err 97 } 98 debugInfo, err := dSYM.DWARF() 99 if err != nil { 100 return nil, nil, fmt.Errorf("failed to parse DWARF: %w", err) 101 } 102 return readTextRanges(debugInfo, module, nil) 103 } 104 105 func machoReadTextData(module *Module) ([]byte, error) { 106 file, err := macho.Open(module.Path) 107 if err != nil { 108 return nil, err 109 } 110 text := file.Section("__text") 111 if text == nil { 112 return nil, fmt.Errorf("no __text section in the object file") 113 } 114 return text.Data() 115 } 116 117 func machoReadModuleCoverPoints(target *targets.Target, module *Module, info *symbolInfo) ([2][]uint64, error) { 118 // TODO: Linux/ELF supports module symbols. We should probably also do that 119 // for XNU/Mach-O. To maximize code re-use we already have a lot of the 120 // plumbing for module support. I think we mainly miss an equivalent to 121 // discoverModules and this function at the moment. 122 return [2][]uint64{}, fmt.Errorf("machoReadModuleCoverPoints not implemented") 123 }