├── LICENSE ├── README ├── asmregs_386.h ├── asmregs_amd64.h ├── calltest_386.go ├── doc.go ├── variadic_386.S ├── variadic_386.go ├── variadic_386_test.go ├── variadic_amd64.S └── variadic_amd64.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2013 Mikkel Krautz 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | - Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | - Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR 19 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Package variadic 2 | ================ 3 | 4 | This packge implements variadic calling conventions for calling foreign C code from within Go. 5 | 6 | The primary motivation for this package was to be able to interface with Apple's Objective-C 7 | runtime on Mac OS X, which uses a variadic function (objc_msgSend) for sending messages to objects. 8 | 9 | Status 10 | ====== 11 | 12 | amd64: Works pretty well - http://github.com/mkrautz/objc uses it (only tested on darwin, though!) 13 | 14 | 386: Works on linux/386 and darwin/386 15 | 16 | Note: darwin/386 requires 16 byte stack alignment, and no 17 | effort has been made to support this at the moment. However, 18 | the tests pass on darwin/386 - not sure if I've been lucky. 19 | -------------------------------------------------------------------------------- /asmregs_386.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Mikkel Krautz. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #define REGS_NARGS 48 6 | #define REGS_ADDR 52 -------------------------------------------------------------------------------- /asmregs_amd64.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Mikkel Krautz. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #define REGS_RDI 0 6 | #define REGS_RSI 8 7 | #define REGS_RDX 16 8 | #define REGS_RCX 24 9 | #define REGS_R8 32 10 | #define REGS_R9 40 11 | #define REGS_XMM0 48 12 | #define REGS_XMM1 56 13 | #define REGS_XMM2 64 14 | #define REGS_XMM3 72 15 | #define REGS_XMM4 80 16 | #define REGS_XMM5 88 17 | #define REGS_XMM6 96 18 | #define REGS_XMM7 104 19 | #define REGS_NFLOAT 112 20 | #define REGS_NMEMORY 120 21 | #define REGS_MEMORY 128 22 | #define REGS_ADDR 136 23 | -------------------------------------------------------------------------------- /calltest_386.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Mikkel Krautz. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package variadic 6 | 7 | import "unsafe" 8 | 9 | /* 10 | #include 11 | 12 | void *arg0(void *arg0, ...) { 13 | return arg0; 14 | } 15 | 16 | void *argN(int n, ...) { 17 | va_list v; 18 | va_start(v, n); 19 | int skip = n-1; 20 | while (skip > 0) { 21 | va_arg(v, void *); 22 | skip--; 23 | } 24 | void *val = va_arg(v, void *); 25 | return val; 26 | } 27 | 28 | void *arg0fn() { 29 | return arg0; 30 | } 31 | 32 | void *argNfn() { 33 | return argN; 34 | } 35 | 36 | float floatret(void *arg0) { 37 | return 3.141592f; 38 | } 39 | 40 | void *floatretfn() { 41 | return floatret; 42 | } 43 | 44 | double doubleret(void *arg0) { 45 | return 3.141592; 46 | } 47 | 48 | void *doubleretfn() { 49 | return doubleret; 50 | } 51 | */ 52 | import "C" 53 | 54 | func arg0fn() unsafe.Pointer { 55 | return unsafe.Pointer(C.arg0fn()) 56 | } 57 | 58 | func argNfn() unsafe.Pointer { 59 | return unsafe.Pointer(C.argNfn()) 60 | } 61 | 62 | func floatret() unsafe.Pointer { 63 | return unsafe.Pointer(C.floatretfn()) 64 | } 65 | 66 | func doubleret() unsafe.Pointer { 67 | return unsafe.Pointer(C.doubleretfn()) 68 | } -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Mikkel Krautz. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package variadic implements interfaces for various variadic C calling conventions 6 | package variadic 7 | -------------------------------------------------------------------------------- /variadic_386.S: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Mikkel Krautz. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include "asmregs_386.h" 6 | 7 | #ifndef __linux__ 8 | # define name(x) _ ## x 9 | #else 10 | # define name(x) x 11 | #endif 12 | 13 | .globl name(VariadicCall) 14 | name(VariadicCall): 15 | .globl name(VariadicCallFloat) 16 | name(VariadicCallFloat): 17 | .globl name(VariadicCallDouble) 18 | name(VariadicCallDouble): 19 | pushl %ebp 20 | movl %esp, %ebp 21 | 22 | movl 8(%esp), %eax 23 | 24 | // Align the stack to a 16-byte boundary 25 | movl %esp, %ecx 26 | andl $0xfffffff0, %ecx 27 | movl %ecx, %esp 28 | 29 | movl REGS_NARGS(%eax), %ecx 30 | 31 | cmpl $0, %ecx 32 | je memdone 33 | imull $4, %ecx 34 | addl %eax, %ecx 35 | mem: 36 | subl $4, %ecx 37 | pushl (%ecx) 38 | cmpl %ecx, %eax 39 | jne mem 40 | memdone: 41 | 42 | addl $REGS_ADDR, %eax 43 | call *(%eax) 44 | 45 | leave 46 | ret 47 | -------------------------------------------------------------------------------- /variadic_386.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Mikkel Krautz. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package variadic 6 | 7 | /* 8 | #cgo LDFLAGS: -ldl 9 | 10 | #include 11 | #include 12 | 13 | void *VariadicCall(void *ctx); 14 | float VariadicCallFloat(void *ctx); 15 | double VariadicCallDouble(void *ctx); 16 | 17 | void *LookupSymAddr(char *str) { 18 | #ifdef RTLD_DEFAULT 19 | return dlsym(RTLD_DEFAULT, str); 20 | #else 21 | return dlsym(NULL, str); 22 | #endif 23 | } 24 | */ 25 | import "C" 26 | 27 | import "unsafe" 28 | 29 | type FunctionCall struct { 30 | Words [12]uintptr 31 | NumArgs int 32 | addr unsafe.Pointer 33 | } 34 | 35 | // NewFunctionCall creates a new FunctionCall than can be 36 | // used to call the C function named by the name parameter. 37 | func NewFunctionCall(name string) *FunctionCall { 38 | fc := new(FunctionCall) 39 | fc.addr = C.LookupSymAddr(C.CString(name)) 40 | return fc 41 | } 42 | 43 | // NewFunctionCallAddr creates a new FunctionCall that can be 44 | // used to cll the C function at the address given by the addr 45 | // parameter. 46 | func NewFunctionCallAddr(addr unsafe.Pointer) *FunctionCall { 47 | fc := new(FunctionCall) 48 | fc.addr = addr 49 | return fc 50 | } 51 | 52 | // Call calls the FunctionCall's underlying function, returning 53 | // its return value as an uintptr. 54 | func (f *FunctionCall) Call() uintptr { 55 | // Ensure stack is 16 byte aligned. 56 | rem := (f.NumArgs % 4) 57 | if rem > 0 { 58 | f.NumArgs = f.NumArgs + (4 - rem) 59 | } 60 | 61 | if f.NumArgs > len(f.Words) { 62 | panic("bad NumArgs") 63 | } 64 | 65 | if f.addr == nil { 66 | panic("variadic: Call called with nil function addr") 67 | } 68 | return uintptr(C.VariadicCall(unsafe.Pointer(f))) 69 | } 70 | 71 | // CallFloat32 calls the FunctionCall's underlying function, returning 72 | // its return value as a float32. 73 | func (f *FunctionCall) CallFloat32() float32 { 74 | if f.NumArgs > len(f.Words) { 75 | panic("bad NumArgs") 76 | } 77 | if f.addr == nil { 78 | panic("variadic: CallFloat32 called with nil function addr") 79 | } 80 | return float32(C.VariadicCallFloat(unsafe.Pointer(f))) 81 | } 82 | 83 | // CallFloat64 calls the FunctionCall's underlying function, returning 84 | // its return value as float64. 85 | func (f *FunctionCall) CallFloat64() float64 { 86 | if f.NumArgs > len(f.Words) { 87 | panic("bad NumArgs") 88 | } 89 | if f.addr == nil { 90 | panic("variadic: CallFloat64 called with nil function addr") 91 | } 92 | return float64(C.VariadicCallDouble(unsafe.Pointer(f))) 93 | } 94 | -------------------------------------------------------------------------------- /variadic_386_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Mikkel Krautz. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package variadic 6 | 7 | import ( 8 | "testing" 9 | "unsafe" 10 | "reflect" 11 | "math" 12 | ) 13 | 14 | func TestIntArg0(t *testing.T) { 15 | fc := new(FunctionCall) 16 | fc.addr = arg0fn() 17 | fc.Words[0] = 0xcafebabe 18 | fc.NumArgs = 1 19 | ret := fc.Call() 20 | if ret != fc.Words[0] { 21 | t.Fatalf("got 0x%x; expected 0x%x", ret, fc.Words[0]) 22 | } 23 | } 24 | 25 | func TestArgN(t *testing.T) { 26 | for n := 1; n < 10; n++ { 27 | fc := new(FunctionCall) 28 | fc.addr = argNfn() 29 | for i := 0; i < len(fc.Words); i++ { 30 | fc.Words[i] = 0 31 | } 32 | fc.Words[0] = uintptr(n) 33 | fc.Words[n] = uintptr(0xdead + n) 34 | fc.NumArgs = n+1 35 | ret := fc.Call() 36 | if ret != fc.Words[n] { 37 | t.Fatalf("got 0x%x; expected 0x%x", ret, fc.Words[n]) 38 | } 39 | } 40 | } 41 | 42 | func TestFloat32Ret(t *testing.T) { 43 | fc := new(FunctionCall) 44 | fc.addr = floatret() 45 | fc.Words[0] = 0xf00 46 | fc.NumArgs = 1 47 | ret := fc.CallFloat32() 48 | expected := float32(3.141592) 49 | if ret != expected { 50 | t.Fatalf("got %v; expected %v", ret, expected) 51 | } 52 | } 53 | 54 | func TestFloat64Ret(t *testing.T) { 55 | fc := new(FunctionCall) 56 | fc.addr = doubleret() 57 | fc.Words[0] = 0xf00 58 | fc.NumArgs = 1 59 | ret := fc.CallFloat64() 60 | expected := float64(3.141592) 61 | if ret != expected { 62 | t.Fatalf("got %v; expected %v", ret, expected) 63 | } 64 | } 65 | 66 | func TestPrintf(t *testing.T) { 67 | buf := make([]byte, 256) 68 | fmt := "%i 0x%x %u\n\x00" 69 | fc := NewFunctionCall("sprintf") 70 | slicehdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) 71 | strhdr := (*reflect.StringHeader)(unsafe.Pointer(&fmt)) 72 | fc.Words[0] = slicehdr.Data 73 | fc.Words[1] = strhdr.Data 74 | fc.Words[2] = 1 75 | fc.Words[3] = 0xcafebabe 76 | fc.Words[4] = 0xff 77 | fc.NumArgs = 5 78 | n := fc.Call() 79 | if n > 256 { 80 | t.Fatal("bad return value from sprintf") 81 | } 82 | val := string(buf[0:int(n)]) 83 | expected := "1 0xcafebabe 255\n" 84 | if val != expected { 85 | t.Fatalf("got %v; expected %v", val, expected) 86 | } 87 | } 88 | 89 | func TestFloatPrintf(t *testing.T) { 90 | buf := make([]byte, 256) 91 | fmt := "%.6f %i\n\x00" 92 | fc := NewFunctionCall("sprintf") 93 | slicehdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) 94 | strhdr := (*reflect.StringHeader)(unsafe.Pointer(&fmt)) 95 | fc.Words[0] = slicehdr.Data 96 | fc.Words[1] = strhdr.Data 97 | u64 := math.Float64bits(3.141592) 98 | fc.Words[2] = uintptr(u64 & 0xffffffff) 99 | fc.Words[3] = uintptr((u64 >> 32) & 0xffffffff) 100 | fc.Words[4] = 2 101 | fc.NumArgs = 5 102 | n := fc.Call() 103 | if n > 256 { 104 | t.Fatal("bad return value from sprintf") 105 | } 106 | val := string(buf[0:int(n)]) 107 | expected := "3.141592 2\n" 108 | if val != expected { 109 | t.Fatalf("got %v; expected %v", val, expected) 110 | } 111 | } -------------------------------------------------------------------------------- /variadic_amd64.S: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Mikkel Krautz. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include "asmregs_amd64.h" 6 | 7 | .globl _VariadicCall 8 | _VariadicCall: 9 | .globl _VariadicCallFloat 10 | _VariadicCallFloat: 11 | .globl _VariadicCallDouble 12 | _VariadicCallDouble: 13 | pushq %rbp 14 | movq %rsp, %rbp 15 | 16 | movq %rdi, %rax 17 | 18 | movq REGS_RDI(%rax), %rdi 19 | movq REGS_RSI(%rax), %rsi 20 | movq REGS_RDX(%rax), %rdx 21 | movq REGS_RCX(%rax), %rcx 22 | movq REGS_R8(%rax), %r8 23 | movq REGS_R9(%rax), %r9 24 | 25 | movsd REGS_XMM0(%rax), %xmm0 26 | movsd REGS_XMM1(%rax), %xmm1 27 | movsd REGS_XMM2(%rax), %xmm2 28 | movsd REGS_XMM3(%rax), %xmm3 29 | movsd REGS_XMM4(%rax), %xmm4 30 | movsd REGS_XMM5(%rax), %xmm5 31 | movsd REGS_XMM6(%rax), %xmm6 32 | movsd REGS_XMM7(%rax), %xmm7 33 | 34 | movq REGS_NMEMORY(%rax), %r11 35 | movq REGS_MEMORY(%rax), %r10 36 | 37 | cmpq $0, %r11 38 | je memdone 39 | imulq $8, %r11 40 | addq %r10, %r11 41 | mem: 42 | subq $8, %r11 43 | pushq (%r11) 44 | cmpq %r11, %r10 45 | jne mem 46 | memdone: 47 | 48 | movq REGS_ADDR(%rax), %r10 49 | movq REGS_NFLOAT(%rax), %rax 50 | 51 | callq *%r10 52 | 53 | movq %rbp, %rsp 54 | popq %rbp 55 | retq 56 | -------------------------------------------------------------------------------- /variadic_amd64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Mikkel Krautz. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package variadic 6 | 7 | /* 8 | #include 9 | 10 | void *VariadicCall(void *ctx); 11 | float VariadicCallFloat(void *ctx); 12 | double VariadicCallDouble(void *ctx); 13 | 14 | void *LookupSymAddr(char *str) { 15 | return dlsym(RTLD_DEFAULT, str); 16 | } 17 | */ 18 | import "C" 19 | 20 | import "unsafe" 21 | 22 | const ( 23 | RDI = iota 24 | RDX 25 | RCX 26 | R8 27 | R9 28 | XMM0 29 | XMM1 30 | XMM2 31 | XMM3 32 | XMM4 33 | XMM5 34 | XMM6 35 | XMM7 36 | ) 37 | 38 | type FunctionCall struct { 39 | Words [14]uintptr 40 | NumFloat int64 41 | NumMemory int64 42 | Memory unsafe.Pointer 43 | addr unsafe.Pointer 44 | } 45 | 46 | // NewFunctionCall creates a new FunctionCall than can be 47 | // used to call the C function named by the name parameter. 48 | func NewFunctionCall(name string) *FunctionCall { 49 | fc := new(FunctionCall) 50 | fc.addr = C.LookupSymAddr(C.CString(name)) 51 | return fc 52 | } 53 | 54 | // NewFunctionCallAddr creates a new FunctionCall that can be 55 | // used to cll the C function at the address given by the addr 56 | // parameter. 57 | func NewFunctionCallAddr(addr unsafe.Pointer) *FunctionCall { 58 | fc := new(FunctionCall) 59 | fc.addr = addr 60 | return fc 61 | } 62 | 63 | // Call calls the FunctionCall's underlying function, returning 64 | // its return value as an uintptr. 65 | func (f *FunctionCall) Call() uintptr { 66 | return uintptr(C.VariadicCall(unsafe.Pointer(f))) 67 | } 68 | 69 | // CallFloat32 calls the FunctionCall's underlying function, returning 70 | // its return value as a float32. 71 | func (f *FunctionCall) CallFloat32() float32 { 72 | return float32(C.VariadicCallFloat(unsafe.Pointer(f))) 73 | } 74 | 75 | // CallFloat64 calls the FunctionCall's underlying function, returning 76 | // its return value as float64. 77 | func (f *FunctionCall) CallFloat64() float64 { 78 | return float64(C.VariadicCallDouble(unsafe.Pointer(f))) 79 | } 80 | --------------------------------------------------------------------------------