Make Windows symbol-prefixing work.
This teaches read_symbols.go to use debug/pe, and fixes miscellaneous issues with NASM. It also reveals a problem with this strategy of getting symbols out at the linker level: inline functions. I'm thinking a better long-term mechanism may be to parse our header files. Change-Id: I11b008543a7a97db3db9d4062ee4ddb910d174b7 Reviewed-on: https://boringssl-review.googlesource.com/c/33349 Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
parent
c8cf62bba8
commit
8c23d3a5df
@ -53,6 +53,9 @@ endif()
|
|||||||
|
|
||||||
if(BORINGSSL_PREFIX AND BORINGSSL_PREFIX_SYMBOLS)
|
if(BORINGSSL_PREFIX AND BORINGSSL_PREFIX_SYMBOLS)
|
||||||
add_definitions(-DBORINGSSL_PREFIX=${BORINGSSL_PREFIX})
|
add_definitions(-DBORINGSSL_PREFIX=${BORINGSSL_PREFIX})
|
||||||
|
# CMake automatically connects include_directories to the NASM command-line,
|
||||||
|
# but not add_definitions.
|
||||||
|
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DBORINGSSL_PREFIX=${BORINGSSL_PREFIX}")
|
||||||
|
|
||||||
# Use "symbol_prefix_include" to store generated header files
|
# Use "symbol_prefix_include" to store generated header files
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/symbol_prefix_include)
|
include_directories(${CMAKE_CURRENT_BINARY_DIR}/symbol_prefix_include)
|
||||||
|
@ -53,7 +53,7 @@ if(NOT OPENSSL_NO_ASM)
|
|||||||
set(PERLASM_STYLE win32n)
|
set(PERLASM_STYLE win32n)
|
||||||
set(PERLASM_FLAGS "-DOPENSSL_IA32_SSE2")
|
set(PERLASM_FLAGS "-DOPENSSL_IA32_SSE2")
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_ASM_NASM_FLAGS "-gcv8")
|
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -gcv8")
|
||||||
|
|
||||||
# On Windows, we use the NASM output, specifically built with Yasm.
|
# On Windows, we use the NASM output, specifically built with Yasm.
|
||||||
set(ASM_EXT asm)
|
set(ASM_EXT asm)
|
||||||
|
@ -146,12 +146,18 @@ static void NTAPI thread_local_destructor(PVOID module, DWORD reason,
|
|||||||
// if it's not already there. (E.g. if __declspec(thread) is not used). Force
|
// if it's not already there. (E.g. if __declspec(thread) is not used). Force
|
||||||
// a reference to p_thread_callback_boringssl to prevent whole program
|
// a reference to p_thread_callback_boringssl to prevent whole program
|
||||||
// optimization from discarding the variable.
|
// optimization from discarding the variable.
|
||||||
|
//
|
||||||
|
// Note, in the prefixed build, |p_thread_callback_boringssl| may be a macro.
|
||||||
|
#define STRINGIFY(x) #x
|
||||||
|
#define EXPAND_AND_STRINGIFY(x) STRINGIFY(x)
|
||||||
#ifdef _WIN64
|
#ifdef _WIN64
|
||||||
#pragma comment(linker, "/INCLUDE:_tls_used")
|
__pragma(comment(linker, "/INCLUDE:_tls_used"))
|
||||||
#pragma comment(linker, "/INCLUDE:p_thread_callback_boringssl")
|
__pragma(comment(
|
||||||
|
linker, "/INCLUDE:" EXPAND_AND_STRINGIFY(p_thread_callback_boringssl)))
|
||||||
#else
|
#else
|
||||||
#pragma comment(linker, "/INCLUDE:__tls_used")
|
__pragma(comment(linker, "/INCLUDE:__tls_used"))
|
||||||
#pragma comment(linker, "/INCLUDE:_p_thread_callback_boringssl")
|
__pragma(comment(
|
||||||
|
linker, "/INCLUDE:_" EXPAND_AND_STRINGIFY(p_thread_callback_boringssl)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
|
// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
|
||||||
|
@ -172,16 +172,32 @@ func writeNASMHeader(symbols []string, path string) error {
|
|||||||
; OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
; OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||||
; CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
; CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
; 32-bit Windows adds underscores to C functions, while 64-bit Windows does not.
|
||||||
|
%ifidn __OUTPUT_FORMAT__, win32
|
||||||
`); err != nil {
|
`); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, symbol := range symbols {
|
for _, symbol := range symbols {
|
||||||
if _, err := fmt.Fprintf(f, "%%define %s BORINGSSL_PREFIX %%+ %s\n", symbol, symbol); err != nil {
|
if _, err := fmt.Fprintf(f, "%%xdefine _%s _ %%+ BORINGSSL_PREFIX %%+ _%s\n", symbol, symbol); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := fmt.Fprintf(f, "%%else\n"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, symbol := range symbols {
|
||||||
|
if _, err := fmt.Fprintf(f, "%%xdefine %s BORINGSSL_PREFIX %%+ _%s\n", symbol, symbol); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := fmt.Fprintf(f, "%%endif\n"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"debug/macho"
|
"debug/macho"
|
||||||
|
"debug/pe"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -33,11 +34,12 @@ import (
|
|||||||
const (
|
const (
|
||||||
ObjFileFormatELF = "elf"
|
ObjFileFormatELF = "elf"
|
||||||
ObjFileFormatMachO = "macho"
|
ObjFileFormatMachO = "macho"
|
||||||
|
ObjFileFormatPE = "pe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
outFlag = flag.String("out", "-", "File to write output symbols")
|
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)")
|
objFileFormat = flag.String("obj-file-format", defaultObjFileFormat(runtime.GOOS), "Object file format to expect (options are elf, macho, pe)")
|
||||||
)
|
)
|
||||||
|
|
||||||
func defaultObjFileFormat(goos string) string {
|
func defaultObjFileFormat(goos string) string {
|
||||||
@ -46,6 +48,8 @@ func defaultObjFileFormat(goos string) string {
|
|||||||
return ObjFileFormatELF
|
return ObjFileFormatELF
|
||||||
case "darwin":
|
case "darwin":
|
||||||
return ObjFileFormatMachO
|
return ObjFileFormatMachO
|
||||||
|
case "windows":
|
||||||
|
return ObjFileFormatPE
|
||||||
default:
|
default:
|
||||||
// By returning a value here rather than panicking, the user can still
|
// By returning a value here rather than panicking, the user can still
|
||||||
// cross-compile from an unsupported platform to a supported platform by
|
// cross-compile from an unsupported platform to a supported platform by
|
||||||
@ -105,15 +109,51 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(symbols)
|
sort.Strings(symbols)
|
||||||
for _, s := range symbols {
|
for _, s := range symbols {
|
||||||
// Filter out C++ mangled names.
|
var skipSymbols = []string{
|
||||||
if !strings.HasPrefix(s, "_Z") {
|
// 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 blacklist
|
||||||
|
// 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@") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if _, err := fmt.Fprintln(out, s); err != nil {
|
if _, err := fmt.Fprintln(out, s); err != nil {
|
||||||
printAndExit("Error writing to %s: %s", *outFlag, err)
|
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.
|
// listSymbols lists the exported symbols from an object file.
|
||||||
@ -123,6 +163,8 @@ func listSymbols(contents []byte) ([]string, error) {
|
|||||||
return listSymbolsELF(contents)
|
return listSymbolsELF(contents)
|
||||||
case ObjFileFormatMachO:
|
case ObjFileFormatMachO:
|
||||||
return listSymbolsMachO(contents)
|
return listSymbolsMachO(contents)
|
||||||
|
case ObjFileFormatPE:
|
||||||
|
return listSymbolsPE(contents)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported object file format %q", *objFileFormat)
|
return nil, fmt.Errorf("unsupported object file format %q", *objFileFormat)
|
||||||
}
|
}
|
||||||
@ -181,3 +223,40 @@ func listSymbolsMachO(contents []byte) ([]string, error) {
|
|||||||
}
|
}
|
||||||
return names, nil
|
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
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user