diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-06-21 17:45:53 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-06-22 16:40:45 +0200 |
| commit | ea804a7120e0b87dabd3f24227f9550332c42c79 (patch) | |
| tree | ab4a2a07ca204e6756219af41edecd2ba1d4919c /pkg | |
| parent | 8c9738f9c7a89b865866092a7b48cc0b6c1d34c9 (diff) | |
pkg/build: pave way for multi-OS support
Unify kernel and image build, that distinction is really uninteresting.
Define interface that each OS needs to implement.
Add gvisor stub.
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/bisect/bisect.go | 6 | ||||
| -rw-r--r-- | pkg/build/build.go | 47 | ||||
| -rw-r--r-- | pkg/build/gvisor.go | 15 | ||||
| -rw-r--r-- | pkg/build/linux.go | 80 | ||||
| -rw-r--r-- | pkg/instance/instance.go | 17 |
5 files changed, 121 insertions, 44 deletions
diff --git a/pkg/bisect/bisect.go b/pkg/bisect/bisect.go index 4e2ee143f..78ac998a4 100644 --- a/pkg/bisect/bisect.go +++ b/pkg/bisect/bisect.go @@ -101,7 +101,8 @@ func (env *env) bisect() (*git.Commit, error) { if env.head, err = git.Poll(cfg.Manager.KernelSrc, cfg.Kernel.Repo, cfg.Kernel.Branch); err != nil { return nil, err } - if err := build.Clean(cfg.Manager.KernelSrc); err != nil { + if err := build.Clean(cfg.Manager.TargetOS, cfg.Manager.TargetArch, + cfg.Manager.Type, cfg.Manager.KernelSrc); err != nil { return nil, fmt.Errorf("kernel clean failed: %v", err) } env.log("building syzkaller on %v", cfg.Syzkaller.Commit) @@ -220,7 +221,8 @@ func (env *env) test() (git.BisectResult, error) { } env.log("testing commit %v with %v", current.Hash, compilerID) buildStart := time.Now() - if err := build.Clean(cfg.Manager.KernelSrc); err != nil { + if err := build.Clean(cfg.Manager.TargetOS, cfg.Manager.TargetArch, + cfg.Manager.Type, cfg.Manager.KernelSrc); err != nil { return 0, fmt.Errorf("kernel clean failed: %v", err) } err = env.inst.BuildKernel(be.compiler, cfg.Kernel.Userspace, diff --git a/pkg/build/build.go b/pkg/build/build.go index 0d959cbb4..6eb912e21 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -12,6 +12,53 @@ import ( "github.com/google/syzkaller/pkg/osutil" ) +// Image creates a disk image for the specified OS/ARCH/VM. +// Kernel is taken from kernelDir, userspace system is taken from userspaceDir. +// If cmdlineFile is not empty, contents of the file are appended to the kernel command line. +// If sysctlFile is not empty, contents of the file are appended to the image /etc/sysctl.conf. +// Output is stored in outputDir and includes: +// - image: the image +// - key: ssh key for the image if applicable +// - kernel.config: actual kernel config used during build +// - obj/: directory with kernel object files (e.g. vmlinux for linux) +func Image(targetOS, targetArch, vmType, kernelDir, outputDir, compiler, userspaceDir, + cmdlineFile, sysctlFile string, config []byte) error { + builder, err := getBuilder(targetOS, targetArch, vmType) + if err != nil { + return err + } + return builder.build(targetArch, vmType, kernelDir, outputDir, compiler, userspaceDir, cmdlineFile, sysctlFile, config) +} + +func Clean(targetOS, targetArch, vmType, kernelDir string) error { + builder, err := getBuilder(targetOS, targetArch, vmType) + if err != nil { + return err + } + return builder.clean(kernelDir) +} + +type KernelBuildError struct { + *osutil.VerboseError +} + +type builder interface { + build(targetArch, vmType, kernelDir, outputDir, compiler, userspaceDir, + cmdlineFile, sysctlFile string, config []byte) error + clean(kernelDir string) error +} + +func getBuilder(targetOS, targetArch, vmType string) (builder, error) { + switch { + case targetOS == "linux" && targetArch == "amd64" && vmType == "gvisor": + return gvisor{}, nil + case targetOS == "linux" && targetArch == "amd64" && (vmType == "qemu" || vmType == "gce"): + return linux{}, nil + default: + return nil, fmt.Errorf("unsupported image type %v/%v/%v", targetOS, targetArch, vmType) + } +} + func CompilerIdentity(compiler string) (string, error) { arg := "--version" if strings.HasSuffix(compiler, "bazel") { diff --git a/pkg/build/gvisor.go b/pkg/build/gvisor.go new file mode 100644 index 000000000..8649e7e65 --- /dev/null +++ b/pkg/build/gvisor.go @@ -0,0 +1,15 @@ +// Copyright 2018 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 build + +type gvisor struct{} + +func (gvisor) build(targetArch, vmType, kernelDir, outputDir, compiler, userspaceDir, + cmdlineFile, sysctlFile string, config []byte) error { + return nil +} + +func (gvisor) clean(kernelDir string) error { + return nil +} diff --git a/pkg/build/linux.go b/pkg/build/linux.go index ae296674a..a2afc3011 100644 --- a/pkg/build/linux.go +++ b/pkg/build/linux.go @@ -22,8 +22,24 @@ import ( "github.com/google/syzkaller/pkg/osutil" ) -func Build(dir, compiler string, config []byte) error { - configFile := filepath.Join(dir, ".config") +type linux struct{} + +func (linux linux) build(targetArch, vmType, kernelDir, outputDir, compiler, userspaceDir, + cmdlineFile, sysctlFile string, config []byte) error { + if err := osutil.MkdirAll(filepath.Join(outputDir, "obj")); err != nil { + return err + } + if err := linux.buildKernel(kernelDir, outputDir, compiler, config); err != nil { + return err + } + if err := linux.createImage(vmType, kernelDir, outputDir, userspaceDir, cmdlineFile, sysctlFile); err != nil { + return err + } + return nil +} + +func (linux) buildKernel(kernelDir, outputDir, compiler string, config []byte) error { + configFile := filepath.Join(kernelDir, ".config") if err := osutil.WriteFile(configFile, config); err != nil { return fmt.Errorf("failed to write config file: %v", err) } @@ -37,7 +53,7 @@ func Build(dir, compiler string, config []byte) error { if err := osutil.Sandbox(cmd, true, true); err != nil { return err } - cmd.Dir = dir + cmd.Dir = kernelDir if _, err := osutil.Run(10*time.Minute, cmd); err != nil { return err } @@ -47,38 +63,23 @@ func Build(dir, compiler string, config []byte) error { if err := osutil.Sandbox(cmd, true, true); err != nil { return err } - cmd.Dir = dir - // Build of a large kernel can take a while on a 1 CPU VM. - if _, err := osutil.Run(3*time.Hour, cmd); err != nil { + cmd.Dir = kernelDir + if _, err := osutil.Run(time.Hour, cmd); err != nil { return extractRootCause(err) } - return nil -} - -func Clean(dir string) error { - cpu := strconv.Itoa(runtime.NumCPU()) - cmd := osutil.Command("make", "distclean", "-j", cpu) - if err := osutil.Sandbox(cmd, true, true); err != nil { + outputConfig := filepath.Join(outputDir, "kernel.config") + if err := osutil.CopyFile(configFile, outputConfig); err != nil { return err } - cmd.Dir = dir - _, err := osutil.Run(10*time.Minute, cmd) - return err + vmlinux := filepath.Join(kernelDir, "vmlinux") + outputVmlinux := filepath.Join(outputDir, "obj", "vmlinux") + if err := os.Rename(vmlinux, outputVmlinux); err != nil { + return fmt.Errorf("failed to rename vmlinux: %v", err) + } + return nil } -// CreateImage creates a disk image that is suitable for syzkaller. -// Kernel is taken from kernelDir, userspace system is taken from userspaceDir. -// If cmdlineFile is not empty, contents of the file are appended to the kernel command line. -// If sysctlFile is not empty, contents of the file are appended to the image /etc/sysctl.conf. -// Produces image and root ssh key in the specified files. -func CreateImage(targetOS, targetArch, vmType, kernelDir, userspaceDir, cmdlineFile, sysctlFile, - image, sshkey string) error { - if targetOS != "linux" || targetArch != "amd64" { - return fmt.Errorf("only linux/amd64 is supported") - } - if vmType != "qemu" && vmType != "gce" { - return fmt.Errorf("images can be built only for qemu/gce machines") - } +func (linux) createImage(vmType, kernelDir, outputDir, userspaceDir, cmdlineFile, sysctlFile string) error { tempDir, err := ioutil.TempDir("", "syz-build") if err != nil { return err @@ -101,18 +102,31 @@ func CreateImage(targetOS, targetArch, vmType, kernelDir, userspaceDir, cmdlineF return fmt.Errorf("image build failed: %v", err) } // Note: we use CopyFile instead of Rename because src and dst can be on different filesystems. - if err := osutil.CopyFile(filepath.Join(tempDir, "disk.raw"), image); err != nil { + imageFile := filepath.Join(outputDir, "image") + if err := osutil.CopyFile(filepath.Join(tempDir, "disk.raw"), imageFile); err != nil { return err } - if err := osutil.CopyFile(filepath.Join(tempDir, "key"), sshkey); err != nil { + keyFile := filepath.Join(outputDir, "key") + if err := osutil.CopyFile(filepath.Join(tempDir, "key"), keyFile); err != nil { return err } - if err := os.Chmod(sshkey, 0600); err != nil { + if err := os.Chmod(keyFile, 0600); err != nil { return err } return nil } +func (linux) clean(kernelDir string) error { + cpu := strconv.Itoa(runtime.NumCPU()) + cmd := osutil.Command("make", "distclean", "-j", cpu) + if err := osutil.Sandbox(cmd, true, true); err != nil { + return err + } + cmd.Dir = kernelDir + _, err := osutil.Run(10*time.Minute, cmd) + return err +} + func extractRootCause(err error) error { verr, ok := err.(*osutil.VerboseError) if !ok { @@ -133,7 +147,7 @@ func extractRootCause(err error) error { if cause != nil { verr.Title = string(cause) } - return verr + return KernelBuildError{verr} } type buildFailureCause struct { diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index 2294fed9b..cec79459a 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -79,16 +79,15 @@ func (env *Env) BuildSyzkaller(repo, commit string) error { func (env *Env) BuildKernel(compilerBin, userspaceDir, cmdlineFile, sysctlFile string, kernelConfig []byte) error { cfg := env.cfg - if err := build.Build(cfg.KernelSrc, compilerBin, kernelConfig); err != nil { - return osutil.PrependContext("kernel build failed", err) - } - cfg.KernelObj = cfg.KernelSrc - cfg.Image = filepath.Join(cfg.Workdir, "syz-image") - cfg.SSHKey = filepath.Join(cfg.Workdir, "syz-key") - if err := build.CreateImage(cfg.TargetOS, cfg.TargetVMArch, cfg.Type, - cfg.KernelSrc, userspaceDir, cmdlineFile, sysctlFile, cfg.Image, cfg.SSHKey); err != nil { - return osutil.PrependContext("image build failed", err) + imageDir := filepath.Join(cfg.Workdir, "image") + if err := build.Image(cfg.TargetOS, cfg.TargetVMArch, cfg.Type, + cfg.KernelSrc, imageDir, compilerBin, userspaceDir, + cmdlineFile, sysctlFile, kernelConfig); err != nil { + return err } + cfg.KernelObj = filepath.Join(imageDir, "obj") + cfg.Image = filepath.Join(imageDir, "image") + cfg.SSHKey = filepath.Join(imageDir, "key") return nil } |
