Makefile, sys/targets: move all native compilation logic to sys/targets

We currently have native cross-compilation logic duplicated
in Makefile and in sys/targets. Some pieces are missed in one
place, some are in another. Only pkg/csource knows how to check
for -static support.

Move all CC/CFLAGS logic to sys/targets and pull results in Makefile.

This should make Makefile work on distros that have broken x86_64-linux-gnu-gcc,
now we will use just gcc. And this removes the need to define NOSTATIC,
as it's always auto-detected.

This also paves the way for making pkg/csource work on OSes other than Linux.
This commit is contained in:
Dmitry Vyukov 2018-06-05 17:14:47 +02:00
parent 65c0e1a37c
commit 6479ab2a75
9 changed files with 198 additions and 107 deletions

View File

@ -1,8 +1,7 @@
# 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.
# There are 4 OS/arch pairs:
# - BUILDOS/BUILDARCH: the current machine's pair used for build.
# There are 3 OS/arch pairs:
# - 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.
@ -21,74 +20,42 @@
# (you don't need this unless you update system call descriptions):
# make extract TARGETOS=android SOURCEDIR=/path/to/android/checkout
BUILDOS := $(shell go env GOOS)
BUILDARCH := $(shell go env GOARCH)
HOSTOS ?= $(BUILDOS)
HOSTARCH ?= $(BUILDARCH)
TARGETOS ?= $(HOSTOS)
TARGETARCH ?= $(HOSTARCH)
TARGETVMARCH ?= $(TARGETARCH)
define newline
endef
ENV := $(subst \n,$(newline),$(shell \
SOURCEDIR=$(SOURCEDIR) HOSTOS=$(HOSTOS) HOSTARCH=$(HOSTARCH) \
TARGETOS=$(TARGETOS) TARGETARCH=$(TARGETARCH) TARGETVMARCH=$(TARGETVMARCH) \
go run tools/syz-env/env.go))
$(info $(ENV))
$(eval $(ENV))
ifeq ("$(NCORES)", "")
$(error syz-env failed)
endif
MAKEFLAGS += " -j$(NCORES) "
export MAKEFLAGS
GO := go
EXE :=
ifeq ("$(BUILDOS)", "linux")
NCORES ?= $(shell grep -c "vendor_id" /proc/cpuinfo)
MAKEFLAGS += " -j$(NCORES) "
endif
ifeq ("$(TARGETARCH)", "amd64")
CC = "x86_64-linux-gnu-gcc"
else ifeq ("$(TARGETARCH)", "386")
ifeq ("$(BUILDARCH)", "386")
CC = "i686-linux-gnu-gcc"
else
CC = "x86_64-linux-gnu-gcc"
endif
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
# By default, build all Go binaries as static. We don't need cgo and it is
# known to cause problems at least on Android emulator.
export CGO_ENABLED=0
ifeq ("$(TARGETOS)", "fuchsia")
# SOURCEDIR should point to fuchsia checkout.
# SOURCEDIR should point to zircon checkout.
GO = $(SOURCEDIR)/buildtools/go
CC = $(SOURCEDIR)/buildtools/linux-x64/clang/bin/clang++
export CGO_ENABLED=1
NOSTATIC = 1
ifeq ("$(TARGETARCH)", "amd64")
ADDCFLAGS = --target=x86_64-fuchsia -lfdio -lzircon --sysroot $(SOURCEDIR)/out/build-zircon/build-x64/sysroot
export GOROOT=$(SOURCEDIR)/out/debug-x64/goroot
# Required by the goroot.
export ZIRCON_BUILD_DIR=$(SOURCEDIR)/out/build-zircon/build-x64
else ifeq ("$(TARGETARCH)", "arm64")
ADDCFLAGS = --target=aarch64-fuchsia -lfdio -lzircon --sysroot $(SOURCEDIR)/out/build-zircon/build-arm64/sysroot
export GOROOT=$(SOURCEDIR)/out/debug-arm64/goroot
# Required by the goroot.
export ZIRCON_BUILD_DIR=$(SOURCEDIR)/out/build-zircon/build-arm64
endif
endif
ifeq ("$(TARGETOS)", "akaros")
# SOURCEDIR should point to bootstrapped akaros checkout.
# There is no up-to-date Go for akaros, so building Go will fail.
CC = $(SOURCEDIR)/install/x86_64-ucb-akaros-gcc/bin/x86_64-ucb-akaros-g++
# Most likely this is incorrect (why doesn't it know own sysroot?), but worked for me.
ADDCFLAGS = -I $(SOURCEDIR)/tools/compilers/gcc-glibc/x86_64-ucb-akaros-gcc-stage3-builddir/x86_64-ucb-akaros/libstdc++-v3/include/x86_64-ucb-akaros -I $(SOURCEDIR)/tools/compilers/gcc-glibc/x86_64-ucb-akaros-gcc-stage3-builddir/x86_64-ucb-akaros/libstdc++-v3/include -I $(SOURCEDIR)/tools/compilers/gcc-glibc/gcc-4.9.2/libstdc++-v3/libsupc++ -L $(SOURCEDIR)/tools/compilers/gcc-glibc/x86_64-ucb-akaros-gcc-stage3-builddir/x86_64-ucb-akaros/libstdc++-v3/src/.libs
endif
ifeq ("$(TARGETOS)", "windows")
EXE = .exe
endif
GITREV=$(shell git rev-parse HEAD)
ifeq ("$(shell git diff --shortstat)", "")
REV=$(GITREV)
@ -96,11 +63,6 @@ else
REV=$(GITREV)+
endif
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.

View File

@ -19,8 +19,8 @@ import (
// Build builds a C/C++ program from source src and returns name of the resulting binary.
// lang can be "c" or "c++".
func Build(target *prog.Target, lang, src string) (string, error) {
sysTarget := targets.List[target.OS][target.Arch]
compiler := sysTarget.CCompilerPrefix + "gcc"
sysTarget := targets.Get(target.OS, target.Arch)
compiler := sysTarget.CCompiler
if _, err := exec.LookPath(compiler); err != nil {
return "", ErrNoCompiler
}
@ -38,11 +38,7 @@ func Build(target *prog.Target, lang, src string) (string, error) {
// We do generate uint64's for syscall arguments that overflow longs on 32-bit archs.
flags = append(flags, "-Wno-overflow")
}
out, err := osutil.Command(compiler, append(flags, "-static")...).CombinedOutput()
if err != nil {
// Some distributions don't have static libraries.
out, err = osutil.Command(compiler, flags...).CombinedOutput()
}
out, err := osutil.Command(compiler, flags...).CombinedOutput()
if err != nil {
os.Remove(bin)
data, _ := ioutil.ReadFile(src)

View File

@ -113,7 +113,7 @@ func defineList(p *prog.Prog, opts Options) ([]string, error) {
for _, c := range p.Calls {
defines = append(defines, "__NR_"+c.Meta.CallName)
}
defines = append(defines, targets.List[p.Target.OS][p.Target.Arch].CArch...)
defines = append(defines, targets.Get(p.Target.OS, p.Target.Arch).CArch...)
return defines, nil
}

View File

@ -22,7 +22,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) {
p: p,
opts: opts,
target: p.Target,
sysTarget: targets.List[p.Target.OS][p.Target.Arch],
sysTarget: targets.Get(p.Target.OS, p.Target.Arch),
w: new(bytes.Buffer),
calls: make(map[string]uint64),
}

View File

@ -128,7 +128,7 @@ func DefaultConfig() (*Config, *ExecOpts, error) {
return nil, nil, fmt.Errorf("flag sandbox must contain one of none/setuid/namespace")
}
sysTarget := targets.List[runtime.GOOS][runtime.GOARCH]
sysTarget := targets.Get(runtime.GOOS, runtime.GOARCH)
if sysTarget.ExecutorUsesShmem {
c.Flags |= FlagUseShmem
}

View File

@ -104,7 +104,7 @@ func main() {
buildDir = *flagSourceDir
}
target := targets.List[osStr][archStr]
target := targets.Get(osStr, archStr)
if target == nil {
failf("unknown arch: %v", archStr)
}

View File

@ -3,26 +3,35 @@
package targets
import (
"os"
"os/exec"
"runtime"
"strings"
"sync"
)
type Target struct {
os
OS string
Arch string
PtrSize uint64
PageSize uint64
NumPages uint64
DataOffset uint64
CArch []string
CFlags []string
CrossCFlags []string
CCompilerPrefix string
KernelArch string
KernelHeaderArch string
KernelCrossCompile string
init sync.Once
osCommon
OS string
Arch string
PtrSize uint64
PageSize uint64
NumPages uint64
DataOffset uint64
CArch []string
CFlags []string
CrossCFlags []string
CCompilerPrefix string
CCompiler string
KernelArch string
KernelHeaderArch string
// NeedSyscallDefine is used by csource package to decide when to emit __NR_* defines.
NeedSyscallDefine func(nr uint64) bool
}
type os struct {
type osCommon struct {
// Does the OS use syscall numbers (e.g. Linux) or has interface based on functions (e.g. fuchsia).
SyscallNumbers bool
// E.g. "__NR_" or "SYS_".
@ -32,8 +41,22 @@ type os struct {
ExecutorUsesShmem bool
// If ExecutorUsesForkServer, executor uses extended protocol with handshake.
ExecutorUsesForkServer bool
// Extension of executable files (notably, .exe for windows).
ExeExtension string
}
func Get(OS, arch string) *Target {
target := List[OS][arch]
if target == nil {
return nil
}
target.init.Do(func() {
checkStaticBuild(target)
})
return target
}
// nolint: lll
var List = map[string]map[string]*Target{
"test": map[string]*Target{
"32": {
@ -51,7 +74,7 @@ var List = map[string]map[string]*Target{
PageSize: 4 << 10,
CArch: []string{"__x86_64__"},
CFlags: []string{"-m64"},
CrossCFlags: []string{"-m64"},
CrossCFlags: []string{"-m64", "-static"},
CCompilerPrefix: "x86_64-linux-gnu-",
KernelArch: "x86_64",
KernelHeaderArch: "x86",
@ -66,7 +89,7 @@ var List = map[string]map[string]*Target{
PageSize: 4 << 10,
CArch: []string{"__i386__"},
CFlags: []string{"-m32"},
CrossCFlags: []string{"-m32"},
CrossCFlags: []string{"-m32", "-static"},
CCompilerPrefix: "x86_64-linux-gnu-",
KernelArch: "i386",
KernelHeaderArch: "x86",
@ -75,6 +98,7 @@ var List = map[string]map[string]*Target{
PtrSize: 8,
PageSize: 4 << 10,
CArch: []string{"__aarch64__"},
CrossCFlags: []string{"-static"},
CCompilerPrefix: "aarch64-linux-gnu-",
KernelArch: "arm64",
KernelHeaderArch: "arm64",
@ -84,7 +108,7 @@ var List = map[string]map[string]*Target{
PageSize: 4 << 10,
CArch: []string{"__arm__"},
CFlags: []string{"-D__LINUX_ARM_ARCH__=6", "-m32", "-D__ARM_EABI__"},
CrossCFlags: []string{"-D__LINUX_ARM_ARCH__=6", "-march=armv6t2"},
CrossCFlags: []string{"-D__LINUX_ARM_ARCH__=6", "-march=armv6t2", "-static"},
CCompilerPrefix: "arm-linux-gnueabihf-",
KernelArch: "arm",
KernelHeaderArch: "arm",
@ -94,7 +118,7 @@ var List = map[string]map[string]*Target{
PageSize: 4 << 10,
CArch: []string{"__ppc64__", "__PPC64__", "__powerpc64__"},
CFlags: []string{"-D__powerpc64__"},
CrossCFlags: []string{"-D__powerpc64__"},
CrossCFlags: []string{"-D__powerpc64__", "-static"},
CCompilerPrefix: "powerpc64le-linux-gnu-",
KernelArch: "powerpc",
KernelHeaderArch: "powerpc",
@ -102,18 +126,20 @@ var List = map[string]map[string]*Target{
},
"freebsd": map[string]*Target{
"amd64": {
PtrSize: 8,
PageSize: 4 << 10,
CArch: []string{"__x86_64__"},
CFlags: []string{"-m64"},
PtrSize: 8,
PageSize: 4 << 10,
CArch: []string{"__x86_64__"},
CFlags: []string{"-m64"},
CrossCFlags: []string{"-m64", "-static"},
},
},
"netbsd": map[string]*Target{
"amd64": {
PtrSize: 8,
PageSize: 4 << 10,
CArch: []string{"__x86_64__"},
CFlags: []string{"-m64"},
PtrSize: 8,
PageSize: 4 << 10,
CArch: []string{"__x86_64__"},
CFlags: []string{"-m64"},
CrossCFlags: []string{"-m64", "-static"},
},
},
"fuchsia": map[string]*Target{
@ -122,12 +148,26 @@ var List = map[string]map[string]*Target{
PageSize: 4 << 10,
CArch: []string{"__x86_64__"},
KernelHeaderArch: "x64",
CCompiler: os.ExpandEnv("${SOURCEDIR}/buildtools/linux-x64/clang/bin/clang"),
CrossCFlags: []string{
"--target=x86_64-fuchsia",
"-lfdio",
"-lzircon",
"--sysroot", os.ExpandEnv("${SOURCEDIR}/out/build-zircon/build-x64/sysroot"),
},
},
"arm64": {
PtrSize: 8,
PageSize: 4 << 10,
CArch: []string{"__aarch64__"},
KernelHeaderArch: "arm64",
CCompiler: os.ExpandEnv("${SOURCEDIR}/buildtools/linux-x64/clang/bin/clang"),
CrossCFlags: []string{
"--target=aarch64-fuchsia",
"-lfdio",
"-lzircon",
"--sysroot", os.ExpandEnv("${SOURCEDIR}/out/build-zircon/build-arm64/sysroot"),
},
},
},
"windows": map[string]*Target{
@ -144,11 +184,20 @@ var List = map[string]map[string]*Target{
PageSize: 4 << 10,
CArch: []string{"__x86_64__"},
NeedSyscallDefine: dontNeedSyscallDefine,
CCompiler: os.ExpandEnv("${SOURCEDIR}/install/x86_64-ucb-akaros-gcc/bin/x86_64-ucb-akaros-g++"),
// Most likely this is incorrect (why doesn't it know own sysroot?), but worked for me.
CrossCFlags: []string{
"-static",
"-I", os.ExpandEnv("${SOURCEDIR}/tools/compilers/gcc-glibc/x86_64-ucb-akaros-gcc-stage3-builddir/x86_64-ucb-akaros/libstdc++-v3/include/x86_64-ucb-akaros"),
"-I", os.ExpandEnv("${SOURCEDIR}/tools/compilers/gcc-glibc/x86_64-ucb-akaros-gcc-stage3-builddir/x86_64-ucb-akaros/libstdc++-v3/include"),
"-I", os.ExpandEnv("${SOURCEDIR}/tools/compilers/gcc-glibc/gcc-4.9.2/libstdc++-v3/libsupc++"),
"-L", os.ExpandEnv("${SOURCEDIR}/tools/compilers/gcc-glibc/x86_64-ucb-akaros-gcc-stage3-builddir/x86_64-ucb-akaros/libstdc++-v3/src/.libs"),
},
},
},
}
var oses = map[string]os{
var oses = map[string]osCommon{
"linux": {
SyscallNumbers: true,
SyscallPrefix: "__NR_",
@ -176,6 +225,7 @@ var oses = map[string]os{
SyscallNumbers: false,
ExecutorUsesShmem: false,
ExecutorUsesForkServer: false,
ExeExtension: ".exe",
},
"akaros": {
SyscallNumbers: true,
@ -188,18 +238,51 @@ var oses = map[string]os{
func init() {
for OS, archs := range List {
for arch, target := range archs {
target.os = oses[OS]
target.OS = OS
target.Arch = arch
if target.NeedSyscallDefine == nil {
target.NeedSyscallDefine = needSyscallDefine
}
target.DataOffset = 512 << 20
target.NumPages = (16 << 20) / target.PageSize
initTarget(target, OS, arch)
}
}
}
func initTarget(target *Target, OS, arch string) {
target.osCommon = oses[OS]
target.OS = OS
target.Arch = arch
if target.NeedSyscallDefine == nil {
target.NeedSyscallDefine = needSyscallDefine
}
target.DataOffset = 512 << 20
target.NumPages = (16 << 20) / target.PageSize
if OS == runtime.GOOS && arch == runtime.GOARCH {
// Don't use cross-compiler for native compilation, there are cases when this does not work:
// https://github.com/google/syzkaller/pull/619
// https://github.com/google/syzkaller/issues/387
// https://github.com/google/syzkaller/commit/06db3cec94c54e1cf720cdd5db72761514569d56
target.CCompilerPrefix = ""
}
if target.CCompiler == "" {
target.CCompiler = target.CCompilerPrefix + "gcc"
}
}
func checkStaticBuild(target *Target) {
for i, flag := range target.CrossCFlags {
if flag == "-static" {
// Some distributions don't have static libraries.
if !supportsStatic(target) {
copy(target.CrossCFlags[i:], target.CrossCFlags[i+1:])
target.CrossCFlags = target.CrossCFlags[:len(target.CrossCFlags)-1]
}
break
}
}
}
func supportsStatic(target *Target) bool {
cmd := exec.Command(target.CCompiler, "-x", "c", "-", "-o", "/dev/null")
cmd.Stdin = strings.NewReader("int main(){}")
return cmd.Run() == nil
}
func needSyscallDefine(nr uint64) bool {
return true
}

View File

@ -15,6 +15,7 @@ import (
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/prog"
_ "github.com/google/syzkaller/sys" // most mgrconfig users want targets too
"github.com/google/syzkaller/sys/targets"
"github.com/google/syzkaller/vm"
)
@ -169,11 +170,8 @@ func Complete(cfg *Config) error {
return fmt.Errorf("config param syzkaller is empty")
}
cfg.Syzkaller = osutil.Abs(cfg.Syzkaller)
exe := targets.Get(cfg.TargetOS, cfg.TargetArch).ExeExtension
targetBin := func(name, arch string) string {
exe := ""
if cfg.TargetOS == "windows" {
exe = ".exe"
}
return filepath.Join(cfg.Syzkaller, "bin", cfg.TargetOS+"_"+arch, name+exe)
}
cfg.SyzFuzzerBin = targetBin("syz-fuzzer", cfg.TargetVMArch)

52
tools/syz-env/env.go Normal file
View File

@ -0,0 +1,52 @@
// 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 main
import (
"fmt"
"os"
"runtime"
"strconv"
"strings"
"github.com/google/syzkaller/sys/targets"
)
func main() {
hostOS := or(os.Getenv("HOSTOS"), runtime.GOOS)
hostArch := or(os.Getenv("HOSTARCH"), runtime.GOARCH)
targetOS := or(os.Getenv("TARGETOS"), hostOS)
targetArch := or(os.Getenv("TARGETARCH"), hostArch)
targetVMArch := or(os.Getenv("TARGETVMARCH"), targetArch)
target := targets.Get(targetOS, targetArch)
if target == nil {
fmt.Printf("unknown target %v/%v\n", targetOS, targetArch)
os.Exit(1)
}
type Var struct {
Name string
Val string
}
vars := []Var{
{"HOSTOS", hostOS},
{"HOSTARCH", hostArch},
{"TARGETOS", targetOS},
{"TARGETARCH", targetArch},
{"TARGETVMARCH", targetVMArch},
{"CC", target.CCompiler},
{"ADDCFLAGS", strings.Join(target.CrossCFlags, " ")},
{"NCORES", strconv.Itoa(runtime.NumCPU())},
{"EXE", target.ExeExtension},
}
for _, v := range vars {
fmt.Printf("export %v=%v\\n", v.Name, v.Val)
}
}
func or(s1, s2 string) string {
if s1 != "" {
return s1
}
return s2
}