mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-27 21:30:33 +00:00
ffe7e17368
Large overhaul moves syscalls and arg types from sys to prog. Sys package now depends on prog and contains only generated descriptions of syscalls. Introduce prog.Target type that encapsulates all targer properties, like syscall list, ptr/page size, etc. Also moves OS-dependent pieces like mmap call generation from prog to sys. Update #191
681 lines
18 KiB
Go
681 lines
18 KiB
Go
// 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"
|
|
"strconv"
|
|
|
|
"github.com/google/syzkaller/pkg/ast"
|
|
"github.com/google/syzkaller/prog"
|
|
)
|
|
|
|
// typeDesc is arg/field type descriptor.
|
|
type typeDesc struct {
|
|
Names []string
|
|
CanBeArg bool // can be argument of syscall?
|
|
CantBeOpt bool // can't be marked as opt?
|
|
CantBeRet bool // can't be syscall return (directly or indirectly)?
|
|
NeedBase bool // needs base type when used as field?
|
|
AllowColon bool // allow colon (int8:2) on fields?
|
|
ResourceBase bool // can be resource base type?
|
|
OptArgs int // number of optional arguments in Args array
|
|
Args []namedArg // type arguments
|
|
// Check does custom verification of the type (optional).
|
|
Check func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon)
|
|
// Varlen returns if the type is variable-length (false if not set).
|
|
Varlen func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool
|
|
// Gen generates corresponding prog.Type.
|
|
Gen func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type
|
|
}
|
|
|
|
// typeArg describes a type argument.
|
|
type typeArg struct {
|
|
Names []string
|
|
Kind int // int/ident/string
|
|
AllowColon bool // allow colon (2:3)?
|
|
// Check does custom verification of the arg (optional).
|
|
Check func(comp *compiler, t *ast.Type)
|
|
}
|
|
|
|
type namedArg struct {
|
|
Name string
|
|
Type *typeArg
|
|
}
|
|
|
|
const (
|
|
kindAny = iota
|
|
kindInt
|
|
kindIdent
|
|
kindString
|
|
)
|
|
|
|
var typeInt = &typeDesc{
|
|
Names: []string{"int8", "int16", "int32", "int64", "int16be", "int32be", "int64be", "intptr"},
|
|
CanBeArg: true,
|
|
AllowColon: true,
|
|
ResourceBase: true,
|
|
OptArgs: 1,
|
|
Args: []namedArg{{"range", typeArgRange}},
|
|
Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) {
|
|
typeArgBase.Type.Check(comp, t)
|
|
},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
size, be := comp.parseIntType(t.Ident)
|
|
kind, rangeBegin, rangeEnd := prog.IntPlain, uint64(0), uint64(0)
|
|
if len(args) > 0 {
|
|
kind, rangeBegin, rangeEnd = prog.IntRange, args[0].Value, args[0].Value2
|
|
}
|
|
base.TypeSize = size
|
|
return &prog.IntType{
|
|
IntTypeCommon: genIntCommon(base.TypeCommon, t.Value2, be),
|
|
Kind: kind,
|
|
RangeBegin: rangeBegin,
|
|
RangeEnd: rangeEnd,
|
|
}
|
|
},
|
|
}
|
|
|
|
var typePtr = &typeDesc{
|
|
Names: []string{"ptr", "ptr64"},
|
|
CanBeArg: true,
|
|
Args: []namedArg{{"direction", typeArgDir}, {"type", typeArgType}},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
base.ArgDir = prog.DirIn // pointers are always in
|
|
base.TypeSize = comp.ptrSize
|
|
if t.Ident == "ptr64" {
|
|
base.TypeSize = 8
|
|
}
|
|
return &prog.PtrType{
|
|
TypeCommon: base.TypeCommon,
|
|
Type: comp.genType(args[1], "", genDir(args[0]), false),
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeArray = &typeDesc{
|
|
Names: []string{"array"},
|
|
CantBeOpt: true,
|
|
OptArgs: 1,
|
|
Args: []namedArg{{"type", typeArgType}, {"size", typeArgRange}},
|
|
Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) {
|
|
if len(args) > 1 && args[1].Value == 0 && args[1].Value2 == 0 {
|
|
// This is the only case that can yield 0 static type size.
|
|
comp.error(args[1].Pos, "arrays of size 0 are not supported")
|
|
}
|
|
},
|
|
Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool {
|
|
if comp.isVarlen(args[0]) {
|
|
return true
|
|
}
|
|
if len(args) > 1 {
|
|
return args[1].Value != args[1].Value2
|
|
}
|
|
return true
|
|
},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
elemType := comp.genType(args[0], "", base.ArgDir, false)
|
|
kind, begin, end := prog.ArrayRandLen, uint64(0), uint64(0)
|
|
if len(args) > 1 {
|
|
kind, begin, end = prog.ArrayRangeLen, args[1].Value, args[1].Value2
|
|
}
|
|
if it, ok := elemType.(*prog.IntType); ok && it.Kind == prog.IntPlain && it.TypeSize == 1 {
|
|
// Special case: buffer is better mutated.
|
|
bufKind := prog.BufferBlobRand
|
|
base.TypeSize = 0
|
|
if kind == prog.ArrayRangeLen {
|
|
bufKind = prog.BufferBlobRange
|
|
if begin == end {
|
|
base.TypeSize = begin * elemType.Size()
|
|
}
|
|
}
|
|
return &prog.BufferType{
|
|
TypeCommon: base.TypeCommon,
|
|
Kind: bufKind,
|
|
RangeBegin: begin,
|
|
RangeEnd: end,
|
|
}
|
|
}
|
|
// TypeSize is assigned later in genStructDescs.
|
|
return &prog.ArrayType{
|
|
TypeCommon: base.TypeCommon,
|
|
Type: elemType,
|
|
Kind: kind,
|
|
RangeBegin: begin,
|
|
RangeEnd: end,
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeLen = &typeDesc{
|
|
Names: []string{"len", "bytesize", "bytesize2", "bytesize4", "bytesize8"},
|
|
CanBeArg: true,
|
|
CantBeOpt: true,
|
|
CantBeRet: true,
|
|
NeedBase: true,
|
|
Args: []namedArg{{"len target", typeArgLenTarget}},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
var byteSize uint64
|
|
switch t.Ident {
|
|
case "bytesize":
|
|
byteSize = 1
|
|
case "bytesize2", "bytesize4", "bytesize8":
|
|
byteSize, _ = strconv.ParseUint(t.Ident[8:], 10, 8)
|
|
}
|
|
return &prog.LenType{
|
|
IntTypeCommon: base,
|
|
Buf: args[0].Ident,
|
|
ByteSize: byteSize,
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeConst = &typeDesc{
|
|
Names: []string{"const"},
|
|
CanBeArg: true,
|
|
CantBeOpt: true,
|
|
NeedBase: true,
|
|
Args: []namedArg{{"value", typeArgInt}},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
return &prog.ConstType{
|
|
IntTypeCommon: base,
|
|
Val: args[0].Value,
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeArgLenTarget = &typeArg{
|
|
Kind: kindIdent,
|
|
}
|
|
|
|
var typeFlags = &typeDesc{
|
|
Names: []string{"flags"},
|
|
CanBeArg: true,
|
|
CantBeOpt: true,
|
|
NeedBase: true,
|
|
Args: []namedArg{{"flags", typeArgFlags}},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
name := args[0].Ident
|
|
base.TypeName = name
|
|
f := comp.intFlags[name]
|
|
if len(f.Values) == 0 {
|
|
// We can get this if all values are unsupported consts.
|
|
return &prog.IntType{
|
|
IntTypeCommon: base,
|
|
Kind: prog.IntPlain,
|
|
}
|
|
}
|
|
return &prog.FlagsType{
|
|
IntTypeCommon: base,
|
|
Vals: genIntArray(f.Values),
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeArgFlags = &typeArg{
|
|
Kind: kindIdent,
|
|
Check: func(comp *compiler, t *ast.Type) {
|
|
if comp.intFlags[t.Ident] == nil {
|
|
comp.error(t.Pos, "unknown flags %v", t.Ident)
|
|
return
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeFilename = &typeDesc{
|
|
Names: []string{"filename"},
|
|
CantBeOpt: true,
|
|
Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool {
|
|
return true
|
|
},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
base.TypeSize = 0
|
|
return &prog.BufferType{
|
|
TypeCommon: base.TypeCommon,
|
|
Kind: prog.BufferFilename,
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeFileoff = &typeDesc{
|
|
Names: []string{"fileoff"},
|
|
CanBeArg: true,
|
|
CantBeOpt: true,
|
|
NeedBase: true,
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
return &prog.IntType{
|
|
IntTypeCommon: base,
|
|
Kind: prog.IntFileoff,
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeVMA = &typeDesc{
|
|
Names: []string{"vma"},
|
|
CanBeArg: true,
|
|
OptArgs: 1,
|
|
Args: []namedArg{{"size range", typeArgRange}},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
begin, end := uint64(0), uint64(0)
|
|
if len(args) > 0 {
|
|
begin, end = args[0].Value, args[0].Value2
|
|
}
|
|
base.TypeSize = comp.ptrSize
|
|
return &prog.VmaType{
|
|
TypeCommon: base.TypeCommon,
|
|
RangeBegin: begin,
|
|
RangeEnd: end,
|
|
}
|
|
},
|
|
}
|
|
|
|
// TODO(dvyukov): perhaps, we need something like typedefs for such types.
|
|
// So that users can introduce them as necessary without modifying compiler:
|
|
// type signalno int32[0:64]
|
|
var typeSignalno = &typeDesc{
|
|
Names: []string{"signalno"},
|
|
CanBeArg: true,
|
|
CantBeOpt: true,
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
base.TypeSize = 4
|
|
return &prog.IntType{
|
|
IntTypeCommon: base,
|
|
Kind: prog.IntRange,
|
|
RangeBegin: 0,
|
|
RangeEnd: 65,
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeCsum = &typeDesc{
|
|
Names: []string{"csum"},
|
|
NeedBase: true,
|
|
CantBeOpt: true,
|
|
CantBeRet: true,
|
|
OptArgs: 1,
|
|
Args: []namedArg{{"csum target", typeArgLenTarget}, {"kind", typeArgCsumType}, {"proto", typeArgInt}},
|
|
Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) {
|
|
if len(args) > 2 && genCsumKind(args[1]) != prog.CsumPseudo {
|
|
comp.error(args[2].Pos, "only pseudo csum can have proto")
|
|
}
|
|
},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
var proto uint64
|
|
if len(args) > 2 {
|
|
proto = args[2].Value
|
|
}
|
|
return &prog.CsumType{
|
|
IntTypeCommon: base,
|
|
Buf: args[0].Ident,
|
|
Kind: genCsumKind(args[1]),
|
|
Protocol: proto,
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeArgCsumType = &typeArg{
|
|
Kind: kindIdent,
|
|
Names: []string{"inet", "pseudo"},
|
|
}
|
|
|
|
func genCsumKind(t *ast.Type) prog.CsumKind {
|
|
switch t.Ident {
|
|
case "inet":
|
|
return prog.CsumInet
|
|
case "pseudo":
|
|
return prog.CsumPseudo
|
|
default:
|
|
panic(fmt.Sprintf("unknown csum kind %q", t.Ident))
|
|
}
|
|
}
|
|
|
|
var typeProc = &typeDesc{
|
|
Names: []string{"proc"},
|
|
CanBeArg: true,
|
|
CantBeOpt: true,
|
|
NeedBase: true,
|
|
Args: []namedArg{{"range start", typeArgInt}, {"per-proc values", typeArgInt}},
|
|
Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) {
|
|
start := args[0].Value
|
|
perProc := args[1].Value
|
|
if perProc == 0 {
|
|
comp.error(args[1].Pos, "proc per-process values must not be 0")
|
|
return
|
|
}
|
|
size := base.TypeSize * 8
|
|
if size != 64 {
|
|
const maxPids = 32 // executor knows about this constant (MAX_PIDS)
|
|
if start >= 1<<size {
|
|
comp.error(args[0].Pos, "values starting from %v overflow base type", start)
|
|
} else if start+maxPids*perProc >= 1<<size {
|
|
comp.error(args[0].Pos, "values starting from %v with step %v overflow base type for %v procs",
|
|
start, perProc, maxPids)
|
|
}
|
|
}
|
|
},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
return &prog.ProcType{
|
|
IntTypeCommon: base,
|
|
ValuesStart: args[0].Value,
|
|
ValuesPerProc: args[1].Value,
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeText = &typeDesc{
|
|
Names: []string{"text"},
|
|
CantBeOpt: true,
|
|
Args: []namedArg{{"kind", typeArgTextType}},
|
|
Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool {
|
|
return true
|
|
},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
base.TypeSize = 0
|
|
return &prog.BufferType{
|
|
TypeCommon: base.TypeCommon,
|
|
Kind: prog.BufferText,
|
|
Text: genTextType(args[0]),
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeArgTextType = &typeArg{
|
|
Kind: kindIdent,
|
|
Names: []string{"x86_real", "x86_16", "x86_32", "x86_64", "arm64"},
|
|
}
|
|
|
|
func genTextType(t *ast.Type) prog.TextKind {
|
|
switch t.Ident {
|
|
case "x86_real":
|
|
return prog.Text_x86_real
|
|
case "x86_16":
|
|
return prog.Text_x86_16
|
|
case "x86_32":
|
|
return prog.Text_x86_32
|
|
case "x86_64":
|
|
return prog.Text_x86_64
|
|
case "arm64":
|
|
return prog.Text_arm64
|
|
default:
|
|
panic(fmt.Sprintf("unknown text type %q", t.Ident))
|
|
}
|
|
}
|
|
|
|
var typeBuffer = &typeDesc{
|
|
Names: []string{"buffer"},
|
|
CanBeArg: true,
|
|
Args: []namedArg{{"direction", typeArgDir}},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
base.TypeSize = comp.ptrSize
|
|
return &prog.PtrType{
|
|
TypeCommon: base.TypeCommon,
|
|
Type: &prog.BufferType{
|
|
// BufferBlobRand is always varlen.
|
|
TypeCommon: genCommon("", "", 0, genDir(args[0]), false),
|
|
Kind: prog.BufferBlobRand,
|
|
},
|
|
}
|
|
},
|
|
}
|
|
|
|
var typeString = &typeDesc{
|
|
Names: []string{"string"},
|
|
OptArgs: 2,
|
|
Args: []namedArg{{"literal or flags", typeArgStringFlags}, {"size", typeArgInt}},
|
|
Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) {
|
|
if len(args) > 1 {
|
|
size := args[1].Value
|
|
vals := []string{args[0].String}
|
|
if args[0].Ident != "" {
|
|
vals = genStrArray(comp.strFlags[args[0].Ident].Values)
|
|
}
|
|
for _, s := range vals {
|
|
s += "\x00"
|
|
if uint64(len(s)) > size {
|
|
comp.error(args[0].Pos, "string value %q exceeds buffer length %v",
|
|
s, size)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool {
|
|
return comp.stringSize(args) == 0
|
|
},
|
|
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
subkind := ""
|
|
var vals []string
|
|
if len(args) > 0 {
|
|
if args[0].String != "" {
|
|
vals = append(vals, args[0].String)
|
|
} else {
|
|
subkind = args[0].Ident
|
|
vals = genStrArray(comp.strFlags[subkind].Values)
|
|
}
|
|
}
|
|
var size uint64
|
|
if len(args) > 1 {
|
|
size = args[1].Value
|
|
}
|
|
for i, s := range vals {
|
|
s += "\x00"
|
|
for uint64(len(s)) < size {
|
|
s += "\x00"
|
|
}
|
|
vals[i] = s
|
|
}
|
|
base.TypeSize = comp.stringSize(args)
|
|
return &prog.BufferType{
|
|
TypeCommon: base.TypeCommon,
|
|
Kind: prog.BufferString,
|
|
SubKind: subkind,
|
|
Values: vals,
|
|
}
|
|
},
|
|
}
|
|
|
|
// stringSize returns static string size, or 0 if it is variable length.
|
|
func (comp *compiler) stringSize(args []*ast.Type) uint64 {
|
|
switch len(args) {
|
|
case 0:
|
|
return 0 // a random string
|
|
case 1:
|
|
if args[0].String != "" {
|
|
return uint64(len(args[0].String)) + 1 // string constant
|
|
}
|
|
var size uint64
|
|
for _, s := range comp.strFlags[args[0].Ident].Values {
|
|
s1 := uint64(len(s.Value)) + 1
|
|
if size != 0 && size != s1 {
|
|
return 0 // strings of different lengths
|
|
}
|
|
size = s1
|
|
}
|
|
return size // all strings have the same length
|
|
case 2:
|
|
return args[1].Value // have explicit length
|
|
default:
|
|
panic("too many string args")
|
|
}
|
|
}
|
|
|
|
var typeArgStringFlags = &typeArg{
|
|
Check: func(comp *compiler, t *ast.Type) {
|
|
if t.String == "" && t.Ident == "" {
|
|
comp.error(t.Pos, "unexpected int %v, string arg must be a string literal or string flags", t.Value)
|
|
return
|
|
}
|
|
if t.Ident != "" && comp.strFlags[t.Ident] == nil {
|
|
comp.error(t.Pos, "unknown string flags %v", t.Ident)
|
|
return
|
|
}
|
|
},
|
|
}
|
|
|
|
// typeArgType is used as placeholder for any type (e.g. ptr target type).
|
|
var typeArgType = &typeArg{
|
|
Check: func(comp *compiler, t *ast.Type) {
|
|
panic("must not be called")
|
|
},
|
|
}
|
|
|
|
var typeResource = &typeDesc{
|
|
// No Names, but compiler knows how to match it.
|
|
CanBeArg: true,
|
|
ResourceBase: true,
|
|
// Gen is assigned below to avoid initialization loop.
|
|
}
|
|
|
|
func init() {
|
|
typeResource.Gen = func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
// Find and generate base type to get its size.
|
|
var baseType *ast.Type
|
|
for r := comp.resources[t.Ident]; r != nil; {
|
|
baseType = r.Base
|
|
r = comp.resources[r.Base.Ident]
|
|
}
|
|
base.TypeSize = comp.genType(baseType, "", prog.DirIn, false).Size()
|
|
return &prog.ResourceType{
|
|
TypeCommon: base.TypeCommon,
|
|
}
|
|
}
|
|
}
|
|
|
|
var typeStruct = &typeDesc{
|
|
// No Names, but compiler knows how to match it.
|
|
CantBeOpt: true,
|
|
// Varlen/Gen are assigned below due to initialization cycle.
|
|
}
|
|
|
|
func init() {
|
|
typeStruct.Varlen = func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool {
|
|
return comp.isStructVarlen(t.Ident)
|
|
}
|
|
typeStruct.Gen = func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
|
|
s := comp.structs[t.Ident]
|
|
key := prog.StructKey{t.Ident, base.ArgDir}
|
|
desc := comp.structDescs[key]
|
|
if desc == nil {
|
|
// Need to assign to structDescs before calling genStructDesc to break recursion.
|
|
desc = new(prog.StructDesc)
|
|
comp.structDescs[key] = desc
|
|
comp.genStructDesc(desc, s, base.ArgDir)
|
|
}
|
|
if s.IsUnion {
|
|
return &prog.UnionType{
|
|
Key: key,
|
|
FldName: base.FldName,
|
|
StructDesc: desc,
|
|
}
|
|
} else {
|
|
return &prog.StructType{
|
|
Key: key,
|
|
FldName: base.FldName,
|
|
StructDesc: desc,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var typeArgDir = &typeArg{
|
|
Kind: kindIdent,
|
|
Names: []string{"in", "out", "inout"},
|
|
}
|
|
|
|
func genDir(t *ast.Type) prog.Dir {
|
|
switch t.Ident {
|
|
case "in":
|
|
return prog.DirIn
|
|
case "out":
|
|
return prog.DirOut
|
|
case "inout":
|
|
return prog.DirInOut
|
|
default:
|
|
panic(fmt.Sprintf("unknown direction %q", t.Ident))
|
|
}
|
|
}
|
|
|
|
var typeArgInt = &typeArg{
|
|
Kind: kindInt,
|
|
}
|
|
|
|
var typeArgRange = &typeArg{
|
|
Kind: kindInt,
|
|
AllowColon: true,
|
|
Check: func(comp *compiler, t *ast.Type) {
|
|
if !t.HasColon {
|
|
t.Value2 = t.Value
|
|
}
|
|
if t.Value > t.Value2 {
|
|
comp.error(t.Pos, "bad int range [%v:%v]", t.Value, t.Value2)
|
|
}
|
|
},
|
|
}
|
|
|
|
// Base type of const/len/etc. Same as typeInt, but can't have range.
|
|
var typeArgBase = namedArg{
|
|
Name: "base type",
|
|
Type: &typeArg{
|
|
Names: []string{"int8", "int16", "int32", "int64", "int16be", "int32be", "int64be", "intptr"},
|
|
AllowColon: true,
|
|
Check: func(comp *compiler, t *ast.Type) {
|
|
if t.HasColon {
|
|
if t.Value2 == 0 {
|
|
// This was not supported historically
|
|
// and does not work the way C bitfields of size 0 work.
|
|
// We could allow this, but then we need to make
|
|
// this work the way C bitfields work.
|
|
comp.error(t.Pos2, "bitfields of size 0 are not supported")
|
|
}
|
|
size, _ := comp.parseIntType(t.Ident)
|
|
if t.Value2 > size*8 {
|
|
comp.error(t.Pos2, "bitfield of size %v is too large for base type of size %v",
|
|
t.Value2, size*8)
|
|
}
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
var (
|
|
builtinTypes = make(map[string]*typeDesc)
|
|
|
|
// To avoid weird cases like ptr[in, in] and ptr[out, opt].
|
|
reservedName = map[string]bool{
|
|
"opt": true,
|
|
"in": true,
|
|
"out": true,
|
|
"inout": true,
|
|
}
|
|
)
|
|
|
|
func init() {
|
|
builtins := []*typeDesc{
|
|
typeInt,
|
|
typePtr,
|
|
typeArray,
|
|
typeLen,
|
|
typeConst,
|
|
typeFlags,
|
|
typeFilename,
|
|
typeFileoff,
|
|
typeVMA,
|
|
typeSignalno,
|
|
typeCsum,
|
|
typeProc,
|
|
typeText,
|
|
typeBuffer,
|
|
typeString,
|
|
typeResource,
|
|
typeStruct,
|
|
}
|
|
for _, desc := range builtins {
|
|
for _, name := range desc.Names {
|
|
if builtinTypes[name] != nil {
|
|
panic(fmt.Sprintf("duplicate builtin type %q", name))
|
|
}
|
|
builtinTypes[name] = desc
|
|
}
|
|
}
|
|
}
|