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