{"id":16440256,"url":"https://github.com/soypat/so","last_synced_at":"2025-10-27T03:31:16.204Z","repository":{"id":104802007,"uuid":"604208303","full_name":"soypat/so","owner":"soypat","description":"MWE tutorial of .so dynamic linking. Call C from Go; and Go from Python","archived":false,"fork":false,"pushed_at":"2023-07-17T00:00:36.000Z","size":22,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-05-01T16:22:37.379Z","etag":null,"topics":["abi","c","ctypes","dynamic","dynamic-linking","ffi","go","golang","python","python-ctypes","shared-library","shared-object"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/soypat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-02-20T15:06:31.000Z","updated_at":"2023-09-13T12:26:24.000Z","dependencies_parsed_at":"2023-09-25T01:48:35.801Z","dependency_job_id":"cab7d634-caef-4a97-bf5a-77e6554ff580","html_url":"https://github.com/soypat/so","commit_stats":{"total_commits":10,"total_committers":1,"mean_commits":10.0,"dds":0.0,"last_synced_commit":"53bcff46906af4807937b4d8da159b49afffcd64"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fso","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fso/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fso/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fso/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/soypat","download_url":"https://codeload.github.com/soypat/so/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219861933,"owners_count":16555980,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abi","c","ctypes","dynamic","dynamic-linking","ffi","go","golang","python","python-ctypes","shared-library","shared-object"],"created_at":"2024-10-11T09:11:39.398Z","updated_at":"2025-10-27T03:31:10.913Z","avatar_url":"https://github.com/soypat.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# so\nThis is a README driven repository with a short demo of how to get started with \nthe creation of dynamic libraries and the linking of them to a C program and then\na Go program.\n\nThis is for **linux**.\n\n## Summary\n\n### Linking Go to Python dynamically\nSee [`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.\n\n\n### Linking C to Go dynamically\n```sh\n# Generate .o files\ngcc -c -Wall -Werror -fpic dyn.c\n# Create shared object from .o files.\ngcc -shared -o libdyn.so *.o\n# Create dynamically linked C program.\ngcc -L. -Wall -o main.bin main.c -ldyn\n# Run the output `main.bin` program telling OS where dynamic libraries are:\nLD_LIBRARY_PATH=. ./main.bin\n# Run the Go version.\nLD_LIBRARY_PATH=. go run main.go\n```\n\nAbove are instructions to link C code to Go. More detailed instructions below.\n\n\n\n# C dynamic linking\nPrerequisites:\n- `gcc`. Quick fix: `sudo apt install gcc`\n\n## Define dynamic library code\nCreate your `.c` file with the dynamic code.\n\n`dyn.c`\n```c\n#include\u003cstdio.h\u003e\n\nvoid foo(void) {\n    puts(\"Shared library call detected!\");\n}\n```\nWe must also create the header file that will be distributed with the dynamic library.\nThe pragma compiler directive is so the code is not included multiple times in a single project.\nIT may not be well supported across compilers so prefer using `ifndef` styled pragmas if cross compiling.\n\n`dyn.h`\n```h\n#pragma once\nextern void foo(void);\n```\n\n\n## Create the .so file so it can be dynamically loaded into programs\nStart by creating the object files (`*.o`) for each source file.\n```sh\ngcc -c -Wall -Werror -fpic dyn.c\n```\nThis command generates a single object file for each source `.c` file.\nIf there are various `.c` files list them as arguments to `gcc. `fpic` flag ensures\ncode is position independent (PIC). `-c` file ensures each `.o` file is not linked yet.\n\nThe result is that a **`dyn.o`** file is created for our single `dyn.c` source file.\n\nNow we may compile the dynamic library from the `.o` files into a dynamic library\nwe will call **`libdyn.so`**. It's common to prefix dynamic library `.so` files with \"lib\".\n\n```sh\ngcc -shared -o libdyn.so *.o\n```\nYou should now have a dynamic library ready to be loaded into a project!\n\n## Use your dynamic library\nLet's write our user or client-side program that uses the dynamic code:\n\n```c\n#include \u003cstdio.h\u003e\n#include \"dyn.h\"\n\nint main(void) {\n    puts(\"running program\");\n    foo();\n    return 0;\n}\n```\nNotice we include the header file of the dynamic library and with that we are ready to use the library's `foo` function.\n\n## Dynamically link your program\nWe can attempt to now compile our program to a program called **`test`**:\n```sh\n$ gcc -Wall -o test main.c -ldyn\n/usr/bin/ld: cannot find -ldyn: No such file or directory\ncollect2: error: ld returned 1 exit status\n```\nLooks like we need to tell gcc where it can find our library with the `-L` flag.\n\nWe tell gcc to look in the current directory `.` for shared libraries.\n```sh\ngcc -L. -Wall -o test main.c -ldyn\n```\nWe've now created our program **`test`**! Let's try running it!\n\n## Running your dynamically linked program\n\n```sh\n$ ./test \n./test: error while loading shared libraries: libdyn.so: cannot open shared object file: No such file or directory\n```\nLooks like we need to tell the operating system where to look for the dynamic library.\nWe can do that by setting the `LD_LIBRARY_PATH` environement variable. The easiest\nway to do that is just passing it as the command's current environment:\n\n```sh\n$ LD_LIBRARY_PATH=. ./test \nrunning program\nShared library call detected!\n```\n\n**Tada!**\n\nIf the above does not work try including the actual path into the variable:\n```sh\nLD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./test \n```\n\n# Go dynamic linking\nPrerequisites:\n- Any version of [Go](https://go.dev/dl/)\n- `libdyn.so` generated in the previous shared object creation step\n\n## Using dynamic library from Go\nTo link any kind of C code to a Go program we use what is called \"Cgo\". A staple of\nCgo lets us interoperate between C and Go easily. To do this we must include the\nspecial `\"C\"` package into our Go program. This is really not a package like other Go packages,\nit lets us define C code inline above it. We can then call C functions from the `C`\nnamespace as if we were programming in C within a Go source file.\n\n`main.go`\n```go\npackage main\n\n/*\n#include \"dyn.h\"\n*/\nimport \"C\"\n\nfunc main() {\n\tprintln(\"go program started\")\n\tC.foo()\n}\n```\n\n## Compiling and running a dynamically linked Go program\nLet's try running this program:\n```sh\n$ go run main.go\n# command-line-arguments\n/usr/local/go/pkg/tool/linux_amd64/link: running gcc failed: exit status 1\n/usr/bin/ld: /tmp/go-link-185139139/000001.o: in function `_cgo_403d439493e8_Cfunc_foo':\n/tmp/go-build/cgo-gcc-prolog:49: undefined reference to `foo'\ncollect2: error: ld returned 1 exit status\n```\nOof! Lots of errors. Link step is failing. We need to let Go know `foo` is a dynamically\nlinked function just like we told `gcc`.\n\n```sh\n$ LD_LIBRARY_PATH=. CGO_LDFLAGS=\"-L. -ldyn\" go run main.go\ngo program started\nShared library call detected!\n```\n\nWe may simplify this further by adding cgo pragmas to the inline C code in `main.go`:\n```\n#cgo LDFLAGS: -L. -ldyn\n#include \"dyn.h\"\n```\nNow we just need the LD_LIBRARY_PATH variable to be set, just like when using `gcc`.\n\n```sh\n$ LD_LIBRARY_PATH=. go run main.go\ngo program started\nShared library call detected!\n```\n\n# Dynamic linking functions with arguments\nThe procedure is identical, just modify the header file to reflect the function signature.\nYou may find a worked example under [`withargs`](./withargs)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoypat%2Fso","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoypat%2Fso","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoypat%2Fso/lists"}