Skip to content

Commit

Permalink
Allow version override to affect pclntab parsing. Support go1.19 and …
Browse files Browse the repository at this point in the history
…go1.20
  • Loading branch information
Stephen Eckels authored and Stephen Eckels committed Aug 17, 2022
1 parent 78e960f commit db9fbb3
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 36 deletions.
64 changes: 50 additions & 14 deletions debug/gosym/pclntab.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"encoding/binary"
"fmt"
"sort"
"strings"
"sync"
)

Expand All @@ -27,6 +28,7 @@ const (
ver12
ver116
ver118
ver120
)

func (v version) String() string {
Expand All @@ -41,6 +43,8 @@ func (v version) String() string {
return "1.16"
case ver118:
return "1.18"
case ver120:
return "1.20"
default:
return fmt.Sprintf("ERROR Unknown ID number %d", int(v))
}
Expand Down Expand Up @@ -141,7 +145,7 @@ func (t *LineTable) slice(pc uint64) *LineTable {
//
// Deprecated: Use Table's PCToLine method instead.
func (t *LineTable) PCToLine(pc uint64) int {
if t.isGo12() {
if t.isGo12("") {
return t.go12PCToLine(pc)
}
_, _, line := t.parse(pc, -1)
Expand All @@ -153,7 +157,7 @@ func (t *LineTable) PCToLine(pc uint64) int {
//
// Deprecated: Use Table's LineToPC method instead.
func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 {
if t.isGo12() {
if t.isGo12("") {
return 0
}
_, pc, line1 := t.parse(maxpc, line)
Expand Down Expand Up @@ -184,15 +188,16 @@ func NewLineTable(data []byte, text uint64) *LineTable {
// are expected to have that recovery logic.

// isGo12 reports whether this is a Go 1.2 (or later) symbol table.
func (t *LineTable) isGo12() bool {
t.parsePclnTab()
func (t *LineTable) isGo12(versionOverride string) bool {
t.parsePclnTab(versionOverride)
return t.Version >= ver12
}

const (
go12magic = 0xfffffffb
go116magic = 0xfffffffa
go118magic = 0xfffffff0
go120magic = 0xfffffff1
)

// uintptr returns the pointer-sized value encoded at b.
Expand All @@ -205,7 +210,7 @@ func (t *LineTable) uintptr(b []byte) uint64 {
}

// parsePclnTab parses the pclntab, setting the version.
func (t *LineTable) parsePclnTab() {
func (t *LineTable) parsePclnTab(versionOverride string) {
t.mu.Lock()
defer t.mu.Unlock()
if t.Version != verUnknown {
Expand Down Expand Up @@ -249,11 +254,41 @@ func (t *LineTable) parsePclnTab() {
t.Binary, possibleVersion = binary.LittleEndian, ver118
case beMagic == go118magic:
t.Binary, possibleVersion = binary.BigEndian, ver118
case leMagic == go120magic:
t.Binary, possibleVersion = binary.LittleEndian, ver120
case beMagic == go120magic:
t.Binary, possibleVersion = binary.BigEndian, ver120
default:
return
}
t.Version = possibleVersion

if len(versionOverride) > 0 {
if strings.Contains(versionOverride, "1.20") {
t.Version = ver120
} else if strings.Contains(versionOverride, "1.19") {
t.Version = ver118
} else if strings.Contains(versionOverride, "1.18") {
t.Version = ver118
} else if strings.Contains(versionOverride, "1.17") {
t.Version = ver116
} else if strings.Contains(versionOverride, "1.16") {
t.Version = ver116
} else if strings.Contains(versionOverride, "1.15") {
t.Version = ver12
} else if strings.Contains(versionOverride, "1.14") {
t.Version = ver12
} else if strings.Contains(versionOverride, "1.13") {
t.Version = ver12
} else if strings.Contains(versionOverride, "1.12") {
t.Version = ver12
} else if strings.Contains(versionOverride, "1.11") {
t.Version = ver11
} else {
t.Version = ver11
}
}

// quantum and ptrSize are the same between 1.2, 1.16, and 1.18
t.Quantum = uint32(t.Data[6])
t.Ptrsize = uint32(t.Data[7])
Expand All @@ -265,8 +300,8 @@ func (t *LineTable) parsePclnTab() {
return t.Data[offset(word):]
}

switch possibleVersion {
case ver118:
switch t.Version {
case ver118, ver120:
t.nfunctab = uint32(offset(0))
t.nfiletab = uint32(offset(1))
t.textStart = t.PC // use the start PC instead of reading from the table, which may be unrelocated
Expand Down Expand Up @@ -326,11 +361,12 @@ func (t *LineTable) go12Funcs() []Func {
f.LineTable = t
f.FrameSize = int(info.deferreturn())
syms[i] = Sym{
Value: f.Entry,
Type: 'T',
Name: t.funcName(info.nameoff()),
GoType: 0,
Func: f,
Value: f.Entry,
Type: 'T',
Name: t.funcName(info.nameoff()),
GoType: 0,
Func: f,
GoVersion: t.Version,
}
f.Sym = &syms[i]
}
Expand Down Expand Up @@ -547,7 +583,7 @@ func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum,
fileStartPC := filePC
for t.step(&fp, &filePC, &fileVal, filePC == entry) {
fileIndex := fileVal
if t.Version == ver116 || t.Version == ver118 {
if t.Version == ver116 || t.Version == ver118 || t.Version == ver120 {
fileIndex = int32(t.Binary.Uint32(cutab[fileVal*4:]))
}
if fileIndex == filenum && fileStartPC < filePC {
Expand Down Expand Up @@ -646,7 +682,7 @@ func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
entry := f.entryPC()
filetab := f.pcfile()
linetab := f.pcln()
if t.Version == ver116 || t.Version == ver118 {
if t.Version == ver116 || t.Version == ver118 || t.Version == ver120 {
cutab = t.cutab[f.cuOffset()*4:]
}
pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line), cutab)
Expand Down
21 changes: 15 additions & 6 deletions debug/gosym/symtab.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ type Sym struct {
Name string
GoType uint64
// If this symbol is a function symbol, the corresponding Func
Func *Func
Func *Func
GoVersion version
}

// Static reports whether this symbol is static (not visible outside its file).
Expand Down Expand Up @@ -57,9 +58,16 @@ func (s *Sym) nameWithoutInst() string {
func (s *Sym) PackageName() string {
name := s.nameWithoutInst()

// A prefix of "type." and "go." is a compiler-generated symbol that doesn't belong to any package.
// See variable reservedimports in cmd/compile/internal/gc/subr.go
if strings.HasPrefix(name, "go.") || strings.HasPrefix(name, "type.") {
// Since go1.20, a prefix of "type:" and "go:" is a compiler-generated symbol,
// they do not belong to any package.
//
// See cmd/compile/internal/base/link.go:ReservedImports variable.
if s.GoVersion >= ver120 && (strings.HasPrefix(name, "go:") || strings.HasPrefix(name, "type:")) {
return ""
}

// For go1.18 and below, the prefix are "type." and "go." instead.
if s.GoVersion <= ver118 && (strings.HasPrefix(name, "go.") || strings.HasPrefix(name, "type.")) {
return ""
}

Expand Down Expand Up @@ -326,7 +334,7 @@ func walksymtab(data []byte, fn func(sym) error) error {
// NewTable decodes the Go symbol table (the ".gosymtab" section in ELF),
// returning an in-memory representation.
// Starting with Go 1.3, the Go symbol table no longer includes symbol data.
func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
func NewTable(symtab []byte, pcln *LineTable, versionOverride string) (*Table, error) {
var n int
err := walksymtab(symtab, func(s sym) error {
n++
Expand All @@ -337,7 +345,7 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
}

var t Table
if pcln.isGo12() {
if pcln.isGo12(versionOverride) {
t.Go12line = pcln
}
fname := make(map[uint16]string)
Expand All @@ -352,6 +360,7 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
ts.Type = s.typ
ts.Value = s.value
ts.GoType = s.gotype
ts.GoVersion = pcln.Version
switch s.typ {
default:
// rewrite name to use . instead of · (c2 b7)
Expand Down
20 changes: 10 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,16 @@ func main_impl(fileName string, printStdPkgs bool, printFilePaths bool, printTyp
}
}

tab, tabva, err := file.PCLineTable(versionOverride)
if err != nil {
return ExtractMetadata{}, fmt.Errorf("failed to read pclntab: %w", err)
}

if tab.Go12line == nil {
log.Fatalf("pclntab read, but is nil")
return ExtractMetadata{}, fmt.Errorf("read pclntab, but parsing failed. The file may not be fully unpacked or corrupted: %w", err)
}

if len(versionOverride) > 0 {
extractMetadata.Version = versionOverride
}
Expand All @@ -182,16 +192,6 @@ func main_impl(fileName string, printStdPkgs bool, printFilePaths bool, printTyp
extractMetadata.Version = strings.Split(extractMetadata.Version+"-", "-")[0]
}

tab, tabva, err := file.PCLineTable()
if err != nil {
return ExtractMetadata{}, fmt.Errorf("failed to read pclntab: %w", err)
}

if tab.Go12line == nil {
log.Fatalf("pclntab read, but is nil")
return ExtractMetadata{}, fmt.Errorf("read pclntab, but parsing failed. The file may not be fully unpacked or corrupted: %w", err)
}

extractMetadata.TabMeta.CpuQuantum = tab.Go12line.Quantum

// quantum is the minimal unit for a program counter (1 on x86, 4 on most other systems).
Expand Down
2 changes: 1 addition & 1 deletion objfile/disasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (e *Entry) Disasm() (*Disasm, error) {
return nil, err
}

pcln, _, err := e.PCLineTable()
pcln, _, err := e.PCLineTable("")
if err != nil {
return nil, err
}
Expand Down
8 changes: 4 additions & 4 deletions objfile/objfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ func (f *File) Symbols() ([]Sym, error) {
}

// previously : func (f *File) PCLineTable() (Liner, error) {
func (f *File) PCLineTable() (*gosym.Table, uint64, error) {
return f.entries[0].PCLineTable()
func (f *File) PCLineTable(versionOverride string) (*gosym.Table, uint64, error) {
return f.entries[0].PCLineTable(versionOverride)
}

func (f *File) ModuleDataTable(pclntabVA uint64, runtimeVersion string, version string, is64bit bool, littleendian bool) (secStart uint64, moduleData *ModuleData, err error) {
Expand Down Expand Up @@ -188,7 +188,7 @@ func findAllOccurrences(data []byte, searches [][]byte) []int {
}

// previously: func (e *Entry) PCLineTable() (Liner, error)
func (e *Entry) PCLineTable() (*gosym.Table, uint64, error) {
func (e *Entry) PCLineTable(versionOverride string) (*gosym.Table, uint64, error) {
// If the raw file implements Liner directly, use that.
// Currently, only Go intermediate objects and archives (goobj) use this path.

Expand Down Expand Up @@ -226,7 +226,7 @@ func (e *Entry) PCLineTable() (*gosym.Table, uint64, error) {
}

for _, candidate := range candidates {
table, err := gosym.NewTable(candidate.symtab, gosym.NewLineTable(candidate.pclntab, textStart))
table, err := gosym.NewTable(candidate.symtab, gosym.NewLineTable(candidate.pclntab, textStart), versionOverride)
if err != nil || table.Go12line == nil {
continue
}
Expand Down
2 changes: 1 addition & 1 deletion run_test.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash
trap "exit" INT
sudo rm -rf $(pwd)/test
versions=("1.17" "1.16" "1.15" "1.14" "1.13" "1.12" "1.11" "1.10" "1.9" "1.8" "1.7" "1.6" "1.5")
versions=("1.19" "1.18" "1.17" "1.16" "1.15" "1.14" "1.13" "1.12" "1.11" "1.10" "1.9" "1.8" "1.7" "1.6" "1.5")
for v in "${versions[@]}"
do
GO_TAG=$v
Expand Down

0 comments on commit db9fbb3

Please sign in to comment.