Mirror of BoringSSL (grpc依赖)
https://boringssl.googlesource.com/boringssl
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
7.5 KiB
267 lines
7.5 KiB
// Copyright (c) 2018, Google Inc. |
|
// |
|
// Permission to use, copy, modify, and/or distribute this software for any |
|
// purpose with or without fee is hereby granted, provided that the above |
|
// copyright notice and this permission notice appear in all copies. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
|
|
//go:build ignore |
|
|
|
// read_symbols scans one or more .a files and, for each object contained in |
|
// the .a files, reads the list of symbols in that object file. |
|
package main |
|
|
|
import ( |
|
"bytes" |
|
"debug/elf" |
|
"debug/macho" |
|
"debug/pe" |
|
"flag" |
|
"fmt" |
|
"os" |
|
"runtime" |
|
"sort" |
|
"strings" |
|
|
|
"boringssl.googlesource.com/boringssl/util/ar" |
|
) |
|
|
|
const ( |
|
ObjFileFormatELF = "elf" |
|
ObjFileFormatMachO = "macho" |
|
ObjFileFormatPE = "pe" |
|
) |
|
|
|
var ( |
|
outFlag = flag.String("out", "-", "File to write output symbols") |
|
objFileFormat = flag.String("obj-file-format", defaultObjFileFormat(runtime.GOOS), "Object file format to expect (options are elf, macho, pe)") |
|
) |
|
|
|
func defaultObjFileFormat(goos string) string { |
|
switch goos { |
|
case "linux": |
|
return ObjFileFormatELF |
|
case "darwin": |
|
return ObjFileFormatMachO |
|
case "windows": |
|
return ObjFileFormatPE |
|
default: |
|
// By returning a value here rather than panicking, the user can still |
|
// cross-compile from an unsupported platform to a supported platform by |
|
// overriding this default with a flag. If the user doesn't provide the |
|
// flag, we will panic during flag parsing. |
|
return "unsupported" |
|
} |
|
} |
|
|
|
func printAndExit(format string, args ...any) { |
|
s := fmt.Sprintf(format, args...) |
|
fmt.Fprintln(os.Stderr, s) |
|
os.Exit(1) |
|
} |
|
|
|
func main() { |
|
flag.Parse() |
|
if flag.NArg() < 1 { |
|
printAndExit("Usage: %s [-out OUT] [-obj-file-format FORMAT] ARCHIVE_FILE [ARCHIVE_FILE [...]]", os.Args[0]) |
|
} |
|
archiveFiles := flag.Args() |
|
|
|
out := os.Stdout |
|
if *outFlag != "-" { |
|
var err error |
|
out, err = os.Create(*outFlag) |
|
if err != nil { |
|
printAndExit("Error opening %q: %s", *outFlag, err) |
|
} |
|
defer out.Close() |
|
} |
|
|
|
var symbols []string |
|
// Only add first instance of any symbol; keep track of them in this map. |
|
added := make(map[string]struct{}) |
|
for _, archive := range archiveFiles { |
|
f, err := os.Open(archive) |
|
if err != nil { |
|
printAndExit("Error opening %s: %s", archive, err) |
|
} |
|
objectFiles, err := ar.ParseAR(f) |
|
f.Close() |
|
if err != nil { |
|
printAndExit("Error parsing %s: %s", archive, err) |
|
} |
|
|
|
for name, contents := range objectFiles { |
|
syms, err := listSymbols(contents) |
|
if err != nil { |
|
printAndExit("Error listing symbols from %q in %q: %s", name, archive, err) |
|
} |
|
for _, s := range syms { |
|
if _, ok := added[s]; !ok { |
|
added[s] = struct{}{} |
|
symbols = append(symbols, s) |
|
} |
|
} |
|
} |
|
} |
|
|
|
sort.Strings(symbols) |
|
for _, s := range symbols { |
|
var skipSymbols = []string{ |
|
// Inline functions, etc., from the compiler or language |
|
// runtime will naturally end up in the library, to be |
|
// deduplicated against other object files. Such symbols |
|
// should not be prefixed. It is a limitation of this |
|
// symbol-prefixing strategy that we cannot distinguish |
|
// our own inline symbols (which should be prefixed) |
|
// from the system's (which should not), so we skip known |
|
// system symbols. |
|
"__local_stdio_printf_options", |
|
"__local_stdio_scanf_options", |
|
"_vscprintf", |
|
"_vscprintf_l", |
|
"_vsscanf_l", |
|
"_xmm", |
|
"sscanf", |
|
"vsnprintf", |
|
// sdallocx is a weak symbol and intended to merge with |
|
// the real one, if present. |
|
"sdallocx", |
|
} |
|
var skip bool |
|
for _, sym := range skipSymbols { |
|
if sym == s { |
|
skip = true |
|
break |
|
} |
|
} |
|
if skip || isCXXSymbol(s) || strings.HasPrefix(s, "__real@") || strings.HasPrefix(s, "__x86.get_pc_thunk.") { |
|
continue |
|
} |
|
if _, err := fmt.Fprintln(out, s); err != nil { |
|
printAndExit("Error writing to %s: %s", *outFlag, err) |
|
} |
|
} |
|
} |
|
|
|
func isCXXSymbol(s string) bool { |
|
if *objFileFormat == ObjFileFormatPE { |
|
return strings.HasPrefix(s, "?") |
|
} |
|
return strings.HasPrefix(s, "_Z") |
|
} |
|
|
|
// listSymbols lists the exported symbols from an object file. |
|
func listSymbols(contents []byte) ([]string, error) { |
|
switch *objFileFormat { |
|
case ObjFileFormatELF: |
|
return listSymbolsELF(contents) |
|
case ObjFileFormatMachO: |
|
return listSymbolsMachO(contents) |
|
case ObjFileFormatPE: |
|
return listSymbolsPE(contents) |
|
default: |
|
return nil, fmt.Errorf("unsupported object file format %q", *objFileFormat) |
|
} |
|
} |
|
|
|
func listSymbolsELF(contents []byte) ([]string, error) { |
|
f, err := elf.NewFile(bytes.NewReader(contents)) |
|
if err != nil { |
|
return nil, err |
|
} |
|
syms, err := f.Symbols() |
|
if err == elf.ErrNoSymbols { |
|
return nil, nil |
|
} |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
var names []string |
|
for _, sym := range syms { |
|
// Only include exported, defined symbols |
|
if elf.ST_BIND(sym.Info) != elf.STB_LOCAL && sym.Section != elf.SHN_UNDEF { |
|
names = append(names, sym.Name) |
|
} |
|
} |
|
return names, nil |
|
} |
|
|
|
func listSymbolsMachO(contents []byte) ([]string, error) { |
|
f, err := macho.NewFile(bytes.NewReader(contents)) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if f.Symtab == nil { |
|
return nil, nil |
|
} |
|
var names []string |
|
for _, sym := range f.Symtab.Syms { |
|
// Source: https://opensource.apple.com/source/xnu/xnu-3789.51.2/EXTERNAL_HEADERS/mach-o/nlist.h.auto.html |
|
const ( |
|
N_PEXT uint8 = 0x10 // Private external symbol bit |
|
N_EXT uint8 = 0x01 // External symbol bit, set for external symbols |
|
N_TYPE uint8 = 0x0e // mask for the type bits |
|
|
|
N_UNDF uint8 = 0x0 // undefined, n_sect == NO_SECT |
|
N_ABS uint8 = 0x2 // absolute, n_sect == NO_SECT |
|
N_SECT uint8 = 0xe // defined in section number n_sect |
|
N_PBUD uint8 = 0xc // prebound undefined (defined in a dylib) |
|
N_INDR uint8 = 0xa // indirect |
|
) |
|
|
|
// Only include exported, defined symbols. |
|
if sym.Type&N_EXT != 0 && sym.Type&N_TYPE != N_UNDF { |
|
if len(sym.Name) == 0 || sym.Name[0] != '_' { |
|
return nil, fmt.Errorf("unexpected symbol without underscore prefix: %q", sym.Name) |
|
} |
|
names = append(names, sym.Name[1:]) |
|
} |
|
} |
|
return names, nil |
|
} |
|
|
|
func listSymbolsPE(contents []byte) ([]string, error) { |
|
f, err := pe.NewFile(bytes.NewReader(contents)) |
|
if err != nil { |
|
return nil, err |
|
} |
|
var ret []string |
|
for _, sym := range f.Symbols { |
|
const ( |
|
// https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#section-number-values |
|
IMAGE_SYM_UNDEFINED = 0 |
|
// https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#storage-class |
|
IMAGE_SYM_CLASS_EXTERNAL = 2 |
|
) |
|
if sym.SectionNumber != IMAGE_SYM_UNDEFINED && sym.StorageClass == IMAGE_SYM_CLASS_EXTERNAL { |
|
name := sym.Name |
|
if f.Machine == pe.IMAGE_FILE_MACHINE_I386 { |
|
// On 32-bit Windows, C symbols are decorated by calling |
|
// convention. |
|
// https://msdn.microsoft.com/en-us/library/56h2zst2.aspx#FormatC |
|
if strings.HasPrefix(name, "_") || strings.HasPrefix(name, "@") { |
|
// __cdecl, __stdcall, or __fastcall. Remove the prefix and |
|
// suffix, if present. |
|
name = name[1:] |
|
if idx := strings.LastIndex(name, "@"); idx >= 0 { |
|
name = name[:idx] |
|
} |
|
} else if idx := strings.LastIndex(name, "@@"); idx >= 0 { |
|
// __vectorcall. Remove the suffix. |
|
name = name[:idx] |
|
} |
|
} |
|
ret = append(ret, name) |
|
} |
|
} |
|
return ret, nil |
|
}
|
|
|