1
0
Fork 0
go_plugin_demo/VERIFICATION.md
Frédéric G. MARAND be1f9236e0 Code and findings updated for go 1.27.3.
tl;dr:
- Linux debugging works
- macOS running works, debugging doesn't.
2026-11-02 17:17:20 +01:00

9.3 KiB

Vérification du 2026-11-02

Reprise des constats du README de 2020, avec la méthode suivie pour chacun, afin de pouvoir la rejouer sur une version ultérieure de Go ou de Delve.

Environnement

Élément Version
Système hôte macOS 27.2, arm64
Go 1.27.3
Delve 1.27.0, épinglé dans go.mod par la directive tool, lancé par go tool dlv
Docker 29.9.2, image officielle golang:1.27.3, linux/arm64
  • Delve 1.24.2, installé à part dans $GOPATH/bin, refuse les binaires Go 1.27 : c'est l'une des raisons de l'épingler dans le module.
  • GoLand 2026.2.4 embarque de son côté Delve 1.27.0, dans GoLand.app/Contents/plugins/go-plugin/lib/dlv/<plateforme>/dlv.

1. Incompatibilité des drapeaux de compilation

Constat de 2020 : un hôte compilé avec -gcflags="all=-N -l" ne charge pas un greffon compilé sans.

make all
./cmd/demo                                  # fonctionne
go build -buildmode=plugin -o plugins/foo/foo.so ./plugins/foo
./cmd/demo                                  # échoue

Résultat : toujours vrai, et le message désigne désormais internal/abi :

plugin was built with a different version of package internal/abi

2. Delve sous macOS

Script : debug/macos-step.dlv, après make all :

go tool dlv exec ./cmd/demo --allow-non-terminal-interactive=true --init debug/macos-step.dlv

Résultats :

  • libraries ne liste aucun greffon, même une fois les deux chargés.
  • break foo.go:19 échoue : location "foo.go:19" not found. Delve propose seulement un point d'arrêt "suspendu", en attente d'un chargement qu'il ne voit pas.
  • step sur l'appel au greffon, res := (*work)(n), s'arrête à la première instruction du greffon, sans source : no source available, cadre ???.
  • stepout depuis là ne revient pas dans main.Work : le programme continue jusqu'au point d'arrêt suivant.
  • next par-dessus l'appel fonctionne normalement, car il ne demande que la table de lignes de l'hôte et l'adresse de retour.

3. Les informations de déboguage des greffons sous macOS

Hypothèse concurrente à écarter : le greffon macOS serait dépourvu d'informations DWARF.

Outil et commande Résultat
otool -l plugins/foo/foo.so (XCode) un segment __DWARF d'environ 90 Kio
dwarfdump --debug-info plugins/foo/foo.so (XCode) 0 unité de compilation : ne décode pas les sections compressées __zdebug_* de l'éditeur de liens Go
go tool nm plugins/foo/foo.so les symboles du greffon, préfixés par son chemin d'import
go run ./tools/dwarfcu cmd/demo plugins/foo/foo.so 78 unités pour l'hôte, 66 pour le greffon, dont la sienne propre

Le greffon porte son chemin d'import, code.osinet.fr/fgm/go_plugin_demo/plugins/foo, parce que le Makefile le construit par paquet : construit à partir d'une liste de fichiers, il serait nommé plugin/unnamed-<hash>.

Conclusion :

  • les informations DWARF du greffon sont complètes ;
  • c'est Delve qui ne les charge pas sous macOS, faute de suivre les bibliothèques chargées dynamiquement, comme le montre libraries.

4. Delve sous Linux, dans un conteneur

Tout se compile dans le conteneur :

  • un greffon a besoin de cgo (-buildmode=plugin requires external (cgo) linking), donc la compilation croisée depuis macOS demanderait un compilateur C croisé ;
  • un hôte ne charge de toute façon que des greffons de son propre système.
docker run --rm \
  --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
  -v "$PWD":"$PWD" -w "$PWD" \
  -v go_plugin_demo-gomod:/go/pkg/mod \
  golang:1.27.3 \
  sh -c 'make clean all &&
    go tool dlv exec ./cmd/demo --allow-non-terminal-interactive=true --init debug/linux-plugin.dlv'
  • SYS_PTRACE et seccomp=unconfined permettent à Delve de contrôler le processus.
  • Le dépôt est monté au même chemin que sur l'hôte, pour que les chemins des informations de déboguage correspondent aux sources locales.
  • Le volume nommé conserve le cache de modules d'une exécution à l'autre.
  • make clean all remplace les binaires macOS éventuels par des binaires Linux.

Script : debug/linux-plugin.dlv.

Résultats :

  • libraries liste plugins/foo/foo.so et plugins/bar/bar.so, informations de déboguage chargées.
  • break foo.go:19 se pose dans le greffon, sur code.osinet.fr/fgm/go_plugin_demo/plugins/foo.init.func1(), et s'y arrête.
  • Dans le greffon, print n et print InternalName donnent leurs valeurs.
  • La pile remonte du greffon à main.Work puis main.main.
  • step entre dans fmt.Sprintf depuis le greffon.
  • go run ./tools/dwarfcu lit aussi ces binaires ELF depuis macOS : 90 unités pour l'hôte, 74 pour le greffon.

5. Valeurs renvoyées par plugin.Lookup

Constat de 2020 : le débogueur n'a pas accès aux valeurs renvoyées par plugin.Lookup tant qu'elles ne sont pas converties dans des types statiques.

Script : debug/lookup.dlv, lancé comme ceux des sections 2 et 4.

  • Il s'arrête juste après p.Lookup(symbolWork).
  • Il affiche workSymbol, de type plugin.Symbol, c'est-à-dire any, avant et après l'assertion de type.

Résultats avant conversion :

  • Linux, soit le type dynamique et la fonction désignée : plugin.Symbol(*code.osinet.fr/fgm/go_plugin_demo.Worker) *code.osinet.fr/fgm/go_plugin_demo/plugins/foo.init.func1.
  • macOS : le type dynamique est lisible, mais pas la fonction : *(unreadable could not find function for 0x...).
    • Une donnée, en revanche, s'affiche entièrement : dans GoLand, nameSymbol vaut {plugin.Symbol | *string} "Foo".
    • La table syms du *plugin.Plugin montre InternalName -> Foo, mais Work -> unreadable.

Résultats après l'assertion workSymbol.(*go_plugin_demo.Worker) : rien ne change.

  • Sous macOS, *work reste illisible.
  • GoLand montre la même chose :
    • ok = true ;
    • work = {*go_plugin_demo.Worker | 0x...}, avec son adresse ;
    • unreadable: could not find function for 0x... pour la fonction désignée, comme pour workSymbol avant conversion.

Conclusion : le constat de 2020 ne tient plus.

  • Le type dynamique d'un plugin.Symbol s'inspecte sans conversion, puisqu'il vient des informations de type à l'exécution, communes à l'hôte et au greffon.
  • La seule limite, propre à macOS, concerne les fonctions du greffon, qui relèvent des tables de symboles que Delve n'y charge pas, comme en section 2.

Références

  • go-delve/delve#865, "Support plugins" : fermé le 2019-07-11, avec la prise en charge des greffons par Delve, sous Linux.
  • go-delve/delve#1628, "Cannot debug go plugins on mac" : ouvert le 2019-07-22, toujours ouvert au 2026-11-02.
    • Le mainteneur ne l'a pas implémenté, en citant golang/go#25841.
    • Le constat de 2020 était donc déjà propre à macOS.
  • Avant de refaire cette vérification sous macOS, regarder si #1628 est fermé, puis rejouer debug/macos-step.dlv.

Conclusion

Linux macOS
plugin.Open oui, si hôte et greffons ont les mêmes réglages de compilation idem
Points d'arrêt et pas à pas dans l'hôte oui oui
next par-dessus un appel au greffon oui oui
Points d'arrêt, variables et pas à pas dans un greffon oui non : Delve ne charge pas les informations du greffon
Valeurs renvoyées par plugin.Lookup, avant conversion oui données oui, fonctions non