mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-25 12:29:43 +00:00
805 lines
22 KiB
Go
805 lines
22 KiB
Go
// 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"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
. "github.com/google/syzkaller/sysparser"
|
|
)
|
|
|
|
var (
|
|
flagV = flag.Int("v", 0, "verbosity")
|
|
)
|
|
|
|
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)
|
|
}
|
|
|
|
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, "func initCalls() {\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)
|
|
}
|
|
}
|
|
fmt.Fprintf(out, "func() { Calls = append(Calls, &Call{Name: \"%v\", CallName: \"%v\"", s.Name, s.CallName)
|
|
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 Resources = map[string]*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, "\"%v\": &ResourceDesc{Name: \"%v\", Type: ", name, 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, key structKey, out io.Writer) {
|
|
typ := "StructType"
|
|
if str.IsUnion {
|
|
typ = "UnionType"
|
|
}
|
|
name := key.field
|
|
if name == "" {
|
|
name = key.name
|
|
}
|
|
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\": &%v{TypeCommon: TypeCommon{TypeName: \"%v\", ArgDir: %v, IsOptional: %v} %v %v %v},\n",
|
|
key, typ, name, fmtDir(key.dir), false, packed, align, varlen)
|
|
}
|
|
|
|
func generateStructFields(str Struct, key structKey, desc *Description, consts map[string]uint64, out io.Writer) {
|
|
typ := "StructType"
|
|
fields := "Fields"
|
|
if str.IsUnion {
|
|
typ = "UnionType"
|
|
fields = "Options"
|
|
}
|
|
fmt.Fprintf(out, "func() { s := Structs[\"%v\"].(*%v)\n", key, typ)
|
|
for _, a := range str.Flds {
|
|
fmt.Fprintf(out, "s.%v = append(s.%v, ", fields, fields)
|
|
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 map during Structs map initialization. So we do
|
|
// it in 2 passes: on the first pass create types and assign them to
|
|
// the map, on the second pass 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 and the
|
|
// field name.
|
|
|
|
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 Structs = map[string]Type{\n")
|
|
for key, str := range structMap {
|
|
generateStructEntry(str, key, out)
|
|
}
|
|
fmt.Fprintf(out, "}\n")
|
|
|
|
fmt.Fprintf(out, "func initStructFields() {\n")
|
|
for key, str := range structMap {
|
|
generateStructFields(str, 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
|
|
}
|
|
}
|
|
common := func() string {
|
|
return fmt.Sprintf("TypeCommon: TypeCommon{TypeName: %v, ArgDir: %v, IsOptional: %v}", 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"
|
|
}
|
|
if len(a) >= 2 {
|
|
var size uint64
|
|
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
|
|
}
|
|
}
|
|
fmt.Fprintf(out, "&BufferType{%v, Kind: BufferString, SubKind: %q, Values: %#v}", common(), subkind, vals)
|
|
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 "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 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 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":
|
|
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))
|
|
}
|
|
ptrCommonHdr := common()
|
|
dir = "in"
|
|
opt = false
|
|
fmt.Fprintf(out, "&PtrType{%v, Type: &BufferType{%v, Kind: BufferFilename}}", ptrCommonHdr, 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:
|
|
intRegExp := regexp.MustCompile("^int([0-9]+|ptr)(be)?(:[0-9]+)?$")
|
|
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, "Structs[\"%v\"]", structKey{typ, origName, dir})
|
|
} else if _, ok := desc.Resources[typ]; ok {
|
|
if len(a) != 0 {
|
|
failf("resource '%v' has args", typ)
|
|
}
|
|
fmt.Fprintf(out, "&ResourceType{%v, Desc: Resources[\"%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)
|
|
}
|
|
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] }
|
|
|
|
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...)
|
|
}
|
|
}
|