From a7206b24cac96c08aecf2f3b4cc3c2e555eec708 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Mon, 28 Aug 2017 15:59:22 +0200 Subject: pkg/compiler: check and generate types Move most of the logic from sysgen to pkg/compiler. Update #217 --- pkg/compiler/consts.go | 187 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 175 insertions(+), 12 deletions(-) (limited to 'pkg/compiler/consts.go') diff --git a/pkg/compiler/consts.go b/pkg/compiler/consts.go index dd56d99c7..970a79050 100644 --- a/pkg/compiler/consts.go +++ b/pkg/compiler/consts.go @@ -93,12 +93,186 @@ func ExtractConsts(desc *ast.Description, eh0 ast.ErrorHandler) *ConstInfo { return info } +// assignSyscallNumbers assigns syscall numbers, discards unsupported syscalls +// and removes no longer irrelevant nodes from the tree (comments, new lines, etc). +func (comp *compiler) assignSyscallNumbers(consts map[string]uint64) { + // Pseudo syscalls starting from syz_ are assigned numbers starting from syzbase. + // Note: the numbers must be stable (not depend on file reading order, etc), + // so we have to do it in 2 passes. + const syzbase = 1000000 + syzcalls := make(map[string]bool) + for _, decl := range comp.desc.Nodes { + c, ok := decl.(*ast.Call) + if !ok { + continue + } + if strings.HasPrefix(c.CallName, "syz_") { + syzcalls[c.CallName] = true + } + } + syznr := make(map[string]uint64) + for i, name := range toArray(syzcalls) { + syznr[name] = syzbase + uint64(i) + } + + var top []ast.Node + for _, decl := range comp.desc.Nodes { + switch decl.(type) { + case *ast.Call: + c := decl.(*ast.Call) + if strings.HasPrefix(c.CallName, "syz_") { + c.NR = syznr[c.CallName] + top = append(top, decl) + continue + } + // Lookup in consts. + str := "__NR_" + c.CallName + nr, ok := consts[str] + if ok { + c.NR = nr + top = append(top, decl) + continue + } + name := "syscall " + c.CallName + if !comp.unsupported[name] { + comp.unsupported[name] = true + comp.warning(c.Pos, "unsupported syscall: %v due to missing const %v", + c.CallName, str) + } + case *ast.IntFlags, *ast.Resource, *ast.Struct, *ast.StrFlags: + top = append(top, decl) + case *ast.NewLine, *ast.Comment, *ast.Include, *ast.Incdir, *ast.Define: + // These are not needed anymore. + default: + panic(fmt.Sprintf("unknown node type: %#v", decl)) + } + } + comp.desc.Nodes = top +} + +// patchConsts replaces all symbolic consts with their numeric values taken from consts map. +// Updates desc and returns set of unsupported syscalls and flags. +// After this pass consts are not needed for compilation. +func (comp *compiler) patchConsts(consts map[string]uint64) { + var top []ast.Node + for _, decl := range comp.desc.Nodes { + switch decl.(type) { + case *ast.IntFlags: + // Unsupported flag values are dropped. + n := decl.(*ast.IntFlags) + var values []*ast.Int + for _, v := range n.Values { + if comp.patchIntConst(v.Pos, &v.Value, &v.Ident, consts, nil) { + values = append(values, v) + } + } + n.Values = values + top = append(top, n) + case *ast.StrFlags: + top = append(top, decl) + case *ast.Resource, *ast.Struct, *ast.Call: + // Walk whole tree and replace consts in Int's and Type's. + missing := "" + ast.WalkNode(decl, func(n0 ast.Node) { + switch n := n0.(type) { + case *ast.Int: + comp.patchIntConst(n.Pos, &n.Value, &n.Ident, consts, &missing) + case *ast.Type: + if c := typeConstIdentifier(n); c != nil { + comp.patchIntConst(c.Pos, &c.Value, &c.Ident, + consts, &missing) + if c.HasColon { + comp.patchIntConst(c.Pos2, &c.Value2, &c.Ident2, + consts, &missing) + } + } + } + }) + if missing == "" { + top = append(top, decl) + continue + } + // Produce a warning about unsupported syscall/resource/struct. + // TODO(dvyukov): we should transitively remove everything that + // depends on unsupported things. + // Potentially we still can get, say, a bad int range error + // due to the 0 const value. + pos, typ, name := decl.Info() + if id := typ + " " + name; !comp.unsupported[id] { + comp.unsupported[id] = true + comp.warning(pos, "unsupported %v: %v due to missing const %v", + typ, name, missing) + } + // We have to keep partially broken resources and structs, + // because otherwise their usages will error. + if _, ok := decl.(*ast.Call); !ok { + top = append(top, decl) + } + } + } + comp.desc.Nodes = top +} + +func (comp *compiler) patchIntConst(pos ast.Pos, val *uint64, id *string, + consts map[string]uint64, missing *string) bool { + if *id == "" { + return true + } + v, ok := consts[*id] + if !ok { + name := "const " + *id + if !comp.unsupported[name] { + comp.unsupported[name] = true + comp.warning(pos, "unsupported const: %v", *id) + } + if missing != nil && *missing == "" { + *missing = *id + } + } + *val = v + *id = "" + return ok +} + +// typeConstIdentifier returns type arg that is an integer constant (subject for const patching), if any. +func typeConstIdentifier(n *ast.Type) *ast.Type { + // TODO: see if we can extract this info from typeDesc/typeArg. + if n.Ident == "const" && len(n.Args) > 0 { + return n.Args[0] + } + if n.Ident == "array" && len(n.Args) > 1 && n.Args[1].Ident != "opt" { + return n.Args[1] + } + if n.Ident == "vma" && len(n.Args) > 0 && n.Args[0].Ident != "opt" { + return n.Args[0] + } + if n.Ident == "string" && len(n.Args) > 1 && n.Args[1].Ident != "opt" { + return n.Args[1] + } + if n.Ident == "csum" && len(n.Args) > 2 && n.Args[1].Ident == "pseudo" { + return n.Args[2] + } + switch n.Ident { + case "int8", "int16", "int16be", "int32", "int32be", "int64", "int64be", "intptr": + if len(n.Args) > 0 && n.Args[0].Ident != "opt" { + return n.Args[0] + } + } + return nil +} + func SerializeConsts(consts map[string]uint64) []byte { + type nameValuePair struct { + name string + val uint64 + } var nv []nameValuePair for k, v := range consts { nv = append(nv, nameValuePair{k, v}) } - sort.Sort(nameValueArray(nv)) + sort.Slice(nv, func(i, j int) bool { + return nv[i].name < nv[j].name + }) buf := new(bytes.Buffer) fmt.Fprintf(buf, "# AUTOGENERATED FILE\n") @@ -188,14 +362,3 @@ func DeserializeConstsGlob(glob string, eh ast.ErrorHandler) map[string]uint64 { } return consts } - -type nameValuePair struct { - name string - val uint64 -} - -type nameValueArray []nameValuePair - -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] } -- cgit mrf-deployment