aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-08-18 16:38:41 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-08-18 17:04:12 +0200
commit19b893936bebc6189c7627d56d1dc454fbd42714 (patch)
tree2c58dca1528c2e7d05cbe6ca49fd34a3f802a681 /pkg
parent41bbf437e1e843fd3ab5603ec4c2eb4a42dca76f (diff)
sys/syz-extract: switch to the new parser
Diffstat (limited to 'pkg')
-rw-r--r--pkg/ast/ast.go9
-rw-r--r--pkg/ast/parser.go13
-rw-r--r--pkg/ast/scanner.go6
-rw-r--r--pkg/ast/walk.go94
-rw-r--r--pkg/compiler/compiler.go65
-rw-r--r--pkg/compiler/compiler_test.go59
6 files changed, 240 insertions, 6 deletions
diff --git a/pkg/ast/ast.go b/pkg/ast/ast.go
index 27497b2a9..e2ddc0224 100644
--- a/pkg/ast/ast.go
+++ b/pkg/ast/ast.go
@@ -47,10 +47,11 @@ type Resource struct {
}
type Call struct {
- Pos Pos
- Name *Ident
- Args []*Field
- Ret *Type
+ Pos Pos
+ Name *Ident
+ CallName string
+ Args []*Field
+ Ret *Type
}
type Struct struct {
diff --git a/pkg/ast/parser.go b/pkg/ast/parser.go
index 737d08068..10f389424 100644
--- a/pkg/ast/parser.go
+++ b/pkg/ast/parser.go
@@ -210,8 +210,9 @@ func (p *parser) parseResource() *Resource {
func (p *parser) parseCall(name *Ident) *Call {
c := &Call{
- Pos: name.Pos,
- Name: name,
+ Pos: name.Pos,
+ Name: name,
+ CallName: callName(name.Name),
}
p.consume(tokLParen)
for p.tok != tokRParen {
@@ -226,6 +227,14 @@ func (p *parser) parseCall(name *Ident) *Call {
return c
}
+func callName(s string) string {
+ pos := strings.IndexByte(s, '$')
+ if pos == -1 {
+ return s
+ }
+ return s[:pos]
+}
+
func (p *parser) parseFlags(name *Ident) interface{} {
p.consume(tokEq)
switch p.tok {
diff --git a/pkg/ast/scanner.go b/pkg/ast/scanner.go
index ee15cee03..d3e35895c 100644
--- a/pkg/ast/scanner.go
+++ b/pkg/ast/scanner.go
@@ -5,6 +5,7 @@ package ast
import (
"fmt"
+ "os"
"strconv"
)
@@ -101,6 +102,11 @@ type scanner struct {
}
func newScanner(data []byte, filename string, errorHandler func(pos Pos, msg string)) *scanner {
+ if errorHandler == nil {
+ errorHandler = func(pos Pos, msg string) {
+ fmt.Fprintf(os.Stderr, "%v:%v:%v: %v\n", pos.File, pos.Line, pos.Col, msg)
+ }
+ }
s := &scanner{
data: data,
filename: filename,
diff --git a/pkg/ast/walk.go b/pkg/ast/walk.go
new file mode 100644
index 000000000..f4daeaccd
--- /dev/null
+++ b/pkg/ast/walk.go
@@ -0,0 +1,94 @@
+// Copyright 2017 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 ast
+
+import (
+ "fmt"
+)
+
+// Walk calls callback cb for every node in AST.
+func Walk(top []interface{}, cb func(n interface{})) {
+ for _, decl := range top {
+ walkNode(decl, cb)
+ }
+}
+
+func walkNode(n0 interface{}, cb func(n interface{})) {
+ switch n := n0.(type) {
+ case *NewLine:
+ cb(n)
+ case *Comment:
+ cb(n)
+ case *Include:
+ cb(n)
+ walkNode(n.File, cb)
+ case *Incdir:
+ cb(n)
+ walkNode(n.Dir, cb)
+ case *Define:
+ cb(n)
+ walkNode(n.Name, cb)
+ walkNode(n.Value, cb)
+ case *Resource:
+ cb(n)
+ walkNode(n.Name, cb)
+ walkNode(n.Base, cb)
+ for _, v := range n.Values {
+ walkNode(v, cb)
+ }
+ case *Call:
+ cb(n)
+ walkNode(n.Name, cb)
+ for _, f := range n.Args {
+ walkNode(f, cb)
+ }
+ if n.Ret != nil {
+ walkNode(n.Ret, cb)
+ }
+ case *Struct:
+ cb(n)
+ walkNode(n.Name, cb)
+ for _, f := range n.Fields {
+ walkNode(f, cb)
+ }
+ for _, a := range n.Attrs {
+ walkNode(a, cb)
+ }
+ for _, c := range n.Comments {
+ walkNode(c, cb)
+ }
+ case *IntFlags:
+ cb(n)
+ walkNode(n.Name, cb)
+ for _, v := range n.Values {
+ walkNode(v, cb)
+ }
+ case *StrFlags:
+ cb(n)
+ walkNode(n.Name, cb)
+ for _, v := range n.Values {
+ walkNode(v, cb)
+ }
+ case *Ident:
+ cb(n)
+ case *String:
+ cb(n)
+ case *Int:
+ cb(n)
+ case *Type:
+ cb(n)
+ for _, t := range n.Args {
+ walkNode(t, cb)
+ }
+ case *Field:
+ cb(n)
+ walkNode(n.Name, cb)
+ walkNode(n.Type, cb)
+ for _, c := range n.Comments {
+ walkNode(c, cb)
+ }
+ default:
+ panic(fmt.Sprintf("unknown AST node: %#v", n))
+ }
+}
diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go
new file mode 100644
index 000000000..7892661fa
--- /dev/null
+++ b/pkg/compiler/compiler.go
@@ -0,0 +1,65 @@
+// Copyright 2017 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 compiler
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "github.com/google/syzkaller/pkg/ast"
+)
+
+// ExtractConsts returns list of literal constants and other info required const value extraction.
+func ExtractConsts(top []interface{}) (consts, includes, incdirs []string, defines map[string]string) {
+ constMap := make(map[string]bool)
+ defines = make(map[string]string)
+
+ ast.Walk(top, func(n1 interface{}) {
+ switch n := n1.(type) {
+ case *ast.Include:
+ includes = append(includes, n.File.Value)
+ case *ast.Incdir:
+ incdirs = append(incdirs, n.Dir.Value)
+ case *ast.Define:
+ v := fmt.Sprint(n.Value.Value)
+ switch {
+ case n.Value.CExpr != "":
+ v = n.Value.CExpr
+ case n.Value.Ident != "":
+ v = n.Value.Ident
+ }
+ defines[n.Name.Name] = v
+ constMap[n.Name.Name] = true
+ case *ast.Call:
+ if !strings.HasPrefix(n.CallName, "syz_") {
+ constMap["__NR_"+n.CallName] = true
+ }
+ case *ast.Type:
+ if n.Ident == "const" && len(n.Args) > 0 {
+ constMap[n.Args[0].Ident] = true
+ }
+ if n.Ident == "array" && len(n.Args) > 1 {
+ constMap[n.Args[1].Ident] = true
+ }
+ case *ast.Int:
+ constMap[n.Ident] = true
+ }
+ })
+
+ consts = toArray(constMap)
+ return
+}
+
+func toArray(m map[string]bool) []string {
+ delete(m, "")
+ var res []string
+ for v := range m {
+ if v != "" {
+ res = append(res, v)
+ }
+ }
+ sort.Strings(res)
+ return res
+}
diff --git a/pkg/compiler/compiler_test.go b/pkg/compiler/compiler_test.go
new file mode 100644
index 000000000..13ed97b4c
--- /dev/null
+++ b/pkg/compiler/compiler_test.go
@@ -0,0 +1,59 @@
+// Copyright 2017 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 compiler
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/google/syzkaller/pkg/ast"
+)
+
+func TestExtractConsts(t *testing.T) {
+ top, ok := ast.Parse([]byte(extractConstsInput), "test", nil)
+ if !ok {
+ t.Fatalf("failed to parse input")
+ }
+ consts, includes, incdirs, defines := ExtractConsts(top)
+ wantConsts := []string{"CONST1", "CONST2", "CONST3", "CONST4", "CONST5",
+ "CONST6", "CONST7", "__NR_bar", "__NR_foo"}
+ if !reflect.DeepEqual(consts, wantConsts) {
+ t.Fatalf("got consts:\n%q\nwant:\n%q", consts, wantConsts)
+ }
+ wantIncludes := []string{"foo/bar.h", "bar/foo.h"}
+ if !reflect.DeepEqual(includes, wantIncludes) {
+ t.Fatalf("got includes:\n%q\nwant:\n%q", includes, wantIncludes)
+ }
+ wantIncdirs := []string{"/foo", "/bar"}
+ if !reflect.DeepEqual(incdirs, wantIncdirs) {
+ t.Fatalf("got incdirs:\n%q\nwant:\n%q", incdirs, wantIncdirs)
+ }
+ wantDefines := map[string]string{
+ "CONST1": "1",
+ "CONST2": "FOOBAR + 1",
+ }
+ if !reflect.DeepEqual(defines, wantDefines) {
+ t.Fatalf("got defines:\n%q\nwant:\n%q", defines, wantDefines)
+ }
+}
+
+const extractConstsInput = `
+include <foo/bar.h>
+incdir </foo>
+include <bar/foo.h>
+incdir </bar>
+
+flags = CONST3, CONST2, CONST1
+
+define CONST1 1
+define CONST2 FOOBAR + 1
+
+foo(x const[CONST4]) ptr[out, array[int32, CONST5]]
+bar$BAR()
+
+str {
+ f1 const[CONST6, int32]
+ f2 array[array[int8, CONST7]]
+}
+`