blob: 117c10e5891d22c768b27692f1dca35e182c8e21 [file] [log] [blame]
// Copyright 2016 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 (
"bytes"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"github.com/google/syzkaller/pkg/ast"
"github.com/google/syzkaller/pkg/compiler"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/pkg/tool"
"github.com/google/syzkaller/sys/targets"
)
var (
flagOS = flag.String("os", runtime.GOOS, "target OS")
flagBuild = flag.Bool("build", false, "regenerate arch-specific kernel headers")
flagSourceDir = flag.String("sourcedir", "", "path to kernel source checkout dir")
flagIncludes = flag.String("includedirs", "", "path to other kernel source include dirs separated by commas")
flagBuildDir = flag.String("builddir", "", "path to kernel build dir")
flagArch = flag.String("arch", "", "comma-separated list of arches to generate (all by default)")
)
type Arch struct {
target *targets.Target
sourceDir string
includeDirs string
buildDir string
build bool
files []*File
err error
done chan bool
}
type File struct {
arch *Arch
name string
consts map[string]uint64
undeclared map[string]bool
info *compiler.ConstInfo
err error
done chan bool
}
type Extractor interface {
prepare(sourcedir string, build bool, arches []*Arch) error
prepareArch(arch *Arch) error
processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint64, map[string]bool, error)
}
var extractors = map[string]Extractor{
targets.Akaros: new(akaros),
targets.Linux: new(linux),
targets.FreeBSD: new(freebsd),
targets.Darwin: new(darwin),
targets.NetBSD: new(netbsd),
targets.OpenBSD: new(openbsd),
"android": new(linux),
targets.Fuchsia: new(fuchsia),
targets.Windows: new(windows),
targets.Trusty: new(trusty),
}
func main() {
flag.Parse()
if *flagBuild && *flagBuildDir != "" {
tool.Failf("-build and -builddir is an invalid combination")
}
OS := *flagOS
extractor := extractors[OS]
if extractor == nil {
tool.Failf("unknown os: %v", OS)
}
arches, err := createArches(OS, archList(OS, *flagArch), flag.Args())
if err != nil {
tool.Fail(err)
}
if *flagSourceDir == "" {
tool.Fail(fmt.Errorf("provide path to kernel checkout via -sourcedir " +
"flag (or make extract SOURCEDIR)"))
}
if err := extractor.prepare(*flagSourceDir, *flagBuild, arches); err != nil {
tool.Fail(err)
}
jobC := make(chan interface{}, len(arches))
for _, arch := range arches {
jobC <- arch
}
for p := 0; p < runtime.GOMAXPROCS(0); p++ {
go worker(extractor, jobC)
}
failed := false
constFiles := make(map[string]*compiler.ConstFile)
for _, arch := range arches {
fmt.Printf("generating %v/%v...\n", OS, arch.target.Arch)
<-arch.done
if arch.err != nil {
failed = true
fmt.Printf("%v\n", arch.err)
continue
}
for _, f := range arch.files {
<-f.done
if f.err != nil {
failed = true
fmt.Printf("%v: %v\n", f.name, f.err)
continue
}
if constFiles[f.name] == nil {
constFiles[f.name] = compiler.NewConstFile()
}
constFiles[f.name].AddArch(f.arch.target.Arch, f.consts, f.undeclared)
}
}
for file, cf := range constFiles {
outname := filepath.Join("sys", OS, file+".const")
data := cf.Serialize()
if len(data) == 0 {
os.Remove(outname)
continue
}
if err := osutil.WriteFile(outname, data); err != nil {
tool.Failf("failed to write output file: %v", err)
}
}
if !failed && *flagArch == "" {
failed = checkUnsupportedCalls(arches)
}
for _, arch := range arches {
if arch.build {
os.RemoveAll(arch.buildDir)
}
}
if failed {
os.Exit(1)
}
}
func worker(extractor Extractor, jobC chan interface{}) {
for job := range jobC {
switch j := job.(type) {
case *Arch:
infos, err := processArch(extractor, j)
j.err = err
close(j.done)
if j.err == nil {
for _, f := range j.files {
f.info = infos[filepath.Join("sys", j.target.OS, f.name)]
jobC <- f
}
}
case *File:
j.consts, j.undeclared, j.err = processFile(extractor, j.arch, j)
close(j.done)
}
}
}
func createArches(OS string, archArray, files []string) ([]*Arch, error) {
errBuf := new(bytes.Buffer)
eh := func(pos ast.Pos, msg string) {
fmt.Fprintf(errBuf, "%v: %v\n", pos, msg)
}
top := ast.ParseGlob(filepath.Join("sys", OS, "*.txt"), eh)
if top == nil {
return nil, fmt.Errorf("%v", errBuf.String())
}
allFiles := compiler.FileList(top, OS, eh)
if allFiles == nil {
return nil, fmt.Errorf("%v", errBuf.String())
}
var arches []*Arch
for _, archStr := range archArray {
buildDir := ""
if *flagBuild {
dir, err := ioutil.TempDir("", "syzkaller-kernel-build")
if err != nil {
return nil, fmt.Errorf("failed to create temp dir: %v", err)
}
buildDir = dir
} else if *flagBuildDir != "" {
buildDir = *flagBuildDir
} else {
buildDir = *flagSourceDir
}
target := targets.Get(OS, archStr)
if target == nil {
return nil, fmt.Errorf("unknown arch: %v", archStr)
}
arch := &Arch{
target: target,
sourceDir: *flagSourceDir,
includeDirs: *flagIncludes,
buildDir: buildDir,
build: *flagBuild,
done: make(chan bool),
}
archFiles := files
if len(archFiles) == 0 {
for file, meta := range allFiles {
if meta.NoExtract || !meta.SupportsArch(archStr) {
continue
}
archFiles = append(archFiles, file)
}
}
sort.Strings(archFiles)
for _, f := range archFiles {
arch.files = append(arch.files, &File{
arch: arch,
name: f,
done: make(chan bool),
})
}
arches = append(arches, arch)
}
return arches, nil
}
func archList(OS, arches string) []string {
if arches != "" {
return strings.Split(arches, ",")
}
var archArray []string
for arch := range targets.List[OS] {
archArray = append(archArray, arch)
}
sort.Strings(archArray)
return archArray
}
func checkUnsupportedCalls(arches []*Arch) bool {
supported := make(map[string]bool)
unsupported := make(map[string]string)
for _, arch := range arches {
for _, f := range arch.files {
for name := range f.consts {
supported[name] = true
}
for name := range f.undeclared {
unsupported[name] = f.name
}
}
}
failed := false
for name, file := range unsupported {
if supported[name] {
continue
}
failed = true
fmt.Printf("%v: %v is unsupported on all arches (typo?)\n",
file, name)
}
return failed
}
func processArch(extractor Extractor, arch *Arch) (map[string]*compiler.ConstInfo, error) {
errBuf := new(bytes.Buffer)
eh := func(pos ast.Pos, msg string) {
fmt.Fprintf(errBuf, "%v: %v\n", pos, msg)
}
top := ast.ParseGlob(filepath.Join("sys", arch.target.OS, "*.txt"), eh)
if top == nil {
return nil, fmt.Errorf("%v", errBuf.String())
}
infos := compiler.ExtractConsts(top, arch.target, eh)
if infos == nil {
return nil, fmt.Errorf("%v", errBuf.String())
}
if err := extractor.prepareArch(arch); err != nil {
return nil, err
}
return infos, nil
}
func processFile(extractor Extractor, arch *Arch, file *File) (map[string]uint64, map[string]bool, error) {
inname := filepath.Join("sys", arch.target.OS, file.name)
if file.info == nil {
return nil, nil, fmt.Errorf("const info for input file %v is missing", inname)
}
if len(file.info.Consts) == 0 {
return nil, nil, nil
}
return extractor.processFile(arch, file.info)
}