diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-09-14 15:25:06 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-09-15 16:02:37 +0200 |
| commit | 4503776d2bc15a9e2738402760ff731252e4d2ec (patch) | |
| tree | fed90e104c93c02675e34ca4a4a41498239dd844 /sys | |
| parent | 7296cf374d9ec92c394edf965d5347742fe5aed9 (diff) | |
sys/syz-extract: generate build files out of tree
This does not pollute user kernel dir (we do make mrproper, though)
and enables parallel generation.
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/syz-extract/extract.go | 172 | ||||
| -rw-r--r-- | sys/syz-extract/fetch.go | 37 |
2 files changed, 139 insertions, 70 deletions
diff --git a/sys/syz-extract/extract.go b/sys/syz-extract/extract.go index 707b77e58..52a0ae7f0 100644 --- a/sys/syz-extract/extract.go +++ b/sys/syz-extract/extract.go @@ -26,16 +26,31 @@ var ( flagLinux = flag.String("linux", "", "path to linux kernel source checkout") flagLinuxBld = flag.String("linuxbld", "", "path to linux kernel build directory") flagArch = flag.String("arch", "", "comma-separated list of arches to generate (all by default)") - flagBuild = flag.Bool("build", false, "generate arch-specific files in the linux dir") + flagBuild = flag.Bool("build", false, "regenerate arch-specific kernel headers") ) +type Arch struct { + target *sys.Target + kernelDir string + buildDir string + build bool + files []*File + err error +} + type File struct { + arch *Arch name string undeclared map[string]bool err error } func main() { + failf := func(msg string, args ...interface{}) { + fmt.Fprintf(os.Stderr, msg+"\n", args...) + os.Exit(1) + } + const OS = "linux" flag.Parse() if *flagLinux == "" { @@ -48,49 +63,105 @@ func main() { if n == 0 { failf("usage: syz-extract -linux=/linux/checkout -arch=arch input_file.txt...") } - var arches []string + + var archArray []string if *flagArch != "" { - arches = strings.Split(*flagArch, ",") + archArray = strings.Split(*flagArch, ",") } else { for arch := range sys.Targets[OS] { - arches = append(arches, arch) + archArray = append(archArray, arch) } - sort.Strings(arches) + sort.Strings(archArray) } - for _, arch := range arches { - fmt.Printf("generating %v/%v...\n", OS, arch) - target := sys.Targets[OS][arch] - if target == nil { - failf("unknown arch %v", arch) + + if *flagBuild { + // Otherwise out-of-tree build fails. + fmt.Printf("make mrproper\n") + out, err := osutil.RunCmd(time.Hour, *flagLinux, "make", "mrproper") + if err != nil { + failf("make mrproper failed: %v\n%s\n", err, out) } + } else { + if len(archArray) > 1 { + failf("more than 1 arch is invalid without -build") + } + } + + jobC := make(chan interface{}, len(archArray)*len(flag.Args())) + var wg sync.WaitGroup + + var arches []*Arch + for _, archStr := range archArray { + buildDir := "" if *flagBuild { - buildKernel(target, *flagLinux) - *flagLinuxBld = *flagLinux - } else if *flagLinuxBld == "" { - *flagLinuxBld = *flagLinux + dir, err := ioutil.TempDir("", "syzkaller-kernel-build") + if err != nil { + failf("failed to create temp dir: %v", err) + } + buildDir = dir + } else if *flagLinuxBld != "" { + buildDir = *flagLinuxBld + } else { + buildDir = *flagLinux + } + + target := sys.Targets[OS][archStr] + if target == nil { + failf("unknown arch: %v", archStr) } - files := make([]File, n) - inc := make(chan *File, n) - for i, f := range flag.Args() { - files[i].name = f - inc <- &files[i] + arch := &Arch{ + target: target, + kernelDir: *flagLinux, + buildDir: buildDir, + build: *flagBuild, } - close(inc) - - procs := runtime.GOMAXPROCS(0) - var wg sync.WaitGroup - wg.Add(procs) - for p := 0; p < procs; p++ { - go func() { - defer wg.Done() - for f := range inc { - f.undeclared, f.err = processFile(target, f.name) + for _, f := range flag.Args() { + arch.files = append(arch.files, &File{ + arch: arch, + name: f, + }) + } + arches = append(arches, arch) + jobC <- arch + wg.Add(1) + } + + for p := 0; p < runtime.GOMAXPROCS(0); p++ { + go func() { + for job := range jobC { + switch j := job.(type) { + case *Arch: + if j.build { + j.err = buildKernel(j) + } + if j.err == nil { + for _, f := range j.files { + wg.Add(1) + jobC <- f + } + } + case *File: + j.undeclared, j.err = processFile(j.arch, j.name) } - }() + wg.Done() + } + }() + } + wg.Wait() + + for _, arch := range arches { + if arch.build { + os.RemoveAll(arch.buildDir) } - wg.Wait() - for _, f := range files { + } + + for _, arch := range arches { + fmt.Printf("generating %v/%v...\n", arch.target.OS, arch.target.Arch) + if arch.err != nil { + failf("%v", arch.err) + } + for _, f := range arch.files { fmt.Printf("extracting from %v\n", f.name) if f.err != nil { failf("%v", f.err) @@ -103,8 +174,8 @@ func main() { } } -func processFile(target *sys.Target, inname string) (map[string]bool, error) { - outname := strings.TrimSuffix(inname, ".txt") + "_" + target.Arch + ".const" +func processFile(arch *Arch, inname string) (map[string]bool, error) { + outname := strings.TrimSuffix(inname, ".txt") + "_" + arch.target.Arch + ".const" indata, err := ioutil.ReadFile(inname) if err != nil { return nil, fmt.Errorf("failed to read input file: %v", err) @@ -125,7 +196,7 @@ func processFile(target *sys.Target, inname string) (map[string]bool, error) { return nil, nil } includes := append(info.Includes, "asm/unistd.h") - consts, undeclared, err := fetchValues(target, info.Consts, includes, info.Incdirs, info.Defines) + consts, undeclared, err := fetchValues(arch.target, arch.kernelDir, arch.buildDir, info.Consts, includes, info.Incdirs, info.Defines) if err != nil { return nil, err } @@ -136,36 +207,33 @@ func processFile(target *sys.Target, inname string) (map[string]bool, error) { return undeclared, nil } -func buildKernel(target *sys.Target, dir string) { - // TODO(dvyukov): use separate temp build dir. - // This will allow to do build for all archs in parallel and - // won't destroy user's build state. +func buildKernel(arch *Arch) error { + target := arch.target + kernelDir := arch.kernelDir + buildDir := arch.buildDir makeArgs := []string{ "ARCH=" + target.KernelArch, "CROSS_COMPILE=" + target.CCompilerPrefix, "CFLAGS=" + strings.Join(target.CrossCFlags, " "), + "O=" + buildDir, } - out, err := osutil.RunCmd(time.Hour, dir, "make", append(makeArgs, "defconfig")...) + out, err := osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "defconfig")...) if err != nil { - failf("make defconfig failed: %v\n%s\n", err, out) + return fmt.Errorf("make defconfig failed: %v\n%s\n", err, out) } // Without CONFIG_NETFILTER kernel does not build. - out, err = osutil.RunCmd(time.Minute, dir, "sed", "-i", + out, err = osutil.RunCmd(time.Minute, buildDir, "sed", "-i", "s@# CONFIG_NETFILTER is not set@CONFIG_NETFILTER=y@g", ".config") if err != nil { - failf("sed .config failed: %v\n%s\n", err, out) + return fmt.Errorf("sed .config failed: %v\n%s\n", err, out) } - out, err = osutil.RunCmd(time.Hour, dir, "make", append(makeArgs, "olddefconfig")...) + out, err = osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "olddefconfig")...) if err != nil { - failf("make olddefconfig failed: %v\n%s\n", err, out) + return fmt.Errorf("make olddefconfig failed: %v\n%s\n", err, out) } - out, err = osutil.RunCmd(time.Hour, dir, "make", append(makeArgs, "init/main.o")...) + out, err = osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "init/main.o")...) if err != nil { - failf("make failed: %v\n%s\n", err, out) + return fmt.Errorf("make failed: %v\n%s\n", err, out) } -} - -func failf(msg string, args ...interface{}) { - fmt.Fprintf(os.Stderr, msg+"\n", args...) - os.Exit(1) + return nil } diff --git a/sys/syz-extract/fetch.go b/sys/syz-extract/fetch.go index 25f3f2f47..5def165b5 100644 --- a/sys/syz-extract/fetch.go +++ b/sys/syz-extract/fetch.go @@ -18,9 +18,10 @@ import ( // fetchValues converts literal constants (e.g. O_APPEND) or any other C expressions // into their respective numeric values. It does so by builting and executing a C program // that prints values of the provided expressions. -func fetchValues(target *sys.Target, vals, includes, incdirs []string, - defines map[string]string) (map[string]uint64, map[string]bool, error) { - bin, out, err := runCompiler(target, nil, includes, incdirs, nil, nil) +func fetchValues(target *sys.Target, kernelDir, buildDir string, + vals, includes, incdirs []string, defines map[string]string) ( + map[string]uint64, map[string]bool, error) { + bin, out, err := runCompiler(target, kernelDir, buildDir, nil, includes, incdirs, nil, nil) if err != nil { return nil, nil, fmt.Errorf("failed to run gcc: %v\n%v", err, string(out)) } @@ -32,7 +33,7 @@ func fetchValues(target *sys.Target, vals, includes, incdirs []string, } undeclared := make(map[string]bool) - bin, out, err = runCompiler(target, vals, includes, incdirs, defines, undeclared) + bin, out, err = runCompiler(target, kernelDir, buildDir, vals, includes, incdirs, defines, undeclared) if err != nil { for _, errMsg := range []string{ "error: ‘([a-zA-Z0-9_]+)’ undeclared", @@ -47,7 +48,7 @@ func fetchValues(target *sys.Target, vals, includes, incdirs []string, } } } - bin, out, err = runCompiler(target, vals, includes, incdirs, defines, undeclared) + bin, out, err = runCompiler(target, kernelDir, buildDir, vals, includes, incdirs, defines, undeclared) if err != nil { return nil, nil, fmt.Errorf("failed to run gcc: %v\n%v", err, string(out)) } @@ -86,7 +87,7 @@ func fetchValues(target *sys.Target, vals, includes, incdirs []string, return res, undeclared, nil } -func runCompiler(target *sys.Target, vals, includes, incdirs []string, defines map[string]string, undeclared map[string]bool) (bin string, out []byte, err error) { +func runCompiler(target *sys.Target, kernelDir, buildDir string, vals, includes, incdirs []string, defines map[string]string, undeclared map[string]bool) (bin string, out []byte, err error) { includeText := "" for _, inc := range includes { includeText += fmt.Sprintf("#include <%v>\n", inc) @@ -126,20 +127,20 @@ func runCompiler(target *sys.Target, vals, includes, incdirs []string, defines m "-I.", "-D__KERNEL__", "-DKBUILD_MODNAME=\"-\"", - "-I" + *flagLinux + "/arch/" + arch + "/include", - "-I" + *flagLinuxBld + "/arch/" + arch + "/include/generated/uapi", - "-I" + *flagLinuxBld + "/arch/" + arch + "/include/generated", - "-I" + *flagLinuxBld + "/include", - "-I" + *flagLinux + "/include", - "-I" + *flagLinux + "/arch/" + arch + "/include/uapi", - "-I" + *flagLinuxBld + "/arch/" + arch + "/include/generated/uapi", - "-I" + *flagLinux + "/include/uapi", - "-I" + *flagLinuxBld + "/include/generated/uapi", - "-I" + *flagLinux, - "-include", *flagLinux + "/include/linux/kconfig.h", + "-I" + kernelDir + "/arch/" + arch + "/include", + "-I" + buildDir + "/arch/" + arch + "/include/generated/uapi", + "-I" + buildDir + "/arch/" + arch + "/include/generated", + "-I" + buildDir + "/include", + "-I" + kernelDir + "/include", + "-I" + kernelDir + "/arch/" + arch + "/include/uapi", + "-I" + buildDir + "/arch/" + arch + "/include/generated/uapi", + "-I" + kernelDir + "/include/uapi", + "-I" + buildDir + "/include/generated/uapi", + "-I" + kernelDir, + "-include", kernelDir + "/include/linux/kconfig.h", }...) for _, incdir := range incdirs { - args = append(args, "-I"+*flagLinux+"/"+incdir) + args = append(args, "-I"+kernelDir+"/"+incdir) } cmd := exec.Command("gcc", args...) cmd.Stdin = strings.NewReader(src) |
