Bug 1471132 - Change how static xpcom components are linked. r?froydnj
Overall, this makes the whole setup less fragile, and make it work with
LTO in more situations.
--- a/python/mozbuild/mozbuild/action/check_binary.py
+++ b/python/mozbuild/mozbuild/action/check_binary.py
@@ -171,52 +171,76 @@ def check_nsmodules(target, binary):
data = line.split(None, 3)
if data and len(data) == 4 and data[0].isdigit() and \
ishex(data[1]) and ishex(data[2]):
# - Some symbols in the table can be aliases, and appear as
# `foo = bar`.
# - The MSVC mangling has some type info following `@@`
# - Any namespacing that can happen on the symbol appears as a
# suffix, after a `@`.
- name = data[3].split(' = ')[0].split('@@')[0].split('@')[0]
- if name.endswith('_NSModule'):
- symbols.append((int(data[2], 16), 0, name.lstrip('?')))
+ # - Mangled symbols are prefixed with `?`.
+ name = data[3].split(' = ')[0].split('@@')[0].split('@')[0] \
+ .lstrip('?')
+ if name.endswith('_NSModule') or name in (
+ '__start_kPStaticModules',
+ '__stop_kPStaticModules'):
+ symbols.append((int(data[2], 16), GUESSED_NSMODULE_SIZE,
+ name))
else:
- for line in get_output(target['nm'], '-gP', binary):
+ for line in get_output(target['nm'], '-P', binary):
data = line.split()
- # NSModules symbols end with _NSModule or _NSModuleE when
- # C++-mangled.
- if len(data) == 4 and data[0].endswith(('_NSModule', '_NSModuleE')):
+ # Some symbols may not have a size listed at all.
+ if len(data) == 3:
+ data.append('0')
+ if len(data) == 4:
sym, _, addr, size = data
- symbols.append((int(addr, 16), int(size, 16), sym))
+ # NSModules symbols end with _NSModule or _NSModuleE when
+ # C++-mangled.
+ if sym.endswith(('_NSModule', '_NSModuleE')):
+ # On mac, nm doesn't actually print anything other than 0
+ # for the size. So take our best guess.
+ size = int(size, 16) or GUESSED_NSMODULE_SIZE
+ symbols.append((int(addr, 16), size, sym))
+ elif sym.endswith(('__start_kPStaticModules',
+ '__stop_kPStaticModules')):
+ # On ELF and mac systems, these symbols have no size, such
+ # that the first actual NSModule has the same address as
+ # the start symbol.
+ symbols.append((int(addr, 16), 0, sym))
if not symbols:
raise RuntimeError('Could not find NSModules')
def print_symbols(symbols):
for addr, size, sym in symbols:
print('%x %d %s' % (addr, size, sym))
symbols = sorted(symbols)
next_addr = None
for addr, size, sym in symbols:
if next_addr is not None and next_addr != addr:
print_symbols(symbols)
raise RuntimeError('NSModules are not adjacent')
- # On mac, nm doesn't actually print anything other than 0 for the
- # size. So take our best guess. On Windows, dumpbin doesn't give us
- # any size at all.
- if size == 0:
- size = GUESSED_NSMODULE_SIZE
next_addr = addr + size
+
+ # The mac linker doesn't emit the start/stop symbols in the symbol table.
+ # We'll just assume it did the job correctly.
+ if get_type(binary) == MACHO:
+ return
+
first = symbols[0][2]
last = symbols[-1][2]
# On some platforms, there are extra underscores on symbol names.
- if first.lstrip('_') != 'start_kPStaticModules_NSModule' or \
- last.lstrip('_') != 'end_kPStaticModules_NSModule':
+ if first.lstrip('_') != 'start_kPStaticModules' or \
+ last.lstrip('_') != 'stop_kPStaticModules':
print_symbols(symbols)
+ syms = set(sym for add, size, sym in symbols)
+ if 'start_kPStaticModules' not in syms:
+ raise RuntimeError('Could not find start_kPStaticModules symbol')
+ if 'stop_kPStaticModules' not in syms:
+ raise RuntimeError('Could not find stop_kPStaticModules symbol')
raise RuntimeError('NSModules are not ordered appropriately')
def check_pt_load(target, binary):
if target is HOST or get_type(binary) != ELF or not is_libxul(binary):
raise Skip()
count = 0
for line in get_output(target['readelf'], '-l', binary):
--- a/toolkit/library/StaticXULComponents.ld
+++ b/toolkit/library/StaticXULComponents.ld
@@ -1,5 +1,7 @@
SECTIONS {
.data.rel.ro : {
- *(.kPStaticModules)
+ PROVIDE_HIDDEN(__start_kPStaticModules = .);
+ *(kPStaticModules)
+ PROVIDE_HIDDEN(__stop_kPStaticModules = .);
}
}
deleted file mode 100644
--- a/toolkit/library/StaticXULComponentsEnd/StaticXULComponentsEnd.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "mozilla/Module.h"
-
-/* Ensure end_kPStaticModules is at the end of the .kPStaticModules section
- * on Windows. Somehow, placing the object last is not enough with PGO/LTCG. */
-#ifdef _MSC_VER
-/* Sections on Windows are in two parts, separated with $. When linking,
- * sections with the same first part are all grouped, and ordered
- * alphabetically with the second part as sort key. */
-# pragma section(".kPStaticModules$Z", read)
-# undef NSMODULE_SECTION
-# define NSMODULE_SECTION __declspec(allocate(".kPStaticModules$Z"), dllexport)
-#elif MOZ_LTO
-/* Clang+lld with LTO does not order modules correctly either, but fortunately
- * the same trick works. */
-# undef NSMODULE_SECTION
-# define NSMODULE_SECTION __attribute__((section(".kPStaticModules$Z"), visibility("default")))
-#endif
-/* This could be null, but this needs a dummy value to ensure it actually ends
- * up in the same section as other NSMODULE_DEFNs, instead of being moved to a
- * separate readonly section. */
-NSMODULE_DEFN(end_kPStaticModules) = (mozilla::Module*)&NSMODULE_NAME(end_kPStaticModules);
deleted file mode 100644
--- a/toolkit/library/StaticXULComponentsEnd/moz.build
+++ /dev/null
@@ -1,17 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-SOURCES += [
- 'StaticXULComponentsEnd.cpp',
-]
-
-# Don't let LTO reorder StaticXULComponentsStart.o.
-for f in CONFIG['OS_CXXFLAGS']:
- if f.startswith('-flto'):
- SOURCES['StaticXULComponentsEnd.cpp'].flags += ['-fno-lto']
- break
-
-Library('StaticXULComponentsEnd')
-
-DEFINES['MOZILLA_INTERNAL_API'] = True
deleted file mode 100644
--- a/toolkit/library/StaticXULComponentsStart.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "mozilla/Module.h"
-
-/* This could be null, but this needs a dummy value to ensure it actually ends
- * up in the same section as other NSMODULE_DEFNs, instead of being moved to a
- * separate readonly section. */
-NSMODULE_DEFN(start_kPStaticModules) = (mozilla::Module*)&NSMODULE_NAME(start_kPStaticModules);
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -83,30 +83,16 @@ def Libxul(name):
LDFLAGS += ['-NATVIS:%s/toolkit/library/gecko.natvis' % TOPSRCDIR]
Libxul('xul')
FORCE_STATIC_LIB = True
STATIC_LIBRARY_NAME = 'xul_s'
-SOURCES += [
- 'StaticXULComponentsStart.cpp',
-]
-
-# This, combined with the fact the file is first, makes the start pointer
-# it contains first in Windows PGO builds.
-SOURCES['StaticXULComponentsStart.cpp'].no_pgo = True
-
-# Don't let LTO reorder StaticXULComponentsStart.o.
-for f in CONFIG['OS_CXXFLAGS']:
- if f.startswith('-flto'):
- SOURCES['StaticXULComponentsStart.cpp'].flags += ['-fno-lto']
- break
-
if CONFIG['OS_ARCH'] == 'WINNT':
SOURCES += [
'nsDllMain.cpp',
]
LOCAL_INCLUDES += [
'/config',
# need widget/windows for resource.h (included from widget.rc)
@@ -359,15 +345,11 @@ if CONFIG['COMPILE_ENVIRONMENT']:
FINAL_TARGET_FILES += ['!dependentlibs.list', '!dependentlibs.list.gtest']
if CONFIG['OS_ARCH'] == 'Linux' and CONFIG['OS_TARGET'] != 'Android':
GENERATED_FILES += ['symverscript']
GENERATED_FILES['symverscript'].script = 'gen_symverscript.py'
GENERATED_FILES['symverscript'].inputs = ['symverscript.in']
SYMBOLS_FILE = '!symverscript'
-# This library needs to be last to make XPCOM module registration work.
-USE_LIBS += ['StaticXULComponentsEnd']
-
-# The above library needs to be last for C++ purposes. This library,
-# however, is entirely composed of Rust code, and needs to come after
+# This library is entirely composed of Rust code, and needs to come after
# all the C++ code so any possible C++ -> Rust calls can be resolved.
USE_LIBS += ['gkrust']
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -145,17 +145,16 @@ DIRS += [
]
if CONFIG['MOZ_PREF_EXTENSIONS']:
DIRS += ['/extensions/pref']
DIRS += [
'/devtools',
'/toolkit/library',
- '/toolkit/library/StaticXULComponentsEnd',
'/services',
'/startupcache',
'/js/ductwork/debugger',
'/other-licenses/snappy',
]
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
DIRS += ['/toolkit/system/gnome']
--- a/xpcom/components/Module.h
+++ b/xpcom/components/Module.h
@@ -134,21 +134,21 @@ struct Module
#if defined(MOZILLA_INTERNAL_API)
# define NSMODULE_NAME(_name) _name##_NSModule
# if defined(_MSC_VER)
# pragma section(".kPStaticModules$M", read)
# pragma comment(linker, "/merge:.kPStaticModules=.rdata")
# define NSMODULE_SECTION __declspec(allocate(".kPStaticModules$M"), dllexport)
# elif defined(__GNUC__)
# if defined(__ELF__)
-# define NSMODULE_SECTION __attribute__((section(".kPStaticModules"), visibility("default")))
+# define NSMODULE_SECTION __attribute__((section("kPStaticModules"), visibility("default")))
# elif defined(__MACH__)
# define NSMODULE_SECTION __attribute__((section("__DATA, .kPStaticModules"), visibility("default")))
# elif defined (_WIN32)
-# define NSMODULE_SECTION __attribute__((section(".kPStaticModules"), dllexport))
+# define NSMODULE_SECTION __attribute__((section("kPStaticModules"), dllexport))
# endif
# endif
# if !defined(NSMODULE_SECTION)
# error Do not know how to define sections.
# endif
# if defined(MOZ_HAVE_ASAN_BLACKLIST)
# define NSMODULE_ASAN_BLACKLIST __attribute__((no_sanitize_address))
# else
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -242,31 +242,103 @@ nsComponentManagerImpl::nsComponentManag
, mContractIDs(CONTRACTID_HASHTABLE_INITIAL_LENGTH)
, mLock("nsComponentManagerImpl.mLock")
, mStatus(NOT_INITIALIZED)
{
}
nsTArray<const mozilla::Module*>* nsComponentManagerImpl::sStaticModules;
-NSMODULE_DEFN(start_kPStaticModules);
-NSMODULE_DEFN(end_kPStaticModules);
+/* NSMODULE_DEFN places NSModules in specific sections, as per Module.h.
+ * The linker will group them all together, and we use tricks below to
+ * find the start and end of the grouped list of NSModules.
+ *
+ * On Windows, all the symbols in the .kPStaticModules* sections are
+ * grouped together, by lexical order of the section names. The NSModules
+ * themselves are in .kPStaticModules$M. We use the section name
+ * .kPStaticModules$A to add an empty entry that will be the first,
+ * and the section name .kPStaticModules$Z for another empty entry that
+ * will be the last. We make both null pointers, and skip them in the
+ * AllStaticModules range-iterator.
+ *
+ * On ELF (Linux, BSDs, ...), as well as Mingw builds, the linker itself
+ * provides symbols for the beginning and end of the consolidated section,
+ * but it only does so for sections that can be represented as C identifiers,
+ * so the section is named `kPStaticModules` rather than `.kPStaticModules`.
+ *
+ * We also use a linker script with BFD ld so that the sections end up
+ * folded into the .data.rel.ro section, but that actually breaks the above
+ * described behavior, so the linker script contains an additional trick
+ * to still provide the __start and __stop symbols (the linker script
+ * doesn't work with gold or lld).
+ *
+ * On Darwin, a similar setup is available through the use of some
+ * synthesized symbols (section$...).
+ *
+ * On all platforms, the __stop_kPStaticModules symbol is past all NSModule
+ * pointers.
+ * On Windows, the __start_kPStaticModules symbol points to an empty pointer
+ * preceding the first NSModule pointer. On other platforms, it points to the
+ * first NSModule pointer.
+ */
+
+// Dummy class to define a range-iterator for the static modules.
+class AllStaticModules {};
+
+#if defined(_MSC_VER)
+
+# pragma section(".kPStaticModules$A", read)
+NSMODULE_ASAN_BLACKLIST __declspec(allocate(".kPStaticModules$A"), dllexport)
+extern mozilla::Module const* const __start_kPStaticModules = nullptr;
+
+mozilla::Module const* const* begin(AllStaticModules& _) {
+ return &__start_kPStaticModules + 1;
+}
+
+# pragma section(".kPStaticModules$Z", read)
+NSMODULE_ASAN_BLACKLIST __declspec(allocate(".kPStaticModules$Z"), dllexport)
+extern mozilla::Module const* const __stop_kPStaticModules = nullptr;
+
+#else
+
+# if defined(__ELF__) || (defined(_WIN32) && defined(__GNUC__))
+
+extern "C" mozilla::Module const* const __start_kPStaticModules;
+extern "C" mozilla::Module const* const __stop_kPStaticModules;
+
+# elif defined(__MACH__)
+
+extern mozilla::Module const *const __start_kPStaticModules __asm("section$start$__DATA$.kPStaticModules");
+extern mozilla::Module const* const __stop_kPStaticModules __asm("section$end$__DATA$.kPStaticModules");
+
+# else
+# error Do not know how to find NSModules.
+# endif
+
+mozilla::Module const* const* begin(AllStaticModules& _) {
+ return &__start_kPStaticModules;
+}
+
+#endif
+
+mozilla::Module const* const* end(AllStaticModules& _) {
+ return &__stop_kPStaticModules;
+}
/* static */ void
nsComponentManagerImpl::InitializeStaticModules()
{
if (sStaticModules) {
return;
}
sStaticModules = new nsTArray<const mozilla::Module*>;
- for (const mozilla::Module * const* staticModules =
- &NSMODULE_NAME(start_kPStaticModules) + 1;
- staticModules < &NSMODULE_NAME(end_kPStaticModules); ++staticModules)
- sStaticModules->AppendElement(*staticModules);
+ for (auto module : AllStaticModules()) {
+ sStaticModules->AppendElement(module);
+ }
}
nsTArray<nsComponentManagerImpl::ComponentLocation>*
nsComponentManagerImpl::sModuleLocations;
/* static */ void
nsComponentManagerImpl::InitializeModuleLocations()
{