[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 &&