[tools][elflib] GetBuildIds(): don't complain if not ELF/PE

It is convenient for elflib.GetBuildIds() (and, by extension, the
`buildidtool`) to not error out if it is passed a binary that is neither
ELF nor PE. These are reasonable semantics in their own right - and this
enables cleaner integration of `buildidtool` in the build in cases in
which exectutable() targets are linked in a manner that produces output
in raw binary format.

Bug: 67615
Change-Id: Ia11d58c4f3573ddff0e2dfc21cbab100b99bac99
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/475874
Fuchsia-Auto-Submit: Joshua Seaton <joshuaseaton@google.com>
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
Reviewed-by: Roland McGrath <mcgrathr@google.com>
diff --git a/tools/debug/elflib/elflib.go b/tools/debug/elflib/elflib.go
index 4e66c77..4930666 100644
--- a/tools/debug/elflib/elflib.go
+++ b/tools/debug/elflib/elflib.go
@@ -60,6 +60,14 @@
 	return err == nil && bytes.Equal(magicBytes, []byte(pdbMagic))
 }
 
+func hasElfMagic(r io.ReaderAt) bool {
+	var magic [4]byte
+	if _, err := r.ReadAt(magic[0:], 0); err != nil {
+		return false
+	}
+	return bytes.Compare(magic[0:], []byte(elf.ELFMAG)) == 0
+}
+
 // Verify verifies that the build id of b matches the build id found in the file.
 func (b BinaryFileRef) Verify() error {
 	file, err := os.Open(b.Filepath)
@@ -158,13 +166,23 @@
 
 // GetBuildIDs parses and returns all the build ids from file's section/program headers.
 func GetBuildIDs(filename string, file io.ReaderAt) ([][]byte, error) {
+	// Consider other binary formats if the file does not appear to be ELF.
+	if !hasElfMagic(file) {
+		if hasPeMagic(file) {
+			ids, err := PEGetBuildIDs(filename, file)
+			if err != nil {
+				return nil, fmt.Errorf("error with assumption of %q as PE/COFF: %w", filename, err)
+			}
+			return ids, nil
+		} else {
+			// No build IDs that we are aware of.
+			return nil, nil
+		}
+	}
+
 	elfFile, err := elf.NewFile(file)
 	if err != nil {
-		// If it's not ELF, maybe it's PE?
-		if out, peErr := PEGetBuildIDs(filename, file); peErr == nil {
-			return out, peErr
-		}
-		return nil, fmt.Errorf("could not parse ELF file %s: %w", filename, err)
+		return nil, fmt.Errorf("could not parse %q as ELF: %w", filename, err)
 	}
 	if len(elfFile.Progs) == 0 && len(elfFile.Sections) == 0 {
 		return nil, fmt.Errorf("no program headers or sections in %s", filename)
diff --git a/tools/debug/elflib/elflib_test.go b/tools/debug/elflib/elflib_test.go
index 79c3ef8..3c492fd 100644
--- a/tools/debug/elflib/elflib_test.go
+++ b/tools/debug/elflib/elflib_test.go
@@ -5,6 +5,7 @@
 package elflib
 
 import (
+	"bytes"
 	"encoding/hex"
 	"flag"
 	"os"
@@ -51,3 +52,48 @@
 		t.Fatal("expected ", expected, " but got ", buildIDs[0])
 	}
 }
+
+func TestHasMagic(t *testing.T) {
+	{
+		buff := []byte("\177ELF...")
+		r := bytes.NewReader(buff)
+		if !hasElfMagic(r) {
+			t.Errorf("expected %q to have ELF magic", buff)
+		}
+	}
+
+	{
+		buff := []byte("Not at the beginning: \177ELF...")
+		r := bytes.NewReader(buff)
+		if hasElfMagic(r) {
+			t.Errorf("expected %q to not have ELF magic", buff)
+		}
+	}
+
+	{
+		buff := []byte("MZ...")
+		r := bytes.NewReader(buff)
+		if !hasPeMagic(r) {
+			t.Errorf("expected %q to have PE magic", buff)
+		}
+	}
+
+	{
+		buff := []byte("Not at the beginning: MZ...")
+		r := bytes.NewReader(buff)
+		if hasPeMagic(r) {
+			t.Errorf("expected %q to not have ELF magic", buff)
+		}
+	}
+
+	{
+		buff := []byte("garbage")
+		r := bytes.NewReader(buff)
+		if hasElfMagic(r) {
+			t.Errorf("expected %q to not have ELF magic", buff)
+		}
+		if hasPeMagic(r) {
+			t.Errorf("expected %q to not have PE magic", buff)
+		}
+	}
+}
diff --git a/tools/debug/elflib/pe.go b/tools/debug/elflib/pe.go
index 49b17a6..fc89ee0 100644
--- a/tools/debug/elflib/pe.go
+++ b/tools/debug/elflib/pe.go
@@ -5,6 +5,7 @@
 package elflib
 
 import (
+	"bytes"
 	"debug/pe"
 	"encoding/binary"
 	"fmt"
@@ -22,8 +23,17 @@
 	DebugDirectoryAddressOffset = 20
 	IMAGE_DEBUG_TYPE_CODEVIEW   = 2
 	PDB70                       = 0x53445352
+	peMagic                     = "MZ"
 )
 
+func hasPeMagic(r io.ReaderAt) bool {
+	var magic [2]byte
+	if _, err := r.ReadAt(magic[0:], 0); err != nil {
+		return false
+	}
+	return bytes.Compare(magic[0:], []byte(peMagic)) == 0
+}
+
 func loadData(filename string, file io.ReaderAt, peFile *pe.File, dir pe.DataDirectory) ([]byte, error) {
 	for _, scn := range peFile.Sections {
 		if start := dir.VirtualAddress - scn.VirtualAddress; scn.VirtualAddress <= dir.VirtualAddress &&