detached 0befdc3

Return errors from `Dlopen`, `Dlsym`, and `Dlclose` (#103)

This PR returns errors from the Dlopen, Dlsym, and Dlclose functions in order to provide a more idiomatic API.

Closes #100
commit
0befdc3778764be7c9997dbe0326b2761eeafcd1
author
Arsen Musayelyan · · 2023-02-24 12:48 UTC
committer
GitHub · · 2023-02-24 12:48 UTC
parent
0c858ac
signature
GPG Unverified4AEE18F83AFDEB23 The key is unknown
refs
tag: v0.3.0-alpha
8 files · +56 -34
M README.md +2 -2
@@ -41,8 +41,8 @@ func getSystemLibrary() string {
4141 }
4242
4343 func main() {
44- libc := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
45- if err := purego.Dlerror(); err != "" {
44+ libc, err := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
45+ if err != nil {
4646 panic(err)
4747 }
4848 var puts func(string)
@@ -41,8 +41,8 @@ func getSystemLibrary() string {
41 }41 }
42 42
43 func main() {43 func main() {
44- libc := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)44+ libc, err := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
45- if err := purego.Dlerror(); err != "" {45+ if err != nil {
46 panic(err)46 panic(err)
47 }47 }
48 var puts func(string)48 var puts func(string)
M dlfcn.go +17 -17
@@ -33,34 +33,34 @@ func init() {
3333 // A second call to Dlopen with the same path will return the same handle, but the internal
3434 // reference count for the handle will be incremented. Therefore, all
3535 // Dlopen calls should be balanced with a Dlclose call.
36-func Dlopen(path string, mode int) uintptr {
37- return fnDlopen(path, mode)
36+func Dlopen(path string, mode int) (uintptr, error) {
37+ u := fnDlopen(path, mode)
38+ if errStr := fnDlerror(); errStr != "" {
39+ return 0, Dlerror{errStr}
40+ }
41+ return u, nil
3842 }
3943
4044 // Dlsym takes a "handle" of a dynamic library returned by Dlopen and the symbol name.
4145 // It returns the address where that symbol is loaded into memory. If the symbol is not found,
4246 // in the specified library or any of the libraries that were automatically loaded by Dlopen
4347 // when that library was loaded, Dlsym returns zero.
44-func Dlsym(handle uintptr, name string) uintptr {
45- return fnDlsym(handle, name)
46-}
47-
48-// Dlerror returns a human-readable string describing the most recent error that
49-// occurred from Dlopen, Dlsym or Dlclose since the last call to Dlerror. It
50-// returns an empty string if no errors have occurred since initialization or
51-// since it was last called.
52-func Dlerror() string {
53- // msg is only valid until the next call to Dlerror
54- // which is why it gets copied into a Go string
55- return fnDlerror()
48+func Dlsym(handle uintptr, name string) (uintptr, error) {
49+ u := fnDlsym(handle, name)
50+ if errStr := fnDlerror(); errStr != "" {
51+ return 0, Dlerror{errStr}
52+ }
53+ return u, nil
5654 }
5755
5856 // Dlclose decrements the reference count on the dynamic library handle.
5957 // If the reference count drops to zero and no other loaded libraries
6058 // use symbols in it, then the dynamic library is unloaded.
61-// Dlclose returns false on success, and true on error.
62-func Dlclose(handle uintptr) bool {
63- return fnDlclose(handle)
59+func Dlclose(handle uintptr) error {
60+ if fnDlclose(handle) {
61+ return Dlerror{fnDlerror()}
62+ }
63+ return nil
6464 }
6565
6666 // these functions exist in dlfcn_stubs.s and are calling C functions linked to in dlfcn_GOOS.go
@@ -33,34 +33,34 @@ func init() {
33 // A second call to Dlopen with the same path will return the same handle, but the internal33 // A second call to Dlopen with the same path will return the same handle, but the internal
34 // reference count for the handle will be incremented. Therefore, all34 // reference count for the handle will be incremented. Therefore, all
35 // Dlopen calls should be balanced with a Dlclose call.35 // Dlopen calls should be balanced with a Dlclose call.
36-func Dlopen(path string, mode int) uintptr {36+func Dlopen(path string, mode int) (uintptr, error) {
37- return fnDlopen(path, mode)37+ u := fnDlopen(path, mode)
38+ if errStr := fnDlerror(); errStr != "" {
39+ return 0, Dlerror{errStr}
40+ }
41+ return u, nil
38 }42 }
39 43
40 // Dlsym takes a "handle" of a dynamic library returned by Dlopen and the symbol name.44 // Dlsym takes a "handle" of a dynamic library returned by Dlopen and the symbol name.
41 // It returns the address where that symbol is loaded into memory. If the symbol is not found,45 // It returns the address where that symbol is loaded into memory. If the symbol is not found,
42 // in the specified library or any of the libraries that were automatically loaded by Dlopen46 // in the specified library or any of the libraries that were automatically loaded by Dlopen
43 // when that library was loaded, Dlsym returns zero.47 // when that library was loaded, Dlsym returns zero.
44-func Dlsym(handle uintptr, name string) uintptr {48+func Dlsym(handle uintptr, name string) (uintptr, error) {
45- return fnDlsym(handle, name)49+ u := fnDlsym(handle, name)
46-}50+ if errStr := fnDlerror(); errStr != "" {
47-51+ return 0, Dlerror{errStr}
48-// Dlerror returns a human-readable string describing the most recent error that52+ }
49-// occurred from Dlopen, Dlsym or Dlclose since the last call to Dlerror. It53+ return u, nil
50-// returns an empty string if no errors have occurred since initialization or
51-// since it was last called.
52-func Dlerror() string {
53- // msg is only valid until the next call to Dlerror
54- // which is why it gets copied into a Go string
55- return fnDlerror()
56 }54 }
57 55
58 // Dlclose decrements the reference count on the dynamic library handle.56 // Dlclose decrements the reference count on the dynamic library handle.
59 // If the reference count drops to zero and no other loaded libraries57 // If the reference count drops to zero and no other loaded libraries
60 // use symbols in it, then the dynamic library is unloaded.58 // use symbols in it, then the dynamic library is unloaded.
61-// Dlclose returns false on success, and true on error.59+func Dlclose(handle uintptr) error {
62-func Dlclose(handle uintptr) bool {60+ if fnDlclose(handle) {
63- return fnDlclose(handle)61+ return Dlerror{fnDlerror()}
62+ }
63+ return nil
64 }64 }
65 65
66 // these functions exist in dlfcn_stubs.s and are calling C functions linked to in dlfcn_GOOS.go66 // these functions exist in dlfcn_stubs.s and are calling C functions linked to in dlfcn_GOOS.go
M dlfcn_test.go +4 -4
@@ -30,8 +30,8 @@ func TestRegisterFunc(t *testing.T) {
3030 if err != nil {
3131 t.Errorf("couldn't get system library: %s", err)
3232 }
33- libc := purego.Dlopen(library, purego.RTLD_NOW|purego.RTLD_GLOBAL)
34- if err := purego.Dlerror(); err != "" {
33+ libc, err := purego.Dlopen(library, purego.RTLD_NOW|purego.RTLD_GLOBAL)
34+ if err != nil {
3535 t.Errorf("failed to dlopen: %s", err)
3636 }
3737 var puts func(string)
@@ -71,8 +71,8 @@ func Test_qsort(t *testing.T) {
7171 if err != nil {
7272 t.Errorf("couldn't get system library: %s", err)
7373 }
74- libc := purego.Dlopen(library, purego.RTLD_NOW|purego.RTLD_GLOBAL)
75- if err := purego.Dlerror(); err != "" {
74+ libc, err := purego.Dlopen(library, purego.RTLD_NOW|purego.RTLD_GLOBAL)
75+ if err != nil {
7676 t.Errorf("failed to dlopen: %s", err)
7777 }
7878
@@ -30,8 +30,8 @@ func TestRegisterFunc(t *testing.T) {
30 if err != nil {30 if err != nil {
31 t.Errorf("couldn't get system library: %s", err)31 t.Errorf("couldn't get system library: %s", err)
32 }32 }
33- libc := purego.Dlopen(library, purego.RTLD_NOW|purego.RTLD_GLOBAL)33+ libc, err := purego.Dlopen(library, purego.RTLD_NOW|purego.RTLD_GLOBAL)
34- if err := purego.Dlerror(); err != "" {34+ if err != nil {
35 t.Errorf("failed to dlopen: %s", err)35 t.Errorf("failed to dlopen: %s", err)
36 }36 }
37 var puts func(string)37 var puts func(string)
@@ -71,8 +71,8 @@ func Test_qsort(t *testing.T) {
71 if err != nil {71 if err != nil {
72 t.Errorf("couldn't get system library: %s", err)72 t.Errorf("couldn't get system library: %s", err)
73 }73 }
74- libc := purego.Dlopen(library, purego.RTLD_NOW|purego.RTLD_GLOBAL)74+ libc, err := purego.Dlopen(library, purego.RTLD_NOW|purego.RTLD_GLOBAL)
75- if err := purego.Dlerror(); err != "" {75+ if err != nil {
76 t.Errorf("failed to dlopen: %s", err)76 t.Errorf("failed to dlopen: %s", err)
77 }77 }
78 78
A error.go +13 -0
@@ -0,0 +1,13 @@
1+// SPDX-License-Identifier: Apache-2.0
2+// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
3+
4+package purego
5+
6+// Dlerror represents an error value returned from Dlopen, Dlsym, or Dlclose.
7+type Dlerror struct {
8+ s string
9+}
10+
11+func (e Dlerror) Error() string {
12+ return e.s
13+}
@@ -0,0 +1,13 @@
1+// SPDX-License-Identifier: Apache-2.0
2+// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
3+
4+package purego
5+
6+// Dlerror represents an error value returned from Dlopen, Dlsym, or Dlclose.
7+type Dlerror struct {
8+ s string
9+}
10+
11+func (e Dlerror) Error() string {
12+ return e.s
13+}
M examples/libc/main.go +2 -2
@@ -24,8 +24,8 @@ func getSystemLibrary() string {
2424 }
2525
2626 func main() {
27- libc := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
28- if err := purego.Dlerror(); err != "" {
27+ libc, err := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
28+ if err != nil {
2929 panic(err)
3030 }
3131 var puts func(string)
@@ -24,8 +24,8 @@ func getSystemLibrary() string {
24 }24 }
25 25
26 func main() {26 func main() {
27- libc := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)27+ libc, err := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
28- if err := purego.Dlerror(); err != "" {28+ if err != nil {
29 panic(err)29 panic(err)
30 }30 }
31 var puts func(string)31 var puts func(string)
M func.go +3 -3
@@ -19,9 +19,9 @@ import (
1919 //
2020 // Windows does not support this function.
2121 func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {
22- sym := Dlsym(handle, name)
23- if sym == 0 {
24- panic("purego: couldn't find symbol: " + Dlerror())
22+ sym, err := Dlsym(handle, name)
23+ if err != nil {
24+ panic(err)
2525 }
2626 RegisterFunc(fptr, sym)
2727 }
@@ -19,9 +19,9 @@ import (
19 //19 //
20 // Windows does not support this function.20 // Windows does not support this function.
21 func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {21 func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {
22- sym := Dlsym(handle, name)22+ sym, err := Dlsym(handle, name)
23- if sym == 0 {23+ if err != nil {
24- panic("purego: couldn't find symbol: " + Dlerror())24+ panic(err)
25 }25 }
26 RegisterFunc(fptr, sym)26 RegisterFunc(fptr, sym)
27 }27 }
M objc/objc_runtime_darwin.go +11 -5
@@ -38,13 +38,19 @@ var (
3838 )
3939
4040 func init() {
41- objc := purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL)
42- if err := purego.Dlerror(); err != "" {
43- panic("objc: " + err)
41+ objc, err := purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL)
42+ if err != nil {
43+ panic(fmt.Errorf("objc: %w", err))
44+ }
45+ objc_msgSend_fn, err = purego.Dlsym(objc, "objc_msgSend")
46+ if err != nil {
47+ panic(fmt.Errorf("objc: %w", err))
4448 }
45- objc_msgSend_fn = purego.Dlsym(objc, "objc_msgSend")
4649 purego.RegisterFunc(&objc_msgSend, objc_msgSend_fn)
47- objc_msgSendSuper2_fn = purego.Dlsym(objc, "objc_msgSendSuper2")
50+ objc_msgSendSuper2_fn, err = purego.Dlsym(objc, "objc_msgSendSuper2")
51+ if err != nil {
52+ panic(fmt.Errorf("objc: %w", err))
53+ }
4854 purego.RegisterFunc(&objc_msgSendSuper2, objc_msgSendSuper2_fn)
4955 purego.RegisterLibFunc(&object_getClass, objc, "object_getClass")
5056 purego.RegisterLibFunc(&objc_getClass, objc, "objc_getClass")
@@ -38,13 +38,19 @@ var (
38 )38 )
39 39
40 func init() {40 func init() {
41- objc := purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL)41+ objc, err := purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL)
42- if err := purego.Dlerror(); err != "" {42+ if err != nil {
43- panic("objc: " + err)43+ panic(fmt.Errorf("objc: %w", err))
44+ }
45+ objc_msgSend_fn, err = purego.Dlsym(objc, "objc_msgSend")
46+ if err != nil {
47+ panic(fmt.Errorf("objc: %w", err))
44 }48 }
45- objc_msgSend_fn = purego.Dlsym(objc, "objc_msgSend")
46 purego.RegisterFunc(&objc_msgSend, objc_msgSend_fn)49 purego.RegisterFunc(&objc_msgSend, objc_msgSend_fn)
47- objc_msgSendSuper2_fn = purego.Dlsym(objc, "objc_msgSendSuper2")50+ objc_msgSendSuper2_fn, err = purego.Dlsym(objc, "objc_msgSendSuper2")
51+ if err != nil {
52+ panic(fmt.Errorf("objc: %w", err))
53+ }
48 purego.RegisterFunc(&objc_msgSendSuper2, objc_msgSendSuper2_fn)54 purego.RegisterFunc(&objc_msgSendSuper2, objc_msgSendSuper2_fn)
49 purego.RegisterLibFunc(&object_getClass, objc, "object_getClass")55 purego.RegisterLibFunc(&object_getClass, objc, "object_getClass")
50 purego.RegisterLibFunc(&objc_getClass, objc, "objc_getClass")56 purego.RegisterLibFunc(&objc_getClass, objc, "objc_getClass")
M objc/objc_runtime_darwin_test.go +4 -1
@@ -77,7 +77,10 @@ func ExampleID_SendSuper() {
7777
7878 func TestSend(t *testing.T) {
7979 // NSNumber comes from Foundation so make sure we have linked to that framework.
80- _ = purego.Dlopen("Foundation.framework/Foundation", purego.RTLD_GLOBAL)
80+ _, err := purego.Dlopen("Foundation.framework/Foundation", purego.RTLD_GLOBAL)
81+ if err != nil {
82+ t.Fatal(err)
83+ }
8184 const double = float64(2.34)
8285 // Initialize a NSNumber
8386 NSNumber := objc.ID(objc.GetClass("NSNumber")).Send(objc.RegisterName("numberWithDouble:"), double)
@@ -77,7 +77,10 @@ func ExampleID_SendSuper() {
77 77
78 func TestSend(t *testing.T) {78 func TestSend(t *testing.T) {
79 // NSNumber comes from Foundation so make sure we have linked to that framework.79 // NSNumber comes from Foundation so make sure we have linked to that framework.
80- _ = purego.Dlopen("Foundation.framework/Foundation", purego.RTLD_GLOBAL)80+ _, err := purego.Dlopen("Foundation.framework/Foundation", purego.RTLD_GLOBAL)
81+ if err != nil {
82+ t.Fatal(err)
83+ }
81 const double = float64(2.34)84 const double = float64(2.34)
82 // Initialize a NSNumber85 // Initialize a NSNumber
83 NSNumber := objc.ID(objc.GetClass("NSNumber")).Send(objc.RegisterName("numberWithDouble:"), double)86 NSNumber := objc.ID(objc.GetClass("NSNumber")).Send(objc.RegisterName("numberWithDouble:"), double)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close