Giter Site home page Giter Site logo

purego's People

Contributors

cuonglm avatar elara6331 avatar eliottness avatar erdichen avatar hajimehoshi avatar iurisilvio avatar jupiterrider avatar jwijenbergh avatar ldnetgate avatar sealoftime avatar srlehn avatar totallygamerjet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

purego's Issues

`go build` a main package with purego with Cgo enabled might fail on Windows and Linux

https://github.com/ebitengine/purego/blob/test/example/main.go

The test bot builds the above main package like below:

go build -v ./...

And this failed at least on Linux like this:

https://github.com/ebitengine/purego/runs/8276795548

# github.com/ebitengine/purego/example
2022/09/09 18:57:24 duplicated definition of symbol _cgo_init

I think this is a minimal reproducible case of hajimehoshi/ebiten#2313: just building a main package with purego with Cgo enabled.

SIGSEGV with gotip when using -race

Do: CGO_ENABLED=0 gotip run -race ./example/main.go and receive signal: segmentation fault.

Go 1.20 rewrote the race detector to not require a C compiler on macOS. That's exciting! However, it break fakecgo. :(

The issue occurs in calling x_cgo_init from x_cgo_init_trampoline because it adds a bunch of calls to race functions. These race functions are not allowed in this Go code pretending to be C. Adding //go:norace does work in removing the race calls in the function itself but since we are calling this function from assembly it goes through the ABI0 stub which still has the race code despite the comment. Ugh

TEXT github.com/ebitengine/purego/internal/fakecgo.x_cgo_init(SB) 
0x10f2400		4881ec98000000		SUBQ $0x98, SP										
0x10f2407		4889ac2490000000	MOVQ BP, 0x90(SP)									
0x10f240f		488dac2490000000	LEAQ 0x90(SP), BP									
0x10f2417		48898424a0000000	MOVQ AX, 0xa0(SP)									
0x10f241f		48899c24a8000000	MOVQ BX, 0xa8(SP)									
0x10f2427		488b842498000000	MOVQ 0x98(SP), AX									
0x10f242f		4889442420		MOVQ AX, 0x20(SP)									
0x10f2434		e8278ffcff		CALL runtime.racefuncenter(SB)								
0x10f2439		48c744241000000000	MOVQ $0x0, 0x10(SP)									
0x10f2442		440f117c2428		MOVUPS X15, 0x28(SP)									
0x10f2448		440f117c2438		MOVUPS X15, 0x38(SP)									
0x10f244e		440f117c2448		MOVUPS X15, 0x48(SP)									
0x10f2454		440f117c2458		MOVUPS X15, 0x58(SP)									
0x10f245a		488b8c24a8000000	MOVQ 0xa8(SP), CX									
0x10f2462		48894c2418		MOVQ CX, 0x18(SP)									
0x10f2467		488d0552a51100		LEAQ github.com/ebitengine/purego/internal/fakecgo.setg_func(SB), AX			
0x10f246e		e84d7afcff		CALL runtime.racewrite(SB)								
0x10f2473		488b4c2418		MOVQ 0x18(SP), CX									
0x10f2478		48890d41a51100		MOVQ CX, github.com/ebitengine/purego/internal/fakecgo.setg_func(SB)			
0x10f247f		488d442428		LEAQ 0x28(SP), AX									
0x10f2484		4889842480000000	MOVQ AX, 0x80(SP)									
0x10f248c		e86f090000		CALL github.com/ebitengine/purego/internal/fakecgo.pthread_attr_init(SB)		
0x10f2491		488d4c2428		LEAQ 0x28(SP), CX									
0x10f2496		48894c2478		MOVQ CX, 0x78(SP)									
0x10f249b		488d5c2410		LEAQ 0x10(SP), BX									
0x10f24a0		48895c2470		MOVQ BX, 0x70(SP)									
0x10f24a5		488b442478		MOVQ 0x78(SP), AX									
0x10f24aa		e8910e0000		CALL github.com/ebitengine/purego/internal/fakecgo.pthread_attr_getstacksize(SB)	
0x10f24af		488b8424a0000000	MOVQ 0xa0(SP), AX									
0x10f24b7		4889842488000000	MOVQ AX, 0x88(SP)									
0x10f24bf		8400			TESTB AL, 0(AX)										
0x10f24c1		488d4c2410		LEAQ 0x10(SP), CX									
0x10f24c6		482b4c2410		SUBQ 0x10(SP), CX									
0x10f24cb		48894c2418		MOVQ CX, 0x18(SP)									
0x10f24d0		e8eb79fcff		CALL runtime.racewrite(SB)								
0x10f24d5		488b4c2418		MOVQ 0x18(SP), CX									
0x10f24da		4881c100100000		ADDQ $0x1000, CX									
0x10f24e1		488b942488000000	MOVQ 0x88(SP), DX									
0x10f24e9		48890a			MOVQ CX, 0(DX)										
0x10f24ec		488d442428		LEAQ 0x28(SP), AX									
0x10f24f1		4889442468		MOVQ AX, 0x68(SP)									
0x10f24f6		e8a50f0000		CALL github.com/ebitengine/purego/internal/fakecgo.pthread_attr_destroy(SB)		
0x10f24fb		0f1f440000		NOPL 0(AX)(AX*1)									
0x10f2500		e89b8efcff		CALL runtime.racefuncexit(SB)								
0x10f2505		488bac2490000000	MOVQ 0x90(SP), BP									
0x10f250d		4881c498000000		ADDQ $0x98, SP										
0x10f2514		c3			RET		

If this was in the runtime we could just call the function as ABIInternal and save all the hassle. The only solution I can currently think of is to compile the runtime/cgo version and then convert that to go assembly similar to c2goasm.

@hajimehoshi do you think we should petition that CALL ยทx_cgo_init<ABIInternal>(SB) be allowed outside the runtime? Even if it means we have to be careful about calling convention. Or perhaps, it's more likely that //go:norace should also affect the ABI0 stubs? It might be a bug none the less so that might be more likely to be approved.

Unexpected data after calling shared library C function

Full repro here.

Given the following C function exported via a linux shared library:

unsigned long FuncKO(unsigned long input1, char* input2,
	unsigned long* input2Len, unsigned long* input3,
	unsigned long* input4, unsigned char* input5, unsigned long* input5Len)
{
	unsigned long rv = 0;

	if (input2Len)
		LogToFile("/var/tmp/so_sample.log", "IN : input2Len=%ld\n", *input2Len);
	if (input5Len)
		LogToFile("/var/tmp/so_sample.log", "IN : input5Len=%ld\n", *input5Len);

	if (input3)
		*input3 = 0;

	if (input4)
		*input4 = 0;

	if (input2Len)
		*input2Len = 32;

	if (input5Len)
		*input5Len = 32;

	if (input3)
		*input3 = 1;

	if (input4)
		*input4 = 1;

	if (input2Len)
		LogToFile("/var/tmp/so_sample.log", "OUT: input2Len=%ld\n", *input2Len);
	if (input5Len)
		LogToFile("/var/tmp/so_sample.log", "OUT: input5Len=%ld\n", *input5Len);

	return rv;
}

And given the following equivalent Golang type definition (which I hope is correct):

type FuncKO func(
        input1 uint32,
        input2 *byte,
        input2Len *uint32,
        input3 *uint32,
        input4 *uint32,
        input5 *byte,
        input5Len *uint32,
) uint32

Calling the C function from Go using the aforementioned type definition with purego does not set input5Len to 32 as expected, it stays 0.

var input1 uint32
var input2Len uint32
var imput3 uint32
var imput4 uint32
var input5Len uint32

r := funcKOProc(
        input1,
        nil,
        &input2Len,
        &imput3,
        &imput4,
        nil,
        &input5Len,
)
fmt.Printf("=== FuncKO (nil)\n")
fmt.Printf("r             = %d\n", r)
fmt.Printf("input2Len     = %d\n", input2Len)
fmt.Printf("imput3        = %d\n", imput3)
fmt.Printf("imput4        = %d\n", imput4)
fmt.Printf("input5Len     = %d\n\n", input5Len)

Output:

=== FuncKO (nil)
r             = 0
input2Len     = 32
imput3        = 1
imput4        = 1
input5Len     = 0

If we look at the C function logs, we find the following:

$ cat /var/tmp/so_sample.log 
IN: input2Len=0
IN: input5Len=0
OUT: input2Len=4294967328
OUT: input5Len=0

Notice input2Len being set to 4294967328 in C, but still holding 32 in Go, and input5Len value not changing.

If we run a sample C program that loads the same shared library and calls the same function, we get the following:

r             = 0
input2Len     = 32
imput3        = 1
imput4        = 1
input5Len     = 32

and the C function logs show:

$ cat /var/tmp/so_sample.log 
IN: input2Len=0
IN: input5Len=0
OUT: input2Len=32
OUT: input5Len=32

which is the expected behavior.

I'm not sure what could be causing this ? Maybe there is a corruption somewhere ?

enable `RegisterClass` for a struct with member variables

Now RegisterClass is available only for structs without member variables. If a struct had member variables, memory corruption might happen (examples/objc uses RegisterClass for a struct with a member variable, but this is a known issue).

As @TotallyGamerJet and Hajime discussed on Ebitengine Discord, would it be possible giving up mirroring member variables in the Go world to the Objective-C world? This makes the Objective-C struct size predictable and then memory corruption would no longer happen. Unfortunately, this would be a breaking change since GetIvar and SetIvar no longer works for Go member variables. However, the current major version is still 0, Hajime is fine to have such a breaking change.

Currently, a struct with members can use AllocateClassPair, AddMethod, and AddIvar, but this is quite painful. RegisterClass cares all of them, which is pretty nice.

Static builds

I am finding that its not possible to build static binaries with the purego linked in and CGO disabled.

Trying to use an external linker causes relocation and symbol lookup errors.

Is it possible to eliminate the linkages to libc, libpthread, libdl, etc... either through a musl CC or other techniques?

internal/fakecgo: add build tags

Now building internal/fakecgo for Windows fails since internal/fakecgo cannot be built for Windows.

$ env GOOS=windows GOARCH=amd64 go build -v ./...
github.com/ebitengine/purego/internal/fakecgo
# github.com/ebitengine/purego/internal/fakecgo
internal/fakecgo/go_util.go:29:2: undefined: _cgo_sys_thread_start

This is a little inconvenient since I cannot use ./... for Windows. Would it be fine to add darwin for the fakecgo package?

Segmentation fault on startup (linux) v0.4.0-alpha

Running the output from a compile crashes on startup:

$ ./s2c
Segmentation fault (core dumped)
$ gdb ./s2c
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
[...]

(gdb) r
Starting program: /home/user1/cmd/s2c/s2c
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7e2a540 in _int_malloc (av=av@entry=0x7ffff7f9fc80 <main_arena>, bytes=bytes@entry=640) at ./malloc/malloc.c:4375
4375    ./malloc/malloc.c: No such file or directory.
(gdb) where
#0  0x00007ffff7e2a540 in _int_malloc (av=av@entry=0x7ffff7f9fc80 <main_arena>, bytes=bytes@entry=640)
    at ./malloc/malloc.c:4375
#1  0x00007ffff7e2aa49 in tcache_init () at ./malloc/malloc.c:3245
#2  0x00007ffff7e2b25e in tcache_init () at ./malloc/malloc.c:3241
#3  __GI___libc_malloc (bytes=64) at ./malloc/malloc.c:3306
#4  0x0000000000657942 in github.com/ebitengine/purego/internal/fakecgo.call5 ()
    at e:/gopath/pkg/mod/github.com/ebitengine/[email protected]/internal/fakecgo/trampolines_linux_amd64.s:72
#5  0x000000000065744c in github.com/ebitengine/purego/internal/fakecgo.malloc (size=<optimized out>)
    at e:/gopath/pkg/mod/github.com/ebitengine/[email protected]/internal/fakecgo/symbols.go:25
#6  github.com/ebitengine/purego/internal/fakecgo.x_cgo_init (g=0x7ffff7f9fce0 <main_arena+96>, setg=6650208)
    at e:/gopath/pkg/mod/github.com/ebitengine/[email protected]/internal/fakecgo/go_linux_amd64.go:81
#7  0x0000000000657b8a in github.com/ebitengine/purego/internal/fakecgo.x_cgo_init (g=0x8e7340 <runtime.g0>,
    setg=4612384) at <autogenerated>:1
#8  0x00000000006577bc in x_cgo_init_trampoline ()
    at e:/gopath/pkg/mod/github.com/ebitengine/[email protected]/internal/fakecgo/trampolines_linux_amd64.s:29
#9  0x0000000000466d11 in runtime.rt0_go () at c:/go/src/runtime/asm_amd64.s:220
#10 0x0000000000000000 in ?? ()

Version 0.3.0 crashes as well:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7e2a540 in _int_malloc (av=av@entry=0x7ffff7f9fc80 <main_arena>, bytes=bytes@entry=640) at ./malloc/malloc.c:4375
4375    ./malloc/malloc.c: No such file or directory.
(gdb) where
#0  0x00007ffff7e2a540 in _int_malloc (av=av@entry=0x7ffff7f9fc80 <main_arena>, bytes=bytes@entry=640)
    at ./malloc/malloc.c:4375
#1  0x00007ffff7e2aa49 in tcache_init () at ./malloc/malloc.c:3245
#2  0x00007ffff7e2b25e in tcache_init () at ./malloc/malloc.c:3241
#3  __GI___libc_malloc (bytes=64) at ./malloc/malloc.c:3306
#4  0x000000000065dee2 in github.com/ebitengine/purego/internal/fakecgo.call5 ()
    at e:/gopath/pkg/mod/github.com/ebitengine/[email protected]/internal/fakecgo/trampolines_linux_amd64.s:72
#5  0x000000000065d9ec in github.com/ebitengine/purego/internal/fakecgo.malloc (size=<optimized out>)
    at e:/gopath/pkg/mod/github.com/ebitengine/[email protected]/internal/fakecgo/symbols.go:25
#6  github.com/ebitengine/purego/internal/fakecgo.x_cgo_init (g=0x7ffff7f9fce0 <main_arena+96>, setg=6676224)
    at e:/gopath/pkg/mod/github.com/ebitengine/[email protected]/internal/fakecgo/go_linux_amd64.go:81
#7  0x000000000065e12a in github.com/ebitengine/purego/internal/fakecgo.x_cgo_init (g=0x8f5dc0 <runtime.g0>,
    setg=4615808) at <autogenerated>:1
#8  0x000000000065dd5c in x_cgo_init_trampoline ()
    at e:/gopath/pkg/mod/github.com/ebitengine/[email protected]/internal/fakecgo/trampolines_linux_amd64.s:29
#9  0x0000000000467a71 in runtime.rt0_go () at c:/go/src/runtime/asm_amd64.s:220
#10 0x0000000000000000 in ?? ()

Go versions tested (for compile):

ฮป go version
go version go1.19.5 windows/amd64
ฮป SET GOOS=linux
ฮป go build .

or 

ฮป go version
go version go1.20.1 windows/amd64
ฮป SET GOOS=linux
ฮป go build .

All crash on startup.

Are variadic functions supported?

The source has a comment about a special case for variadic functions -

// 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.

Does this mean they are not supported? I made my own library in C and am unable to find a way to get my function to see any of the variadic parameters.

void printSomething(int a, const char *fmt, ...)
{
    va_list ap;
    printf("start %d\n", a);
    printf("fmt %s\n", fmt);
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
    printf("\n");
    printf("end %d\n", a);
}

Calling code:

	var printSomething func(int, string, []interface{})
	purego.RegisterLibFunc(&printSomething, libcraig, `printSomething`)
	printSomething(5, `%s`, []interface{}{`foo`})

This prints:

start 5
fmt %s
(null)
end 5

SIGSEGV when running any test/application using go 1.21.0rc2

This is on MacOS 13.5 / Intel (16" MacBook 2019). I have tried some of my ebiten applications, ebiten tests and purego tests and example. They all seem to fail here:

raff|main:purego$ go run examples/objc/main_darwin.go
SIGSEGV: segmentation violation
PC=0x7ff800864b62 m=0 sigcode=1
signal arrived during cgo execution

goroutine 1 [syscall, locked to thread]:
runtime.cgocall(0x10cbd80, 0xc0000b2000)
/usr/local/go/src/runtime/cgocall.go:157 +0x4b fp=0xc0000ab4b0 sp=0xc0000ab478 pc=0x1007b2b
github.com/ebitengine/purego.RegisterFunc.func1({0xc00009c3c0?, 0x2?, 0x2?})
/Users/rsena/go/src/github.com/ebitengine/purego/func.go:212 +0xa25 fp=0xc0000ab820 sp=0xc0000ab4b0 pc=0x10cab05
reflect.callReflect(0xc00009c1b0, 0xc0000abd88, 0xc0000abc60, 0xc0000abc68)
/usr/local/go/src/reflect/value.go:782 +0x54c fp=0xc0000abc10 sp=0xc0000ab820 pc=0x1085c0c
reflect.callReflect(0xc00009c1b0, 0xc0000abd88, 0xc0000abc60, 0xc0000abc68)
:1 +0x45 fp=0xc0000abc40 sp=0xc0000abc10 pc=0x1095a05
reflect.makeFuncStub()
/usr/local/go/src/reflect/asm_amd64.s:47 +0x6e fp=0xc0000abd88 sp=0xc0000abc40 pc=0x1091d8e
github.com/ebitengine/purego.Dlopen({0x10fb7b0?, 0x10dafe0?}, 0x11d5348?)
/Users/rsena/go/src/github.com/ebitengine/purego/dlfcn.go:37 +0x1f fp=0xc0000abdc0 sp=0xc0000abd88 pc=0x10c9b3f
github.com/ebitengine/purego/objc.init.0()
/Users/rsena/go/src/github.com/ebitengine/purego/objc/objc_runtime_darwin.go:41 +0x28 fp=0xc0000abe10 sp=0xc0000abdc0 pc=0x10ce6c8
runtime.doInit1(0x11ca600)
/usr/local/go/src/runtime/proc.go:6739 +0xd8 fp=0xc0000abf40 sp=0xc0000abe10 pc=0x10449d8
runtime.doInit(...)
/usr/local/go/src/runtime/proc.go:6706
runtime.main()
/usr/local/go/src/runtime/proc.go:249 +0x374 fp=0xc0000abfe0 sp=0xc0000abf40 pc=0x1037ab4
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:1650 +0x1 fp=0xc0000abfe8 sp=0xc0000abfe0 pc=0x1063661

RTLD_DEFAULT has wrong value

According to opensource.apple.com RTLD_DEFAULT is defined as #define RTLD_DEFAULT ((void *) -2). In purego I wrote const RTLD_DEFAULT = ^uintptr(1) which is not the same binary representation.

This is a bug in version 0.1.0 and should be corrected to something like const RTLD_DEFAULT = uintptr(0x8000000000000002) .

Secondly, I think we should remove this constant from purego 0.2.0 since it is not used in purego, Oto or Ebitengine and is darwin specific. See the Unix specification.

Stubs for other platforms?

I would like to use this for cross-platform development, and use purego where supported, but keep code compiling on all platforms.

However most functions, like Dlopen(...) aren't available for Windows, which is fair enough, but I would need to guard all code with build tags.

In these cases it is very nice to have stubs available, so code compiles, but just returns errors:

var ErrUnsupported = errors.New("purego not available on this platform")

func Dlopen(path string, mode int) (uintptr, error) {
	return 0, ErrUnsupported
}

func Dlclose(handle uintptr) error {
	return ErrUnsupported 
}

func Dlsym(handle uintptr, name string) (uintptr, error) {
	return 0, ErrUnsupported
}

// etc

For my projects this would mean a lot less code duplication. I don't really see any significant downside to adding stubs.

Back Port to 0.1

Should we backport this commit to 1.0 since it's the only tagged version and this bug can cause issues? 382f4c6

Segmentation fault on Ubuntu 22.04 + Go 1.20.1

Hello,

I got a segmentation fault on WSL running Ubuntu 22.04 LTS when compiling the example with Go 1.20.1 using the latest available version v0.2.0-alpha.1.0.20230213042757-086b819e2f9a.

go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/mo/.cache/go-build"
GOENV="/home/mo/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/mo/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/mo/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.20.1"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="0"
GOMOD="/mnt/c/Users/MrGol/Projects/expermients/Go/purego/go.mod"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build3406589635=/tmp/go-build -gno-record-gcc-switches"
The example code
package main

import (
	"fmt"
	"runtime"

	"github.com/ebitengine/purego"
)

func getSystemLibrary() string {
	switch runtime.GOOS {
	case "darwin":
		return "/usr/lib/libSystem.B.dylib"
	case "linux":
		return "libc.so.6"
	default:
		panic(fmt.Errorf("GOOS=%s is not supported", runtime.GOOS))
	}
}

func main() {
	libc := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
	if err := purego.Dlerror(); err != "" {
		panic(err)
	}
	var puts func(string)
	purego.RegisterLibFunc(&puts, libc, "puts")
	puts("Calling C from Go without Cgo!")
}

Command used to compile

CGO_ENABLED=0 go build .

Output

Segmentation fault
gdb output
Starting program: /mnt/c/Users/MrGol/Projects/expermients/Go/purego/xpr-purego
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7e28540 in _int_malloc (av=av@entry=0x7ffff7f9dc80 <main_arena>, bytes=bytes@entry=640) at ./malloc/malloc.c:4375
4375    ./malloc/malloc.c: No such file or directory.

SSE instruction segfaults fakecgo init

Some SSE instructions have requirements that make the fakecgo package segfault. In my case, the call to malloc during x_cgo_init in internal/fakecgo/go_linux_amd64.go makes use of a movaps instruction on 0x10(%rsp). But since %rsp was not aligned on 16 bits (as the instruction requires it), the cgo initialization segfaults. Fixing the register manually using gdb does the trick but of course it messes with the stack and segfault laters.

My team and I are really fond and the project and really want it to work on linux so I will probably drop a PR if I find a fix in the following days. If you have a definitive idea on how to fix this or if you want more debug information, please let me know.

internal/abi/abi_arm64.h Not included when vendoring dependencies

I noticed this issue when upgrading Ebiten from 2.3.8 to 2.4.0, however this was the dependency throwing the error. So I hope this is the right place to report this.
Error: # github.com/ebitengine/purego vendor/github.com/ebitengine/purego/sys_darwin_arm64.s:8: #include: open /Users/<redacted>/sdk/go1.19/pkg/include/internal/abi/abi_arm64.h: no such file or directory

Digging further I found that the folder /internal/abi/ didn't exist in my vendored copy but strings and fakecgo did. Which led me to this comment that mentions that there needs to be at least one .go file present in a directory in order for all the files to be included during vendoring. I made sure it wasn't a fluke by deleting the vendor folder and starting over but the results were consistent.

There is a work around, I cloned the repo and manually copied it to the vendor folder and the issue resolved (until I vendor again)

Drop 1.15 Support

This is not an issue that should be addressed now but exists solely to list the pros and cons of dropping support for 1.15.

Pros:

  • It has caused many issues (#27, & #25 ) and required fixes that make the code more complicated (#34)
  • It would allow us to close #37 which doesn't have a fix yet
  • It would put us in line with gomobile support
  • Linux support would be easier (#38)
  • All the features of Go 1.16 which includes go:embed &GOOS=iOS
  • Close ebitengine#2281 as gomobile's lowest version is 1.13

Cons:

  • Debian's default version is 1.15
  • This effects both big projects (Oto and Ebitengine)

can not use from windows

I use v0.4.0-alpha.4 for windows, the openLibrary from example is not public.

Is there a unite public library load method ?

Return errors from `Dlerror()`

It would be better if Dlerror() returned errors, rather than strings as it does now. That would allow users to check errors using a nil check, rather than an empty string check, as well as allow them to return an error from purego as an error type from their own function. It would also provide the ability to wrap purego errors, such as with %w in fmt.Errorf(), as well as with the new functions added in Go 1.20, such as errors.Join().

Dlerror() could simply use errors.New() to create a new error with the given string, but I feel it would be even better to define a custom error type that just returns a string. That way, the type information tells the caller that the error came from purego.

This would look something like:

type Error struct {
	s string
}

func (e Error) Error() string {
	return e.s
}

If this seems like a good idea, I could submit a PR to implement it.

go test fails CGO_ENABLED=0 Go 1.21.0-rc2

To get this working just add the variable and check for any other Cgo related changes in runtime/cgo.

CGO_ENABLED=0 go test ./...
?       github.com/ebitengine/purego/cmd        [no test files]
?       github.com/ebitengine/purego/examples/libc      [no test files]
?       github.com/ebitengine/purego/examples/objc      [no test files]
?       github.com/ebitengine/purego/internal/fakecgo   [no test files]
?       github.com/ebitengine/purego/internal/strings   [no test files]
fatal error: _cgo_pthread_key_created missing

goroutine 1 [running, locked to thread]:
runtime.throw({0x1029ad194?, 0x0?})
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/panic.go:1077 +0x40 fp=0x14000046730 sp=0x14000046700 pc=0x1028cdcd0
runtime.main()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:213 +0x3fc fp=0x140000467d0 sp=0x14000046730 pc=0x1028d067c
runtime.goexit()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140000467d0 sp=0x140000467d0 pc=0x102903204

goroutine 2 [force gc (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:398 +0xc8 fp=0x14000046f90 sp=0x14000046f70 pc=0x1028d0998
runtime.goparkunlock(...)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:404
runtime.forcegchelper()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:322 +0xb8 fp=0x14000046fd0 sp=0x14000046f90 pc=0x1028d07f8
runtime.goexit()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/asm_arm64.s:1197 +0x4 fp=0x14000046fd0 sp=0x14000046fd0 pc=0x102903204
created by runtime.init.6 in goroutine 1
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:310 +0x24

goroutine 18 [GC sweep wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:398 +0xc8 fp=0x14000042760 sp=0x14000042740 pc=0x1028d0998
runtime.goparkunlock(...)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:404
runtime.bgsweep(0x0?)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgcsweep.go:280 +0xa0 fp=0x140000427b0 sp=0x14000042760 pc=0x1028bbdc0
runtime.gcenable.func1()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgc.go:200 +0x28 fp=0x140000427d0 sp=0x140000427b0 pc=0x1028b0888
runtime.goexit()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140000427d0 sp=0x140000427d0 pc=0x102903204
created by runtime.gcenable in goroutine 1
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgc.go:200 +0x6c

goroutine 19 [GC scavenge wait]:
runtime.gopark(0x1400008e000?, 0x1029d8668?, 0x1?, 0x0?, 0x140000824e0?)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:398 +0xc8 fp=0x14000042f50 sp=0x14000042f30 pc=0x1028d0998
runtime.goparkunlock(...)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:404
runtime.(*scavengerState).park(0x102aecdc0)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgcscavenge.go:425 +0x5c fp=0x14000042f80 sp=0x14000042f50 pc=0x1028b962c
runtime.bgscavenge(0x0?)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgcscavenge.go:653 +0x44 fp=0x14000042fb0 sp=0x14000042f80 pc=0x1028b9b84
runtime.gcenable.func2()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgc.go:201 +0x28 fp=0x14000042fd0 sp=0x14000042fb0 pc=0x1028b0828
runtime.goexit()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/asm_arm64.s:1197 +0x4 fp=0x14000042fd0 sp=0x14000042fd0 pc=0x102903204
created by runtime.gcenable in goroutine 1
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgc.go:201 +0xac
FAIL    github.com/ebitengine/purego    0.265s
fatal error: _cgo_pthread_key_created missing

goroutine 1 [running, locked to thread]:
runtime.throw({0x1029d4a08?, 0x0?})
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/panic.go:1077 +0x40 fp=0x14000046730 sp=0x14000046700 pc=0x1029057b0
runtime.main()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:213 +0x3fc fp=0x140000467d0 sp=0x14000046730 pc=0x10290815c
runtime.goexit()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140000467d0 sp=0x140000467d0 pc=0x1029394d4

goroutine 2 [force gc (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:398 +0xc8 fp=0x14000046f90 sp=0x14000046f70 pc=0x102908478
runtime.goparkunlock(...)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:404
runtime.forcegchelper()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:322 +0xb8 fp=0x14000046fd0 sp=0x14000046f90 pc=0x1029082d8
runtime.goexit()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/asm_arm64.s:1197 +0x4 fp=0x14000046fd0 sp=0x14000046fd0 pc=0x1029394d4
created by runtime.init.6 in goroutine 1
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:310 +0x24

goroutine 3 [GC sweep wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:398 +0xc8 fp=0x14000047760 sp=0x14000047740 pc=0x102908478
runtime.goparkunlock(...)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:404
runtime.bgsweep(0x0?)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgcsweep.go:280 +0xa0 fp=0x140000477b0 sp=0x14000047760 pc=0x1028f38a0
runtime.gcenable.func1()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgc.go:200 +0x28 fp=0x140000477d0 sp=0x140000477b0 pc=0x1028e8368
runtime.goexit()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140000477d0 sp=0x140000477d0 pc=0x1029394d4
created by runtime.gcenable in goroutine 1
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgc.go:200 +0x6c

goroutine 4 [GC scavenge wait]:
runtime.gopark(0x14000078000?, 0x1029fd770?, 0x1?, 0x0?, 0x14000003520?)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:398 +0xc8 fp=0x14000047f50 sp=0x14000047f30 pc=0x102908478
runtime.goparkunlock(...)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/proc.go:404
runtime.(*scavengerState).park(0x102b08a00)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgcscavenge.go:425 +0x5c fp=0x14000047f80 sp=0x14000047f50 pc=0x1028f110c
runtime.bgscavenge(0x0?)
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgcscavenge.go:653 +0x44 fp=0x14000047fb0 sp=0x14000047f80 pc=0x1028f1664
runtime.gcenable.func2()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgc.go:201 +0x28 fp=0x14000047fd0 sp=0x14000047fb0 pc=0x1028e8308
runtime.goexit()
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/asm_arm64.s:1197 +0x4 fp=0x14000047fd0 sp=0x14000047fd0 pc=0x1029394d4
created by runtime.gcenable in goroutine 1
        /Users/jarrettkuklis/go/go1.21rc2/src/runtime/mgc.go:201 +0xac
FAIL    github.com/ebitengine/purego/objc       0.340s
FAIL

Support for static initialization dlopen/dlsym calls

Hello,

I noticed that in dlopen and dlsym in dlfcn.go , the return value is never checked against nil. Is there a reason for this ? This blocks the use of any call to dlopen or any similar function in the static initialization of the library loaded. In the end, if the inner call fails, the dlerror value set by the inner call would make the top call fail.

Do tell me if you need more precisions or if you want me to do a PR or an MRE.

test failed with Go 1.15 and Go 1.16

I think this is the same issue as ebitengine/oto#183

$ go1.15.15 test ./...
?       github.com/ebitengine/purego    [no test files]
?       github.com/ebitengine/purego/internal/fakecgo   [no test files]
?       github.com/ebitengine/purego/internal/strings   [no test files]
--- FAIL: ExampleAllocateClassPair (0.00s)
panic: runtime error: index out of range [18056875977166063944] with length 2000 [recovered]
        panic: runtime error: index out of range [18056875977166063944] with length 2000

goroutine 1 [running]:
testing.(*InternalExample).processRunResult(0xc00009dd28, 0x0, 0x0, 0xd67a, 0x113f920, 0xc000018420, 0x10144d4)
        /Users/hajimehoshi/sdk/go1.15.15/src/testing/example.go:89 +0x648
testing.runExample.func2(0xc0b9b04c927ab680, 0x861ae, 0x1237440, 0xc000010050, 0xc000010018, 0xc000072240, 0xc000051d28, 0xc00009dd58)
        /Users/hajimehoshi/sdk/go1.15.15/src/testing/run_example.go:58 +0x10d
panic(0x113f920, 0xc000018420)
        /Users/hajimehoshi/sdk/go1.15.15/src/runtime/panic.go:969 +0x1b9
github.com/ebitengine/purego.callbackWrap(0x110fb60)
        /Users/hajimehoshi/purego/syscall_darwin.go:102 +0x50f
github.com/ebitengine/purego.syscall_syscall9X(0x7ff80afe3400, 0x600000004090, 0x7ff825565129, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
        /Users/hajimehoshi/purego/syscall_darwin.go:20 +0x128
github.com/ebitengine/purego.SyscallN(0x7ff80afe3400, 0xc00001a1e0, 0x2, 0x2, 0x0, 0xc00001a1e0, 0x2)
        /Users/hajimehoshi/purego/syscall.go:37 +0x11e
github.com/ebitengine/purego/objc.ID.Send(0x600000004090, 0x7ff825565129, 0x0, 0x0, 0x0, 0x0)
        /Users/hajimehoshi/purego/objc/objc_runtime_darwin.go:52 +0xb7
github.com/ebitengine/purego/objc_test.ExampleAllocateClassPair()
        /Users/hajimehoshi/purego/objc/objc_runtime_darwin_test.go:22 +0x1b3
testing.runExample(0x1152cb4, 0x18, 0x1158988, 0x11503b1, 0xd, 0x0, 0x0)
        /Users/hajimehoshi/sdk/go1.15.15/src/testing/run_example.go:62 +0x209
testing.runExamples(0xc00009ded0, 0x1233f80, 0x4, 0x4, 0xc0b9b0e292792428)
        /Users/hajimehoshi/sdk/go1.15.15/src/testing/example.go:44 +0x1af
testing.(*M).Run(0xc00011a000, 0x0)
        /Users/hajimehoshi/sdk/go1.15.15/src/testing/testing.go:1346 +0x273
main.main()
        _testmain.go:51 +0x138
FAIL    github.com/ebitengine/purego/objc       0.130s
FAIL
$ go1.16.15 test ./...
?       github.com/ebitengine/purego    [no test files]
?       github.com/ebitengine/purego/internal/fakecgo   [no test files]
?       github.com/ebitengine/purego/internal/strings   [no test files]
--- FAIL: ExampleAllocateClassPair (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x2 pc=0x110edf4]

goroutine 1 [running]:
testing.(*InternalExample).processRunResult(0xc00010fd58, 0x0, 0x0, 0x16e10, 0x20300000000000, 0x112c280, 0x1229dc0, 0x1529200)
        /Users/hajimehoshi/sdk/go1.16.15/src/testing/example.go:91 +0x69b
testing.runExample.func2(0xc0b9b06081eed3c8, 0x948ba, 0x1231ec0, 0xc000010050, 0xc000010018, 0xc000074240, 0xc00010fd58, 0xc00010fcb6, 0xc00010fd88)
        /Users/hajimehoshi/sdk/go1.16.15/src/testing/run_example.go:59 +0x11c
panic(0x112c280, 0x1229dc0)
        /Users/hajimehoshi/sdk/go1.16.15/src/runtime/panic.go:965 +0x1b9
github.com/ebitengine/purego.callbackWrap(0x2)
        /Users/hajimehoshi/purego/syscall_darwin.go:102 +0x54
github.com/ebitengine/purego.syscall_syscall9X(0x7ff80afe3400, 0x600000008040, 0x7ff825565129, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
        /Users/hajimehoshi/purego/syscall_darwin.go:20 +0x128
github.com/ebitengine/purego.SyscallN(0x7ff80afe3400, 0xc00001a1f0, 0x2, 0x2, 0x0, 0xc00001a1f0, 0x2)
        /Users/hajimehoshi/purego/syscall.go:37 +0x11e
github.com/ebitengine/purego/objc.ID.Send(0x600000008040, 0x7ff825565129, 0x0, 0x0, 0x0, 0x0)
        /Users/hajimehoshi/purego/objc/objc_runtime_darwin.go:52 +0xb7
github.com/ebitengine/purego/objc_test.ExampleAllocateClassPair()
        /Users/hajimehoshi/purego/objc/objc_runtime_darwin_test.go:22 +0x1b3
testing.runExample(0x1152bba, 0x18, 0x11588f8, 0x115025e, 0xd, 0x0, 0x0)
        /Users/hajimehoshi/sdk/go1.16.15/src/testing/run_example.go:63 +0x222
testing.runExamples(0xc00010fed0, 0x122e780, 0x4, 0x4, 0xc0b9b0f681ed4940)
        /Users/hajimehoshi/sdk/go1.16.15/src/testing/example.go:44 +0x17a
testing.(*M).Run(0xc000126000, 0x0)
        /Users/hajimehoshi/sdk/go1.16.15/src/testing/testing.go:1428 +0x273
main.main()
        _testmain.go:51 +0x138
FAIL    github.com/ebitengine/purego/objc       0.123s
FAIL

Support for __dso_handle

Hello,
I thought I would try using purego to interface with macOS's unified logging framework. However I've hit a snag.

The unified logger uses a set of macros like os_log_debug that ultimately call _os_log_internal. This is exported by libSystem.B.dylib - so far so good. However, the first argument to that function is &__dso_handle, which is defined in C headers as

extern struct mach_header __dso_handle;

I'm not sure how to resolve this symbol. It's not exported by libSystem.B.dylib, but rather it's specific to the running executable. I don't understand this fully, but I've read that it's actually dynamically generated by the linker.

Is there a way to reference this symbol with purego, or is this not possible?

Cocoa Example

Hi team ๐Ÿ‘‹ Awesome job on this! I'm looking to port a decent amount of Cocoa based Objective-C code to purego so am happy to put in some serious time for testing and contributing. I'm currently stuck with where to start. If anyone can point me to a simple example of spinning up the equivalent of this code, then I should be able to get going and provide feedback:

package main

/*
#cgo CFLAGS: -mmacosx-version-min=10.13 -x objective-c
#cgo LDFLAGS: -framework AppKit -mmacosx-version-min=10.13

#import <AppKit/AppKit.h>

void start() {
	[NSApplication sharedApplication];
	[NSApp run];
}

*/
import "C"

func main() {
	C.start()
}

I gave it a shot but couldn't determine the right approach from the examples. Here's my initial attempt:

package main

import (
	"github.com/ebitengine/purego"
	"github.com/ebitengine/purego/objc"
)

var (
	class_NSApplication   = objc.GetClass("NSApplication")
	class_NSApp           = objc.GetClass("NSApp")
	sel_sharedApplication = objc.RegisterName("sharedApplication")
	sel_run               = objc.RegisterName("run")
)

func main() {
	_, err := purego.Dlopen("/System/Library/Frameworks/AppKit.framework/AppKit", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
	if err != nil {
		panic(err)
	}

	objc.ID(class_NSApplication).Send(sel_sharedApplication)
	objc.ID(class_NSApp).Send(sel_run)
}

Looks like the main runloop isn't executed and the application exits immediately.

Many thanks for your work on this - it's awesome!

dyld: could not create thread local variables pthread key

Greetings ๐Ÿ‘‹, we encoutered this error:

$ git clone https://github.com/DataDog/go-libddwaf && cd go-libddwaf
[...]
$ go test -count=2000 -run 'TestWafObject'
dyld[98255]: could not create thread local variables pthread key
SIGABRT: abort
PC=0x1a8d6d118 m=16 sigcode=0
signal arrived during cgo execution
[...]

This happens on Mac M1 and go 1.20.5 and 1.21rc3 with cgo enabled and disabled but it does not happens on linux and it happens in the latest version of purego but also on the previous one. I did not manage to reproduce it in a test (yet) but this is very likely related to something else: as you can see I put the -count=2000 for a lot dlopen calls happens so the error happens, but even from the second call to dlopen some wierd behaviours starts to happens, like calls to new in C++ throwing std::bad_alloc. We avoided those wierd behaviour but this error stops us from doing more dlopen calls. Could the TLS be badly setup ? Or do you think the OSX dyld is going astray ?

C Stacktrace (I had to let lldb run for 15min) ``` * thread #19, stop reason = signal SIGABRT * frame #0: 0x00000001a8d6d118 dyld`__abort_with_payload + 8 frame #1: 0x00000001a8d78d7c dyld`abort_with_payload_wrapper_internal + 104 frame #2: 0x00000001a8d78db0 dyld`abort_with_payload + 16 frame #3: 0x00000001a8d048a8 dyld`dyld4::halt(char const*) + 328 frame #4: 0x00000001a8d0deb8 dyld`invocation function for block in dyld4::RuntimeState::setUpTLVs(dyld3::MachOAnalyzer const*) + 328 frame #5: 0x00000001a8d5f988 dyld`void dyld3::MachOAnalyzer::forEachThreadLocalVariableInSection(Diagnostics&, dyld3::MachOFile::SectionInfo const&, void (void* (**)(dyld3::MachOAnalyzer::TLV_Thunk*), unsigned long*) block_pointer) const + 104 frame #6: 0x00000001a8d511a4 dyld`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 528 frame #7: 0x00000001a8cfc2d8 dyld`dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 296 frame #8: 0x00000001a8d501cc dyld`dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 192 frame #9: 0x00000001a8d5f7e8 dyld`dyld3::MachOAnalyzer::forEachThreadLocalVariable(Diagnostics&, void (void* (**)(dyld3::MachOAnalyzer::TLV_Thunk*), unsigned long*) block_pointer) const + 208 frame #10: 0x00000001a8d0dcd4 dyld`dyld4::RuntimeState::setUpTLVs(dyld3::MachOAnalyzer const*) + 312 frame #11: 0x00000001a8d3d4d4 dyld`dyld4::APIs::dlopen_from(char const*, int, void*)::$_1::operator()() const + 1196 frame #12: 0x00000001a8d36b6c dyld`dyld4::APIs::dlopen_from(char const*, int, void*) + 948 frame #13: 0x00000001001a1654 go-libddwaf.test`syscall9X + 100 frame #14: 0x000000010006bb9c go-libddwaf.test`runtime.asmcgocall.abi0 + 124 ```
Go Stacktrace ``` dyld[98255]: could not create thread local variables pthread key SIGABRT: abort PC=0x1a8d6d118 m=16 sigcode=0 signal arrived during cgo execution

goroutine 365100 [syscall]:
runtime.cgocall(0x100be24c0, 0x140003733f0)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/cgocall.go:157 +0x44 fp=0x140006bb4d0 sp=0x140006bb490 pc=0x100a43714
github.com/ebitengine/purego.RegisterFunc.func1({0x1400080f0e0?, 0x2?, 0x2?})
/Users/eliott.bouhana/go/pkg/mod/github.com/ebitengine/[email protected]/func.go:212 +0x788 fp=0x140006bb850 sp=0x140006bb4d0 pc=0x100be1178
reflect.callReflect(0x1400007a330, 0x140006bbe38, 0x140006bbc98, 0x140006bbca0)
/Users/eliott.bouhana/go/go1.21rc3/src/reflect/value.go:782 +0x3f4 fp=0x140006bbc40 sp=0x140006bb850 pc=0x100b0f424
reflect.callReflect(0x1400007a330, 0x140005a4e38, 0x140005a4c98, 0x140005a4ca0)
:1 +0x28 fp=0x140006bbc70 sp=0x140006bbc40 pc=0x100b1ed78
reflect.makeFuncStub()
/Users/eliott.bouhana/go/go1.21rc3/src/reflect/asm_arm64.s:48 +0x58 fp=0x140006bbe30 sp=0x140006bbc70 pc=0x100b1ad88
github.com/ebitengine/purego.Dlopen({0x140000240f0?, 0x140001451e0?}, 0x0?)
/Users/eliott.bouhana/go/pkg/mod/github.com/ebitengine/[email protected]/dlfcn.go:37 +0x2c fp=0x140006bbe70 sp=0x140006bbe30 pc=0x100be04bc
github.com/DataDog/go-libddwaf.getLibddwaf(0x6c7829b91d?)
/Users/eliott.bouhana/dd/go-libddwaf/ctypes_test.go:29 +0x114 fp=0x140006bbf00 sp=0x140006bbe70 pc=0x100ccba94
github.com/DataDog/go-libddwaf.TestWafObject(0x0?)
/Users/eliott.bouhana/dd/go-libddwaf/ctypes_test.go:42 +0x20 fp=0x140006bbf60 sp=0x140006bbf00 pc=0x100ccbc30
testing.tRunner(0x140001451e0, 0x100def828)
/Users/eliott.bouhana/go/go1.21rc3/src/testing/testing.go:1595 +0xe8 fp=0x140006bbfb0 sp=0x140006bbf60 pc=0x100b4f528
testing.(*T).Run.func1()
/Users/eliott.bouhana/go/go1.21rc3/src/testing/testing.go:1648 +0x2c fp=0x140006bbfd0 sp=0x140006bbfb0 pc=0x100b5033c
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140006bbfd0 sp=0x140006bbfd0 pc=0x100aacc74
created by testing.(*T).Run in goroutine 1
/Users/eliott.bouhana/go/go1.21rc3/src/testing/testing.go:1648 +0x33c

goroutine 1 [chan receive]:
runtime.gopark(0x140006bd9b8?, 0x100a4becc?, 0xe8?, 0xe2?, 0x18?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x140006bd940 sp=0x140006bd920 pc=0x100a78978
runtime.chanrecv(0x1400036ae00, 0x140006bda3f, 0x1)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/chan.go:583 +0x414 fp=0x140006bd9c0 sp=0x140006bd940 pc=0x100a45144
runtime.chanrecv1(0x1011289e0?, 0x100d88100?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/chan.go:442 +0x14 fp=0x140006bd9f0 sp=0x140006bd9c0 pc=0x100a44cf4
testing.(*T).Run(0x14000145040, {0x100cdbd3b?, 0x11a78eca6863?}, 0x100def828)
/Users/eliott.bouhana/go/go1.21rc3/src/testing/testing.go:1649 +0x350 fp=0x140006bdab0 sp=0x140006bd9f0 pc=0x100b50200
testing.runTests.func1(0x14000145040?)
/Users/eliott.bouhana/go/go1.21rc3/src/testing/testing.go:2054 +0x48 fp=0x140006bdb00 sp=0x140006bdab0 pc=0x100b52018
testing.tRunner(0x14000145040, 0x140006bdc28)
/Users/eliott.bouhana/go/go1.21rc3/src/testing/testing.go:1595 +0xe8 fp=0x140006bdb50 sp=0x140006bdb00 pc=0x100b4f528
testing.runTests(0x140000a8000?, {0x1011225c0, 0x11, 0x11}, {0x147e355b0?, 0x100a66ac0?, 0x1011290a0?})
/Users/eliott.bouhana/go/go1.21rc3/src/testing/testing.go:2052 +0x3b4 fp=0x140006bdc50 sp=0x140006bdb50 pc=0x100b51f14
testing.(*M).Run(0x140000a8000)
/Users/eliott.bouhana/go/go1.21rc3/src/testing/testing.go:1925 +0x534 fp=0x140006bdea0 sp=0x140006bdc50 pc=0x100b50bf4
main.main()
_testmain.go:81 +0x1a8 fp=0x140006bdf30 sp=0x140006bdea0 pc=0x100cd81e8
runtime.main()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:267 +0x2bc fp=0x140006bdfd0 sp=0x140006bdf30 pc=0x100a7851c
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140006bdfd0 sp=0x140006bdfd0 pc=0x100aacc74

goroutine 2 [force gc (idle), 2 minutes]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x1400004af90 sp=0x1400004af70 pc=0x100a78978
runtime.goparkunlock(...)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:404
runtime.forcegchelper()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:322 +0xb8 fp=0x1400004afd0 sp=0x1400004af90 pc=0x100a787d8
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x1400004afd0 sp=0x1400004afd0 pc=0x100aacc74
created by runtime.init.6 in goroutine 1
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:310 +0x24

goroutine 3 [GC sweep wait]:
runtime.gopark(0x101128b01?, 0x101128be0?, 0xc?, 0x14?, 0x1?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x1400004b760 sp=0x1400004b740 pc=0x100a78978
runtime.goparkunlock(...)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:404
runtime.bgsweep(0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgcsweep.go:321 +0x108 fp=0x1400004b7b0 sp=0x1400004b760 pc=0x100a63678
runtime.gcenable.func1()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:200 +0x28 fp=0x1400004b7d0 sp=0x1400004b7b0 pc=0x100a580d8
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x1400004b7d0 sp=0x1400004b7d0 pc=0x100aacc74
created by runtime.gcenable in goroutine 1
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:200 +0x6c

goroutine 4 [sleep]:
runtime.gopark(0x140000120a0?, 0x11a7f4d3c3c5?, 0x0?, 0x0?, 0x100def9d0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x1400004bf10 sp=0x1400004bef0 pc=0x100a78978
runtime.goparkunlock(...)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:404
runtime.(*scavengerState).sleep(0x101129120, 0x410e4b3000000000)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgcscavenge.go:504 +0x128 fp=0x1400004bf80 sp=0x1400004bf10 pc=0x100a61078
runtime.bgscavenge(0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgcscavenge.go:662 +0x9c fp=0x1400004bfb0 sp=0x1400004bf80 pc=0x100a6142c
runtime.gcenable.func2()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:201 +0x28 fp=0x1400004bfd0 sp=0x1400004bfb0 pc=0x100a58078
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x1400004bfd0 sp=0x1400004bfd0 pc=0x100aacc74
created by runtime.gcenable in goroutine 1
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:201 +0xac

goroutine 5 [finalizer wait]:
runtime.gopark(0x0?, 0x100def850?, 0x20?, 0x81?, 0x2000000020?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x1400004a580 sp=0x1400004a560 pc=0x100a78978
runtime.runfinq()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mfinal.go:193 +0x108 fp=0x1400004a7d0 sp=0x1400004a580 pc=0x100a571c8
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x1400004a7d0 sp=0x1400004a7d0 pc=0x100aacc74
created by runtime.createfing in goroutine 1
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mfinal.go:163 +0x80

goroutine 258 [GC worker (idle)]:
runtime.gopark(0x11a78ebbcada?, 0x1400059cf40?, 0x1a?, 0x14?, 0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x140001e9f30 sp=0x140001e9f10 pc=0x100a78978
runtime.gcBgMarkWorker()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1293 +0xd8 fp=0x140001e9fd0 sp=0x140001e9f30 pc=0x100a59d28
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140001e9fd0 sp=0x140001e9fd0 pc=0x100aacc74
created by runtime.gcBgMarkStartWorkers in goroutine 72
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1217 +0x28

goroutine 259 [GC worker (idle)]:
runtime.gopark(0x11a78ebcb31c?, 0x1?, 0xa7?, 0xed?, 0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x140001def30 sp=0x140001def10 pc=0x100a78978
runtime.gcBgMarkWorker()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1293 +0xd8 fp=0x140001defd0 sp=0x140001def30 pc=0x100a59d28
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140001defd0 sp=0x140001defd0 pc=0x100aacc74
created by runtime.gcBgMarkStartWorkers in goroutine 72
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1217 +0x28

goroutine 260 [GC worker (idle)]:
runtime.gopark(0x11a78e2039fb?, 0x1?, 0x92?, 0x28?, 0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x140001dcf30 sp=0x140001dcf10 pc=0x100a78978
runtime.gcBgMarkWorker()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1293 +0xd8 fp=0x140001dcfd0 sp=0x140001dcf30 pc=0x100a59d28
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140001dcfd0 sp=0x140001dcfd0 pc=0x100aacc74
created by runtime.gcBgMarkStartWorkers in goroutine 72
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1217 +0x28

goroutine 274 [GC worker (idle)]:
runtime.gopark(0x11a78e4a65b8?, 0x1?, 0x46?, 0x18?, 0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x1400028e730 sp=0x1400028e710 pc=0x100a78978
runtime.gcBgMarkWorker()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1293 +0xd8 fp=0x1400028e7d0 sp=0x1400028e730 pc=0x100a59d28
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x1400028e7d0 sp=0x1400028e7d0 pc=0x100aacc74
created by runtime.gcBgMarkStartWorkers in goroutine 72
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1217 +0x28

goroutine 261 [GC worker (idle)]:
runtime.gopark(0x10116aca0?, 0x1?, 0x19?, 0x5e?, 0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x14000287730 sp=0x14000287710 pc=0x100a78978
runtime.gcBgMarkWorker()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1293 +0xd8 fp=0x140002877d0 sp=0x14000287730 pc=0x100a59d28
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140002877d0 sp=0x140002877d0 pc=0x100aacc74
created by runtime.gcBgMarkStartWorkers in goroutine 72
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1217 +0x28

goroutine 290 [GC worker (idle)]:
runtime.gopark(0x10116aca0?, 0x1?, 0x1b?, 0xb7?, 0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x140001e2f30 sp=0x140001e2f10 pc=0x100a78978
runtime.gcBgMarkWorker()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1293 +0xd8 fp=0x140001e2fd0 sp=0x140001e2f30 pc=0x100a59d28
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140001e2fd0 sp=0x140001e2fd0 pc=0x100aacc74
created by runtime.gcBgMarkStartWorkers in goroutine 72
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1217 +0x28

goroutine 275 [GC worker (idle)]:
runtime.gopark(0x11a78ebc2c58?, 0x3?, 0x9e?, 0x39?, 0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x1400028ef30 sp=0x1400028ef10 pc=0x100a78978
runtime.gcBgMarkWorker()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1293 +0xd8 fp=0x1400028efd0 sp=0x1400028ef30 pc=0x100a59d28
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x1400028efd0 sp=0x1400028efd0 pc=0x100aacc74
created by runtime.gcBgMarkStartWorkers in goroutine 72
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1217 +0x28

goroutine 276 [GC worker (idle)]:
runtime.gopark(0x11a78e488104?, 0x3?, 0x8e?, 0xec?, 0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x140001f2730 sp=0x140001f2710 pc=0x100a78978
runtime.gcBgMarkWorker()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1293 +0xd8 fp=0x140001f27d0 sp=0x140001f2730 pc=0x100a59d28
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140001f27d0 sp=0x140001f27d0 pc=0x100aacc74
created by runtime.gcBgMarkStartWorkers in goroutine 72
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1217 +0x28

goroutine 262 [GC worker (idle)]:
runtime.gopark(0x11a78ebc2f46?, 0x3?, 0xc4?, 0x2?, 0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x140001e6f30 sp=0x140001e6f10 pc=0x100a78978
runtime.gcBgMarkWorker()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1293 +0xd8 fp=0x140001e6fd0 sp=0x140001e6f30 pc=0x100a59d28
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x140001e6fd0 sp=0x140001e6fd0 pc=0x100aacc74
created by runtime.gcBgMarkStartWorkers in goroutine 72
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1217 +0x28

goroutine 291 [GC worker (idle)]:
runtime.gopark(0x11a78e4a84a4?, 0x1?, 0x3d?, 0xcd?, 0x0?)
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/proc.go:398 +0xc8 fp=0x1400029a730 sp=0x1400029a710 pc=0x100a78978
runtime.gcBgMarkWorker()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1293 +0xd8 fp=0x1400029a7d0 sp=0x1400029a730 pc=0x100a59d28
runtime.goexit()
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/asm_arm64.s:1197 +0x4 fp=0x1400029a7d0 sp=0x1400029a7d0 pc=0x100aacc74
created by runtime.gcBgMarkStartWorkers in goroutine 72
/Users/eliott.bouhana/go/go1.21rc3/src/runtime/mgc.go:1217 +0x28

r0 0x6
r1 0x9
r2 0x176cb0ab0
r3 0x14
r4 0x176cb06b0
r5 0x0
r6 0x0
r7 0x0
r8 0x20
r9 0x9
r10 0x1
r11 0xa
r12 0x0
r13 0x39
r14 0xf0000
r15 0x8000
r16 0x209
r17 0x1a8cfd35c
r18 0x0
r19 0x0
r20 0x176cb06b0
r21 0x14
r22 0x176cb0ab0
r23 0x9
r24 0x6
r25 0x2d3144558
r26 0x176cb14c0
r27 0x2d3144648
r28 0x8
r29 0x176cb0680
lr 0x1a8d78d7c
sp 0x176cb0640
pc 0x1a8d6d118
fault 0x1a8d6d118
exit status 2
FAIL github.com/DataDog/go-libddwaf 171.877s

</details>

Can we have a string instead of SEL for `Send`?

I think we have already discussed this, but I was wondering why we have SEL as the first argument of Send, not a string.

In most cases, uses make a SEL by objc.RegisterName at that place, so isn't this redundant? Sorry if you have already explained the reason.

@TotallyGamerJet

support Arm64 iOS with Go 1.15

Now Go 1.15 for Arm64 iOS doesn't work as callbackWrapPicker is missing.

We don't have to care about macOS since Go 1.15 doesn't support Arm macOS. This support started as of Go1.16.

This is nice to have and low priority.

document package maturity

This project seems very cool!

However it's hard to assess the relative maturity of this package; which features it has or doesn't have, what are the largest caveats or roadblocks that one might encounter.

It would be super great to have some high-level documentation about what consumers of this package can expect so that people can assess how suitable/viable this package is.

Thanks! :)

Bazel build of purego failing

Bazel does not use the go usual build process for an increase in building speed. For this, they use their usual sandbox which makes the current working directory a random temporary directory and not the current package. This makes cross-package #include unable to work since the included file is not at the right place at build time. Here is the error:

external/com_github_ebitengine_purego/internal/fakecgo/asm_arm64.s:7: 
#include: open /var/folders/fn/j3_08pb94p9_hyz4myff84br0000gq/T/abi/abi_arm64.h: no such file or directory
compilepkg: error running subcommand external/go_sdk/pkg/tool/darwin_arm64/asm: exit status 1

The files where it fails are the following:

Purego is especially useful in Bazel environments because CGO is disabled by default. I checked on bazel side and this behaviour seems unavoidable. Do we know if cross-package usage of #include is really something that is officially supported (in which case bazel is at fault) and not a happy coincidence ? It is only a matter of copying the files in purego/internal/abi to purego/internal/fakecgo and purego/, in which case I can do the PR, or am I missing something ?

Question: strategy for determining file names

On Linux, the name of the file is needed to open it. A recent test failed on one machine as the installed library had a suffix of ".0". What is the guidance on determining filenames on Linux so that they can be opened? Many thanks ๐Ÿ™

Linux NewCallback

It would seem that NewCallback is not implemented on Linux.
Is this already in development?

Add GoString and CString functions to match C package API

When using the the special C package there are a few functions that convert from Go types to C types and vice versa. When porting code that uses C it's annoying not having an equivalent to these functions. Especially, GoString and CString which convert a C string to a Go string and vice versa. Even ebitengine had to have it's own implementation and writing these over and over is begging for mistakes. Purego already uses it's own internal versions.

I purpose adding these two functions to purego with the following signatures.

// Cstring converts a Go string to a null-terminated C string.
// The C string is allocated in the C heap using malloc.
// It is the caller's responsibility to arrange for it to be
// freed, such as by calling free.
func CString(string) *byte

// GoString converts a null-terminated C string and copies it into
// a Go allocated string.
func GoString(*byte) string

I believe these are the most used functions so should be added first. If there is strong demand later for the other ones a new issue can be created.

Mac Callback

Hi there!

I'm trying to hook into Apple's GCD method dispatch_async to schedule callbacks on the main thread. I've gotten pretty far but the final call never returns.

The obj-c code I'm trying to replace is:

	dispatch_async(dispatch_get_main_queue(), ^{
		// some code
	});

I discovered that dispatch_get_main_queue is actually a macro and returns the address of _dispatch_main_q.
The following is where I'm up to. It feels like I'm missing just one final piece:

func dispatchTest() {
	libSystem, err := purego.Dlopen("libSystem.dylib", purego.RTLD_NOW|purego.RTLD_GLOBAL)
	if err != nil {
		panic(err)
	}
	dispatchMainQueue, err := purego.Dlsym(libSystem, "_dispatch_main_q")
	if err != nil {
		panic(err)
	}
	var dispatch_async func(uintptr, uintptr)
	goFunc := func() {
		fmt.Println("Hello from the main thread")
	}
	callback := purego.NewCallback(goFunc)
	purego.RegisterLibFunc(&dispatch_async, libSystem, "dispatch_async")
	dispatch_async(dispatchMainQueue, callback)
	println("i never get here")
}

func main() {

	// Since the error wasn't checked before check it before calling any code.
	NSApp := NewNSSharedApplication()
	go func() {
		time.Sleep(1 * time.Second)
		dispatchTest()
	}()
	NSApp.Run()
}

The NSApp code is just the same as from last ticket so left out to simplify the example.
Nothing is crashing or suggesting anything bad is happening. Am I using NewCallback correctly? My guess is that it's to do with C vs Obj-C calling conventions...

Any help appreciated!

chip: Apple M2 Pro
macOS: 13.2.1 (22D68)

Implement fakecgo

This issue tracks the implementation of fakecgo which is needed for some frameworks to function properly when CGO_ENABLED=0. It will be similar to notti/nocgo

thread local storage not work

when link the dynamic library by cgo (without purego), thread local storage work fine.

when use purego load the dynamic library, thread local storage return 0 at first visit, then return value before assign from other os thread.

confirm the problem from linux, windows, macOS. (go1.20.5)

Give the ability to perform dlopen(NULL)

As per the man page, dlopen(NULL) is supposed to return the equivalent of the macro RTLD_DEFAULT which not accessible from go code obviously and is implementation defined. This function is especially useful to run already loaded symbols like the ones loaded with the pragma go:cgo_import_dynamic.

But since we pass a string to Dlopen, a nil value cannot be passed as parameter. How could we give access to this feature ?

tag `v0.2.0-alpha.1`

The latest tag is now v0.2.0-alpha but this is outdated. As there has not been drastic changes in the main branch, I'd like to tag v0.2.0-alpha.1 for the latest commit.

@TotallyGamerJet, what do you think?

BTW, I plan to make 0.2 branch including non-alpha v0.2.x versions when Ebitengine v2.5 is released.

MTLRenderCommandEncoder setFrontFacing

I tried to extend the Metal wrapper with support for the "setFrontFacing:" method on the MTLRenderCommandEncoder class, but it consistently crashes. I've included the code and the error below. Any ideas what might be wrong?

var sel_setFrontFacing = objc.RegisterName("setFrontFacing:")

// SetFrontFacing sets the winding order of front-facing primitives. The default is clockwise.
//
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515499-setfrontfacing
func (rce RenderCommandEncoder) SetFrontFacing(winding Winding) {
	rce.commandEncoder.Send(sel_setFrontFacing, winding)
}

// Winding defines the vertex winding rule that determines a front-facing primitive.
//
// Reference: https://developer.apple.com/documentation/metal/mtlwinding.
type Winding uint8

const (
	// Primitives whose vertices are specified in clockwise order are front-facing.
	WindingClockwise Winding = 0
	// Primitives whose vertices are specified in counter-clockwise order are front-facing.
	WindingCounterClockwise Winding = 1
)
2023-03-23 20:37:53.213 myapp[7163:25611636] -[AGXG13GFamilyRenderContext setFrontFacing:]: unrecognized selector sent to instance 0x6000024442d0
2023-03-23 20:37:53.213 myapp[7163:25611636] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[AGXG13GFamilyRenderContext setFrontFacing:]: unrecognized selector sent to instance 0x6000024442d0'
*** First throw call stack:
(
	0   CoreFoundation                      0x00000001b002ab08 __exceptionPreprocess + 240
	1   libobjc.A.dylib                     0x00000001afd75e14 objc_exception_throw + 60
	2   CoreFoundation                      0x00000001b00bea68 -[NSObject(NSObject) __retain_OA] + 0
	3   CoreFoundation                      0x00000001aff89e14 ___forwarding___ + 1764
	4   CoreFoundation                      0x00000001aff89670 _CF_forwarding_prep_0 + 96
	5   myapp                               0x00000001005c84f4 syscall9X + 100
	6   myapp                               0x000000010056b27c runtime.asmcgocall.abi0 + 124
)
libc++abi: terminating with uncaught exception of type NSException
SIGABRT: abort
PC=0x1afea8db8 m=0 sigcode=0
signal arrived during cgo execution

goroutine 1 [syscall, locked to thread]:
runtime.cgocall(0x1005c8490, 0x1400018b3f0)
	/opt/homebrew/Cellar/go/1.19/libexec/src/runtime/cgocall.go:158 +0x54 fp=0x1400012cf20 sp=0x1400012cee0 pc=0x10050d894
github.com/ebitengine/purego.RegisterFunc.func1({0x14000189540?, 0x3?, 0x8?})
	...snip.../go/pkg/mod/github.com/ebitengine/[email protected]/func.go:188 +0x2ac fp=0x1400012d240 sp=0x1400012cf20 pc=0x1005c69cc
reflect.callReflect(0x1400011c690, 0x1400012d818, 0x1400012d678, 0x1400012d680)
	/opt/homebrew/Cellar/go/1.19/libexec/src/reflect/value.go:770 +0x3e4 fp=0x1400012d620 sp=0x1400012d240 pc=0x100587044
reflect.callReflect(0x1400011c690, 0x1400012d818, 0x1400012d678, 0x1400012d680)
	<autogenerated>:1 +0x28 fp=0x1400012d650 sp=0x1400012d620 pc=0x10058ad48
reflect.makeFuncStub()
	/opt/homebrew/Cellar/go/1.19/libexec/src/reflect/asm_arm64.s:48 +0x58 fp=0x1400012d810 sp=0x1400012d650 pc=0x10058ac58
github.com/ebitengine/purego/objc.ID.Send(...)
	...snip.../go/pkg/mod/github.com/ebitengine/[email protected]/objc/objc_runtime_darwin.go:76
...snip.../metal/mtl.RenderCommandEncoder.SetFrontFacing(...)
	...snip.../metal/mtl/command_encoder_render.go:119

Is it possible to Dlopen an embed shared object?

Golang has native embed support since golang 1.16.

import "embed"

//go:embed hello.txt
var f embed.FS
data, _ := f.ReadFile("hello.txt")
print(string(data))

Is it possible to Dlopen a shared object within embed fs? This way we can distribute binaries more easily.

test fails with Go 1.15 on amd64 and purego 6ed67cd5f570b9c0d5710dfced2b1e77ad92ca1b

go1.15.15 run -tags=example ./examples/audio
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4176b9e]

goroutine 17 [running, locked to thread]:
github.com/ebitengine/purego.callbackWrap(0xc000070000)
        /Users/hajimehoshi/go/pkg/mod/github.com/ebitengine/[email protected]/syscall_darwin.go:123 +0x17e
github.com/ebitengine/purego.callbackWrapPicker(0xc000070000, 0x4176f80)
        /Users/hajimehoshi/go/pkg/mod/github.com/ebitengine/[email protected]/syscall_darwin.go:106 +0x2b
exit status 2

/CC @TotallyGamerJet Please take a look... Thanks,

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.