diff --git a/Dockerfile b/Dockerfile index fcc2860..7790074 100644 --- a/Dockerfile +++ b/Dockerfile @@ -301,7 +301,6 @@ RUN (cd /dl/gcc \ --disable-libstdcxx-verbose \ --disable-dependency-tracking \ --disable-nls \ - --disable-lto \ CFLAGS_FOR_TARGET="-O2" \ CXXFLAGS_FOR_TARGET="-O2" \ LDFLAGS_FOR_TARGET="-s" \ @@ -507,6 +506,15 @@ RUN if [ "$GCC_MULTILIB" = enable ]; then \ WORKDIR /gcc COPY src/gcc-final/ $PREFIX/src/gcc-final/ +# libstdc++, libgfortran, and libquadmath are built as fat LTO objects: +# they link normally without -flto, but an -flto link can inline into them +# and drop their unused functions, which --gc-sections cannot do on PE. +# libstdc++ is the only C++ target library, so CXXFLAGS_FOR_TARGET reaches +# it alone. +# libgfortran and libquadmath share CFLAGS_FOR_TARGET with libgcc, libgomp, +# and libatomic, which must stay plain, so they are rebuilt afterward with +# their own flags. The LTO plugin is also installed where ar, nm, and +# ranlib find it, so they can handle slim LTO objects without gcc-ar. RUN (cd /dl/gcc \ && QUILT_PATCHES=$PREFIX/src/gcc-final quilt push -a \ && rm -rf .pc) \ @@ -529,22 +537,26 @@ RUN (cd /dl/gcc \ --enable-threads=posix \ --enable-tls \ --enable-version-specific-runtime-libs \ + --disable-libstdcxx-dual-abi \ --disable-libstdcxx-verbose \ --disable-dependency-tracking \ - --disable-lto \ --disable-nls \ --disable-win32-registry \ $GCC_MANIFEST_FLAG \ --enable-mingw-wildcard \ CFLAGS_FOR_TARGET="-O2" \ - CXXFLAGS_FOR_TARGET="-O2" \ + CXXFLAGS_FOR_TARGET="-O2 -flto -ffat-lto-objects" \ LDFLAGS_FOR_TARGET="-s" \ CFLAGS="-O2" \ CXXFLAGS="-O2" \ LDFLAGS="-s" \ && make -j$(nproc) \ + && make clean-target-libquadmath clean-target-libgfortran \ + && make -j$(nproc) all-target-libquadmath all-target-libgfortran \ + CFLAGS_FOR_TARGET="-O2 -flto -ffat-lto-objects" \ && make install \ - && rm -f $PREFIX/bin/ld.bfd.exe \ + && cp $PREFIX/libexec/gcc/$ARCH/*/liblto_plugin.dll $PREFIX/lib/bfd-plugins/ \ + && rm -f $PREFIX/bin/ld.bfd.exe $PREFIX/bin/lto-dump.exe \ && $ARCH-gcc -DEXE=g++.exe -DCMD=c++ \ -Oz -fno-asynchronous-unwind-tables \ -Wl,--gc-sections -s -nostdlib \ diff --git a/src/binutils/coff-comdat-key-leading-char.patch b/src/binutils/coff-comdat-key-leading-char.patch new file mode 100644 index 0000000..1ac7b6e --- /dev/null +++ b/src/binutils/coff-comdat-key-leading-char.patch @@ -0,0 +1,36 @@ +Match COFF COMDAT keys between real objects and LTO IR on i386 + +The linker deduplicates COMDAT sections by key. For a real COFF +section the key is its COMDAT symbol name, which on i386 PE carries the +user label prefix (__ZTISt9bad_alloc). For a symbol from an LTO IR +object, ld creates a placeholder section named after the COMDAT group +the compiler reported, which is the group name without any prefix +(_ZTISt9bad_alloc). The two never match, so whenever a symbol is +defined both by a regular object and by LTO bytecode, ld reports a +multiple definition instead of keeping one copy. That is every -flto +link on i686 once libstdc++ carries bytecode, since libsupc++ and the +CRT are regular objects that define the same typeinfo and template +instantiations, and it also breaks linking a plain object into an -flto +program. x86_64 PE has no prefix and is unaffected. + +Strip the target's symbol leading char from keys derived from real +objects so that they match the compiler's group names. + +--- a/bfd/coffgen.c ++++ b/bfd/coffgen.c +@@ -2803,6 +2803,15 @@ + key = name; + } + ++ /* LTO IR plugin sections are keyed by the COMDAT group name as the ++ compiler knows it, without the symbol leading char that COMDAT ++ symbols in real objects carry on targets like i386 PE. Strip it ++ from keys derived from real objects so both kinds match. */ ++ if ((abfd->flags & BFD_PLUGIN) == 0 ++ && key[0] != '\0' ++ && key[0] == bfd_get_symbol_leading_char (abfd)) ++ key++; ++ + already_linked_list = bfd_section_already_linked_table_lookup (key); + if (!already_linked_list) + goto bad; diff --git a/src/binutils/pe-lto-no-export-library-symbols.patch b/src/binutils/pe-lto-no-export-library-symbols.patch new file mode 100644 index 0000000..cdbd042 --- /dev/null +++ b/src/binutils/pe-lto-no-export-library-symbols.patch @@ -0,0 +1,295 @@ +Don't export library symbols from DLLs built with LTO + +By default a DLL exports every global symbol, minus those from the +standard libraries, startup objects, and (here) any archive under the +sysroot, which ld filters by the symbol's archive of origin, and minus +weak symbols, which is what COMDAT template instantiations are. LTO +defeated all of that: a DLL using std::string exported 704 symbols +instead of 2, with every one of them pinned against dead-code +elimination. + +The exports came from the placeholder bfds that stand in for claimed +IR objects. They stay in the input list after LTO, still listing every +definition the plugin reported as a strong global, and they have no +archive to be excluded by. Skip them in the export loop; the definitions +that survive LTO are in the output objects the plugin added, and only +those count. + +That alone would still export what the compiler keeps global, since ld +tells the plugin that in a DLL link every symbol may be visible from +outside. Add an emulation hook consulted during symbol resolution, which +the PE emulations answer with the same auto-export decision, using the +IR object's original archive member and treating weak or COMDAT +definitions as unexportable. A library symbol is then reported as not +visible, so the compiler localizes and prunes it as it would in an +executable (the test DLL halves in size), and its export name is +remembered so that auto-export also rejects the ones that survive as +globals because regular objects reference them. Symbols listed in a +.def file remain exported, and dllexport attributes are unaffected since +the compiler honors them regardless of resolution. + +--- a/ld/emultempl/emulation.em ++++ b/ld/emultempl/emulation.em +@@ -37,6 +37,7 @@ + ${LDEMUL_ACQUIRE_STRINGS_FOR_CTF-NULL}, + ${LDEMUL_NEW_DYNSYM_FOR_CTF-NULL}, + ${LDEMUL_PRINT_SYMBOL-NULL}, +- ${LDEMUL_FIND_START_SYMBOL-NULL} ++ ${LDEMUL_FIND_START_SYMBOL-NULL}, ++ ${LDEMUL_LTO_SYMBOL_VISIBLE-NULL} + }; + EOF +--- a/ld/emultempl/pe.em ++++ b/ld/emultempl/pe.em +@@ -1463,6 +1463,17 @@ + #endif + } + ++static bool ++gld${EMULATION_NAME}_lto_symbol_visible (bfd *abfd, const char *name, ++ bool weak) ++{ ++#ifdef DLL_SUPPORT ++ return pe_dll_lto_symbol_visible (abfd, name, weak); ++#else ++ return true; ++#endif ++} ++ + static void + gld${EMULATION_NAME}_after_open (void) + { +@@ -2572,6 +2583,7 @@ + + LDEMUL_AFTER_PARSE=gld${EMULATION_NAME}_after_parse + LDEMUL_BEFORE_PLUGIN_ALL_SYMBOLS_READ=gld${EMULATION_NAME}_before_plugin_all_symbols_read ++LDEMUL_LTO_SYMBOL_VISIBLE=gld${EMULATION_NAME}_lto_symbol_visible + LDEMUL_AFTER_OPEN=gld${EMULATION_NAME}_after_open + LDEMUL_BEFORE_ALLOCATION=gld${EMULATION_NAME}_before_allocation + LDEMUL_FINISH=gld${EMULATION_NAME}_finish +--- a/ld/emultempl/pep.em ++++ b/ld/emultempl/pep.em +@@ -1471,6 +1471,17 @@ + #endif + } + ++static bool ++gld${EMULATION_NAME}_lto_symbol_visible (bfd *abfd, const char *name, ++ bool weak) ++{ ++#ifdef DLL_SUPPORT ++ return pep_dll_lto_symbol_visible (abfd, name, weak); ++#else ++ return true; ++#endif ++} ++ + static void + gld${EMULATION_NAME}_after_open (void) + { +@@ -2364,6 +2375,7 @@ + + LDEMUL_AFTER_PARSE=gld${EMULATION_NAME}_after_parse + LDEMUL_BEFORE_PLUGIN_ALL_SYMBOLS_READ=gld${EMULATION_NAME}_before_plugin_all_symbols_read ++LDEMUL_LTO_SYMBOL_VISIBLE=gld${EMULATION_NAME}_lto_symbol_visible + LDEMUL_AFTER_OPEN=gld${EMULATION_NAME}_after_open + LDEMUL_BEFORE_ALLOCATION=gld${EMULATION_NAME}_before_allocation + LDEMUL_FINISH=gld${EMULATION_NAME}_finish +--- a/ld/ldemul.c ++++ b/ld/ldemul.c +@@ -74,6 +74,14 @@ + ld_emulation->before_plugin_all_symbols_read (); + } + ++bool ++ldemul_lto_symbol_visible (bfd *abfd, const char *name, bool weak) ++{ ++ if (ld_emulation->lto_symbol_visible) ++ return ld_emulation->lto_symbol_visible (abfd, name, weak); ++ return true; ++} ++ + void + ldemul_after_open (void) + { +--- a/ld/ldemul.h ++++ b/ld/ldemul.h +@@ -34,6 +34,8 @@ + (void); + extern void ldemul_before_plugin_all_symbols_read + (void); ++extern bool ldemul_lto_symbol_visible ++ (bfd *, const char *, bool); + extern void ldemul_after_open + (void); + extern void ldemul_after_check_relocs +@@ -265,6 +267,14 @@ + struct bfd_link_hash_entry * (*find_alt_start_symbol) + (struct bfd_sym_chain *entry); + ++ /* Called during LTO symbol resolution for a symbol defined in IR ++ file ABFD (the archive member when it came from one). WEAK says ++ whether the definition is weak or in a COMDAT group. Return false ++ if the symbol could never be visible from outside the output, e.g. ++ because it would not be exported from a DLL, so that the compiler ++ may localize it. */ ++ bool (*lto_symbol_visible) (bfd *abfd, const char *name, bool weak); ++ + } ld_emulation_xfer_type; + + typedef enum { +--- a/ld/pe-dll.c ++++ b/ld/pe-dll.c +@@ -41,6 +41,7 @@ + #include "coff/internal.h" + #include "../bfd/libcoff.h" + #include "deffile.h" ++#include "hashtab.h" + + #ifdef pe_use_plus + +@@ -612,6 +613,50 @@ + return (startswith (n, "__imp_")); + } + ++/* Names of symbols defined in LTO bytecode that auto-export would have ++ rejected had they come from a regular object, typically members of ++ the standard libraries. Recorded at symbol resolution, when their ++ origin is still known, and consulted when exporting the LTO output, ++ where it is not. */ ++static htab_t lto_unexported; ++ ++static int auto_export (bfd *, def_file *, const char *); ++ ++/* Called during LTO symbol resolution for NAME, defined in IR file ++ ABFD, weak or in a COMDAT group if WEAK. Return whether the symbol ++ could be exported from this DLL. */ ++ ++bool ++pe_dll_lto_symbol_visible (bfd *abfd, const char *name, bool weak) ++{ ++ if (!bfd_link_dll (&link_info)) ++ return true; ++ ++ if (pe_def_file == NULL) ++ pe_def_file = def_file_empty (); ++ ++ /* Export names, in a .def file and in auto_export below, lack the ++ leading underscore that symbols carry on underscored targets. */ ++ if (pe_details->underscored && name[0] == '_') ++ name++; ++ for (int i = 0; i < pe_def_file->num_exports; i++) ++ if (strcmp (pe_def_file->exports[i].name, name) == 0) ++ return true; ++ ++ /* Auto-export skips weak symbols, which is what COMDAT template ++ instantiations are in regular objects; after LTO the prevailing ++ copies would come out strong and be exported instead. */ ++ if (!weak && auto_export (abfd, pe_def_file, name)) ++ return true; ++ ++ if (lto_unexported == NULL) ++ lto_unexported = htab_create_alloc (1024, htab_hash_string, ++ htab_eq_string, NULL, ++ xcalloc, free); ++ *htab_find_slot (lto_unexported, name, INSERT) = (void *) name; ++ return false; ++} ++ + /* abfd is a bfd containing n (or NULL) + It can be used for contextual checks. */ + +@@ -623,6 +668,12 @@ + const autofilter_entry_type *afptr; + const char * libname = NULL; + ++ /* Symbols in LTO output have lost their origin; use what symbol ++ resolution recorded about them. */ ++ if (abfd && abfd->lto_output && lto_unexported ++ && htab_find (lto_unexported, n) != NULL) ++ return 0; ++ + if (abfd && abfd->my_archive) + libname = lbasename (bfd_get_filename (abfd->my_archive)); + +@@ -818,6 +869,12 @@ + asymbol **symbols; + int nsyms; + ++ /* An LTO IR placeholder still lists every symbol the plugin ++ reported, but the definitions that survive LTO are in the ++ output objects the plugin added, and only those count. */ ++ if ((b->flags & BFD_PLUGIN) != 0) ++ continue; ++ + if (!bfd_generic_link_read_symbols (b)) + { + fatal (_("%P: %pB: could not read symbols: %E\n"), b); +--- a/ld/pe-dll.h ++++ b/ld/pe-dll.h +@@ -46,6 +46,8 @@ + (const char *); + extern void pe_dll_add_excludes + (const char *, const exclude_type); ++extern bool pe_dll_lto_symbol_visible ++ (bfd *, const char *, bool); + extern void pe_dll_generate_def_file + (const char *); + extern void pe_dll_generate_implib +--- a/ld/pep-dll.c ++++ b/ld/pep-dll.c +@@ -53,6 +53,7 @@ + #define pe_exe_fill_sections pep_exe_fill_sections + #define pe_dll_generate_implib pep_dll_generate_implib + #define pe_dll_add_excludes pep_dll_add_excludes ++#define pe_dll_lto_symbol_visible pep_dll_lto_symbol_visible + #define pe_bfd_is_dll pep_bfd_is_dll + #define pe_output_file_set_long_section_names \ + pep_output_file_set_long_section_names +--- a/ld/pep-dll.h ++++ b/ld/pep-dll.h +@@ -45,6 +45,7 @@ + + extern void pep_dll_id_target (const char *); + extern void pep_dll_add_excludes (const char *, const exclude_type); ++extern bool pep_dll_lto_symbol_visible (bfd *, const char *, bool); + extern void pep_dll_generate_def_file (const char *); + extern void pep_dll_generate_implib (def_file *, const char *, struct bfd_link_info *); + extern void pep_process_import_defs (bfd *, struct bfd_link_info *); +--- a/ld/plugin.c ++++ b/ld/plugin.c +@@ -30,6 +30,7 @@ + #include "ldexp.h" + #include "ldlang.h" + #include "ldfile.h" ++#include "ldemul.h" + #include "plugin-api.h" + #include "../bfd/plugin.h" + #include "plugin.h" +@@ -880,6 +881,17 @@ + + if (res == LDPR_PREVAILING_DEF_IRONLY) + { ++ /* Let the emulation rule out visibility from outside, e.g. a ++ symbol that would never be exported from a DLL. Ask for every ++ prevailing IR definition, since the emulation may also use the ++ answer to filter exports after LTO. */ ++ bool visible ++ = ldemul_lto_symbol_visible (input->ibfd ? input->ibfd ++ : input->abfd, ++ blhe->root.string, ++ (syms[n].def == LDPK_WEAKDEF ++ || syms[n].comdat_key != NULL)); ++ + /* We need to know if the sym is referenced from non-IR files. Or + even potentially-referenced, perhaps in a future final link if + this is a partial one, perhaps dynamically at load-time if the +@@ -891,7 +903,7 @@ + res = LDPR_PREVAILING_DEF; + else if (wrap_status == wrapped) + res = LDPR_RESOLVED_IR; +- else if (is_visible_from_outside (&syms[n], blhe)) ++ else if (visible && is_visible_from_outside (&syms[n], blhe)) + res = def_ironly_exp; + } + diff --git a/src/binutils/pe-lto-no-relocator-ref-for-relocatable.patch b/src/binutils/pe-lto-no-relocator-ref-for-relocatable.patch new file mode 100644 index 0000000..3a02e62 --- /dev/null +++ b/src/binutils/pe-lto-no-relocator-ref-for-relocatable.patch @@ -0,0 +1,48 @@ +Since Binutils 2.41 (PR30343, commit defb881754d), whenever the LTO +plugin is loaded the PE linker unconditionally creates an undefined +reference to _pei386_runtime_relocator before the plugin's "all symbols +read" phase. Because it cannot yet know whether LTO output will need +runtime pseudo-relocations, it makes the reference anyway, marking the +symbol as referenced from a regular object so that an LTO-compiled +definition survives. In a final link this is harmless: the reference +resolves to pseudo-reloc.o in libmingw32.a, which the CRT startup drags +in regardless. + +In a relocatable link (-r) the reference has no definition to resolve +against and leaks into the output as a stray undefined symbol. Since GCC +passes the plugin to the linker for every link when built with LTO, this +affects even links with no LTO objects at all: + + $ cc -r -s -u ___chkstk_ms -o chkstk.o -lgcc + $ nm chkstk.o + 0000000000000000 T ___chkstk_ms + U _pei386_runtime_relocator + +An object linked with -r is never a final image, so no pseudo-relocation +will run from it. If it is later used in a final link, that link makes +its own reference. The early reference also buys nothing for LTO in a +relocatable link: the linker's plugin interface already reports every +symbol as visible from outside for relocatable output, so none resolve +as IR-only and GCC keeps them all. So skip the early reference for +relocatable output. + +--- a/ld/emultempl/pe.em ++++ b/ld/emultempl/pe.em +@@ -1457,6 +1457,7 @@ + { + #ifdef DLL_SUPPORT + if (link_info.lto_plugin_active ++ && !bfd_link_relocatable (&link_info) + && link_info.pei386_auto_import) + make_runtime_ref (); + #endif +--- a/ld/emultempl/pep.em ++++ b/ld/emultempl/pep.em +@@ -1465,6 +1465,7 @@ + { + #ifdef DLL_SUPPORT + if (link_info.lto_plugin_active ++ && !bfd_link_relocatable (&link_info) + && link_info.pei386_auto_import) /* -1=warn or 1=enable */ + make_runtime_ref (); + #endif diff --git a/src/binutils/series b/src/binutils/series index bd1a6f3..0c39eb4 100644 --- a/src/binutils/series +++ b/src/binutils/series @@ -3,3 +3,6 @@ exclude-sysroot.patch windmc-bom.patch windmc-carry-severity-facility.patch windmc-per-facility-ids.patch +pe-lto-no-relocator-ref-for-relocatable.patch +coff-comdat-key-leading-char.patch +pe-lto-no-export-library-symbols.patch diff --git a/src/gcc-final/driver-self-locate.patch b/src/gcc-final/driver-self-locate.patch index c91e169..ad27f19 100644 --- a/src/gcc-final/driver-self-locate.patch +++ b/src/gcc-final/driver-self-locate.patch @@ -7,7 +7,7 @@ in bin/ rather than a tooldir. With this patch the toolchain works without w64devkit bin/ in PATH. --- a/gcc/gcc.cc +++ b/gcc/gcc.cc -@@ -56,6 +56,11 @@ compilation is specified by a string called a "spec". */ +@@ -56,6 +56,11 @@ #include "opts-diagnostic.h" #include "auto-profile.h" /* for AUTO_PROFILE_VERSION. */ @@ -19,7 +19,7 @@ without w64devkit bin/ in PATH. #ifndef MATH_LIBRARY #define MATH_LIBRARY "m" #endif -@@ -5566,6 +5571,17 @@ process_command (unsigned int decoded_options_count, +@@ -5566,6 +5571,17 @@ "BINUTILS", PREFIX_PRIORITY_LAST, 0, 1); free (tooldir_prefix); @@ -37,7 +37,7 @@ without w64devkit bin/ in PATH. #if defined(TARGET_SYSTEM_ROOT_RELOCATABLE) && !defined(VMS) /* If the normal TARGET_SYSTEM_ROOT is inside of $exec_prefix, then consider it to relocate with the rest of the GCC installation -@@ -8307,6 +8323,14 @@ driver::main (int argc, char **argv) +@@ -8307,6 +8323,22 @@ bool early_exit; set_progname (argv[0]); @@ -47,7 +47,15 @@ without w64devkit bin/ in PATH. + static char self[260]; + unsigned selflen = GetModuleFileNameA (0, self, sizeof self); + if (selflen && selflen < sizeof self) -+ argv[0] = self; ++ { ++ /* Use forward slashes: this path becomes COLLECT_GCC, which ++ lto-wrapper writes unquoted into a Makefile for parallel LTO, ++ where a shell would consume backslashes. */ ++ for (char *p = self; *p; p++) ++ if (*p == '\\') ++ *p = '/'; ++ argv[0] = self; ++ } +#endif expand_at_files (&argc, &argv); decode_argv (argc, const_cast (argv)); diff --git a/src/gcc-final/i686-dumpmachine.patch b/src/gcc-final/i686-dumpmachine.patch index 3669a7e..5197d3a 100644 --- a/src/gcc-final/i686-dumpmachine.patch +++ b/src/gcc-final/i686-dumpmachine.patch @@ -7,7 +7,7 @@ compilers, and target-detecting build systems expect that prefix and invocation name. --- a/gcc/gcc.cc +++ b/gcc/gcc.cc -@@ -4249,6 +4249,13 @@ driver_handle_option (struct gcc_options *opts, +@@ -4254,6 +4254,13 @@ exit (0); case OPT_dumpmachine: diff --git a/src/gcc-final/libstdcxx-no-lto-punned-types.patch b/src/gcc-final/libstdcxx-no-lto-punned-types.patch new file mode 100644 index 0000000..d8985af --- /dev/null +++ b/src/gcc-final/libstdcxx-no-lto-punned-types.patch @@ -0,0 +1,97 @@ +Keep libsupc++ and ios_init.cc out of LTO bytecode + +Two parts of libstdc++ cannot safely be link-time optimized, even +though the rest of the library is built as fat LTO objects. + +libsupc++ is the C++ runtime proper. Compiler-generated code refers to +some of its entry points in ways the LTO symbol resolution never sees: +a must-not-throw landing pad calls __cxa_call_terminate, but that call +is only materialized late, in pass_lower_resx, after whole-program +analysis has already dropped the apparently unreferenced definition +from eh_call.o. An -flto link of a program using or +then fails with "defined in discarded section". Compile all of +libsupc++ without LTO, as libgcc is. + +ios_init.cc defines cout, cin, and the other standard streams with +deliberately unrelated types (character arrays sized and aligned like +the stream classes) to control their construction. That is a textbook +ODR violation that the compiler can only see across translation units +with LTO, and -Wodr then reports every one of them on every -flto link +that uses iostreams. Keep that one file plain as well. + +--- a/libstdc++-v3/libsupc++/Makefile.am ++++ b/libstdc++-v3/libsupc++/Makefile.am +@@ -199,11 +199,17 @@ + # 2) Need to explicitly set LTCXXCOMPILE so that EXTRA_CXX_FLAGS is + # last. (That way, things like -O2 passed down from the toplevel can + # be overridden by --enable-debug.) ++# ++# Never compile the C++ runtime to LTO bytecode: compiler-generated code ++# refers to some of its entry points (e.g. __cxa_call_terminate from ++# must-not-throw landing pads) only after whole-program analysis, by ++# which time the linker plugin may have discarded the definition. + LTCXXCOMPILE = \ + $(LIBTOOL) --tag CXX --tag disable-shared \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(TOPLEVEL_INCLUDES) \ +- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) $(EXTRA_CXX_FLAGS) ++ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) $(EXTRA_CXX_FLAGS) \ ++ -fno-lto + + LTLDFLAGS = $(shell $(SHELL) $(top_srcdir)/../libtool-ldflags $(LDFLAGS)) + +--- a/libstdc++-v3/libsupc++/Makefile.in ++++ b/libstdc++-v3/libsupc++/Makefile.in +@@ -639,11 +639,17 @@ + # 2) Need to explicitly set LTCXXCOMPILE so that EXTRA_CXX_FLAGS is + # last. (That way, things like -O2 passed down from the toplevel can + # be overridden by --enable-debug.) ++# ++# Never compile the C++ runtime to LTO bytecode: compiler-generated code ++# refers to some of its entry points (e.g. __cxa_call_terminate from ++# must-not-throw landing pads) only after whole-program analysis, by ++# which time the linker plugin may have discarded the definition. + LTCXXCOMPILE = \ + $(LIBTOOL) --tag CXX --tag disable-shared \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(TOPLEVEL_INCLUDES) \ +- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) $(EXTRA_CXX_FLAGS) ++ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) $(EXTRA_CXX_FLAGS) \ ++ -fno-lto + + LTLDFLAGS = $(shell $(SHELL) $(top_srcdir)/../libtool-ldflags $(LDFLAGS)) + +--- a/libstdc++-v3/src/c++98/Makefile.am ++++ b/libstdc++-v3/src/c++98/Makefile.am +@@ -200,6 +200,14 @@ + parallel_settings.o: parallel_settings.cc + $(CXXCOMPILE) $(PARALLEL_FLAGS) -c $< + ++# ios_init.cc deliberately defines the standard streams with types ++# unrelated to their declarations, so keep it out of LTO bytecode, where ++# -Wodr would report them on every link. ++ios_init.lo: ios_init.cc ++ $(LTCXXCOMPILE) -fno-lto -c $< ++ios_init.o: ios_init.cc ++ $(CXXCOMPILE) -fno-lto -c $< ++ + # AM_CXXFLAGS needs to be in each subdirectory so that it can be + # modified in a per-library or per-sub-library way. Need to manually + # set this option because CONFIG_CXXFLAGS has to be after +--- a/libstdc++-v3/src/c++98/Makefile.in ++++ b/libstdc++-v3/src/c++98/Makefile.in +@@ -913,6 +913,14 @@ + parallel_settings.o: parallel_settings.cc + $(CXXCOMPILE) $(PARALLEL_FLAGS) -c $< + ++# ios_init.cc deliberately defines the standard streams with types ++# unrelated to their declarations, so keep it out of LTO bytecode, where ++# -Wodr would report them on every link. ++ios_init.lo: ios_init.cc ++ $(LTCXXCOMPILE) -fno-lto -c $< ++ios_init.o: ios_init.cc ++ $(CXXCOMPILE) -fno-lto -c $< ++ + # Tell versions [3.59,3.63) of GNU make to not export all variables. + # Otherwise a system limit (for SysV at least) may be exceeded. + .NOEXPORT: diff --git a/src/gcc-final/lto-plugin-only-with-flto.patch b/src/gcc-final/lto-plugin-only-with-flto.patch new file mode 100644 index 0000000..6f3d993 --- /dev/null +++ b/src/gcc-final/lto-plugin-only-with-flto.patch @@ -0,0 +1,34 @@ +Use the LTO linker plugin only when -flto is given at link time + +With full plugin support in the linker, the driver passes the LTO +plugin on every link, and the plugin claims any input containing LTO +bytecode. Since libstdc++, libgfortran, and libquadmath are now fat +LTO objects, that recompiled the library members of every C++ program, +whether or not the user asked for LTO: links took 4x longer for tiny +programs and 30-45x for typical ones, lto-wrapper nagged about serial +compilation, and every LTO edge case reached every user. + +Only pass the plugin when -flto, -flto=N, or -fuse-linker-plugin is on +the link line. Ordinary links use the libraries' regular code exactly +as before, and -flto links get the full benefit, including dead-code +elimination inside the libraries. The one behavior change is that a +slim LTO object linked without -flto now fails with "plugin needed to +handle lto object", which is what the GCC manual documents anyway. + +--- a/gcc/gcc.cc ++++ b/gcc/gcc.cc +@@ -1105,9 +1105,12 @@ + + /* The linker has some plugin support. */ + #if HAVE_LTO_PLUGIN > 0 +-/* The linker used has full plugin support, use LTO plugin by default. */ ++/* The linker used has full plugin support, but only use the LTO plugin ++ when LTO is requested at link time. The runtime libraries are fat ++ LTO objects, and the plugin would otherwise claim their IR on every ++ link, recompiling the library and slowing all links. */ + #if HAVE_LTO_PLUGIN == 2 +-#define PLUGIN_COND "!fno-use-linker-plugin:%{!fno-lto" ++#define PLUGIN_COND "!fno-use-linker-plugin:%{flto|flto=*|fuse-linker-plugin" + #define PLUGIN_COND_CLOSE "}" + #else + /* The linker used has limited plugin support, use LTO plugin with explicit diff --git a/src/gcc-final/lto-wrapper-make-beside-driver.patch b/src/gcc-final/lto-wrapper-make-beside-driver.patch new file mode 100644 index 0000000..0225f8e --- /dev/null +++ b/src/gcc-final/lto-wrapper-make-beside-driver.patch @@ -0,0 +1,106 @@ +lto-wrapper: Look for make beside the driver when it is not on PATH + +Parallel LTRANS (-flto=N, -flto=auto, or a detected jobserver) hands +the partitions to make through a generated Makefile, and lto-wrapper +finds make only through $MAKE or PATH. When the toolchain is invoked by +path without its directory on PATH, which the self-locating driver +otherwise supports, that search fails and lto-wrapper silently falls +back to serial compilation, with a warning suggesting -flto=auto that +the user may already be passing. + +After $MAKE and PATH, also try make in the driver's own directory, +known from COLLECT_GCC, and run that one. + +--- a/gcc/lto-wrapper.cc ++++ b/gcc/lto-wrapper.cc +@@ -1387,26 +1387,63 @@ + url.get ()); + } + +-/* Test that a make command is present and working, return true if so. */ ++#ifndef HOST_EXECUTABLE_SUFFIX ++#define HOST_EXECUTABLE_SUFFIX "" ++#endif + ++/* The make program to run for parallel LTRANS when $MAKE is unset. */ ++ ++static const char *make_program = "make"; ++ ++/* Return true if MAKE runs and reports a version. */ ++ + static bool +-make_exists (void) ++make_works (const char *make) + { +- const char *make = "make"; +- char **make_argv = buildargv (getenv ("MAKE")); +- if (make_argv) +- make = make_argv[0]; + const char *make_args[] = {make, "--version", NULL}; +- + int exit_status = 0; + int err = 0; + const char *errmsg +- = pex_one (PEX_SEARCH, make_args[0], const_cast (make_args), ++ = pex_one (PEX_SEARCH, make, const_cast (make_args), + "make", NULL, NULL, &exit_status, &err); +- freeargv (make_argv); + return errmsg == NULL && exit_status == 0 && err == 0; + } + ++/* Test that a make command is present and working, return true if so. ++ Honor $MAKE, then search PATH, then look beside the driver ++ COLLECT_GCC, so that a toolchain invoked by path works without its ++ directory on PATH. */ ++ ++static bool ++make_exists (const char *collect_gcc) ++{ ++ char **make_argv = buildargv (getenv ("MAKE")); ++ if (make_argv) ++ { ++ bool ok = make_works (make_argv[0]); ++ freeargv (make_argv); ++ return ok; ++ } ++ ++ if (make_works (make_program)) ++ return true; ++ ++ size_t dirlen = lbasename (collect_gcc) - collect_gcc; ++ if (dirlen) ++ { ++ char *dir = xstrndup (collect_gcc, dirlen); ++ char *make = concat (dir, "make" HOST_EXECUTABLE_SUFFIX, NULL); ++ free (dir); ++ if (make_works (make)) ++ { ++ make_program = make; ++ return true; ++ } ++ free (make); ++ } ++ return false; ++} ++ + /* Execute gcc. ARGC is the number of arguments. ARGV contains the arguments. */ + + static void +@@ -1710,7 +1747,7 @@ + } + + /* We need make working for a parallel execution. */ +- if (parallel && !make_exists ()) ++ if (parallel && !make_exists (collect_gcc)) + parallel = 0; + + if (!dumppfx) +@@ -2192,7 +2229,7 @@ + obstack_ptr_grow (&argv_obstack, make_argv[argc]); + } + else +- obstack_ptr_grow (&argv_obstack, "make"); ++ obstack_ptr_grow (&argv_obstack, make_program); + + obstack_ptr_grow (&argv_obstack, "-f"); + obstack_ptr_grow (&argv_obstack, makefile); diff --git a/src/gcc-final/new-abi-only.patch b/src/gcc-final/new-abi-only.patch new file mode 100644 index 0000000..bbf3c70 --- /dev/null +++ b/src/gcc-final/new-abi-only.patch @@ -0,0 +1,245 @@ +Keep the new std::string ABI when the dual ABI is disabled + +--disable-libstdcxx-dual-abi hardwires the surviving ABI to the old +gcc4-compatible COW std::string. For a from-scratch toolchain the +useful single-ABI configuration is the reverse: only the modern SSO +string. The library supports either direction: c++config.h neutralizes +the per-file ABI pins in single-ABI builds, and the Makefiles already +exclude the cow-*, cxx11-*, and shim facet objects. Four fixups +beyond the configure default: + +ios_base::failure derives from exception in the old ABI but from +system_error in the new one. Its new-ABI members normally come from +src/c++11/cxx11-ios_failure.cc, built only for the dual ABI, and they +cannot live in src/c++98/ios_failure.cc, compiled as C++98, where + declares the new failure against a layout-compatible stand-in +for system_error. Define them in the C++11 translation unit +src/c++11/ios_errcat.cc instead. + +locale-inst.cc guards ABI-agnostic instantiations (facet ids, num_get, +time_put, ctype, codecvt, __pad) with !_GLIBCXX_USE_CXX11_ABI so the +dual ABI instantiates them exactly once, in the old-ABI object. A +new-ABI-only build must instantiate them in its only compile. + +The Transactional Memory TS clones in cow-stdexcept.cc are written +against COW string internals (_Rep, public _M_dataplus), so they can +only be compiled when std::basic_string is the COW string. This is +invisible on x86_64, where _GLIBCXX_USE_WEAK_REF is false, but breaks +the i686 build. + +--- a/libstdc++-v3/configure ++++ b/libstdc++-v3/configure +@@ -51668,7 +51668,7 @@ + if test x"$enable_libstdcxx_dual_abi" != xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: dual ABI is disabled" >&5 + $as_echo "$as_me: dual ABI is disabled" >&6;} +- default_libstdcxx_abi="gcc4-compatible" ++ default_libstdcxx_abi="new" + fi + + +--- a/libstdc++-v3/src/c++11/cow-stdexcept.cc ++++ b/libstdc++-v3/src/c++11/cow-stdexcept.cc +@@ -198,7 +198,7 @@ + // are not defined by us. Without such support, the exceptions will not be + // declared transaction-safe, so we just don't provide transactional clones + // in this case. +-#if _GLIBCXX_USE_WEAK_REF ++#if _GLIBCXX_USE_WEAK_REF && ! _GLIBCXX_USE_CXX11_ABI + #ifdef _GLIBCXX_USE_C99_STDINT + + #include +--- a/libstdc++-v3/src/c++11/ios_errcat.cc ++++ b/libstdc++-v3/src/c++11/ios_errcat.cc +@@ -28,6 +28,14 @@ + + #define _GLIBCXX_USE_CXX11_ABI 1 + #include ++#include ++ ++#ifdef _GLIBCXX_USE_NLS ++# include ++# define _(msgid) gettext (msgid) ++#else ++# define _(msgid) (msgid) ++#endif + + #if __has_cpp_attribute(clang::require_constant_initialization) + # define __constinit [[clang::require_constant_initialization]] +@@ -80,5 +88,39 @@ + iostream_category() noexcept + { return io_category_instance.cat; } + ++#if _GLIBCXX_USE_CXX11_ABI && ! _GLIBCXX_USE_DUAL_ABI ++ // In a single-ABI build with the new string ABI, these definitions ++ // cannot live in src/c++98/ios_failure.cc, which is compiled as C++98, ++ // nor in src/c++11/cxx11-ios_failure.cc, which is built only for the ++ // dual ABI. Class ios_base::failure derives from system_error. ++ ios_base::failure::failure(const string& __str) ++ : system_error(io_errc::stream, __str) { } ++ ++ ios_base::failure::failure(const string& __str, const error_code& __ec) ++ : system_error(__ec, __str) { } ++ ++ ios_base::failure::failure(const char* __str, const error_code& __ec) ++ : system_error(__ec, __str) { } ++ ++ ios_base::failure::~failure() ++ { } ++ ++ const char* ++ ios_base::failure::what() const throw() ++ { return runtime_error::what(); } ++ ++ void ++ __throw_ios_failure(const char* __s __attribute__((unused))) ++ { _GLIBCXX_THROW_OR_ABORT(ios::failure(_(__s))); } ++ ++ void ++ __throw_ios_failure(const char* __s __attribute__((unused)), ++ int __err __attribute__((unused))) ++ { ++ _GLIBCXX_THROW_OR_ABORT(ios::failure(_(__s), ++ __err ? error_code(__err, generic_category()) : io_errc::stream)); ++ } ++#endif ++ + _GLIBCXX_END_NAMESPACE_VERSION + } // namespace +--- a/libstdc++-v3/src/c++11/locale-inst-numeric.h ++++ b/libstdc++-v3/src/c++11/locale-inst-numeric.h +@@ -30,7 +30,7 @@ + + namespace std _GLIBCXX_VISIBILITY(default) + { +-#if ! _GLIBCXX_USE_CXX11_ABI ++#if ! _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + // use_facet and has_facet instantiations + INSTANTIATE_FACET_ACCESSORS(num_get); + INSTANTIATE_FACET_ACCESSORS(num_put); +@@ -38,7 +38,7 @@ + + _GLIBCXX_BEGIN_NAMESPACE_LDBL + +-#if ! _GLIBCXX_USE_CXX11_ABI ++#if ! _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + template class num_get >; + template class num_put >; + #endif +@@ -88,7 +88,7 @@ + unsigned long long&) const; + #endif + +-#if ! _GLIBCXX_USE_CXX11_ABI ++#if ! _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + // num_put member function templates + template + ostreambuf_iterator +--- a/libstdc++-v3/src/c++11/locale-inst.cc ++++ b/libstdc++-v3/src/c++11/locale-inst.cc +@@ -52,7 +52,7 @@ + _GLIBCXX_BEGIN_NAMESPACE_VERSION + + // moneypunct, money_get, and money_put +-#if ! _GLIBCXX_USE_CXX11_ABI ++#if ! _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + template struct __moneypunct_cache; + template struct __moneypunct_cache; + #endif +@@ -64,7 +64,7 @@ + _GLIBCXX_END_NAMESPACE_CXX11 + + // numpunct, numpunct_byname, num_get, and num_put +-#if ! _GLIBCXX_USE_CXX11_ABI ++#if ! _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + template struct __numpunct_cache; + #endif + _GLIBCXX_BEGIN_NAMESPACE_CXX11 +@@ -73,7 +73,7 @@ + _GLIBCXX_END_NAMESPACE_CXX11 + + // time_get and time_put +-#if ! _GLIBCXX_USE_CXX11_ABI ++#if ! _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + template class __timepunct; + template struct __timepunct_cache; + template class time_put >; +@@ -97,13 +97,13 @@ + ctype_byname::ctype_byname(const string& __s, size_t __refs) + : ctype_byname(__s.c_str(), __refs) { } + +-#if ! _GLIBCXX_USE_CXX11_ABI ++#if ! _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + inline template class __ctype_abstract_base; + template class ctype_byname; + #endif + + // codecvt +-#if ! _GLIBCXX_USE_CXX11_ABI ++#if ! _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + inline template class __codecvt_abstract_base; + template class codecvt_byname; + #else +@@ -118,7 +118,7 @@ + _GLIBCXX_END_NAMESPACE_CXX11 + + // use_facet and has_facet instantiations +-#if ! _GLIBCXX_USE_CXX11_ABI ++#if ! _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + INSTANTIATE_FACET_ACCESSORS(ctype); + INSTANTIATE_FACET_ACCESSORS(codecvt); + #endif +@@ -127,14 +127,14 @@ + INSTANTIATE_FACET_ACCESSORS(moneypunct); + // No explicit instantiation of has_facet> for some reason. + INSTANTIATE_USE_FACET (moneypunct); +-#if ! _GLIBCXX_USE_CXX11_ABI ++#if ! _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + INSTANTIATE_FACET_ACCESSORS(__timepunct); + INSTANTIATE_FACET_ACCESSORS(time_put); + #endif + INSTANTIATE_FACET_ACCESSORS(time_get); + INSTANTIATE_FACET_ACCESSORS(messages); + +-#if ! _GLIBCXX_USE_CXX11_ABI ++#if ! _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + // locale functions. + template + C* +--- a/libstdc++-v3/src/c++98/ios_failure.cc ++++ b/libstdc++-v3/src/c++98/ios_failure.cc +@@ -45,6 +45,10 @@ + { + _GLIBCXX_BEGIN_NAMESPACE_VERSION + ++#if ! _GLIBCXX_USE_CXX11_ABI ++// For a single-ABI build with the new string ABI, ios::failure derives ++// from system_error and its members are defined in a C++11 translation ++// unit, src/c++11/ios_errcat.cc. + ios_base::failure::failure(const string& __str) throw() + : _M_msg(__str) { } + +@@ -54,6 +58,7 @@ + const char* + ios_base::failure::what() const throw() + { return _M_msg.c_str(); } ++#endif + + #if _GLIBCXX_USE_DUAL_ABI + // When the dual ABI is enabled __throw_ios_failure() is defined in +@@ -84,6 +89,7 @@ + + #else // ! _GLIBCXX_USE_DUAL_ABI + ++#if ! _GLIBCXX_USE_CXX11_ABI + void + __throw_ios_failure(const char* __s __attribute__((unused))) + { _GLIBCXX_THROW_OR_ABORT(ios::failure(_(__s))); } +@@ -91,6 +97,7 @@ + void + __throw_ios_failure(const char* str, int) + { __throw_ios_failure(str); } ++#endif + + #endif // _GLIBCXX_USE_DUAL_ABI + diff --git a/src/gcc-final/series b/src/gcc-final/series index 45893e4..99b2147 100644 --- a/src/gcc-final/series +++ b/src/gcc-final/series @@ -4,3 +4,7 @@ i686-dumpmachine.patch libgomp-gfortran-path.patch relocatable.patch static-complit-flex-array.patch +new-abi-only.patch +lto-plugin-only-with-flto.patch +libstdcxx-no-lto-punned-types.patch +lto-wrapper-make-beside-driver.patch diff --git a/src/gcc/lto-portable-real-cst.patch b/src/gcc/lto-portable-real-cst.patch new file mode 100644 index 0000000..4687c16 --- /dev/null +++ b/src/gcc/lto-portable-real-cst.patch @@ -0,0 +1,97 @@ +Stream REAL_CST significands in a host-independent layout + +LTO bytecode packs a REAL_VALUE_TYPE's significand as SIGSZ words of +HOST_BITS_PER_LONG bits, which is the host's own layout: three 64-bit +words on the Linux build host, five 32-bit words on Windows. The +runtime libraries' bytecode is produced by the Linux-hosted cross +compiler and consumed by the Windows-hosted compiler, so every +floating-point constant in it arrived garbled: FLT_MAX read as zero, +libgfortran printed every real as Infinity, and std::from_chars +returned 0 for any floating input. Integers are unaffected because +they stream as HOST_WIDE_INT, which is 64 bits on both hosts. + +Stream the significand's top 128 bits as four 32-bit words instead, +most significant first, which covers every target format (binary128 +needs 113), and the 16-byte decimal128 encoding in memory order for +decimal values. Both hosts have at least 128 significand bits. + +This changes the bytecode format relative to upstream, so objects +compiled by another GCC 16.2 build would misread constants here; the +toolchain is self-contained and the version check never covered host +differences anyway. Applied before both compilers are built. + +--- a/gcc/data-streamer.cc ++++ b/gcc/data-streamer.cc +@@ -122,6 +122,35 @@ + } + } + ++/* The significand of a REAL_VALUE_TYPE is an array of SIGSZ words of ++ HOST_BITS_PER_LONG bits, so its layout depends on the host. Stream ++ it in a host-independent form instead, so that bytecode produced by ++ a cross compiler on a host with a 64-bit long can be consumed by the ++ same compiler hosted on Windows, where long is 32 bits. ++ ++ A binary significand is aligned at the top of the array, and any ++ value in a target format occupies at most its top 128 bits, so stream ++ those as four 32-bit words, most significant first. A decimal value ++ keeps its decimal128 encoding in the first 16 bytes of the array, ++ which are streamed in memory order. Chunks never straddle a word ++ since HOST_BITS_PER_LONG is a multiple of 32. */ ++ ++#define SIG_STREAM_BITS 128 ++ ++static inline unsigned int ++sig_get32 (const REAL_VALUE_TYPE *r, unsigned bit) ++{ ++ return (unsigned int) (r->sig[bit / HOST_BITS_PER_LONG] ++ >> (bit % HOST_BITS_PER_LONG)); ++} ++ ++static inline void ++sig_set32 (REAL_VALUE_TYPE *r, unsigned bit, unsigned int v) ++{ ++ r->sig[bit / HOST_BITS_PER_LONG] ++ |= (unsigned long) v << (bit % HOST_BITS_PER_LONG); ++} ++ + /* Pack REAL_VALUE_TYPE R into BP. */ + + void +@@ -133,8 +162,16 @@ + bp_pack_value (bp, r->signalling, 1); + bp_pack_value (bp, r->canonical, 1); + bp_pack_value (bp, r->uexp, EXP_BITS); +- for (unsigned i = 0; i < SIGSZ; i++) +- bp_pack_value (bp, r->sig[i], HOST_BITS_PER_LONG); ++ if (r->decimal) ++ { ++ unsigned int w[SIG_STREAM_BITS / 32]; ++ memcpy (w, r->sig, sizeof w); ++ for (unsigned i = 0; i < SIG_STREAM_BITS / 32; i++) ++ bp_pack_value (bp, w[i], 32); ++ } ++ else ++ for (unsigned i = 0; i < SIG_STREAM_BITS / 32; i++) ++ bp_pack_value (bp, sig_get32 (r, SIGNIFICAND_BITS - 32 * (i + 1)), 32); + } + + /* Unpack REAL_VALUE_TYPE R from BP. */ +@@ -151,6 +188,15 @@ + r->signalling = (unsigned) bp_unpack_value (bp, 1); + r->canonical = (unsigned) bp_unpack_value (bp, 1); + r->uexp = (unsigned) bp_unpack_value (bp, EXP_BITS); +- for (unsigned i = 0; i < SIGSZ; i++) +- r->sig[i] = (unsigned long) bp_unpack_value (bp, HOST_BITS_PER_LONG); ++ if (r->decimal) ++ { ++ unsigned int w[SIG_STREAM_BITS / 32]; ++ for (unsigned i = 0; i < SIG_STREAM_BITS / 32; i++) ++ w[i] = (unsigned int) bp_unpack_value (bp, 32); ++ memcpy (r->sig, w, sizeof w); ++ } ++ else ++ for (unsigned i = 0; i < SIG_STREAM_BITS / 32; i++) ++ sig_set32 (r, SIGNIFICAND_BITS - 32 * (i + 1), ++ (unsigned int) bp_unpack_value (bp, 32)); + } diff --git a/src/gcc/no-abi-compat-aliases.patch b/src/gcc/no-abi-compat-aliases.patch new file mode 100644 index 0000000..d8e6081 --- /dev/null +++ b/src/gcc/no-abi-compat-aliases.patch @@ -0,0 +1,57 @@ +Never emit mangling compatibility aliases + +When a function's mangled name changed in some ABI version, GCC also +emits its old name as a weak alias so that objects compiled by older +compilers can still link. By default it does so for changes since ABI +18 (GCC 13), and -Wabi=N quietly implies -fabi-compat-version=N, which +libstdc++ uses (-Wabi=19) and so gets a different set of aliases than +user code. That asymmetry breaks LTO on PE: the aliases share a COMDAT +group with their target, ld cannot reconcile a group that has two +members in one IR object and one in another, and any C++20 constrained +function explicitly instantiated in libstdc++ and implicitly in an +-flto user object fails with a multiple definition error. + +This toolchain is static and self-contained, so there are no older +objects to stay compatible with. Emit no compatibility aliases unless +-fabi-compat-version is given explicitly, and leave -Wabi= as a pure +diagnostic. Applied before both compilers are built, since the +bootstrap cross compiler is what compiles libstdc++. + +--- a/gcc/c-family/c-opts.cc ++++ b/gcc/c-family/c-opts.cc +@@ -1110,8 +1110,6 @@ + const int latest_abi_version = 21; + /* Possibly different for non-default ABI fixes within a release. */ + const int default_abi_version = latest_abi_version; +- /* Generate compatibility aliases for ABI v18 (GCC 13) by default. */ +- const int abi_compat_default = 18; + + if (flag_abi_version > latest_abi_version) + warning (0, "%<-fabi-version=%d%> is not supported, using =%d", +@@ -1126,18 +1124,17 @@ + clamp (flag_abi_compat_version); + #undef clamp + +- /* Default -Wabi= or -fabi-compat-version= from each other. */ ++ /* Default -Wabi= from -fabi-compat-version=. Unless the latter is ++ given explicitly, never emit mangling compatibility aliases: they ++ only serve linking against objects from older compilers, and ++ -Wabi= is a diagnostic, not a request for them. */ + if (warn_abi_version == -1 && flag_abi_compat_version != -1) + warn_abi_version = flag_abi_compat_version; +- else if (flag_abi_compat_version == -1 && warn_abi_version != -1) +- flag_abi_compat_version = warn_abi_version; +- else if (warn_abi_version == -1 && flag_abi_compat_version == -1) ++ else if (flag_abi_compat_version == -1) + { +- warn_abi_version = 0; +- if (flag_abi_version == default_abi_version) +- flag_abi_compat_version = abi_compat_default; +- else +- flag_abi_compat_version = latest_abi_version; ++ if (warn_abi_version == -1) ++ warn_abi_version = 0; ++ flag_abi_compat_version = flag_abi_version; + } + + /* Allow warnings vs ABI versions beyond what we currently support. */ diff --git a/src/gcc/series b/src/gcc/series index 7598d77..fd591b6 100644 --- a/src/gcc/series +++ b/src/gcc/series @@ -2,3 +2,5 @@ avx-misaligned.patch dynamic-load-tzdb.patch libgfortran-mapview.patch trap-terminate.patch +no-abi-compat-aliases.patch +lto-portable-real-cst.patch diff --git a/src/make/self-locate-shell.patch b/src/make/self-locate-shell.patch new file mode 100644 index 0000000..08bbda7 --- /dev/null +++ b/src/make/self-locate-shell.patch @@ -0,0 +1,47 @@ +Look for the shell beside make before searching PATH + +On Windows, make finds its shell by searching PATH for sh.exe and falls +back to cmd.exe, whose quoting rules break ordinary recipes. When make +is invoked by path without w64devkit's bin/ on PATH, as GCC's +lto-wrapper now does for parallel LTO, the search fails even though +sh.exe sits right next to make.exe. Check make's own directory first, +so that make works on its own like the rest of the toolchain. + +--- a/src/main.c ++++ b/src/main.c +@@ -1056,8 +1056,34 @@ + char *p; + struct variable *v = lookup_variable (STRING_SIZE_TUPLE ("PATH")); + ++ /* Prefer a shell beside make itself, so that make works when ++ invoked by path without its directory on PATH. */ ++ { ++ PATH_VAR (exe_path); ++ DWORD n = GetModuleFileNameA (NULL, exe_path, GET_PATH_MAX); ++ if (token == NULL && n > 0 && n < GET_PATH_MAX) ++ { ++ char *base = exe_path + n; ++ while (base > exe_path && !ISDIRSEP (base[-1])) ++ base--; ++ if (base > exe_path ++ && strlen (search_token) < GET_PATH_MAX - (base - exe_path)) ++ { ++ strcpy (base, search_token); ++ if (_access (exe_path, 0) == 0) ++ { ++ default_shell = xstrdup (w32ify (exe_path, 0)); ++ sh_found = 1; ++ DB (DB_VERBOSE, ++ (_("find_and_set_shell() setting default_shell = %s\n"), ++ default_shell)); ++ } ++ } ++ } ++ } ++ + /* Search Path for shell */ +- if (v && v->value) ++ if (!sh_found && v && v->value) + { + char *ep; + diff --git a/src/make/series b/src/make/series index 5fd1c71..15f8355 100644 --- a/src/make/series +++ b/src/make/series @@ -1,2 +1,3 @@ force-slow.patch shell-space.patch +self-locate-shell.patch