aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/compiler/attrs.go
blob: ba877caf6219c91f6fe6c0a56e004ffc3c6f7891 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Copyright 2020 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"

	"github.com/google/syzkaller/pkg/ast"
	"github.com/google/syzkaller/prog"
)

type attrDescAttrType int

const (
	flagAttr attrDescAttrType = iota
	// TODO: Ultimately we want to replace intAttr with exprAttr.
	// This will facilitate const expressions in e.g. size[] or align[].
	intAttr
	exprAttr
	stringAttr
)

type attrDesc struct {
	Name string
	// For now we assume attributes can have only 1 argument and it's either an
	// integer or an expression.
	Type attrDescAttrType
	// This function is not invoked for per-field attributes, only for whole
	// structs/unions.
	CheckConsts func(comp *compiler, parent ast.Node, attr *ast.Type)
}

var (
	attrPacked     = &attrDesc{Name: "packed"}
	attrVarlen     = &attrDesc{Name: "varlen"}
	attrSize       = &attrDesc{Name: "size", Type: intAttr}
	attrAlign      = &attrDesc{Name: "align", Type: intAttr}
	attrIn         = &attrDesc{Name: "in"}
	attrOut        = &attrDesc{Name: "out"}
	attrInOut      = &attrDesc{Name: "inout"}
	attrOutOverlay = &attrDesc{Name: "out_overlay"}
	attrIf         = &attrDesc{Name: "if", Type: exprAttr}

	structAttrs      = makeAttrs(attrPacked, attrSize, attrAlign)
	unionAttrs       = makeAttrs(attrVarlen, attrSize)
	structFieldAttrs = makeAttrs(attrIn, attrOut, attrInOut, attrOutOverlay, attrIf)
	unionFieldAttrs  = makeAttrs(attrIn, attrIf) // attrIn is safe.
	callAttrs        = make(map[string]*attrDesc)
)

func init() {
	initCallAttrs()

	attrSize.CheckConsts = func(comp *compiler, parent ast.Node, attr *ast.Type) {
		_, typ, name := parent.Info()
		if comp.structIsVarlen(name) {
			comp.error(attr.Pos, "varlen %v %v has size attribute", typ, name)
		}
		sz := attr.Args[0].Value
		if sz == 0 || sz > 1<<20 {
			comp.error(attr.Args[0].Pos, "size attribute has bad value %v"+
				", expect [1, 1<<20]", sz)
		}
	}
	attrAlign.CheckConsts = func(comp *compiler, parent ast.Node, attr *ast.Type) {
		_, _, name := parent.Info()
		a := attr.Args[0].Value
		if a&(a-1) != 0 || a == 0 || a > 1<<30 {
			comp.error(attr.Pos, "bad struct %v alignment %v (must be a sane power of 2)", name, a)
		}
	}
}

func initCallAttrs() {
	attrs := reflect.TypeOf(prog.SyscallAttrs{})
	for i := 0; i < attrs.NumField(); i++ {
		attr := attrs.Field(i)
		desc := &attrDesc{Name: attr.Name}
		switch attr.Type.Kind() {
		case reflect.Bool:
		case reflect.Uint64:
			desc.Type = intAttr
		case reflect.String:
			desc.Type = stringAttr
		default:
			panic("unsupported syscall attribute type")
		}
		callAttrs[prog.CppName(desc.Name)] = desc
	}
}

func structOrUnionAttrs(n *ast.Struct) map[string]*attrDesc {
	if n.IsUnion {
		return unionAttrs
	}
	return structAttrs
}

func structOrUnionFieldAttrs(n *ast.Struct) map[string]*attrDesc {
	if n.IsUnion {
		return unionFieldAttrs
	}
	return structFieldAttrs
}

func makeAttrs(attrs ...*attrDesc) map[string]*attrDesc {
	m := make(map[string]*attrDesc)
	for _, attr := range attrs {
		m[attr.Name] = attr
	}
	return m
}