1# -*- Python -*- 2 3import os 4import platform 5import re 6import shlex 7 8import lit.formats 9 10 11def get_required_attr(config, attr_name): 12 attr_value = getattr(config, attr_name, None) 13 if attr_value is None: 14 lit_config.fatal( 15 "No attribute %r in test configuration! You may need to run " 16 "tests from your build directory or add this attribute " 17 "to lit.site.cfg.py " % attr_name 18 ) 19 return attr_value 20 21# Setup config name. 22config.name = "AddressSanitizer" + config.name_suffix 23 24# Platform-specific default ASAN_OPTIONS for lit tests. 25default_asan_opts = list(config.default_sanitizer_opts) 26 27# On Darwin, leak checking is not enabled by default. Enable on macOS 28# tests to prevent regressions 29if config.host_os == "Darwin" and config.apple_platform == "osx": 30 default_asan_opts += ["detect_leaks=1"] 31 32default_asan_opts_str = ":".join(default_asan_opts) 33if default_asan_opts_str: 34 config.environment["ASAN_OPTIONS"] = default_asan_opts_str 35 default_asan_opts_str += ":" 36config.substitutions.append( 37 ("%env_asan_opts=", "env ASAN_OPTIONS=" + default_asan_opts_str) 38) 39 40# Setup source root. 41config.test_source_root = os.path.dirname(__file__) 42 43if config.host_os not in ["FreeBSD", "NetBSD"]: 44 libdl_flag = "-ldl" 45else: 46 libdl_flag = "" 47 48# GCC-ASan doesn't link in all the necessary libraries automatically, so 49# we have to do it ourselves. 50if config.compiler_id == "GNU": 51 extra_link_flags = ["-pthread", "-lstdc++", libdl_flag] 52else: 53 extra_link_flags = [] 54 55# Setup default compiler flags used with -fsanitize=address option. 56# FIXME: Review the set of required flags and check if it can be reduced. 57target_cflags = [get_required_attr(config, "target_cflags")] + extra_link_flags 58target_cxxflags = config.cxx_mode_flags + target_cflags 59clang_asan_static_cflags = ( 60 [ 61 "-fsanitize=address", 62 "-mno-omit-leaf-frame-pointer", 63 "-fno-omit-frame-pointer", 64 "-fno-optimize-sibling-calls", 65 ] 66 + config.debug_info_flags 67 + target_cflags 68) 69if config.target_arch == "s390x": 70 clang_asan_static_cflags.append("-mbackchain") 71clang_asan_static_cxxflags = config.cxx_mode_flags + clang_asan_static_cflags 72 73target_is_msvc = bool(re.match(r".*-windows-msvc$", config.target_triple)) 74 75asan_dynamic_flags = [] 76if config.asan_dynamic: 77 asan_dynamic_flags = ["-shared-libasan"] 78 if platform.system() == "Windows" and target_is_msvc: 79 # On MSVC target, we need to simulate "clang-cl /MD" on the clang driver side. 80 asan_dynamic_flags += [ 81 "-D_MT", 82 "-D_DLL", 83 "-Wl,-nodefaultlib:libcmt,-defaultlib:msvcrt,-defaultlib:oldnames", 84 ] 85 elif platform.system() == "FreeBSD": 86 # On FreeBSD, we need to add -pthread to ensure pthread functions are available. 87 asan_dynamic_flags += ["-pthread"] 88 config.available_features.add("asan-dynamic-runtime") 89else: 90 config.available_features.add("asan-static-runtime") 91clang_asan_cflags = clang_asan_static_cflags + asan_dynamic_flags 92clang_asan_cxxflags = clang_asan_static_cxxflags + asan_dynamic_flags 93 94# Add win32-(static|dynamic)-asan features to mark tests as passing or failing 95# in those modes. lit doesn't support logical feature test combinations. 96if platform.system() == "Windows": 97 if config.asan_dynamic: 98 win_runtime_feature = "win32-dynamic-asan" 99 else: 100 win_runtime_feature = "win32-static-asan" 101 config.available_features.add(win_runtime_feature) 102 103 104def build_invocation(compile_flags, with_lto=False): 105 lto_flags = [] 106 if with_lto and config.lto_supported: 107 lto_flags += config.lto_flags 108 109 return " " + " ".join([config.clang] + lto_flags + compile_flags) + " " 110 111 112config.substitutions.append(("%clang ", build_invocation(target_cflags))) 113config.substitutions.append(("%clangxx ", build_invocation(target_cxxflags))) 114config.substitutions.append(("%clang_asan ", build_invocation(clang_asan_cflags))) 115config.substitutions.append(("%clangxx_asan ", build_invocation(clang_asan_cxxflags))) 116config.substitutions.append( 117 ("%clang_asan_lto ", build_invocation(clang_asan_cflags, True)) 118) 119config.substitutions.append( 120 ("%clangxx_asan_lto ", build_invocation(clang_asan_cxxflags, True)) 121) 122if config.asan_dynamic: 123 if config.host_os in ["Linux", "FreeBSD", "NetBSD", "SunOS"]: 124 shared_libasan_path = os.path.join( 125 config.compiler_rt_libdir, 126 "libclang_rt.asan{}.so".format(config.target_suffix), 127 ) 128 elif config.host_os == "Darwin": 129 shared_libasan_path = os.path.join( 130 config.compiler_rt_libdir, 131 "libclang_rt.asan_{}_dynamic.dylib".format(config.apple_platform), 132 ) 133 elif config.host_os == "Windows": 134 shared_libasan_path = os.path.join( 135 config.compiler_rt_libdir, 136 "clang_rt.asan_dynamic-{}.lib".format(config.target_suffix), 137 ) 138 else: 139 lit_config.warning( 140 "%shared_libasan substitution not set but dynamic ASan is available." 141 ) 142 shared_libasan_path = None 143 144 if shared_libasan_path is not None: 145 config.substitutions.append(("%shared_libasan", shared_libasan_path)) 146 config.substitutions.append( 147 ("%clang_asan_static ", build_invocation(clang_asan_static_cflags)) 148 ) 149 config.substitutions.append( 150 ("%clangxx_asan_static ", build_invocation(clang_asan_static_cxxflags)) 151 ) 152 153if platform.system() == "Windows": 154 # MSVC-specific tests might also use the clang-cl.exe driver. 155 if target_is_msvc: 156 clang_cl_cxxflags = ( 157 [ 158 "-WX", 159 "-D_HAS_EXCEPTIONS=0", 160 ] 161 + config.debug_info_flags 162 + target_cflags 163 ) 164 if config.compiler_id != "MSVC": 165 clang_cl_cxxflags = ["-Wno-deprecated-declarations"] + clang_cl_cxxflags 166 clang_cl_asan_cxxflags = ["-fsanitize=address"] + clang_cl_cxxflags 167 if config.asan_dynamic: 168 clang_cl_asan_cxxflags.append("-MD") 169 170 clang_cl_invocation = build_invocation(clang_cl_cxxflags) 171 clang_cl_invocation = clang_cl_invocation.replace("clang.exe", "clang-cl.exe") 172 config.substitutions.append(("%clang_cl ", clang_cl_invocation)) 173 174 clang_cl_asan_invocation = build_invocation(clang_cl_asan_cxxflags) 175 clang_cl_asan_invocation = clang_cl_asan_invocation.replace( 176 "clang.exe", "clang-cl.exe" 177 ) 178 config.substitutions.append(("%clang_cl_asan ", clang_cl_asan_invocation)) 179 config.substitutions.append(("%clang_cl_nocxx_asan ", clang_cl_asan_invocation)) 180 config.substitutions.append(("%Od", "-Od")) 181 config.substitutions.append(("%Fe", "-Fe")) 182 config.substitutions.append(("%LD", "-LD")) 183 config.substitutions.append(("%MD", "-MD")) 184 config.substitutions.append(("%MT", "-MT")) 185 config.substitutions.append(("%Gw", "-Gw")) 186 187 base_lib = os.path.join( 188 config.compiler_rt_libdir, "clang_rt.asan%%s%s.lib" % config.target_suffix 189 ) 190 config.substitutions.append(("%asan_lib", base_lib % "_dynamic")) 191 if config.asan_dynamic: 192 config.substitutions.append( 193 ("%asan_thunk", base_lib % "_dynamic_runtime_thunk") 194 ) 195 else: 196 config.substitutions.append( 197 ("%asan_thunk", base_lib % "_static_runtime_thunk") 198 ) 199 config.substitutions.append(("%asan_cxx_lib", base_lib % "_cxx")) 200 config.substitutions.append( 201 ("%asan_dynamic_runtime_thunk", base_lib % "_dynamic_runtime_thunk") 202 ) 203 config.substitutions.append( 204 ("%asan_static_runtime_thunk", base_lib % "_static_runtime_thunk") 205 ) 206 config.substitutions.append(("%asan_dll_thunk", base_lib % "_dll_thunk")) 207 else: 208 # To make some of these tests work on MinGW target without changing their 209 # behaviour for MSVC target, substitute clang-cl flags with gcc-like ones. 210 config.substitutions.append(("%clang_cl ", build_invocation(target_cxxflags))) 211 config.substitutions.append( 212 ("%clang_cl_asan ", build_invocation(clang_asan_cxxflags)) 213 ) 214 config.substitutions.append( 215 ("%clang_cl_nocxx_asan ", build_invocation(clang_asan_cflags)) 216 ) 217 config.substitutions.append(("%Od", "-O0")) 218 config.substitutions.append(("%Fe", "-o")) 219 config.substitutions.append(("%LD", "-shared")) 220 config.substitutions.append(("%MD", "")) 221 config.substitutions.append(("%MT", "")) 222 config.substitutions.append(("%Gw", "-fdata-sections")) 223 224# FIXME: De-hardcode this path. 225asan_source_dir = os.path.join( 226 get_required_attr(config, "compiler_rt_src_root"), "lib", "asan" 227) 228python_exec = shlex.quote(get_required_attr(config, "python_executable")) 229# Setup path to asan_symbolize.py script. 230asan_symbolize = os.path.join(asan_source_dir, "scripts", "asan_symbolize.py") 231if not os.path.exists(asan_symbolize): 232 lit_config.fatal("Can't find script on path %r" % asan_symbolize) 233config.substitutions.append( 234 ("%asan_symbolize", python_exec + " " + asan_symbolize + " ") 235) 236# Setup path to sancov.py script. 237sanitizer_common_source_dir = os.path.join( 238 get_required_attr(config, "compiler_rt_src_root"), "lib", "sanitizer_common" 239) 240sancov = os.path.join(sanitizer_common_source_dir, "scripts", "sancov.py") 241if not os.path.exists(sancov): 242 lit_config.fatal("Can't find script on path %r" % sancov) 243config.substitutions.append(("%sancov ", python_exec + " " + sancov + " ")) 244 245# Determine kernel bitness 246if config.host_arch.find("64") != -1 and not config.android: 247 kernel_bits = "64" 248else: 249 kernel_bits = "32" 250 251config.substitutions.append( 252 ("CHECK-%kernel_bits", ("CHECK-kernel-" + kernel_bits + "-bits")) 253) 254 255config.substitutions.append(("%libdl", libdl_flag)) 256 257config.available_features.add("asan-" + config.bits + "-bits") 258 259# Fast unwinder doesn't work with Thumb 260if not config.arm_thumb: 261 config.available_features.add("fast-unwinder-works") 262 263# Turn on leak detection on 64-bit Linux. 264leak_detection_android = ( 265 config.android 266 and "android-thread-properties-api" in config.available_features 267 and (config.target_arch in ["x86_64", "i386", "i686", "aarch64"]) 268) 269leak_detection_linux = ( 270 (config.host_os == "Linux") 271 and (not config.android) 272 and (config.target_arch in ["x86_64", "i386", "riscv64", "loongarch64"]) 273) 274leak_detection_mac = (config.host_os == "Darwin") and (config.apple_platform == "osx") 275leak_detection_netbsd = (config.host_os == "NetBSD") and ( 276 config.target_arch in ["x86_64", "i386"] 277) 278if ( 279 leak_detection_android 280 or leak_detection_linux 281 or leak_detection_mac 282 or leak_detection_netbsd 283): 284 config.available_features.add("leak-detection") 285 286# Add the RT libdir to PATH directly so that we can successfully run the gtest 287# binary to list its tests. 288if config.host_os == "Windows": 289 os.environ["PATH"] = os.path.pathsep.join( 290 [config.compiler_rt_libdir, os.environ.get("PATH", "")] 291 ) 292 293# msvc needs to be instructed where the compiler-rt libraries are 294if config.compiler_id == "MSVC": 295 config.environment["LIB"] = os.path.pathsep.join( 296 [config.compiler_rt_libdir, config.environment.get("LIB", "")] 297 ) 298 299# Default test suffixes. 300config.suffixes = [".c", ".cpp"] 301 302if config.host_os == "Darwin": 303 config.suffixes.append(".mm") 304 305if config.host_os == "Windows": 306 config.substitutions.append(("%fPIC", "")) 307 config.substitutions.append(("%fPIE", "")) 308 config.substitutions.append(("%pie", "")) 309else: 310 config.substitutions.append(("%fPIC", "-fPIC")) 311 config.substitutions.append(("%fPIE", "-fPIE")) 312 config.substitutions.append(("%pie", "-pie")) 313 314# Only run the tests on supported OSs. 315if config.host_os not in ["Linux", "Darwin", "FreeBSD", "SunOS", "Windows", "NetBSD"]: 316 config.unsupported = True 317 318if not config.parallelism_group: 319 config.parallelism_group = "shadow-memory" 320 321if config.host_os == "NetBSD": 322 config.substitutions.insert(0, ("%run", config.netbsd_noaslr_prefix)) 323