summaryrefslogtreecommitdiff
path: root/site_scons
diff options
context:
space:
mode:
authorDaniel Moody <daniel.moody@mongodb.com>2020-10-09 01:18:53 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-10-09 03:22:14 +0000
commit899679127a04255c0f57222d8f5363b9728382f4 (patch)
tree136efebe4de83c73295d9a872bc713e7980baed3 /site_scons
parentde8c206fd8c2a8c1333d07e2d9b8fd481eb37131 (diff)
downloadmongo-899679127a04255c0f57222d8f5363b9728382f4.tar.gz
SERVER-49798 Added LIBDEPS_TYPEINFO for ubsan builds to add typeinfo dependencies.
Diffstat (limited to 'site_scons')
-rw-r--r--site_scons/libdeps_next.py98
-rw-r--r--site_scons/site_tools/next/ninja.py15
2 files changed, 105 insertions, 8 deletions
diff --git a/site_scons/libdeps_next.py b/site_scons/libdeps_next.py
index c39a37ff25b..ae3dbe9ae5b 100644
--- a/site_scons/libdeps_next.py
+++ b/site_scons/libdeps_next.py
@@ -59,7 +59,7 @@ import textwrap
import SCons.Errors
import SCons.Scanner
import SCons.Util
-
+import SCons
class Constants:
Libdeps = "LIBDEPS"
@@ -67,6 +67,7 @@ class Constants:
LibdepsDependents = "LIBDEPS_DEPENDENTS"
LibdepsInterface ="LIBDEPS_INTERFACE"
LibdepsPrivate = "LIBDEPS_PRIVATE"
+ LibdepsTypeinfo = "LIBDEPS_TYPEINFO"
LibdepsTags = "LIBDEPS_TAGS"
LibdepsTagExpansion = "LIBDEPS_TAG_EXPANSIONS"
MissingLibdep = "MISSING_LIBDEP_"
@@ -76,7 +77,7 @@ class Constants:
SysLibdepsPrivate = "SYSLIBDEPS_PRIVATE"
class dependency:
- Public, Private, Interface = list(range(3))
+ Public, Private, Interface, Typeinfo = list(range(4))
def __init__(self, value, deptype, listed_name):
self.target_node = value
@@ -564,10 +565,18 @@ dependency_visibility_honored = {
dependency.Interface: dependency.Interface,
}
+dependency_visibility_typeinfo = {
+ dependency.Public: dependency.Public,
+ dependency.Private: dependency.Private,
+ dependency.Interface: dependency.Interface,
+ dependency.Typeinfo: dependency.Private,
+}
+
dep_type_to_env_var = {
dependency.Public: Constants.Libdeps,
dependency.Private: Constants.LibdepsPrivate,
dependency.Interface: Constants.LibdepsInterface,
+ dependency.Typeinfo: Constants.LibdepsTypeinfo,
}
class DependencyCycleError(SCons.Errors.UserError):
@@ -954,8 +963,59 @@ def expand_libdeps_with_flags(source, target, env, for_signature):
return libdeps_with_flags
-
-def setup_environment(env, emitting_shared=False, linting='on'):
+def get_typeinfo_link_command():
+ if LibdepLinter.skip_linting:
+ return "{ninjalink}"
+ else:
+ return (
+ # This command is has flexibility to be used by the ninja tool, but the ninja tool
+ # does not currently support list actions as the command is used below, so we can
+ # allow ninja to put its link command here in front simulating a list action. For
+ # a normal scons build ninjalink should be empty string.
+ # TODO: When the ninja tool supports multi rule type link actions this can go
+ # away and turn into functions actions.
+ # https://jira.mongodb.org/browse/SERVER-51435
+ # https://jira.mongodb.org/browse/SERVER-51436
+ "{ninjalink}"
+
+ # Dependencies must exist in a shared lib that was built as a
+ # dependent to the current node, so LD_LIBRARY_PATH should be set to
+ # include all the LIBDEPS directories.
+ + "UNDEF_SYMBOLS=$$({ldpath}ldd -r {target} "
+
+ # filter out only undefined symbols and then remove the sanitizer symbols because
+ # we are focused on the missing mongo db symbols.
+ + "| grep '^undefined symbol: ' | grep -v '__[a-z]*san' "
+
+ # Demangle the remaining symbols, and filter out mongo typeinfo symbols for the current library.
+ + "| c++filt | grep 'mongo::' | grep 'typeinfo for' | grep {target}) "
+
+ # Check if a exemption tag exists to see if we should skip this error, or if
+ # we are in libdeps printing mode, disregard its existence since we will always print
+ + "&& {} ".format("true" if LibdepLinter.print_linter_errors else " [ -z \"$$({libdeps_tags} | grep \"{tag}\")\" ]")
+
+ # output the missing symbols to stdout and use sed to insert tabs at the front of
+ # each new line for neatness.
+ + r"""&& echo '\n\tLibdepLinter:\n\t\tMissing typeinfo definitions:' """
+ + r"""&& echo "$$UNDEF_SYMBOLS\n" | sed 's/^/\t\t\t/g' 1>&2 """
+
+ # Fail the build if we found issues and are not in print mode.
+ + "&& return {} ".format(int(not LibdepLinter.print_linter_errors))
+
+ # The final checks will pass the build if the tag exists or no symbols were found.
+ + "|| {libdeps_tags} | grep -q \"{tag}\" || [ -z \"$$UNDEF_SYMBOLS\" ]")
+
+def get_libdeps_ld_path(source, target, env, for_signature):
+ result = ""
+ for libdep in env['_LIBDEPS_GET_LIBS'](source, target, env, for_signature):
+ if libdep:
+ result += os.path.dirname(str(libdep)) + ":"
+ if result:
+ result = result[:-1]
+
+ return [result]
+
+def setup_environment(env, emitting_shared=False, linting='on', sanitize_typeinfo=False):
"""Set up the given build environment to do LIBDEPS tracking."""
LibdepLinter.skip_linting = linting == 'off'
@@ -974,6 +1034,34 @@ def setup_environment(env, emitting_shared=False, linting='on'):
env[Constants.Libdeps] = SCons.Util.CLVar()
env[Constants.SysLibdeps] = SCons.Util.CLVar()
+ if sanitize_typeinfo and not LibdepLinter.skip_linting:
+
+ # Some sanitizers, notably the vptr check of ubsan, can cause
+ # additional symbol dependencies to exist. Unfortunately,
+ # building with the sanitizers also requires that we not build
+ # with -z,defs, which means that we cannot make such undefined
+ # symbols errors at link time. We can however hack together
+ # something which looks for undefined typeinfo nodes in the
+ # mongo namespace using `ldd -r`. See
+ # https://jira.mongodb.org/browse/SERVER-49798 for more
+ # details.
+ env["_LIBDEPS_LD_PATH"] = get_libdeps_ld_path
+
+ base_action = env['BUILDERS']['SharedLibrary'].action
+ if not isinstance(base_action, SCons.Action.ListAction):
+ base_action = SCons.Action.ListAction([base_action])
+
+ base_action.list.extend([
+ SCons.Action.Action(
+ get_typeinfo_link_command().format(
+ ninjalink="",
+ ldpath="LD_LIBRARY_PATH=$_LIBDEPS_LD_PATH ",
+ target="${TARGET}",
+ libdeps_tags="echo \"$LIBDEPS_TAGS\"",
+ tag='libdeps-cyclic-typeinfo'),
+ None)
+ ])
+
# We need a way for environments to alter just which libdeps
# emitter they want, without altering the overall program or
# library emitter which may have important effects. The
@@ -992,7 +1080,7 @@ def setup_environment(env, emitting_shared=False, linting='on'):
LIBDEPS_SHAREMITTER=make_libdeps_emitter("SharedArchive", ignore_progdeps=True),
SHAREMITTER=make_indirect_emitter("LIBDEPS_SHAREMITTER"),
LIBDEPS_SHLIBEMITTER=make_libdeps_emitter(
- "SharedLibrary", dependency_visibility_honored
+ "SharedLibrary", dependency_visibility_typeinfo if sanitize_typeinfo else dependency_visibility_honored
),
SHLIBEMITTER=make_indirect_emitter("LIBDEPS_SHLIBEMITTER"),
LIBDEPS_PROGEMITTER=make_libdeps_emitter(
diff --git a/site_scons/site_tools/next/ninja.py b/site_scons/site_tools/next/ninja.py
index 6448bf428b5..17254013914 100644
--- a/site_scons/site_tools/next/ninja.py
+++ b/site_scons/site_tools/next/ninja.py
@@ -942,13 +942,13 @@ def get_command_env(env):
# doesn't make builds on paths with spaces (Ninja and SCons issues)
# nor expanding response file paths with spaces (Ninja issue) work.
value = value.replace(r' ', r'$ ')
- command_env += "{}='{}' ".format(key, value)
+ command_env += "export {}='{}';".format(key, value)
env["NINJA_ENV_VAR_CACHE"] = command_env
return command_env
-def gen_get_response_file_command(env, rule, tool, tool_is_dynamic=False):
+def gen_get_response_file_command(env, rule, tool, tool_is_dynamic=False, custom_env={}):
"""Generate a response file command provider for rule name."""
# If win32 using the environment with a response file command will cause
@@ -996,6 +996,11 @@ def gen_get_response_file_command(env, rule, tool, tool_is_dynamic=False):
variables[rule] = cmd
if use_command_env:
variables["env"] = get_command_env(env)
+
+ for key, value in custom_env.items():
+ variables["env"] += env.subst(
+ f"export {key}={value};", target=targets, source=sources, executor=executor
+ ) + " "
return rule, variables, [tool_command]
return get_response_file_command
@@ -1176,7 +1181,7 @@ def register_custom_rule_mapping(env, pre_subst_string, rule):
__NINJA_RULE_MAPPING[pre_subst_string] = rule
-def register_custom_rule(env, rule, command, description="", deps=None, pool=None, use_depfile=False):
+def register_custom_rule(env, rule, command, description="", deps=None, pool=None, use_depfile=False, use_response_file=False):
"""Allows specification of Ninja rules from inside SCons files."""
rule_obj = {
"command": command,
@@ -1192,6 +1197,10 @@ def register_custom_rule(env, rule, command, description="", deps=None, pool=Non
if pool is not None:
rule_obj["pool"] = pool
+ if use_response_file:
+ rule_obj["rspfile"] = "$out.rsp"
+ rule_obj["rspfile_content"] = "$rspc"
+
env[NINJA_RULES][rule] = rule_obj