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 4package main 5 6import ( 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 31var ( 32 flagMemProfile = flag.String("memprofile", "", "write a memory profile to the file") 33) 34 35type SyscallData struct { 36 Name string 37 CallName string 38 NR int32 39 NeedCall bool 40} 41 42type 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 53type OSData struct { 54 GOOS string 55 Archs []ArchData 56} 57 58func 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 163func 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 208func writeEmpty(OS string) { 209 const data = `// AUTOGENERATED FILE 210// This file is needed if OS is completely excluded by build tags. 211package gen 212` 213 writeSource(filepath.Join("sys", OS, "gen", "empty.go"), []byte(data)) 214} 215 216func 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 244func 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 260func 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 272func 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 281func failf(msg string, args ...interface{}) { 282 fmt.Fprintf(os.Stderr, msg+"\n", args...) 283 os.Exit(1) 284} 285 286var 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 305var 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}} 310const 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