Sfoglia il codice sorgente

Improve "unrecognized binary" error messages (#408)

Improve "unrecognized binary" error messages

Previously it would just print "unrecognized binary" no matter what the underlying error message was. This was partly because it was unable to know the actual file type except by trial and error. This commit uses the magic number instead. It also gives a nicer error for fat Mach-O binaries which are currently unsupported.
Tim 6 anni fa
parent
commit
781f11b1fc

+ 42
- 9
internal/binutils/binutils.go Vedi File

@@ -18,7 +18,9 @@ package binutils
18 18
 import (
19 19
 	"debug/elf"
20 20
 	"debug/macho"
21
+	"encoding/binary"
21 22
 	"fmt"
23
+	"io"
22 24
 	"os"
23 25
 	"os/exec"
24 26
 	"path/filepath"
@@ -173,12 +175,8 @@ func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFi
173 175
 	b := bu.get()
174 176
 
175 177
 	// Make sure file is a supported executable.
176
-	// The pprof driver uses Open to sniff the difference
177
-	// between an executable and a profile.
178
-	// For now, only ELF is supported.
179
-	// Could read the first few bytes of the file and
180
-	// use a table of prefixes if we need to support other
181
-	// systems at some point.
178
+	// This uses magic numbers, mainly to provide better error messages but
179
+	// it should also help speed.
182 180
 
183 181
 	if _, err := os.Stat(name); err != nil {
184 182
 		// For testing, do not require file name to exist.
@@ -188,13 +186,48 @@ func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFi
188 186
 		return nil, err
189 187
 	}
190 188
 
191
-	if f, err := b.openELF(name, start, limit, offset); err == nil {
189
+	// Read the first 4 bytes of the file.
190
+
191
+	f, err := os.Open(name)
192
+	if err != nil {
193
+		return nil, fmt.Errorf("error opening %s: %s", name, err)
194
+	}
195
+	defer f.Close()
196
+
197
+	var header [4]byte
198
+	if _, err = io.ReadFull(f, header[:]); err != nil {
199
+		return nil, fmt.Errorf("error reading magic number from %s: %s", name, err)
200
+	}
201
+
202
+	elfMagic := string(header[:])
203
+
204
+	// Match against supported file types.
205
+	if elfMagic == elf.ELFMAG {
206
+		f, err := b.openELF(name, start, limit, offset)
207
+		if err != nil {
208
+			return nil, fmt.Errorf("error reading ELF file %s: %s", name, err)
209
+		}
192 210
 		return f, nil
193 211
 	}
194
-	if f, err := b.openMachO(name, start, limit, offset); err == nil {
212
+
213
+	// Mach-O magic numbers can be big or little endian.
214
+	machoMagicLittle := binary.LittleEndian.Uint32(header[:])
215
+	machoMagicBig := binary.BigEndian.Uint32(header[:])
216
+
217
+	if machoMagicLittle == macho.Magic32 || machoMagicLittle == macho.Magic64 ||
218
+		machoMagicBig == macho.Magic32 || machoMagicBig == macho.Magic64 {
219
+		f, err := b.openMachO(name, start, limit, offset)
220
+		if err != nil {
221
+			return nil, fmt.Errorf("error reading Mach-O file %s: %s", name, err)
222
+		}
195 223
 		return f, nil
196 224
 	}
197
-	return nil, fmt.Errorf("unrecognized binary: %s", name)
225
+	if machoMagicLittle == macho.MagicFat || machoMagicBig == macho.MagicFat {
226
+		// TODO: #1033
227
+		return nil, fmt.Errorf("fat Mach-O archives are currently unsupported")
228
+	}
229
+
230
+	return nil, fmt.Errorf("unrecognized binary format: %s", name)
198 231
 }
199 232
 
200 233
 func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) {

+ 29
- 0
internal/binutils/binutils_test.go Vedi File

@@ -22,6 +22,7 @@ import (
22 22
 	"reflect"
23 23
 	"regexp"
24 24
 	"runtime"
25
+	"strings"
25 26
 	"testing"
26 27
 
27 28
 	"github.com/google/pprof/internal/plugin"
@@ -361,3 +362,31 @@ func TestLLVMSymbolizer(t *testing.T) {
361 362
 		}
362 363
 	}
363 364
 }
365
+
366
+func TestOpenMalformedELF(t *testing.T) {
367
+	// Test that opening a malformed ELF file will report an error containing
368
+	// the word "ELF".
369
+	bu := &Binutils{}
370
+	_, err := bu.Open(filepath.Join("testdata", "malformed_elf"), 0, 0, 0)
371
+	if err == nil {
372
+		t.Fatalf("Open: unexpected success")
373
+	}
374
+
375
+	if !strings.Contains(err.Error(), "ELF") {
376
+		t.Errorf("Open: got %v, want error containing 'ELF'", err)
377
+	}
378
+}
379
+
380
+func TestOpenMalformedMachO(t *testing.T) {
381
+	// Test that opening a malformed Mach-O file will report an error containing
382
+	// the word "Mach-O".
383
+	bu := &Binutils{}
384
+	_, err := bu.Open(filepath.Join("testdata", "malformed_macho"), 0, 0, 0)
385
+	if err == nil {
386
+		t.Fatalf("Open: unexpected success")
387
+	}
388
+
389
+	if !strings.Contains(err.Error(), "Mach-O") {
390
+		t.Errorf("Open: got %v, want error containing 'Mach-O'", err)
391
+	}
392
+}

+ 1
- 0
internal/binutils/testdata/malformed_elf Vedi File

@@ -0,0 +1 @@
1
+ELF˙˙˙˙˙˙˙˙

+ 1
- 0
internal/binutils/testdata/malformed_macho Vedi File

@@ -0,0 +1 @@
1
+销睨��������