Skip to content

Commit e4cb9f8

Browse files
committed
Initial commit
0 parents  commit e4cb9f8

33 files changed

+1290
-0
lines changed

.gitignore

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# IDE
2+
.idea
3+
.vscode
4+
5+
# Binaries for programs and plugins
6+
*.exe
7+
*.exe~
8+
*.dll
9+
*.so
10+
*.dylib
11+
12+
13+
# Test binary, built with `go test -c`
14+
*.test
15+
16+
# Output of the go coverage tool, specifically when used with LiteIDE
17+
*.out
18+
19+
# Dependency directories (remove the comment below to include it)
20+
vendor/

LICENSE

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Copyright (c) 2022 Alexey Nosov.
2+
Copyright (c) 2009 The Go Authors. All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are
6+
met:
7+
8+
* Redistributions of source code must retain the above copyright
9+
notice, this list of conditions and the following disclaimer.
10+
* Redistributions in binary form must reproduce the above
11+
copyright notice, this list of conditions and the following disclaimer
12+
in the documentation and/or other materials provided with the
13+
distribution.
14+
* Neither the name of Google Inc. nor the names of its
15+
contributors may be used to endorse or promote products derived from
16+
this software without specific prior written permission.
17+
18+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# dlplugin
2+
3+
This package is based on the official Go `plugin` package, but modified to use any dynamic C libraries (Only Linux, FreeBSD, and macOS).
4+
5+
It provides a thread-safe interface for loading/unloading dynamic libraries, but the library symbols should be loaded manually using `PluginInitializer`.
6+
7+
8+
## Installation
9+
10+
`go get github.com/Devoter/dlplugin`
11+
12+
or use `go mod` tool.
13+
14+
15+
## Usage
16+
17+
**WARNING:** Windows implementation was not tested and should not be used.
18+
19+
This package uses `cgo`, it is highly recommended to read the [official CGO documentation](https://pkg.go.dev/cmd/cgo).
20+
21+
Open and prepare a plugin via `dlplugin.Open()` function:
22+
23+
```go
24+
Open(path string, initializer PluginInitializer) (*Plugin, error)
25+
```
26+
27+
It accepts a library filename and an initializer. The `Init()` method denotes a function type which initializes a plugin API.
28+
29+
```go
30+
type PluginInitializer interface {
31+
Init(lookup func(symName string) (uintptr, error)) error
32+
}
33+
```
34+
35+
An opened library may be closed using the `Close()` method of the `Plugin` or the `Close()` function:
36+
37+
```go
38+
func (p *Plugin) Close() error
39+
40+
func Close(p *Plugin) error
41+
```
42+
43+
## Examples
44+
45+
All examples have Makefiles, therefore you can build each example with the `make` command.
46+
47+
### Basic
48+
49+
This example is a program that prints "Hello, world!" via dynamic library call. The example contains two implementations of program: naive and with an interface.
50+
51+
<details>
52+
<summary>Plugin code</summary>
53+
54+
```go
55+
package main
56+
57+
import "C"
58+
import "fmt"
59+
60+
//export println
61+
func println(v *C.char) {
62+
s := C.GoString(v)
63+
64+
fmt.Println(s)
65+
}
66+
67+
func main() {}
68+
69+
```
70+
</details>
71+
72+
<details>
73+
<summary>Program code</summary>
74+
75+
```go
76+
package main
77+
78+
/*
79+
#include <stdint.h>
80+
#include <stdlib.h>
81+
82+
static void println(uintptr_t r, char *s)
83+
{
84+
((void (*)(char *))r)(s);
85+
}
86+
*/
87+
import "C"
88+
import (
89+
"flag"
90+
"fmt"
91+
"os"
92+
"unsafe"
93+
94+
"github.com/Devoter/dlplugin"
95+
)
96+
97+
type PluginAPI struct {
98+
Println func(s string)
99+
}
100+
101+
// Init initializes the plugin API.
102+
func (papi *PluginAPI) Init(lookup func(symName string) (uintptr, error)) error {
103+
printlnPtr, err := lookup("println")
104+
if err != nil {
105+
return err
106+
}
107+
108+
papi.Println = func(s string) {
109+
cs := C.CString(s)
110+
defer C.free(unsafe.Pointer(cs))
111+
112+
C.println(C.uintptr_t(printlnPtr), cs)
113+
}
114+
115+
return nil
116+
}
117+
118+
func main() {
119+
pluginFilename := flag.String("plugin", "", "plugin filename")
120+
help := flag.Bool("help", false, "show this text")
121+
122+
flag.Parse()
123+
124+
if *help {
125+
flag.PrintDefaults()
126+
return
127+
}
128+
129+
var papi PluginAPI
130+
131+
plug, err := dlplugin.Open(*pluginFilename, &papi)
132+
if err != nil {
133+
fmt.Fprintf(os.Stderr, "could not initialize a plugin by the reason: %v\n", err)
134+
os.Exit(1)
135+
}
136+
137+
defer plug.Close()
138+
139+
papi.Println("Hello, world!")
140+
}
141+
```
142+
</details>
143+
144+
[See](./examples/basic).
145+
146+
147+
### Random values
148+
149+
This example starts a random values generator from the library and reads generated values.
150+
151+
[See](./examples/random_values).
152+
153+
154+
### Callback
155+
156+
This example concatenates two string with a dynamic library and returns the result via a callback function.
157+
158+
[See](./examples/callback).
159+
160+
161+
### Multilib
162+
163+
This example loads two libs with the single interface. The program instanciates remote objects and works with them.
164+
165+
[See](https://github.com/Devoter/dlplugin_multilib_example)
166+
167+
## License
168+
169+
[LICENSE](./LICENSE)

doc.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright 2022 Alexey Nosov.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Package dlplugin provides a thread-safe interface for loading/unloading dynamic libraries.
6+
// The library symbols should be loaded manually using PluginInitializer interface.
7+
package dlplugin
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
prog
2+
*.exe

examples/basic/iface_prog/Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
NAME=prog
2+
GOFLAGS=
3+
GOOS=
4+
GOARCH=
5+
GOARM=
6+
CGO_ENABLED=1
7+
CC=
8+
BIN_SUFFIX=
9+
10+
ifeq ($(GOOS), windows)
11+
BIN_SUFFIX=.exe
12+
endif
13+
14+
all: $(NAME)$(BIN_SUFFIX)
15+
16+
clean:
17+
rm -f $(NAME)$(BIN_SUFFIX)
18+
19+
$(NAME)$(BIN_SUFFIX): main.go plugin_api.go
20+
GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) CGO_ENABLED=$(CGO_ENABLED) CC=$(CC) go build $(GOFLAGS) -o $(NAME)$(BIN_SUFFIX) ./...

examples/basic/iface_prog/main.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2022 Alexey Nosov.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"flag"
9+
"fmt"
10+
"os"
11+
12+
"github.com/Devoter/dlplugin"
13+
)
14+
15+
func main() {
16+
pluginFilename := flag.String("plugin", "", "plugin filename")
17+
help := flag.Bool("help", false, "show this text")
18+
19+
flag.Parse()
20+
21+
if *help {
22+
flag.PrintDefaults()
23+
return
24+
}
25+
26+
var plugInst Plugin
27+
28+
// loading the plugin an initializing its inteface.
29+
plug, err := dlplugin.Open(*pluginFilename, &plugInst)
30+
if err != nil {
31+
fmt.Fprintf(os.Stderr, "could not initialize a plugin by the reason: %v\n", err)
32+
os.Exit(1)
33+
}
34+
35+
defer plug.Close() // release a plugin library
36+
37+
var papi PluginAPI = &plugInst
38+
39+
papi.Println("Hello, world!") // call plugin function
40+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2022 Alexey Nosov.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
/*
8+
#include <stdint.h>
9+
#include <stdlib.h>
10+
11+
static void println(uintptr_t r, char *s)
12+
{
13+
((void (*)(char *))r)(s);
14+
}
15+
*/
16+
import "C"
17+
import "unsafe"
18+
19+
// PluginAPI declares a plugin interface.
20+
type PluginAPI interface {
21+
Println(s string)
22+
}
23+
24+
// Plugin struct declares a plugin interface implementation.
25+
type Plugin struct {
26+
println func(s string)
27+
}
28+
29+
// Println calls a plugin `println` function transparently.
30+
func (p *Plugin) Println(s string) {
31+
p.println(s)
32+
}
33+
34+
// InitPluginAPIFactory returns an interface instance and a function that initializes the plugin API.
35+
func (p *Plugin) Init(lookup func(symName string) (uintptr, error)) error {
36+
printlnPtr, err := lookup("println") // search for the library symbol
37+
if err != nil {
38+
return err
39+
}
40+
41+
// defining a Go wrapper.
42+
p.println = func(s string) {
43+
cs := C.CString(s)
44+
defer C.free(unsafe.Pointer(cs))
45+
46+
C.println(C.uintptr_t(printlnPtr), cs)
47+
}
48+
49+
return nil
50+
}

examples/basic/plug/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.h
2+
*.so
3+
*.dll

examples/basic/plug/Makefile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
NAME=plug
2+
GOFLAGS=-buildmode=c-shared
3+
GOOS=
4+
GOARCH=
5+
GOARM=
6+
CGO_ENABLED=1
7+
CC=
8+
LIB_EXT=
9+
10+
ifeq ($(GOOS), windows)
11+
LIB_EXT=dll
12+
else
13+
LIB_EXT=so
14+
endif
15+
16+
all: $(NAME).$(LIB_EXT)
17+
18+
clean:
19+
rm -f $(NAME).$(LIB_EXT) $(NAME).h
20+
21+
$(NAME).$(LIB_EXT): main.go
22+
GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) CGO_ENABLED=$(CGO_ENABLED) CC=$(CC) go build $(GOFLAGS) -o $(NAME).$(LIB_EXT) ./...

0 commit comments

Comments
 (0)