Move ELF build-id reader into a separate library.
Bug: 328702178
Change-Id: I188a8d20d22e67e4f0c7e3441e3781fff369c828
diff --git a/cmd/symbols_map/Android.bp b/cmd/symbols_map/Android.bp
index 0ba3b07..e3ae6ed 100644
--- a/cmd/symbols_map/Android.bp
+++ b/cmd/symbols_map/Android.bp
@@ -5,17 +5,16 @@
blueprint_go_binary {
name: "symbols_map",
srcs: [
- "elf.go",
"r8.go",
"symbols_map.go",
],
testSrcs: [
- "elf_test.go",
"r8_test.go",
],
deps: [
"blueprint-pathtools",
"golang-protobuf-encoding-prototext",
+ "soong-elf",
"soong-response",
"symbols_map_proto",
],
diff --git a/cmd/symbols_map/elf.go b/cmd/symbols_map/elf.go
deleted file mode 100644
index 950e3b2..0000000
--- a/cmd/symbols_map/elf.go
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2022 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "debug/elf"
- "encoding/binary"
- "encoding/hex"
- "errors"
- "fmt"
- "io"
- "os"
-)
-
-const gnuBuildID = "GNU\x00"
-
-// elfIdentifier extracts the elf build ID from an elf file. If allowMissing is true it returns
-// an empty identifier if the file exists but the build ID note does not.
-func elfIdentifier(filename string, allowMissing bool) (string, error) {
- f, err := os.Open(filename)
- if err != nil {
- return "", fmt.Errorf("failed to open %s: %w", filename, err)
- }
- defer f.Close()
-
- return elfIdentifierFromReaderAt(f, filename, allowMissing)
-}
-
-// elfIdentifierFromReaderAt extracts the elf build ID from a ReaderAt. If allowMissing is true it
-// returns an empty identifier if the file exists but the build ID note does not.
-func elfIdentifierFromReaderAt(r io.ReaderAt, filename string, allowMissing bool) (string, error) {
- f, err := elf.NewFile(r)
- if err != nil {
- if allowMissing {
- if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
- return "", nil
- }
- if _, ok := err.(*elf.FormatError); ok {
- // The file was not an elf file.
- return "", nil
- }
- }
- return "", fmt.Errorf("failed to parse elf file %s: %w", filename, err)
- }
- defer f.Close()
-
- buildIDNote := f.Section(".note.gnu.build-id")
- if buildIDNote == nil {
- if allowMissing {
- return "", nil
- }
- return "", fmt.Errorf("failed to find .note.gnu.build-id in %s", filename)
- }
-
- buildIDs, err := readNote(buildIDNote.Open(), f.ByteOrder)
- if err != nil {
- return "", fmt.Errorf("failed to read .note.gnu.build-id: %w", err)
- }
-
- for name, desc := range buildIDs {
- if name == gnuBuildID {
- return hex.EncodeToString(desc), nil
- }
- }
-
- return "", nil
-}
-
-// readNote reads the contents of a note section, returning it as a map from name to descriptor.
-func readNote(note io.Reader, byteOrder binary.ByteOrder) (map[string][]byte, error) {
- var noteHeader struct {
- Namesz uint32
- Descsz uint32
- Type uint32
- }
-
- notes := make(map[string][]byte)
- for {
- err := binary.Read(note, byteOrder, ¬eHeader)
- if err != nil {
- if err == io.EOF {
- return notes, nil
- }
- return nil, fmt.Errorf("failed to read note header: %w", err)
- }
-
- nameBuf := make([]byte, align4(noteHeader.Namesz))
- err = binary.Read(note, byteOrder, &nameBuf)
- if err != nil {
- return nil, fmt.Errorf("failed to read note name: %w", err)
- }
- name := string(nameBuf[:noteHeader.Namesz])
-
- descBuf := make([]byte, align4(noteHeader.Descsz))
- err = binary.Read(note, byteOrder, &descBuf)
- if err != nil {
- return nil, fmt.Errorf("failed to read note desc: %w", err)
- }
- notes[name] = descBuf[:noteHeader.Descsz]
- }
-}
-
-// align4 rounds the input up to the next multiple of 4.
-func align4(i uint32) uint32 {
- return (i + 3) &^ 3
-}
diff --git a/cmd/symbols_map/elf_test.go b/cmd/symbols_map/elf_test.go
deleted file mode 100644
index a94c87f..0000000
--- a/cmd/symbols_map/elf_test.go
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2022 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "bytes"
- "debug/elf"
- "encoding/binary"
- "reflect"
- "testing"
-)
-
-func Test_elfIdentifierFromReaderAt_BadElfFile(t *testing.T) {
- tests := []struct {
- name string
- contents string
- }{
- {
- name: "empty",
- contents: "",
- },
- {
- name: "text",
- contents: "#!/bin/bash\necho foobar",
- },
- {
- name: "empty elf",
- contents: emptyElfFile(),
- },
- {
- name: "short section header",
- contents: shortSectionHeaderElfFile(),
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- buf := bytes.NewReader([]byte(tt.contents))
- _, err := elfIdentifierFromReaderAt(buf, "<>", false)
- if err == nil {
- t.Errorf("expected error reading bad elf file without allowMissing")
- }
- _, err = elfIdentifierFromReaderAt(buf, "<>", true)
- if err != nil {
- t.Errorf("expected no error reading bad elf file with allowMissing, got %q", err.Error())
- }
- })
- }
-}
-
-func Test_readNote(t *testing.T) {
- note := []byte{
- 0x04, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00,
- 0x47, 0x4e, 0x55, 0x00,
- 0xca, 0xaf, 0x44, 0xd2, 0x82, 0x78, 0x68, 0xfe, 0xc0, 0x90, 0xa3, 0x43, 0x85, 0x36, 0x6c, 0xc7,
- }
-
- descs, err := readNote(bytes.NewBuffer(note), binary.LittleEndian)
- if err != nil {
- t.Fatalf("unexpected error in readNote: %s", err)
- }
-
- expectedDescs := map[string][]byte{
- "GNU\x00": []byte{0xca, 0xaf, 0x44, 0xd2, 0x82, 0x78, 0x68, 0xfe, 0xc0, 0x90, 0xa3, 0x43, 0x85, 0x36, 0x6c, 0xc7},
- }
-
- if !reflect.DeepEqual(descs, expectedDescs) {
- t.Errorf("incorrect return, want %#v got %#v", expectedDescs, descs)
- }
-}
-
-// emptyElfFile returns an elf file header with no program headers or sections.
-func emptyElfFile() string {
- ident := [elf.EI_NIDENT]byte{}
- identBuf := bytes.NewBuffer(ident[0:0:elf.EI_NIDENT])
- binary.Write(identBuf, binary.LittleEndian, []byte("\x7fELF"))
- binary.Write(identBuf, binary.LittleEndian, elf.ELFCLASS64)
- binary.Write(identBuf, binary.LittleEndian, elf.ELFDATA2LSB)
- binary.Write(identBuf, binary.LittleEndian, elf.EV_CURRENT)
- binary.Write(identBuf, binary.LittleEndian, elf.ELFOSABI_LINUX)
- binary.Write(identBuf, binary.LittleEndian, make([]byte, 8))
-
- header := elf.Header64{
- Ident: ident,
- Type: uint16(elf.ET_EXEC),
- Machine: uint16(elf.EM_X86_64),
- Version: uint32(elf.EV_CURRENT),
- Entry: 0,
- Phoff: uint64(binary.Size(elf.Header64{})),
- Shoff: uint64(binary.Size(elf.Header64{})),
- Flags: 0,
- Ehsize: uint16(binary.Size(elf.Header64{})),
- Phentsize: 0x38,
- Phnum: 0,
- Shentsize: 0x40,
- Shnum: 0,
- Shstrndx: 0,
- }
-
- buf := &bytes.Buffer{}
- binary.Write(buf, binary.LittleEndian, header)
- return buf.String()
-}
-
-// shortSectionHeader returns an elf file header with a section header that extends past the end of
-// the file.
-func shortSectionHeaderElfFile() string {
- ident := [elf.EI_NIDENT]byte{}
- identBuf := bytes.NewBuffer(ident[0:0:elf.EI_NIDENT])
- binary.Write(identBuf, binary.LittleEndian, []byte("\x7fELF"))
- binary.Write(identBuf, binary.LittleEndian, elf.ELFCLASS64)
- binary.Write(identBuf, binary.LittleEndian, elf.ELFDATA2LSB)
- binary.Write(identBuf, binary.LittleEndian, elf.EV_CURRENT)
- binary.Write(identBuf, binary.LittleEndian, elf.ELFOSABI_LINUX)
- binary.Write(identBuf, binary.LittleEndian, make([]byte, 8))
-
- header := elf.Header64{
- Ident: ident,
- Type: uint16(elf.ET_EXEC),
- Machine: uint16(elf.EM_X86_64),
- Version: uint32(elf.EV_CURRENT),
- Entry: 0,
- Phoff: uint64(binary.Size(elf.Header64{})),
- Shoff: uint64(binary.Size(elf.Header64{})),
- Flags: 0,
- Ehsize: uint16(binary.Size(elf.Header64{})),
- Phentsize: 0x38,
- Phnum: 0,
- Shentsize: 0x40,
- Shnum: 1,
- Shstrndx: 0,
- }
-
- buf := &bytes.Buffer{}
- binary.Write(buf, binary.LittleEndian, header)
- binary.Write(buf, binary.LittleEndian, []byte{0})
- return buf.String()
-}
diff --git a/cmd/symbols_map/symbols_map.go b/cmd/symbols_map/symbols_map.go
index 938446d..c56cf93 100644
--- a/cmd/symbols_map/symbols_map.go
+++ b/cmd/symbols_map/symbols_map.go
@@ -22,6 +22,7 @@
"strings"
"android/soong/cmd/symbols_map/symbols_map_proto"
+ "android/soong/elf"
"android/soong/response"
"github.com/google/blueprint/pathtools"
@@ -116,7 +117,7 @@
if *elfFile != "" {
typ = symbols_map_proto.Mapping_ELF
location = *elfFile
- identifier, err = elfIdentifier(*elfFile, true)
+ identifier, err = elf.Identifier(*elfFile, true)
if err != nil {
fmt.Fprintf(os.Stderr, "error reading elf identifier: %s\n", err)
os.Exit(1)