blob: b38896a328355d665e8834c473519884d3ad480f [file] [log] [blame]
Colin Cross36f55aa2022-03-21 18:46:41 -07001// Copyright 2022 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18 "debug/elf"
19 "encoding/binary"
20 "encoding/hex"
21 "fmt"
22 "io"
23)
24
25const gnuBuildID = "GNU\x00"
26
27// elfIdentifier extracts the elf build ID from an elf file. If allowMissing is true it returns
28// an empty identifier if the file exists but the build ID note does not.
29func elfIdentifier(filename string, allowMissing bool) (string, error) {
30 f, err := elf.Open(filename)
31 if err != nil {
32 return "", fmt.Errorf("failed to open %s: %w", filename, err)
33 }
34 defer f.Close()
35
36 buildIDNote := f.Section(".note.gnu.build-id")
37 if buildIDNote == nil {
38 if allowMissing {
39 return "", nil
40 }
41 return "", fmt.Errorf("failed to find .note.gnu.build-id in %s", filename)
42 }
43
44 buildIDs, err := readNote(buildIDNote.Open(), f.ByteOrder)
45 if err != nil {
46 return "", fmt.Errorf("failed to read .note.gnu.build-id: %w", err)
47 }
48
49 for name, desc := range buildIDs {
50 if name == gnuBuildID {
51 return hex.EncodeToString(desc), nil
52 }
53 }
54
55 return "", nil
56}
57
58// readNote reads the contents of a note section, returning it as a map from name to descriptor.
59func readNote(note io.Reader, byteOrder binary.ByteOrder) (map[string][]byte, error) {
60 var noteHeader struct {
61 Namesz uint32
62 Descsz uint32
63 Type uint32
64 }
65
66 notes := make(map[string][]byte)
67 for {
68 err := binary.Read(note, byteOrder, &noteHeader)
69 if err != nil {
70 if err == io.EOF {
71 return notes, nil
72 }
73 return nil, fmt.Errorf("failed to read note header: %w", err)
74 }
75
76 nameBuf := make([]byte, align4(noteHeader.Namesz))
77 err = binary.Read(note, byteOrder, &nameBuf)
78 if err != nil {
79 return nil, fmt.Errorf("failed to read note name: %w", err)
80 }
81 name := string(nameBuf[:noteHeader.Namesz])
82
83 descBuf := make([]byte, align4(noteHeader.Descsz))
84 err = binary.Read(note, byteOrder, &descBuf)
85 if err != nil {
86 return nil, fmt.Errorf("failed to read note desc: %w", err)
87 }
88 notes[name] = descBuf[:noteHeader.Descsz]
89 }
90}
91
92// align4 rounds the input up to the next multiple of 4.
93func align4(i uint32) uint32 {
94 return (i + 3) &^ 3
95}