tl;dr: - Linux debugging works - macOS running works, debugging doesn't.
180 lines
9.3 KiB
Markdown
180 lines
9.3 KiB
Markdown
# 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 |
|
|
|
|
<!-- link definitions -->
|
|
|
|
[`debug/linux-plugin.dlv`]: debug/linux-plugin.dlv
|
|
[`debug/lookup.dlv`]: debug/lookup.dlv
|
|
[`debug/macos-step.dlv`]: debug/macos-step.dlv
|
|
[go-delve/delve#1628]: https://github.com/go-delve/delve/issues/1628
|
|
[go-delve/delve#865]: https://github.com/go-delve/delve/issues/865
|
|
[golang/go#25841]: https://github.com/golang/go/issues/25841
|