xref: /aosp_15_r20/build/soong/cmd/symbols_map/symbols_map.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2022 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Workerpackage main
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Workerimport (
18*333d2b36SAndroid Build Coastguard Worker	"flag"
19*333d2b36SAndroid Build Coastguard Worker	"fmt"
20*333d2b36SAndroid Build Coastguard Worker	"io/ioutil"
21*333d2b36SAndroid Build Coastguard Worker	"os"
22*333d2b36SAndroid Build Coastguard Worker	"strings"
23*333d2b36SAndroid Build Coastguard Worker
24*333d2b36SAndroid Build Coastguard Worker	"android/soong/cmd/symbols_map/symbols_map_proto"
25*333d2b36SAndroid Build Coastguard Worker	"android/soong/elf"
26*333d2b36SAndroid Build Coastguard Worker	"android/soong/response"
27*333d2b36SAndroid Build Coastguard Worker
28*333d2b36SAndroid Build Coastguard Worker	"github.com/google/blueprint/pathtools"
29*333d2b36SAndroid Build Coastguard Worker	"google.golang.org/protobuf/encoding/prototext"
30*333d2b36SAndroid Build Coastguard Worker	"google.golang.org/protobuf/proto"
31*333d2b36SAndroid Build Coastguard Worker)
32*333d2b36SAndroid Build Coastguard Worker
33*333d2b36SAndroid Build Coastguard Worker// This tool is used to extract a hash from an elf file or an r8 dictionary and store it as a
34*333d2b36SAndroid Build Coastguard Worker// textproto, or to merge multiple textprotos together.
35*333d2b36SAndroid Build Coastguard Worker
36*333d2b36SAndroid Build Coastguard Workerfunc main() {
37*333d2b36SAndroid Build Coastguard Worker	var expandedArgs []string
38*333d2b36SAndroid Build Coastguard Worker	for _, arg := range os.Args[1:] {
39*333d2b36SAndroid Build Coastguard Worker		if strings.HasPrefix(arg, "@") {
40*333d2b36SAndroid Build Coastguard Worker			f, err := os.Open(strings.TrimPrefix(arg, "@"))
41*333d2b36SAndroid Build Coastguard Worker			if err != nil {
42*333d2b36SAndroid Build Coastguard Worker				fmt.Fprintln(os.Stderr, err.Error())
43*333d2b36SAndroid Build Coastguard Worker				os.Exit(1)
44*333d2b36SAndroid Build Coastguard Worker			}
45*333d2b36SAndroid Build Coastguard Worker
46*333d2b36SAndroid Build Coastguard Worker			respArgs, err := response.ReadRspFile(f)
47*333d2b36SAndroid Build Coastguard Worker			f.Close()
48*333d2b36SAndroid Build Coastguard Worker			if err != nil {
49*333d2b36SAndroid Build Coastguard Worker				fmt.Fprintln(os.Stderr, err.Error())
50*333d2b36SAndroid Build Coastguard Worker				os.Exit(1)
51*333d2b36SAndroid Build Coastguard Worker			}
52*333d2b36SAndroid Build Coastguard Worker			expandedArgs = append(expandedArgs, respArgs...)
53*333d2b36SAndroid Build Coastguard Worker		} else {
54*333d2b36SAndroid Build Coastguard Worker			expandedArgs = append(expandedArgs, arg)
55*333d2b36SAndroid Build Coastguard Worker		}
56*333d2b36SAndroid Build Coastguard Worker	}
57*333d2b36SAndroid Build Coastguard Worker
58*333d2b36SAndroid Build Coastguard Worker	flags := flag.NewFlagSet("flags", flag.ExitOnError)
59*333d2b36SAndroid Build Coastguard Worker
60*333d2b36SAndroid Build Coastguard Worker	// Hide the flag package to prevent accidental references to flag instead of flags.
61*333d2b36SAndroid Build Coastguard Worker	flag := struct{}{}
62*333d2b36SAndroid Build Coastguard Worker	_ = flag
63*333d2b36SAndroid Build Coastguard Worker
64*333d2b36SAndroid Build Coastguard Worker	flags.Usage = func() {
65*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(flags.Output(), "Usage of %s:\n", os.Args[0])
66*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(flags.Output(), "  %s -elf|-r8 <input file> [-write_if_changed] <output file>\n", os.Args[0])
67*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(flags.Output(), "  %s -merge <output file> [-write_if_changed] [-ignore_missing_files] [-strip_prefix <prefix>] [<input file>...]\n", os.Args[0])
68*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintln(flags.Output())
69*333d2b36SAndroid Build Coastguard Worker
70*333d2b36SAndroid Build Coastguard Worker		flags.PrintDefaults()
71*333d2b36SAndroid Build Coastguard Worker	}
72*333d2b36SAndroid Build Coastguard Worker
73*333d2b36SAndroid Build Coastguard Worker	elfFile := flags.String("elf", "", "extract identifier from an elf file")
74*333d2b36SAndroid Build Coastguard Worker	r8File := flags.String("r8", "", "extract identifier from an r8 dictionary")
75*333d2b36SAndroid Build Coastguard Worker	merge := flags.String("merge", "", "merge multiple identifier protos")
76*333d2b36SAndroid Build Coastguard Worker
77*333d2b36SAndroid Build Coastguard Worker	writeIfChanged := flags.Bool("write_if_changed", false, "only write output file if it is modified")
78*333d2b36SAndroid Build Coastguard Worker	ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "ignore missing input files in merge mode")
79*333d2b36SAndroid Build Coastguard Worker	stripPrefix := flags.String("strip_prefix", "", "prefix to strip off of the location field in merge mode")
80*333d2b36SAndroid Build Coastguard Worker
81*333d2b36SAndroid Build Coastguard Worker	flags.Parse(expandedArgs)
82*333d2b36SAndroid Build Coastguard Worker
83*333d2b36SAndroid Build Coastguard Worker	if *merge != "" {
84*333d2b36SAndroid Build Coastguard Worker		// If merge mode was requested perform the merge and exit early.
85*333d2b36SAndroid Build Coastguard Worker		err := mergeProtos(*merge, flags.Args(), *stripPrefix, *writeIfChanged, *ignoreMissingFiles)
86*333d2b36SAndroid Build Coastguard Worker		if err != nil {
87*333d2b36SAndroid Build Coastguard Worker			fmt.Fprintf(os.Stderr, "failed to merge protos: %s", err)
88*333d2b36SAndroid Build Coastguard Worker			os.Exit(1)
89*333d2b36SAndroid Build Coastguard Worker		}
90*333d2b36SAndroid Build Coastguard Worker		os.Exit(0)
91*333d2b36SAndroid Build Coastguard Worker	}
92*333d2b36SAndroid Build Coastguard Worker
93*333d2b36SAndroid Build Coastguard Worker	if *elfFile == "" && *r8File == "" {
94*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "-elf or -r8 argument is required\n")
95*333d2b36SAndroid Build Coastguard Worker		flags.Usage()
96*333d2b36SAndroid Build Coastguard Worker		os.Exit(1)
97*333d2b36SAndroid Build Coastguard Worker	}
98*333d2b36SAndroid Build Coastguard Worker
99*333d2b36SAndroid Build Coastguard Worker	if *elfFile != "" && *r8File != "" {
100*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "only one of -elf or -r8 argument is allowed\n")
101*333d2b36SAndroid Build Coastguard Worker		flags.Usage()
102*333d2b36SAndroid Build Coastguard Worker		os.Exit(1)
103*333d2b36SAndroid Build Coastguard Worker	}
104*333d2b36SAndroid Build Coastguard Worker
105*333d2b36SAndroid Build Coastguard Worker	if flags.NArg() != 1 {
106*333d2b36SAndroid Build Coastguard Worker		flags.Usage()
107*333d2b36SAndroid Build Coastguard Worker		os.Exit(1)
108*333d2b36SAndroid Build Coastguard Worker	}
109*333d2b36SAndroid Build Coastguard Worker
110*333d2b36SAndroid Build Coastguard Worker	output := flags.Arg(0)
111*333d2b36SAndroid Build Coastguard Worker
112*333d2b36SAndroid Build Coastguard Worker	var identifier string
113*333d2b36SAndroid Build Coastguard Worker	var location string
114*333d2b36SAndroid Build Coastguard Worker	var typ symbols_map_proto.Mapping_Type
115*333d2b36SAndroid Build Coastguard Worker	var err error
116*333d2b36SAndroid Build Coastguard Worker
117*333d2b36SAndroid Build Coastguard Worker	if *elfFile != "" {
118*333d2b36SAndroid Build Coastguard Worker		typ = symbols_map_proto.Mapping_ELF
119*333d2b36SAndroid Build Coastguard Worker		location = *elfFile
120*333d2b36SAndroid Build Coastguard Worker		identifier, err = elf.Identifier(*elfFile, true)
121*333d2b36SAndroid Build Coastguard Worker		if err != nil {
122*333d2b36SAndroid Build Coastguard Worker			fmt.Fprintf(os.Stderr, "error reading elf identifier: %s\n", err)
123*333d2b36SAndroid Build Coastguard Worker			os.Exit(1)
124*333d2b36SAndroid Build Coastguard Worker		}
125*333d2b36SAndroid Build Coastguard Worker	} else if *r8File != "" {
126*333d2b36SAndroid Build Coastguard Worker		typ = symbols_map_proto.Mapping_R8
127*333d2b36SAndroid Build Coastguard Worker		identifier, err = r8Identifier(*r8File)
128*333d2b36SAndroid Build Coastguard Worker		location = *r8File
129*333d2b36SAndroid Build Coastguard Worker		if err != nil {
130*333d2b36SAndroid Build Coastguard Worker			fmt.Fprintf(os.Stderr, "error reading r8 identifier: %s\n", err)
131*333d2b36SAndroid Build Coastguard Worker			os.Exit(1)
132*333d2b36SAndroid Build Coastguard Worker		}
133*333d2b36SAndroid Build Coastguard Worker	} else {
134*333d2b36SAndroid Build Coastguard Worker		panic("shouldn't get here")
135*333d2b36SAndroid Build Coastguard Worker	}
136*333d2b36SAndroid Build Coastguard Worker
137*333d2b36SAndroid Build Coastguard Worker	mapping := symbols_map_proto.Mapping{
138*333d2b36SAndroid Build Coastguard Worker		Identifier: proto.String(identifier),
139*333d2b36SAndroid Build Coastguard Worker		Location:   proto.String(location),
140*333d2b36SAndroid Build Coastguard Worker		Type:       typ.Enum(),
141*333d2b36SAndroid Build Coastguard Worker	}
142*333d2b36SAndroid Build Coastguard Worker
143*333d2b36SAndroid Build Coastguard Worker	err = writeTextProto(output, &mapping, *writeIfChanged)
144*333d2b36SAndroid Build Coastguard Worker	if err != nil {
145*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "error writing output: %s\n", err)
146*333d2b36SAndroid Build Coastguard Worker		os.Exit(1)
147*333d2b36SAndroid Build Coastguard Worker	}
148*333d2b36SAndroid Build Coastguard Worker}
149*333d2b36SAndroid Build Coastguard Worker
150*333d2b36SAndroid Build Coastguard Worker// writeTextProto writes a proto to an output file as a textproto, optionally leaving the file
151*333d2b36SAndroid Build Coastguard Worker// unmodified if it was already up to date.
152*333d2b36SAndroid Build Coastguard Workerfunc writeTextProto(output string, message proto.Message, writeIfChanged bool) error {
153*333d2b36SAndroid Build Coastguard Worker	marshaller := prototext.MarshalOptions{Multiline: true}
154*333d2b36SAndroid Build Coastguard Worker	data, err := marshaller.Marshal(message)
155*333d2b36SAndroid Build Coastguard Worker	if err != nil {
156*333d2b36SAndroid Build Coastguard Worker		return fmt.Errorf("error marshalling textproto: %w", err)
157*333d2b36SAndroid Build Coastguard Worker	}
158*333d2b36SAndroid Build Coastguard Worker
159*333d2b36SAndroid Build Coastguard Worker	if writeIfChanged {
160*333d2b36SAndroid Build Coastguard Worker		err = pathtools.WriteFileIfChanged(output, data, 0666)
161*333d2b36SAndroid Build Coastguard Worker	} else {
162*333d2b36SAndroid Build Coastguard Worker		err = ioutil.WriteFile(output, data, 0666)
163*333d2b36SAndroid Build Coastguard Worker	}
164*333d2b36SAndroid Build Coastguard Worker
165*333d2b36SAndroid Build Coastguard Worker	if err != nil {
166*333d2b36SAndroid Build Coastguard Worker		return fmt.Errorf("error writing to %s: %w\n", output, err)
167*333d2b36SAndroid Build Coastguard Worker	}
168*333d2b36SAndroid Build Coastguard Worker
169*333d2b36SAndroid Build Coastguard Worker	return nil
170*333d2b36SAndroid Build Coastguard Worker}
171*333d2b36SAndroid Build Coastguard Worker
172*333d2b36SAndroid Build Coastguard Worker// mergeProtos merges a list of textproto files containing Mapping messages into a single textproto
173*333d2b36SAndroid Build Coastguard Worker// containing a Mappings message.
174*333d2b36SAndroid Build Coastguard Workerfunc mergeProtos(output string, inputs []string, stripPrefix string, writeIfChanged bool, ignoreMissingFiles bool) error {
175*333d2b36SAndroid Build Coastguard Worker	mappings := symbols_map_proto.Mappings{}
176*333d2b36SAndroid Build Coastguard Worker	for _, input := range inputs {
177*333d2b36SAndroid Build Coastguard Worker		mapping := symbols_map_proto.Mapping{}
178*333d2b36SAndroid Build Coastguard Worker		data, err := ioutil.ReadFile(input)
179*333d2b36SAndroid Build Coastguard Worker		if err != nil {
180*333d2b36SAndroid Build Coastguard Worker			if ignoreMissingFiles && os.IsNotExist(err) {
181*333d2b36SAndroid Build Coastguard Worker				// Merge mode is used on a list of files in the packaging directory.  If multiple
182*333d2b36SAndroid Build Coastguard Worker				// goals are included on the build command line, for example `dist` and `tests`,
183*333d2b36SAndroid Build Coastguard Worker				// then the symbols packaging rule for `dist` can run while a dependency of `tests`
184*333d2b36SAndroid Build Coastguard Worker				// is modifying the symbols packaging directory.  That can result in a file that
185*333d2b36SAndroid Build Coastguard Worker				// existed when the file list was generated being deleted as part of updating it,
186*333d2b36SAndroid Build Coastguard Worker				// resulting in sporadic ENOENT errors.  Ignore them if -ignore_missing_files
187*333d2b36SAndroid Build Coastguard Worker				// was passed on the command line.
188*333d2b36SAndroid Build Coastguard Worker				continue
189*333d2b36SAndroid Build Coastguard Worker			}
190*333d2b36SAndroid Build Coastguard Worker			return fmt.Errorf("failed to read %s: %w", input, err)
191*333d2b36SAndroid Build Coastguard Worker		}
192*333d2b36SAndroid Build Coastguard Worker		err = prototext.Unmarshal(data, &mapping)
193*333d2b36SAndroid Build Coastguard Worker		if err != nil {
194*333d2b36SAndroid Build Coastguard Worker			return fmt.Errorf("failed to parse textproto %s: %w", input, err)
195*333d2b36SAndroid Build Coastguard Worker		}
196*333d2b36SAndroid Build Coastguard Worker		if stripPrefix != "" && mapping.Location != nil {
197*333d2b36SAndroid Build Coastguard Worker			mapping.Location = proto.String(strings.TrimPrefix(*mapping.Location, stripPrefix))
198*333d2b36SAndroid Build Coastguard Worker		}
199*333d2b36SAndroid Build Coastguard Worker		mappings.Mappings = append(mappings.Mappings, &mapping)
200*333d2b36SAndroid Build Coastguard Worker	}
201*333d2b36SAndroid Build Coastguard Worker
202*333d2b36SAndroid Build Coastguard Worker	return writeTextProto(output, &mappings, writeIfChanged)
203*333d2b36SAndroid Build Coastguard Worker}
204