github.com/notti/go-dynamic@v0.0.0-20190619201224-fc443047424c/internal/ffi/call_386.go (about)

     1  package ffi
     2  
     3  import (
     4  	"unsafe"
     5  )
     6  
     7  // 386 cdecl calling conventions: http://www.sco.com/developers/devspecs/abi386-4.pdf
     8  // Pass everything on the stack in right to left order
     9  // Return is in AX (and DX for 64 bit) or F0 for floats
    10  // according to libffi clang might require the caller to properly (sign)extend stuff - so we do that
    11  // structs are not supported for now (neither as argument nor as return value) - but this is not hard to do
    12  
    13  type argtype uint16
    14  
    15  const (
    16  	type32     argtype = 0 // movl    64 bit
    17  	type16     argtype = 1 // movw    16 bit
    18  	type8      argtype = 2 // movb    8  bit
    19  	typeDouble argtype = 3 // fld     64 bit (only return)
    20  	typeFloat  argtype = 4 // movss   32 bit (only return)
    21  	type64     argtype = 5 // 2x movl        (only return)
    22  	typeUnused argtype = 0xFFFF
    23  )
    24  
    25  type argument struct {
    26  	offset uint16
    27  	t      argtype
    28  }
    29  
    30  // spec a wrapper specifcation with instructions on how to place arguments into registers/stack
    31  type spec struct {
    32  	wrapper uintptr // pointer to callWrapper()
    33  	fn      uintptr // pointer to the C-function
    34  	stack   []argument
    35  	ret     argument
    36  }
    37  
    38  func callWrapper()
    39  
    40  // MakeSpec builds a call specification for the given arguments
    41  func MakeSpec(fn uintptr, fun interface{}) error {
    42  	fptr, arguments, ret, err := stackFields(fun)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	spec := new(spec)
    48  	spec.wrapper = funcPC(callWrapper)
    49  	spec.fn = fn
    50  	spec.ret.t = typeUnused
    51  
    52  	// on 386 we can't directly pass the arguments to the function :(
    53  	// -> go aligns arguments like a struct and 386 aligns every argument to 4 byte boundaries
    54  	for _, arg := range arguments {
    55  		switch arg.size {
    56  		case 8:
    57  			spec.stack = append(spec.stack, argument{uint16(arg.offset), type32})
    58  			spec.stack = append(spec.stack, argument{uint16(arg.offset + 4), type32})
    59  		case 4:
    60  			spec.stack = append(spec.stack, argument{uint16(arg.offset), type32})
    61  		case 2:
    62  			spec.stack = append(spec.stack, argument{uint16(arg.offset), type16})
    63  		case 1:
    64  			spec.stack = append(spec.stack, argument{uint16(arg.offset), type8})
    65  		}
    66  	}
    67  
    68  	if ret.c != classVoid {
    69  		var t argtype
    70  		switch ret.c {
    71  		case classInt, classUint:
    72  			switch ret.size {
    73  			case 8:
    74  				t = type64
    75  			case 4:
    76  				t = type32
    77  			case 2:
    78  				t = type16
    79  			case 1:
    80  				t = type8
    81  			}
    82  		case classFloat:
    83  			switch ret.size {
    84  			case 8:
    85  				t = typeDouble
    86  			case 4:
    87  				t = typeFloat
    88  			}
    89  		}
    90  
    91  		spec.ret.t = t
    92  		spec.ret.offset = uint16(ret.offset)
    93  	}
    94  
    95  	*(*unsafe.Pointer)(fptr) = unsafe.Pointer(spec)
    96  
    97  	return nil
    98  }