1
0
Fork 0
go_plugin_demo/tools/dwarfcu/main.go
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

83 lines
2.1 KiB
Go

// Command dwarfcu lists the DWARF compile units of Mach-O and ELF files.
//
// It reads the compressed DWARF sections the Go linker produces,
// which some platform tools, like macOS dwarfdump, do not decode.
// Use it to check that a host program and its plugins carry debug information:
//
// go run ./tools/dwarfcu cmd/demo plugins/foo/foo.so
package main
import (
"debug/dwarf"
"debug/elf"
"debug/macho"
"fmt"
"os"
"strings"
)
// modulePath is this repository's module, whose plugins the tool reports.
const modulePath = "code.osinet.fr/fgm/go_plugin_demo"
func compileUnits(d *dwarf.Data) ([]string, error) {
var cus []string
r := d.Reader()
for {
e, err := r.Next()
if err != nil {
return cus, err
}
if e == nil {
return cus, nil
}
if e.Tag == dwarf.TagCompileUnit {
if name, ok := e.Val(dwarf.AttrName).(string); ok {
cus = append(cus, name)
}
r.SkipChildren()
}
}
}
func main() {
status := 0
for _, path := range os.Args[1:] {
d, err := openDWARF(path)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
status = 1
continue
}
cus, err := compileUnits(d)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: reading DWARF: %v\n", path, err)
status = 1
}
// The shared package only declares a type, so it has no unit of its own:
// the demo's code is in main and in each plugin.
// A plugin built by package keeps its import path,
// while one built from a file list is named plugin/unnamed-<hash>.
var own []string
for _, cu := range cus {
if cu == "main" || strings.HasPrefix(cu, modulePath+"/") || strings.HasPrefix(cu, "plugin/unnamed-") {
own = append(own, cu)
}
}
fmt.Printf("%s: %d compile units, own: %v\n", path, len(cus), own)
}
os.Exit(status)
}
// openDWARF tries Mach-O first, then ELF, so the tool reads macOS and Linux builds alike.
func openDWARF(path string) (*dwarf.Data, error) {
if f, err := macho.Open(path); err == nil {
defer f.Close()
return f.DWARF()
}
f, err := elf.Open(path)
if err != nil {
return nil, fmt.Errorf("neither Mach-O nor ELF: %w", err)
}
defer f.Close()
return f.DWARF()
}