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