diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-06-17 13:17:11 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-06-17 14:41:15 +0200 |
| commit | 2007005ac39f66f3fb6314745ffb22b6d30314d3 (patch) | |
| tree | dbdfe1600cf905199e058ff799b0c8f31efcf7a8 /sys/sysparser | |
| parent | 017b5dea530573ce49ccc6f09c469a78c7dbaede (diff) | |
sys/sysparser: move from sysparser
Diffstat (limited to 'sys/sysparser')
| -rw-r--r-- | sys/sysparser/lexer.go | 313 | ||||
| -rw-r--r-- | sys/sysparser/parser.go | 100 |
2 files changed, 413 insertions, 0 deletions
diff --git a/sys/sysparser/lexer.go b/sys/sysparser/lexer.go new file mode 100644 index 000000000..505b52059 --- /dev/null +++ b/sys/sysparser/lexer.go @@ -0,0 +1,313 @@ +// 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 sysparser + +import ( + "fmt" + "io" + "os" + "sort" + "strconv" + "strings" +) + +type Description struct { + Includes []string + Defines map[string]string + Syscalls []Syscall + Structs map[string]*Struct + Unnamed map[string][]string + Flags map[string][]string + StrFlags map[string][]string + Resources map[string]Resource +} + +type Syscall struct { + Name string + CallName string + Args [][]string + Ret []string +} + +type Struct struct { + Name string + Flds [][]string + IsUnion bool + Packed bool + Varlen bool + Align int +} + +type Resource struct { + Name string + Base string + Values []string +} + +func Parse(in io.Reader) *Description { + p := newParser(in) + var includes []string + defines := make(map[string]string) + var syscalls []Syscall + structs := make(map[string]*Struct) + unnamed := make(map[string][]string) + flags := make(map[string][]string) + strflags := make(map[string][]string) + resources := make(map[string]Resource) + var str *Struct + for p.Scan() { + if p.EOF() || p.Char() == '#' { + continue + } + if str != nil { + // Parsing a struct. + if p.Char() == '}' || p.Char() == ']' { + p.Parse(p.Char()) + for _, attr := range parseType1(p, unnamed, flags, "")[1:] { + if str.IsUnion { + switch attr { + case "varlen": + str.Varlen = true + default: + failf("unknown union %v attribute: %v", str.Name, attr) + } + } else { + switch { + case attr == "packed": + str.Packed = true + case strings.HasPrefix(attr, "align_ptr"): + str.Align = 8 // TODO: this must be target pointer size + case strings.HasPrefix(attr, "align_"): + a, err := strconv.ParseUint(attr[6:], 10, 64) + if err != nil { + failf("bad struct %v alignment %v: %v", str.Name, attr, err) + } + if a&(a-1) != 0 || a == 0 || a > 1<<30 { + failf("bad struct %v alignment %v: must be sane power of 2", str.Name, a) + } + str.Align = int(a) + default: + failf("unknown struct %v attribute: %v", str.Name, attr) + } + } + } + if str.IsUnion { + if len(str.Flds) <= 1 { + failf("union %v has only %v fields, need at least 2", str.Name, len(str.Flds)) + } + } + fields := make(map[string]bool) + for _, f := range str.Flds { + if f[0] == "parent" { + failf("struct/union %v contains reserved field 'parent'", str.Name) + } + if fields[f[0]] { + failf("duplicate field %v in struct/union %v", f[0], str.Name) + } + fields[f[0]] = true + } + structs[str.Name] = str + str = nil + } else { + p.SkipWs() + fld := []string{p.Ident()} + fld = append(fld, parseType(p, unnamed, flags)...) + str.Flds = append(str.Flds, fld) + } + } else { + name := p.Ident() + if name == "include" { + p.Parse('<') + var include []byte + for { + ch := p.Char() + if ch == '>' { + break + } + p.Parse(ch) + include = append(include, ch) + } + p.Parse('>') + includes = append(includes, string(include)) + } else if name == "define" { + key := p.Ident() + var val []byte + for !p.EOF() { + ch := p.Char() + p.Parse(ch) + val = append(val, ch) + } + if defines[key] != "" { + failf("%v define is defined multiple times", key) + } + defines[key] = fmt.Sprintf("(%s)", val) + } else if name == "resource" { + p.SkipWs() + id := p.Ident() + p.Parse('[') + base := p.Ident() + p.Parse(']') + var vals []string + if !p.EOF() && p.Char() == ':' { + p.Parse(':') + vals = append(vals, p.Ident()) + for !p.EOF() { + p.Parse(',') + vals = append(vals, p.Ident()) + } + } + if _, ok := resources[id]; ok { + failf("resource '%v' is defined multiple times", id) + } + if _, ok := structs[id]; ok { + failf("struct '%v' is redefined as resource", name) + } + resources[id] = Resource{id, base, vals} + } else { + switch ch := p.Char(); ch { + case '(': + // syscall + p.Parse('(') + var args [][]string + for p.Char() != ')' { + arg := []string{p.Ident()} + arg = append(arg, parseType(p, unnamed, flags)...) + args = append(args, arg) + if p.Char() != ')' { + p.Parse(',') + } + } + p.Parse(')') + var ret []string + if !p.EOF() { + ret = parseType(p, unnamed, flags) + } + callName := name + if idx := strings.IndexByte(callName, '$'); idx != -1 { + callName = callName[:idx] + } + fields := make(map[string]bool) + for _, a := range args { + if fields[a[0]] { + failf("duplicate arg %v in syscall %v", a[0], name) + } + fields[a[0]] = true + } + syscalls = append(syscalls, Syscall{name, callName, args, ret}) + case '=': + // flag + p.Parse('=') + str := p.Char() == '"' + var vals []string + for { + v := p.Ident() + if str { + v = v[1 : len(v)-1] + } + vals = append(vals, v) + if p.EOF() { + break + } + p.Parse(',') + } + if str { + strflags[name] = vals + } else { + flags[name] = vals + } + case '{', '[': + p.Parse(ch) + if _, ok := structs[name]; ok { + failf("struct '%v' is defined multiple times", name) + } + if _, ok := resources[name]; ok { + failf("resource '%v' is redefined as struct", name) + } + str = &Struct{Name: name, IsUnion: ch == '['} + default: + failf("bad line (%v)", p.Str()) + } + } + } + if !p.EOF() { + failf("trailing data (%v)", p.Str()) + } + } + sort.Sort(syscallArray(syscalls)) + return &Description{ + Includes: includes, + Defines: defines, + Syscalls: syscalls, + Structs: structs, + Unnamed: unnamed, + Flags: flags, + StrFlags: strflags, + Resources: resources, + } +} + +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 parseType(p *parser, unnamed map[string][]string, flags map[string][]string) []string { + return parseType1(p, unnamed, flags, p.Ident()) +} + +var ( + unnamedSeq int + constSeq int +) + +func parseType1(p *parser, unnamed map[string][]string, flags map[string][]string, name string) []string { + typ := []string{name} + if !p.EOF() && p.Char() == '[' { + p.Parse('[') + for { + id := p.Ident() + if p.Char() == '[' { + inner := parseType1(p, unnamed, flags, id) + id = fmt.Sprintf("unnamed%v", unnamedSeq) + unnamedSeq++ + unnamed[id] = inner + } + typ = append(typ, id) + if p.Char() == ']' { + break + } + p.Parse(',') + } + p.Parse(']') + } + if name == "const" && len(typ) > 1 { + // Create a fake flag with the const value. + id := fmt.Sprintf("const_flag_%v", constSeq) + constSeq++ + flags[id] = typ[1:2] + } + if name == "array" && len(typ) > 2 { + // Create a fake flag with the const value. + id := fmt.Sprintf("const_flag_%v", constSeq) + constSeq++ + flags[id] = typ[2:3] + } + return typ +} + +type syscallArray []Syscall + +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] } + +func failf(msg string, args ...interface{}) { + fmt.Fprintf(os.Stderr, msg+"\n", args...) + os.Exit(1) +} diff --git a/sys/sysparser/parser.go b/sys/sysparser/parser.go new file mode 100644 index 000000000..d5021eee6 --- /dev/null +++ b/sys/sysparser/parser.go @@ -0,0 +1,100 @@ +// 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 sysparser + +import ( + "bufio" + "fmt" + "io" + "os" +) + +type parser struct { + r *bufio.Scanner + s string + i int + l int +} + +func newParser(r io.Reader) *parser { + return &parser{r: bufio.NewScanner(r)} +} + +func (p *parser) Scan() bool { + if !p.r.Scan() { + if err := p.r.Err(); err != nil { + failf("failed to read input file: %v", err) + } + return false + } + p.s = p.r.Text() + p.i = 0 + p.l++ + return true +} + +func (p *parser) Str() string { + return p.s +} + +func (p *parser) EOF() bool { + return p.i == len(p.s) +} + +func (p *parser) Char() byte { + if p.EOF() { + p.failf("unexpected eof") + } + return p.s[p.i] +} + +func (p *parser) Parse(ch byte) { + if p.EOF() { + p.failf("want %s, got EOF", string(ch)) + } + if p.s[p.i] != ch { + p.failf("want '%v', got '%v'", string(ch), string(p.s[p.i])) + } + p.i++ + p.SkipWs() +} + +func (p *parser) SkipWs() { + for p.i < len(p.s) && (p.s[p.i] == ' ' || p.s[p.i] == '\t') { + p.i++ + } +} + +func (p *parser) Ident() string { + start, end := p.i, 0 + if p.Char() == '"' { + p.Parse('"') + for p.Char() != '"' { + p.i++ + } + end = p.i + 1 + p.Parse('"') + } else { + for p.i < len(p.s) && + (p.s[p.i] >= 'a' && p.s[p.i] <= 'z' || + p.s[p.i] >= 'A' && p.s[p.i] <= 'Z' || + p.s[p.i] >= '0' && p.s[p.i] <= '9' || + p.s[p.i] == '_' || p.s[p.i] == '$' || // $ is for n-way syscalls (like ptrace$peek) + p.s[p.i] == '-' || p.s[p.i] == ':') { // : is for ranged int (like int32[-3:10]) + p.i++ + } + if start == p.i { + p.failf("failed to parse identifier at pos %v", start) + } + end = p.i + } + s := p.s[start:end] + p.SkipWs() + return s +} + +func (p *parser) failf(msg string, args ...interface{}) { + fmt.Fprintf(os.Stderr, "line #%v: %v\n", p.l, p.s) + failf(msg, args...) +} |
