aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/compiler/consts.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-08-28 15:59:22 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-09-02 13:06:53 +0200
commita7206b24cac96c08aecf2f3b4cc3c2e555eec708 (patch)
tree80c678141148ce2eafaab5617f168bd840b8c8a6 /pkg/compiler/consts.go
parentaa51461a34f998908d10f551615ad242bdff8fe9 (diff)
pkg/compiler: check and generate types
Move most of the logic from sysgen to pkg/compiler. Update #217
Diffstat (limited to 'pkg/compiler/consts.go')
-rw-r--r--pkg/compiler/consts.go187
1 files changed, 175 insertions, 12 deletions
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] }