Home | History | Annotate | Download | only in syz-sysgen
      1 // Copyright 2015/2016 syzkaller project authors. All rights reserved.
      2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
      3 
      4 package main
      5 
      6 import (
      7 	"bytes"
      8 	"flag"
      9 	"fmt"
     10 	"go/format"
     11 	"io"
     12 	"io/ioutil"
     13 	"os"
     14 	"path/filepath"
     15 	"runtime"
     16 	"runtime/pprof"
     17 	"sort"
     18 	"strings"
     19 	"sync"
     20 	"text/template"
     21 
     22 	"github.com/google/syzkaller/pkg/ast"
     23 	"github.com/google/syzkaller/pkg/compiler"
     24 	"github.com/google/syzkaller/pkg/hash"
     25 	"github.com/google/syzkaller/pkg/osutil"
     26 	"github.com/google/syzkaller/pkg/serializer"
     27 	"github.com/google/syzkaller/prog"
     28 	"github.com/google/syzkaller/sys/targets"
     29 )
     30 
     31 var (
     32 	flagMemProfile = flag.String("memprofile", "", "write a memory profile to the file")
     33 )
     34 
     35 type SyscallData struct {
     36 	Name     string
     37 	CallName string
     38 	NR       int32
     39 	NeedCall bool
     40 }
     41 
     42 type ArchData struct {
     43 	Revision   string
     44 	ForkServer int
     45 	Shmem      int
     46 	GOARCH     string
     47 	PageSize   uint64
     48 	NumPages   uint64
     49 	DataOffset uint64
     50 	Calls      []SyscallData
     51 }
     52 
     53 type OSData struct {
     54 	GOOS  string
     55 	Archs []ArchData
     56 }
     57 
     58 func main() {
     59 	flag.Parse()
     60 
     61 	var oses []OSData
     62 	for OS, archs := range targets.List {
     63 		top := ast.ParseGlob(filepath.Join("sys", OS, "*.txt"), nil)
     64 		if top == nil {
     65 			os.Exit(1)
     66 		}
     67 		osutil.MkdirAll(filepath.Join("sys", OS, "gen"))
     68 
     69 		type Job struct {
     70 			Target      *targets.Target
     71 			OK          bool
     72 			Errors      []string
     73 			Unsupported map[string]bool
     74 			ArchData    ArchData
     75 		}
     76 		var jobs []*Job
     77 		for _, target := range archs {
     78 			jobs = append(jobs, &Job{
     79 				Target: target,
     80 			})
     81 		}
     82 		sort.Slice(jobs, func(i, j int) bool {
     83 			return jobs[i].Target.Arch < jobs[j].Target.Arch
     84 		})
     85 		var wg sync.WaitGroup
     86 		wg.Add(len(jobs))
     87 
     88 		for _, job := range jobs {
     89 			job := job
     90 			go func() {
     91 				defer wg.Done()
     92 				eh := func(pos ast.Pos, msg string) {
     93 					job.Errors = append(job.Errors, fmt.Sprintf("%v: %v\n", pos, msg))
     94 				}
     95 				consts := compiler.DeserializeConstsGlob(filepath.Join("sys", OS, "*_"+job.Target.Arch+".const"), eh)
     96 				if consts == nil {
     97 					return
     98 				}
     99 				prog := compiler.Compile(top, consts, job.Target, eh)
    100 				if prog == nil {
    101 					return
    102 				}
    103 				job.Unsupported = prog.Unsupported
    104 
    105 				sysFile := filepath.Join("sys", OS, "gen", job.Target.Arch+".go")
    106 				out := new(bytes.Buffer)
    107 				generate(job.Target, prog, consts, out)
    108 				rev := hash.String(out.Bytes())
    109 				fmt.Fprintf(out, "const revision_%v = %q\n", job.Target.Arch, rev)
    110 				writeSource(sysFile, out.Bytes())
    111 
    112 				job.ArchData = generateExecutorSyscalls(job.Target, prog.Syscalls, rev)
    113 
    114 				job.OK = true
    115 			}()
    116 		}
    117 		writeEmpty(OS)
    118 		wg.Wait()
    119 
    120 		var syscallArchs []ArchData
    121 		unsupported := make(map[string]int)
    122 		for _, job := range jobs {
    123 			fmt.Printf("generating %v/%v...\n", job.Target.OS, job.Target.Arch)
    124 			for _, msg := range job.Errors {
    125 				fmt.Print(msg)
    126 			}
    127 			if !job.OK {
    128 				os.Exit(1)
    129 			}
    130 			syscallArchs = append(syscallArchs, job.ArchData)
    131 			for u := range job.Unsupported {
    132 				unsupported[u]++
    133 			}
    134 			fmt.Printf("\n")
    135 		}
    136 		oses = append(oses, OSData{
    137 			GOOS:  OS,
    138 			Archs: syscallArchs,
    139 		})
    140 
    141 		for what, count := range unsupported {
    142 			if count == len(jobs) {
    143 				failf("%v is unsupported on all arches (typo?)", what)
    144 			}
    145 		}
    146 	}
    147 
    148 	writeExecutorSyscalls(oses)
    149 
    150 	if *flagMemProfile != "" {
    151 		f, err := os.Create(*flagMemProfile)
    152 		if err != nil {
    153 			failf("could not create memory profile: ", err)
    154 		}
    155 		runtime.GC() // get up-to-date statistics
    156 		if err := pprof.WriteHeapProfile(f); err != nil {
    157 			failf("could not write memory profile: ", err)
    158 		}
    159 		f.Close()
    160 	}
    161 }
    162 
    163 func generate(target *targets.Target, prg *compiler.Prog, consts map[string]uint64, out io.Writer) {
    164 	tag := fmt.Sprintf("syz_target,syz_os_%v,syz_arch_%v", target.OS, target.Arch)
    165 	if target.VMArch != "" {
    166 		tag += fmt.Sprintf(" syz_target,syz_os_%v,syz_arch_%v", target.OS, target.VMArch)
    167 	}
    168 	fmt.Fprintf(out, "// AUTOGENERATED FILE\n")
    169 	fmt.Fprintf(out, "// +build !syz_target %v\n\n", tag)
    170 	fmt.Fprintf(out, "package gen\n\n")
    171 	fmt.Fprintf(out, "import . \"github.com/google/syzkaller/prog\"\n")
    172 	fmt.Fprintf(out, "import . \"github.com/google/syzkaller/sys/%v\"\n\n", target.OS)
    173 
    174 	fmt.Fprintf(out, "func init() {\n")
    175 	fmt.Fprintf(out, "\tRegisterTarget(&Target{"+
    176 		"OS: %q, Arch: %q, Revision: revision_%v, PtrSize: %v, "+
    177 		"PageSize: %v, NumPages: %v, DataOffset: %v, Syscalls: syscalls_%v, "+
    178 		"Resources: resources_%v, Structs: structDescs_%v, Consts: consts_%v}, "+
    179 		"InitTarget)\n}\n\n",
    180 		target.OS, target.Arch, target.Arch, target.PtrSize,
    181 		target.PageSize, target.NumPages, target.DataOffset,
    182 		target.Arch, target.Arch, target.Arch, target.Arch)
    183 
    184 	fmt.Fprintf(out, "var resources_%v = ", target.Arch)
    185 	serializer.Write(out, prg.Resources)
    186 	fmt.Fprintf(out, "\n\n")
    187 
    188 	fmt.Fprintf(out, "var structDescs_%v = ", target.Arch)
    189 	serializer.Write(out, prg.StructDescs)
    190 	fmt.Fprintf(out, "\n\n")
    191 
    192 	fmt.Fprintf(out, "var syscalls_%v = ", target.Arch)
    193 	serializer.Write(out, prg.Syscalls)
    194 	fmt.Fprintf(out, "\n\n")
    195 
    196 	constArr := make([]prog.ConstValue, 0, len(consts))
    197 	for name, val := range consts {
    198 		constArr = append(constArr, prog.ConstValue{Name: name, Value: val})
    199 	}
    200 	sort.Slice(constArr, func(i, j int) bool {
    201 		return constArr[i].Name < constArr[j].Name
    202 	})
    203 	fmt.Fprintf(out, "var consts_%v = ", target.Arch)
    204 	serializer.Write(out, constArr)
    205 	fmt.Fprintf(out, "\n\n")
    206 }
    207 
    208 func writeEmpty(OS string) {
    209 	const data = `// AUTOGENERATED FILE
    210 // This file is needed if OS is completely excluded by build tags.
    211 package gen
    212 `
    213 	writeSource(filepath.Join("sys", OS, "gen", "empty.go"), []byte(data))
    214 }
    215 
    216 func generateExecutorSyscalls(target *targets.Target, syscalls []*prog.Syscall, rev string) ArchData {
    217 	data := ArchData{
    218 		Revision:   rev,
    219 		GOARCH:     target.Arch,
    220 		PageSize:   target.PageSize,
    221 		NumPages:   target.NumPages,
    222 		DataOffset: target.DataOffset,
    223 	}
    224 	if target.ExecutorUsesForkServer {
    225 		data.ForkServer = 1
    226 	}
    227 	if target.ExecutorUsesShmem {
    228 		data.Shmem = 1
    229 	}
    230 	for _, c := range syscalls {
    231 		data.Calls = append(data.Calls, SyscallData{
    232 			Name:     c.Name,
    233 			CallName: c.CallName,
    234 			NR:       int32(c.NR),
    235 			NeedCall: !target.SyscallNumbers || strings.HasPrefix(c.CallName, "syz_"),
    236 		})
    237 	}
    238 	sort.Slice(data.Calls, func(i, j int) bool {
    239 		return data.Calls[i].Name < data.Calls[j].Name
    240 	})
    241 	return data
    242 }
    243 
    244 func writeExecutorSyscalls(oses []OSData) {
    245 	sort.Slice(oses, func(i, j int) bool {
    246 		return oses[i].GOOS < oses[j].GOOS
    247 	})
    248 	buf := new(bytes.Buffer)
    249 	if err := defsTempl.Execute(buf, oses); err != nil {
    250 		failf("failed to execute defs template: %v", err)
    251 	}
    252 	writeFile(filepath.FromSlash("executor/defs.h"), buf.Bytes())
    253 	buf.Reset()
    254 	if err := syscallsTempl.Execute(buf, oses); err != nil {
    255 		failf("failed to execute syscalls template: %v", err)
    256 	}
    257 	writeFile(filepath.FromSlash("executor/syscalls.h"), buf.Bytes())
    258 }
    259 
    260 func writeSource(file string, data []byte) {
    261 	src, err := format.Source(data)
    262 	if err != nil {
    263 		fmt.Printf("%s\n", data)
    264 		failf("failed to format output: %v", err)
    265 	}
    266 	if oldSrc, err := ioutil.ReadFile(file); err == nil && bytes.Equal(src, oldSrc) {
    267 		return
    268 	}
    269 	writeFile(file, src)
    270 }
    271 
    272 func writeFile(file string, data []byte) {
    273 	outf, err := os.Create(file)
    274 	if err != nil {
    275 		failf("failed to create output file: %v", err)
    276 	}
    277 	defer outf.Close()
    278 	outf.Write(data)
    279 }
    280 
    281 func failf(msg string, args ...interface{}) {
    282 	fmt.Fprintf(os.Stderr, msg+"\n", args...)
    283 	os.Exit(1)
    284 }
    285 
    286 var defsTempl = template.Must(template.New("").Parse(`// AUTOGENERATED FILE
    287 {{range $os := $}}
    288 #if GOOS_{{$os.GOOS}}
    289 #define GOOS "{{$os.GOOS}}"
    290 {{range $arch := $os.Archs}}
    291 #if GOARCH_{{$arch.GOARCH}}
    292 #define GOARCH "{{.GOARCH}}"
    293 #define SYZ_REVISION "{{.Revision}}"
    294 #define SYZ_EXECUTOR_USES_FORK_SERVER {{.ForkServer}}
    295 #define SYZ_EXECUTOR_USES_SHMEM {{.Shmem}}
    296 #define SYZ_PAGE_SIZE {{.PageSize}}
    297 #define SYZ_NUM_PAGES {{.NumPages}}
    298 #define SYZ_DATA_OFFSET {{.DataOffset}}
    299 #endif
    300 {{end}}
    301 #endif
    302 {{end}}
    303 `))
    304 
    305 var syscallsTempl = template.Must(template.New("").Parse(`// AUTOGENERATED FILE
    306 {{range $os := $}}
    307 #if GOOS_{{$os.GOOS}}
    308 {{range $arch := $os.Archs}}
    309 #if GOARCH_{{$arch.GOARCH}}
    310 const call_t syscalls[] = {
    311 {{range $c := $arch.Calls}}	{"{{$c.Name}}", {{$c.NR}}{{if $c.NeedCall}}, (syscall_t){{$c.CallName}}{{end}}},
    312 {{end}}
    313 };
    314 #endif
    315 {{end}}
    316 #endif
    317 {{end}}
    318 `))
    319