|
@@ -25,6 +25,7 @@ import (
|
25
|
25
|
"os/exec"
|
26
|
26
|
"path/filepath"
|
27
|
27
|
"regexp"
|
|
28
|
+ "runtime"
|
28
|
29
|
"strings"
|
29
|
30
|
"sync"
|
30
|
31
|
|
|
@@ -190,13 +191,13 @@ func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFi
|
190
|
191
|
|
191
|
192
|
f, err := os.Open(name)
|
192
|
193
|
if err != nil {
|
193
|
|
- return nil, fmt.Errorf("error opening %s: %s", name, err)
|
|
194
|
+ return nil, fmt.Errorf("error opening %s: %v", name, err)
|
194
|
195
|
}
|
195
|
196
|
defer f.Close()
|
196
|
197
|
|
197
|
198
|
var header [4]byte
|
198
|
199
|
if _, err = io.ReadFull(f, header[:]); err != nil {
|
199
|
|
- return nil, fmt.Errorf("error reading magic number from %s: %s", name, err)
|
|
200
|
+ return nil, fmt.Errorf("error reading magic number from %s: %v", name, err)
|
200
|
201
|
}
|
201
|
202
|
|
202
|
203
|
elfMagic := string(header[:])
|
|
@@ -205,7 +206,7 @@ func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFi
|
205
|
206
|
if elfMagic == elf.ELFMAG {
|
206
|
207
|
f, err := b.openELF(name, start, limit, offset)
|
207
|
208
|
if err != nil {
|
208
|
|
- return nil, fmt.Errorf("error reading ELF file %s: %s", name, err)
|
|
209
|
+ return nil, fmt.Errorf("error reading ELF file %s: %v", name, err)
|
209
|
210
|
}
|
210
|
211
|
return f, nil
|
211
|
212
|
}
|
|
@@ -218,24 +219,22 @@ func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFi
|
218
|
219
|
machoMagicBig == macho.Magic32 || machoMagicBig == macho.Magic64 {
|
219
|
220
|
f, err := b.openMachO(name, start, limit, offset)
|
220
|
221
|
if err != nil {
|
221
|
|
- return nil, fmt.Errorf("error reading Mach-O file %s: %s", name, err)
|
|
222
|
+ return nil, fmt.Errorf("error reading Mach-O file %s: %v", name, err)
|
222
|
223
|
}
|
223
|
224
|
return f, nil
|
224
|
225
|
}
|
225
|
226
|
if machoMagicLittle == macho.MagicFat || machoMagicBig == macho.MagicFat {
|
226
|
|
- // TODO: #1033
|
227
|
|
- return nil, fmt.Errorf("fat Mach-O archives are currently unsupported")
|
|
227
|
+ f, err := b.openFatMachO(name, start, limit, offset)
|
|
228
|
+ if err != nil {
|
|
229
|
+ return nil, fmt.Errorf("error reading fat Mach-O file %s: %v", name, err)
|
|
230
|
+ }
|
|
231
|
+ return f, nil
|
228
|
232
|
}
|
229
|
233
|
|
230
|
234
|
return nil, fmt.Errorf("unrecognized binary format: %s", name)
|
231
|
235
|
}
|
232
|
236
|
|
233
|
|
-func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
234
|
|
- of, err := macho.Open(name)
|
235
|
|
- if err != nil {
|
236
|
|
- return nil, fmt.Errorf("error parsing %s: %v", name, err)
|
237
|
|
- }
|
238
|
|
- defer of.Close()
|
|
237
|
+func (b *binrep) openMachOCommon(name string, of *macho.File, start, limit, offset uint64) (plugin.ObjFile, error) {
|
239
|
238
|
|
240
|
239
|
// Subtract the load address of the __TEXT section. Usually 0 for shared
|
241
|
240
|
// libraries or 0x100000000 for executables. You can check this value by
|
|
@@ -258,6 +257,53 @@ func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.Obj
|
258
|
257
|
return &fileAddr2Line{file: file{b: b, name: name, base: base}}, nil
|
259
|
258
|
}
|
260
|
259
|
|
|
260
|
+func (b *binrep) openFatMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
|
261
|
+ of, err := macho.OpenFat(name)
|
|
262
|
+ if err != nil {
|
|
263
|
+ return nil, fmt.Errorf("error parsing %s: %v", name, err)
|
|
264
|
+ }
|
|
265
|
+ defer of.Close()
|
|
266
|
+
|
|
267
|
+ if len(of.Arches) == 0 {
|
|
268
|
+ return nil, fmt.Errorf("empty fat Mach-O file: %s", name)
|
|
269
|
+ }
|
|
270
|
+
|
|
271
|
+ var arch macho.Cpu
|
|
272
|
+ // Use the host architecture.
|
|
273
|
+ // TODO: This is not ideal because the host architecture may not be the one
|
|
274
|
+ // that was profiled. E.g. an amd64 host can profile a 386 program.
|
|
275
|
+ switch runtime.GOARCH {
|
|
276
|
+ case "386":
|
|
277
|
+ arch = macho.Cpu386
|
|
278
|
+ case "amd64", "amd64p32":
|
|
279
|
+ arch = macho.CpuAmd64
|
|
280
|
+ case "arm", "armbe", "arm64", "arm64be":
|
|
281
|
+ arch = macho.CpuArm
|
|
282
|
+ case "ppc":
|
|
283
|
+ arch = macho.CpuPpc
|
|
284
|
+ case "ppc64", "ppc64le":
|
|
285
|
+ arch = macho.CpuPpc64
|
|
286
|
+ default:
|
|
287
|
+ return nil, fmt.Errorf("unsupported host architecture for %s: %s", name, runtime.GOARCH)
|
|
288
|
+ }
|
|
289
|
+ for i := range of.Arches {
|
|
290
|
+ if of.Arches[i].Cpu == arch {
|
|
291
|
+ return b.openMachOCommon(name, of.Arches[i].File, start, limit, offset)
|
|
292
|
+ }
|
|
293
|
+ }
|
|
294
|
+ return nil, fmt.Errorf("architecture not found in %s: %s", name, runtime.GOARCH)
|
|
295
|
+}
|
|
296
|
+
|
|
297
|
+func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
|
298
|
+ of, err := macho.Open(name)
|
|
299
|
+ if err != nil {
|
|
300
|
+ return nil, fmt.Errorf("error parsing %s: %v", name, err)
|
|
301
|
+ }
|
|
302
|
+ defer of.Close()
|
|
303
|
+
|
|
304
|
+ return b.openMachOCommon(name, of, start, limit, offset)
|
|
305
|
+}
|
|
306
|
+
|
261
|
307
|
func (b *binrep) openELF(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
262
|
308
|
ef, err := elf.Open(name)
|
263
|
309
|
if err != nil {
|