diff --git a/AMBuildScript b/AMBuildScript index 85c12c97..6e2fd936 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -1,6 +1,7 @@ # vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: import collections import os, sys +import subprocess import traceback class SDK(object): @@ -121,7 +122,8 @@ class SMConfig(object): self.versionlib = None self.all_targets = [] self.target_archs = set() - + self.enable_asan = getattr(builder.options, 'enable_asan', False) + self.asan_libs = {} if builder.options.targets: target_archs = builder.options.targets.split(',') @@ -349,6 +351,11 @@ class SMConfig(object): cxx.cxxflags += ['-Wno-deprecated'] cxx.cflags += ['-Wno-sometimes-uninitialized'] + if self.enable_asan: + if not have_clang: + raise Exception('--enable-asan only supported when using Clang') + self.configure_asan(cxx) + # Work around SDK warnings. if cxx.version >= 'clang-10.0' or cxx.version >= 'apple-clang-12.0': cxx.cflags += [ @@ -361,12 +368,18 @@ class SMConfig(object): cxx.cflags += ['-Wno-maybe-uninitialized'] if builder.options.opt == '1': - cxx.cflags += ['-O3'] + if self.enable_asan: + cxx.cflags += ['-O1'] + else: + cxx.cflags += ['-O3'] # Don't omit the frame pointer. cxx.cflags += ['-fno-omit-frame-pointer'] def configure_msvc(self, cxx): + if self.enable_asan: + raise Exception('--enable-asan only supported when using Clang') + if builder.options.debug == '1': cxx.cflags += ['/MTd'] cxx.linkflags += ['/NODEFAULTLIB:libcmt'] @@ -412,6 +425,29 @@ class SMConfig(object): # Don't omit the frame pointer. cxx.cflags += ['/Oy-'] + def configure_asan(self, cxx): + if cxx.target.platform != 'linux': + raise Exception('--enable-asan only supported on Linux') + cxx.cflags += ['-fsanitize=address'] + cxx.linkflags += ['-fsanitize=address'] + if cxx.target.arch == 'x86': + libclang_rt = 'libclang_rt.asan-i386.so' + else: + libclang_rt = 'libclang_rt.asan-x86_64.so' + + try: + argv = cxx.cxx_argv + ['--print-file-name', libclang_rt] + output = subprocess.check_output(argv) + output = output.decode('utf-8') + output = output.strip() + except: + raise Exception('Could not find {}'.format(libclang_rt)) + + print('ASAN library for {}: {}'.format(cxx.target.arch, output)) + print('You will need to LD_PRELOAD this into srcds.') + + self.asan_libs[cxx.target.arch] = os.path.dirname(output) + def configure_linux(self, cxx): cxx.defines += ['_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64'] cxx.linkflags += ['-lm'] @@ -469,6 +505,13 @@ class SMConfig(object): self.AddVersioning(binary) if binary.compiler.like('msvc'): binary.compiler.linkflags += ['/SUBSYSTEM:WINDOWS'] + + # Dumb clang behavior means we have to manually find libclang_rt. + if self.enable_asan: + binary.compiler.linkflags += [ + '-shared-libsan', + '-Wl,-rpath={}'.format(self.asan_libs[binary.compiler.target.arch]), + ] return binary def ProgramBuilder(self, compiler, name): @@ -476,6 +519,8 @@ class SMConfig(object): self.AddVersioning(binary) if '-static-libgcc' in binary.compiler.linkflags: binary.compiler.linkflags.remove('-static-libgcc') + if self.enable_asan: + binary.compiler.linkflags.append('-static-libsan') if '-lgcc_eh' in binary.compiler.linkflags: binary.compiler.linkflags.remove('-lgcc_eh') if binary.compiler.like('gcc'): diff --git a/configure.py b/configure.py index f864d939..edb64371 100644 --- a/configure.py +++ b/configure.py @@ -1,30 +1,30 @@ # vim: set ts=2 sw=2 tw=99 noet: import sys try: - from ambuild2 import run, util + from ambuild2 import run, util except: - try: - import ambuild - sys.stderr.write('It looks like you have AMBuild 1 installed, but this project uses AMBuild 2.\n') - sys.stderr.write('Upgrade to the latest version of AMBuild to continue.\n') - except: - sys.stderr.write('AMBuild must be installed to build this project.\n') - sys.stderr.write('http://www.alliedmods.net/ambuild\n') - sys.exit(1) + try: + import ambuild + sys.stderr.write('It looks like you have AMBuild 1 installed, but this project uses AMBuild 2.\n') + sys.stderr.write('Upgrade to the latest version of AMBuild to continue.\n') + except: + sys.stderr.write('AMBuild must be installed to build this project.\n') + sys.stderr.write('http://www.alliedmods.net/ambuild\n') + sys.exit(1) # Hack to show a decent upgrade message, which wasn't done until 2.2. ambuild_version = getattr(run, 'CURRENT_API', '2.1') if ambuild_version.startswith('2.1'): - sys.stderr.write("AMBuild 2.2 or higher is required; please update\n") - sys.exit(1) + sys.stderr.write("AMBuild 2.2 or higher is required; please update\n") + sys.exit(1) parser = run.BuildParser(sourcePath=sys.path[0], api='2.2') parser.options.add_argument('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None, - help='Root search folder for HL2SDKs') + help='Root search folder for HL2SDKs') parser.options.add_argument('--mysql-path', type=str, dest='mysql_path', default=None, - help='Path to MySQL 5') + help='Path to MySQL 5') parser.options.add_argument('--mysql64-path', type=str, dest='mysql64_path', default=None, - help='Path to 64-bit MySQL 5') + help='Path to 64-bit MySQL 5') parser.options.add_argument('--mms-path', type=str, dest='mms_path', default=None, help='Path to Metamod:Source') parser.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug', @@ -37,11 +37,13 @@ parser.options.add_argument('-s', '--sdks', default='present', dest='sdks', help='Build against specified SDKs; valid args are "none", "all", "present",' ' or comma-delimited list of engine names') parser.options.add_argument('--breakpad-dump', action='store_true', dest='breakpad_dump', - default=False, help='Dump and upload breakpad symbols') + default=False, help='Dump and upload breakpad symbols') parser.options.add_argument('--disable-auto-versioning', action='store_true', dest='disable_auto_versioning', default=False, help='Disable the auto versioning script') parser.options.add_argument('--targets', type=str, dest='targets', default=None, - help="Override the target architecture (use commas to separate multiple targets).") + help="Override the target architecture (use commas to separate multiple targets).") parser.options.add_argument('--scripting-only', action='store_true', dest='scripting_only', default=False, - help="Only build and package the files required for scripting in SourcePawn.") + help="Only build and package the files required for scripting in SourcePawn.") +parser.options.add_argument('--enable-asan', action='store_true', dest='enable_asan', + default=False, help='Enable ASAN (clang only)') parser.Configure()