symbols_map.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // 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. package main
  15. import (
  16. "flag"
  17. "fmt"
  18. "io/ioutil"
  19. "os"
  20. "strings"
  21. "android/soong/cmd/symbols_map/symbols_map_proto"
  22. "android/soong/response"
  23. "github.com/google/blueprint/pathtools"
  24. "google.golang.org/protobuf/encoding/prototext"
  25. "google.golang.org/protobuf/proto"
  26. )
  27. // This tool is used to extract a hash from an elf file or an r8 dictionary and store it as a
  28. // textproto, or to merge multiple textprotos together.
  29. func main() {
  30. var expandedArgs []string
  31. for _, arg := range os.Args[1:] {
  32. if strings.HasPrefix(arg, "@") {
  33. f, err := os.Open(strings.TrimPrefix(arg, "@"))
  34. if err != nil {
  35. fmt.Fprintln(os.Stderr, err.Error())
  36. os.Exit(1)
  37. }
  38. respArgs, err := response.ReadRspFile(f)
  39. f.Close()
  40. if err != nil {
  41. fmt.Fprintln(os.Stderr, err.Error())
  42. os.Exit(1)
  43. }
  44. expandedArgs = append(expandedArgs, respArgs...)
  45. } else {
  46. expandedArgs = append(expandedArgs, arg)
  47. }
  48. }
  49. flags := flag.NewFlagSet("flags", flag.ExitOnError)
  50. // Hide the flag package to prevent accidental references to flag instead of flags.
  51. flag := struct{}{}
  52. _ = flag
  53. flags.Usage = func() {
  54. fmt.Fprintf(flags.Output(), "Usage of %s:\n", os.Args[0])
  55. fmt.Fprintf(flags.Output(), " %s -elf|-r8 <input file> [-write_if_changed] <output file>\n", os.Args[0])
  56. fmt.Fprintf(flags.Output(), " %s -merge <output file> [-write_if_changed] [-ignore_missing_files] [-strip_prefix <prefix>] [<input file>...]\n", os.Args[0])
  57. fmt.Fprintln(flags.Output())
  58. flags.PrintDefaults()
  59. }
  60. elfFile := flags.String("elf", "", "extract identifier from an elf file")
  61. r8File := flags.String("r8", "", "extract identifier from an r8 dictionary")
  62. merge := flags.String("merge", "", "merge multiple identifier protos")
  63. writeIfChanged := flags.Bool("write_if_changed", false, "only write output file if it is modified")
  64. ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "ignore missing input files in merge mode")
  65. stripPrefix := flags.String("strip_prefix", "", "prefix to strip off of the location field in merge mode")
  66. flags.Parse(expandedArgs)
  67. if *merge != "" {
  68. // If merge mode was requested perform the merge and exit early.
  69. err := mergeProtos(*merge, flags.Args(), *stripPrefix, *writeIfChanged, *ignoreMissingFiles)
  70. if err != nil {
  71. fmt.Fprintf(os.Stderr, "failed to merge protos: %s", err)
  72. os.Exit(1)
  73. }
  74. os.Exit(0)
  75. }
  76. if *elfFile == "" && *r8File == "" {
  77. fmt.Fprintf(os.Stderr, "-elf or -r8 argument is required\n")
  78. flags.Usage()
  79. os.Exit(1)
  80. }
  81. if *elfFile != "" && *r8File != "" {
  82. fmt.Fprintf(os.Stderr, "only one of -elf or -r8 argument is allowed\n")
  83. flags.Usage()
  84. os.Exit(1)
  85. }
  86. if flags.NArg() != 1 {
  87. flags.Usage()
  88. os.Exit(1)
  89. }
  90. output := flags.Arg(0)
  91. var identifier string
  92. var location string
  93. var typ symbols_map_proto.Mapping_Type
  94. var err error
  95. if *elfFile != "" {
  96. typ = symbols_map_proto.Mapping_ELF
  97. location = *elfFile
  98. identifier, err = elfIdentifier(*elfFile, true)
  99. if err != nil {
  100. fmt.Fprintf(os.Stderr, "error reading elf identifier: %s\n", err)
  101. os.Exit(1)
  102. }
  103. } else if *r8File != "" {
  104. typ = symbols_map_proto.Mapping_R8
  105. identifier, err = r8Identifier(*r8File)
  106. location = *r8File
  107. if err != nil {
  108. fmt.Fprintf(os.Stderr, "error reading r8 identifier: %s\n", err)
  109. os.Exit(1)
  110. }
  111. } else {
  112. panic("shouldn't get here")
  113. }
  114. mapping := symbols_map_proto.Mapping{
  115. Identifier: proto.String(identifier),
  116. Location: proto.String(location),
  117. Type: typ.Enum(),
  118. }
  119. err = writeTextProto(output, &mapping, *writeIfChanged)
  120. if err != nil {
  121. fmt.Fprintf(os.Stderr, "error writing output: %s\n", err)
  122. os.Exit(1)
  123. }
  124. }
  125. // writeTextProto writes a proto to an output file as a textproto, optionally leaving the file
  126. // unmodified if it was already up to date.
  127. func writeTextProto(output string, message proto.Message, writeIfChanged bool) error {
  128. marshaller := prototext.MarshalOptions{Multiline: true}
  129. data, err := marshaller.Marshal(message)
  130. if err != nil {
  131. return fmt.Errorf("error marshalling textproto: %w", err)
  132. }
  133. if writeIfChanged {
  134. err = pathtools.WriteFileIfChanged(output, data, 0666)
  135. } else {
  136. err = ioutil.WriteFile(output, data, 0666)
  137. }
  138. if err != nil {
  139. return fmt.Errorf("error writing to %s: %w\n", output, err)
  140. }
  141. return nil
  142. }
  143. // mergeProtos merges a list of textproto files containing Mapping messages into a single textproto
  144. // containing a Mappings message.
  145. func mergeProtos(output string, inputs []string, stripPrefix string, writeIfChanged bool, ignoreMissingFiles bool) error {
  146. mappings := symbols_map_proto.Mappings{}
  147. for _, input := range inputs {
  148. mapping := symbols_map_proto.Mapping{}
  149. data, err := ioutil.ReadFile(input)
  150. if err != nil {
  151. if ignoreMissingFiles && os.IsNotExist(err) {
  152. // Merge mode is used on a list of files in the packaging directory. If multiple
  153. // goals are included on the build command line, for example `dist` and `tests`,
  154. // then the symbols packaging rule for `dist` can run while a dependency of `tests`
  155. // is modifying the symbols packaging directory. That can result in a file that
  156. // existed when the file list was generated being deleted as part of updating it,
  157. // resulting in sporadic ENOENT errors. Ignore them if -ignore_missing_files
  158. // was passed on the command line.
  159. continue
  160. }
  161. return fmt.Errorf("failed to read %s: %w", input, err)
  162. }
  163. err = prototext.Unmarshal(data, &mapping)
  164. if err != nil {
  165. return fmt.Errorf("failed to parse textproto %s: %w", input, err)
  166. }
  167. if stripPrefix != "" && mapping.Location != nil {
  168. mapping.Location = proto.String(strings.TrimPrefix(*mapping.Location, stripPrefix))
  169. }
  170. mappings.Mappings = append(mappings.Mappings, &mapping)
  171. }
  172. return writeTextProto(output, &mappings, writeIfChanged)
  173. }