Skip to content

CONCEPT Cited by 1 source

Go plugin dynamic-linking implication

Importing the stdlib plugin package — even without using it — puts the Go linker into dynamically-linked mode, which disables method dead-code elimination AND forces retention of every unexported method of every reachable type.

Why

Go plugins allow dynamically loading Go code at runtime from another Go program. The main binary and the plugin share the same state — global variables, types, values, methods. A plugin loaded at runtime can call anything the host linked; therefore, if the linker sees that a binary might be a dynamic-link host, it must conservatively keep every symbol a plugin could reach.

Concretely, the linker source code treats any binary that imports plugin as dynamically linked. deadcode.go disables method DCE in that mode, and deadcode.go L462 forces retention of every unexported method.

Canonical wiki instance

containerd's plugin/plugin_go18.go imports plugin unconditionally to support user-loadable containerd plugins — a feature the Datadog Agent didn't use.

Symptom: amd64 Agent binaries were 245 MiB larger than the arm64 equivalent because containerd was built in on amd64 but not arm64. whydeadcode localised the disabler; systems/goda traced the plugin import to containerd.

Fix: upstream PR containerd#11203 gated the plugin import behind a build tag that Datadog could not pass. Agent PRs #32538 + #32885 applied it. 245 MiB (~20 %) reduction on main Linux amd64 artifacts, benefiting ~75 % of users. Instance of patterns/upstream-the-fix.

Generalisation

Any library or program that imports plugin imposes this cost on every downstream consumer"simply importing a package has side effects" (Datadog 2026-02-18). If you don't need Go plugins at runtime, you should not transitively import plugin. Audit with goda:

GOOS=linux GOARCH=amd64 goda graph "reach(., plugin)"

Operational tension

The plugin package is a legitimate Go feature (some applications genuinely need dynamic code loading), so the linker's conservatism is correct. The fix is at the import edge: upstream the build tag, apply it downstream.

Seen in

Last updated · 200 distilled / 1,178 read