read_symbols.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Copyright (c) 2018, Google Inc.
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  10. // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  12. // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  13. // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. // read_symbols scans one or more .a files and, for each object contained in
  15. // the .a files, reads the list of symbols in that object file.
  16. package main
  17. import (
  18. "bytes"
  19. "debug/elf"
  20. "debug/macho"
  21. "debug/pe"
  22. "flag"
  23. "fmt"
  24. "os"
  25. "runtime"
  26. "sort"
  27. "strings"
  28. "boringssl.googlesource.com/boringssl/util/ar"
  29. )
  30. const (
  31. ObjFileFormatELF = "elf"
  32. ObjFileFormatMachO = "macho"
  33. ObjFileFormatPE = "pe"
  34. )
  35. var (
  36. outFlag = flag.String("out", "-", "File to write output symbols")
  37. objFileFormat = flag.String("obj-file-format", defaultObjFileFormat(runtime.GOOS), "Object file format to expect (options are elf, macho, pe)")
  38. )
  39. func defaultObjFileFormat(goos string) string {
  40. switch goos {
  41. case "linux":
  42. return ObjFileFormatELF
  43. case "darwin":
  44. return ObjFileFormatMachO
  45. case "windows":
  46. return ObjFileFormatPE
  47. default:
  48. // By returning a value here rather than panicking, the user can still
  49. // cross-compile from an unsupported platform to a supported platform by
  50. // overriding this default with a flag. If the user doesn't provide the
  51. // flag, we will panic during flag parsing.
  52. return "unsupported"
  53. }
  54. }
  55. func printAndExit(format string, args ...interface{}) {
  56. s := fmt.Sprintf(format, args...)
  57. fmt.Fprintln(os.Stderr, s)
  58. os.Exit(1)
  59. }
  60. func main() {
  61. flag.Parse()
  62. if flag.NArg() < 1 {
  63. printAndExit("Usage: %s [-out OUT] [-obj-file-format FORMAT] ARCHIVE_FILE [ARCHIVE_FILE [...]]", os.Args[0])
  64. }
  65. archiveFiles := flag.Args()
  66. out := os.Stdout
  67. if *outFlag != "-" {
  68. var err error
  69. out, err = os.Create(*outFlag)
  70. if err != nil {
  71. printAndExit("Error opening %q: %s", *outFlag, err)
  72. }
  73. defer out.Close()
  74. }
  75. var symbols []string
  76. // Only add first instance of any symbol; keep track of them in this map.
  77. added := make(map[string]struct{})
  78. for _, archive := range archiveFiles {
  79. f, err := os.Open(archive)
  80. if err != nil {
  81. printAndExit("Error opening %s: %s", archive, err)
  82. }
  83. objectFiles, err := ar.ParseAR(f)
  84. f.Close()
  85. if err != nil {
  86. printAndExit("Error parsing %s: %s", archive, err)
  87. }
  88. for name, contents := range objectFiles {
  89. syms, err := listSymbols(contents)
  90. if err != nil {
  91. printAndExit("Error listing symbols from %q in %q: %s", name, archive, err)
  92. }
  93. for _, s := range syms {
  94. if _, ok := added[s]; !ok {
  95. added[s] = struct{}{}
  96. symbols = append(symbols, s)
  97. }
  98. }
  99. }
  100. }
  101. sort.Strings(symbols)
  102. for _, s := range symbols {
  103. var skipSymbols = []string{
  104. // Inline functions, etc., from the compiler or language
  105. // runtime will naturally end up in the library, to be
  106. // deduplicated against other object files. Such symbols
  107. // should not be prefixed. It is a limitation of this
  108. // symbol-prefixing strategy that we cannot distinguish
  109. // our own inline symbols (which should be prefixed)
  110. // from the system's (which should not), so we blacklist
  111. // known system symbols.
  112. "__local_stdio_printf_options",
  113. "__local_stdio_scanf_options",
  114. "_vscprintf",
  115. "_vscprintf_l",
  116. "_vsscanf_l",
  117. "_xmm",
  118. "sscanf",
  119. "vsnprintf",
  120. // sdallocx is a weak symbol and intended to merge with
  121. // the real one, if present.
  122. "sdallocx",
  123. }
  124. var skip bool
  125. for _, sym := range skipSymbols {
  126. if sym == s {
  127. skip = true
  128. break
  129. }
  130. }
  131. if skip || isCXXSymbol(s) || strings.HasPrefix(s, "__real@") || strings.HasPrefix(s, "__x86.get_pc_thunk.") {
  132. continue
  133. }
  134. if _, err := fmt.Fprintln(out, s); err != nil {
  135. printAndExit("Error writing to %s: %s", *outFlag, err)
  136. }
  137. }
  138. }
  139. func isCXXSymbol(s string) bool {
  140. if *objFileFormat == ObjFileFormatPE {
  141. return strings.HasPrefix(s, "?")
  142. }
  143. return strings.HasPrefix(s, "_Z")
  144. }
  145. // listSymbols lists the exported symbols from an object file.
  146. func listSymbols(contents []byte) ([]string, error) {
  147. switch *objFileFormat {
  148. case ObjFileFormatELF:
  149. return listSymbolsELF(contents)
  150. case ObjFileFormatMachO:
  151. return listSymbolsMachO(contents)
  152. case ObjFileFormatPE:
  153. return listSymbolsPE(contents)
  154. default:
  155. return nil, fmt.Errorf("unsupported object file format %q", *objFileFormat)
  156. }
  157. }
  158. func listSymbolsELF(contents []byte) ([]string, error) {
  159. f, err := elf.NewFile(bytes.NewReader(contents))
  160. if err != nil {
  161. return nil, err
  162. }
  163. syms, err := f.Symbols()
  164. if err != nil {
  165. return nil, err
  166. }
  167. var names []string
  168. for _, sym := range syms {
  169. // Only include exported, defined symbols
  170. if elf.ST_BIND(sym.Info) != elf.STB_LOCAL && sym.Section != elf.SHN_UNDEF {
  171. names = append(names, sym.Name)
  172. }
  173. }
  174. return names, nil
  175. }
  176. func listSymbolsMachO(contents []byte) ([]string, error) {
  177. f, err := macho.NewFile(bytes.NewReader(contents))
  178. if err != nil {
  179. return nil, err
  180. }
  181. if f.Symtab == nil {
  182. return nil, nil
  183. }
  184. var names []string
  185. for _, sym := range f.Symtab.Syms {
  186. // Source: https://opensource.apple.com/source/xnu/xnu-3789.51.2/EXTERNAL_HEADERS/mach-o/nlist.h.auto.html
  187. const (
  188. N_PEXT uint8 = 0x10 // Private external symbol bit
  189. N_EXT uint8 = 0x01 // External symbol bit, set for external symbols
  190. N_TYPE uint8 = 0x0e // mask for the type bits
  191. N_UNDF uint8 = 0x0 // undefined, n_sect == NO_SECT
  192. N_ABS uint8 = 0x2 // absolute, n_sect == NO_SECT
  193. N_SECT uint8 = 0xe // defined in section number n_sect
  194. N_PBUD uint8 = 0xc // prebound undefined (defined in a dylib)
  195. N_INDR uint8 = 0xa // indirect
  196. )
  197. // Only include exported, defined symbols.
  198. if sym.Type&N_EXT != 0 && sym.Type&N_TYPE != N_UNDF {
  199. if len(sym.Name) == 0 || sym.Name[0] != '_' {
  200. return nil, fmt.Errorf("unexpected symbol without underscore prefix: %q", sym.Name)
  201. }
  202. names = append(names, sym.Name[1:])
  203. }
  204. }
  205. return names, nil
  206. }
  207. func listSymbolsPE(contents []byte) ([]string, error) {
  208. f, err := pe.NewFile(bytes.NewReader(contents))
  209. if err != nil {
  210. return nil, err
  211. }
  212. var ret []string
  213. for _, sym := range f.Symbols {
  214. const (
  215. // https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#section-number-values
  216. IMAGE_SYM_UNDEFINED = 0
  217. // https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#storage-class
  218. IMAGE_SYM_CLASS_EXTERNAL = 2
  219. )
  220. if sym.SectionNumber != IMAGE_SYM_UNDEFINED && sym.StorageClass == IMAGE_SYM_CLASS_EXTERNAL {
  221. name := sym.Name
  222. if f.Machine == pe.IMAGE_FILE_MACHINE_I386 {
  223. // On 32-bit Windows, C symbols are decorated by calling
  224. // convention.
  225. // https://msdn.microsoft.com/en-us/library/56h2zst2.aspx#FormatC
  226. if strings.HasPrefix(name, "_") || strings.HasPrefix(name, "@") {
  227. // __cdecl, __stdcall, or __fastcall. Remove the prefix and
  228. // suffix, if present.
  229. name = name[1:]
  230. if idx := strings.LastIndex(name, "@"); idx >= 0 {
  231. name = name[:idx]
  232. }
  233. } else if idx := strings.LastIndex(name, "@@"); idx >= 0 {
  234. // __vectorcall. Remove the suffix.
  235. name = name[:idx]
  236. }
  237. }
  238. ret = append(ret, name)
  239. }
  240. }
  241. return ret, nil
  242. }