Index: libcxx/include/CMakeLists.txt =================================================================== --- libcxx/include/CMakeLists.txt +++ libcxx/include/CMakeLists.txt @@ -237,14 +237,15 @@ add_library(${CXX_HEADER_TARGET} INTERFACE) add_dependencies(${CXX_HEADER_TARGET} generate-cxx-headers ${LIBCXX_CXX_ABI_HEADER_TARGET}) # TODO: Use target_include_directories once we figure out why that breaks the runtimes build - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") + + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC" AND NOT ${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} STREQUAL "GNU") target_compile_options(${CXX_HEADER_TARGET} INTERFACE /I "${output_dir}") else() target_compile_options(${CXX_HEADER_TARGET} INTERFACE -I "${output_dir}") endif() # Make sure the generated __config_site header is included when we build the library. - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC" AND NOT ${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} STREQUAL "GNU") target_compile_options(${CXX_HEADER_TARGET} INTERFACE /FI "${LIBCXX_BINARY_DIR}/__config_site") else() target_compile_options(${CXX_HEADER_TARGET} INTERFACE -include "${LIBCXX_BINARY_DIR}/__config_site") Index: libcxx/include/support/win32/locale_win32.h =================================================================== --- libcxx/include/support/win32/locale_win32.h +++ libcxx/include/support/win32/locale_win32.h @@ -12,9 +12,41 @@ #include <__config> #include -#include // _locale_t +#include // _locale_t #include <__nullptr> +<<>>> + #define LC_COLLATE_MASK _M_COLLATE #define LC_CTYPE_MASK _M_CTYPE #define LC_MONETARY_MASK _M_MONETARY Index: libcxx/src/CMakeLists.txt =================================================================== --- libcxx/src/CMakeLists.txt +++ libcxx/src/CMakeLists.txt @@ -57,11 +57,14 @@ ) if(WIN32) - list(APPEND LIBCXX_SOURCES +list(APPEND LIBCXX_SOURCES support/win32/locale_win32.cpp - support/win32/support.cpp - support/win32/thread_win32.cpp - ) + support/win32/support.cpp) +if (LIBCXX_ENABLE_THREADS) + list(APPEND LIBCXX_SOURCES + support/win32/thread_win32.cpp) +endif() + elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS") list(APPEND LIBCXX_SOURCES support/solaris/mbsnrtowcs.inc @@ -192,6 +195,8 @@ if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY) if (APPLE) target_link_libraries(cxx_shared PRIVATE "-Wl,-force_load" "${LIBCXX_CXX_STATIC_ABI_LIBRARY}") + elseif (MSVC OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") + target_link_libraries(cxx_shared PRIVATE "-Wl,/WHOLEARCHIVE:${LIBCXX_CXX_STATIC_ABI_LIBRARY}${CMAKE_STATIC_LIBRARY_SUFFIX}") else() target_link_libraries(cxx_shared PRIVATE "-Wl,--whole-archive,-Bstatic" "${LIBCXX_CXX_STATIC_ABI_LIBRARY}" "-Wl,-Bdynamic,--no-whole-archive") endif() @@ -277,6 +282,9 @@ endif() if (TARGET "${LIBCXX_CXX_STATIC_ABI_LIBRARY}" OR HAVE_LIBCXXABI) set(MERGE_ARCHIVES_ABI_TARGET "$") + elseif (MSVC OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") + set(MERGE_ARCHIVES_ABI_TARGET + "${LIBCXX_CXX_STATIC_ABI_LIBRARY}${CMAKE_STATIC_LIBRARY_SUFFIX}") else() set(MERGE_ARCHIVES_ABI_TARGET "${CMAKE_STATIC_LIBRARY_PREFIX}${LIBCXX_CXX_STATIC_ABI_LIBRARY}${CMAKE_STATIC_LIBRARY_SUFFIX}") Index: libcxx/utils/merge_archives.py =================================================================== --- libcxx/utils/merge_archives.py +++ libcxx/utils/merge_archives.py @@ -31,9 +31,6 @@ def find_and_diagnose_missing(lib, search_paths): if os.path.exists(lib): return os.path.abspath(lib) - if not lib.startswith('lib') or not lib.endswith('.a'): - print_and_exit(("input file '%s' not not name a static library. " - "It should start with 'lib' and end with '.a") % lib) for sp in search_paths: assert type(sp) is list and len(sp) == 1 path = os.path.join(sp[0], lib) Index: libcxxabi/CMakeLists.txt =================================================================== --- libcxxabi/CMakeLists.txt +++ libcxxabi/CMakeLists.txt @@ -103,6 +103,8 @@ option(LIBCXXABI_ENABLE_SHARED "Build libc++abi as a shared library." ON) option(LIBCXXABI_ENABLE_STATIC "Build libc++abi as a static library." ON) +option(LIBCXXABI_ENABLE_STATIC_FOR_LIBCXX_SHARED "Build libc++abi as a static library to be linked into a libcxx shared library." ON) + cmake_dependent_option(LIBCXXABI_INSTALL_STATIC_LIBRARY "Install the static libc++abi library." ON "LIBCXXABI_ENABLE_STATIC;LIBCXXABI_INSTALL_LIBRARY" OFF) @@ -257,10 +259,17 @@ add_definitions(-D_LIBCXXABI_BUILDING_LIBRARY) # Disable DLL annotations on Windows for static builds. -if (WIN32 AND LIBCXXABI_ENABLE_STATIC AND NOT LIBCXXABI_ENABLE_SHARED) +if (WIN32 AND LIBCXXABI_ENABLE_STATIC AND NOT LIBCXXABI_ENABLE_SHARED AND NOT LIBCXXABI_ENABLE_STATIC_FOR_LIBCXX_SHARED) add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS) endif() +# We are building libcxxabi as a static library and linking it into libcxx; +# therefore, we need the export annotations to be in place so that the +# required symobls will be exported from the final libcxx dll. +if (LIBCXXABI_ENABLE_STATIC_FOR_LIBCXX_SHARED) + add_definitions(-D_LIBCPP_BUILDING_LIBRARY) +endif() + add_compile_flags_if_supported(-Werror=return-type) # Get warning flags @@ -409,8 +418,22 @@ # pre-C++17. add_definitions(-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS) -if (MSVC) +if (MSVC OR "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") add_definitions(-D_CRT_SECURE_NO_WARNINGS) + if (LIBCXXABI_ENABLE_STATIC_FOR_LIBCXX_SHARED) + # There is a symbol dependency between libc++ and libc++abi. To break this dependency + # we build libc++abi as a library and then statically link that library into the + # libc++ dll. In order to make this work I have copied this block of compiler flags from + # libc++'s' CMakeLists.txt. Unless these flags are normalized link errors are emitted + # when trying to link the libc++ dll, for example: + # lld-link: error: /failifmismatch: mismatch detected for '_CRT_STDIO_ISO_WIDE_SPECIFIERS': + # >>> src/CMakeFiles/cxx_shared.dir/support/win32/support.cpp.obj has value 1 + # >>> c++abi.lib(cxa_handlers.cpp.obj) has value 0 + add_definitions(-D_ALLOW_MSC_VER_MISMATCH) + add_definitions(-D_ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH) + add_definitions(-D_CRTBLD) + add_definitions(-D_CRT_STDIO_ISO_WIDE_SPECIFIERS) + endif() endif() # Define LIBCXXABI_USE_LLVM_UNWINDER for conditional compilation. Index: libcxxabi/src/cxa_guard_impl.h =================================================================== --- libcxxabi/src/cxa_guard_impl.h +++ libcxxabi/src/cxa_guard_impl.h @@ -39,7 +39,9 @@ #include "__cxxabi_config.h" #include "include/atomic_support.h" -#include +// Avoid dragging in vcruntime headers +// TODO: Control this properly with the preprocessor. +//#include #if defined(__has_include) # if __has_include() # include Index: libunwind/CMakeLists.txt =================================================================== --- libunwind/CMakeLists.txt +++ libunwind/CMakeLists.txt @@ -138,6 +138,7 @@ option(LIBUNWIND_USE_COMPILER_RT "Use compiler-rt instead of libgcc" OFF) option(LIBUNWIND_INCLUDE_DOCS "Build the libunwind documentation." ${LLVM_INCLUDE_DOCS}) option(LIBUNWIND_IS_BAREMETAL "Build libunwind for baremetal targets." OFF) +option(LIBUNWIND_NO_LIBRARIES "Don't add any libraries to the link line." OFF) set(LIBUNWIND_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING "Define suffix of library directory name (32/64)") Index: libunwind/src/CMakeLists.txt =================================================================== --- libunwind/src/CMakeLists.txt +++ libunwind/src/CMakeLists.txt @@ -67,17 +67,19 @@ ${LIBUNWIND_ASM_SOURCES}) # Generate library list. -add_library_flags_if(LIBUNWIND_HAS_C_LIB c) -if (LIBUNWIND_USE_COMPILER_RT) - add_library_flags("${LIBUNWIND_BUILTINS_LIBRARY}") -else() - add_library_flags_if(LIBUNWIND_HAS_GCC_S_LIB gcc_s) - add_library_flags_if(LIBUNWIND_HAS_GCC_LIB gcc) -endif() -add_library_flags_if(LIBUNWIND_HAS_DL_LIB dl) -if (LIBUNWIND_ENABLE_THREADS) - add_library_flags_if(LIBUNWIND_HAS_PTHREAD_LIB pthread) - add_compile_flags_if(LIBUNWIND_WEAK_PTHREAD_LIB -DLIBUNWIND_USE_WEAK_PTHREAD=1) +if (NOT LIBUNWIND_NO_LIBRARIES) + add_library_flags_if(LIBUNWIND_HAS_C_LIB c) + if (LIBUNWIND_USE_COMPILER_RT) + add_library_flags("${LIBUNWIND_BUILTINS_LIBRARY}") + else() + add_library_flags_if(LIBUNWIND_HAS_GCC_S_LIB gcc_s) + add_library_flags_if(LIBUNWIND_HAS_GCC_LIB gcc) + endif() + add_library_flags_if(LIBUNWIND_HAS_DL_LIB dl) + if (LIBUNWIND_ENABLE_THREADS) + add_library_flags_if(LIBUNWIND_HAS_PTHREAD_LIB pthread) + add_compile_flags_if(LIBUNWIND_WEAK_PTHREAD_LIB -DLIBUNWIND_USE_WEAK_PTHREAD=1) + endif() endif() # Setup flags. Index: wi-test/lit.cfg =================================================================== --- /dev/null +++ wi-test/lit.cfg @@ -0,0 +1,10 @@ +import os, platform, sys, lit.formats + +# name: The name of this test suite. +config.name = 'wi-test' + +# testFormat: The test format to use to interpret tests. +config.test_format = lit.formats.ShTest(execute_external=False) + +# suffixes: A list of file extensions to treat as test files. +config.suffixes = ['.py'] Index: wi-test/test/dll_class/foo.h =================================================================== --- /dev/null +++ wi-test/test/dll_class/foo.h @@ -0,0 +1,13 @@ +#if EXPORTING + #define IMPORT_EXPORT __declspec(dllexport) +#else + #define IMPORT_EXPORT __declspec(dllimport) +#endif + +struct Foo +{ + virtual int TypeId() const { return 1; } + IMPORT_EXPORT static int mCounter; + IMPORT_EXPORT virtual int Func(); + IMPORT_EXPORT virtual void AnotherFunc(); +}; Index: wi-test/test/dll_class/main.cpp =================================================================== --- /dev/null +++ wi-test/test/dll_class/main.cpp @@ -0,0 +1,46 @@ +#include + +// EXPORTING == 0 => we are importing in this module +#define EXPORTING 0 +#include "foo.h" + +extern void doImportedWork(); +void doImportedWork() +{ + Foo f; + printf("doImportedWork(), mCounter = %d\n", Foo::mCounter); + ++Foo::mCounter; + (void) f.Func(); // Foo::Func() is imported + f.AnotherFunc(); // Foo::AnotherFunc() is imported +} + +#include +int load_and_run_plugin() +{ + HMODULE module = LoadLibraryA("test.py.tmp_plugin.dll"); + typedef int (*PluginMainT)(); + PluginMainT pstart = (PluginMainT)GetProcAddress(module, "module_start"); + if (!pstart) { + printf("Failure to load DLL or to find 'module_start' in it.\n"); + printf("load module result: 0x%08x\n", (unsigned int) module); + return -1; + } + int res = pstart(); + printf("plugin module: %u\n", (unsigned int) module); + printf("plugin pstart: %u\n", (unsigned int) pstart); + printf("Successfully loaded and ran plugin.\n"); + doImportedWork(); + printf("Plugin returned: %d\n", res); + return 0; +} + +int main() +{ + printf("app_main_start\n"); + if (load_and_run_plugin() == 0) + printf("{PASS}\n"); + else + printf("{FAIL}\n"); + printf("app_main_end\n"); + return 0; +} Index: wi-test/test/dll_class/plugin.cpp =================================================================== --- /dev/null +++ wi-test/test/dll_class/plugin.cpp @@ -0,0 +1,43 @@ +#include + +#define EXPORTING 1 +#include "foo.h" + +// This is a vitual method, exported from here. It is declared as follows in "foo.h": +// IMPORT_EXPORT virtual int Func(); +// +// This is the key function, so the vtable of 'Foo' is defined in this TU. +int Foo::Func() +{ + mCounter += 28; + printf("Foo::Func(), mCounter = %d\n", mCounter); + return mCounter; +} + +// This is a vitual method, exported from here. It is declared as follows in "foo.h": +// IMPORT_EXPORT virtual void AnotherFunc(); +void Foo::AnotherFunc() +{ + printf("Foo::AnotherFunc(), mCounter = %d\n", mCounter); +} + +IMPORT_EXPORT int Foo::mCounter = 1; + +int plugin_main() +{ + Foo f; + Foo::mCounter += 11; + printf("plugin_main(), mCounter = %d\n", Foo::mCounter); + ++Foo::mCounter; + return f.TypeId() + Foo::mCounter; +} + + +extern "C" +__declspec(dllexport) +int module_start(size_t unused_a, const void* unused_b) +{ + printf("module_start\n"); + int i = plugin_main(); + return i + 3; +} Index: wi-test/test/dll_class/test.py =================================================================== --- /dev/null +++ wi-test/test/dll_class/test.py @@ -0,0 +1,9 @@ +## Check that we can export the member functions of a class + +# RUN: %{clang-wi} plugin.cpp -Wl,/dll -o %t_plugin.dll -Wl,/IMPLIB:%t_plugin.lib -Wno-pointer-to-int-cast +# RUN: %{clang-wi} main.cpp -l%t_plugin.lib -Wno-pointer-to-int-cast -o %t_main.exe +# RUN: %t_main.exe | FileCheck %s + +# CHECK: Plugin returned: 17 +# CHECK: {PASS} + Index: wi-test/test/dll_typeinfo/foo.h =================================================================== --- /dev/null +++ wi-test/test/dll_typeinfo/foo.h @@ -0,0 +1,19 @@ +#if EXPORTING + #define IMPORT_EXPORT __declspec(dllexport) +#else + #define IMPORT_EXPORT __declspec(dllimport) +#endif + +struct Base +{ + virtual int getVersion() const { return 1; } + IMPORT_EXPORT virtual void keyFunc(); +}; + +// Derive a class from 'Base', just so we can have interesting typeinfo +struct Derived : public Base +{ + int mUnused; +}; + +extern IMPORT_EXPORT void obscuro(); Index: wi-test/test/dll_typeinfo/main.cpp =================================================================== --- /dev/null +++ wi-test/test/dll_typeinfo/main.cpp @@ -0,0 +1,56 @@ +#include +#include + +#define EXPORTING 0 +#include "foo.h" + +#define EXPECTED_RESULT 17 + +Base *basePtr; + +extern void doImportedWork(); +void doImportedWork() +{ + Base b; + Derived d; + printf("doImportedWork() invoked.\n"); + b.keyFunc(); // Base::keyFunc() is imported + + basePtr = &b; // address of a 'Base' + obscuro(); // don't let the optimizer figure out the typeid().name() + printf("Base typeid: '%s'\n", typeid(*basePtr).name()); + + basePtr = &d; // address of a 'Derived' + obscuro(); // don't let the optimizer figure out the typeid().name() + printf("Derived typeid: '%s'\n", typeid(*basePtr).name()); +} + +#include +int load_and_run_plugin() +{ + HMODULE module = LoadLibraryA("test.py.tmp_plugin.dll"); + typedef int (*PluginMainT)(); + PluginMainT pstart = (PluginMainT)GetProcAddress(module, "module_start"); + if (!pstart) { + printf("Failure to load DLL or to find 'module_start' in it.\n"); + printf("load module result: 0x%08x\n", (unsigned int) module); + return -1; + } + int res = pstart(); + printf("plugin module: %u\n", (unsigned int) module); + printf("plugin pstart: %u\n", (unsigned int) pstart); + printf("Successfully loaded and ran plugin.\n"); + doImportedWork(); + printf("Plugin returned: %d\n", res); + return res; +} + +int main() +{ + printf("begin main()\n"); + if (load_and_run_plugin() == EXPECTED_RESULT) + printf("{PASS}\n"); + else + printf("{FAIL}\n"); + return 0; +} Index: wi-test/test/dll_typeinfo/plugin.cpp =================================================================== --- /dev/null +++ wi-test/test/dll_typeinfo/plugin.cpp @@ -0,0 +1,30 @@ +#include + +#define EXPORTING 1 +#include "foo.h" + +// This is the key function, so the vtable of 'Base' is defined in this TU. +void Base::keyFunc() +{ + printf("Base::keyFunc() invoked.\n"); +} + +int plugin_main() +{ + Base b; + printf("plugin_main() invoked.\n"); + return 16 + b.getVersion(); +} + +extern "C" +IMPORT_EXPORT int module_start(size_t unused_a, const void* unused_b) +{ + printf("module_start\n"); + return plugin_main(); +} + +// A function to call to prevent the optimizer from seeing too much. That is, +// to keep the optimizer from optimizing-away RTTI calls in the other module. +IMPORT_EXPORT void obscuro() +{ +} Index: wi-test/test/dll_typeinfo/test.py =================================================================== --- /dev/null +++ wi-test/test/dll_typeinfo/test.py @@ -0,0 +1,77 @@ +# This test-case is for a program with more than one "module", which contains a class +# referenced in the multiple modules, and where the typeinfo symbol for RTTI, and the +# vtable, are only defined in one place (because of the "key function" concept of the +# Itanium C++ ABI rules). +# +# For this to link, the typeinfo symbol and vtable must be exported from the module that +# contains their definitions, and imported into the module that uses them but does not +# have the definitions. The user can express this by either: + +# (a) declaring the class as a whole as '__declspec(dllexport)'/'__declspec(dllimport)', or +# +# (b) declaring all non-inline virtual methods of the class as +# '__declspec(dllexport)'/'__declspec(dllimport)'. +# ______________________________________________________________________ +# +# For this test-case, we have the following 'Base' and 'Derived' class: +# +# #if EXPORTING +# #define IMPORT_EXPORT __declspec(dllexport) +# #else +# #define IMPORT_EXPORT __declspec(dllimport) +# #endif +# +# struct Base +# { +# virtual int getVersion() const { return 1; } +# IMPORT_EXPORT virtual void keyFunc(); +# }; +# +# // Derive a class from 'Base', just so we can have interesting typeinfo +# struct Derived : public Base +# { +# int mUnused; +# }; +# +# This class is compiled into two separate modules: an exporting module (compiled with +# EXPORTING defined as 1), and an importing module (compiled with EXPORTING defined as 0). +# ______________________________________________________________________ +# +# The test-case prints the string returned by the typeid().name() method: +# +# Base *basePtr; +# +# extern void doImportedWork(); +# void doImportedWork() +# { +# Base b; +# Derived d; +# printf("doImportedWork() invoked.\n"); +# b.keyFunc(); // Base::keyFunc() is imported +# +# basePtr = &b; // address of a 'Base' +# obscuro(); // don't let the optimizer figure out the typeid().name() +# printf("Base typeid: '%s'\n", typeid(*basePtr).name()); +# +# basePtr = &d; // address of a 'Derived' +# obscuro(); // don't let the optimizer figure out the typeid().name() +# printf("Derived typeid: '%s'\n", typeid(*basePtr).name()); +# } +# +# The form of that string is implementation-dependent. To make the test somewhat flexible, +# we assume that the string returned by the name() method will contain "Base" for the class +# named 'Base', and "Derived" for the class named 'Derived'. This will be the case on most +# platforms. + + +# RUN: %{clang-wi} plugin.cpp -Wl,/dll -o %t_plugin.dll -Wl,/IMPLIB:%t_plugin.lib -Wno-pointer-to-int-cast -frtti +# RUN: %{clang-wi} main.cpp -l%t_plugin.lib -Wno-pointer-to-int-cast -frtti -o %t_main.exe 2>&1 +# RUN: %t_main.exe | FileCheck %s + +# CHECK: Successfully loaded and ran plugin. +# CHECK-NEXT: doImportedWork() invoked. +# CHECK-NEXT: Base::keyFunc() invoked. +# CHECK-NEXT: Base typeid: '{{.*}}Base{{.*}}' +# CHECK-NEXT: Derived typeid: '{{.*}}Derived{{.*}}' +# CHECK-NEXT: Plugin returned: 17 +# CHECK-NEXT: {PASS} Index: wi-test/test/dll_vtable_missing/foo.h =================================================================== --- /dev/null +++ wi-test/test/dll_vtable_missing/foo.h @@ -0,0 +1,26 @@ +#if EXPORTING + #define IMPORT_EXPORT __declspec(dllexport) +#else + #define IMPORT_EXPORT __declspec(dllimport) +#endif + +// Since all out-of-line virtual member functions of the following class are not +// imported/exported, Microsoft will fail to link this. Specifically, with AnotherFunc() +// being being virtual, it's referenced in the vtable. The definition appears in one +// place ("plugin.obj"/"plugin.o", which goes into "plugin.dll"/"plugin.prx" in this +// test-case). With the Microsoft vtable model, the vtable will be in COMDAT everywhere +// it is referenced. So since it's referenced in "main.obj"/"main.o", to be linked into +// "test.exe"/"test.elf", and there will be a reference to AnotherFunc() in the vtable +// in "main.obj", but AnotherFunc() is not exported from its definition in "plugin.obj", so +// there will be a link-time failure when liking "test.exe" by Microsoft. We want to +// make sure we get a link-time failure with the Windwos Itanium toolchain. +struct Foo +{ + virtual int TypeId() const { return 1; } + IMPORT_EXPORT static int mCounter; + + // AnotherFunc() not being exported should cause a link-time failure, as described above. + /* IMPORT_EXPORT */ virtual void AnotherFunc(); + + IMPORT_EXPORT virtual int Func(); +}; Index: wi-test/test/dll_vtable_missing/main.cpp =================================================================== --- /dev/null +++ wi-test/test/dll_vtable_missing/main.cpp @@ -0,0 +1,46 @@ +#include + +// EXPORTING == 0 => we are importing in this module +#define EXPORTING 0 +#include "foo.h" + +extern void doImportedWork(); +void doImportedWork() +{ + Foo f; // constructor call will reference vtable of 'Foo' + printf("doImportedWork(), mCounter = %d\n", Foo::mCounter); + ++Foo::mCounter; + (void) f.Func(); // Foo::Func() is imported + // f.AnotherFunc(); // Foo::AnotherFunc() is not imported -- don't call it +} + +#include +int load_and_run_plugin() +{ + HMODULE module = LoadLibraryA("test.py.tmp_plugin.dll"); + typedef int (*PluginMainT)(); + PluginMainT pstart = (PluginMainT)GetProcAddress(module, "module_start"); + if (!pstart) { + printf("Failure to load DLL or to find 'module_start' in it.\n"); + printf("load module result: 0x%08x\n", (unsigned int) module); + return -1; + } + int res = pstart(); + printf("plugin module: %u\n", (unsigned int) module); + printf("plugin pstart: %u\n", (unsigned int) pstart); + printf("Successfully loaded and ran plugin.\n"); + doImportedWork(); + printf("Plugin returned: %d\n", res); + return 0; +} + +int main() +{ + printf("app_main_start\n"); + if (load_and_run_plugin() == 0) + printf("{PASS}\n"); + else + printf("{FAIL}\n"); + printf("app_main_end\n"); + return 0; +} Index: wi-test/test/dll_vtable_missing/plugin.cpp =================================================================== --- /dev/null +++ wi-test/test/dll_vtable_missing/plugin.cpp @@ -0,0 +1,43 @@ +#include + +#define EXPORTING 1 +#include "foo.h" + +// This is a vitual method, but not exported from here. It is declared as follows in "foo.h": +// virtual void AnotherFunc(); +// Consequently, the vtable of 'Foo' will not be exported either. This will cause an unresolved +// refence to 'Foo::AnotherFunc()' for the Microsoft toolchain). +void Foo::AnotherFunc() +{ + printf("Foo::AnotherFunc(), mCounter = %d\n", mCounter); +} + +// This is a vitual method, exported from here. It is declared as follows in "foo.h": +// IMPORT_EXPORT virtual int Func(); +// This is the key function, so the vtable of 'Foo' is defined in this TU. +int Foo::Func() +{ + mCounter += 28; + printf("Foo::Func(), mCounter = %d\n", mCounter); + return mCounter; +} + +IMPORT_EXPORT int Foo::mCounter = 1; + +int plugin_main() +{ + Foo f; + Foo::mCounter += 11; + printf("plugin_main(), mCounter = %d\n", Foo::mCounter); + ++Foo::mCounter; + return f.TypeId() + Foo::mCounter; +} + +extern "C" +__declspec(dllexport) +int module_start(size_t unused_a, const void* unused_b) +{ + printf("module_start\n"); + int i = plugin_main(); + return i + 3; +} Index: wi-test/test/dll_vtable_missing/test.py =================================================================== --- /dev/null +++ wi-test/test/dll_vtable_missing/test.py @@ -0,0 +1,8 @@ +## Test that the link fails if some, but not all, of the +## non-inline member functions of a class are marked as +## dllimport/export. VC++ fails in this cirumstance. + +# RUN: %{clang-wi} plugin.cpp -Wl,/dll -o %tplugin.dll -Wl,/IMPLIB:%t_plugin.lib -Wno-pointer-to-int-cast +# RUN: not %{clang-wi} main.cpp -l%t_plugin.lib -Wno-pointer-to-int-cast 2>&1 | FileCheck %s + +# CHECK: error: undefined symbol: vtable for Foo Index: wi-test/test/hello/hello.cpp =================================================================== --- /dev/null +++ wi-test/test/hello/hello.cpp @@ -0,0 +1,2 @@ +#include +int main() {std::cout << "Hello!";} \ No newline at end of file Index: wi-test/test/hello/test.py =================================================================== --- /dev/null +++ wi-test/test/hello/test.py @@ -0,0 +1,6 @@ +## Simple C++ hello world smoke test + +# RUN: %{clang-wi} hello.cpp -o %t.exe +# RUN: %t.exe | FileCheck %s + +# CHECK: Hello Index: wi-test/test/typeinfo_vtable_ptr/main.cpp =================================================================== --- /dev/null +++ wi-test/test/typeinfo_vtable_ptr/main.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +struct B1 {}; +struct D1 : B1 {}; + +struct B2 { virtual void foo() {} }; +struct D2 : B2 {}; + +int main() { + { + D1 d1; + auto & td1 = typeid(d1); + std::cout << td1.name() << '\n'; + B1& b1 = d1; + auto & tb1 = typeid(b1); + std::cout << tb1.name() << '\n'; + } + { + D2 d2; + auto & td2 = typeid(d2); + std::cout << td2.name() << '\n'; + B2& b2 = d2; + auto & tb2 = typeid(b2); + std::cout << tb2.name() << '\n'; + } +} Index: wi-test/test/typeinfo_vtable_ptr/test.py =================================================================== --- /dev/null +++ wi-test/test/typeinfo_vtable_ptr/test.py @@ -0,0 +1,16 @@ +# This testcase tries to exercise typeinfo in the hope of +# exposing a bug due to the uninitialised vtable ptrs in +# the case of the references to e.g. vtables for +# __cxxabiv1::__class_type_info from typeinto objects. +# See _pei386_runtime_relocator which handles the runtime +# component of the autoimporting scheme used for mingw and +# comments in https://reviews.llvm.org/D43184 and +# https://reviews.llvm.org/D89518 for more. + +# RUN: %{clang-wi} main.cpp -Wno-pointer-to-int-cast -frtti -fexceptions -o %t_main.exe 2>&1 +# RUN: %t_main.exe | FileCheck %s + +# CHECK: {{.*}}D1{{.*}} +# CHECK-NEXT: {{.*}}B1{{.*}} +# CHECK-NEXT: {{.*}}D2{{.*}} +# CHECK-NEXT: {{.*}}D2{{.*}} Index: wi.py =================================================================== --- /dev/null +++ wi.py @@ -0,0 +1,557 @@ +#!/usr/bin/env python3 + +# Build a windows-itanium environment: +# We require libunwind, libcxx-abi, libcxx and the MS ucrt. +# +# This script is windows specific; however, a similar process +# could be used to cross-compile from linux. +# +# The process *should be* to first build a window-itanium clang +# and then do a stand-alone compilation of each of the +# libraries using that compiler. However, this doesn't +# work (currently) due to the lack of a suitable driver for +# windows-itanium (in the clang driver code). Instead we build +# an msvc targeting clang and then we invoke that clang but force +# the triple to windows-itanium using the -cc1 layer -triple option. +# +# This script automates a build of the required components using cmake +# and ninja. Build products are left in the source tree for simplicity. The +# result is a "wi" directory containing the toolchain binaries, include +# files and libraries. Due to the lack of a suitable windows-itanium +# driver this script creates a .bat file in the toolchain bin directory +# named "wi.bat" which assists in driving the windows-itanium compiler. +# +# usage: +# 1. Ensure pre-requisites are available as described in "pre-requisites:". +# 2. cd into a git workspace and apply the patch from D88124. +# 3. Manually fix up "locale_win32.h" (see notes in the patch). +# 4. Run "wi.py" to build. +# +# Note: The "--clean-all" option can be supplied to force a rebuild. +# +# pre-requisites: +# This script assumes it's run from within an llvm git checkout (tested +# using: 4cb016cd2d8467c572b2e5c5d34f376ee79e4ac1) with the patch from +# https://reviews.llvm.org/D88124 applied. +# +# Ninja and cmake must be on your path. Tested with: +# - Ninga version v1.10.1, binaries from https://github.com/ninja-build/ninja/releases +# - cmake version 3.18.2, binary zip from https://cmake.org/download/ +# +# Note: We need a version of cmake that is high enough to have: +# https://gitlab.kitware.com/cmake/cmake/-/issues/16439 +# +# The build needs to be able to find the installed windows sdk. I tested +# the script with an sdk installed via VS2017. The following include files +# and library locations were added when running vcvarsall.bat on my system: +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\ATLMFC\\include +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\include +# C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17763.0\\ucrt +# C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17763.0\\shared +# C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17763.0\\um +# C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17763.0\\winrt +# C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17763.0\\cppwinrt +# +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\ATLMFC\\lib\\x64 +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\lib\\x64 +# C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.17763.0\\ucrt\\x64 +# C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.17763.0\\um\\x64 +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\ATLMFC\\lib\\x64 +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\lib\\x64 +# C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Tools\\MSVC\\14.16.27023\\lib\\x86\\store\\references +# C:\\Program Files (x86)\\Windows Kits\\10\\UnionMetadata\\10.0.17763.0 +# C:\\Program Files (x86)\\Windows Kits\\10\\References\\10.0.17763.0 +# C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319 +# +# Notes on inter-dependencies: +# libunwind can be built as a dll and is not dependent on other projects. +# +# In libcxx/include/exception the "class _LIBCPP_TYPE_VIS exception_ptr" is declared. This has these three +# functions +# exception_ptr(const exception_ptr&) _NOEXCEPT; +# exception_ptr& operator=(const exception_ptr&) _NOEXCEPT; +# ~exception_ptr() _NOEXCEPT; +# +# This header is included when building stdlib_exception.cpp in libcxxabi. The definitions of the above three +# functions are in libcxx not libcxxabi. Therefore, there is a circular dependency and you get a link error when +# trying to link libcxxabi into a DLL. Maybe this works with other itanium toolchains as they would generate +# ELF output and the missing symbols would become references to be resolved later by the dynamic linker? We +# tackle this by statically linking libcxxabi into libcxx when building the libcxx DLL. +# See: https://reviews.llvm.org/D90021 for a discussion on this way of building libcxx. +# + +import os +import shutil +import stat +import subprocess +import sys +import winreg + +curr_dir = os.getcwd() + +def do_exit(exit_code): + print('Build {}'.format('failed' if exit_code != 0 else 'succeeded')) + exit(exit_code) + +def remove_file(file): + if os.path.exists(file): + os.remove(file) + +def remove_readonly(func, path, _): + "Clear the readonly bit and reattempt the removal" + os.chmod(path, stat.S_IWRITE) + func(path) + +def remove_dir(dir): + if os.path.exists(dir): + shutil.rmtree(dir, onerror = remove_readonly) + +def run_cmd(cmds, bat_file, input=None): + with open('{}'.format(bat_file), 'w') as tf: + for cmd in cmds: + tf.write('{}\n@if %ERRORLEVEL% NEQ 0 exit %ERRORLEVEL%\n\n'.format(cmd)); + tf.write('exit %ERRORLEVEL%\n') + tf.flush() + + proc = subprocess.Popen(['cmd', '/c', tf.name], stdin = subprocess.PIPE) + proc.communicate(input.encode() if input else None) + if proc.returncode != 0: + do_exit(proc.returncode) + +vcvars_cmd_line = None +def get_vcvars_cmd(): + global vcvars_cmd_line + if not vcvars_cmd_line: + try: + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7') as key: + path = winreg.QueryValueEx(key, r'15.0') + vcvars_cmd_line = '@call "' + path[0] + r'VC\Auxiliary\Build\vcvarsall.bat" amd64' + except: + vcvars_cmd_line = r'@echo "Warning: could not find vcvarsall.bat"' + return vcvars_cmd_line + +build_dirs_name = 'build-wi' + +toolchain_build_path = os.path.join(curr_dir, build_dirs_name) + +install_path = os.path.join(curr_dir, 'wi') + +toolchain_bin_path = os.path.join(install_path, 'bin') + +def build_clang_and_lld(): + ''' + Build a windows-msvc (default triple) toolchain + with support for the windows-itanium triple. See comments + on the lack of a suitable windows-itanium driver elsewhere. + ''' + if not os.path.exists(toolchain_build_path): + os.mkdir(toolchain_build_path) + + cd_build_dir = '@cd {}'.format(toolchain_build_path) + + cmake_cmd = ' cmake' + cmake_cmd += ' -GNinja' + cmake_cmd += ' -DCMAKE_CXX_COMPILER=cl' + cmake_cmd += ' -DCMAKE_C_COMPILER=cl' + cmake_cmd += ' -DCMAKE_MAKE_PROGRAM=ninja' + cmake_cmd += ' -DLLVM_TARGETS_TO_BUILD:STRING=X86' + cmake_cmd += ' -DLLVM_OPTIMIZED_TABLEGEN:BOOL=ON' + cmake_cmd += ' -DLLVM_ENABLE_ASSERTIONS=ON' + cmake_cmd += ' -DLLVM_INCLUDE_EXAMPLES=OFF' + cmake_cmd += ' -DLLVM_ENABLE_TIMESTAMPS=OFF' + cmake_cmd += ' -DLLVM_VERSION_SUFFIX=' + cmake_cmd += ' -DLLVM_BUILD_RUNTIME=OFF' + cmake_cmd += ' -DLLVM_APPEND_VC_REV=OFF' + cmake_cmd += ' -DLLVM_ENABLE_PROJECTS="clang;lld"' + cmake_cmd += ' -DCMAKE_BUILD_TYPE=Release' + cmake_cmd += ' -DCLANG_ENABLE_STATIC_ANALYZER=OFF' + cmake_cmd += ' -DCLANG_ENABLE_ARCMT=OFF' + cmake_cmd += ' ../llvm' + cmake_cmd += ' -DCMAKE_INSTALL_PREFIX=\"{}\"'.format(install_path) + + run_cmd([get_vcvars_cmd(), + cd_build_dir, + cmake_cmd, + 'ninja -v', + 'ninja install'], + 'toolchain.bat') + +ar_bin = os.path.join(toolchain_bin_path, 'llvm-ar') +ranlib_bin = os.path.join(toolchain_bin_path, 'llvm-ranlib') + +unwind_build_dir = os.path.join('libunwind', build_dirs_name) + +llvm_dir = os.path.join(curr_dir, 'llvm') + +install_libs_path = os.path.join(install_path, 'lib') +install_bin_path = os.path.join(install_path, 'bin') + +# EXPLAIN: Use -Xclang -triple rather than -target (see other comments about the lack of a suitable windows-itanium driver.) +common_clang_options = ' -Xclang -triple -Xclang x86_64-unknown-windows-itanium' + +# EXPLAIN: My understanding is that we shouldn't need to set any _LIBCPP_* flags when +# building libcxxabi. Unfortunately, I found that if you don't set this then you get +# compiler errors for "missing refstring" because refstring is declared in libcxx/include/stdexcept +# but the declaration is guarded by: #ifndef _LIBCPP_ABI_VCRUNTIME and _LIBCPP_ABI_VCRUNTIME is on. This +# seemed the best way to force it off. It also seems right as it makes sense to set this for windows-itanium. +common_clang_options += ' -D_LIBCPP_ABI_FORCE_ITANIUM' + +# EXPLAIN: We rather crudely specify linker options even when doing compilation. +# suppress warnings about this. +common_clang_options += ' -Qunused-arguments' + +# EXPLAIN: Use -fsjlj-exceptions for now! +# TODO: Get DWARF zero cost exceptions working properly! +common_clang_options += ' -fsjlj-exceptions' + +clang_builtins_path = os.path.join(toolchain_build_path, 'lib', 'clang', '12.0.0', 'include') + +def build_unwind(): + if not os.path.exists(unwind_build_dir): + os.mkdir(unwind_build_dir) + + if not os.path.exists(install_path): + os.mkdir(install_path) + + cd_build_dir = '@cd {}'.format(unwind_build_dir) + + run_cmake = 'cmake' + run_cmake += ' --debug-trycompile' + run_cmake += ' -G \"Ninja\"' + run_cmake += ' -DCMAKE_MAKE_PROGRAM=ninja' + run_cmake += ' -DCMAKE_BUILD_TYPE=Release' + run_cmake += ' -DCMAKE_INSTALL_PREFIX=\"{}\"'.format(install_path) + run_cmake += ' -DCMAKE_C_COMPILER=clang' + run_cmake += ' -DCMAKE_CXX_COMPILER=clang' + run_cmake += ' -DCMAKE_CROSSCOMPILING=ON' + run_cmake += ' -DCMAKE_SYSTEM_NAME=Windows' + run_cmake += ' -DCMAKE_C_COMPILER_WORKS=ON' + run_cmake += ' -DCMAKE_CXX_COMPILER_WORKS=ON' + run_cmake += ' -DLLVM_PATH=\"{}\"'.format(llvm_dir) + run_cmake += ' -DLLVM_COMPILER_CHECKED=ON' + run_cmake += ' -DCMAKE_AR=\"{}\"'.format(ar_bin) + run_cmake += ' -DCMAKE_RANLIB=\"{}\"'.format(ranlib_bin) + run_cmake += ' -DCXX_SUPPORTS_CXX11=ON' + run_cmake += ' -DCXX_SUPPORTS_CXX_STD=ON' + run_cmake += ' -DLIBUNWIND_USE_COMPILER_RT=OFF' + run_cmake += ' -DLIBUNWIND_ENABLE_THREADS=OFF' + run_cmake += ' -DLIBUNWIND_ENABLE_SHARED=ON' + run_cmake += ' -DLIBUNWIND_ENABLE_STATIC=OFF' + run_cmake += ' -DLIBUNWIND_ENABLE_CROSS_UNWINDING=OFF' + run_cmake += ' -DLIBUNWIND_INSTALL_LIBRARY=ON' + + # EXPLAIN: I added this flag to the cmake files to prevent cmake adding + # the following libraries: -lc.lib -lgcc_s.lib -lgcc.lib -ldl.lib. I presume + # that building for windows is just not a thing that libunwind really supports? + # Can we upstream something similar? + run_cmake += ' -DLIBUNWIND_NO_LIBRARIES=ON' + + # EXPLAN: Adding these two libraries fixes the following error during linking: + # lld-link: warning: undefined symbol: fprintf + # >>> referenced by src/CMakeFiles/unwind_shared.dir/Unwind-sjlj.c.obj:(_Unwind_SjLj_RaiseException) + # >>> referenced 33 more times + libunwind_c_flags = ' -lmsvcrt -llegacy_stdio_definitions' + + # EXPLAN: We don't want the inline stdio functions as they will casuse multiple + # defintiion errors when the same symbols are pulled in from "legacy_stdio_definitions" + libunwind_c_flags += ' -D_NO_CRT_STDIO_INLINE' + + run_cmake += ' -DCMAKE_CXX_FLAGS=\"-Wno-dll-attribute-on-redeclaration {} {}\"'.format(common_clang_options, libunwind_c_flags) + run_cmake += ' -DCMAKE_C_FLAGS=\"-Wno-dll-attribute-on-redeclaration {} {}\"'.format(common_clang_options, libunwind_c_flags) + + # EXPLAIN: Don't run the linker on compiler check + run_cmake += ' -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY' + run_cmake += ' ..' + + run_cmd([get_vcvars_cmd(), + 'set PATH={};%PATH%'.format(toolchain_bin_path), + cd_build_dir, + run_cmake, + 'ninja -v', + 'ninja install'], + 'libunwind.bat') + + # TODO: Why doesn't "ninja install" put the dll + # into the correct install location? Currently, it + # doesn't so we manually do this here: + if not os.path.exists(install_bin_path): + os.mkdir(install_bin_path) + built_dll = os.path.join(unwind_build_dir, 'lib', 'unwind.dll') + installed_dll = os.path.join(install_bin_path, 'unwind.dll') + shutil.copyfile(built_dll, installed_dll) + +cxx_build_dir = os.path.join('libcxx', build_dirs_name) + +def build_cxx(): + if not os.path.exists(cxx_build_dir): + os.mkdir(cxx_build_dir) + + cd_build_dir = '@cd {}'.format(cxx_build_dir) + + run_cmake = 'cmake' + # EXPLAIN: --debug-trycompile leaves around the files and dirs that cmake + # uses to decide which features are supported. This process can easily go + # awry due to setting incorrect flags here and leaving this stuff around + # allows for investigation. + run_cmake += ' --debug-trycompile' + run_cmake += ' -G \"Ninja\"' + run_cmake += ' -DCMAKE_MAKE_PROGRAM=ninja' + run_cmake += ' -DCMAKE_BUILD_TYPE=Release' + run_cmake += ' -DCMAKE_INSTALL_PREFIX=\"{}\"'.format(install_path) + run_cmake += ' -DCMAKE_C_COMPILER=clang' + run_cmake += ' -DCMAKE_CXX_COMPILER=clang' + run_cmake += ' -DCMAKE_CROSSCOMPILING=ON' + run_cmake += ' -DCMAKE_SYSTEM_NAME=Windows' + run_cmake += ' -DCMAKE_C_COMPILER_WORKS=ON' + run_cmake += ' -DCMAKE_CXX_COMPILER_WORKS=ON' + run_cmake += ' -DLLVM_PATH=\"{}\"'.format(llvm_dir) + run_cmake += ' -DLLVM_COMPILER_CHECKED=ON' + run_cmake += ' -DCMAKE_AR=\"{}\"'.format(ar_bin) + run_cmake += ' -DCMAKE_RANLIB=\"{}\"'.format(ranlib_bin) + run_cmake += ' -DLIBCXX_INSTALL_HEADERS=ON' + run_cmake += ' -DLIBCXX_ENABLE_SHARED=ON' + run_cmake += ' -DLIBCXX_ENABLE_STATIC=OFF' + run_cmake += ' -DLIBCXX_ENABLE_EXCEPTIONS=ON' + + # EXPLAIN: for windows-itanium we want to use the MS supplied runtime. + run_cmake += ' -DLIBCXX_USE_COMPILER_RT=OFF' + + # EXPLAIN: turn threads off for now - hopefully simplifies the build. + # TODO: enable threads + run_cmake += ' -DLIBCXX_ENABLE_THREADS=OFF' + + # EXPLAIN: _LIBCPP_HAS_THREAD_API_WIN32 can't be set if -DLIBCXXABI_ENABLE_THREADS" is "OFF" + # run_cmake += ' -DLIBCXX_HAS_WIN32_THREAD_API=ON' + + run_cmake += ' -DLIBCXX_ENABLE_MONOTONIC_CLOCK=ON' + run_cmake += ' -DLIBCXX_SUPPORTS_STD_EQ_CXX11_FLAG=ON' + run_cmake += ' -DLIBCXX_HAVE_CXX_ATOMICS_WITHOUT_LIB=ON' + run_cmake += ' -DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=OFF' + run_cmake += ' -DLIBCXX_ENABLE_FILESYSTEM=OFF' + run_cmake += ' -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON' + run_cmake += ' -DLIBCXX_CXX_ABI=libcxxabi' + run_cmake += ' -DLIBCXX_CXX_ABI_INCLUDE_PATHS=../../libcxxabi/include' + run_cmake += ' -DLIBCXX_CXX_ABI_LIBRARY_PATH=../../libcxxabi/{}/lib'.format(build_dirs_name) + run_cmake += ' -DLIBCXX_LIBDIR_SUFFIX=""' + run_cmake += ' -DLIBCXX_INCLUDE_TESTS=OFF' + run_cmake += ' -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT=OFF' + + # EXPLAIN: Try to remove any dependency on the VC runtime - we + # need libc++abi to supply the C++ runtime. + run_cmake += ' -DLIBCXX_NO_VCRUNTIME=ON' + + # EXPLAIN: As we are statically linking against libcxxabi we need + # to link against the unwind import library to resolve + # unwind references from the libcxxabi objects. + libcxx_c_flags = ' {}'.format(os.path.join(install_path, 'lib', 'unwind.lib')) + + # EXPLAIN: force this to be undefined to prevent the inclusion of sys/time that MS doesn't provide. + libcxx_c_flags += ' -UCLOCK_REALTIME' + + libcxx_c_flags += ' -fuse-ld=link' + + run_cmake += ' -DCMAKE_CXX_FLAGS=\"{} {}\"'.format(common_clang_options, libcxx_c_flags) + run_cmake += ' -DCMAKE_C_FLAGS=\"{} {}\"'.format(common_clang_options, libcxx_c_flags) + + # EXPLAIN: Don't run the linker on CMAKE compiler check. + run_cmake += ' -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY' + + run_cmake += ' ..' + + run_cmd(['set PATH={};%PATH%'.format(toolchain_bin_path), + cd_build_dir, + run_cmake, + 'ninja -v', + 'ninja install'], + 'libcxx.bat') + +cxxabi_build_dir = os.path.join('libcxxabi', build_dirs_name) + +def build_cxxabi(): + if not os.path.exists(cxxabi_build_dir): + os.mkdir(cxxabi_build_dir) + + cd_build_dir = '@cd {}'.format(cxxabi_build_dir) + + run_cmake = 'cmake' + run_cmake += ' --debug-trycompile' + run_cmake += ' -G \"Ninja\"' + run_cmake += ' -DCMAKE_MAKE_PROGRAM=ninja' + run_cmake += ' -DCMAKE_BUILD_TYPE=Release' + run_cmake += ' -DCMAKE_INSTALL_PREFIX=\"{}\"'.format(install_path) + run_cmake += ' -DCMAKE_C_COMPILER=clang' + run_cmake += ' -DCMAKE_CXX_COMPILER=clang' + run_cmake += ' -DCMAKE_CROSSCOMPILING=ON' + run_cmake += ' -DCMAKE_SYSTEM_NAME=Windows' + run_cmake += ' -DCMAKE_C_COMPILER_WORKS=ON' + run_cmake += ' -DCMAKE_CXX_COMPILER_WORKS=ON' + run_cmake += ' -DLLVM_PATH=\"{}\"'.format(llvm_dir) + run_cmake += ' -DLLVM_COMPILER_CHECKED=ON' + run_cmake += ' -DCMAKE_AR=\"{}\"'.format(ar_bin) + run_cmake += ' -DCMAKE_RANLIB=\"{}\"'.format(ranlib_bin) + run_cmake += ' -DLIBCXXABI_ENABLE_EXCEPTIONS=ON' + + # EXPLAIN: we want to use the MS CRT. + run_cmake += ' -DLIBUNWIND_USE_COMPILER_RT=OFF' + + # EXPLAIN: turn threads off - hopefully simplifies the build. + # TODO: enable threads + run_cmake += ' -DLIBCXXABI_ENABLE_THREADS=OFF' + + run_cmake += ' -DLIBCXXABI_ENABLE_SHARED=OFF' + run_cmake += ' -DLIBCXXABI_ENABLE_STATIC=ON' + + # EXPLAIN: Add visibility annotations as if building a dll. + # This cmake option does not exist upstream. See + # comments in code changes for details. + run_cmake += ' -LIBCXXABI_ENABLE_STATIC_FOR_LIBCXX_SHARED=ON' + + run_cmake += ' -DLIBCXXABI_LIBCXX_INCLUDES=../../libcxx/include' + run_cmake += ' -DLIBCXXABI_LIBDIR_SUFFIX=""' + run_cmake += ' -DLIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS=ON' + run_cmake += ' -DCXX_SUPPORTS_CXX_STD=ON' + + # EXPLAIN: Unless we force this off then cxxabi will contain + # references to threading symbols that will not be defined + # by libc++ e.g: + # lld-link: error: undefined symbol: std::__1::__libcpp_thread_yield() + # >>> referenced by c++abi.lib(cxa_default_handlers.cpp.obj):(std::__1::__libcpp_timed_backoff_policy::operator()(std::__1::chrono::duration >) const) + # >>> referenced by c++abi.lib(cxa_demangle.cpp.obj) + # >>> referenced by c++abi.lib(cxa_exception_storage.cpp.obj) + libcxxabi_c_flags = ' -D_LIBCPP_HAS_NO_THREADS' + + libcxxabi_c_flags += ' -fuse-ld=link' + + # EXPLAIN: Use -Xclang -triple rather than -target to avoid: clang: error: invalid linker name in argument '-fuse-ld=lld-link' + run_cmake += ' -DCMAKE_CXX_FLAGS=\"{} {}\"'.format(libcxxabi_c_flags, common_clang_options) + run_cmake += ' -DCMAKE_C_FLAGS=\"{} {}\"'.format(libcxxabi_c_flags, common_clang_options) + + run_cmake += ' ..' + + run_cmd(['set PATH={};%PATH%'.format(toolchain_bin_path), + cd_build_dir, + run_cmake, + 'ninja -v'], + 'libcxxabi.bat') + +def clang_wi_args(): + return [ + os.path.join(toolchain_bin_path, 'clang.exe') + ,'-fuse-ld=lld-link' + ,'-Xclang', '-triple', '-Xclang', 'x86_64-unknown-windows-itanium' + ,'-fsjlj-exceptions' + ,'-isystem', os.path.join(install_path, 'include') + ,'-isystem', os.path.join(install_path, 'include', 'c++', 'v1') + ,'-lkernel32' + ,'-luser32' + ,'-lgdi32' + ,'-lwinspool' + ,'-lshell32' + ,'-lole32' + ,'-loleaut32' + ,'-luuid' + ,'-lcomdlg32' + ,'-ladvapi32' + ,'-loldnames' + ,os.path.join(install_path, 'lib', 'c++.lib') + ,os.path.join(install_path, 'lib', 'unwind.lib') + ,'-D_LIBCPP_HAS_NO_THREADS' + ,'-Wl,/nodefaultlib' + ,'-lmsvcrt' + ,'-lucrt' + ,'-lvcruntime' + # EXPLAIN: Use autoimporting to resolve references to + # "vtable for cxxabiv1::class_type_info" from typeinfos. The symptom was linking errors like: + # lld-link: error: undefined symbol: vtable for cxxabiv1::class_type_info + # referenced by plugin-8ae1e2.o:(typeinfo for Foo) + # The dll_class test case will fail without this. + # Have to use mingw here as using -auto-import diagnoses a problem: + # "lld-link: error: automatic dllimport of _ZTVN10__cxxabiv117__class_type_infoE in plugin-f2d5e0.o requires pseudo relocations" + # This allows for linking but code that actually uses such fields will not work + # as they these will not be fixed up at runtime: see _pei386_runtime_relocator which + # handles the runtime component of the autoimporting scheme used for mingw. + # See: https://reviews.llvm.org/D43184 and comments in https://reviews.llvm.org/D89518 for more. + # TODO: Address this limitation. + ,'-Wl,-lldmingw'] + +def write_driver_script(): + script = get_vcvars_cmd() + script += '\nset PATH={};%PATH%\n'.format(os.path.join(install_path, 'bin')) + for a in clang_wi_args(): + script += ' {} ^\n'.format(a) + script += ' %*' + + with open('{}'.format(os.path.join(install_path, 'bin', 'clang-wi.bat')), 'w') as file: + file.write(script) + +def write_lit_cfg(): + cmdline = '' + for a in clang_wi_args(): + cmdline += '\"{}\" '.format(a.replace('\\','\\\\')) + + with open('{}'.format('wi-test/lit.site.cfg.py'), 'w') as cfg: + cfg.write(r''' +config.substitutions.append(('%{{clang-wi}}',r'{}')) + +import sys +import lit.llvm +# lit.llvm.initialize(lit_config, config) + +# Let the main config do the real work. +lit_config.load_config(config, "./lit.cfg") +'''.format(cmdline)) + +def run_tests(action): + run_cmd([get_vcvars_cmd(), + 'set PATH={};%PATH%'.format(toolchain_bin_path), + 'cd wi-test', + 'python ..\\llvm\\utils\\lit\\lit.py --path ..\\build-wi\\bin\\ test'], + '{}.bat'.format(action)) + +if '--clean-libs' in sys.argv or '--clean-all' in sys.argv: + remove_file('toolchain.bat') + remove_file('libunwind.bat') + remove_file('libcxxabi.bat') + remove_file('libcxx.bat') + remove_file('test.bat') + remove_file('clean.bat') + remove_file(os.path.join(install_libs_path, 'unwind.lib')) + remove_file(os.path.join(install_bin_path, 'unwind.dll')) + remove_file(os.path.join(install_libs_path, 'c++.lib')) + remove_file(os.path.join(install_bin_path, 'c++.dll')) + remove_dir(unwind_build_dir) + remove_dir(cxxabi_build_dir) + remove_dir(cxx_build_dir) + run_tests('clean') + +if '--clean-all' in sys.argv: + remove_dir(toolchain_build_path) + remove_dir(install_path) + +if '--debug' in sys.argv: + # EXPLAIN: produce more debugging output from the linker in the log files. + common_clang_options += ' -Xlinker /verbose' + + # EXPLAIN: Log which include files were pulled in. + common_clang_options += ' -H' + + # EXPLAIN: Save the preprocessed output files next to the object files + # Note: I have seen this option cause comdat errors to be reported. + # It seems that source -> assembly -> obj may report comdat + # errors (when assembling) that are not reported doing source-> obj. + # Example: + # src/CMakeFiles/cxx_shared.dir\locale.s:107958:2: error: section '.xdata$' is already linkonce + # .linkonce discard + # TODO: Investigate these comdat errors. + # Note: Comdat errors only occur for the libc++ build. Building the Uwind + # and c++abi libs doesn't show this problem. + common_clang_options += ' -save-temps=obj' + +build_clang_and_lld() +build_unwind() +build_cxxabi() +build_cxx() +write_driver_script() +write_lit_cfg() +run_tests('test') +do_exit(0)