summaryrefslogtreecommitdiff
path: root/SConstruct
diff options
context:
space:
mode:
authorRyan Egesdahl <ryan.egesdahl@mongodb.com>2020-12-07 09:33:33 -0800
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-12-09 16:16:17 +0000
commitb0120ee8b2a50a62f0b92a259f32d82f4c1aea22 (patch)
treee854f47000b6ec2d01de250677d8fc946ade8889 /SConstruct
parent51e3e36ac48cac80c920e5e418013fdb0a6747f2 (diff)
downloadmongo-b0120ee8b2a50a62f0b92a259f32d82f4c1aea22.tar.gz
SERVER-48291 Ensure runtime is dynamically linked in dynamic builds
Prior to this point, a dynamic build might have resulted in some runtime libraries being statically linked into shared objects and executables in cases where "shared" runtime libraries were actually linker scripts that linked static versions. This was the case with the MongoDB toolchain and some distro toolchains, including those installed as updated compiler versions in RHEL. The effect of having runtime libraries statically linked was that symbols from those libraries would end up scattered over the compiled objects, increasing object sizes and slowing down server startup. Now, whenever a dynamic build is selected, the user can choose whether to create "shim" runtime libraries that wrap the static ones. The default behavior remains as it was before, and dynamic runtime must be enabled in order to use it.
Diffstat (limited to 'SConstruct')
-rw-r--r--SConstruct165
1 files changed, 156 insertions, 9 deletions
diff --git a/SConstruct b/SConstruct
index 2f96c965b83..f1d4d225009 100644
--- a/SConstruct
+++ b/SConstruct
@@ -429,6 +429,15 @@ add_option("cxx-std",
help="Select the C++ langauge standard to build with",
)
+add_option("dynamic-runtime",
+ choices=["force", "off", "auto"],
+ const="on",
+ default="auto",
+ help="Force the static compiler and C++ runtimes to be linked dynamically",
+ nargs="?",
+ type="choice",
+)
+
def find_mongo_custom_variables():
files = []
paths = [path for path in sys.path if 'site_scons' in path]
@@ -1146,6 +1155,25 @@ for var in ['CC', 'CXX']:
env.AddMethod(mongo_platform.env_os_is_wrapper, 'TargetOSIs')
env.AddMethod(mongo_platform.env_get_os_name_wrapper, 'GetTargetOSName')
+
+def shim_library(env, name, needs_link=False, *args, **kwargs):
+ nodes = env.Library(
+ target=f"shim_{name}" if name else name,
+ source=[
+ f"shim_{name}.cpp" if name else name,
+ ],
+ *args,
+ **kwargs
+ )
+
+ for n in nodes:
+ setattr(n.attributes, "needs_link", needs_link)
+
+ return nodes
+
+env.AddMethod(shim_library, 'ShimLibrary')
+
+
def conf_error(env, msg, *args):
print(msg.format(*args))
print("See {0} for details".format(env.File('$CONFIGURELOG').abspath))
@@ -1634,6 +1662,122 @@ if link_model.startswith("dynamic"):
return []
env['LIBDEPS_TAG_EXPANSIONS'].append(libdeps_tags_expand_incomplete)
+
+# If requested, wrap the static runtime libraries in shims and use those to link
+# them dynamically. This allows us to "convert" runtimes in toolchains that have
+# linker scripts in place of shared libraries which actually link the static
+# library instead. The benefit of making this conversion is that shared
+# libraries produced by these toolchains are smaller because we don't end up
+# spreading runtime symbols all over the place, and in turn they should also
+# get loaded by the dynamic linker more quickly as well.
+dynamicRT = get_option("dynamic-runtime")
+
+if dynamicRT == "auto":
+ if env.ToolchainIs('msvc'):
+ # TODO: SERVER-53102
+ # Windows Enterprise *requires* a dynamic CRT because it needs to have
+ # shared state in order to load the external libraries required for SSL,
+ # LDAP, etc. This state of affairs may eventually change as Windows
+ # dynamic builds improve, but for now we just force a dynamic CRT with
+ # Windows until we have some way of detecting when we can get away with
+ # a static CRT.
+ #
+ # Ideally, we should be determining whether a static build is requested,
+ # and if so, whether a dynamic CRT *must* be used in such a case.
+
+ dynamicRT = "force"
+
+ elif get_option("link-model") != "dynamic":
+ dynamicRT = "off"
+
+ elif env.TargetOSIs('linux') and env.ToolchainIs('gcc', 'clang'):
+ def CheckRuntimeLibraries(context):
+ context.Message("Checking whether any runtime libraries are linker scripts... ")
+
+ result = {}
+ libs = [ 'libgcc', 'libgcc_s', 'libgcc_eh' ]
+
+ if get_option('libc++'):
+ libs.append('libc++')
+ else:
+ libs.append('libstdc++')
+
+ compiler = subprocess.Popen(
+ [context.env['CXX'], "-print-search-dirs"],
+ stdout=subprocess.PIPE
+ )
+
+ # This just pulls out the library paths and *only* the library
+ # paths, deleting all other lines. It also removes the leading
+ # "libraries" tag from the line so only the paths are left in
+ # the output.
+ sed = subprocess.Popen(
+ [
+ "sed",
+ "/^lib/b 1;d;:1;s,.*:[^=]*=,,",
+ ],
+ stdin=compiler.stdout,
+ stdout=subprocess.PIPE
+ )
+ compiler.stdout.close()
+
+ search_paths = sed.communicate()[0].decode('utf-8').split(':')
+
+ for lib in libs:
+ for search_path in search_paths:
+ lib_file = os.path.join(search_path, lib + ".so")
+ if os.path.exists(lib_file):
+ file_type = subprocess.check_output(["file", lib_file]).decode('utf-8')
+ match = re.search('ASCII text', file_type)
+ result[lib] = bool(match)
+ break
+ if any(result.values()):
+ ret = "yes"
+ else:
+ ret = "no"
+ context.Result(ret)
+ return ret
+
+ detectStaticRuntime = Configure(detectEnv, help=False, custom_tests = {
+ 'CheckRuntimeLibraries' : CheckRuntimeLibraries,
+ })
+
+ if detectStaticRuntime.CheckRuntimeLibraries() == "yes":
+ # TODO: SERVER-48291
+ # Set this to "force" when the issue with jsCore test failures with
+ # dynamic runtime have been resolved.
+ dynamicRT = "off"
+ else:
+ dynamicRT = "off"
+
+ detectStaticRuntime.Finish()
+
+if dynamicRT == "force":
+ if not (env.TargetOSIs('linux') or env.TargetOSIs('windows')):
+ env.FatalError("A dynamic runtime can be forced only on Windows and Linux at this time.")
+
+ # GCC and Clang get configured in src/SConscript so as to avoid affecting
+ # the conftests.
+ if env.ToolchainIs('msvc'):
+ if debugBuild:
+ env.Append(CCFLAGS=["/MDd"])
+ else:
+ env.Append(CCFLAGS=["/MD"])
+
+ else:
+ if get_option("link-model") != "dynamic":
+ env.FatalError("A dynamic runtime can only be forced with dynamic linking on this toolchain.")
+
+ if not env.ToolchainIs('gcc', 'clang'):
+ env.FatalError("Don't know how to force a dynamic runtime on this toolchain.")
+
+if dynamicRT == "off" and env.ToolchainIs('msvc'):
+ if debugBuild:
+ env.Append(CCFLAGS=["/MTd"])
+ else:
+ env.Append(CCFLAGS=["/MT"])
+
+
if optBuild:
env.SetConfigHeaderDefine("MONGO_CONFIG_OPTIMIZED_BUILD")
@@ -2090,11 +2234,6 @@ elif env.TargetOSIs('windows'):
if not any(flag.startswith('/DEBUG') for flag in env['LINKFLAGS']):
env.Append(LINKFLAGS=["/DEBUG"])
- # /MD: use the multithreaded, DLL version of the run-time library (MSVCRT.lib/MSVCR###.DLL)
- # /MDd: Defines _DEBUG, _MT, _DLL, and uses MSVCRTD.lib/MSVCRD###.DLL
-
- env.Append(CCFLAGS=["/MDd" if debugBuild else "/MD"])
-
if optBuild:
# /O1: optimize for size
# /O2: optimize for speed (as opposed to size)
@@ -2226,7 +2365,6 @@ if env.TargetOSIs('posix'):
"-fno-strict-aliasing",
"-fasynchronous-unwind-tables",
"-ggdb" if not env.TargetOSIs('emscripten') else "-g",
- "-pthread",
"-Wall",
"-Wsign-compare",
"-Wno-unknown-pragmas",
@@ -2243,8 +2381,14 @@ if env.TargetOSIs('posix'):
# On OS X, clang doesn't want the pthread flag at link time, or it
# issues warnings which make it impossible for us to declare link
# warnings as errors. See http://stackoverflow.com/a/19382663.
- if not (env.TargetOSIs('darwin') and env.ToolchainIs('clang')):
- env.Append( LINKFLAGS=["-pthread"] )
+ #
+ # We don't need it anyway since we explicitly link to -lpthread,
+ # so all we need beyond that is the preprocessor variable.
+ if not env.ToolchainIs('clang'):
+ env.Append(
+ CPPDEFINES=[("_REENTRANT", "1")],
+ LINKFLAGS=["-pthread"]
+ )
# SERVER-9761: Ensure early detection of missing symbols in dependent libraries at program
# startup.
@@ -3810,8 +3954,10 @@ def doConfigure(myenv):
language='C++')
if posix_system:
conf.env.SetConfigHeaderDefine("MONGO_CONFIG_HAVE_HEADER_UNISTD_H")
+ conf.CheckLib('c')
conf.CheckLib('rt')
conf.CheckLib('dl')
+ conf.CheckLib('pthread')
if posix_monotonic_clock:
conf.env.SetConfigHeaderDefine("MONGO_CONFIG_HAVE_POSIX_MONOTONIC_CLOCK")
@@ -4777,6 +4923,7 @@ module_sconscripts = moduleconfig.get_module_sconscripts(mongo_modules)
# and they are exported here, as well.
Export([
'debugBuild',
+ 'dynamicRT',
'endian',
'free_monitoring',
'get_option',
@@ -5007,4 +5154,4 @@ for i, s in enumerate(BUILD_TARGETS):
# SConscripts have been read but before building begins.
if get_option('build-tools') == 'next':
libdeps.LibdepLinter(env).final_checks()
- libdeps.generate_libdeps_graph(env) \ No newline at end of file
+ libdeps.generate_libdeps_graph(env)