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>
diff --git a/CMakeLists.txt b/CMakeLists.txt index dd2d937..bfde5d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -53,6 +53,9 @@ if(BORINGSSL_PREFIX AND BORINGSSL_PREFIX_SYMBOLS) 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 include_directories(${CMAKE_CURRENT_BINARY_DIR}/symbol_prefix_include)
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index ee9626a..b1ca70e 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt
@@ -53,7 +53,7 @@ set(PERLASM_STYLE win32n) set(PERLASM_FLAGS "-DOPENSSL_IA32_SSE2") 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. set(ASM_EXT asm)
diff --git a/crypto/thread_win.c b/crypto/thread_win.c index 8b2b2da..4501165 100644 --- a/crypto/thread_win.c +++ b/crypto/thread_win.c
@@ -146,12 +146,18 @@ // 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 // 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 -#pragma comment(linker, "/INCLUDE:_tls_used") -#pragma comment(linker, "/INCLUDE:p_thread_callback_boringssl") +__pragma(comment(linker, "/INCLUDE:_tls_used")) +__pragma(comment( + linker, "/INCLUDE:" EXPAND_AND_STRINGIFY(p_thread_callback_boringssl))) #else -#pragma comment(linker, "/INCLUDE:__tls_used") -#pragma comment(linker, "/INCLUDE:_p_thread_callback_boringssl") +__pragma(comment(linker, "/INCLUDE:__tls_used")) +__pragma(comment( + linker, "/INCLUDE:_" EXPAND_AND_STRINGIFY(p_thread_callback_boringssl))) #endif // .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
diff --git a/util/make_prefix_headers.go b/util/make_prefix_headers.go index a5e5441..b536f14 100644 --- a/util/make_prefix_headers.go +++ b/util/make_prefix_headers.go
@@ -172,16 +172,32 @@ ; OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN ; 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 { return err } 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 } } + 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 }
diff --git a/util/read_symbols.go b/util/read_symbols.go index fc3e069..d6d5a9a 100644 --- a/util/read_symbols.go +++ b/util/read_symbols.go
@@ -20,6 +20,7 @@ "bytes" "debug/elf" "debug/macho" + "debug/pe" "flag" "fmt" "os" @@ -33,11 +34,12 @@ 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)") + objFileFormat = flag.String("obj-file-format", defaultObjFileFormat(runtime.GOOS), "Object file format to expect (options are elf, macho, pe)") ) func defaultObjFileFormat(goos string) string { @@ -46,6 +48,8 @@ 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 @@ -105,17 +109,53 @@ } } } + sort.Strings(symbols) for _, s := range symbols { - // Filter out C++ mangled names. - if !strings.HasPrefix(s, "_Z") { - if _, err := fmt.Fprintln(out, s); err != nil { - printAndExit("Error writing to %s: %s", *outFlag, err) + 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 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 { + 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 { @@ -123,6 +163,8 @@ return listSymbolsELF(contents) case ObjFileFormatMachO: return listSymbolsMachO(contents) + case ObjFileFormatPE: + return listSymbolsPE(contents) default: return nil, fmt.Errorf("unsupported object file format %q", *objFileFormat) } @@ -181,3 +223,40 @@ } 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 +}