blob: cd8479ef55145bee54e718483fa437a08af58507 [file] [log] [blame]
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Only build this file if libffi is supported.
// +build libffi
package runtime
import "unsafe"
// This file contains the code that converts a Go type to an FFI type.
// This has to be written in Go because it allocates memory in the Go heap.
// C functions to return pointers to libffi variables.
func ffi_type_pointer() *__ffi_type
func ffi_type_sint8() *__ffi_type
func ffi_type_sint16() *__ffi_type
func ffi_type_sint32() *__ffi_type
func ffi_type_sint64() *__ffi_type
func ffi_type_uint8() *__ffi_type
func ffi_type_uint16() *__ffi_type
func ffi_type_uint32() *__ffi_type
func ffi_type_uint64() *__ffi_type
func ffi_type_float() *__ffi_type
func ffi_type_double() *__ffi_type
func ffi_supports_complex() bool
func ffi_type_complex_float() *__ffi_type
func ffi_type_complex_double() *__ffi_type
func ffi_type_void() *__ffi_type
// C functions defined in libffi.
//extern ffi_prep_cif
func ffi_prep_cif(*_ffi_cif, _ffi_abi, uint32, *__ffi_type, **__ffi_type) _ffi_status
// ffiFuncToCIF is called from C code.
//go:linkname ffiFuncToCIF
// ffiFuncToCIF builds an _ffi_cif struct for function described by ft.
func ffiFuncToCIF(ft *functype, isInterface bool, isMethod bool, cif *_ffi_cif) {
nparams := len(ft.in)
nargs := nparams
if isInterface {
nargs++
}
args := make([]*__ffi_type, nargs)
i := 0
off := 0
if isInterface {
args[0] = ffi_type_pointer()
off = 1
} else if isMethod {
args[0] = ffi_type_pointer()
i = 1
}
for ; i < nparams; i++ {
args[i+off] = typeToFFI(ft.in[i])
}
rettype := funcReturnFFI(ft)
var pargs **__ffi_type
if len(args) > 0 {
pargs = &args[0]
}
status := ffi_prep_cif(cif, _FFI_DEFAULT_ABI, uint32(nargs), rettype, pargs)
if status != _FFI_OK {
throw("ffi_prep_cif failed")
}
}
// funcReturnFFI returns the FFI definition of the return type of ft.
func funcReturnFFI(ft *functype) *__ffi_type {
c := len(ft.out)
if c == 0 {
return ffi_type_void()
}
// Compile a function that returns a zero-sized value as
// though it returns void. This works around a problem in
// libffi: it can't represent a zero-sized value.
var size uintptr
for _, v := range ft.out {
size += v.size
}
if size == 0 {
return ffi_type_void()
}
if c == 1 {
return typeToFFI(ft.out[0])
}
elements := make([]*__ffi_type, c+1)
for i, v := range ft.out {
elements[i] = typeToFFI(v)
}
elements[c] = nil
return &__ffi_type{
_type: _FFI_TYPE_STRUCT,
elements: &elements[0],
}
}
// typeToFFI returns the __ffi_type for a Go type.
func typeToFFI(typ *_type) *__ffi_type {
switch typ.kind & kindMask {
case kindBool:
switch unsafe.Sizeof(false) {
case 1:
return ffi_type_uint8()
case 4:
return ffi_type_uint32()
default:
throw("bad bool size")
return nil
}
case kindInt:
return intToFFI()
case kindInt8:
return ffi_type_sint8()
case kindInt16:
return ffi_type_sint16()
case kindInt32:
return ffi_type_sint32()
case kindInt64:
return ffi_type_sint64()
case kindUint:
switch unsafe.Sizeof(uint(0)) {
case 4:
return ffi_type_uint32()
case 8:
return ffi_type_uint64()
default:
throw("bad uint size")
return nil
}
case kindUint8:
return ffi_type_uint8()
case kindUint16:
return ffi_type_uint16()
case kindUint32:
return ffi_type_uint32()
case kindUint64:
return ffi_type_uint64()
case kindUintptr:
switch unsafe.Sizeof(uintptr(0)) {
case 4:
return ffi_type_uint32()
case 8:
return ffi_type_uint64()
default:
throw("bad uinptr size")
return nil
}
case kindFloat32:
return ffi_type_float()
case kindFloat64:
return ffi_type_double()
case kindComplex64:
if ffi_supports_complex() {
return ffi_type_complex_float()
} else {
return complexToFFI(ffi_type_float())
}
case kindComplex128:
if ffi_supports_complex() {
return ffi_type_complex_double()
} else {
return complexToFFI(ffi_type_double())
}
case kindArray:
return arrayToFFI((*arraytype)(unsafe.Pointer(typ)))
case kindChan, kindFunc, kindMap, kindPtr, kindUnsafePointer:
// These types are always simple pointers, and for FFI
// purposes nothing else matters.
return ffi_type_pointer()
case kindInterface:
return interfaceToFFI()
case kindSlice:
return sliceToFFI((*slicetype)(unsafe.Pointer(typ)))
case kindString:
return stringToFFI()
case kindStruct:
return structToFFI((*structtype)(unsafe.Pointer(typ)))
default:
throw("unknown type kind")
return nil
}
}
// interfaceToFFI returns an ffi_type for a Go interface type.
// This is used for both empty and non-empty interface types.
func interfaceToFFI() *__ffi_type {
elements := make([]*__ffi_type, 3)
elements[0] = ffi_type_pointer()
elements[1] = elements[0]
elements[2] = nil
return &__ffi_type{
_type: _FFI_TYPE_STRUCT,
elements: &elements[0],
}
}
// stringToFFI returns an ffi_type for a Go string type.
func stringToFFI() *__ffi_type {
elements := make([]*__ffi_type, 3)
elements[0] = ffi_type_pointer()
elements[1] = intToFFI()
elements[2] = nil
return &__ffi_type{
_type: _FFI_TYPE_STRUCT,
elements: &elements[0],
}
}
// structToFFI returns an ffi_type for a Go struct type.
func structToFFI(typ *structtype) *__ffi_type {
c := len(typ.fields)
if c == 0 {
return emptyStructToFFI()
}
if typ.typ.kind&kindDirectIface != 0 {
return ffi_type_pointer()
}
fields := make([]*__ffi_type, 0, c+1)
checkPad := false
lastzero := false
for i, v := range typ.fields {
// Skip zero-sized fields; they confuse libffi,
// and there is no value to pass in any case.
// We do have to check whether the alignment of the
// zero-sized field introduces any padding for the
// next field.
if v.typ.size == 0 {
checkPad = true
lastzero = true
continue
}
lastzero = false
if checkPad {
off := uintptr(0)
for j := i - 1; j >= 0; j-- {
if typ.fields[j].typ.size > 0 {
off = typ.fields[j].offset() + typ.fields[j].typ.size
break
}
}
off += uintptr(v.typ.align) - 1
off &^= uintptr(v.typ.align) - 1
if off != v.offset() {
fields = append(fields, padFFI(v.offset()-off))
}
checkPad = false
}
fields = append(fields, typeToFFI(v.typ))
}
if lastzero {
// The compiler adds one byte padding to non-empty struct ending
// with a zero-sized field (types.cc:get_backend_struct_fields).
// Add this padding to the FFI type.
fields = append(fields, ffi_type_uint8())
}
fields = append(fields, nil)
return &__ffi_type{
_type: _FFI_TYPE_STRUCT,
elements: &fields[0],
}
}
// sliceToFFI returns an ffi_type for a Go slice type.
func sliceToFFI(typ *slicetype) *__ffi_type {
elements := make([]*__ffi_type, 4)
elements[0] = ffi_type_pointer()
elements[1] = intToFFI()
elements[2] = elements[1]
elements[3] = nil
return &__ffi_type{
_type: _FFI_TYPE_STRUCT,
elements: &elements[0],
}
}
// complexToFFI returns an ffi_type for a Go complex type.
// This is only used if libffi does not support complex types internally
// for this target.
func complexToFFI(ffiFloatType *__ffi_type) *__ffi_type {
elements := make([]*__ffi_type, 3)
elements[0] = ffiFloatType
elements[1] = ffiFloatType
elements[2] = nil
return &__ffi_type{
_type: _FFI_TYPE_STRUCT,
elements: &elements[0],
}
}
// arrayToFFI returns an ffi_type for a Go array type.
func arrayToFFI(typ *arraytype) *__ffi_type {
if typ.len == 0 {
return emptyStructToFFI()
}
if typ.typ.kind&kindDirectIface != 0 {
return ffi_type_pointer()
}
elements := make([]*__ffi_type, typ.len+1)
et := typeToFFI(typ.elem)
for i := uintptr(0); i < typ.len; i++ {
elements[i] = et
}
elements[typ.len] = nil
return &__ffi_type{
_type: _FFI_TYPE_STRUCT,
elements: &elements[0],
}
}
// intToFFI returns an ffi_type for the Go int type.
func intToFFI() *__ffi_type {
switch unsafe.Sizeof(0) {
case 4:
return ffi_type_sint32()
case 8:
return ffi_type_sint64()
default:
throw("bad int size")
return nil
}
}
// emptyStructToFFI returns an ffi_type for an empty struct.
// The libffi library won't accept a struct with no fields.
func emptyStructToFFI() *__ffi_type {
elements := make([]*__ffi_type, 2)
elements[0] = ffi_type_void()
elements[1] = nil
return &__ffi_type{
_type: _FFI_TYPE_STRUCT,
elements: &elements[0],
}
}
// padFFI returns a padding field of the given size
func padFFI(size uintptr) *__ffi_type {
elements := make([]*__ffi_type, size+1)
for i := uintptr(0); i < size; i++ {
elements[i] = ffi_type_uint8()
}
elements[size] = nil
return &__ffi_type{
_type: _FFI_TYPE_STRUCT,
elements: &elements[0],
}
}
//go:linkname makeCIF reflect.makeCIF
// makeCIF is used by the reflect package to allocate a CIF.
func makeCIF(ft *functype) *_ffi_cif {
cif := new(_ffi_cif)
ffiFuncToCIF(ft, false, false, cif)
return cif
}