blob: da8c568fe769a37a82eac9b5ba49a2e933cd5065 [file] [log] [blame]
// 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 targets
import (
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"sync"
)
type Target struct {
init sync.Once
osCommon
OS string
Arch string
VMArch string // e.g. amd64 for 386, or arm64 for arm
PtrSize uint64
PageSize uint64
NumPages uint64
DataOffset uint64
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 osCommon struct {
// What OS can build native binaries for this OS.
// If not set, defaults to itself (i.e. native build).
// Later we can extend this to be a list, but so far we don't have more than one OS.
BuildOS string
// 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_".
SyscallPrefix string
// ipc<->executor communication tuning.
// If ExecutorUsesShmem, programs and coverage are passed through shmem, otherwise via pipes.
ExecutorUsesShmem bool
// If ExecutorUsesForkServer, executor uses extended protocol with handshake.
ExecutorUsesForkServer bool
// Extension of executable files (notably, .exe for windows).
ExeExtension string
// Name of the kernel object file.
KernelObject string
// Name of cpp(1) executable.
CPP string
}
func Get(OS, arch string) *Target {
target := List[OS][arch]
if target == nil {
return nil
}
target.init.Do(func() {
checkOptionalFlags(target)
})
return target
}
// nolint: lll
var List = map[string]map[string]*Target{
"test": {
"64": {
PtrSize: 8,
PageSize: 4 << 10,
CFlags: []string{"-m64"},
CrossCFlags: []string{"-m64", "-static"},
osCommon: osCommon{
SyscallNumbers: true,
SyscallPrefix: "SYS_",
ExecutorUsesShmem: false,
ExecutorUsesForkServer: false,
},
},
"64_fork": {
PtrSize: 8,
PageSize: 8 << 10,
CFlags: []string{"-m64"},
CrossCFlags: []string{"-m64", "-static"},
osCommon: osCommon{
SyscallNumbers: true,
SyscallPrefix: "SYS_",
ExecutorUsesShmem: false,
ExecutorUsesForkServer: true,
},
},
"32_shmem": {
PtrSize: 4,
PageSize: 8 << 10,
CFlags: []string{"-m32"},
CrossCFlags: []string{"-m32", "-static"},
osCommon: osCommon{
SyscallNumbers: true,
SyscallPrefix: "SYS_",
ExecutorUsesShmem: true,
ExecutorUsesForkServer: false,
},
},
"32_fork_shmem": {
PtrSize: 4,
PageSize: 4 << 10,
CFlags: []string{"-m32"},
CrossCFlags: []string{"-m32", "-static"},
osCommon: osCommon{
SyscallNumbers: true,
SyscallPrefix: "SYS_",
ExecutorUsesShmem: true,
ExecutorUsesForkServer: true,
},
},
},
"linux": {
"amd64": {
PtrSize: 8,
PageSize: 4 << 10,
CFlags: []string{"-m64"},
CrossCFlags: []string{"-m64", "-static"},
CCompilerPrefix: "x86_64-linux-gnu-",
KernelArch: "x86_64",
KernelHeaderArch: "x86",
NeedSyscallDefine: func(nr uint64) bool {
// Only generate defines for new syscalls
// (added after commit 8a1ab3155c2ac on 2012-10-04).
return nr >= 313
},
},
"386": {
VMArch: "amd64",
PtrSize: 4,
PageSize: 4 << 10,
CFlags: []string{"-m32"},
CrossCFlags: []string{"-m32", "-static"},
CCompilerPrefix: "x86_64-linux-gnu-",
KernelArch: "i386",
KernelHeaderArch: "x86",
},
"arm64": {
PtrSize: 8,
PageSize: 4 << 10,
CrossCFlags: []string{"-static"},
CCompilerPrefix: "aarch64-linux-gnu-",
KernelArch: "arm64",
KernelHeaderArch: "arm64",
},
"arm": {
VMArch: "arm64",
PtrSize: 4,
PageSize: 4 << 10,
CFlags: []string{"-D__LINUX_ARM_ARCH__=6", "-m32", "-D__ARM_EABI__"},
CrossCFlags: []string{"-D__LINUX_ARM_ARCH__=6", "-march=armv6", "-static"},
CCompilerPrefix: "arm-linux-gnueabi-",
KernelArch: "arm",
KernelHeaderArch: "arm",
},
"ppc64le": {
PtrSize: 8,
PageSize: 4 << 10,
CFlags: []string{"-D__powerpc64__"},
CrossCFlags: []string{"-D__powerpc64__", "-static"},
CCompilerPrefix: "powerpc64le-linux-gnu-",
KernelArch: "powerpc",
KernelHeaderArch: "powerpc",
},
},
"freebsd": {
"amd64": {
PtrSize: 8,
PageSize: 4 << 10,
CFlags: []string{"-m64"},
CrossCFlags: []string{"-m64", "-static"},
NeedSyscallDefine: dontNeedSyscallDefine,
},
},
"netbsd": {
"amd64": {
PtrSize: 8,
PageSize: 4 << 10,
CFlags: []string{"-m64"},
CrossCFlags: []string{"-m64", "-static",
"--sysroot", os.ExpandEnv("${SOURCEDIR}/../dest/"),
},
CCompiler: os.ExpandEnv("${SOURCEDIR}/../tools/bin/x86_64--netbsd-g++"),
},
},
"openbsd": {
"amd64": {
PtrSize: 8,
PageSize: 4 << 10,
CFlags: []string{"-m64"},
CCompiler: "c++",
CrossCFlags: []string{"-m64", "-static", "-lutil"},
NeedSyscallDefine: func(nr uint64) bool {
switch nr {
case 8: // SYS___tfork
return true
case 94: // SYS___thrsleep
return true
case 198: // SYS___syscall
return true
case 295: // SYS___semctl
return true
case 301: // SYS___thrwakeup
return true
case 302: // SYS___threxit
return true
case 303: // SYS___thrsigdivert
return true
case 304: // SYS___getcwd
return true
case 329: // SYS___set_tcb
return true
case 330: // SYS___get_tcb
return true
}
return false
},
},
},
"fuchsia": {
"amd64": {
PtrSize: 8,
PageSize: 4 << 10,
KernelHeaderArch: "x64",
CCompiler: os.ExpandEnv("${SOURCEDIR}/buildtools/linux-x64/clang/bin/clang++"),
CrossCFlags: []string{
"-Wno-deprecated",
"--target=x86_64-fuchsia",
"-lfdio",
"-lzircon",
"-ldriver",
"--sysroot", os.ExpandEnv("${SOURCEDIR}/out/x64/sdk/exported/zircon_sysroot/arch/x64/sysroot"),
"-I", os.ExpandEnv("${SOURCEDIR}/zircon/system/ulib/fdio/include"),
"-I", os.ExpandEnv("${SOURCEDIR}/zircon/system/ulib/ddk/include"),
"-L", os.ExpandEnv("${SOURCEDIR}/out/x64/x64-shared"),
},
},
"arm64": {
PtrSize: 8,
PageSize: 4 << 10,
KernelHeaderArch: "arm64",
CCompiler: os.ExpandEnv("${SOURCEDIR}/buildtools/linux-x64/clang/bin/clang++"),
CrossCFlags: []string{
"-Wno-deprecated",
"--target=aarch64-fuchsia",
"-lfdio",
"-lzircon",
"-ldriver",
"--sysroot", os.ExpandEnv("${SOURCEDIR}/out/arm64/sdk/exported/zircon_sysroot/arch/arm64/sysroot"),
"-L", os.ExpandEnv("${SOURCEDIR}/out/arm64/arm64-shared"),
"-I", os.ExpandEnv("${SOURCEDIR}/zircon/system/ulib/fdio/include"),
"-I", os.ExpandEnv("${SOURCEDIR}/zircon/system/ulib/ddk/include"),
},
},
},
"windows": {
"amd64": {
PtrSize: 8,
// TODO(dvyukov): what should we do about 4k vs 64k?
PageSize: 4 << 10,
},
},
"akaros": {
"amd64": {
PtrSize: 8,
PageSize: 4 << 10,
KernelHeaderArch: "x86",
NeedSyscallDefine: dontNeedSyscallDefine,
CCompiler: os.ExpandEnv("${SOURCEDIR}/toolchain/x86_64-ucb-akaros-gcc/bin/x86_64-ucb-akaros-g++"),
CrossCFlags: []string{
"-static",
},
},
},
"trusty": {
"arm": {
PtrSize: 4,
PageSize: 4 << 10,
NeedSyscallDefine: dontNeedSyscallDefine,
},
},
}
var oses = map[string]osCommon{
"linux": {
SyscallNumbers: true,
SyscallPrefix: "__NR_",
ExecutorUsesShmem: true,
ExecutorUsesForkServer: true,
KernelObject: "vmlinux",
CPP: "cpp",
},
"freebsd": {
SyscallNumbers: true,
SyscallPrefix: "SYS_",
ExecutorUsesShmem: true,
ExecutorUsesForkServer: true,
KernelObject: "kernel.full",
CPP: "g++",
},
"netbsd": {
BuildOS: "linux",
SyscallNumbers: true,
SyscallPrefix: "SYS_",
ExecutorUsesShmem: true,
ExecutorUsesForkServer: true,
KernelObject: "netbsd.gdb",
CPP: "cpp",
},
"openbsd": {
SyscallNumbers: true,
SyscallPrefix: "SYS_",
ExecutorUsesShmem: true,
ExecutorUsesForkServer: true,
KernelObject: "bsd.gdb",
CPP: "ecpp",
},
"fuchsia": {
BuildOS: "linux",
SyscallNumbers: false,
ExecutorUsesShmem: false,
ExecutorUsesForkServer: false,
KernelObject: "zircon.elf",
CPP: "cpp",
},
"windows": {
SyscallNumbers: false,
ExecutorUsesShmem: false,
ExecutorUsesForkServer: false,
ExeExtension: ".exe",
KernelObject: "vmlinux",
CPP: "cpp",
},
"akaros": {
BuildOS: "linux",
SyscallNumbers: true,
SyscallPrefix: "SYS_",
ExecutorUsesShmem: false,
ExecutorUsesForkServer: true,
KernelObject: "akaros-kernel-64b",
CPP: "cpp",
},
"trusty": {
SyscallNumbers: true,
SyscallPrefix: "__NR_",
CPP: "cpp",
},
}
var (
commonCFlags = []string{
"-O2",
"-pthread",
"-Wall",
"-Werror",
"-Wparentheses",
"-Wunused-const-variable",
"-Wframe-larger-than=8192",
}
optionalCFlags = map[string]bool{
"-static": true, // some distributions don't have static libraries
"-Wunused-const-variable": true, // gcc 5 does not support this flag
}
)
func init() {
for OS, archs := range List {
for arch, target := range archs {
initTarget(target, OS, arch)
}
}
goos := runtime.GOOS
if goos == "android" {
goos = "linux"
}
for _, target := range List["test"] {
target.CCompiler = List[goos][runtime.GOARCH].CCompiler
target.CPP = List[goos][runtime.GOARCH].CPP
target.BuildOS = goos
if runtime.GOOS == "freebsd" && runtime.GOARCH == "amd64" && target.PtrSize == 4 {
// -m32 alone does not work on freebsd with gcc.
// TODO(dvyukov): consider switching to clang on freebsd instead.
target.CFlags = append(target.CFlags, "-B/usr/lib32")
target.CrossCFlags = append(target.CrossCFlags, "-B/usr/lib32")
}
}
}
func initTarget(target *Target, OS, arch string) {
if common, ok := oses[OS]; ok {
target.osCommon = common
}
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 == "linux" && 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"
}
if target.BuildOS == "" {
target.BuildOS = OS
}
if runtime.GOOS != target.BuildOS {
// Spoil native binaries if they are not usable, so that nobody tries to use them later.
target.CCompiler = fmt.Sprintf("cant-build-%v-on-%v", target.OS, runtime.GOOS)
target.CPP = target.CCompiler
}
target.CrossCFlags = append(append([]string{}, commonCFlags...), target.CrossCFlags...)
}
func checkOptionalFlags(target *Target) {
flags := make(map[string]*bool)
var wg sync.WaitGroup
for _, flag := range target.CrossCFlags {
if !optionalCFlags[flag] {
continue
}
res := new(bool)
flags[flag] = res
wg.Add(1)
go func(flag string) {
defer wg.Done()
*res = checkFlagSupported(target, flag)
}(flag)
}
wg.Wait()
for i := 0; i < len(target.CrossCFlags); i++ {
if res := flags[target.CrossCFlags[i]]; res != nil && !*res {
copy(target.CrossCFlags[i:], target.CrossCFlags[i+1:])
target.CrossCFlags = target.CrossCFlags[:len(target.CrossCFlags)-1]
i--
}
}
}
func checkFlagSupported(target *Target, flag string) bool {
cmd := exec.Command(target.CCompiler, "-x", "c", "-", "-o", "/dev/null", flag)
cmd.Stdin = strings.NewReader("int main(){}")
return cmd.Run() == nil
}
func needSyscallDefine(nr uint64) bool {
return true
}
func dontNeedSyscallDefine(nr uint64) bool {
return false
}