b7ec44e82b
CL 157557 changes the compiler to add one byte padding to non-empty struct ending with a zero-sized field. Add the same padding to the FFI type, so reflect.Call works. This fixes test/fixedbugs/issue26335.go in the main repo. Reviewed-on: https://go-review.googlesource.com/c/158018 From-SVN: r267956
368 lines
8.6 KiB
Go
368 lines
8.6 KiB
Go
// 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 runtime.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()
|
|
}
|
|
|
|
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()
|
|
}
|
|
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
|
|
}
|