Makefile: build target binaries into separate dirs

We currently build binaries for all targets into bin.
This makes mess in bin/ and does not allow testing of different archs.
Build target binaries into bin/OS_ARCH/ subdirs.

Host binaries are still built into bin/.

Update #333
Update #324
Update #191
This commit is contained in:
Dmitry Vyukov 2017-09-19 11:00:40 +02:00
parent 5b89a8780f
commit 62114d6064
8 changed files with 201 additions and 115 deletions

View File

@ -17,7 +17,7 @@ before_install:
- sudo cp clang+llvm-4.0.1-x86_64-linux-gnu-debian8/bin/clang-format /usr/local/bin/
- which clang-format
- clang-format --version
- sudo apt-get install -y -q libc6-dev-i386 gcc-aarch64-linux-gnu gcc-powerpc64le-linux-gnu gcc-arm-linux-gnueabihf
- sudo apt-get install -y -q libc6-dev-i386 linux-libc-dev g++-aarch64-linux-gnu g++-powerpc64le-linux-gnu g++-arm-linux-gnueabihf
install: true

174
Makefile
View File

@ -1,19 +1,80 @@
# 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.
NOSTATIC ?= 0
ifeq ($(NOSTATIC), 0)
STATIC_FLAG=-static
# There are 4 OS/arch pairs:
# - BUILDOS/BUILDARCH: the current machine's pair used for build.
# - HOSTOS/HOSTARCH: pair where syz-manager will run.
# - TARGETOS/TARGETVMARCH: pair of the target OS under test.
# - TARGETOS/TARGETARCH: pair of the target test process.
#
# The last 2 differ for e.g. amd64 OS and 386 test processes (compat syscall testing).
# All pairs default to the current machine. All but BUILD can be overriden.
#
# For example, to test linux/amd64 on linux/amd64, you just run:
# make
# To test linux/arm64 from darwin/amd64 host, run:
# make HOSTOS=darwin HOSTARCH=amd64 TARGETOS=linux TARGETARCH=arm64
# To test x86 compat syscalls, run:
# make TARGETVMARCH=amd64 TARGETARCH=386
#
# There is a special case for Android builds:
# NDK=/path/to/android/ndk make TARGETOS=android TARGETARCH=arm64
# But you still need to specify "target": "linux/arm64" in syz-manager config.
BUILDOS := $(shell go env GOOS)
BUILDARCH := $(shell go env GOARCH)
HOSTOS ?= $(BUILDOS)
HOSTARCH ?= $(BUILDARCH)
TARGETOS ?= $(HOSTOS)
TARGETARCH ?= $(HOSTARCH)
TARGETVMARCH ?= $(TARGETARCH)
ifeq ("$(TARGETARCH)", "amd64")
CC = "x86_64-linux-gnu-gcc"
else ifeq ("$(TARGETARCH)", "386")
CC = "x86_64-linux-gnu-gcc"
ADDCFLAGS = "-m32"
else ifeq ("$(TARGETARCH)", "arm64")
CC = "aarch64-linux-gnu-gcc"
else ifeq ("$(TARGETARCH)", "arm")
CC = "arm-linux-gnueabihf-gcc"
ADDCFLAGS = "-march=armv6t2"
else ifeq ("$(TARGETARCH)", "ppc64le")
CC = "powerpc64le-linux-gnu-gcc"
endif
.PHONY: all main tools \
manager fuzzer executor \
ci hub \
execprog mutate prog2c stress repro upgrade db \
bin/syz-sysgen bin/syz-extract bin/syz-fmt \
extract generate \
android \
format tidy test arch cross-compile presubmit clean
ifeq ("$(TARGETOS)", "android")
override TARGETOS = "linux"
ANDROID_API = 24
BUILDGCCARCH = ""
ANDROIDARCH = ""
TOOLCHAIN = ""
GCCBIN = ""
ifeq ("$(TARGETARCH)", "amd64")
ANDROIDARCH = "x86_64"
TOOLCHAIN = "x86_64-4.9"
GCCBIN = "x86_64-linux-android-g++"
else ifeq ("$(TARGETARCH)", "386")
ANDROIDARCH = "x86"
TOOLCHAIN = "x86-4.9"
GCCBIN = "i686-linux-android-g++"
else ifeq ("$(TARGETARCH)", "arm64")
ANDROIDARCH = "arm64"
TOOLCHAIN = "aarch64-linux-android-4.9"
GCCBIN = "aarch64-linux-android-g++"
else ifeq ("$(TARGETARCH)", "arm")
ANDROIDARCH = "arm"
TOOLCHAIN = "arm-linux-androideabi-4.9"
GCCBIN = "arm-linux-androideabi-g++"
endif
ifeq ("$(BUILDARCH)", "amd64")
BUILDGCCARCH = "x86_64"
else ifeq ("$(BUILDARCH)", "arm64")
BUILDGCCARCH = "aarch64"
endif
CC = $(NDK)/toolchains/$(TOOLCHAIN)/prebuilt/$(BUILDOS)-$(BUILDGCCARCH)/bin/$(GCCBIN)
CFLAGS = -I $(NDK)/sources/cxx-stl/llvm-libc++/include --sysroot=$(NDK)/platforms/android-$(ANDROID_API)/arch-$(ANDROIDARCH) -O1 -g -Wall -static
endif
GITREV=$(shell git rev-parse HEAD)
ifeq ($(`git diff --shortstat`), "")
@ -22,37 +83,54 @@ else
REV=$(GITREV)+
endif
all:
$(MAKE) main
$(MAKE) tools
main:
go install ./syz-manager ./syz-fuzzer
$(MAKE) manager
$(MAKE) fuzzer
$(MAKE) executor
tools: execprog mutate prog2c stress repro upgrade db
# executor uses stacks of limited size, so no jumbo frames.
executor:
$(CC) -o ./bin/syz-executor executor/executor.cc -pthread -Wall -Wframe-larger-than=8192 \
-Wparentheses -Werror -O1 -g $(STATIC_FLAG) $(CFLAGS) -DGIT_REVISION=\"$(REV)\"
NOSTATIC ?= 0
ifeq ($(NOSTATIC), 0)
ADDCFLAGS += -static
endif
# Don't generate symbol table and DWARF debug info.
# Reduces build time and binary sizes considerably.
# That's only needed if you use gdb or nm.
# If you need that, build manually without these flags.
GOFLAGS="-ldflags=-s -w -X github.com/google/syzkaller/sys.GitRevision=$(REV)"
GOFLAGS := "-ldflags=-s -w -X github.com/google/syzkaller/sys.GitRevision=$(REV)"
ifneq ("$(GOTAGS)", "")
GOFLAGS += "-tags=$(GOTAGS)"
endif
.PHONY: all host target \
manager fuzzer executor \
ci hub \
execprog mutate prog2c stress repro upgrade db \
bin/syz-sysgen bin/syz-extract bin/syz-fmt \
extract generate \
format tidy test arch presubmit clean
all: host target
host:
GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) go install ./syz-manager
env GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(MAKE) manager repro mutate prog2c db upgrade
target:
GOOS=$(TARGETOS) GOARCH=$(TARGETVMARCH) go install ./syz-fuzzer
env GOOS=$(TARGETOS) GOARCH=$(TARGETVMARCH) $(MAKE) fuzzer execprog stress
$(MAKE) executor
# executor uses stacks of limited size, so no jumbo frames.
executor:
mkdir -p ./bin/$(TARGETOS)_$(TARGETARCH)
$(CC) -o ./bin/$(TARGETOS)_$(TARGETARCH)/syz-executor executor/executor.cc \
-pthread -Wall -Wframe-larger-than=8192 -Wparentheses -Werror -O1 -g \
$(ADDCFLAGS) $(CFLAGS) -DGIT_REVISION=\"$(REV)\"
manager:
go build $(GOFLAGS) -o ./bin/syz-manager github.com/google/syzkaller/syz-manager
fuzzer:
go build $(GOFLAGS) -o ./bin/syz-fuzzer github.com/google/syzkaller/syz-fuzzer
go build $(GOFLAGS) -o ./bin/$(TARGETOS)_$(TARGETVMARCH)/syz-fuzzer github.com/google/syzkaller/syz-fuzzer
execprog:
go build $(GOFLAGS) -o ./bin/syz-execprog github.com/google/syzkaller/tools/syz-execprog
go build $(GOFLAGS) -o ./bin/$(TARGETOS)_$(TARGETVMARCH)/syz-execprog github.com/google/syzkaller/tools/syz-execprog
ci:
go build $(GOFLAGS) -o ./bin/syz-ci github.com/google/syzkaller/syz-ci
@ -70,7 +148,7 @@ prog2c:
go build $(GOFLAGS) -o ./bin/syz-prog2c github.com/google/syzkaller/tools/syz-prog2c
stress:
go build $(GOFLAGS) -o ./bin/syz-stress github.com/google/syzkaller/tools/syz-stress
go build $(GOFLAGS) -o ./bin/$(TARGETOS)_$(TARGETVMARCH)/syz-stress github.com/google/syzkaller/tools/syz-stress
db:
go build $(GOFLAGS) -o ./bin/syz-db github.com/google/syzkaller/tools/syz-db
@ -93,7 +171,7 @@ bin/syz-sysgen:
format: bin/syz-fmt
go fmt ./...
clang-format --style=file -i executor/*.cc executor/*.h tools/kcovtrace/*.c
bin/syz-fmt sys
bin/syz-fmt sys/linux
bin/syz-fmt:
go build $(GOFLAGS) -o $@ ./tools/syz-fmt
@ -108,12 +186,15 @@ test:
go test -short -race ./...
arch:
GOOS=linux GOARCH=amd64 go install ./...
GOOS=linux GOARCH=arm64 go install ./...
GOOS=linux GOARCH=386 go install ./...
GOOS=linux GOARCH=arm go install ./...
GOOS=linux GOARCH=ppc64le go install ./...
GOOS=darwin GOARCH=amd64 go build -o /dev/null ./syz-manager
env HOSTOS=darwin HOSTARCH=amd64 $(MAKE) host
env HOSTOS=linux HOSTARCH=amd64 $(MAKE) host
env TARGETOS=linux TARGETARCH=amd64 $(MAKE) target
env TARGETOS=linux TARGETARCH=386 $(MAKE) target
env TARGETOS=linux TARGETARCH=arm64 $(MAKE) target
env TARGETOS=linux TARGETARCH=ppc64le $(MAKE) target
# executor build on arm fails with:
# Error: alignment too large: 15 assumed
env TARGETOS=linux TARGETARCH=arm64 TARGETVMARCH=arm $(MAKE) target
presubmit:
$(MAKE) generate
@ -124,20 +205,3 @@ presubmit:
clean:
rm -rf ./bin/
cross-compile:
# We could use arm-linux-gnueabihf-gcc from g++-arm-linux-gnueabihf package,
# but it is broken with "Error: alignment too large: 15 assumed"
env CC="clang" CFLAGS="--target=linux-armv6 -mfloat-abi=hard" $(MAKE) executor
android: UNAME=$(shell uname | tr '[:upper:]' '[:lower:]')
android: ANDROID_ARCH=arm64
android: ANDROID_API=24
android: TOOLCHAIN=aarch64-linux-android
android:
test -d $(NDK)
$(MAKE) manager
env GOOS=linux GOARCH=arm64 $(MAKE) execprog fuzzer
env CC="$(NDK)/toolchains/$(TOOLCHAIN)-4.9/prebuilt/$(UNAME)-x86_64/bin/$(TOOLCHAIN)-g++" \
CFLAGS="-I $(NDK)/sources/cxx-stl/llvm-libc++/include --sysroot=$(NDK)/platforms/android-$(ANDROID_API)/arch-$(ANDROID_ARCH) -O1 -g -Wall -static" \
$(MAKE) executor

View File

@ -63,8 +63,8 @@ Unpack Go into a directory, say, `$HOME/go`.
Then, set `GOROOT=$HOME/go` env var.
Then, add Go binaries to `PATH`, `PATH=$HOME/go/bin:$PATH`.
Then, set `GOPATH` env var to some empty dir, say `GOPATH=$HOME/gopath`.
Then, run `go get -u -d github.com/google/syzkaller/...` to checkout syzkaller sources with all dependencies.
Then, run `go get -u -d github.com/google/syzkaller/...` to checkout syzkaller sources.
Then, `cd $GOPATH/src/github.com/google/syzkaller` and
build with `make`, which generates compiled binaries in the `bin/` folder.
To build additional syzkaller tools run `make all-tools`.
Note: if you want to do cross-OS/arch testing, you need to specify `TARGETOS`,
`TARGETVMARCH` and `TARGETARCH` arguments to `make`. See the [Makefile](../Makefile) for details.

View File

@ -9,17 +9,7 @@ Prerequisites:
- Build syzkaller
```sh
$ make android
```
- Check the output files are correct
```sh
$ file bin/*
bin/syz-execprog: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, stripped
bin/syz-executor: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, not stripped
bin/syz-fuzzer: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, stripped
bin/syz-manager: Mach-O 64-bit executable x86_64
$ NDK=/path/to/android/ndk make TARGETOS=android TARGETARCH=arm64
```
- Create config with `"type": "adb"` and specify adb devices to use. For example:

View File

@ -278,19 +278,9 @@ Now make sure you can ssh with the key:
ssh -i ./ssh/id_rsa root@172.16.0.31
```
Build syzkaller on you host machine as you do usually.
Build `syz-manager` and `syz-repro` with `odroid` build tag:
Build syzkaller with `odroid` build tag:
``` bash
go build -tags odroid -o ./bin/syz-manager ./syz-manager
go build -tags odroid -o ./bin/syz-repro ./tools/syz-repro
```
Cross compile `syz-fuzzer`, `syz-execprog` and `syz-executor` binaries for arm64:
``` bash
GOARCH=arm64 go build -o bin/syz-fuzzer ./syz-fuzzer
GOARCH=arm64 go build -o bin/syz-execprog ./tools/syz-execprog
$PREFIX/bin/aarch64-linux-gcc executor/executor.cc -O1 -g -Wall -static -o bin/syz-executor -lpthread
make GOTAGS=odroid TARGETARCH=arm64
```
Use the following config:

View File

@ -36,14 +36,13 @@ Here are some things to check if there are problems running syzkaller.
- If logging indicates problems with the executor program (e.g. `executor failure`),
try manually running a short sequence of system calls:
- Build additional tools with `make all-tools`
- Copy `syz-executor` and `syz-execprog` into a running VM.
- In the VM run `./syz-execprog -executor ./syz-executor -debug sampleprog` where
sampleprog is a simple system call script (e.g. just containing `getpid()`).
- For example, if this reports that `clone` has failed, this probably indicates
that the test kernel does not include support for all of the required namespaces.
In this case, running the `syz-execprog` test with the `-nobody=0` option fixes the problem,
so the main configuration needs to be updated to set `dropprivs` to `false`.
In this case, running the `syz-execprog` test with the `-sandbox=setuid` option fixes the problem,
so the main configuration needs to be updated to set `sandbox` to `setuid`.
If none of the above helps, file a bug on [the bug tracker](https://github.com/google/syzkaller/issues)
or ask us directly on the syzkaller@googlegroups.com mailing list.

View File

@ -8,12 +8,15 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/google/syzkaller/pkg/config"
"github.com/google/syzkaller/pkg/git"
. "github.com/google/syzkaller/pkg/log"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/syz-manager/mgrconfig"
)
const (
@ -21,16 +24,6 @@ const (
buildRetryPeriod = 15 * time.Minute // used for both syzkaller and kernel
)
// List of required files in syzkaller build (contents of latest/current dirs).
var syzFiles = []string{
"tag", // contains syzkaller repo git hash
"bin/syz-ci", // these are just copied from syzkaller dir
"bin/syz-manager",
"bin/syz-fuzzer",
"bin/syz-executor",
"bin/syz-execprog",
}
// SyzUpdater handles everything related to syzkaller updates.
// As kernel builder, it maintains 2 builds:
// - latest: latest known good syzkaller build
@ -45,6 +38,8 @@ type SyzUpdater struct {
syzkallerDir string
latestDir string
currentDir string
syzFiles []string
targets map[string]bool
}
func NewSyzUpdater(cfg *Config) *SyzUpdater {
@ -70,6 +65,32 @@ func NewSyzUpdater(cfg *Config) *SyzUpdater {
syzkallerDir := filepath.Join(gopath, "src", "github.com", "google", "syzkaller")
osutil.MkdirAll(syzkallerDir)
// List of required files in syzkaller build (contents of latest/current dirs).
files := map[string]bool{
"tag": true, // contains syzkaller repo git hash
"bin/syz-ci": true, // these are just copied from syzkaller dir
"bin/syz-manager": true,
}
targets := make(map[string]bool)
for _, mgr := range cfg.Managers {
mgrcfg := new(mgrconfig.Config)
if err := config.LoadData(mgr.Manager_Config, mgrcfg); err != nil {
Fatalf("failed to load manager %v config: %v", mgr.Name, err)
}
os, vmarch, arch, err := mgrconfig.SplitTarget(mgrcfg.Target)
if err != nil {
Fatalf("failed to load manager %v config: %v", mgr.Name, err)
}
targets[os+"/"+vmarch+"/"+arch] = true
files[fmt.Sprintf("bin/%v_%v/syz-fuzzer", os, vmarch)] = true
files[fmt.Sprintf("bin/%v_%v/syz-execprog", os, vmarch)] = true
files[fmt.Sprintf("bin/%v_%v/syz-executor", os, arch)] = true
}
var syzFiles []string
for f := range files {
syzFiles = append(syzFiles, f)
}
return &SyzUpdater{
exe: exe,
repo: cfg.Syzkaller_Repo,
@ -78,6 +99,8 @@ func NewSyzUpdater(cfg *Config) *SyzUpdater {
syzkallerDir: syzkallerDir,
latestDir: filepath.Join("syzkaller", "latest"),
currentDir: filepath.Join("syzkaller", "current"),
syzFiles: syzFiles,
targets: targets,
}
}
@ -91,7 +114,7 @@ func (upd *SyzUpdater) UpdateOnStart(shutdown chan struct{}) {
if exeTag == latestTag && time.Since(exeMod) < syzkallerRebuildPeriod/2 {
// Have a freash up-to-date build, probably just restarted.
Logf(0, "current executable is up-to-date (%v)", exeTag)
if err := osutil.LinkFiles(upd.latestDir, upd.currentDir, syzFiles); err != nil {
if err := osutil.LinkFiles(upd.latestDir, upd.currentDir, upd.syzFiles); err != nil {
Fatal(err)
}
return
@ -112,7 +135,7 @@ func (upd *SyzUpdater) UpdateOnStart(shutdown chan struct{}) {
// The build was successful or we had the latest build from previous runs.
// Either way, use the latest build.
Logf(0, "using syzkaller built on %v", latestTag)
if err := osutil.LinkFiles(upd.latestDir, upd.currentDir, syzFiles); err != nil {
if err := osutil.LinkFiles(upd.latestDir, upd.currentDir, upd.syzFiles); err != nil {
Fatal(err)
}
if exeTag != latestTag {
@ -203,9 +226,20 @@ func (upd *SyzUpdater) build() error {
if _, err := osutil.RunCmd(time.Hour, upd.syzkallerDir, "make", "generate"); err != nil {
return fmt.Errorf("build failed: %v", err)
}
if _, err := osutil.RunCmd(time.Hour, upd.syzkallerDir, "make", "all", "ci"); err != nil {
if _, err := osutil.RunCmd(time.Hour, upd.syzkallerDir, "make", "host", "ci"); err != nil {
return fmt.Errorf("build failed: %v", err)
}
for target := range upd.targets {
parts := strings.Split(target, "/")
env := []string{
"TARGETOS=" + parts[0],
"TARGETVMARCH=" + parts[1],
"TARGETARCH=" + parts[2],
}
if _, err := osutil.RunCmdEnv(time.Hour, env, upd.syzkallerDir, "make", "target"); err != nil {
return fmt.Errorf("build failed: %v", err)
}
}
if _, err := osutil.RunCmd(time.Hour, upd.syzkallerDir, "go", "test", "-short", "./..."); err != nil {
return fmt.Errorf("tests failed: %v", err)
}
@ -213,7 +247,7 @@ func (upd *SyzUpdater) build() error {
if err := osutil.WriteFile(tagFile, []byte(commit)); err != nil {
return fmt.Errorf("filed to write tag file: %v", err)
}
if err := osutil.CopyFiles(upd.syzkallerDir, upd.latestDir, syzFiles); err != nil {
if err := osutil.CopyFiles(upd.syzkallerDir, upd.latestDir, upd.syzFiles); err != nil {
return fmt.Errorf("filed to copy syzkaller: %v", err)
}
return nil
@ -222,7 +256,7 @@ func (upd *SyzUpdater) build() error {
// checkLatest returns tag of the latest build,
// or an empty string if latest build is missing/broken.
func (upd *SyzUpdater) checkLatest() string {
if !osutil.FilesExist(upd.latestDir, syzFiles) {
if !osutil.FilesExist(upd.latestDir, upd.syzFiles) {
return ""
}
tag, _ := readTag(filepath.Join(upd.latestDir, "tag"))

View File

@ -101,23 +101,15 @@ func load(data []byte, filename string) (*Config, error) {
}
}
if cfg.Target == "" {
return nil, fmt.Errorf("config param target is empty")
}
targetParts := strings.Split(cfg.Target, "/")
if len(targetParts) != 2 && len(targetParts) != 3 {
return nil, fmt.Errorf("bad config param target")
}
cfg.TargetOS = targetParts[0]
cfg.TargetVMArch = targetParts[1]
cfg.TargetArch = targetParts[1]
if len(targetParts) == 3 {
cfg.TargetArch = targetParts[2]
var err error
cfg.TargetOS, cfg.TargetVMArch, cfg.TargetArch, err = SplitTarget(cfg.Target)
if err != nil {
return nil, err
}
cfg.SyzFuzzerBin = filepath.Join(cfg.Syzkaller, "bin", "syz-fuzzer")
cfg.SyzExecprogBin = filepath.Join(cfg.Syzkaller, "bin", "syz-execprog")
cfg.SyzExecutorBin = filepath.Join(cfg.Syzkaller, "bin", "syz-executor")
cfg.SyzFuzzerBin = filepath.Join(cfg.Syzkaller, "bin", cfg.TargetOS+"_"+cfg.TargetVMArch, "syz-fuzzer")
cfg.SyzExecprogBin = filepath.Join(cfg.Syzkaller, "bin", cfg.TargetOS+"_"+cfg.TargetVMArch, "syz-execprog")
cfg.SyzExecutorBin = filepath.Join(cfg.Syzkaller, "bin", cfg.TargetOS+"_"+cfg.TargetArch, "syz-executor")
if !osutil.IsExist(cfg.SyzFuzzerBin) {
return nil, fmt.Errorf("bad config syzkaller param: can't find %v", cfg.SyzFuzzerBin)
}
@ -171,6 +163,23 @@ func load(data []byte, filename string) (*Config, error) {
return cfg, nil
}
func SplitTarget(target string) (string, string, string, error) {
if target == "" {
return "", "", "", fmt.Errorf("target is empty")
}
targetParts := strings.Split(target, "/")
if len(targetParts) != 2 && len(targetParts) != 3 {
return "", "", "", fmt.Errorf("bad config param target")
}
os := targetParts[0]
vmarch := targetParts[1]
arch := targetParts[1]
if len(targetParts) == 3 {
arch = targetParts[2]
}
return os, vmarch, arch, nil
}
func ParseEnabledSyscalls(cfg *Config) (map[int]bool, error) {
match := func(call *prog.Syscall, str string) bool {
if str == call.CallName || str == call.Name {