1# -*- Python -*-
2
3import os
4import platform
5import re
6
7import lit.formats
8
9# Get shlex.quote if available (added in 3.3), and fall back to pipes.quote if
10# it's not available.
11try:
12  import shlex
13  sh_quote = shlex.quote
14except:
15  import pipes
16  sh_quote = pipes.quote
17
18def get_required_attr(config, attr_name):
19  attr_value = getattr(config, attr_name, None)
20  if attr_value == None:
21    lit_config.fatal(
22      "No attribute %r in test configuration! You may need to run "
23      "tests from your build directory or add this attribute "
24      "to lit.site.cfg.py " % attr_name)
25  return attr_value
26
27def push_dynamic_library_lookup_path(config, new_path):
28  if platform.system() == 'Windows':
29    dynamic_library_lookup_var = 'PATH'
30  elif platform.system() == 'Darwin':
31    dynamic_library_lookup_var = 'DYLD_LIBRARY_PATH'
32  else:
33    dynamic_library_lookup_var = 'LD_LIBRARY_PATH'
34
35  new_ld_library_path = os.path.pathsep.join(
36    (new_path, config.environment.get(dynamic_library_lookup_var, '')))
37  config.environment[dynamic_library_lookup_var] = new_ld_library_path
38
39  if platform.system() == 'FreeBSD':
40    dynamic_library_lookup_var = 'LD_32_LIBRARY_PATH'
41    new_ld_32_library_path = os.path.pathsep.join(
42      (new_path, config.environment.get(dynamic_library_lookup_var, '')))
43    config.environment[dynamic_library_lookup_var] = new_ld_32_library_path
44
45  if platform.system() == 'SunOS':
46    dynamic_library_lookup_var = 'LD_LIBRARY_PATH_32'
47    new_ld_library_path_32 = os.path.pathsep.join(
48      (new_path, config.environment.get(dynamic_library_lookup_var, '')))
49    config.environment[dynamic_library_lookup_var] = new_ld_library_path_32
50
51    dynamic_library_lookup_var = 'LD_LIBRARY_PATH_64'
52    new_ld_library_path_64 = os.path.pathsep.join(
53      (new_path, config.environment.get(dynamic_library_lookup_var, '')))
54    config.environment[dynamic_library_lookup_var] = new_ld_library_path_64
55
56# Setup config name.
57config.name = 'AddressSanitizer' + config.name_suffix
58
59# Platform-specific default ASAN_OPTIONS for lit tests.
60default_asan_opts = list(config.default_sanitizer_opts)
61
62# On Darwin, leak checking is not enabled by default. Enable for x86_64
63# tests to prevent regressions
64if config.host_os == 'Darwin' and config.target_arch == 'x86_64':
65  default_asan_opts += ['detect_leaks=1']
66
67default_asan_opts_str = ':'.join(default_asan_opts)
68if default_asan_opts_str:
69  config.environment['ASAN_OPTIONS'] = default_asan_opts_str
70  default_asan_opts_str += ':'
71config.substitutions.append(('%env_asan_opts=',
72                             'env ASAN_OPTIONS=' + default_asan_opts_str))
73
74# Setup source root.
75config.test_source_root = os.path.dirname(__file__)
76
77if config.host_os not in ['FreeBSD', 'NetBSD']:
78  libdl_flag = "-ldl"
79else:
80  libdl_flag = ""
81
82# GCC-ASan doesn't link in all the necessary libraries automatically, so
83# we have to do it ourselves.
84if config.compiler_id == 'GNU':
85  extra_link_flags = ["-pthread", "-lstdc++", libdl_flag]
86else:
87  extra_link_flags = []
88
89# Setup default compiler flags used with -fsanitize=address option.
90# FIXME: Review the set of required flags and check if it can be reduced.
91target_cflags = [get_required_attr(config, "target_cflags")] + extra_link_flags
92target_cxxflags = config.cxx_mode_flags + target_cflags
93clang_asan_static_cflags = (["-fsanitize=address",
94                            "-mno-omit-leaf-frame-pointer",
95                            "-fno-omit-frame-pointer",
96                            "-fno-optimize-sibling-calls"] +
97                            config.debug_info_flags + target_cflags)
98if config.target_arch == 's390x':
99  clang_asan_static_cflags.append("-mbackchain")
100clang_asan_static_cxxflags = config.cxx_mode_flags + clang_asan_static_cflags
101
102asan_dynamic_flags = []
103if config.asan_dynamic:
104  asan_dynamic_flags = ["-shared-libasan"]
105  if platform.system() == 'Windows':
106    # On Windows, we need to simulate "clang-cl /MD" on the clang driver side.
107    asan_dynamic_flags += ["-D_MT", "-D_DLL", "-Wl,-nodefaultlib:libcmt,-defaultlib:msvcrt,-defaultlib:oldnames"]
108  elif platform.system() == 'FreeBSD':
109    # On FreeBSD, we need to add -pthread to ensure pthread functions are available.
110    asan_dynamic_flags += ['-pthread']
111  config.available_features.add("asan-dynamic-runtime")
112else:
113  config.available_features.add("asan-static-runtime")
114clang_asan_cflags = clang_asan_static_cflags + asan_dynamic_flags
115clang_asan_cxxflags = clang_asan_static_cxxflags + asan_dynamic_flags
116
117# Add win32-(static|dynamic)-asan features to mark tests as passing or failing
118# in those modes. lit doesn't support logical feature test combinations.
119if platform.system() == 'Windows':
120  if config.asan_dynamic:
121    win_runtime_feature = "win32-dynamic-asan"
122  else:
123    win_runtime_feature = "win32-static-asan"
124  config.available_features.add(win_runtime_feature)
125
126def build_invocation(compile_flags):
127  return " " + " ".join([config.clang] + compile_flags) + " "
128
129config.substitutions.append( ("%clang ", build_invocation(target_cflags)) )
130config.substitutions.append( ("%clangxx ", build_invocation(target_cxxflags)) )
131config.substitutions.append( ("%clang_asan ", build_invocation(clang_asan_cflags)) )
132config.substitutions.append( ("%clangxx_asan ", build_invocation(clang_asan_cxxflags)) )
133if config.asan_dynamic:
134  if config.host_os in ['Linux', 'FreeBSD', 'NetBSD', 'SunOS']:
135    shared_libasan_path = os.path.join(config.compiler_rt_libdir, "libclang_rt.asan{}.so".format(config.target_suffix))
136  elif config.host_os == 'Darwin':
137    shared_libasan_path = os.path.join(config.compiler_rt_libdir, 'libclang_rt.asan_{}_dynamic.dylib'.format(config.apple_platform))
138  else:
139    lit_config.warning('%shared_libasan substitution not set but dynamic ASan is available.')
140    shared_libasan_path = None
141
142  if shared_libasan_path is not None:
143    config.substitutions.append( ("%shared_libasan", shared_libasan_path) )
144  config.substitutions.append( ("%clang_asan_static ", build_invocation(clang_asan_static_cflags)) )
145  config.substitutions.append( ("%clangxx_asan_static ", build_invocation(clang_asan_static_cxxflags)) )
146
147# Windows-specific tests might also use the clang-cl.exe driver.
148if platform.system() == 'Windows':
149  clang_cl_cxxflags = ["-Wno-deprecated-declarations",
150                       "-WX",
151                       "-D_HAS_EXCEPTIONS=0",
152                       "-Zi"] + target_cflags
153  clang_cl_asan_cxxflags = ["-fsanitize=address"] + clang_cl_cxxflags
154  if config.asan_dynamic:
155    clang_cl_asan_cxxflags.append("-MD")
156
157  clang_cl_invocation = build_invocation(clang_cl_cxxflags)
158  clang_cl_invocation = clang_cl_invocation.replace("clang.exe","clang-cl.exe")
159  config.substitutions.append( ("%clang_cl ", clang_cl_invocation) )
160
161  clang_cl_asan_invocation = build_invocation(clang_cl_asan_cxxflags)
162  clang_cl_asan_invocation = clang_cl_asan_invocation.replace("clang.exe","clang-cl.exe")
163  config.substitutions.append( ("%clang_cl_asan ", clang_cl_asan_invocation) )
164
165  base_lib = os.path.join(config.compiler_rt_libdir, "clang_rt.asan%%s%s.lib" % config.target_suffix)
166  config.substitutions.append( ("%asan_lib", base_lib % "") )
167  config.substitutions.append( ("%asan_cxx_lib", base_lib % "_cxx") )
168  config.substitutions.append( ("%asan_dll_thunk", base_lib % "_dll_thunk") )
169
170if platform.system() == 'Windows':
171  # Don't use -std=c++11 on Windows, as the driver will detect the appropriate
172  # default needed to use with the STL.
173  config.substitutions.append(("%stdcxx11 ", ""))
174else:
175  # Some tests uses C++11 features such as lambdas and need to pass -std=c++11.
176  config.substitutions.append(("%stdcxx11 ", "-std=c++11 "))
177
178# FIXME: De-hardcode this path.
179asan_source_dir = os.path.join(
180  get_required_attr(config, "compiler_rt_src_root"), "lib", "asan")
181python_exec = sh_quote(get_required_attr(config, "python_executable"))
182# Setup path to asan_symbolize.py script.
183asan_symbolize = os.path.join(asan_source_dir, "scripts", "asan_symbolize.py")
184if not os.path.exists(asan_symbolize):
185  lit_config.fatal("Can't find script on path %r" % asan_symbolize)
186config.substitutions.append( ("%asan_symbolize", python_exec + " " + asan_symbolize + " ") )
187# Setup path to sancov.py script.
188sanitizer_common_source_dir = os.path.join(
189  get_required_attr(config, "compiler_rt_src_root"), "lib", "sanitizer_common")
190sancov = os.path.join(sanitizer_common_source_dir, "scripts", "sancov.py")
191if not os.path.exists(sancov):
192  lit_config.fatal("Can't find script on path %r" % sancov)
193config.substitutions.append( ("%sancov ", python_exec + " " + sancov + " ") )
194
195# Determine kernel bitness
196if config.host_arch.find('64') != -1 and not config.android:
197  kernel_bits = '64'
198else:
199  kernel_bits = '32'
200
201config.substitutions.append( ('CHECK-%kernel_bits', ("CHECK-kernel-" + kernel_bits + "-bits")))
202
203config.substitutions.append( ("%libdl", libdl_flag) )
204
205config.available_features.add("asan-" + config.bits + "-bits")
206
207# Fast unwinder doesn't work with Thumb
208if not config.arm_thumb:
209  config.available_features.add('fast-unwinder-works')
210
211# Turn on leak detection on 64-bit Linux.
212leak_detection_android = config.android and 'android-thread-properties-api' in config.available_features and (config.target_arch in ['x86_64', 'i386', 'i686', 'aarch64'])
213leak_detection_linux = (config.host_os == 'Linux') and (not config.android) and (config.target_arch in ['x86_64', 'i386', 'riscv64'])
214leak_detection_mac = (config.host_os == 'Darwin') and (config.target_arch == 'x86_64')
215leak_detection_netbsd = (config.host_os == 'NetBSD') and (config.target_arch in ['x86_64', 'i386'])
216if leak_detection_android or leak_detection_linux or leak_detection_mac or leak_detection_netbsd:
217  config.available_features.add('leak-detection')
218
219# Set LD_LIBRARY_PATH to pick dynamic runtime up properly.
220push_dynamic_library_lookup_path(config, config.compiler_rt_libdir)
221
222# GCC-ASan uses dynamic runtime by default.
223if config.compiler_id == 'GNU':
224  gcc_dir = os.path.dirname(config.clang)
225  libasan_dir = os.path.join(gcc_dir, "..", "lib" + config.bits)
226  push_dynamic_library_lookup_path(config, libasan_dir)
227
228# Add the RT libdir to PATH directly so that we can successfully run the gtest
229# binary to list its tests.
230if config.host_os == 'Windows' and config.asan_dynamic:
231  os.environ['PATH'] = os.path.pathsep.join([config.compiler_rt_libdir,
232                                             os.environ.get('PATH', '')])
233
234# Default test suffixes.
235config.suffixes = ['.c', '.cpp']
236
237if config.host_os == 'Darwin':
238  config.suffixes.append('.mm')
239
240if config.host_os == 'Windows':
241  config.substitutions.append(('%fPIC', ''))
242  config.substitutions.append(('%fPIE', ''))
243  config.substitutions.append(('%pie', ''))
244else:
245  config.substitutions.append(('%fPIC', '-fPIC'))
246  config.substitutions.append(('%fPIE', '-fPIE'))
247  config.substitutions.append(('%pie', '-pie'))
248
249# Only run the tests on supported OSs.
250if config.host_os not in ['Linux', 'Darwin', 'FreeBSD', 'SunOS', 'Windows', 'NetBSD']:
251  config.unsupported = True
252
253if not config.parallelism_group:
254  config.parallelism_group = 'shadow-memory'
255
256if config.host_os == 'NetBSD':
257  config.substitutions.insert(0, ('%run', config.netbsd_noaslr_prefix))
258