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