diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-06-17 13:28:22 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-06-17 14:41:15 +0200 |
| commit | 6828dc3a613eab724840ba670955f4eaca70ab12 (patch) | |
| tree | b58f760c7c6306e71b6881c75722a21715df6d73 /sys | |
| parent | 2007005ac39f66f3fb6314745ffb22b6d30314d3 (diff) | |
sys/syz-sysgen: from from sysgen
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/syz-sysgen/syscallnr.go | 103 | ||||
| -rw-r--r-- | sys/syz-sysgen/sysgen.go | 887 |
2 files changed, 990 insertions, 0 deletions
diff --git a/sys/syz-sysgen/syscallnr.go b/sys/syz-sysgen/syscallnr.go new file mode 100644 index 000000000..0b41a8634 --- /dev/null +++ b/sys/syz-sysgen/syscallnr.go @@ -0,0 +1,103 @@ +// Copyright 2015 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" + "sort" + "text/template" + + . "github.com/google/syzkaller/sys/sysparser" +) + +type Arch struct { + Name string + CARCH []string +} + +var archs = []*Arch{ + {"amd64", []string{"__x86_64__"}}, + {"arm64", []string{"__aarch64__"}}, + {"ppc64le", []string{"__ppc64__", "__PPC64__", "__powerpc64__"}}, +} + +var syzkalls = map[string]uint64{ + "syz_test": 1000001, + "syz_open_dev": 1000002, + "syz_open_pts": 1000003, + "syz_fuse_mount": 1000004, + "syz_fuseblk_mount": 1000005, + "syz_emit_ethernet": 1000006, + "syz_kvm_setup_cpu": 1000007, + "syz_extract_tcp_res": 1000008, +} + +func generateExecutorSyscalls(syscalls []Syscall, consts map[string]map[string]uint64) { + var data SyscallsData + for _, arch := range archs { + var calls []SyscallData + for _, c := range syscalls { + syscallNR := -1 + if nr, ok := consts[arch.Name]["__NR_"+c.CallName]; ok { + syscallNR = int(nr) + } + calls = append(calls, SyscallData{c.Name, syscallNR}) + } + data.Archs = append(data.Archs, ArchData{arch.CARCH, calls}) + } + for name, nr := range syzkalls { + data.FakeCalls = append(data.FakeCalls, SyscallData{name, int(nr)}) + } + sort.Sort(SyscallArray(data.FakeCalls)) + + hdrcode := "executor/syscalls.h" + logf(1, "Generate header with syscall numbers in %v", hdrcode) + buf := new(bytes.Buffer) + if err := syscallsTempl.Execute(buf, data); err != nil { + failf("failed to execute syscalls template: %v", err) + } + writeFile(hdrcode, buf.Bytes()) +} + +type SyscallsData struct { + Archs []ArchData + FakeCalls []SyscallData +} + +type ArchData struct { + CARCH []string + Calls []SyscallData +} + +type SyscallData struct { + Name string + NR int +} + +type SyscallArray []SyscallData + +func (a SyscallArray) Len() int { return len(a) } +func (a SyscallArray) Less(i, j int) bool { return a[i].Name < a[j].Name } +func (a SyscallArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +var syscallsTempl = template.Must(template.New("").Parse( + `// AUTOGENERATED FILE + +{{range $c := $.FakeCalls}}#define __NR_{{$c.Name}} {{$c.NR}} +{{end}} + +struct call_t { + const char* name; + int sys_nr; +}; + +{{range $arch := $.Archs}} +#if {{range $cdef := $arch.CARCH}}defined({{$cdef}}) || {{end}}0 +static call_t syscalls[] = { +{{range $c := $arch.Calls}} {"{{$c.Name}}", {{$c.NR}}}, +{{end}} +}; +#endif +{{end}} +`)) diff --git a/sys/syz-sysgen/sysgen.go b/sys/syz-sysgen/sysgen.go new file mode 100644 index 000000000..c1a6caf27 --- /dev/null +++ b/sys/syz-sysgen/sysgen.go @@ -0,0 +1,887 @@ +// 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 ( + "bufio" + "bytes" + "flag" + "fmt" + "go/format" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "runtime" + "runtime/pprof" + "sort" + "strconv" + "strings" + + . "github.com/google/syzkaller/sys/sysparser" +) + +var ( + flagV = flag.Int("v", 0, "verbosity") + flagMemProfile = flag.String("memprofile", "", "write a memory profile to the file") + + intRegExp = regexp.MustCompile("^int([0-9]+|ptr)(be)?(:[0-9]+)?$") +) + +const ( + ptrSize = 8 +) + +func main() { + flag.Parse() + + inputFiles, err := filepath.Glob("sys/*\\.txt") + if err != nil { + failf("failed to find input files: %v", err) + } + var r io.Reader = bytes.NewReader(nil) + for _, f := range inputFiles { + inf, err := os.Open(f) + logf(1, "Load descriptions from file %v", f) + if err != nil { + failf("failed to open input file: %v", err) + } + defer inf.Close() + r = io.MultiReader(r, bufio.NewReader(inf)) + } + + logf(1, "Parse system call descriptions") + desc := Parse(r) + + consts := make(map[string]map[string]uint64) + for _, arch := range archs { + logf(0, "generating %v...", arch.Name) + consts[arch.Name] = readConsts(arch.Name) + + unsupported := make(map[string]bool) + archFlags := make(map[string][]string) + for f, vals := range desc.Flags { + var archVals []string + for _, val := range vals { + if isIdentifier(val) { + if v, ok := consts[arch.Name][val]; ok { + archVals = append(archVals, fmt.Sprint(v)) + } else { + if !unsupported[val] { + unsupported[val] = true + logf(0, "unsupported flag: %v", val) + } + } + } else { + archVals = append(archVals, val) + } + } + archFlags[f] = archVals + } + + sysFile := filepath.Join("sys", "sys_"+arch.Name+".go") + logf(1, "Generate code to init system call data in %v", sysFile) + out := new(bytes.Buffer) + archDesc := *desc + archDesc.Flags = archFlags + generate(arch.Name, &archDesc, consts[arch.Name], out) + writeSource(sysFile, out.Bytes()) + logf(0, "") + } + + generateExecutorSyscalls(desc.Syscalls, consts) + + 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 readConsts(arch string) map[string]uint64 { + constFiles, err := filepath.Glob("sys/*_" + arch + ".const") + if err != nil { + failf("failed to find const files: %v", err) + } + consts := make(map[string]uint64) + for _, fname := range constFiles { + f, err := os.Open(fname) + if err != nil { + failf("failed to open const file: %v", err) + } + defer f.Close() + s := bufio.NewScanner(f) + for s.Scan() { + line := s.Text() + if line == "" || line[0] == '#' { + continue + } + eq := strings.IndexByte(line, '=') + if eq == -1 { + failf("malformed const file %v: no '=' in '%v'", fname, line) + } + name := strings.TrimSpace(line[:eq]) + val, err := strconv.ParseUint(strings.TrimSpace(line[eq+1:]), 0, 64) + if err != nil { + failf("malformed const file %v: bad value in '%v'", fname, line) + } + if old, ok := consts[name]; ok && old != val { + failf("const %v has different values for %v: %v vs %v", name, arch, old, val) + } + consts[name] = val + } + if err := s.Err(); err != nil { + failf("failed to read const file: %v", err) + } + } + for name, nr := range syzkalls { + consts["__NR_"+name] = nr + } + return consts +} + +var skipCurrentSyscall string + +func skipSyscall(why string) { + if skipCurrentSyscall != "" { + skipCurrentSyscall = why + } +} + +func generate(arch string, desc *Description, consts map[string]uint64, out io.Writer) { + unsupported := make(map[string]bool) + + fmt.Fprintf(out, "// AUTOGENERATED FILE\n") + fmt.Fprintf(out, "package sys\n\n") + + generateResources(desc, consts, out) + generateStructs(desc, consts, out) + + fmt.Fprintf(out, "var Calls = []*Call{\n") + for _, s := range desc.Syscalls { + logf(4, " generate population code for %v", s.Name) + skipCurrentSyscall = "" + syscallNR := -1 + if nr, ok := consts["__NR_"+s.CallName]; ok { + syscallNR = int(nr) + } else { + if !unsupported[s.CallName] { + unsupported[s.CallName] = true + logf(0, "unsupported syscall: %v", s.CallName) + } + } + native := true + if _, ok := syzkalls[s.CallName]; ok { + native = false + } + fmt.Fprintf(out, "&Call{Name: \"%v\", CallName: \"%v\", Native: %v", s.Name, s.CallName, native) + if len(s.Ret) != 0 { + fmt.Fprintf(out, ", Ret: ") + generateArg("", "ret", s.Ret[0], "out", s.Ret[1:], desc, consts, true, false, out) + } + fmt.Fprintf(out, ", Args: []Type{") + for i, a := range s.Args { + if i != 0 { + fmt.Fprintf(out, ", ") + } + logf(5, " generate description for arg %v", i) + generateArg("", a[0], a[1], "in", a[2:], desc, consts, true, false, out) + } + if skipCurrentSyscall != "" { + logf(0, "unsupported syscall: %v due to %v", s.Name, skipCurrentSyscall) + syscallNR = -1 + } + fmt.Fprintf(out, "}, NR: %v},\n", syscallNR) + } + fmt.Fprintf(out, "}\n\n") + + var constArr []NameValue + for name, val := range consts { + constArr = append(constArr, NameValue{name, val}) + } + sort.Sort(NameValueArray(constArr)) + + fmt.Fprintf(out, "const (\n") + for _, nv := range constArr { + fmt.Fprintf(out, "%v = %v\n", nv.name, nv.val) + } + fmt.Fprintf(out, ")\n") +} + +func generateResources(desc *Description, consts map[string]uint64, out io.Writer) { + var resArray ResourceArray + for _, res := range desc.Resources { + resArray = append(resArray, res) + } + sort.Sort(resArray) + + fmt.Fprintf(out, "var resourceArray = []*ResourceDesc{\n") + for _, res := range resArray { + underlying := "" + name := res.Name + kind := []string{name} + var values []string + loop: + for { + var values1 []string + for _, v := range res.Values { + if v1, ok := consts[v]; ok { + values1 = append(values1, fmt.Sprint(v1)) + } else if !isIdentifier(v) { + values1 = append(values1, v) + } + } + values = append(values1, values...) + switch res.Base { + case "int8", "int16", "int32", "int64", "intptr": + underlying = res.Base + break loop + default: + if _, ok := desc.Resources[res.Base]; !ok { + failf("resource '%v' has unknown parent resource '%v'", name, res.Base) + } + kind = append([]string{res.Base}, kind...) + res = desc.Resources[res.Base] + } + } + fmt.Fprintf(out, "&ResourceDesc{Name: \"%v\", Type: ", name) + generateArg("", "resource-type", underlying, "inout", nil, desc, consts, true, true, out) + fmt.Fprintf(out, ", Kind: []string{") + for i, k := range kind { + if i != 0 { + fmt.Fprintf(out, ", ") + } + fmt.Fprintf(out, "\"%v\"", k) + } + fmt.Fprintf(out, "}, Values: []uintptr{") + if len(values) == 0 { + values = append(values, "0") + } + for i, v := range values { + if i != 0 { + fmt.Fprintf(out, ", ") + } + fmt.Fprintf(out, "%v", v) + } + fmt.Fprintf(out, "}},\n") + } + fmt.Fprintf(out, "}\n") +} + +type structKey struct { + name string + field string + dir string +} + +func generateStructEntry(str *Struct, out io.Writer) { + typ := "StructType" + if str.IsUnion { + typ = "UnionType" + } + packed := "" + if str.Packed { + packed = ", packed: true" + } + varlen := "" + if str.Varlen { + varlen = ", varlen: true" + } + align := "" + if str.Align != 0 { + align = fmt.Sprintf(", align: %v", str.Align) + } + fmt.Fprintf(out, "&%v{TypeCommon: TypeCommon{TypeName: \"%v\", IsOptional: %v} %v %v %v},\n", + typ, str.Name, false, packed, align, varlen) +} + +func generateStructFields(str *Struct, key structKey, desc *Description, consts map[string]uint64, out io.Writer) { + fmt.Fprintf(out, "{structKey{\"%v\", \"%v\", %v}, []Type{\n", key.name, key.field, fmtDir(key.dir)) + for _, a := range str.Flds { + generateArg(str.Name, a[0], a[1], key.dir, a[2:], desc, consts, false, true, out) + fmt.Fprintf(out, ",\n") + } + fmt.Fprintf(out, "}},\n") + +} + +func generateStructs(desc *Description, consts map[string]uint64, out io.Writer) { + // Struct fields can refer to other structs. Go compiler won't like if + // we refer to Structs during Structs initialization. So we do + // it in 2 passes: on the first pass create struct types without fields, + // on the second pass we fill in fields. + + // Since structs of the same type can be fields with different names + // of multiple other structs, we have an instance of those structs + // for each field indexed by the name of the parent struct, field name and dir. + + structMap := make(map[structKey]*Struct) + for _, str := range desc.Structs { + for _, dir := range []string{"in", "out", "inout"} { + structMap[structKey{str.Name, "", dir}] = str + } + for _, a := range str.Flds { + if innerStr, ok := desc.Structs[a[1]]; ok { + for _, dir := range []string{"in", "out", "inout"} { + structMap[structKey{a[1], a[0], dir}] = innerStr + } + } + } + } + + fmt.Fprintf(out, "var structArray = []Type{\n") + sortedStructs := make([]*Struct, 0, len(desc.Structs)) + for _, str := range desc.Structs { + sortedStructs = append(sortedStructs, str) + } + sort.Sort(structSorter(sortedStructs)) + for _, str := range sortedStructs { + generateStructEntry(str, out) + } + fmt.Fprintf(out, "}\n") + + fmt.Fprintf(out, "var structFields = []struct{key structKey; fields []Type}{") + sortedKeys := make([]structKey, 0, len(structMap)) + for key := range structMap { + sortedKeys = append(sortedKeys, key) + } + sort.Sort(structKeySorter(sortedKeys)) + for _, key := range sortedKeys { + generateStructFields(structMap[key], key, desc, consts, out) + } + fmt.Fprintf(out, "}\n") +} + +func parseRange(buffer string, consts map[string]uint64) (string, string) { + lookupConst := func(name string) string { + if v, ok := consts[name]; ok { + return fmt.Sprint(v) + } + return name + } + + parts := strings.Split(buffer, ":") + switch len(parts) { + case 1: + v := lookupConst(buffer) + return v, v + case 2: + return lookupConst(parts[0]), lookupConst(parts[1]) + default: + failf("bad range: %v", buffer) + return "", "" + } +} + +func generateArg( + parent, name, typ, dir string, + a []string, + desc *Description, + consts map[string]uint64, + isArg, isField bool, + out io.Writer) { + origName := name + name = "\"" + name + "\"" + opt := false + for i, v := range a { + if v == "opt" { + opt = true + copy(a[i:], a[i+1:]) + a = a[:len(a)-1] + break + } + } + fmtDir(dir) // Make sure that dir is "in", "out" or "inout" + common := func() string { + return fmt.Sprintf("TypeCommon: TypeCommon{TypeName: \"%v\", FldName: %v, ArgDir: %v, IsOptional: %v}", typ, name, fmtDir(dir), opt) + } + intCommon := func(typeSize uint64, bigEndian bool, bitfieldLen uint64) string { + // BitfieldOff and BitfieldLst will be filled in in initAlign(). + return fmt.Sprintf("IntTypeCommon: IntTypeCommon{%v, TypeSize: %v, BigEndian: %v, BitfieldLen: %v}", common(), typeSize, bigEndian, bitfieldLen) + } + canBeArg := false + switch typ { + case "fileoff": + canBeArg = true + size := uint64(ptrSize) + bigEndian := false + bitfieldLen := uint64(0) + if isField { + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + size, bigEndian, bitfieldLen = decodeIntType(a[0]) + } else { + if want := 0; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + } + fmt.Fprintf(out, "&IntType{%v, Kind: IntFileoff}", intCommon(size, bigEndian, bitfieldLen)) + case "buffer": + canBeArg = true + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + ptrCommonHdr := common() + dir = a[0] + opt = false + fmt.Fprintf(out, "&PtrType{%v, Type: &BufferType{%v, Kind: BufferBlobRand}}", ptrCommonHdr, common()) + case "string": + if len(a) != 0 && len(a) != 1 && len(a) != 2 { + failf("wrong number of arguments for %v arg %v, want 0-2, got %v", typ, name, len(a)) + } + var vals []string + subkind := "" + if len(a) >= 1 { + if a[0][0] == '"' { + vals = append(vals, a[0][1:len(a[0])-1]) + } else { + vals1, ok := desc.StrFlags[a[0]] + if !ok { + failf("unknown string flags %v", a[0]) + } + vals = append([]string{}, vals1...) + subkind = a[0] + } + } + for i, s := range vals { + vals[i] = s + "\x00" + } + var size uint64 + if len(a) >= 2 { + if v, ok := consts[a[1]]; ok { + size = v + } else { + v, err := strconv.ParseUint(a[1], 10, 64) + if err != nil { + failf("failed to parse string length for %v", name, a[1]) + } + size = v + } + for i, s := range vals { + if uint64(len(s)) > size { + failf("string value %q exceeds buffer length %v for arg %v", s, size, name) + } + for uint64(len(s)) < size { + s += "\x00" + } + vals[i] = s + } + } else { + for _, s := range vals { + if size != 0 && size != uint64(len(s)) { + size = 0 + break + } + size = uint64(len(s)) + } + } + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferString, SubKind: %q, Values: %#v, Length: %v}", common(), subkind, vals, size) + case "salg_type": + if want := 0; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferAlgType}", common()) + case "salg_name": + if want := 0; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferAlgName}", common()) + case "vma": + canBeArg = true + begin, end := "0", "0" + switch len(a) { + case 0: + case 1: + begin, end = parseRange(a[0], consts) + default: + failf("wrong number of arguments for %v arg %v, want 0 or 1, got %v", typ, name, len(a)) + } + fmt.Fprintf(out, "&VmaType{%v, RangeBegin: %v, RangeEnd: %v}", common(), begin, end) + case "len", "bytesize", "bytesize2", "bytesize4", "bytesize8": + canBeArg = true + size := uint64(ptrSize) + bigEndian := false + bitfieldLen := uint64(0) + if isField { + if want := 2; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + size, bigEndian, bitfieldLen = decodeIntType(a[1]) + } else { + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + } + byteSize := uint8(0) + if typ != "len" { + byteSize = decodeByteSizeType(typ) + } + fmt.Fprintf(out, "&LenType{%v, Buf: \"%v\", ByteSize: %v}", intCommon(size, bigEndian, bitfieldLen), a[0], byteSize) + case "csum": + if len(a) != 3 && len(a) != 4 { + failf("wrong number of arguments for %v arg %v, want 3-4, got %v", typ, name, len(a)) + } + var size uint64 + var bigEndian bool + var bitfieldLen uint64 + var protocol uint64 + var kind string + switch a[1] { + case "inet": + kind = "CsumInet" + size, bigEndian, bitfieldLen = decodeIntType(a[2]) + case "pseudo": + kind = "CsumPseudo" + size, bigEndian, bitfieldLen = decodeIntType(a[3]) + if v, ok := consts[a[2]]; ok { + protocol = v + } else { + v, err := strconv.ParseUint(a[2], 10, 64) + if err != nil { + failf("failed to parse protocol %v", a[2]) + } + protocol = v + } + default: + failf("unknown checksum kind '%v'", a[0]) + } + fmt.Fprintf(out, "&CsumType{%v, Buf: \"%s\", Kind: %v, Protocol: %v}", intCommon(size, bigEndian, bitfieldLen), a[0], kind, protocol) + case "flags": + canBeArg = true + size := uint64(ptrSize) + bigEndian := false + bitfieldLen := uint64(0) + if isField { + if want := 2; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + size, bigEndian, bitfieldLen = decodeIntType(a[1]) + } else { + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + } + vals, ok := desc.Flags[a[0]] + if !ok { + failf("unknown flag %v", a[0]) + } + if len(vals) == 0 { + fmt.Fprintf(out, "&IntType{%v}", intCommon(size, bigEndian, bitfieldLen)) + } else { + fmt.Fprintf(out, "&FlagsType{%v, Vals: []uintptr{%v}}", intCommon(size, bigEndian, bitfieldLen), strings.Join(vals, ",")) + } + case "const": + canBeArg = true + size := uint64(ptrSize) + bigEndian := false + bitfieldLen := uint64(0) + if isField { + if want := 2; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + size, bigEndian, bitfieldLen = decodeIntType(a[1]) + } else { + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + } + val := a[0] + if v, ok := consts[a[0]]; ok { + val = fmt.Sprint(v) + } else if isIdentifier(a[0]) { + // This is an identifier for which we don't have a value for this arch. + // Skip this syscall on this arch. + val = "0" + skipSyscall(fmt.Sprintf("missing const %v", a[0])) + } + fmt.Fprintf(out, "&ConstType{%v, Val: uintptr(%v)}", intCommon(size, bigEndian, bitfieldLen), val) + case "proc": + canBeArg = true + size := uint64(ptrSize) + bigEndian := false + bitfieldLen := uint64(0) + var valuesStart string + var valuesPerProc string + if isField { + if want := 3; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + size, bigEndian, bitfieldLen = decodeIntType(a[0]) + valuesStart = a[1] + valuesPerProc = a[2] + } else { + if want := 2; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + valuesStart = a[0] + valuesPerProc = a[1] + } + valuesStartInt, err := strconv.ParseInt(valuesStart, 10, 64) + if err != nil { + failf("couldn't parse '%v' as int64", valuesStart) + } + valuesPerProcInt, err := strconv.ParseInt(valuesPerProc, 10, 64) + if err != nil { + failf("couldn't parse '%v' as int64", valuesPerProc) + } + if valuesPerProcInt < 1 { + failf("values per proc '%v' should be >= 1", valuesPerProcInt) + } + if size != 8 && valuesStartInt >= (1<<(size*8)) { + failf("values starting from '%v' overflow desired type of size '%v'", valuesStartInt, size) + } + const maxPids = 32 // executor knows about this constant (MAX_PIDS) + if size != 8 && valuesStartInt+maxPids*valuesPerProcInt >= (1<<(size*8)) { + failf("not enough values starting from '%v' with step '%v' and type size '%v' for 32 procs", valuesStartInt, valuesPerProcInt, size) + } + fmt.Fprintf(out, "&ProcType{%v, ValuesStart: %v, ValuesPerProc: %v}", intCommon(size, bigEndian, bitfieldLen), valuesStartInt, valuesPerProcInt) + case "signalno": + canBeArg = true + if want := 0; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + fmt.Fprintf(out, "&IntType{%v, Kind: IntSignalno}", intCommon(4, false, 0)) + case "filename": + if want := 0; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferFilename}", common()) + case "text": + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + kind := "" + switch a[0] { + case "x86_real", "x86_16", "x86_32", "x86_64", "arm64": + kind = "Text_" + a[0] + default: + failf("unknown text type %v for %v arg %v", a[0], typ, name) + } + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferText, Text: %v}", common(), kind) + case "array": + if len(a) != 1 && len(a) != 2 { + failf("wrong number of arguments for %v arg %v, want 1 or 2, got %v", typ, name, len(a)) + } + if len(a) == 1 { + if a[0] == "int8" { + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferBlobRand}", common()) + } else { + fmt.Fprintf(out, "&ArrayType{%v, Type: %v, Kind: ArrayRandLen}", common(), generateType(a[0], dir, desc, consts)) + } + } else { + begin, end := parseRange(a[1], consts) + if a[0] == "int8" { + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferBlobRange, RangeBegin: %v, RangeEnd: %v}", common(), begin, end) + } else { + fmt.Fprintf(out, "&ArrayType{%v, Type: %v, Kind: ArrayRangeLen, RangeBegin: %v, RangeEnd: %v}", common(), generateType(a[0], dir, desc, consts), begin, end) + } + } + case "ptr": + canBeArg = true + if want := 2; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + dir = "in" + fmt.Fprintf(out, "&PtrType{%v, Type: %v}", common(), generateType(a[1], a[0], desc, consts)) + default: + if intRegExp.MatchString(typ) { + canBeArg = true + size, bigEndian, bitfieldLen := decodeIntType(typ) + switch len(a) { + case 0: + fmt.Fprintf(out, "&IntType{%v}", intCommon(size, bigEndian, bitfieldLen)) + case 1: + begin, end := parseRange(a[0], consts) + fmt.Fprintf(out, "&IntType{%v, Kind: IntRange, RangeBegin: %v, RangeEnd: %v}", + intCommon(size, bigEndian, bitfieldLen), begin, end) + default: + failf("wrong number of arguments for %v arg %v, want 0 or 1, got %v", typ, name, len(a)) + } + } else if strings.HasPrefix(typ, "unnamed") { + if inner, ok := desc.Unnamed[typ]; ok { + generateArg("", "", inner[0], dir, inner[1:], desc, consts, false, isField, out) + } else { + failf("unknown unnamed type '%v'", typ) + } + } else if _, ok := desc.Structs[typ]; ok { + if len(a) != 0 { + failf("struct '%v' has args", typ) + } + fmt.Fprintf(out, "getStruct(structKey{\"%v\", \"%v\", %v})", typ, origName, fmtDir(dir)) + } else if _, ok := desc.Resources[typ]; ok { + if len(a) != 0 { + failf("resource '%v' has args", typ) + } + fmt.Fprintf(out, "&ResourceType{%v, Desc: resource(\"%v\")}", common(), typ) + return + } else { + failf("unknown arg type \"%v\" for %v", typ, name) + } + } + if isArg && !canBeArg { + failf("%v %v can't be syscall argument/return", name, typ) + } +} + +func generateType(typ, dir string, desc *Description, consts map[string]uint64) string { + buf := new(bytes.Buffer) + generateArg("", "", typ, dir, nil, desc, consts, false, true, buf) + return buf.String() +} + +func fmtDir(s string) string { + switch s { + case "in": + return "DirIn" + case "out": + return "DirOut" + case "inout": + return "DirInOut" + default: + failf("bad direction %v", s) + return "" + } +} + +func decodeIntType(typ string) (uint64, bool, uint64) { + bigEndian := false + bitfieldLen := uint64(0) + + parts := strings.Split(typ, ":") + if len(parts) == 2 { + var err error + bitfieldLen, err = strconv.ParseUint(parts[1], 10, 64) + if err != nil { + failf("failed to parse bitfield length '%v'", parts[1]) + } + typ = parts[0] + } + + if strings.HasSuffix(typ, "be") { + bigEndian = true + typ = typ[:len(typ)-2] + } + + switch typ { + case "int8", "int16", "int32", "int64", "intptr": + default: + failf("unknown type %v", typ) + } + sz := int64(ptrSize * 8) + if typ != "intptr" { + sz, _ = strconv.ParseInt(typ[3:], 10, 64) + } + + if bitfieldLen > uint64(sz) { + failf("bitfield of size %v is too large for base type of size %v", bitfieldLen, sz/8) + } + + return uint64(sz / 8), bigEndian, bitfieldLen +} + +func decodeByteSizeType(typ string) uint8 { + switch typ { + case "bytesize", "bytesize2", "bytesize4", "bytesize8": + default: + failf("unknown type %v", typ) + } + sz := int64(1) + if typ != "bytesize" { + sz, _ = strconv.ParseInt(typ[8:], 10, 8) + } + return uint8(sz) +} + +func isIdentifier(s string) bool { + for i, c := range s { + if c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || i > 0 && (c >= '0' && c <= '9') { + continue + } + return false + } + return true +} + +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) +} + +type NameValue struct { + name string + val uint64 +} + +type NameValueArray []NameValue + +func (a NameValueArray) Len() int { return len(a) } +func (a NameValueArray) Less(i, j int) bool { return a[i].name < a[j].name } +func (a NameValueArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +type ResourceArray []Resource + +func (a ResourceArray) Len() int { return len(a) } +func (a ResourceArray) Less(i, j int) bool { return a[i].Name < a[j].Name } +func (a ResourceArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +type structSorter []*Struct + +func (a structSorter) Len() int { return len(a) } +func (a structSorter) Less(i, j int) bool { return a[i].Name < a[j].Name } +func (a structSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +type structKeySorter []structKey + +func (a structKeySorter) Len() int { return len(a) } +func (a structKeySorter) Less(i, j int) bool { + if a[i].name < a[j].name { + return true + } + if a[i].name > a[j].name { + return false + } + if a[i].field < a[j].field { + return true + } + if a[i].field > a[j].field { + return false + } + return a[i].dir < a[j].dir +} +func (a structKeySorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +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...) + } +} |
