Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/soypat/so
MWE tutorial of .so dynamic linking. Call C from Go; and Go from Python
https://github.com/soypat/so
abi c ctypes dynamic dynamic-linking ffi go golang python python-ctypes shared-library shared-object
Last synced: 3 months ago
JSON representation
MWE tutorial of .so dynamic linking. Call C from Go; and Go from Python
- Host: GitHub
- URL: https://github.com/soypat/so
- Owner: soypat
- License: mit
- Created: 2023-02-20T15:06:31.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2023-07-17T00:00:36.000Z (over 1 year ago)
- Last Synced: 2024-05-01T16:22:37.379Z (9 months ago)
- Topics: abi, c, ctypes, dynamic, dynamic-linking, ffi, go, golang, python, python-ctypes, shared-library, shared-object
- Language: Python
- Homepage:
- Size: 21.5 KB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# so
This is a README driven repository with a short demo of how to get started with
the creation of dynamic libraries and the linking of them to a C program and then
a Go program.This is for **linux**.
## Summary
### Linking Go to Python dynamically
See [`python`](./python/) folder for instructions on how to call Go functions from Python. Benchmarks included to give readers an idea of use cases for linking.### Linking C to Go dynamically
```sh
# Generate .o files
gcc -c -Wall -Werror -fpic dyn.c
# Create shared object from .o files.
gcc -shared -o libdyn.so *.o
# Create dynamically linked C program.
gcc -L. -Wall -o main.bin main.c -ldyn
# Run the output `main.bin` program telling OS where dynamic libraries are:
LD_LIBRARY_PATH=. ./main.bin
# Run the Go version.
LD_LIBRARY_PATH=. go run main.go
```Above are instructions to link C code to Go. More detailed instructions below.
# C dynamic linking
Prerequisites:
- `gcc`. Quick fix: `sudo apt install gcc`## Define dynamic library code
Create your `.c` file with the dynamic code.`dyn.c`
```c
#includevoid foo(void) {
puts("Shared library call detected!");
}
```
We must also create the header file that will be distributed with the dynamic library.
The pragma compiler directive is so the code is not included multiple times in a single project.
IT may not be well supported across compilers so prefer using `ifndef` styled pragmas if cross compiling.`dyn.h`
```h
#pragma once
extern void foo(void);
```## Create the .so file so it can be dynamically loaded into programs
Start by creating the object files (`*.o`) for each source file.
```sh
gcc -c -Wall -Werror -fpic dyn.c
```
This command generates a single object file for each source `.c` file.
If there are various `.c` files list them as arguments to `gcc. `fpic` flag ensures
code is position independent (PIC). `-c` file ensures each `.o` file is not linked yet.The result is that a **`dyn.o`** file is created for our single `dyn.c` source file.
Now we may compile the dynamic library from the `.o` files into a dynamic library
we will call **`libdyn.so`**. It's common to prefix dynamic library `.so` files with "lib".```sh
gcc -shared -o libdyn.so *.o
```
You should now have a dynamic library ready to be loaded into a project!## Use your dynamic library
Let's write our user or client-side program that uses the dynamic code:```c
#include
#include "dyn.h"int main(void) {
puts("running program");
foo();
return 0;
}
```
Notice we include the header file of the dynamic library and with that we are ready to use the library's `foo` function.## Dynamically link your program
We can attempt to now compile our program to a program called **`test`**:
```sh
$ gcc -Wall -o test main.c -ldyn
/usr/bin/ld: cannot find -ldyn: No such file or directory
collect2: error: ld returned 1 exit status
```
Looks like we need to tell gcc where it can find our library with the `-L` flag.We tell gcc to look in the current directory `.` for shared libraries.
```sh
gcc -L. -Wall -o test main.c -ldyn
```
We've now created our program **`test`**! Let's try running it!## Running your dynamically linked program
```sh
$ ./test
./test: error while loading shared libraries: libdyn.so: cannot open shared object file: No such file or directory
```
Looks like we need to tell the operating system where to look for the dynamic library.
We can do that by setting the `LD_LIBRARY_PATH` environement variable. The easiest
way to do that is just passing it as the command's current environment:```sh
$ LD_LIBRARY_PATH=. ./test
running program
Shared library call detected!
```**Tada!**
If the above does not work try including the actual path into the variable:
```sh
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./test
```# Go dynamic linking
Prerequisites:
- Any version of [Go](https://go.dev/dl/)
- `libdyn.so` generated in the previous shared object creation step## Using dynamic library from Go
To link any kind of C code to a Go program we use what is called "Cgo". A staple of
Cgo lets us interoperate between C and Go easily. To do this we must include the
special `"C"` package into our Go program. This is really not a package like other Go packages,
it lets us define C code inline above it. We can then call C functions from the `C`
namespace as if we were programming in C within a Go source file.`main.go`
```go
package main/*
#include "dyn.h"
*/
import "C"func main() {
println("go program started")
C.foo()
}
```## Compiling and running a dynamically linked Go program
Let's try running this program:
```sh
$ go run main.go
# command-line-arguments
/usr/local/go/pkg/tool/linux_amd64/link: running gcc failed: exit status 1
/usr/bin/ld: /tmp/go-link-185139139/000001.o: in function `_cgo_403d439493e8_Cfunc_foo':
/tmp/go-build/cgo-gcc-prolog:49: undefined reference to `foo'
collect2: error: ld returned 1 exit status
```
Oof! Lots of errors. Link step is failing. We need to let Go know `foo` is a dynamically
linked function just like we told `gcc`.```sh
$ LD_LIBRARY_PATH=. CGO_LDFLAGS="-L. -ldyn" go run main.go
go program started
Shared library call detected!
```We may simplify this further by adding cgo pragmas to the inline C code in `main.go`:
```
#cgo LDFLAGS: -L. -ldyn
#include "dyn.h"
```
Now we just need the LD_LIBRARY_PATH variable to be set, just like when using `gcc`.```sh
$ LD_LIBRARY_PATH=. go run main.go
go program started
Shared library call detected!
```# Dynamic linking functions with arguments
The procedure is identical, just modify the header file to reflect the function signature.
You may find a worked example under [`withargs`](./withargs)