|
@@ -17,6 +17,7 @@ package binutils
|
17
|
17
|
|
18
|
18
|
import (
|
19
|
19
|
"debug/elf"
|
|
20
|
+ "debug/macho"
|
20
|
21
|
"fmt"
|
21
|
22
|
"os"
|
22
|
23
|
"os/exec"
|
|
@@ -32,10 +33,14 @@ import (
|
32
|
33
|
// SetConfig must be called before any of the other methods.
|
33
|
34
|
type Binutils struct {
|
34
|
35
|
// Commands to invoke.
|
35
|
|
- llvmSymbolizer string
|
36
|
|
- addr2line string
|
37
|
|
- nm string
|
38
|
|
- objdump string
|
|
36
|
+ llvmSymbolizer string
|
|
37
|
+ llvmSymbolizerFound bool
|
|
38
|
+ addr2line string
|
|
39
|
+ addr2lineFound bool
|
|
40
|
+ nm string
|
|
41
|
+ nmFound bool
|
|
42
|
+ objdump string
|
|
43
|
+ objdumpFound bool
|
39
|
44
|
|
40
|
45
|
// if fast, perform symbolization using nm (symbol names only),
|
41
|
46
|
// instead of file-line detail from the slower addr2line.
|
|
@@ -66,22 +71,22 @@ func (b *Binutils) SetTools(config string) {
|
66
|
71
|
}
|
67
|
72
|
|
68
|
73
|
defaultPath := paths[""]
|
69
|
|
- b.llvmSymbolizer = findExe("llvm-symbolizer", append(paths["llvm-symbolizer"], defaultPath...))
|
70
|
|
- b.addr2line = findExe("addr2line", append(paths["addr2line"], defaultPath...))
|
71
|
|
- b.nm = findExe("nm", append(paths["nm"], defaultPath...))
|
72
|
|
- b.objdump = findExe("objdump", append(paths["objdump"], defaultPath...))
|
|
74
|
+ b.llvmSymbolizer, b.llvmSymbolizerFound = findExe("llvm-symbolizer", append(paths["llvm-symbolizer"], defaultPath...))
|
|
75
|
+ b.addr2line, b.addr2lineFound = findExe("addr2line", append(paths["addr2line"], defaultPath...))
|
|
76
|
+ b.nm, b.nmFound = findExe("nm", append(paths["nm"], defaultPath...))
|
|
77
|
+ b.objdump, b.objdumpFound = findExe("objdump", append(paths["objdump"], defaultPath...))
|
73
|
78
|
}
|
74
|
79
|
|
75
|
80
|
// findExe looks for an executable command on a set of paths.
|
76
|
81
|
// If it cannot find it, returns cmd.
|
77
|
|
-func findExe(cmd string, paths []string) string {
|
|
82
|
+func findExe(cmd string, paths []string) (string, bool) {
|
78
|
83
|
for _, p := range paths {
|
79
|
84
|
cp := filepath.Join(p, cmd)
|
80
|
85
|
if c, err := exec.LookPath(cp); err == nil {
|
81
|
|
- return c
|
|
86
|
+ return c, true
|
82
|
87
|
}
|
83
|
88
|
}
|
84
|
|
- return cmd
|
|
89
|
+ return cmd, false
|
85
|
90
|
}
|
86
|
91
|
|
87
|
92
|
// Disasm returns the assembly instructions for the specified address range
|
|
@@ -118,21 +123,42 @@ func (b *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFil
|
118
|
123
|
// use a table of prefixes if we need to support other
|
119
|
124
|
// systems at some point.
|
120
|
125
|
|
121
|
|
- f, err := os.Open(name)
|
122
|
|
- if err != nil {
|
|
126
|
+ if _, err := os.Stat(name); err != nil {
|
123
|
127
|
// For testing, do not require file name to exist.
|
124
|
128
|
if strings.Contains(b.addr2line, "testdata/") {
|
125
|
129
|
return &fileAddr2Line{file: file{b: b, name: name}}, nil
|
126
|
130
|
}
|
127
|
|
-
|
128
|
131
|
return nil, err
|
129
|
132
|
}
|
130
|
|
- defer f.Close()
|
131
|
133
|
|
132
|
|
- ef, err := elf.NewFile(f)
|
|
134
|
+ if f, err := b.openELF(name, start, limit, offset); err == nil {
|
|
135
|
+ return f, nil
|
|
136
|
+ }
|
|
137
|
+ if f, err := b.openMachO(name, start, limit, offset); err == nil {
|
|
138
|
+ return f, nil
|
|
139
|
+ }
|
|
140
|
+ return nil, fmt.Errorf("unrecognized binary: %s", name)
|
|
141
|
+}
|
|
142
|
+
|
|
143
|
+func (b *Binutils) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
|
144
|
+ of, err := macho.Open(name)
|
|
145
|
+ if err != nil {
|
|
146
|
+ return nil, fmt.Errorf("Parsing %s: %v", name, err)
|
|
147
|
+ }
|
|
148
|
+ defer of.Close()
|
|
149
|
+
|
|
150
|
+ if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) {
|
|
151
|
+ return &fileNM{file: file{b: b, name: name}}, nil
|
|
152
|
+ }
|
|
153
|
+ return &fileAddr2Line{file: file{b: b, name: name}}, nil
|
|
154
|
+}
|
|
155
|
+
|
|
156
|
+func (b *Binutils) openELF(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
|
157
|
+ ef, err := elf.Open(name)
|
133
|
158
|
if err != nil {
|
134
|
159
|
return nil, fmt.Errorf("Parsing %s: %v", name, err)
|
135
|
160
|
}
|
|
161
|
+ defer ef.Close()
|
136
|
162
|
|
137
|
163
|
var stextOffset *uint64
|
138
|
164
|
var pageAligned = func(addr uint64) bool { return addr%4096 == 0 }
|
|
@@ -162,12 +188,13 @@ func (b *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFil
|
162
|
188
|
return nil, fmt.Errorf("Could not identify base for %s: %v", name, err)
|
163
|
189
|
}
|
164
|
190
|
|
165
|
|
- // Find build ID, while we have the file open.
|
166
|
191
|
buildID := ""
|
167
|
|
- if id, err := elfexec.GetBuildID(f); err == nil {
|
168
|
|
- buildID = fmt.Sprintf("%x", id)
|
|
192
|
+ if f, err := os.Open(name); err == nil {
|
|
193
|
+ if id, err := elfexec.GetBuildID(f); err == nil {
|
|
194
|
+ buildID = fmt.Sprintf("%x", id)
|
|
195
|
+ }
|
169
|
196
|
}
|
170
|
|
- if b.fast {
|
|
197
|
+ if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) {
|
171
|
198
|
return &fileNM{file: file{b, name, base, buildID}}, nil
|
172
|
199
|
}
|
173
|
200
|
return &fileAddr2Line{file: file{b, name, base, buildID}}, nil
|