blob: 791ea5d1263f8ec70de23fd71aaf0e57aff1590d [file] [log] [blame]
Robert Sloanf068def2018-10-10 18:45:40 -07001// 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
Robert Sloan6e8c9592018-12-03 11:20:49 -080015// read_symbols scans one or more .a files and, for each object contained in
Robert Sloanf068def2018-10-10 18:45:40 -070016// the .a files, reads the list of symbols in that object file.
17package main
18
19import (
20 "bytes"
21 "debug/elf"
22 "debug/macho"
Robert Sloan6e8c9592018-12-03 11:20:49 -080023 "debug/pe"
Robert Sloanf068def2018-10-10 18:45:40 -070024 "flag"
25 "fmt"
26 "os"
27 "runtime"
28 "sort"
29 "strings"
30
31 "boringssl.googlesource.com/boringssl/util/ar"
32)
33
34const (
35 ObjFileFormatELF = "elf"
36 ObjFileFormatMachO = "macho"
Robert Sloan6e8c9592018-12-03 11:20:49 -080037 ObjFileFormatPE = "pe"
Robert Sloanf068def2018-10-10 18:45:40 -070038)
39
Robert Sloan6e8c9592018-12-03 11:20:49 -080040var (
41 outFlag = flag.String("out", "-", "File to write output symbols")
42 objFileFormat = flag.String("obj-file-format", defaultObjFileFormat(runtime.GOOS), "Object file format to expect (options are elf, macho, pe)")
43)
Robert Sloanf068def2018-10-10 18:45:40 -070044
45func defaultObjFileFormat(goos string) string {
46 switch goos {
47 case "linux":
48 return ObjFileFormatELF
49 case "darwin":
50 return ObjFileFormatMachO
Robert Sloan6e8c9592018-12-03 11:20:49 -080051 case "windows":
52 return ObjFileFormatPE
Robert Sloanf068def2018-10-10 18:45:40 -070053 default:
54 // By returning a value here rather than panicking, the user can still
55 // cross-compile from an unsupported platform to a supported platform by
56 // overriding this default with a flag. If the user doesn't provide the
57 // flag, we will panic during flag parsing.
58 return "unsupported"
59 }
60}
61
Robert Sloan6e8c9592018-12-03 11:20:49 -080062func printAndExit(format string, args ...interface{}) {
63 s := fmt.Sprintf(format, args...)
64 fmt.Fprintln(os.Stderr, s)
65 os.Exit(1)
66}
67
Robert Sloanf068def2018-10-10 18:45:40 -070068func main() {
69 flag.Parse()
70 if flag.NArg() < 1 {
Robert Sloan6e8c9592018-12-03 11:20:49 -080071 printAndExit("Usage: %s [-out OUT] [-obj-file-format FORMAT] ARCHIVE_FILE [ARCHIVE_FILE [...]]", os.Args[0])
Robert Sloanf068def2018-10-10 18:45:40 -070072 }
73 archiveFiles := flag.Args()
74
75 out := os.Stdout
76 if *outFlag != "-" {
77 var err error
78 out, err = os.Create(*outFlag)
Robert Sloan6e8c9592018-12-03 11:20:49 -080079 if err != nil {
80 printAndExit("Error opening %q: %s", *outFlag, err)
81 }
Robert Sloanf068def2018-10-10 18:45:40 -070082 defer out.Close()
83 }
84
85 var symbols []string
86 // Only add first instance of any symbol; keep track of them in this map.
Robert Sloan6e8c9592018-12-03 11:20:49 -080087 added := make(map[string]struct{})
Robert Sloanf068def2018-10-10 18:45:40 -070088 for _, archive := range archiveFiles {
89 f, err := os.Open(archive)
Robert Sloan6e8c9592018-12-03 11:20:49 -080090 if err != nil {
91 printAndExit("Error opening %s: %s", archive, err)
92 }
Robert Sloanf068def2018-10-10 18:45:40 -070093 objectFiles, err := ar.ParseAR(f)
Robert Sloan6e8c9592018-12-03 11:20:49 -080094 f.Close()
95 if err != nil {
96 printAndExit("Error parsing %s: %s", archive, err)
97 }
Robert Sloanf068def2018-10-10 18:45:40 -070098
99 for name, contents := range objectFiles {
Robert Sloan6e8c9592018-12-03 11:20:49 -0800100 syms, err := listSymbols(contents)
101 if err != nil {
102 printAndExit("Error listing symbols from %q in %q: %s", name, archive, err)
Robert Sloanf068def2018-10-10 18:45:40 -0700103 }
Robert Sloan6e8c9592018-12-03 11:20:49 -0800104 for _, s := range syms {
105 if _, ok := added[s]; !ok {
106 added[s] = struct{}{}
Robert Sloanf068def2018-10-10 18:45:40 -0700107 symbols = append(symbols, s)
108 }
109 }
110 }
111 }
Robert Sloan6e8c9592018-12-03 11:20:49 -0800112
Robert Sloanf068def2018-10-10 18:45:40 -0700113 sort.Strings(symbols)
114 for _, s := range symbols {
Robert Sloan6e8c9592018-12-03 11:20:49 -0800115 var skipSymbols = []string{
116 // Inline functions, etc., from the compiler or language
117 // runtime will naturally end up in the library, to be
118 // deduplicated against other object files. Such symbols
119 // should not be prefixed. It is a limitation of this
120 // symbol-prefixing strategy that we cannot distinguish
121 // our own inline symbols (which should be prefixed)
122 // from the system's (which should not), so we blacklist
123 // known system symbols.
124 "__local_stdio_printf_options",
125 "__local_stdio_scanf_options",
126 "_vscprintf",
127 "_vscprintf_l",
128 "_vsscanf_l",
129 "_xmm",
130 "sscanf",
131 "vsnprintf",
132 // sdallocx is a weak symbol and intended to merge with
133 // the real one, if present.
134 "sdallocx",
Robert Sloanf068def2018-10-10 18:45:40 -0700135 }
Robert Sloan6e8c9592018-12-03 11:20:49 -0800136 var skip bool
137 for _, sym := range skipSymbols {
138 if sym == s {
139 skip = true
140 break
141 }
142 }
143 if skip || isCXXSymbol(s) || strings.HasPrefix(s, "__real@") || strings.HasPrefix(s, "__x86.get_pc_thunk.") {
144 continue
145 }
146 if _, err := fmt.Fprintln(out, s); err != nil {
147 printAndExit("Error writing to %s: %s", *outFlag, err)
Robert Sloanf068def2018-10-10 18:45:40 -0700148 }
149 }
150}
151
Robert Sloan6e8c9592018-12-03 11:20:49 -0800152func isCXXSymbol(s string) bool {
153 if *objFileFormat == ObjFileFormatPE {
154 return strings.HasPrefix(s, "?")
155 }
156 return strings.HasPrefix(s, "_Z")
157}
158
Robert Sloanf068def2018-10-10 18:45:40 -0700159// listSymbols lists the exported symbols from an object file.
Robert Sloan6e8c9592018-12-03 11:20:49 -0800160func listSymbols(contents []byte) ([]string, error) {
Robert Sloanf068def2018-10-10 18:45:40 -0700161 switch *objFileFormat {
162 case ObjFileFormatELF:
Robert Sloan6e8c9592018-12-03 11:20:49 -0800163 return listSymbolsELF(contents)
Robert Sloanf068def2018-10-10 18:45:40 -0700164 case ObjFileFormatMachO:
Robert Sloan6e8c9592018-12-03 11:20:49 -0800165 return listSymbolsMachO(contents)
166 case ObjFileFormatPE:
167 return listSymbolsPE(contents)
Robert Sloanf068def2018-10-10 18:45:40 -0700168 default:
Robert Sloan6e8c9592018-12-03 11:20:49 -0800169 return nil, fmt.Errorf("unsupported object file format %q", *objFileFormat)
Robert Sloanf068def2018-10-10 18:45:40 -0700170 }
171}
172
Robert Sloan6e8c9592018-12-03 11:20:49 -0800173func listSymbolsELF(contents []byte) ([]string, error) {
Robert Sloanf068def2018-10-10 18:45:40 -0700174 f, err := elf.NewFile(bytes.NewReader(contents))
Robert Sloan6e8c9592018-12-03 11:20:49 -0800175 if err != nil {
176 return nil, err
177 }
Robert Sloanf068def2018-10-10 18:45:40 -0700178 syms, err := f.Symbols()
Robert Sloan6e8c9592018-12-03 11:20:49 -0800179 if err != nil {
180 return nil, err
181 }
Robert Sloanf068def2018-10-10 18:45:40 -0700182
183 var names []string
184 for _, sym := range syms {
185 // Only include exported, defined symbols
186 if elf.ST_BIND(sym.Info) != elf.STB_LOCAL && sym.Section != elf.SHN_UNDEF {
187 names = append(names, sym.Name)
188 }
189 }
Robert Sloan6e8c9592018-12-03 11:20:49 -0800190 return names, nil
Robert Sloanf068def2018-10-10 18:45:40 -0700191}
192
Robert Sloan6e8c9592018-12-03 11:20:49 -0800193func listSymbolsMachO(contents []byte) ([]string, error) {
Robert Sloanf068def2018-10-10 18:45:40 -0700194 f, err := macho.NewFile(bytes.NewReader(contents))
Robert Sloan6e8c9592018-12-03 11:20:49 -0800195 if err != nil {
196 return nil, err
197 }
Robert Sloanf068def2018-10-10 18:45:40 -0700198 if f.Symtab == nil {
Robert Sloan6e8c9592018-12-03 11:20:49 -0800199 return nil, nil
Robert Sloanf068def2018-10-10 18:45:40 -0700200 }
201 var names []string
202 for _, sym := range f.Symtab.Syms {
203 // Source: https://opensource.apple.com/source/xnu/xnu-3789.51.2/EXTERNAL_HEADERS/mach-o/nlist.h.auto.html
204 const (
205 N_PEXT uint8 = 0x10 // Private external symbol bit
206 N_EXT uint8 = 0x01 // External symbol bit, set for external symbols
207 N_TYPE uint8 = 0x0e // mask for the type bits
208
209 N_UNDF uint8 = 0x0 // undefined, n_sect == NO_SECT
210 N_ABS uint8 = 0x2 // absolute, n_sect == NO_SECT
211 N_SECT uint8 = 0xe // defined in section number n_sect
212 N_PBUD uint8 = 0xc // prebound undefined (defined in a dylib)
213 N_INDR uint8 = 0xa // indirect
214 )
215
216 // Only include exported, defined symbols.
217 if sym.Type&N_EXT != 0 && sym.Type&N_TYPE != N_UNDF {
218 if len(sym.Name) == 0 || sym.Name[0] != '_' {
Robert Sloan6e8c9592018-12-03 11:20:49 -0800219 return nil, fmt.Errorf("unexpected symbol without underscore prefix: %q", sym.Name)
Robert Sloanf068def2018-10-10 18:45:40 -0700220 }
221 names = append(names, sym.Name[1:])
222 }
223 }
Robert Sloan6e8c9592018-12-03 11:20:49 -0800224 return names, nil
Robert Sloanf068def2018-10-10 18:45:40 -0700225}
226
Robert Sloan6e8c9592018-12-03 11:20:49 -0800227func listSymbolsPE(contents []byte) ([]string, error) {
228 f, err := pe.NewFile(bytes.NewReader(contents))
Robert Sloanf068def2018-10-10 18:45:40 -0700229 if err != nil {
Robert Sloan6e8c9592018-12-03 11:20:49 -0800230 return nil, err
Robert Sloanf068def2018-10-10 18:45:40 -0700231 }
Robert Sloan6e8c9592018-12-03 11:20:49 -0800232 var ret []string
233 for _, sym := range f.Symbols {
234 const (
235 // https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#section-number-values
236 IMAGE_SYM_UNDEFINED = 0
237 // https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#storage-class
238 IMAGE_SYM_CLASS_EXTERNAL = 2
239 )
240 if sym.SectionNumber != IMAGE_SYM_UNDEFINED && sym.StorageClass == IMAGE_SYM_CLASS_EXTERNAL {
241 name := sym.Name
242 if f.Machine == pe.IMAGE_FILE_MACHINE_I386 {
243 // On 32-bit Windows, C symbols are decorated by calling
244 // convention.
245 // https://msdn.microsoft.com/en-us/library/56h2zst2.aspx#FormatC
246 if strings.HasPrefix(name, "_") || strings.HasPrefix(name, "@") {
247 // __cdecl, __stdcall, or __fastcall. Remove the prefix and
248 // suffix, if present.
249 name = name[1:]
250 if idx := strings.LastIndex(name, "@"); idx >= 0 {
251 name = name[:idx]
252 }
253 } else if idx := strings.LastIndex(name, "@@"); idx >= 0 {
254 // __vectorcall. Remove the suffix.
255 name = name[:idx]
256 }
257 }
258 ret = append(ret, name)
259 }
260 }
261 return ret, nil
Robert Sloanf068def2018-10-10 18:45:40 -0700262}