Hacker News new | ask | show | jobs
by Xeoncross 1356 days ago
Plugins for a compiled binary are just plain hard. Changing the code works better with an interpreted language. Usually you see the software implement it's own interpreter or a scripting language on top to handle this.
2 comments

Plugins for ELF- and Mach-O-based systems aren't any different than regular shared libraries, modulo slightly different symbol precedence. AFAIU, while a quite different architecture, Windows PE linking treats them quite similarly, too.

Perhaps you meant statically compiled binaries? Even then I'm not so sure about that. Right now I'm working on a project that statically compiles a plugin so its various libraries can't leak or be overridden by the loading application, yet which itself can load other plugins. OTOH, this is in C, C++, Objective-C, and Lua (basically C from the perspective of binary linking), all of which have mature linking semantics. (Well, at least this is true for C. Controlling symbol namespace pollution by C++ and Objective-C code requires more complexity than for C, but at least the necessary compiler and linker flags exist and are mature.)

When you use languages or toolchains that don't invest in an ABI, or which make linking too automagic (with no or little recourse for exposing various linking features a platform may offer), then runtime linking is likely to become a major limitation in some solution areas. For example, there's probably no way to coax XCode to compile my plugins properly, without outsourcing some logic to additional scripts. Ultimately a Makefile is likely the best solution--and the one I chose, anticipating these headaches--as it keeps most of the build transparent. Fortunately, XCode seems to just be a wrapper around a bunch of command-line utilities, so you don't actually need XCode at all.

Beyond linking it is also a problem with ABI compatibility of object layouts, etc. It is so much simpler with more dynamic languages.
The C ABI on pretty much any platform defines object layout for structs, which is usually good enough.
The C ABI is nowhere near enough for passing arbitrary Go structs, all you need is a map or a sync.Mutex field and you need to exactly match plugin compilation to caller compilation.

I wish https://pkg.go.dev/plugin was clearly documented as not being a viable 3rd party plugin solution.

It's not enough to pass arbitrary C++ classes, either. The point is that you can still design a fairly expressive API involving structured data within the constraints that the C ABI imposes on you - and that C ABI will guarantee stability. This is how plugins were usually handled in the C++ world, historically speaking.

(There are also higher-level ABIs like COM, but they are usually reducible to the C ABI in practice - e.g. COM can be described entirely in terms of struct and function pointers.)

You can write C plugins, or C-ABI plugins in any language that can do that, and call them from Go just fine. Nobody just considers that a very good way to write Go, or plugins for Go.
I know, I was thinking C# does something like that though, so I was hoping it would be achievable.

The annoying part about the scripting language is that if something is written in Go, you want developers to also work in Go, otherwise the people writing plugins for your software, knowing better the API and use-cases, have a disconnection with the language actually used for the software, losing potential opportunities for contributions.

C# (usually) runs on top of a VM with a JIT, so the linking is effectively always done at runtime.

It's also why it can do things like generic virtual methods - it can compile an instantiation and adjust the vtable at runtime.