|
@@ -19,6 +19,7 @@ import (
|
19
|
19
|
"debug/elf"
|
20
|
20
|
"debug/macho"
|
21
|
21
|
"encoding/binary"
|
|
22
|
+ "errors"
|
22
|
23
|
"fmt"
|
23
|
24
|
"io"
|
24
|
25
|
"os"
|
|
@@ -26,6 +27,7 @@ import (
|
26
|
27
|
"path/filepath"
|
27
|
28
|
"regexp"
|
28
|
29
|
"runtime"
|
|
30
|
+ "strconv"
|
29
|
31
|
"strings"
|
30
|
32
|
"sync"
|
31
|
33
|
|
|
@@ -39,6 +41,8 @@ type Binutils struct {
|
39
|
41
|
rep *binrep
|
40
|
42
|
}
|
41
|
43
|
|
|
44
|
+var objdumpLLVMVerRE = regexp.MustCompile(`LLVM version (?:(\d*)\.(\d*)\.(\d*)|.*(trunk).*)`)
|
|
45
|
+
|
42
|
46
|
// binrep is an immutable representation for Binutils. It is atomically
|
43
|
47
|
// replaced on every mutation to provide thread-safe access.
|
44
|
48
|
type binrep struct {
|
|
@@ -51,6 +55,7 @@ type binrep struct {
|
51
|
55
|
nmFound bool
|
52
|
56
|
objdump string
|
53
|
57
|
objdumpFound bool
|
|
58
|
+ isLLVMObjdump bool
|
54
|
59
|
|
55
|
60
|
// if fast, perform symbolization using nm (symbol names only),
|
56
|
61
|
// instead of file-line detail from the slower addr2line.
|
|
@@ -140,7 +145,77 @@ func initTools(b *binrep, config string) {
|
140
|
145
|
b.addr2line, b.addr2lineFound = findExe("gaddr2line", append(paths["addr2line"], defaultPath...))
|
141
|
146
|
}
|
142
|
147
|
b.nm, b.nmFound = findExe("nm", append(paths["nm"], defaultPath...))
|
143
|
|
- b.objdump, b.objdumpFound = findExe("objdump", append(paths["objdump"], defaultPath...))
|
|
148
|
+ b.objdump, b.objdumpFound, b.isLLVMObjdump = findObjdump(append(paths["objdump"], defaultPath...))
|
|
149
|
+}
|
|
150
|
+
|
|
151
|
+// findObjdump finds and returns path to preferred objdump binary.
|
|
152
|
+// Order of preference is: llvm-objdump, objdump.
|
|
153
|
+// On MacOS only, also looks for gobjdump with least preference.
|
|
154
|
+// Accepts a list of paths and returns:
|
|
155
|
+// a string with path to the preferred objdump binary if found,
|
|
156
|
+// or an empty string if not found;
|
|
157
|
+// a boolean if any acceptable objdump was found;
|
|
158
|
+// a boolen indicating if it is an LLVM objdump.
|
|
159
|
+func findObjdump(paths []string) (string, bool, bool) {
|
|
160
|
+ objdumpNames := []string{"llvm-objdump", "objdump"}
|
|
161
|
+ if runtime.GOOS == "darwin" {
|
|
162
|
+ objdumpNames = append(objdumpNames, "gobjdump")
|
|
163
|
+ }
|
|
164
|
+
|
|
165
|
+ for _, objdumpName := range objdumpNames {
|
|
166
|
+ if objdump, objdumpFound := findExe(objdumpName, paths); objdumpFound {
|
|
167
|
+ cmdOut, err := exec.Command(objdump, "--version").Output()
|
|
168
|
+ if err != nil {
|
|
169
|
+ continue
|
|
170
|
+ }
|
|
171
|
+ if isLLVMObjdump(string(cmdOut)) {
|
|
172
|
+ return objdump, true, true
|
|
173
|
+ }
|
|
174
|
+ if isBuObjdump(string(cmdOut)) {
|
|
175
|
+ return objdump, true, false
|
|
176
|
+ }
|
|
177
|
+ }
|
|
178
|
+ }
|
|
179
|
+ return "", false, false
|
|
180
|
+}
|
|
181
|
+
|
|
182
|
+// isLLVMObjdump accepts a string with path to an objdump binary,
|
|
183
|
+// and returns a boolean indicating if the given binary is an LLVM
|
|
184
|
+// objdump binary of an acceptable version.
|
|
185
|
+func isLLVMObjdump(output string) bool {
|
|
186
|
+ fields := objdumpLLVMVerRE.FindStringSubmatch(output)
|
|
187
|
+ if len(fields) != 5 {
|
|
188
|
+ return false
|
|
189
|
+ }
|
|
190
|
+ if fields[4] == "trunk" {
|
|
191
|
+ return true
|
|
192
|
+ }
|
|
193
|
+ verMajor, err := strconv.Atoi(fields[1])
|
|
194
|
+ if err != nil {
|
|
195
|
+ return false
|
|
196
|
+ }
|
|
197
|
+ verPatch, err := strconv.Atoi(fields[3])
|
|
198
|
+ if err != nil {
|
|
199
|
+ return false
|
|
200
|
+ }
|
|
201
|
+ if runtime.GOOS == "linux" && verMajor >= 8 {
|
|
202
|
+ // Ensure LLVM objdump is at least version 8.0 on Linux.
|
|
203
|
+ // Some flags, like --demangle, and double dashes for options are
|
|
204
|
+ // not supported by previous versions.
|
|
205
|
+ return true
|
|
206
|
+ }
|
|
207
|
+ if runtime.GOOS == "darwin" {
|
|
208
|
+ // Ensure LLVM objdump is at least version 10.0.1 on MacOS.
|
|
209
|
+ return verMajor > 10 || (verMajor == 10 && verPatch >= 1)
|
|
210
|
+ }
|
|
211
|
+ return false
|
|
212
|
+}
|
|
213
|
+
|
|
214
|
+// isBuObjdump accepts a string with path to an objdump binary,
|
|
215
|
+// and returns a boolean indicating if the given binary is a GNU
|
|
216
|
+// binutils objdump binary. No version check is performed.
|
|
217
|
+func isBuObjdump(output string) bool {
|
|
218
|
+ return strings.Contains(output, "GNU objdump") && strings.Contains(output, "Binutils")
|
144
|
219
|
}
|
145
|
220
|
|
146
|
221
|
// findExe looks for an executable command on a set of paths.
|
|
@@ -159,13 +234,16 @@ func findExe(cmd string, paths []string) (string, bool) {
|
159
|
234
|
// of a binary.
|
160
|
235
|
func (bu *Binutils) Disasm(file string, start, end uint64, intelSyntax bool) ([]plugin.Inst, error) {
|
161
|
236
|
b := bu.get()
|
162
|
|
- args := []string{"-d", "-C", "--no-show-raw-insn", "-l",
|
163
|
|
- fmt.Sprintf("--start-address=%#x", start),
|
|
237
|
+ if !b.objdumpFound {
|
|
238
|
+ return nil, errors.New("cannot disasm: no objdump tool available")
|
|
239
|
+ }
|
|
240
|
+ args := []string{"--disassemble-all", "--demangle", "--no-show-raw-insn",
|
|
241
|
+ "--line-numbers", fmt.Sprintf("--start-address=%#x", start),
|
164
|
242
|
fmt.Sprintf("--stop-address=%#x", end)}
|
165
|
243
|
|
166
|
244
|
if intelSyntax {
|
167
|
|
- if runtime.GOOS == "darwin" {
|
168
|
|
- args = append(args, "-x86-asm-syntax=intel")
|
|
245
|
+ if b.isLLVMObjdump {
|
|
246
|
+ args = append(args, "--x86-asm-syntax=intel")
|
169
|
247
|
} else {
|
170
|
248
|
args = append(args, "-M", "intel")
|
171
|
249
|
}
|