abbc59896f
It's more verbose, but trimming the panics should make it easier to move to a library (e.g. a symbol checker) or unit test later. Change-Id: Iab37eff2689955e58057528be092d6dd5d8d26bc Reviewed-on: https://boringssl-review.googlesource.com/c/33344 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com>
191 lines
5.2 KiB
Go
191 lines
5.2 KiB
Go
// 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.
|
|
|
|
// 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"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"sort"
|
|
"strings"
|
|
|
|
"boringssl.googlesource.com/boringssl/util/ar"
|
|
)
|
|
|
|
const (
|
|
ObjFileFormatELF = "elf"
|
|
ObjFileFormatMachO = "macho"
|
|
)
|
|
|
|
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)")
|
|
)
|
|
|
|
func defaultObjFileFormat(goos string) string {
|
|
switch goos {
|
|
case "linux":
|
|
return ObjFileFormatELF
|
|
case "darwin":
|
|
return ObjFileFormatMachO
|
|
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 ...interface{}) {
|
|
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 {
|
|
if !strings.HasSuffix(name, ".o") {
|
|
continue
|
|
}
|
|
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 {
|
|
// Filter out C++ mangled names.
|
|
prefix := "_Z"
|
|
if runtime.GOOS == "darwin" {
|
|
prefix = "__Z"
|
|
}
|
|
if !strings.HasPrefix(s, prefix) {
|
|
if _, err := fmt.Fprintln(out, s); err != nil {
|
|
printAndExit("Error writing to %s: %s", *outFlag, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
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 != 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
|
|
}
|