Documentation ¶
Index ¶
- Constants
- func Dlclose(handle uintptr) error
- func Dlopen(path string, mode int) (uintptr, error)
- func Dlsym(handle uintptr, name string) (uintptr, error)
- func NewCallback(_ interface{}) uintptr
- func RegisterFunc(fptr interface{}, cfn uintptr)
- func RegisterLibFunc(fptr interface{}, handle uintptr, name string)
- func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr)
- type Dlerror
Examples ¶
Constants ¶
const ( RTLD_DEFAULT = 0x00000 // Pseudo-handle for dlsym so search for any loaded symbol RTLD_LAZY = 0x00001 // Relocations are performed at an implementation-dependent time. RTLD_NOW = 0x00002 // Relocations are performed when the object is loaded. RTLD_LOCAL = 0x00000 // All symbols are not made available for relocation processing by other modules. RTLD_GLOBAL = 0x00100 // All symbols are available for relocation processing of other modules. )
Variables ¶
This section is empty.
Functions ¶
func Dlclose ¶
Dlclose decrements the reference count on the dynamic library handle. If the reference count drops to zero and no other loaded libraries use symbols in it, then the dynamic library is unloaded.
func Dlopen ¶
Dlopen examines the dynamic library or bundle file specified by path. If the file is compatible with the current process and has not already been loaded into the current process, it is loaded and linked. After being linked, if it contains any initializer functions, they are called, before Dlopen returns. It returns a handle that can be used with Dlsym and Dlclose. A second call to Dlopen with the same path will return the same handle, but the internal reference count for the handle will be incremented. Therefore, all Dlopen calls should be balanced with a Dlclose call.
func Dlsym ¶
Dlsym takes a "handle" of a dynamic library returned by Dlopen and the symbol name. It returns the address where that symbol is loaded into memory. If the symbol is not found, in the specified library or any of the libraries that were automatically loaded by Dlopen when that library was loaded, Dlsym returns zero.
func NewCallback ¶
func NewCallback(_ interface{}) uintptr
Example ¶
package main import ( "fmt" "runtime" "github.com/ebitengine/purego" ) func main() { if runtime.GOOS == "linux" { // TODO: enable once callbacks are working properly on Linux fmt.Println("1 2 3 4 5 6 7 8 9\n45") return } cb := purego.NewCallback(func(a1, a2, a3, a4, a5, a6, a7, a8, a9 int) int { fmt.Println(a1, a2, a3, a4, a5, a6, a7, a8, a9) return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 }) var fn func(a1, a2, a3, a4, a5, a6, a7, a8, a9 int) int purego.RegisterFunc(&fn, cb) ret := fn(1, 2, 3, 4, 5, 6, 7, 8, 9) fmt.Println(ret) }
Output: 1 2 3 4 5 6 7 8 9 45
func RegisterFunc ¶
func RegisterFunc(fptr interface{}, cfn uintptr)
RegisterFunc takes a pointer to a Go function representing the calling convention of the C function. fptr will be set to a function that when called will call the C function given by cfn with the parameters passed in the correct registers and stack.
A panic is produced if the type is not a function pointer or if the function returns more than 1 value.
These conversions describe how a Go type in the fptr will be used to call the C function. It is important to note that there is no way to verify that fptr matches the C function. This also holds true for struct types where the padding needs to be ensured to match that of C; RegisterFunc does not verify this.
Type Conversions (Go <=> C) ¶
string <=> char* bool <=> _Bool uintptr <=> uintptr_t uint <=> uint32_t or uint64_t uint8 <=> uint8_t uint16 <=> uint16_t uint32 <=> uint32_t uint64 <=> uint64_t int <=> int32_t or int64_t int8 <=> int8_t int16 <=> int16_t int32 <=> int32_t int64 <=> int64_t float32 <=> float (WIP) float64 <=> double (WIP) struct <=> struct (WIP) func <=> C function unsafe.Pointer, *T <=> void* []T => void*
There is a special case when the last argument of fptr is a variadic interface (or []interface} it will be expanded into a call to the C function as if it had the arguments in that slice. This means that using arg ...interface{} is like a cast to the function with the arguments inside arg. This is not the same as C variadic.
There are some limitations when using RegisterFunc on Linux. First, there is no support for function arguments. Second, float32 and float64 arguments and return values do not work when CGO_ENABLED=1. Otherwise, Linux has the same feature parity as Darwin.
Memory ¶
In general it is not possible for purego to guarantee the lifetimes of objects returned or received from calling functions using RegisterFunc. For arguments to a C function it is important that the C function doesn't hold onto a reference to Go memory. This is the same as the Cgo rules.
However, there are some special cases. When passing a string as an argument if the string does not end in a null terminated byte (\x00) then the string will be copied into memory maintained by purego. The memory is only valid for that specific call. Therefore, if the C code keeps a reference to that string it may become invalid at some undefined time. However, if the string does already contain a null-terminated byte then no copy is done. It is then the responsibility of the caller to ensure the string stays alive as long as it's needed in C memory. This can be done using runtime.KeepAlive or allocating the string in C memory using malloc. When a C function returns a null-terminated pointer to char a Go string can be used. Purego will allocate a new string in Go memory and copy the data over. This string will be garbage collected whenever Go decides it's no longer referenced. This C created string will not be freed by purego. If the pointer to char is not null-terminated or must continue to point to C memory (because it's a buffer for example) then use a pointer to byte and then convert that to a slice using unsafe.Slice. Doing this means that it becomes the responsibility of the caller to care about the lifetime of the pointer
Example ¶
All functions below call this C function:
char *foo(char *str); // Let purego convert types var foo func(s string) string goString := foo("copied") // Go will garbage collect this string // Manually, handle allocations var foo2 func(b string) *byte mustFree := foo2("not copied\x00") defer free(mustFree)
func RegisterLibFunc ¶
RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). It panics if it can't find the name symbol.
func SyscallN ¶
SyscallN takes fn, a C function pointer and a list of arguments as uintptr. There is an internal maximum number of arguments that SyscallN can take. It panics when the maximum is exceeded. It returns the result and the libc error code if there is one.
NOTE: SyscallN does not properly call functions that have both integer and float parameters. See discussion comment https://github.com/ebiten/purego/pull/1#issuecomment-1128057607 for an explanation of why that is.
On amd64, if there are more than 8 floats the 9th and so on will be placed incorrectly on the stack.
The pragma go:nosplit is not needed at this function declaration because it uses go:uintptrescapes which forces all the objects that the uintptrs point to onto the heap where a stack split won't affect their memory location.