/
linux.go
180 lines (173 loc) · 6.15 KB
/
linux.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Copyright 2017 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 main
import (
"fmt"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/google/syzkaller/pkg/build"
"github.com/google/syzkaller/pkg/compiler"
"github.com/google/syzkaller/pkg/osutil"
)
type linux struct{}
func (*linux) prepare(sourcedir string, build bool, arches []*Arch) error {
if build {
// Run 'make mrproper', otherwise out-of-tree build fails.
// However, it takes unreasonable amount of time,
// so first check few files and if they are missing hope for best.
for _, a := range arches {
arch := a.target.KernelArch
if osutil.IsExist(filepath.Join(sourcedir, ".config")) ||
osutil.IsExist(filepath.Join(sourcedir, "init/main.o")) ||
osutil.IsExist(filepath.Join(sourcedir, "include/config")) ||
osutil.IsExist(filepath.Join(sourcedir, "include/generated/compile.h")) ||
osutil.IsExist(filepath.Join(sourcedir, "arch", arch, "include", "generated")) {
fmt.Printf("make mrproper ARCH=%v\n", arch)
out, err := osutil.RunCmd(time.Hour, sourcedir, "make", "mrproper", "ARCH="+arch,
"-j", fmt.Sprint(runtime.NumCPU()))
if err != nil {
return fmt.Errorf("make mrproper failed: %w\n%s", err, out)
}
}
}
} else {
if len(arches) > 1 {
return fmt.Errorf("more than 1 arch is invalid without -build")
}
}
return nil
}
func (*linux) prepareArch(arch *Arch) error {
// Kernel misses these headers on some arches.
// So we create empty stubs in buildDir/syzkaller and add -IbuildDir/syzkaller
// as the last flag so it won't override real kernel headers.
for hdr, data := range map[string]string{
// This is the only compiler header kernel uses,
// need to provide it since we use -nostdinc below.
"stdarg.h": `
#pragma once
#define va_list __builtin_va_list
#define va_start __builtin_va_start
#define va_end __builtin_va_end
#define va_arg __builtin_va_arg
#define va_copy __builtin_va_copy
#define __va_copy __builtin_va_copy
`,
"asm/a.out.h": "",
"asm/prctl.h": "",
"asm/mce.h": "",
"asm/msr.h": "",
"uapi/asm/msr.h": "",
} {
fullPath := filepath.Join(arch.buildDir, "syzkaller", hdr)
if err := osutil.MkdirAll(filepath.Dir(fullPath)); err != nil {
return err
}
if err := osutil.WriteFile(fullPath, []byte(data)); err != nil {
return nil
}
}
if !arch.build {
return nil
}
kernelDir := arch.sourceDir
makeArgs := build.LinuxMakeArgs(arch.target, "", "", "", arch.buildDir)
out, err := osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "defconfig")...)
if err != nil {
return fmt.Errorf("make defconfig failed: %w\n%s", err, out)
}
_, err = osutil.RunCmd(time.Minute, arch.buildDir, filepath.Join(kernelDir, "scripts", "config"),
// powerpc arch is configured to be big-endian by default, but we want little-endian powerpc.
// Since all of our archs are little-endian for now, we just blindly switch it.
"-d", "CPU_BIG_ENDIAN", "-e", "CPU_LITTLE_ENDIAN",
// s390 enables BTF in defconfig, but our packaged toolchains can't build it.
"-d", "DEBUG_INFO_BTF",
// Without CONFIG_NETFILTER kernel does not build.
"-e", "NETFILTER",
// include/net/mptcp.h is the only header in kernel that guards some
// of the consts with own config, so we need to enable CONFIG_MPTCP.
"-e", "MPTCP",
// security/smack/smack.h requires this to build.
"-e", "SECURITY",
"-e", "SECURITY_SMACK",
// include/net/nl802154.h does not define some consts without this.
"-e", "IEEE802154", "-e", "IEEE802154_NL802154_EXPERIMENTAL",
)
if err != nil {
return err
}
out, err = osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "olddefconfig")...)
if err != nil {
return fmt.Errorf("make olddefconfig failed: %w\n%s", err, out)
}
out, err = osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "init/main.o")...)
if err != nil {
return fmt.Errorf("make failed: %w\n%s", err, out)
}
return nil
}
// nolint: goconst
func (*linux) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint64, map[string]bool, error) {
headerArch := arch.target.KernelHeaderArch
sourceDir := arch.sourceDir
buildDir := arch.buildDir
args := []string{
// This makes the build completely hermetic, only kernel headers are used.
"-nostdinc",
"-w", "-fmessage-length=0",
"-O3", // required to get expected values for some __builtin_constant_p
"-I.",
"-D__KERNEL__",
"-DKBUILD_MODNAME=\"-\"",
"-I" + sourceDir + "/arch/" + headerArch + "/include",
"-I" + buildDir + "/arch/" + headerArch + "/include/generated/uapi",
"-I" + buildDir + "/arch/" + headerArch + "/include/generated",
"-I" + sourceDir + "/arch/" + headerArch + "/include/asm/mach-malta",
"-I" + sourceDir + "/arch/" + headerArch + "/include/asm/mach-generic",
"-I" + buildDir + "/include",
"-I" + sourceDir + "/include",
"-I" + sourceDir + "/arch/" + headerArch + "/include/uapi",
"-I" + sourceDir + "/include/uapi",
"-I" + buildDir + "/include/generated/uapi",
"-I" + sourceDir,
"-I" + sourceDir + "/include/linux",
"-I" + buildDir + "/syzkaller",
"-include", sourceDir + "/include/linux/kconfig.h",
}
args = append(args, arch.target.CFlags...)
for _, incdir := range info.Incdirs {
args = append(args, "-I"+sourceDir+"/"+incdir)
}
if arch.includeDirs != "" {
for _, dir := range strings.Split(arch.includeDirs, ",") {
args = append(args, "-I"+dir)
}
}
params := &extractParams{
AddSource: "#include <asm/unistd.h>",
ExtractFromELF: true,
TargetEndian: arch.target.HostEndian,
}
cc := arch.target.CCompiler
res, undeclared, err := extract(info, cc, args, params)
if err != nil {
return nil, nil, err
}
if arch.target.PtrSize == 4 {
// mmap syscall on i386/arm is translated to old_mmap and has different signature.
// As a workaround fix it up to mmap2, which has signature that we expect.
// pkg/csource has the same hack.
const mmap = "__NR_mmap"
const mmap2 = "__NR_mmap2"
if res[mmap] != 0 || undeclared[mmap] {
if res[mmap2] == 0 {
return nil, nil, fmt.Errorf("%v is missing", mmap2)
}
res[mmap] = res[mmap2]
delete(undeclared, mmap)
}
}
return res, undeclared, nil
}