From 3018a7da0d61782a62f633309deac0948b3e3aa0 Mon Sep 17 00:00:00 2001 From: sergystepanov Date: Wed, 1 Jul 2026 15:38:56 +0300 Subject: [PATCH] Drop dlfcn dependency on Windows Use direct syscalls instead. Needed for static builds. --- README.md | 8 +- pkg/worker/caged/libretro/nanoarch/dl_unix.go | 54 +++++++++++++ .../caged/libretro/nanoarch/dl_windows.go | 30 ++++++++ pkg/worker/caged/libretro/nanoarch/loader.go | 75 ------------------- .../caged/libretro/nanoarch/nanoarch.go | 55 +++++++------- 5 files changed, 113 insertions(+), 109 deletions(-) create mode 100644 pkg/worker/caged/libretro/nanoarch/dl_unix.go create mode 100644 pkg/worker/caged/libretro/nanoarch/dl_windows.go delete mode 100644 pkg/worker/caged/libretro/nanoarch/loader.go diff --git a/README.md b/README.md index 25391f2c..bada8964 100644 --- a/README.md +++ b/README.md @@ -54,17 +54,17 @@ a better sense of performance. ## Development environment * Install [Go](https://golang.org/doc/install) -* Install GStreamer and SDL2: +* Install GStreamer ``` # Ubuntu / Windows (WSL2) -apt-get install -y make gcc pkg-config libsdl2-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-good +apt-get install -y make gcc pkg-config libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-good # macOS -brew install pkg-config sdl2 gstreamer gst-plugins-base gst-plugins-good +brew install pkg-config gstreamer gst-plugins-base gst-plugins-good # Windows (MSYS2) -pacman -Sy --noconfirm --needed git make mingw-w64-ucrt-x86_64-{gcc,pkgconf,dlfcn,SDL2,gstreamer,gst-plugins-base,gst-plugins-good} +pacman -Sy --noconfirm --needed git make mingw-w64-ucrt-x86_64-{gcc,pkgconf,gstreamer,gst-plugins-base,gst-plugins-good} ``` Because the coordinator and workers need to run simultaneously. Workers connect to the coordinator. diff --git a/pkg/worker/caged/libretro/nanoarch/dl_unix.go b/pkg/worker/caged/libretro/nanoarch/dl_unix.go new file mode 100644 index 00000000..e903adfe --- /dev/null +++ b/pkg/worker/caged/libretro/nanoarch/dl_unix.go @@ -0,0 +1,54 @@ +//go:build !windows + +package nanoarch + +import ( + "fmt" + "unsafe" +) + +/* +#cgo linux LDFLAGS: -ldl +#include +#include +*/ +import "C" + +type dlib struct { + ptr unsafe.Pointer +} + +func open(file string) (*dlib, error) { + cs := C.CString(file) + defer C.free(unsafe.Pointer(cs)) + handle := C.dlopen(cs, C.RTLD_LAZY) + if handle == nil { + e := C.dlerror() + if e != nil { + return nil, fmt.Errorf("couldn't load the lib: %s", C.GoString(e)) + } + return nil, fmt.Errorf("couldn't load the lib") + } + return &dlib{ptr: handle}, nil +} + +func (d *dlib) load(name string) unsafe.Pointer { + cs := C.CString(name) + defer C.free(unsafe.Pointer(cs)) + ptr := C.dlsym(d.ptr, cs) + if ptr == nil { + panic("lib function not found: " + name) + } + return ptr +} + +func (d *dlib) close() error { + if d.ptr == nil { + return nil + } + code := int(C.dlclose(d.ptr)) + if code != 0 { + return fmt.Errorf("couldn't close the lib (%d)", code) + } + return nil +} diff --git a/pkg/worker/caged/libretro/nanoarch/dl_windows.go b/pkg/worker/caged/libretro/nanoarch/dl_windows.go new file mode 100644 index 00000000..2dad2dd6 --- /dev/null +++ b/pkg/worker/caged/libretro/nanoarch/dl_windows.go @@ -0,0 +1,30 @@ +//go:build windows + +package nanoarch + +import ( + "syscall" + "unsafe" +) + +type dlib struct { + h syscall.Handle +} + +func open(file string) (*dlib, error) { + h, err := syscall.LoadLibrary(file) + if err != nil { + return nil, err + } + return &dlib{h: h}, nil +} + +func (d *dlib) load(name string) unsafe.Pointer { + proc, err := syscall.GetProcAddress(d.h, name) + if err != nil { + panic("lib function not found: " + name) + } + return unsafe.Pointer(proc) +} + +func (d *dlib) close() error { return syscall.FreeLibrary(d.h) } diff --git a/pkg/worker/caged/libretro/nanoarch/loader.go b/pkg/worker/caged/libretro/nanoarch/loader.go deleted file mode 100644 index d7d0c662..00000000 --- a/pkg/worker/caged/libretro/nanoarch/loader.go +++ /dev/null @@ -1,75 +0,0 @@ -package nanoarch - -import ( - "errors" - "os" - "path" - "strconv" - "strings" - "unsafe" -) - -/* -#cgo LDFLAGS: -ldl -#include -#include -*/ -import "C" - -func loadFunction(handle unsafe.Pointer, name string) unsafe.Pointer { - cs := C.CString(name) - defer C.free(unsafe.Pointer(cs)) - ptr := C.dlsym(handle, cs) - if ptr == nil { - panic("lib function not found: " + name) - } - return ptr -} - -func loadLib(filepath string) (handle unsafe.Pointer, err error) { - handle = open(filepath) - if handle == nil { - e := C.dlerror() - if e != nil { - err = errors.New(C.GoString(e)) - } else { - err = errors.New("couldn't load the lib") - } - } - return -} - -func loadLibRollingRollingRolling(filepath string) (handle unsafe.Pointer, err error) { - dir, lib := path.Dir(filepath), path.Base(filepath) - files, err := os.ReadDir(dir) - if err != nil { - return nil, errors.New("couldn't find 'n load the lib") - } - - for _, file := range files { - if !file.IsDir() && strings.HasPrefix(file.Name(), lib) { - handle = open(path.Join(dir, file.Name())) - if handle != nil { - return handle, nil - } - } - } - return nil, errors.New("couldn't find 'n load the lib") -} - -func open(file string) unsafe.Pointer { - cs := C.CString(file) - defer C.free(unsafe.Pointer(cs)) - return C.dlopen(cs, C.RTLD_LAZY) -} - -func closeLib(handle unsafe.Pointer) (err error) { - if handle == nil { - return - } - code := int(C.dlclose(handle)) - if code != 0 { - return errors.New("couldn't close the lib (" + strconv.Itoa(code) + ")") - } - return -} diff --git a/pkg/worker/caged/libretro/nanoarch/nanoarch.go b/pkg/worker/caged/libretro/nanoarch/nanoarch.go index 079702d9..17f10fc6 100644 --- a/pkg/worker/caged/libretro/nanoarch/nanoarch.go +++ b/pkg/worker/caged/libretro/nanoarch/nanoarch.go @@ -209,37 +209,32 @@ func (n *Nanoarch) CoreLoad(meta Metadata) { n.options4rom = meta.Options4rom corePath := meta.LibPath + meta.LibExt - coreLib, err = loadLib(corePath) - // fallback to sequential lib loader (first successfully loaded) + coreLib, err = open(corePath) if err != nil { - n.log.Error().Err(err).Msgf("load fail: %v", corePath) - coreLib, err = loadLibRollingRollingRolling(corePath) - if err != nil { - n.log.Fatal().Err(err).Msgf("core load: %s", corePath) - } + n.log.Fatal().Err(err).Msgf("core load: %s", corePath) } - retroInit = loadFunction(coreLib, "retro_init") - retroDeinit = loadFunction(coreLib, "retro_deinit") - retroAPIVersion = loadFunction(coreLib, "retro_api_version") - retroGetSystemInfo = loadFunction(coreLib, "retro_get_system_info") - retroGetSystemAVInfo = loadFunction(coreLib, "retro_get_system_av_info") - retroSetEnvironment = loadFunction(coreLib, "retro_set_environment") - retroSetVideoRefresh = loadFunction(coreLib, "retro_set_video_refresh") - retroSetInputPoll = loadFunction(coreLib, "retro_set_input_poll") - retroSetInputState = loadFunction(coreLib, "retro_set_input_state") - retroSetAudioSample = loadFunction(coreLib, "retro_set_audio_sample") - retroSetAudioSampleBatch = loadFunction(coreLib, "retro_set_audio_sample_batch") - retroReset = loadFunction(coreLib, "retro_reset") - retroRun = loadFunction(coreLib, "retro_run") - retroLoadGame = loadFunction(coreLib, "retro_load_game") - retroUnloadGame = loadFunction(coreLib, "retro_unload_game") - retroSerializeSize = loadFunction(coreLib, "retro_serialize_size") - retroSerialize = loadFunction(coreLib, "retro_serialize") - retroUnserialize = loadFunction(coreLib, "retro_unserialize") - retroSetControllerPortDevice = loadFunction(coreLib, "retro_set_controller_port_device") - retroGetMemorySize = loadFunction(coreLib, "retro_get_memory_size") - retroGetMemoryData = loadFunction(coreLib, "retro_get_memory_data") + retroInit = coreLib.load("retro_init") + retroDeinit = coreLib.load("retro_deinit") + retroAPIVersion = coreLib.load("retro_api_version") + retroGetSystemInfo = coreLib.load("retro_get_system_info") + retroGetSystemAVInfo = coreLib.load("retro_get_system_av_info") + retroSetEnvironment = coreLib.load("retro_set_environment") + retroSetVideoRefresh = coreLib.load("retro_set_video_refresh") + retroSetInputPoll = coreLib.load("retro_set_input_poll") + retroSetInputState = coreLib.load("retro_set_input_state") + retroSetAudioSample = coreLib.load("retro_set_audio_sample") + retroSetAudioSampleBatch = coreLib.load("retro_set_audio_sample_batch") + retroReset = coreLib.load("retro_reset") + retroRun = coreLib.load("retro_run") + retroLoadGame = coreLib.load("retro_load_game") + retroUnloadGame = coreLib.load("retro_unload_game") + retroSerializeSize = coreLib.load("retro_serialize_size") + retroSerialize = coreLib.load("retro_serialize") + retroUnserialize = coreLib.load("retro_unserialize") + retroSetControllerPortDevice = coreLib.load("retro_set_controller_port_device") + retroGetMemorySize = coreLib.load("retro_get_memory_size") + retroGetMemoryData = coreLib.load("retro_get_memory_data") C.bridge_retro_set_environment(retroSetEnvironment, C.core_environment_cgo) C.bridge_retro_set_input_state(retroSetInputState, C.core_input_state_cgo) @@ -390,7 +385,7 @@ func (n *Nanoarch) Shutdown() { setRotation(0) Nan0.sys.av = C.struct_retro_system_av_info{} - if err := closeLib(coreLib); err != nil { + if err := coreLib.close(); err != nil { n.log.Error().Err(err).Msg("lib close failed") } n.options = nil @@ -611,11 +606,11 @@ func byteCountBinary(b int64) string { func (m Metadata) HasHack(h string) bool { return slices.Contains(m.Hacks, h) } var ( + coreLib *dlib retroAPIVersion unsafe.Pointer retroDeinit unsafe.Pointer retroGetSystemAVInfo unsafe.Pointer retroGetSystemInfo unsafe.Pointer - coreLib unsafe.Pointer retroInit unsafe.Pointer retroLoadGame unsafe.Pointer retroReset unsafe.Pointer