[go][syscall] Split out abigen parsing into its own package
This change moves the syscalls.abigen parser into its own package. The
parser will continue to be used in syscall/zx/mkfuchsia.go, but also in
a tool to be added to the runtime to generate bindings for resolving
vDSO symbols when not running with cgo.
Change-Id: I95c789d9b7751579b9f070815498acddfbe14f52
diff --git a/src/internal/fuchsia/abigen/defs.go b/src/internal/fuchsia/abigen/defs.go
new file mode 100644
index 0000000..e97b9ac
--- /dev/null
+++ b/src/internal/fuchsia/abigen/defs.go
@@ -0,0 +1,65 @@
+package abigen
+
+// SysArg represents an arg to a vDSO-exported syscall.
+type SysArg struct {
+ Name string // Name represents the name of the argument.
+ Type SysType // Type represents the type of the argument.
+ Constraint string // Constraint represents constraints on the argument (in/out/inout).
+}
+
+// SysDef represents a definition of a vDSO-exported syscall.
+type SysDef struct {
+ Name string // Name is the name of this syscall definition.
+ Ret SysType // Ret is the return type of the syscall.
+ Attrs []string // Attrs contains one or more attributes of the syscall.
+ Args []SysArg // Args represents the arguments taken by the syscall.
+ VDSOCall bool // VDSOCall represents whether the syscall is returned directly from the vDSO.
+}
+
+// GoName returns a string representing the Go name of the syscall in the syscall package.
+func (d *SysDef) GoName() string {
+ if d.Exported() {
+ return "Sys_" + d.Name
+ }
+ return "sys_" + d.Name
+}
+
+// Exported determines whether a particular syscall definition should be exported.
+func (d *SysDef) Exported() bool {
+ switch d.Name {
+ case "handle_duplicate",
+ "object_set_profile",
+ "object_wait_many",
+ "channel_create",
+ "channel_read",
+ "channel_read_etc",
+ "channel_write",
+ "pci_get_bar",
+ "pci_map_interrupt",
+ "pci_query_irq_mode",
+ "pci_set_irq_mode",
+ "pci_init",
+ "pci_add_subtract_to_range",
+ "socket_create",
+ "socket_read",
+ "socket_write",
+ "socket_accept",
+ "socket_share",
+ "system_get_dcache_line_size",
+ "system_get_features",
+ "vmar_allocate",
+ "vmar_allocate_old",
+ "vmar_map_old",
+ "vmar_protect_old",
+ "vmar_destroy",
+ "vmo_read",
+ "vmo_write",
+ "vmo_get_size",
+ "vmo_set_size",
+ "vmo_op_range",
+ "vmo_replace_as_executable":
+ return false
+ default:
+ return true
+ }
+}
diff --git a/src/internal/fuchsia/abigen/doc.go b/src/internal/fuchsia/abigen/doc.go
new file mode 100644
index 0000000..18b13fb
--- /dev/null
+++ b/src/internal/fuchsia/abigen/doc.go
@@ -0,0 +1,9 @@
+// The abigen package provides functionality for parsing Fuchsia's syscalls.abigen file,
+// which contains information on syscalls exposed through the vDSO. This file is used to
+// generate stubs and implementation in the syscall package.
+//
+// Example usage:
+//
+// p := abigen.NewParser(buffer, "/path/to/syscalls.abigen")
+// syscalls, err := p.Parse()
+package abigen
diff --git a/src/internal/fuchsia/abigen/parser.go b/src/internal/fuchsia/abigen/parser.go
new file mode 100644
index 0000000..a59a38e
--- /dev/null
+++ b/src/internal/fuchsia/abigen/parser.go
@@ -0,0 +1,326 @@
+package abigen
+
+import (
+ "fmt"
+ "strings"
+ "unicode/utf8"
+)
+
+// Parser is a type that is able to parse a syscalls.abigen file.
+type Parser struct {
+ buf []byte
+ off int
+ line, col int
+ name string
+
+ r rune
+}
+
+type token rune
+
+const (
+ tokenEOF token = iota
+ tokenBad
+ tokenIdent
+ tokenNumber
+)
+
+// String implements fmt.Stringer on the token type.
+func (t token) String() string {
+ switch t {
+ case tokenEOF:
+ return "token(EOF)"
+ case tokenBad:
+ return "token(BAD)"
+ case tokenIdent:
+ return "token(Ident)"
+ case tokenNumber:
+ return "token(Number)"
+ default:
+ return "token(" + string(t) + ")"
+ }
+}
+
+func NewParser(buf []byte, fn string) *Parser {
+ return &Parser{
+ buf: buf,
+ line: 1,
+ name: fn,
+ }
+}
+
+func (p *Parser) Parse() (defs []SysDef, err error) {
+ if len(p.buf) == 0 {
+ return nil, nil
+ }
+
+ p.nextRune()
+ for {
+ t, lit := p.nextToken()
+ if t == tokenEOF {
+ return defs, nil
+ } else if t == tokenBad {
+ return nil, p.errorf("bad token: %s", lit)
+ }
+
+ if lit == "syscall" {
+ def, err := p.parseSyscall()
+ if err != nil {
+ return nil, err
+ }
+
+ // The PCI syscalls are DDK-only and temporary. The hypervisor syscalls
+ // have complex parameter and return types. Exclude all of these.
+ // helpers for them.
+ if strings.Contains(def.Name, "_test") || strings.HasPrefix(def.Name, "pci_") || strings.HasPrefix(def.Name, "guest_") || strings.HasPrefix(def.Name, "vcpu_") {
+ continue
+ }
+
+ defs = append(defs, def)
+ } else {
+ return nil, p.errorf("unexpected token: %q (literal %q)", t, lit)
+ }
+ }
+
+ return defs, nil
+}
+
+func (p *Parser) expect(want token) {
+ t, lit := p.nextToken()
+ if t != want {
+ p.panicf("expected %q but got %q (literal %q)", want, t, lit)
+ }
+}
+
+func (p *Parser) parseAttributes() (attr []string, err error) {
+ for {
+ p.skipWhitespace()
+ if p.r == '(' {
+ return
+ }
+ attr = append(attr, p.nextIdent())
+ p.skipWhitespace()
+ if p.r == ',' {
+ p.nextRune()
+ }
+ }
+}
+
+func (p *Parser) parseArg() (arg SysArg, err error) {
+ if p.r == ')' {
+ return
+ }
+ arg.Name = p.nextIdent()
+ p.expect(':')
+ arg.Type, err = p.parseType()
+ if err != nil {
+ return SysArg{}, err
+ }
+ p.skipWhitespace()
+ if isLetter(p.r) {
+ arg.Constraint = p.nextIdent()
+ p.skipWhitespace()
+ }
+ return
+}
+
+func (p *Parser) parseSyscall() (d SysDef, err error) {
+ d.Name = p.nextIdent()
+ d.Attrs, err = p.parseAttributes()
+ if err != nil {
+ return SysDef{}, err
+ }
+ for _, a := range d.Attrs {
+ if a == "vdsocall" {
+ d.VDSOCall = true
+ }
+ }
+ p.expect('(')
+ for {
+ p.skipWhitespace()
+ if p.r == ')' {
+ p.nextRune()
+ break
+ }
+ arg, err := p.parseArg()
+ if err != nil {
+ return SysDef{}, err
+ }
+ if arg.Name == "" {
+ break
+ }
+ d.Args = append(d.Args, arg)
+ if p.r == ',' {
+ p.nextRune()
+ }
+ }
+ p.skipWhitespace()
+ if p.r != ';' {
+ kw := p.nextIdent()
+ if kw != "returns" {
+ p.panicf("expected \"returns\" but got \"%s\"", kw)
+ }
+ p.expect('(')
+ first := true
+ for {
+ p.skipWhitespace()
+ if p.r == ')' {
+ p.nextRune()
+ break
+ }
+ if first {
+ first = false
+ rtype, err := p.parseType()
+ if err != nil {
+ return SysDef{}, err
+ }
+ d.Ret = rtype
+ } else {
+ arg, err := p.parseArg()
+ if err != nil {
+ return SysDef{}, err
+ }
+ // Extra return types are returned as pointers.
+ arg.Type.Array = true
+ arg.Type.ArraySize = "1"
+ d.Args = append(d.Args, arg)
+ }
+ if p.r == ',' {
+ p.nextRune()
+ }
+ }
+ }
+ p.skipWhitespace()
+ p.expect(';')
+ return d, nil
+}
+
+func (p *Parser) parseType() (t SysType, err error) {
+ ident := p.nextIdent()
+ n, ok := sysTypeNames[ident]
+ if !ok {
+ return t, p.errorf("unknown type name: %s", ident)
+ }
+ t.Name = n
+ p.skipWhitespace()
+ if p.r == '[' {
+ p.nextRune()
+ t.Array = true
+ t.ArraySize = p.nextIdent()
+ p.skipWhitespace()
+ for p.r == '*' {
+ p.nextRune()
+ t.ArraySize += " * " + p.nextIdent()
+ }
+ p.expect(']')
+ }
+ return t, nil
+}
+
+func (p *Parser) nextToken() (t token, lit string) {
+ p.skipWhitespace()
+
+ if isLetter(p.r) {
+ lit = p.nextIdent()
+ return tokenIdent, lit
+ }
+ if isNumber(p.r) {
+ lit = p.nextNumber()
+ return tokenNumber, lit
+ }
+
+ r := p.r
+ p.nextRune()
+ switch r {
+ case -1:
+ return tokenEOF, ""
+ case '(', ')', '*', ',', ';', ':', '[', ']':
+ return token(r), ""
+ case '#':
+ p.skipComment()
+ return p.nextToken()
+ }
+ return tokenBad, fmt.Sprintf("%q", r)
+}
+
+func (p *Parser) nextIdent() string {
+ p.skipWhitespace()
+ if !isLetter(p.r) && !isNumber(p.r) {
+ p.panicf("expected ident but got %q", p.r)
+ }
+ off := p.off - 1
+ for isLetter(p.r) || isNumber(p.r) {
+ p.nextRune()
+ }
+ r := string(p.buf[off : p.off-1])
+ if r == "type" {
+ // "type" is a reserved word in Go.
+ return "typ"
+ }
+ return r
+}
+
+func (p *Parser) nextNumber() string {
+ p.skipWhitespace()
+ if !isNumber(p.r) {
+ p.panicf("expected number but got %q", p.r)
+ }
+ off := p.off - 1
+ for isNumber(p.r) {
+ p.nextRune()
+ }
+ return string(p.buf[off : p.off-1])
+}
+
+func (p *Parser) skipWhitespace() {
+ for p.r == ' ' || p.r == '\t' || p.r == '\n' {
+ p.nextRune()
+ }
+}
+
+func (p *Parser) skipComment() {
+ // skip until end of line
+ p.nextRune()
+ for p.r != '\n' && p.r >= 0 {
+ p.nextRune()
+ }
+}
+
+func (p *Parser) nextRune() {
+ if p.off >= len(p.buf) {
+ p.r = -1
+ return
+ }
+ if p.r == '\n' {
+ p.line++
+ p.col = 0
+ } else {
+ p.col += utf8.RuneLen(p.r)
+ }
+
+ r, w := utf8.DecodeRune(p.buf[p.off:])
+ if r == utf8.RuneError && w == 1 {
+ p.r = -1
+ p.panicf("input is not UTF-8")
+ return
+ }
+ p.r = r
+ p.off += w
+}
+
+func (p *Parser) panicf(format string, args ...interface{}) {
+ panic(parsePanic{err: p.errorf(format, args...)})
+}
+
+func (p *Parser) errorf(format string, args ...interface{}) error {
+ return fmt.Errorf("%s:%d:%d: %s", p.name, p.line, p.col, fmt.Sprintf(format, args...))
+}
+
+func isLetter(r rune) bool { return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '_' }
+func isNumber(r rune) bool { return '0' <= r && r <= '9' }
+
+type parsePanic struct {
+ err error
+}
+
+func (pp parsePanic) String() string { return pp.err.Error() }
diff --git a/src/internal/fuchsia/abigen/types.go b/src/internal/fuchsia/abigen/types.go
new file mode 100644
index 0000000..9783968
--- /dev/null
+++ b/src/internal/fuchsia/abigen/types.go
@@ -0,0 +1,249 @@
+package abigen
+
+import "fmt"
+
+// SysType represents the type of an argument or vDSO-exported syscall function
+// return value.
+type SysType struct {
+ Name SysTypeName // Name represents the name of the type.
+ Array bool // Array is true when the type represents an array.
+ ArraySize string // When Array is true, ArraySize represents the size of the array.
+}
+
+// SysTypeName represents the name of a SysType.
+type SysTypeName int
+
+const (
+ zxVoid SysTypeName = iota
+ zxAny
+ zxBool
+ zxUint
+ zxInt
+ zxChar
+ zxUint8
+ zxInt16
+ zxUint16
+ zxInt32
+ zxUint32
+ zxInt64
+ zxUint64
+ zxUintptr
+ zxSize
+ zxStatus
+ zxClock
+ zxTime
+ zxDuration
+ zxKOID
+ zxHandle
+ zxSignals
+ zxSignalsState
+ zxSMCParameters
+ zxSMCResult
+ zxWaitItem
+ zxRights
+ zxVAddr
+ zxPAddr
+ zxProcessInfo
+ zxExceptionStatus
+ zxExceptionBehaviour
+ zxFunctionPtr
+ zxRRec
+ zxChannelCallArgs
+ zxFIFOState
+ zxVMOption
+)
+
+var sysTypeNames = map[string]SysTypeName{
+ "any": zxAny,
+ "void": zxVoid,
+ "bool": zxBool,
+ "unsigned int": zxUint,
+ "int": zxInt,
+ "char": zxChar,
+ "uint8_t": zxUint8,
+ "int16_t": zxInt16,
+ "int32_t": zxInt32,
+ "int64_t": zxInt64,
+ "uint16_t": zxUint16,
+ "uint32_t": zxUint32,
+ "uint64_t": zxUint64,
+ "uintptr_t": zxUintptr,
+ "size_t": zxSize,
+ "zx_status_t": zxStatus, // int32_t
+ "zx_clock_t": zxUint32,
+ "zx_time_t": zxTime, // uint64_t monotonic time
+ "zx_duration_t": zxDuration, // uint64_t nanoseconds
+ "zx_koid_t": zxKOID,
+ "zx_handle_t": zxHandle, // int32_t
+ "zx_futex_t": zxInt,
+ "zx_signals_t": zxSignals,
+ "zx_signals_state_t": zxSignalsState,
+ "zx_wait_item_t": zxWaitItem,
+ "zx_rights_t": zxRights,
+ "zx_vaddr_t": zxVAddr,
+ "zx_paddr_t": zxPAddr,
+ "zx_smc_parameters_t": zxSMCParameters,
+ "zx_smc_result_t": zxSMCResult,
+ "zx_process_info_t": zxProcessInfo,
+ "zx_exception_status_t": zxExceptionStatus,
+ "zx_exception_behaviour_t": zxExceptionBehaviour,
+ "zx_function_pointer": zxFunctionPtr,
+ "zx_rrec_t": zxRRec,
+ "zx_channel_call_args_t": zxChannelCallArgs,
+ "zx_fifo_state_t": zxFIFOState,
+ "zx_vm_option_t": zxVMOption,
+ // TODO(dhobsd): Remove the following temporary types. They could be blacklisted, but this is easier.
+ "zx_cache_policy_t": zxInt,
+ "zx_port_packet_t": zxInt,
+ "zx_pci_init_arg_t": zxInt,
+ "zx_pci_irq_mode_t": zxInt,
+ "zx_pci_resource_t": zxInt,
+ "zx_pcie_device_info_t": zxInt,
+ "zx_pcie_get_nth_info_t": zxInt,
+ "zx_vcpu_create_args_t": zxInt,
+ "zx_system_powerctl_arg_t": zxInt,
+ "zx_handle_info_t": zxInt,
+ "zx_profile_info_t": zxInt,
+ "zx_pci_bar_t": zxInt,
+}
+
+// Size returns the size (in bytes) of the type. Its argument represents the size of the register
+// on the machine for which the type is generated.
+func (t SysType) Size() int {
+ if t.Array {
+ return 8
+ }
+ switch t.Name {
+ case zxFunctionPtr:
+ return 8
+ case zxVoid:
+ return 0
+ case zxAny:
+ return 0
+ case zxBool:
+ return 1
+ case zxInt, zxUint, zxUintptr, zxSize:
+ return 8
+ case zxChar, zxUint8:
+ return 1
+ case zxInt16, zxUint16:
+ return 2
+ case zxInt32, zxUint32:
+ return 4
+ case zxInt64, zxUint64:
+ return 8
+ case zxStatus:
+ return 4
+ case zxClock:
+ return 4
+ case zxTime:
+ return 8
+ case zxDuration:
+ return 8
+ case zxKOID:
+ return 8
+ case zxHandle:
+ return 4
+ case zxSignals:
+ return 4
+ case zxRights:
+ return 4
+ case zxVMOption:
+ return 4
+ case zxVAddr, zxPAddr:
+ return 8
+ case zxProcessInfo:
+ panic("cannot pass zxprocess_info")
+ case zxExceptionStatus, zxExceptionBehaviour:
+ return 4
+ default:
+ panic(fmt.Sprintf("UNKNOWN(%s)", t.Name))
+ }
+}
+
+func (t SysType) goType() string {
+ switch t.Name {
+ case zxVoid:
+ return "BAD(void)"
+ case zxAny:
+ return "BAD(any)"
+ case zxBool:
+ return "bool"
+ case zxUint:
+ return "uint"
+ case zxInt:
+ return "int"
+ case zxChar, zxUint8:
+ return "uint8"
+ case zxInt16:
+ return "int16"
+ case zxUint16:
+ return "uint16"
+ case zxInt32:
+ return "int32"
+ case zxUint32:
+ return "uint32"
+ case zxInt64:
+ return "int64"
+ case zxUint64:
+ return "uint64"
+ case zxUintptr:
+ return "uintptr"
+ case zxStatus:
+ return "Status"
+ case zxClock:
+ return "Clock"
+ case zxTime:
+ return "Time"
+ case zxDuration:
+ return "Duration"
+ case zxKOID:
+ return "Koid"
+ case zxHandle:
+ return "Handle"
+ case zxSignals:
+ return "Signals"
+ case zxWaitItem:
+ return "WaitItem"
+ case zxRights:
+ return "Rights"
+ case zxVAddr:
+ return "Vaddr"
+ case zxPAddr:
+ return "Paddr"
+ case zxSize:
+ return "uint"
+ case zxProcessInfo:
+ return "ProcessInfo"
+ case zxExceptionStatus:
+ return "ExceptionStatus"
+ case zxExceptionBehaviour:
+ return "ExceptionBehaviour"
+ case zxRRec:
+ return "Rrec"
+ case zxChannelCallArgs:
+ return "ChannelCallArgs"
+ case zxFIFOState:
+ return "FIFOState"
+ case zxSMCParameters:
+ return "SMCParameters"
+ case zxSMCResult:
+ return "SMCResult"
+ case zxVMOption:
+ return "VMFlag"
+ default:
+ panic(fmt.Sprintf("UNKNOWN(%s)", t.Name))
+ }
+}
+
+// GoType returns the Go type name for the corresponding SysType.
+func (t SysType) GoType() string {
+ if t.Array {
+ if t.Name == zxAny {
+ return "unsafe.Pointer"
+ }
+ return fmt.Sprintf("*%s", t.goType())
+ }
+
+ return t.goType()
+}
diff --git a/src/syscall/zx/mkfuchsia.go b/src/syscall/zx/mkfuchsia.go
index cdd727f..cb8d33b 100644
--- a/src/syscall/zx/mkfuchsia.go
+++ b/src/syscall/zx/mkfuchsia.go
@@ -23,8 +23,8 @@
"log"
"os"
"path/filepath"
- "strings"
- "unicode/utf8"
+
+ "internal/fuchsia/abigen"
)
var (
@@ -56,12 +56,8 @@
log.Fatal(err)
}
- p := &parser{
- buf: b,
- line: 1,
- name: syscallsFile,
- }
- syscalls, err := p.parse()
+ p := abigen.NewParser(b, syscallsFile)
+ defs, err := p.Parse()
if err != nil {
log.Fatal(err)
}
@@ -69,30 +65,30 @@
buf := new(bytes.Buffer)
if *stubs {
fmt.Fprint(buf, stubsHeader[1:])
- for _, call := range syscalls {
- fmt.Fprintf(buf, "//go:cgo_import_dynamic vdso_zx_%s zx_%s\n", call.name, call.name)
+ for _, def := range defs {
+ fmt.Fprintf(buf, "//go:cgo_import_dynamic vdso_zx_%s zx_%s\n", def.Name, def.Name)
}
fmt.Fprint(buf, "\n")
- for _, call := range syscalls {
- fmt.Fprintf(buf, "//go:linkname vdso_zx_%s vdso_zx_%s\n", call.name, call.name)
+ for _, def := range defs {
+ fmt.Fprintf(buf, "//go:linkname vdso_zx_%s vdso_zx_%s\n", def.Name, def.Name)
}
fmt.Fprint(buf, "\nvar (\n")
- for _, call := range syscalls {
- fmt.Fprintf(buf, "\tvdso_zx_%s uintptr\n", call.name)
+ for _, def := range defs {
+ fmt.Fprintf(buf, "\tvdso_zx_%s uintptr\n", def.Name)
}
fmt.Fprint(buf, ")\n\n")
- for _, call := range syscalls {
+ for _, def := range defs {
fmt.Fprint(buf, "//go:noescape\n")
fmt.Fprint(buf, "//go:nosplit\n")
- printStub(buf, call)
+ printStub(buf, def)
fmt.Fprint(buf, "\n")
}
} else {
fmt.Fprint(buf, asmHeader[1:])
- for _, call := range syscalls {
+ for _, def := range defs {
fmt.Fprint(buf, "// ")
- printStub(buf, call)
- printAsm(buf, call)
+ printStub(buf, def)
+ printAsm(buf, def)
fmt.Fprint(buf, "\n")
}
}
@@ -125,22 +121,22 @@
`
-func printStub(buf *bytes.Buffer, call zirconSyscallDef) {
- fmt.Fprintf(buf, "func %s(", call.sysname())
- for i, arg := range call.args {
- if arg.atype == (zirconType{}) {
+func printStub(buf *bytes.Buffer, def abigen.SysDef) {
+ fmt.Fprintf(buf, "func %s(", def.GoName())
+ for i, arg := range def.Args {
+ if arg.Type == (abigen.SysType{}) {
continue
}
if i > 0 {
fmt.Fprint(buf, ", ")
}
- fmt.Fprintf(buf, "%s ", arg.aname)
- printGoType(buf, arg.atype)
+ fmt.Fprintf(buf, "%s ", arg.Name)
+ fmt.Fprintf(buf, "%s", arg.Type.GoType())
}
fmt.Fprint(buf, ")")
- if call.ret != (zirconType{}) {
+ if def.Ret != (abigen.SysType{}) {
fmt.Fprint(buf, " ")
- printGoType(buf, call.ret)
+ fmt.Fprintf(buf, "%s", def.Ret.GoType())
}
fmt.Fprint(buf, "\n")
}
@@ -158,13 +154,11 @@
"object_wait_many": true,
}
-const ZIRCON_SYSCALL_MAGIC = 0x00ff00ff00000000
-
-func printAsm(buf *bytes.Buffer, call zirconSyscallDef) {
+func printAsm(buf *bytes.Buffer, def abigen.SysDef) {
// The summed size of all arguments
argSize := 0
- for _, arg := range call.args {
- sz := typeSize(arg.atype)
+ for _, arg := range def.Args {
+ sz := arg.Type.Size()
if *goarch == "arm64" && sz == 1 {
sz = 8
}
@@ -178,7 +172,7 @@
// Force the return argument on the stack to be 8-byte aligned, not 4-byte aligned
argSize += 4
}
- retSize := typeSize(call.ret)
+ retSize := def.Ret.Size()
var frameSize int
var regArgs []string
@@ -190,7 +184,7 @@
retReg = "AX"
suffix8 = "Q"
suffix4 = "L"
- switch len(call.args) {
+ switch len(def.Args) {
case 7:
frameSize = 16 + 8
case 8:
@@ -206,21 +200,20 @@
panic(fmt.Sprintf("goarch=%s not supported", *goarch))
}
- fmt.Fprintf(buf, "TEXT ·%s(SB),NOSPLIT,$%d-%d\n", call.sysname(), frameSize, argSize+retSize)
+ fmt.Fprintf(buf, "TEXT ·%s(SB),NOSPLIT,$%d-%d\n", def.GoName(), frameSize, argSize+retSize)
- if _, ok := blockingSyscalls[call.name]; ok {
+ if _, ok := blockingSyscalls[def.Name]; ok {
fmt.Fprintf(buf, "\tCALL runtime·entersyscall(SB)\n")
}
off := 0
- for i, arg := range call.args {
- name := arg.aname
+ for i, arg := range def.Args {
+ name := arg.Name
suffix := suffix8
- t := arg.atype
- if typeSize(t) == 4 {
+ t := arg.Type
+ sz := t.Size()
+ if sz == 4 {
suffix = suffix4
- }
- sz := typeSize(t)
- if *goarch == "arm64" && sz == 1 {
+ } else if *goarch == "arm64" && sz == 1 {
sz = 8
}
for off%sz != 0 {
@@ -233,651 +226,35 @@
}
switch *goarch {
case "amd64":
- if len(call.args) >= 7 {
+ if len(def.Args) >= 7 {
fmt.Fprintf(buf, "\tMOVQ SP, BP // BP is preserved across vsdo call by the x86-64 ABI\n")
fmt.Fprintf(buf, "\tANDQ $~15, SP // stack alignment for x86-64 ABI\n")
- if len(call.args) == 8 {
+ if len(def.Args) == 8 {
fmt.Fprintf(buf, "\tPUSHQ R13\n")
}
fmt.Fprintf(buf, "\tPUSHQ R12\n")
}
- fmt.Fprintf(buf, "\tMOVQ vdso_zx_%s(SB), AX\n", call.name)
+ fmt.Fprintf(buf, "\tMOVQ vdso_zx_%s(SB), AX\n", def.Name)
fmt.Fprintf(buf, "\tCALL AX\n")
- if len(call.args) >= 7 {
+ if len(def.Args) >= 7 {
fmt.Fprintf(buf, "\tPOPQ R12\n")
- if len(call.args) == 8 {
+ if len(def.Args) == 8 {
fmt.Fprintf(buf, "\tPOPQ R13\n")
}
fmt.Fprintf(buf, "\tMOVQ BP, SP\n")
}
case "arm64":
- fmt.Fprintf(buf, "\tBL vdso_zx_%s(SB)\n", call.name)
+ fmt.Fprintf(buf, "\tBL vdso_zx_%s(SB)\n", def.Name)
}
- if retSize := typeSize(call.ret); retSize > 0 {
+ if retSize := def.Ret.Size(); retSize > 0 {
suffix := suffix8
if retSize == 4 {
suffix = suffix4
}
fmt.Fprintf(buf, "\tMOV%s %s, ret+%d(FP)\n", suffix, retReg, argSize)
}
- if _, ok := blockingSyscalls[call.name]; ok {
+ if _, ok := blockingSyscalls[def.Name]; ok {
fmt.Fprintf(buf, "\t%s runtime·exitsyscall(SB)\n", callIns)
}
fmt.Fprintf(buf, "\tRET\n")
}
-
-func typeSize(t zirconType) int {
- regSize := 8
- if *goarch == "arm" {
- regSize = 4
- }
- if t.isArray {
- return regSize
- }
- switch t.named {
- case zx_function_pointer:
- return regSize
- case zx_void:
- return 0
- case zx_any:
- return 0
- case zx_bool:
- return 1
- case zx_int, zx_uint, zx_uintptr_t, zx_size_t:
- return regSize
- case zx_char, zx_uint8_t:
- return 1
- case zx_int16_t, zx_uint16_t:
- return 2
- case zx_int32_t, zx_uint32_t:
- return 4
- case zx_int64_t, zx_uint64_t:
- return 8
- case zx_status_t:
- return 4
- case zx_clock_t:
- return 4
- case zx_time_t:
- return 8
- case zx_duration_t:
- return 8
- case zx_koid_t:
- return 8
- case zx_handle_t:
- return 4
- case zx_signals_t:
- return 4
- case zx_rights_t:
- return 4
- case zx_vm_option_t:
- return 4
- case zx_vaddr_t, zx_paddr_t:
- return regSize
- case zx_process_info_t:
- panic("cannot pass zx_process_info_t")
- case zx_exception_status_t, zx_exception_behaviour_t:
- return 4
- default:
- panic(fmt.Sprintf("UNKNOWN(%s)", t.named))
- }
-}
-
-func printGoType(buf *bytes.Buffer, t zirconType) {
- if t.isArray {
- if t.named == zx_any {
- fmt.Fprint(buf, "unsafe.Pointer")
- return
- }
- fmt.Fprint(buf, "*")
- }
- switch t.named {
- case zx_void:
- fmt.Fprint(buf, "BAD(void)")
- case zx_any:
- fmt.Fprint(buf, "BAD(any)")
- case zx_bool:
- fmt.Fprint(buf, "bool")
- case zx_uint:
- fmt.Fprint(buf, "uint")
- case zx_int:
- fmt.Fprint(buf, "int")
- case zx_char, zx_uint8_t:
- fmt.Fprint(buf, "uint8")
- case zx_int16_t:
- fmt.Fprint(buf, "int16")
- case zx_uint16_t:
- fmt.Fprint(buf, "uint16")
- case zx_int32_t:
- fmt.Fprint(buf, "int32")
- case zx_uint32_t:
- fmt.Fprint(buf, "uint32")
- case zx_int64_t:
- fmt.Fprint(buf, "int64")
- case zx_uint64_t:
- fmt.Fprint(buf, "uint64")
- case zx_uintptr_t:
- fmt.Fprint(buf, "uintptr")
- case zx_status_t:
- fmt.Fprint(buf, "Status")
- case zx_clock_t:
- fmt.Fprint(buf, "Clock")
- case zx_time_t:
- fmt.Fprint(buf, "Time")
- case zx_duration_t:
- fmt.Fprint(buf, "Duration")
- case zx_koid_t:
- fmt.Fprint(buf, "Koid")
- case zx_handle_t:
- fmt.Fprint(buf, "Handle")
- case zx_signals_t:
- fmt.Fprint(buf, "Signals")
- case zx_wait_item_t:
- fmt.Fprint(buf, "WaitItem")
- case zx_rights_t:
- fmt.Fprint(buf, "Rights")
- case zx_vaddr_t:
- fmt.Fprint(buf, "Vaddr")
- case zx_paddr_t:
- fmt.Fprint(buf, "Paddr")
- case zx_size_t:
- fmt.Fprint(buf, "uint")
- case zx_process_info_t:
- fmt.Fprint(buf, "ProcessInfo")
- case zx_exception_status_t:
- fmt.Fprint(buf, "ExceptionStatus")
- case zx_exception_behaviour_t:
- fmt.Fprint(buf, "ExceptionBehaviour")
- case zx_rrec_t:
- fmt.Fprint(buf, "Rrec")
- case zx_channel_call_args_t:
- fmt.Fprint(buf, "ChannelCallArgs")
- case zx_fifo_state_t:
- fmt.Fprint(buf, "FIFOState")
- case zx_smc_parameters_t:
- fmt.Fprint(buf, "SMCParameters")
- case zx_smc_result_t:
- fmt.Fprint(buf, "SMCResult")
- case zx_vm_option_t:
- fmt.Fprint(buf, "VMFlag")
- default:
- panic(fmt.Sprintf("UNKNOWN type: %s", t.named))
- }
-}
-
-func (p *parser) parse() (syscalls []zirconSyscallDef, err error) {
- if len(p.buf) == 0 {
- return syscalls, nil
- }
-
- p.nextRune()
- for {
- t, lit := p.nextToken()
- if lit == "syscall" {
- call, err := p.parseSyscall()
- if err != nil {
- return nil, err
- }
- if strings.Contains(call.name, "_test") {
- continue
- }
- // The PCI syscalls are DDK-only and temporary, so exclude generation of
- // helpers for them
- if strings.HasPrefix(call.name, "pci_") {
- continue
- }
- // The hypervisor syscalls have complex parameter and return types. Skip
- // generation for these until we need them.
- if strings.HasPrefix(call.name, "guest_") || strings.HasPrefix(call.name, "vcpu_") {
- continue
- }
- syscalls = append(syscalls, call)
- continue
- }
- if t == tokenEOF {
- return syscalls, nil
- }
- if t == tokenBad {
- return nil, p.errorf("bad token: %s", lit)
- }
- return nil, p.errorf("unexpected token: %q (literal %q)", t, lit)
- }
-}
-
-func (p *parser) expect(want token) {
- t, lit := p.nextToken()
- if t != want {
- p.panicf("expected %q but got %q (literal %q)", want, t, lit)
- }
-}
-
-func (p *parser) parseAttributes() (attr []string, err error) {
- for {
- p.skipWhitespace()
- if p.r == '(' {
- return
- }
- attr = append(attr, p.nextIdent())
- p.skipWhitespace()
- if p.r == ',' {
- p.nextRune()
- }
- }
-}
-
-func (p *parser) parseArg() (arg zirconArg, err error) {
- if p.r == ')' {
- return
- }
- arg.aname = p.nextIdent()
- p.expect(':')
- atype, err := p.parseType()
- if err != nil {
- return
- }
- arg.atype = atype
- p.skipWhitespace()
- if isLetter(p.r) {
- arg.constraint = p.nextIdent()
- p.skipWhitespace()
- }
- return
-}
-
-func (p *parser) parseSyscall() (call zirconSyscallDef, err error) {
- call.name = p.nextIdent()
- attr, err := p.parseAttributes()
- if err != nil {
- return zirconSyscallDef{}, err
- }
- for _, a := range attr {
- if a == "vdsocall" {
- call.vdsocall = true
- }
- }
- call.attr = attr
- p.expect('(')
- for {
- p.skipWhitespace()
- if p.r == ')' {
- p.nextRune()
- break
- }
- arg, err := p.parseArg()
- if err != nil {
- return zirconSyscallDef{}, err
- }
- if arg.aname == "" {
- break
- }
- call.args = append(call.args, arg)
- if p.r == ',' {
- p.nextRune()
- }
- }
- p.skipWhitespace()
- if p.r != ';' {
- kw := p.nextIdent()
- if kw != "returns" {
- p.panicf("expected \"returns\" but got \"%s\"", kw)
- }
- p.expect('(')
- first := true
- for {
- p.skipWhitespace()
- if p.r == ')' {
- p.nextRune()
- break
- }
- if first {
- first = false
- rtype, err := p.parseType()
- if err != nil {
- return zirconSyscallDef{}, err
- }
- call.ret = rtype
- } else {
- arg, err := p.parseArg()
- if err != nil {
- return zirconSyscallDef{}, err
- }
- // Extra return types are returned as pointers.
- arg.atype.isArray = true
- arg.atype.arraySize = "1"
- call.args = append(call.args, arg)
- }
- if p.r == ',' {
- p.nextRune()
- }
- }
- }
- p.skipWhitespace()
- p.expect(';')
- return call, nil
-}
-
-func (p *parser) parseType() (t zirconType, err error) {
- ident := p.nextIdent()
- n, ok := zirconTypeNamedStr[ident]
- if !ok {
- return t, p.errorf("unknown type name: %s", ident)
- }
- t.named = n
- p.skipWhitespace()
- if p.r == '[' {
- p.nextRune()
- t.isArray = true
- t.arraySize = p.nextIdent()
- p.skipWhitespace()
- for p.r == '*' {
- p.nextRune()
- t.arraySize += " * " + p.nextIdent()
- }
- p.expect(']')
- }
- return t, nil
-}
-
-type parser struct {
- buf []byte
- off int
- line, col int
- name string
-
- r rune
-}
-
-type token rune
-
-const (
- tokenEOF token = -1
- tokenBad token = -2
- tokenIdent token = -3
- tokenNumber token = -4
-)
-
-func (t token) String() string {
- switch t {
- case tokenEOF:
- return "token(EOF)"
- case tokenBad:
- return "token(BAD)"
- case tokenIdent:
- return "token(Ident)"
- case tokenNumber:
- return "token(Number)"
- default:
- return "token(" + string(t) + ")"
- }
-}
-
-func (p *parser) nextToken() (t token, lit string) {
- p.skipWhitespace()
-
- if isLetter(p.r) {
- lit = p.nextIdent()
- return tokenIdent, lit
- }
- if isNumber(p.r) {
- lit = p.nextNumber()
- return tokenNumber, lit
- }
-
- r := p.r
- p.nextRune()
- switch r {
- case -1:
- return tokenEOF, ""
- case '(', ')', '*', ',', ';', ':', '[', ']':
- return token(r), ""
- case '#':
- p.skipComment()
- return p.nextToken()
- }
- return tokenBad, fmt.Sprintf("%q", r)
-}
-
-func (p *parser) nextIdent() string {
- p.skipWhitespace()
- if !isLetter(p.r) && !isNumber(p.r) {
- p.panicf("expected ident but got %q", p.r)
- }
- off := p.off - 1
- for isLetter(p.r) || isNumber(p.r) {
- p.nextRune()
- }
- r := string(p.buf[off : p.off-1])
- if r == "type" {
- // "type" is a reserved word in Go.
- return "typ"
- }
- return r
-}
-
-func (p *parser) nextNumber() string {
- p.skipWhitespace()
- if !isNumber(p.r) {
- p.panicf("expected number but got %q", p.r)
- }
- off := p.off - 1
- for isNumber(p.r) {
- p.nextRune()
- }
- return string(p.buf[off : p.off-1])
-}
-
-func (p *parser) skipWhitespace() {
- for p.r == ' ' || p.r == '\t' || p.r == '\n' {
- p.nextRune()
- }
-}
-
-func (p *parser) skipComment() {
- // skip until end of line
- p.nextRune()
- for p.r != '\n' && p.r >= 0 {
- p.nextRune()
- }
-}
-
-func (p *parser) nextRune() {
- if p.off >= len(p.buf) {
- p.r = -1
- return
- }
- if p.r == '\n' {
- p.line++
- p.col = 0
- } else {
- p.col += utf8.RuneLen(p.r)
- }
-
- r, w := utf8.DecodeRune(p.buf[p.off:])
- if r == utf8.RuneError && w == 1 {
- p.r = -1
- p.panicf("input is not UTF-8")
- return
- }
- p.r = r
- p.off += w
-}
-
-func (p *parser) panicf(format string, args ...interface{}) {
- panic(parsePanic{err: p.errorf(format, args...)})
-}
-
-func (p *parser) errorf(format string, args ...interface{}) error {
- return fmt.Errorf("%s:%d:%d: %s", p.name, p.line, p.col, fmt.Sprintf(format, args...))
-}
-
-func isLetter(r rune) bool { return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '_' }
-func isNumber(r rune) bool { return '0' <= r && r <= '9' }
-
-type parsePanic struct {
- err error
-}
-
-func (pp parsePanic) String() string { return pp.err.Error() }
-
-type zirconArg struct {
- aname string
- atype zirconType
- constraint string
-}
-
-type zirconSyscallDef struct {
- name string
- ret zirconType
- attr []string
- args []zirconArg
- vdsocall bool
-}
-
-func (call zirconSyscallDef) sysname() string {
- if call.exported() {
- return "Sys_" + call.name
- }
- return "sys_" + call.name
-}
-
-func (call zirconSyscallDef) exported() bool {
- switch call.name {
- case "handle_duplicate",
- "object_set_profile",
- "object_wait_many",
- "channel_create",
- "channel_read",
- "channel_read_etc",
- "channel_write",
- "pci_get_bar",
- "pci_map_interrupt",
- "pci_query_irq_mode",
- "pci_set_irq_mode",
- "pci_init",
- "pci_add_subtract_to_range",
- "socket_create",
- "socket_read",
- "socket_write",
- "socket_accept",
- "socket_share",
- "system_get_dcache_line_size",
- "system_get_features",
- "vmar_allocate",
- "vmar_allocate_old",
- "vmar_map_old",
- "vmar_protect_old",
- "vmar_destroy",
- "vmo_read",
- "vmo_write",
- "vmo_get_size",
- "vmo_set_size",
- "vmo_op_range",
- "vmo_replace_as_executable":
-
- return false
- default:
- return true
- }
-}
-
-type zirconType struct {
- named zirconTypeNamed
- isArray bool
- arraySize string
-}
-
-type zirconTypeNamed int
-
-const (
- zx_void zirconTypeNamed = iota
- zx_any
- zx_bool
- zx_uint
- zx_int
- zx_char
- zx_uint8_t
- zx_int16_t
- zx_uint16_t
- zx_int32_t
- zx_uint32_t
- zx_int64_t
- zx_uint64_t
- zx_uintptr_t
- zx_size_t
- zx_status_t
- zx_clock_t
- zx_time_t
- zx_duration_t
- zx_koid_t
- zx_handle_t
- zx_signals_t
- zx_signals_state_t
- zx_smc_parameters_t
- zx_smc_result_t
- zx_wait_item_t
- zx_rights_t
- zx_vaddr_t
- zx_paddr_t
- zx_process_info_t
- zx_exception_status_t
- zx_exception_behaviour_t
- zx_function_pointer
- zx_rrec_t
- zx_channel_call_args_t
- zx_fifo_state_t
- zx_vm_option_t
-)
-
-var zirconTypeNamedStr = map[string]zirconTypeNamed{
- "any": zx_any,
- "void": zx_void,
- "bool": zx_bool,
- "unsigned int": zx_uint,
- "int": zx_int,
- "char": zx_char,
- "uint8_t": zx_uint8_t,
- "int16_t": zx_int16_t,
- "int32_t": zx_int32_t,
- "int64_t": zx_int64_t,
- "uint16_t": zx_uint16_t,
- "uint32_t": zx_uint32_t,
- "uint64_t": zx_uint64_t,
- "uintptr_t": zx_uintptr_t,
- "size_t": zx_size_t,
- "zx_status_t": zx_status_t, // int32_t
- "zx_clock_t": zx_uint32_t,
- "zx_time_t": zx_time_t, // uint64_t monotonic time
- "zx_duration_t": zx_duration_t, // uint64_t nanoseconds
- "zx_koid_t": zx_koid_t,
- "zx_handle_t": zx_handle_t, // int32_t
- "zx_futex_t": zx_int,
- "zx_signals_t": zx_signals_t,
- "zx_signals_state_t": zx_signals_state_t,
- "zx_wait_item_t": zx_wait_item_t,
- "zx_rights_t": zx_rights_t,
- "zx_vaddr_t": zx_vaddr_t,
- "zx_paddr_t": zx_paddr_t,
- "zx_smc_parameters_t": zx_smc_parameters_t,
- "zx_smc_result_t": zx_smc_result_t,
- "zx_process_info_t": zx_process_info_t,
- "zx_exception_status_t": zx_exception_status_t,
- "zx_exception_behaviour_t": zx_exception_behaviour_t,
- "zx_function_pointer": zx_function_pointer,
- "zx_rrec_t": zx_rrec_t,
- "zx_channel_call_args_t": zx_channel_call_args_t,
- "zx_fifo_state_t": zx_fifo_state_t,
- "zx_vm_option_t": zx_vm_option_t,
- // TODO(smklein): Remove the following temporary types. They could be blacklisted, but this is
- // easier.
- "zx_cache_policy_t": zx_int,
- "zx_port_packet_t": zx_int,
- "zx_pci_init_arg_t": zx_int,
- "zx_pci_irq_mode_t": zx_int,
- "zx_pci_resource_t": zx_int,
- "zx_pcie_device_info_t": zx_int,
- "zx_pcie_get_nth_info_t": zx_int,
- "zx_vcpu_create_args_t": zx_int,
- "zx_system_powerctl_arg_t": zx_int,
- "zx_handle_info_t": zx_int,
- "zx_profile_info_t": zx_int,
- "zx_pci_bar_t": zx_int,
-}