blob: c0d0cd21b613e0eaeeb6ebcbf5d4bf34751e943a [file] [log] [blame]
// Copyright 2015/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"
"go/format"
"io"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"sort"
"strings"
"sync"
"text/template"
"github.com/google/syzkaller/pkg/ast"
"github.com/google/syzkaller/pkg/compiler"
"github.com/google/syzkaller/pkg/hash"
"github.com/google/syzkaller/pkg/serializer"
"github.com/google/syzkaller/prog"
"github.com/google/syzkaller/sys/targets"
)
var (
flagV = flag.Int("v", 0, "verbosity")
flagMemProfile = flag.String("memprofile", "", "write a memory profile to the file")
)
func main() {
flag.Parse()
for OS, archs := range targets.List {
top := ast.ParseGlob(filepath.Join("sys", OS, "*.txt"), nil)
if top == nil {
os.Exit(1)
}
type Job struct {
Target *targets.Target
OK bool
Errors []string
Unsupported map[string]bool
ArchData []byte
}
var jobs []*Job
for _, target := range archs {
jobs = append(jobs, &Job{
Target: target,
})
}
sort.Slice(jobs, func(i, j int) bool {
return jobs[i].Target.Arch < jobs[j].Target.Arch
})
var wg sync.WaitGroup
wg.Add(len(jobs))
for _, job := range jobs {
job := job
go func() {
defer wg.Done()
eh := func(pos ast.Pos, msg string) {
job.Errors = append(job.Errors, fmt.Sprintf("%v: %v\n", pos, msg))
}
consts := compiler.DeserializeConstsGlob(filepath.Join("sys", OS, "*_"+job.Target.Arch+".const"), eh)
if consts == nil {
return
}
prog := compiler.Compile(top, consts, job.Target, eh)
if prog == nil {
return
}
job.Unsupported = prog.Unsupported
sysFile := filepath.Join("sys", OS, job.Target.Arch+".go")
out := new(bytes.Buffer)
generate(job.Target, prog, consts, out)
rev := hash.String(out.Bytes())
fmt.Fprintf(out, "const revision_%v = %q\n", job.Target.Arch, rev)
writeSource(sysFile, out.Bytes())
job.ArchData = generateExecutorSyscalls(job.Target, prog.Syscalls, rev)
job.OK = true
}()
}
wg.Wait()
var syscallArchs [][]byte
unsupported := make(map[string]int)
for _, job := range jobs {
fmt.Printf("generating %v/%v...\n", job.Target.OS, job.Target.Arch)
for _, msg := range job.Errors {
fmt.Print(msg)
}
if !job.OK {
os.Exit(1)
}
syscallArchs = append(syscallArchs, job.ArchData)
for u := range job.Unsupported {
unsupported[u]++
}
fmt.Printf("\n")
}
for what, count := range unsupported {
if count == len(jobs) {
failf("%v is unsupported on all arches (typo?)", what)
}
}
writeExecutorSyscalls(OS, syscallArchs)
}
if *flagMemProfile != "" {
f, err := os.Create(*flagMemProfile)
if err != nil {
failf("could not create memory profile: ", err)
}
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
failf("could not write memory profile: ", err)
}
f.Close()
}
}
func generate(target *targets.Target, prg *compiler.Prog, consts map[string]uint64, out io.Writer) {
fmt.Fprintf(out, "// AUTOGENERATED FILE\n")
fmt.Fprintf(out, "package %v\n\n", target.OS)
fmt.Fprintf(out, "import . \"github.com/google/syzkaller/prog\"\n\n")
fmt.Fprintf(out, "func init() {\n")
fmt.Fprintf(out, "\tRegisterTarget(&Target{OS: %q, Arch: %q, Revision: revision_%v, PtrSize: %v,"+
"Syscalls: syscalls_%v, Resources: resources_%v, Structs: structDescs_%v, Consts: consts_%v}, "+
"initTarget)\n",
target.OS, target.Arch, target.Arch, target.PtrSize,
target.Arch, target.Arch, target.Arch, target.Arch)
fmt.Fprintf(out, "}\n\n")
fmt.Fprintf(out, "var resources_%v = ", target.Arch)
serializer.Write(out, prg.Resources)
fmt.Fprintf(out, "\n\n")
fmt.Fprintf(out, "var structDescs_%v = ", target.Arch)
serializer.Write(out, prg.StructDescs)
fmt.Fprintf(out, "\n\n")
fmt.Fprintf(out, "var syscalls_%v = ", target.Arch)
serializer.Write(out, prg.Syscalls)
fmt.Fprintf(out, "\n\n")
constArr := make([]prog.ConstValue, 0, len(consts))
for name, val := range consts {
constArr = append(constArr, prog.ConstValue{name, val})
}
sort.Slice(constArr, func(i, j int) bool {
return constArr[i].Name < constArr[j].Name
})
fmt.Fprintf(out, "var consts_%v = ", target.Arch)
serializer.Write(out, constArr)
fmt.Fprintf(out, "\n\n")
}
func generateExecutorSyscalls(target *targets.Target, syscalls []*prog.Syscall, rev string) []byte {
type SyscallData struct {
Name string
CallName string
NR int32
NeedCall bool
}
type ArchData struct {
Revision string
GOARCH string
CARCH []string
Calls []SyscallData
Fake []SyscallData
}
data := ArchData{
Revision: rev,
GOARCH: target.Arch,
CARCH: target.CArch,
}
fake := make(map[string]uint64)
for _, c := range syscalls {
syz := strings.HasPrefix(c.CallName, "syz_")
if syz {
fake[c.CallName] = c.NR
}
data.Calls = append(data.Calls, SyscallData{
Name: c.Name,
CallName: c.CallName,
NR: int32(c.NR),
NeedCall: syz || !target.SyscallNumbers,
})
}
for name, nr := range fake {
data.Fake = append(data.Fake, SyscallData{
Name: name,
NR: int32(nr),
})
}
sort.Slice(data.Calls, func(i, j int) bool {
return data.Calls[i].Name < data.Calls[j].Name
})
sort.Slice(data.Fake, func(i, j int) bool {
return data.Fake[i].Name < data.Fake[j].Name
})
buf := new(bytes.Buffer)
if err := archTempl.Execute(buf, data); err != nil {
failf("failed to execute arch template: %v", err)
}
return buf.Bytes()
}
func writeExecutorSyscalls(OS string, archs [][]byte) {
buf := new(bytes.Buffer)
buf.WriteString("// AUTOGENERATED FILE\n\n")
for _, arch := range archs {
buf.Write(arch)
}
writeFile(filepath.Join("executor", fmt.Sprintf("syscalls_%v.h", OS)), buf.Bytes())
}
func writeSource(file string, data []byte) {
src, err := format.Source(data)
if err != nil {
fmt.Printf("%s\n", data)
failf("failed to format output: %v", err)
}
if oldSrc, err := ioutil.ReadFile(file); err == nil && bytes.Equal(src, oldSrc) {
return
}
writeFile(file, src)
}
func writeFile(file string, data []byte) {
outf, err := os.Create(file)
if err != nil {
failf("failed to create output file: %v", err)
}
defer outf.Close()
outf.Write(data)
}
func failf(msg string, args ...interface{}) {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
os.Exit(1)
}
func logf(v int, msg string, args ...interface{}) {
if *flagV >= v {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
}
}
var archTempl = template.Must(template.New("").Parse(`
#if {{range $cdef := $.CARCH}}defined({{$cdef}}) || {{end}}0
#define GOARCH "{{.GOARCH}}"
#define SYZ_REVISION "{{.Revision}}"
{{range $c := $.Fake}}#define __NR_{{$c.Name}} {{$c.NR}}
{{end}}
unsigned syscall_count = {{len $.Calls}};
call_t syscalls[] = {
{{range $c := $.Calls}} {"{{$c.Name}}", {{$c.NR}}{{if $c.NeedCall}}, (syscall_t){{$c.CallName}}{{end}}},
{{end}}
};
#endif
`))