Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d3c0c68

Browse files
committed
WIP: gosym v2 implementation
1 parent fbac94a commit d3c0c68

File tree

11 files changed

+1858
-0
lines changed

11 files changed

+1858
-0
lines changed

‎src/debug/gosym/v2/cli/main.go

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2016-present Datadog, Inc.
5+
6+
package main
7+
8+
import (
9+
"debug/gosym/v2"
10+
"flag"
11+
"fmt"
12+
"os"
13+
"strconv"
14+
)
15+
16+
var (
17+
inlines = flag.Bool("inlines", false, "List inline functions")
18+
lines = flag.Bool("lines", false, "List function lines")
19+
)
20+
21+
func functions(table *gosym.Table) error {
22+
for f := range table.Functions() {
23+
name, err := f.Name()
24+
if err != nil {
25+
return err
26+
}
27+
entry, err := f.Entry()
28+
if err != nil {
29+
return err
30+
}
31+
end, err := f.End()
32+
if err != nil {
33+
return err
34+
}
35+
deferreturn, err := f.DeferReturn()
36+
if err != nil {
37+
return err
38+
}
39+
file, err := f.File()
40+
if err != nil {
41+
return err
42+
}
43+
startLine, err := f.StartLine()
44+
if err != nil {
45+
return err
46+
}
47+
fmt.Printf("%s@%s:%d pc=[0x%x, 0x%x) deferreturn=0x%x\n", name.Value(), file.Value(), startLine, entry, end, deferreturn)
48+
}
49+
return nil
50+
}
51+
52+
func describe(table *gosym.Table, pc uint64, inlines, lines bool) error {
53+
f, err := table.ResolveFunction(pc)
54+
name, err := f.Name()
55+
if err != nil {
56+
return err
57+
}
58+
entry, err := f.Entry()
59+
if err != nil {
60+
return err
61+
}
62+
end, err := f.End()
63+
if err != nil {
64+
return err
65+
}
66+
deferreturn, err := f.DeferReturn()
67+
if err != nil {
68+
return err
69+
}
70+
file, err := f.File()
71+
if err != nil {
72+
return err
73+
}
74+
startLine, err := f.StartLine()
75+
if err != nil {
76+
return err
77+
}
78+
fmt.Printf("%s@%s:%d pc=[0x%x, 0x%x) deferreturn=0x%x\n",
79+
name.Value(), file.Value(), startLine, entry, end, deferreturn)
80+
81+
if inlines {
82+
inlines, err := f.InlineFunctions(nil)
83+
if err != nil {
84+
return err
85+
}
86+
if len(inlines) == 0 {
87+
fmt.Println(" (no inlines)")
88+
}
89+
for _, inline := range inlines {
90+
fmt.Printf(" %s@%s:%d\n",
91+
inline.Name.Value(), inline.File.Value(), inline.StartLine)
92+
}
93+
}
94+
if lines {
95+
r, err := f.Lines(gosym.LinesResult{})
96+
if err != nil {
97+
return err
98+
}
99+
if len(r.FunctionLines) == 0 {
100+
fmt.Println(" (no lines)")
101+
}
102+
for _, line := range r.FunctionLines {
103+
fmt.Printf(" [0x%x, 0x%x) %s@%s:%d parentPC=0x%x\n",
104+
line.PCLo, line.PCHi, line.Name.Value(), line.File.Value(), line.Line, line.ParentPC)
105+
}
106+
}
107+
return nil
108+
}
109+
110+
func locate(table *gosym.Table, pc uint64) error {
111+
locs, err := table.ResolveLocations(pc, nil)
112+
if err != nil {
113+
return err
114+
}
115+
for _, loc := range locs {
116+
fmt.Printf("%s@%s:%d\n", loc.Function.Value(), loc.File.Value(), loc.Line)
117+
}
118+
return nil
119+
}
120+
121+
func main() {
122+
usage := func() {
123+
fmt.Printf(`Usage:
124+
$ %s functions <binary> # List all function symbols in this binary
125+
$ %s describe <binary> <pc> (--inlines)? (--lines)? # Describe a function at a specific pc
126+
$ %s locate <binary> <pc> # Locate a pc
127+
`, os.Args[0], os.Args[0], os.Args[0])
128+
os.Exit(1)
129+
}
130+
if len(os.Args) < 3 {
131+
fmt.Printf("missing subcommand or binary argument\n")
132+
usage()
133+
os.Exit(1)
134+
}
135+
136+
binary := os.Args[2]
137+
reader, err := os.Open(binary)
138+
if err != nil {
139+
fmt.Printf("failed to open binary: %v\n", err)
140+
os.Exit(1)
141+
}
142+
defer reader.Close()
143+
table, err := gosym.NewMagic(reader)
144+
if err != nil {
145+
fmt.Printf("failed to parse binary: %v\n", err)
146+
os.Exit(1)
147+
}
148+
149+
var pc uint64
150+
if len(os.Args) > 3 {
151+
pc, err = strconv.ParseUint(os.Args[3], 0, 64)
152+
if err != nil {
153+
fmt.Printf("failed to parse pc: %v\n", err)
154+
os.Exit(1)
155+
}
156+
}
157+
158+
subcommand := os.Args[1]
159+
switch subcommand {
160+
case "functions":
161+
if len(os.Args) != 3 {
162+
fmt.Printf("functions expects 1 argument, got %d\n", len(os.Args)-2)
163+
usage()
164+
os.Exit(1)
165+
}
166+
err := functions(table)
167+
if err != nil {
168+
fmt.Println(err)
169+
os.Exit(1)
170+
}
171+
case "describe":
172+
if len(os.Args) < 4 {
173+
fmt.Printf("describe expects 2 arguments, got %d\n", len(os.Args)-2)
174+
usage()
175+
os.Exit(1)
176+
}
177+
flag.CommandLine.Parse(os.Args[4:])
178+
err := describe(table, pc, *inlines, *lines)
179+
if err != nil {
180+
fmt.Println(err)
181+
os.Exit(1)
182+
}
183+
case "locate":
184+
if len(os.Args) < 4 {
185+
fmt.Printf("locate expects 2 arguments, got %d\n", len(os.Args)-2)
186+
usage()
187+
os.Exit(1)
188+
}
189+
err := locate(table, pc)
190+
if err != nil {
191+
fmt.Println(err)
192+
os.Exit(1)
193+
}
194+
default:
195+
fmt.Printf("unknown subcommand: %s\n", subcommand)
196+
usage()
197+
os.Exit(1)
198+
}
199+
}

‎src/debug/gosym/v2/elf.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package gosym
2+
3+
import (
4+
"debug/elf"
5+
"encoding/binary"
6+
"fmt"
7+
)
8+
9+
type elfObject struct {
10+
*elf.File
11+
}
12+
13+
func (o elfObject) Endian() binary.ByteOrder {
14+
return o.ByteOrder
15+
}
16+
17+
func (o elfObject) Sections() (headers []SectionHeader, err error) {
18+
defer func() {
19+
if r := recover(); r != nil {
20+
err = fmt.Errorf("%w: failed to parse section headers: %v", ErrCorrupted, r)
21+
}
22+
}()
23+
headers = make([]SectionHeader, 0, len(o.File.Sections))
24+
for _, s := range o.File.Sections {
25+
headers = append(headers, SectionHeader{
26+
Name: s.Name,
27+
Addr: s.Addr,
28+
Size: s.Size,
29+
})
30+
}
31+
return
32+
}
33+
34+
func (o elfObject) SectionData(i int8) (data []byte, err error) {
35+
defer func() {
36+
if r := recover(); r != nil {
37+
err = fmt.Errorf("%w: failed to read section: %d: %v", ErrCorrupted, i, r)
38+
}
39+
}()
40+
data, err = o.File.Sections[i].Data()
41+
return
42+
}

‎src/debug/gosym/v2/funcdata.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package gosym
2+
3+
import (
4+
"encoding/binary"
5+
"fmt"
6+
"math"
7+
)
8+
9+
func funcdataEntryOffsetSize(version version, ptrSize uint8) uint64 {
10+
switch version {
11+
case ver12, ver116:
12+
return uint64(ptrSize)
13+
default:
14+
return 4
15+
}
16+
}
17+
18+
func (t *Table) funcdataFieldOffset(field uint64) uint64 {
19+
return funcdataEntryOffsetSize(t.version, t.ptrSize) + (field-1)*4
20+
}
21+
22+
func (t *Table) funcdataField(funcOff uint64, field uint64) (uint32, error) {
23+
offset := t.funcdata[0] + funcOff + t.funcdataFieldOffset(field)
24+
if offset+4 > t.funcdata[1] {
25+
return 0, fmt.Errorf("%w: function data out of bounds: off=%d field=%d", ErrCorrupted, offset, field)
26+
}
27+
return binary.LittleEndian.Uint32(t.pclntab[offset:]), nil
28+
}
29+
30+
func (t *Table) funcdataNameOff(funcOff uint64) (uint32, error) {
31+
return t.funcdataField(funcOff, 1)
32+
}
33+
34+
func (t *Table) funcdataDeferReturn(funcOff uint64) (uint32, error) {
35+
return t.funcdataField(funcOff, 3)
36+
}
37+
38+
func (t *Table) funcdataPcfile(funcOff uint64) (uint32, error) {
39+
return t.funcdataField(funcOff, 5)
40+
}
41+
42+
func (t *Table) funcdataPcln(funcOff uint64) (uint32, error) {
43+
return t.funcdataField(funcOff, 6)
44+
}
45+
46+
func (t *Table) funcdataNpcdata(funcOff uint64) (uint32, error) {
47+
return t.funcdataField(funcOff, 7)
48+
}
49+
50+
func (t *Table) funcdataCuOffset(funcOff uint64) (uint32, error) {
51+
return t.funcdataField(funcOff, 8)
52+
}
53+
54+
func (t *Table) funcdataStartLine(funcOff uint64) (uint32, error) {
55+
line, err := t.funcdataField(funcOff, 9)
56+
if err != nil {
57+
return 0, err
58+
}
59+
return line, nil
60+
}
61+
62+
func (t *Table) funcdataFuncID(funcOff uint64) (uint8, error) {
63+
funcIDOff := t.funcdata[0] + funcOff + t.funcdataFieldOffset(10)
64+
if funcIDOff >= t.funcdata[1] {
65+
return 0, fmt.Errorf("%w: function data out of bounds, off=%d", ErrCorrupted, funcIDOff)
66+
}
67+
return t.pclntab[funcIDOff], nil
68+
}
69+
70+
func (t *Table) funcdataFileNoToOff(funcOff uint64, fno uint32) (uint32, error) {
71+
if t.version == ver12 {
72+
return uint32(fno * 4), nil
73+
}
74+
cuOff, err := t.funcdataCuOffset(funcOff)
75+
if err != nil {
76+
return 0, err
77+
}
78+
offOff := t.cutab[0] + uint64(cuOff)*4 + uint64(fno)*4
79+
if offOff+4 > t.cutab[1] {
80+
return 0, fmt.Errorf("%w: file offset out of bounds", ErrCorrupted)
81+
}
82+
off := binary.LittleEndian.Uint32(t.pclntab[offOff:])
83+
if off == math.MaxUint32 {
84+
// Valid for non-function entries. We skip them at higher levels,
85+
// so here we can return an error.
86+
return 0, fmt.Errorf("%w: no file entry", ErrCorrupted)
87+
}
88+
return off, nil
89+
}
90+
91+
func (t *Table) funcdataFileNo(funcOff uint64) (uint32, error) {
92+
pcfile, err := t.funcdataPcfile(funcOff)
93+
if err != nil {
94+
return 0, err
95+
}
96+
fno, err := t.firstPcValue(pcfile)
97+
if err != nil {
98+
return 0, err
99+
}
100+
return uint32(fno), nil
101+
}
102+
103+
func (t *Table) funcdataFileOff(funcOff uint64) (uint32, error) {
104+
fno, err := t.funcdataFileNo(funcOff)
105+
if err != nil {
106+
return 0, err
107+
}
108+
return t.funcdataFileNoToOff(funcOff, uint32(fno))
109+
}
110+
111+
func (t *Table) funcdataInlTreeIndex(funcOff uint64) (uint32, error) {
112+
npcdata, err := t.funcdataNpcdata(funcOff)
113+
if err != nil {
114+
return 0, err
115+
}
116+
const pcdataInlTreeIndex = 2
117+
if pcdataInlTreeIndex >= npcdata {
118+
return 0, fmt.Errorf("%w: inl tree index out of bounds, off=%d", ErrCorrupted, funcOff)
119+
}
120+
return t.funcdataField(funcOff, uint64(11+pcdataInlTreeIndex))
121+
}
122+
123+
func (t *Table) funcdataInlTree(funcOff uint64) ([]byte, error) {
124+
npcdata, err := t.funcdataNpcdata(funcOff)
125+
if err != nil {
126+
return nil, err
127+
}
128+
nfuncdataOff := t.funcdata[0] + funcOff + t.funcdataFieldOffset(11)
129+
if nfuncdataOff >= t.funcdata[1] {
130+
return nil, fmt.Errorf("%w: function data out of bounds, off=%d", ErrCorrupted, nfuncdataOff)
131+
}
132+
nfuncdata := t.pclntab[nfuncdataOff]
133+
const funcdataInlTree = 3
134+
if funcdataInlTree >= nfuncdata {
135+
return nil, fmt.Errorf("%w: inl tree out of bounds, off=%d", ErrCorrupted, funcOff)
136+
}
137+
inlTreeOff, err := t.funcdataField(funcOff, uint64(11+npcdata+funcdataInlTree))
138+
if err != nil {
139+
return nil, err
140+
}
141+
if inlTreeOff == math.MaxUint32 {
142+
// Valid case - this function has no inline functions
143+
return nil, nil
144+
}
145+
return t.gofunc[inlTreeOff:], nil
146+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /