33import os
44import platform
55import re
6+ import shutil
67import subprocess
78import tempfile
89
@@ -77,6 +78,75 @@ def add_runtime(name):
7778 return ToolSubst (f"%{ name } " , find_runtime (name ))
7879
7980
81+ # Provide the path to asan runtime lib 'libclang_rt.asan_osx_dynamic.dylib' if
82+ # available. This is darwin specific since it's currently only needed on darwin.
83+ # Stolen from llvm/test/lit.cfg.py with a few modifications
84+ def get_asan_rtlib ():
85+ if not "asan" in config .available_features or not "Darwin" in config .host_os :
86+ return ""
87+ # Find the asan rt lib
88+ resource_dir = (
89+ subprocess .check_output ([config .host_cc .strip (), "-print-resource-dir" ])
90+ .decode ("utf-8" )
91+ .strip ()
92+ )
93+ return os .path .join (
94+ resource_dir , "lib" , "darwin" , "libclang_rt.asan_osx_dynamic.dylib"
95+ )
96+
97+
98+ # On macOS, we can't do the DYLD_INSERT_LIBRARIES trick with a shim python
99+ # binary as the ASan interceptors get loaded too late. Also, when SIP is
100+ # enabled, we can't inject libraries into system binaries at all, so we need a
101+ # copy of the "real" python to work with.
102+ # Stolen from lldb/test/API/lit.cfg.py with a few modifications
103+ def find_real_python_interpreter ():
104+ # If we're running in a virtual environment, we have to copy Python into
105+ # the virtual environment for it to work.
106+ if sys .prefix != sys .base_prefix :
107+ copied_python = os .path .join (sys .prefix , "bin" , "copied-python" )
108+ else :
109+ copied_python = os .path .join (config .lldb_build_directory , "copied-python" )
110+
111+ # Avoid doing any work if we already copied the binary.
112+ if os .path .isfile (copied_python ):
113+ return copied_python
114+
115+ # Find the "real" python binary.
116+ real_python = (
117+ subprocess .check_output (
118+ [
119+ config .python_executable ,
120+ os .path .join (
121+ os .path .dirname (os .path .realpath (__file__ )),
122+ "get_darwin_real_python.py" ,
123+ ),
124+ ]
125+ )
126+ .decode ("utf-8" )
127+ .strip ()
128+ )
129+
130+ shutil .copy (real_python , copied_python )
131+
132+ # Now make sure the copied Python works. The Python in Xcode has a relative
133+ # RPATH and cannot be copied.
134+ try :
135+ # We don't care about the output, just make sure it runs.
136+ subprocess .check_call ([copied_python , "-V" ])
137+ except subprocess .CalledProcessError :
138+ # The copied Python didn't work. Assume we're dealing with the Python
139+ # interpreter in Xcode. Given that this is not a system binary SIP
140+ # won't prevent us form injecting the interceptors, but when running in
141+ # a virtual environment, we can't use it directly. Create a symlink
142+ # instead.
143+ os .remove (copied_python )
144+ os .symlink (real_python , copied_python )
145+
146+ # The copied Python works.
147+ return copied_python
148+
149+
80150llvm_config .with_system_environment (["HOME" , "INCLUDE" , "LIB" , "TMP" , "TEMP" ])
81151
82152llvm_config .use_default_substitutions ()
@@ -91,6 +161,7 @@ def add_runtime(name):
91161 "LICENSE.txt" ,
92162 "lit.cfg.py" ,
93163 "lit.site.cfg.py" ,
164+ "get_darwin_real_python.py" ,
94165]
95166
96167# Tweak the PATH to include the tools dir.
@@ -172,10 +243,30 @@ def add_runtime(name):
172243)
173244
174245python_executable = config .python_executable
175- # Python configuration with sanitizer requires some magic preloading. This will only work on clang/linux.
176- # TODO: detect Darwin/Windows situation (or mark these tests as unsupported on these platforms).
177- if "asan" in config .available_features and "Linux" in config .host_os :
178- python_executable = f"LD_PRELOAD=$({ config .host_cxx } -print-file-name=libclang_rt.asan-{ config .host_arch } .so) { config .python_executable } "
246+ # Python configuration with sanitizer requires some magic preloading. This will only work on clang/linux/darwin.
247+ # TODO: detect Windows situation (or mark these tests as unsupported on these platforms).
248+ if "asan" in config .available_features :
249+ if "Linux" in config .host_os :
250+ python_executable = f"LD_PRELOAD=$({ config .host_cxx } -print-file-name=libclang_rt.asan-{ config .host_arch } .so) { config .python_executable } "
251+ if "Darwin" in config .host_os :
252+ # Ensure we use a non-shim Python executable, for the `DYLD_INSERT_LIBRARIES`
253+ # env variable to take effect
254+ real_python_executable = find_real_python_interpreter ()
255+ if real_python_executable :
256+ python_executable = real_python_executable
257+ lit_config .note (
258+ "Using {} instead of {}" .format (
259+ python_executable , config .python_executable
260+ )
261+ )
262+
263+ asan_rtlib = get_asan_rtlib ()
264+ lit_config .note ("Using ASan rtlib {}" .format (asan_rtlib ))
265+ config .environment ["MallocNanoZone" ] = "0"
266+ config .environment ["ASAN_OPTIONS" ] = "detect_stack_use_after_return=1"
267+ config .environment ["DYLD_INSERT_LIBRARIES" ] = asan_rtlib
268+
269+
179270# On Windows the path to python could contains spaces in which case it needs to be provided in quotes.
180271# This is the equivalent of how %python is setup in llvm/utils/lit/lit/llvm/config.py.
181272elif "Windows" in config .host_os :
0 commit comments