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