github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/cover/backend/modules.go (about) 1 // Copyright 2021 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 "bytes" 8 "debug/elf" 9 "fmt" 10 "os" 11 "path/filepath" 12 "strings" 13 14 "github.com/google/syzkaller/pkg/log" 15 "github.com/google/syzkaller/sys/targets" 16 ) 17 18 type KernelModule struct { 19 Name string 20 Addr uint64 21 Size uint64 22 } 23 24 func discoverModules(target *targets.Target, objDir string, moduleObj []string, 25 hostModules []KernelModule) ( 26 []*Module, error) { 27 modules := []*Module{ 28 // A dummy module representing the kernel itself. 29 {Path: filepath.Join(objDir, target.KernelObject)}, 30 } 31 if target.OS == targets.Linux { 32 modules1, err := discoverModulesLinux(append([]string{objDir}, moduleObj...), 33 hostModules) 34 if err != nil { 35 return nil, err 36 } 37 modules = append(modules, modules1...) 38 } else if len(hostModules) != 0 { 39 return nil, fmt.Errorf("%v coverage does not support modules", target.OS) 40 } 41 return modules, nil 42 } 43 44 func discoverModulesLinux(dirs []string, hostModules []KernelModule) ([]*Module, error) { 45 paths, err := locateModules(dirs) 46 if err != nil { 47 return nil, err 48 } 49 var modules []*Module 50 for _, mod := range hostModules { 51 path := paths[mod.Name] 52 if path == "" { 53 log.Logf(0, "failed to discover module %v", mod.Name) 54 continue 55 } 56 log.Logf(0, "module %v -> %v", mod.Name, path) 57 modules = append(modules, &Module{ 58 Name: mod.Name, 59 Addr: mod.Addr, 60 Path: path, 61 }) 62 } 63 return modules, nil 64 } 65 66 func locateModules(dirs []string) (map[string]string, error) { 67 paths := make(map[string]string) 68 for _, dir := range dirs { 69 err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { 70 if err != nil || filepath.Ext(path) != ".ko" { 71 return err 72 } 73 name, err := getModuleName(path) 74 if err != nil { 75 // Extracting module name involves parsing ELF and binary data, 76 // let's not fail on it, we still have the file name, 77 // which is usually the right module name. 78 log.Logf(0, "failed to get %v module name: %v", path, err) 79 name = strings.TrimSuffix(filepath.Base(path), "."+filepath.Ext(path)) 80 } 81 // Order of dirs determine priority, so don't overwrite already discovered names. 82 if name != "" && paths[name] == "" { 83 paths[name] = path 84 } 85 return nil 86 }) 87 if err != nil { 88 return nil, err 89 } 90 } 91 return paths, nil 92 } 93 94 func getModuleName(path string) (string, error) { 95 file, err := elf.Open(path) 96 if err != nil { 97 return "", err 98 } 99 defer file.Close() 100 section := file.Section(".modinfo") 101 if section == nil { 102 return "", fmt.Errorf("no .modinfo section") 103 } 104 data, err := section.Data() 105 if err != nil { 106 return "", fmt.Errorf("failed to read .modinfo: %w", err) 107 } 108 if name := searchModuleName(data); name != "" { 109 return name, nil 110 } 111 section = file.Section(".gnu.linkonce.this_module") 112 if section == nil { 113 return "", fmt.Errorf("no .gnu.linkonce.this_module section") 114 } 115 data, err = section.Data() 116 if err != nil { 117 return "", fmt.Errorf("failed to read .gnu.linkonce.this_module: %w", err) 118 } 119 return string(data), nil 120 } 121 122 func searchModuleName(data []byte) string { 123 data = append([]byte{0}, data...) 124 key := []byte("\x00name=") 125 pos := bytes.Index(data, key) 126 if pos == -1 { 127 return "" 128 } 129 end := bytes.IndexByte(data[pos+len(key):], 0) 130 if end == -1 { 131 return "" 132 } 133 end = pos + len(key) + end 134 if end > len(data) { 135 return "" 136 } 137 return string(data[pos+len(key) : end]) 138 }