
We used to parse the .gopclntab section but removed support in favor of simply using DWARF debug information, due to lack of C symbols among other reasons. This makes it impossible to debug stripped binaries, which some distrubutions ship by default. Add back in basic support for .gopclntab which survives if the binary is stripped, allowing for rudimentary debugging such as basic program navigation, tracing, etc...
76 lines
1.8 KiB
Go
76 lines
1.8 KiB
Go
package proc
|
|
|
|
import (
|
|
"bytes"
|
|
"debug/buildinfo"
|
|
"debug/elf"
|
|
"debug/gosym"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// From go/src/debug/gosym/pclntab.go
|
|
const (
|
|
go12magic = 0xfffffffb
|
|
go116magic = 0xfffffffa
|
|
go118magic = 0xfffffff0
|
|
go120magic = 0xfffffff1
|
|
)
|
|
|
|
// Select the magic number based on the Go version
|
|
func magicNumber(goVersion string) []byte {
|
|
bs := make([]byte, 4)
|
|
var magic uint32
|
|
if strings.Compare(goVersion, "go1.20") >= 0 {
|
|
magic = go120magic
|
|
} else if strings.Compare(goVersion, "go1.18") >= 0 {
|
|
magic = go118magic
|
|
} else if strings.Compare(goVersion, "go1.16") >= 0 {
|
|
magic = go116magic
|
|
} else {
|
|
magic = go12magic
|
|
}
|
|
binary.LittleEndian.PutUint32(bs, magic)
|
|
return bs
|
|
}
|
|
|
|
func readPcLnTableElf(exe *elf.File, path string) (*gosym.Table, error) {
|
|
// Default section label is .gopclntab
|
|
sectionLabel := ".gopclntab"
|
|
|
|
section := exe.Section(sectionLabel)
|
|
if section == nil {
|
|
// binary may be built with -pie
|
|
sectionLabel = ".data.rel.ro"
|
|
section = exe.Section(sectionLabel)
|
|
if section == nil {
|
|
return nil, fmt.Errorf("could not read section .gopclntab")
|
|
}
|
|
}
|
|
tableData, err := section.Data()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("found section but could not read .gopclntab")
|
|
}
|
|
|
|
bi, err := buildinfo.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Find .gopclntab by magic number even if there is no section label
|
|
magic := magicNumber(bi.GoVersion)
|
|
pclntabIndex := bytes.Index(tableData, magic)
|
|
if pclntabIndex < 0 {
|
|
return nil, fmt.Errorf("could not find magic number in %s ", path)
|
|
}
|
|
tableData = tableData[pclntabIndex:]
|
|
addr := exe.Section(".text").Addr
|
|
lineTable := gosym.NewLineTable(tableData, addr)
|
|
symTable, err := gosym.NewTable([]byte{}, lineTable)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not create symbol table from %s ", path)
|
|
}
|
|
return symTable, nil
|
|
}
|