1 // Copyright 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 "io/ioutil" 11 "os" 12 "path/filepath" 13 "runtime" 14 "sort" 15 "strings" 16 17 "github.com/google/syzkaller/pkg/ast" 18 "github.com/google/syzkaller/pkg/compiler" 19 "github.com/google/syzkaller/pkg/osutil" 20 "github.com/google/syzkaller/sys/targets" 21 ) 22 23 var ( 24 flagOS = flag.String("os", "", "target OS") 25 flagBuild = flag.Bool("build", false, "regenerate arch-specific kernel headers") 26 flagSourceDir = flag.String("sourcedir", "", "path to kernel source checkout dir") 27 flagBuildDir = flag.String("builddir", "", "path to kernel build dir") 28 flagArch = flag.String("arch", "", "comma-separated list of arches to generate (all by default)") 29 ) 30 31 type Arch struct { 32 target *targets.Target 33 sourceDir string 34 buildDir string 35 build bool 36 files []*File 37 err error 38 done chan bool 39 } 40 41 type File struct { 42 arch *Arch 43 name string 44 consts map[string]uint64 45 undeclared map[string]bool 46 info *compiler.ConstInfo 47 err error 48 done chan bool 49 } 50 51 type Extractor interface { 52 prepare(sourcedir string, build bool, arches []string) error 53 prepareArch(arch *Arch) error 54 processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint64, map[string]bool, error) 55 } 56 57 var extractors = map[string]Extractor{ 58 "akaros": new(akaros), 59 "linux": new(linux), 60 "freebsd": new(freebsd), 61 "netbsd": new(netbsd), 62 "android": new(linux), 63 "fuchsia": new(fuchsia), 64 "windows": new(windows), 65 } 66 67 func main() { 68 failf := func(msg string, args ...interface{}) { 69 fmt.Fprintf(os.Stderr, msg+"\n", args...) 70 os.Exit(1) 71 } 72 flag.Parse() 73 if *flagBuild && *flagBuildDir != "" { 74 failf("-build and -builddir is an invalid combination") 75 } 76 77 OS, archArray, files, err := archFileList(*flagOS, *flagArch, flag.Args()) 78 if err != nil { 79 failf("%v", err) 80 } 81 82 extractor := extractors[OS] 83 if extractor == nil { 84 failf("unknown os: %v", OS) 85 } 86 if err := extractor.prepare(*flagSourceDir, *flagBuild, archArray); err != nil { 87 failf("%v", err) 88 } 89 90 arches, err := createArches(OS, archArray, files) 91 if err != nil { 92 failf("%v", err) 93 } 94 jobC := make(chan interface{}, len(archArray)*len(files)) 95 for _, arch := range arches { 96 jobC <- arch 97 } 98 99 for p := 0; p < runtime.GOMAXPROCS(0); p++ { 100 go func() { 101 for job := range jobC { 102 switch j := job.(type) { 103 case *Arch: 104 infos, err := processArch(extractor, j) 105 j.err = err 106 close(j.done) 107 if j.err == nil { 108 for _, f := range j.files { 109 f.info = infos[f.name] 110 jobC <- f 111 } 112 } 113 case *File: 114 j.consts, j.undeclared, j.err = processFile(extractor, j.arch, j) 115 close(j.done) 116 } 117 } 118 }() 119 } 120 121 failed := false 122 for _, arch := range arches { 123 fmt.Printf("generating %v/%v...\n", arch.target.OS, arch.target.Arch) 124 <-arch.done 125 if arch.err != nil { 126 failed = true 127 fmt.Printf(" %v\n", arch.err) 128 continue 129 } 130 for _, f := range arch.files { 131 fmt.Printf("extracting from %v\n", f.name) 132 <-f.done 133 if f.err != nil { 134 failed = true 135 fmt.Printf(" %v\n", f.err) 136 continue 137 } 138 } 139 fmt.Printf("\n") 140 } 141 142 if !failed { 143 failed = checkUnsupportedCalls(arches) 144 } 145 for _, arch := range arches { 146 if arch.build { 147 os.RemoveAll(arch.buildDir) 148 } 149 } 150 if failed { 151 os.Exit(1) 152 } 153 } 154 155 func createArches(OS string, archArray, files []string) ([]*Arch, error) { 156 var arches []*Arch 157 for _, archStr := range archArray { 158 buildDir := "" 159 if *flagBuild { 160 dir, err := ioutil.TempDir("", "syzkaller-kernel-build") 161 if err != nil { 162 return nil, fmt.Errorf("failed to create temp dir: %v", err) 163 } 164 buildDir = dir 165 } else if *flagBuildDir != "" { 166 buildDir = *flagBuildDir 167 } else { 168 buildDir = *flagSourceDir 169 } 170 171 target := targets.Get(OS, archStr) 172 if target == nil { 173 return nil, fmt.Errorf("unknown arch: %v", archStr) 174 } 175 176 arch := &Arch{ 177 target: target, 178 sourceDir: *flagSourceDir, 179 buildDir: buildDir, 180 build: *flagBuild, 181 done: make(chan bool), 182 } 183 for _, f := range files { 184 arch.files = append(arch.files, &File{ 185 arch: arch, 186 name: f, 187 done: make(chan bool), 188 }) 189 } 190 arches = append(arches, arch) 191 } 192 return arches, nil 193 } 194 195 func checkUnsupportedCalls(arches []*Arch) bool { 196 supported := make(map[string]bool) 197 unsupported := make(map[string]string) 198 for _, arch := range arches { 199 for _, f := range arch.files { 200 for name := range f.consts { 201 supported[name] = true 202 } 203 for name := range f.undeclared { 204 unsupported[name] = f.name 205 } 206 } 207 } 208 failed := false 209 for name, file := range unsupported { 210 if supported[name] { 211 continue 212 } 213 failed = true 214 fmt.Printf("%v: %v is unsupported on all arches (typo?)\n", 215 file, name) 216 } 217 return failed 218 } 219 220 func archFileList(os, arch string, files []string) (string, []string, []string, error) { 221 android := false 222 if os == "android" { 223 android = true 224 os = "linux" 225 } 226 var arches []string 227 if arch != "" { 228 arches = strings.Split(arch, ",") 229 } else { 230 for arch := range targets.List[os] { 231 arches = append(arches, arch) 232 } 233 if android { 234 arches = []string{"386", "amd64", "arm", "arm64"} 235 } 236 sort.Strings(arches) 237 } 238 if len(files) == 0 { 239 matches, err := filepath.Glob(filepath.Join("sys", os, "*.txt")) 240 if err != nil || len(matches) == 0 { 241 return "", nil, nil, fmt.Errorf("failed to find sys files: %v", err) 242 } 243 androidFiles := map[string]bool{ 244 "tlk_device.txt": true, 245 // This was generated on: 246 // https://source.codeaurora.org/quic/la/kernel/msm-4.9 msm-4.9 247 "video4linux.txt": true, 248 } 249 for _, f := range matches { 250 f = filepath.Base(f) 251 if os == "linux" && android != androidFiles[f] { 252 continue 253 } 254 files = append(files, f) 255 } 256 sort.Strings(files) 257 } 258 return os, arches, files, nil 259 } 260 261 func processArch(extractor Extractor, arch *Arch) (map[string]*compiler.ConstInfo, error) { 262 errBuf := new(bytes.Buffer) 263 eh := func(pos ast.Pos, msg string) { 264 fmt.Fprintf(errBuf, "%v: %v\n", pos, msg) 265 } 266 top := ast.ParseGlob(filepath.Join("sys", arch.target.OS, "*.txt"), eh) 267 if top == nil { 268 return nil, fmt.Errorf("%v", errBuf.String()) 269 } 270 infos := compiler.ExtractConsts(top, arch.target, eh) 271 if infos == nil { 272 return nil, fmt.Errorf("%v", errBuf.String()) 273 } 274 if err := extractor.prepareArch(arch); err != nil { 275 return nil, err 276 } 277 return infos, nil 278 } 279 280 func processFile(extractor Extractor, arch *Arch, file *File) (map[string]uint64, map[string]bool, error) { 281 inname := filepath.Join("sys", arch.target.OS, file.name) 282 outname := strings.TrimSuffix(inname, ".txt") + "_" + arch.target.Arch + ".const" 283 if file.info == nil { 284 return nil, nil, fmt.Errorf("input file %v is missing", inname) 285 } 286 if len(file.info.Consts) == 0 { 287 os.Remove(outname) 288 return nil, nil, nil 289 } 290 consts, undeclared, err := extractor.processFile(arch, file.info) 291 if err != nil { 292 return nil, nil, err 293 } 294 data := compiler.SerializeConsts(consts, undeclared) 295 if err := osutil.WriteFile(outname, data); err != nil { 296 return nil, nil, fmt.Errorf("failed to write output file: %v", err) 297 } 298 return consts, undeclared, nil 299 } 300