Skip to content

Commit e738a5d

Browse files
sebastiankreutzerSebastian Kreutzer
and
Sebastian Kreutzer
authored
Reapply " [XRay] Add support for instrumentation of DSOs on x86_64 (#90959)" (#113548)
This fixes remaining issues in my previous PR #90959. Changes: - Removed dependency on LLVM header in `xray_interface.cpp` - Fixed XRay patching for some targets due to missing changes in architecture-specific patching functions - Addressed some remaining compiler warnings that I missed in the previous patch - Formatting I have tested these changes on `x86_64` (natively), as well as `ppc64le`, `aarch64` and `arm32` (cross-compiled and emulated using qemu). **Original description:** This PR introduces shared library (DSO) support for XRay based on a revised version of the implementation outlined in [this RFC](https://discourse.llvm.org/t/rfc-upstreaming-dso-instrumentation-support-for-xray/73000). The feature enables the patching and handling of events from DSOs, supporting both libraries linked at startup or explicitly loaded, e.g. via `dlopen`. This patch adds the following: - The `-fxray-shared` flag to enable the feature (turned off by default) - A small runtime library that is linked into every instrumented DSO, providing position-independent trampolines and code to register with the main XRay runtime - Changes to the XRay runtime to support management and patching of multiple objects These changes are fully backward compatible, i.e. running without instrumented DSOs will produce identical traces (in terms of recorded function IDs) to the previous implementation. Due to my limited ability to test on other architectures, this feature is only implemented and tested with x86_64. Extending support to other architectures is fairly straightforward, requiring only a position-independent implementation of the architecture-specific trampoline implementation (see `compiler-rt/lib/xray/xray_trampoline_x86_64.S` for reference). This patch does not include any functionality to resolve function IDs from DSOs for the provided logging/tracing modes. These modes still work and will record calls from DSOs, but symbol resolution for these functions in not available. Getting this to work properly requires recording information about the loaded DSOs and should IMO be discussed in a separate RFC, as there are mulitple feasible approaches. --------- Co-authored-by: Sebastian Kreutzer <[email protected]>
1 parent ed6ddff commit e738a5d

29 files changed

+1319
-194
lines changed

clang/include/clang/Basic/CodeGenOptions.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ CODEGENOPT(XRayIgnoreLoops , 1, 0)
136136
///< Emit the XRay function index section.
137137
CODEGENOPT(XRayFunctionIndex , 1, 1)
138138

139+
///< Set when -fxray-shared is enabled
140+
CODEGENOPT(XRayShared , 1, 0)
139141

140142
///< Set the minimum number of instructions in a function to determine selective
141143
///< XRay instrumentation.

clang/include/clang/Driver/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2948,6 +2948,11 @@ def fxray_selected_function_group :
29482948
HelpText<"When using -fxray-function-groups, select which group of functions to instrument. Valid range is 0 to fxray-function-groups - 1">,
29492949
MarshallingInfoInt<CodeGenOpts<"XRaySelectedFunctionGroup">, "0">;
29502950

2951+
defm xray_shared : BoolFOption<"xray-shared",
2952+
CodeGenOpts<"XRayShared">, DefaultFalse,
2953+
PosFlag<SetTrue, [], [ClangOption, CC1Option],
2954+
"Enable shared library instrumentation with XRay">,
2955+
NegFlag<SetFalse>>;
29512956

29522957
defm fine_grained_bitfield_accesses : BoolOption<"f", "fine-grained-bitfield-accesses",
29532958
CodeGenOpts<"FineGrainedBitfieldAccesses">, DefaultFalse,

clang/include/clang/Driver/XRayArgs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class XRayArgs {
2727
XRayInstrSet InstrumentationBundle;
2828
llvm::opt::Arg *XRayInstrument = nullptr;
2929
bool XRayRT = true;
30+
bool XRayShared = false;
3031

3132
public:
3233
/// Parses the XRay arguments from an argument list.
@@ -35,6 +36,7 @@ class XRayArgs {
3536
llvm::opt::ArgStringList &CmdArgs, types::ID InputType) const;
3637

3738
bool needsXRayRt() const { return XRayInstrument && XRayRT; }
39+
bool needsXRayDSORt() const { return XRayInstrument && XRayRT && XRayShared; }
3840
llvm::ArrayRef<std::string> modeList() const { return Modes; }
3941
XRayInstrSet instrumentationBundle() const { return InstrumentationBundle; }
4042
};

clang/lib/Driver/ToolChains/CommonArgs.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,10 +1623,14 @@ bool tools::addSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
16231623
}
16241624

16251625
bool tools::addXRayRuntime(const ToolChain&TC, const ArgList &Args, ArgStringList &CmdArgs) {
1626-
if (Args.hasArg(options::OPT_shared))
1627-
return false;
1628-
1629-
if (TC.getXRayArgs().needsXRayRt()) {
1626+
if (Args.hasArg(options::OPT_shared)) {
1627+
if (TC.getXRayArgs().needsXRayDSORt()) {
1628+
CmdArgs.push_back("--whole-archive");
1629+
CmdArgs.push_back(TC.getCompilerRTArgString(Args, "xray-dso"));
1630+
CmdArgs.push_back("--no-whole-archive");
1631+
return true;
1632+
}
1633+
} else if (TC.getXRayArgs().needsXRayRt()) {
16301634
CmdArgs.push_back("--whole-archive");
16311635
CmdArgs.push_back(TC.getCompilerRTArgString(Args, "xray"));
16321636
for (const auto &Mode : TC.getXRayArgs().modeList())

clang/lib/Driver/XRayArgs.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,23 @@ XRayArgs::XRayArgs(const ToolChain &TC, const ArgList &Args) {
6363
<< XRayInstrument->getSpelling() << Triple.str();
6464
}
6565

66+
if (Args.hasFlag(options::OPT_fxray_shared, options::OPT_fno_xray_shared,
67+
false)) {
68+
XRayShared = true;
69+
70+
// DSO instrumentation is currently limited to x86_64
71+
if (Triple.getArch() != llvm::Triple::x86_64) {
72+
D.Diag(diag::err_drv_unsupported_opt_for_target)
73+
<< "-fxray-shared" << Triple.str();
74+
}
75+
76+
unsigned PICLvl = std::get<1>(tools::ParsePICArgs(TC, Args));
77+
if (!PICLvl) {
78+
D.Diag(diag::err_opt_not_valid_without_opt) << "-fxray-shared"
79+
<< "-fPIC";
80+
}
81+
}
82+
6683
// Both XRay and -fpatchable-function-entry use
6784
// TargetOpcode::PATCHABLE_FUNCTION_ENTER.
6885
if (Arg *A = Args.getLastArg(options::OPT_fpatchable_function_entry_EQ))
@@ -177,6 +194,10 @@ void XRayArgs::addArgs(const ToolChain &TC, const ArgList &Args,
177194
Args.addOptOutFlag(CmdArgs, options::OPT_fxray_function_index,
178195
options::OPT_fno_xray_function_index);
179196

197+
if (XRayShared)
198+
Args.addOptInFlag(CmdArgs, options::OPT_fxray_shared,
199+
options::OPT_fno_xray_shared);
200+
180201
if (const Arg *A =
181202
Args.getLastArg(options::OPT_fxray_instruction_threshold_EQ)) {
182203
int Value;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fPIC -fxray-instrument -fxray-shared -c %s -o /dev/null 2>&1 | FileCheck %s
2+
// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fpic -fxray-instrument -fxray-shared -c %s -o /dev/null 2>&1 | FileCheck %s
3+
// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fxray-instrument -fxray-shared -c %s -o /dev/null 2>&1 | FileCheck %s
4+
// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -fno-PIC -fxray-instrument -fxray-shared -c %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR-PIC
5+
// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -fno-pic -fxray-instrument -fxray-shared -c %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR-PIC
6+
7+
// On 64 bit darwin, PIC is always enabled
8+
// RUN: %clang -### --target=x86_64-apple-darwin -fxray-instrument -fxray-shared -c %s -o /dev/null 2>&1 | FileCheck %s
9+
10+
// Check unsupported targets
11+
// RUN: not %clang -### --target=aarch64-pc-freebsd -fPIC -fxray-instrument -fxray-shared -c %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR-TARGET
12+
// RUN: not %clang -### --target=arm64-apple-macos -fPIC -fxray-instrument -fxray-shared -c %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR-TARGET
13+
14+
// CHECK: "-cc1" {{.*}}"-fxray-instrument" {{.*}}"-fxray-shared"
15+
// ERR-TARGET: error: unsupported option '-fxray-shared' for target
16+
// ERR-PIC: error: option '-fxray-shared' cannot be specified without '-fPIC'
17+

compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ else()
104104
set(ALL_XRAY_SUPPORTED_ARCH ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64}
105105
powerpc64le ${HEXAGON} ${LOONGARCH64})
106106
endif()
107+
set(ALL_XRAY_DSO_SUPPORTED_ARCH ${X86_64})
107108
set(ALL_SHADOWCALLSTACK_SUPPORTED_ARCH ${ARM64})
108109

109110
if (UNIX)

compiler-rt/cmake/config-ix.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,9 @@ if(APPLE)
668668
list_intersect(XRAY_SUPPORTED_ARCH
669669
ALL_XRAY_SUPPORTED_ARCH
670670
SANITIZER_COMMON_SUPPORTED_ARCH)
671+
list_intersect(XRAY_DSO_SUPPORTED_ARCH
672+
ALL_XRAY_DSO_SUPPORTED_ARCH
673+
SANITIZER_COMMON_SUPPORTED_ARCH)
671674
list_intersect(SHADOWCALLSTACK_SUPPORTED_ARCH
672675
ALL_SHADOWCALLSTACK_SUPPORTED_ARCH
673676
SANITIZER_COMMON_SUPPORTED_ARCH)
@@ -702,6 +705,7 @@ else()
702705
filter_available_targets(CFI_SUPPORTED_ARCH ${ALL_CFI_SUPPORTED_ARCH})
703706
filter_available_targets(SCUDO_STANDALONE_SUPPORTED_ARCH ${ALL_SCUDO_STANDALONE_SUPPORTED_ARCH})
704707
filter_available_targets(XRAY_SUPPORTED_ARCH ${ALL_XRAY_SUPPORTED_ARCH})
708+
filter_available_targets(XRAY_DSO_SUPPORTED_ARCH ${ALL_XRAY_DSO_SUPPORTED_ARCH})
705709
filter_available_targets(SHADOWCALLSTACK_SUPPORTED_ARCH
706710
${ALL_SHADOWCALLSTACK_SUPPORTED_ARCH})
707711
filter_available_targets(GWP_ASAN_SUPPORTED_ARCH ${ALL_GWP_ASAN_SUPPORTED_ARCH})

compiler-rt/include/xray/xray_interface.h

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,31 +93,78 @@ enum XRayPatchingStatus {
9393
FAILED = 3,
9494
};
9595

96-
/// This tells XRay to patch the instrumentation points. See XRayPatchingStatus
97-
/// for possible result values.
96+
/// This tells XRay to patch the instrumentation points in all currently loaded
97+
/// objects. See XRayPatchingStatus for possible result values.
9898
extern XRayPatchingStatus __xray_patch();
9999

100+
/// This tells XRay to patch the instrumentation points in the given object.
101+
/// See XRayPatchingStatus for possible result values.
102+
extern XRayPatchingStatus __xray_patch_object(int32_t ObjId);
103+
100104
/// Reverses the effect of __xray_patch(). See XRayPatchingStatus for possible
101105
/// result values.
102106
extern XRayPatchingStatus __xray_unpatch();
103107

104-
/// This patches a specific function id. See XRayPatchingStatus for possible
108+
/// Reverses the effect of __xray_patch_object. See XRayPatchingStatus for
109+
/// possible result values.
110+
extern XRayPatchingStatus __xray_unpatch_object(int32_t ObjId);
111+
112+
/// This unpacks the given (packed) function id and patches
113+
/// the corresponding function. See XRayPatchingStatus for possible
105114
/// result values.
106115
extern XRayPatchingStatus __xray_patch_function(int32_t FuncId);
107116

108-
/// This unpatches a specific function id. See XRayPatchingStatus for possible
117+
/// This patches a specific function in the given object. See XRayPatchingStatus
118+
/// for possible result values.
119+
extern XRayPatchingStatus __xray_patch_function_in_object(int32_t FuncId,
120+
int32_t ObjId);
121+
122+
/// This unpacks the given (packed) function id and unpatches
123+
/// the corresponding function. See XRayPatchingStatus for possible
109124
/// result values.
110125
extern XRayPatchingStatus __xray_unpatch_function(int32_t FuncId);
111126

112-
/// This function returns the address of the function provided a valid function
113-
/// id. We return 0 if we encounter any error, even if 0 may be a valid function
114-
/// address.
127+
/// This unpatches a specific function in the given object.
128+
/// See XRayPatchingStatus for possible result values.
129+
extern XRayPatchingStatus __xray_unpatch_function_in_object(int32_t FuncId,
130+
int32_t ObjId);
131+
132+
/// This function unpacks the given (packed) function id and returns the address
133+
/// of the corresponding function. We return 0 if we encounter any error, even
134+
/// if 0 may be a valid function address.
115135
extern uintptr_t __xray_function_address(int32_t FuncId);
116136

117-
/// This function returns the maximum valid function id. Returns 0 if we
118-
/// encounter errors (when there are no instrumented functions, etc.).
137+
/// This function returns the address of the function in the given object
138+
/// provided valid function and object ids. We return 0 if we encounter any
139+
/// error, even if 0 may be a valid function address.
140+
extern uintptr_t __xray_function_address_in_object(int32_t FuncId,
141+
int32_t ObjId);
142+
143+
/// This function returns the maximum valid function id for the main executable
144+
/// (object id = 0). Returns 0 if we encounter errors (when there are no
145+
/// instrumented functions, etc.).
119146
extern size_t __xray_max_function_id();
120147

148+
/// This function returns the maximum valid function id for the given object.
149+
/// Returns 0 if we encounter errors (when there are no instrumented functions,
150+
/// etc.).
151+
extern size_t __xray_max_function_id_in_object(int32_t ObjId);
152+
153+
/// This function returns the number of previously registered objects
154+
/// (executable + loaded DSOs). Returns 0 if XRay has not been initialized.
155+
extern size_t __xray_num_objects();
156+
157+
/// Unpacks the function id from the given packed id.
158+
extern int32_t __xray_unpack_function_id(int32_t PackedId);
159+
160+
/// Unpacks the object id from the given packed id.
161+
extern int32_t __xray_unpack_object_id(int32_t PackedId);
162+
163+
/// Creates and returns a packed id from the given function and object ids.
164+
/// If the ids do not fit within the reserved number of bits for each part, the
165+
/// high bits are truncated.
166+
extern int32_t __xray_pack_id(int32_t FuncId, int32_t ObjId);
167+
121168
/// Initialize the required XRay data structures. This is useful in cases where
122169
/// users want to control precisely when the XRay instrumentation data
123170
/// structures are initialized, for example when the XRay library is built with

compiler-rt/lib/xray/CMakeLists.txt

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ set(XRAY_SOURCES
1010
xray_utils.cpp
1111
)
1212

13+
set(XRAY_DSO_SOURCES
14+
xray_dso_init.cpp
15+
)
16+
1317
# Implementation files for all XRay modes.
1418
set(XRAY_FDR_MODE_SOURCES
1519
xray_fdr_flags.cpp
@@ -33,6 +37,11 @@ set(x86_64_SOURCES
3337
xray_trampoline_x86_64.S
3438
)
3539

40+
set(x86_64_DSO_SOURCES
41+
xray_trampoline_x86_64.S
42+
)
43+
44+
3645
set(arm_SOURCES
3746
xray_arm.cpp
3847
xray_trampoline_arm.S
@@ -128,10 +137,12 @@ set(XRAY_IMPL_HEADERS
128137
# consumption by tests.
129138
set(XRAY_ALL_SOURCE_FILES
130139
${XRAY_SOURCES}
140+
${XRAY_DSO_SOURCES}
131141
${XRAY_FDR_MODE_SOURCES}
132142
${XRAY_BASIC_MODE_SOURCES}
133143
${XRAY_PROFILING_MODE_SOURCES}
134144
${x86_64_SOURCES}
145+
${x86_64_DSO_SOURCES}
135146
${arm_SOURCES}
136147
${armhf_SOURCES}
137148
${hexagon_SOURCES}
@@ -162,6 +173,9 @@ set(XRAY_CFLAGS
162173
${COMPILER_RT_CXX_CFLAGS})
163174
set(XRAY_COMMON_DEFINITIONS SANITIZER_COMMON_NO_REDEFINE_BUILTINS XRAY_HAS_EXCEPTIONS=1)
164175

176+
# DSO trampolines need to be compiled with GOT addressing
177+
set(XRAY_COMMON_DEFINITIONS_DSO ${XRAY_COMMON_DEFINITIONS} XRAY_PIC)
178+
165179
# Too many existing bugs, needs cleanup.
166180
append_list_if(COMPILER_RT_HAS_WNO_FORMAT -Wno-format XRAY_CFLAGS)
167181

@@ -201,7 +215,16 @@ if (APPLE)
201215
CFLAGS ${XRAY_CFLAGS}
202216
DEFS ${XRAY_COMMON_DEFINITIONS}
203217
DEPS ${XRAY_DEPS})
218+
add_compiler_rt_object_libraries(RTXrayDSO
219+
OS ${XRAY_SUPPORTED_OS}
220+
ARCHS ${XRAY_DSO_SUPPORTED_ARCH}
221+
SOURCES ${XRAY_DSO_SOURCES}
222+
ADDITIONAL_HEADERS ${XRAY_IMPL_HEADERS}
223+
CFLAGS ${XRAY_CFLAGS}
224+
DEFS ${XRAY_COMMON_DEFINITIONS_DSO}
225+
DEPS ${XRAY_DEPS})
204226
set(XRAY_RTXRAY_ARCH_LIBS "")
227+
set(XRAY_DSO_RTXRAY_ARCH_LIBS "")
205228
foreach(arch ${XRAY_SUPPORTED_ARCH})
206229
if(NOT ${arch} IN_LIST XRAY_SOURCE_ARCHS)
207230
continue()
@@ -215,6 +238,17 @@ if (APPLE)
215238
DEFS ${XRAY_COMMON_DEFINITIONS}
216239
DEPS ${XRAY_DEPS})
217240
list(APPEND XRAY_RTXRAY_ARCH_LIBS RTXray_${arch})
241+
if (${arch} IN_LIST XRAY_DSO_SUPPORTED_ARCH)
242+
add_compiler_rt_object_libraries(RTXrayDSO_${arch}
243+
OS ${XRAY_SUPPORTED_OS}
244+
ARCHS ${XRAY_DSO_SUPPORTED_ARCH}
245+
SOURCES ${${arch}_DSO_SOURCES}
246+
ADDITIONAL_HEADERS ${XRAY_IMPL_HEADERS}
247+
CFLAGS ${XRAY_CFLAGS}
248+
DEFS ${XRAY_COMMON_DEFINITIONS_DSO}
249+
DEPS ${XRAY_DEPS})
250+
list(APPEND XRAY_DSO_RTXRAY_ARCH_LIBS RTXrayDSO_${arch})
251+
endif()
218252
endforeach()
219253
add_compiler_rt_object_libraries(RTXrayFDR
220254
OS ${XRAY_SUPPORTED_OS}
@@ -252,6 +286,17 @@ if (APPLE)
252286
LINK_FLAGS ${XRAY_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS}
253287
LINK_LIBS ${XRAY_LINK_LIBS}
254288
PARENT_TARGET xray)
289+
add_compiler_rt_runtime(clang_rt.xray-dso
290+
STATIC
291+
OS ${XRAY_SUPPORTED_OS}
292+
ARCHS ${XRAY_DSO_SUPPORTED_ARCH}
293+
OBJECT_LIBS RTXrayDSO ${XRAY_DSO_RTXRAY_ARCH_LIBS}
294+
CFLAGS ${XRAY_CFLAGS}
295+
DEFS ${XRAY_COMMON_DEFINITIONS}
296+
LINK_FLAGS ${XRAY_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS}
297+
LINK_LIBS ${XRAY_LINK_LIBS}
298+
PARENT_TARGET xray)
299+
255300
add_compiler_rt_runtime(clang_rt.xray-fdr
256301
STATIC
257302
OS ${XRAY_SUPPORTED_OS}
@@ -346,16 +391,37 @@ else() # not Apple
346391
DEFS ${XRAY_COMMON_DEFINITIONS}
347392
OBJECT_LIBS RTXrayBASIC
348393
PARENT_TARGET xray)
349-
# Profiler Mode runtime
350-
add_compiler_rt_runtime(clang_rt.xray-profiling
351-
STATIC
352-
ARCHS ${arch}
353-
CFLAGS ${XRAY_CFLAGS}
354-
LINK_FLAGS ${XRAY_LINK_FLAGS}
355-
LINK_LIBS ${XRAY_LINK_LIBS}
356-
DEFS ${XRAY_COMMON_DEFINITIONS}
357-
OBJECT_LIBS RTXrayPROFILING
358-
PARENT_TARGET xray)
394+
# Profiler Mode runtime
395+
add_compiler_rt_runtime(clang_rt.xray-profiling
396+
STATIC
397+
ARCHS ${arch}
398+
CFLAGS ${XRAY_CFLAGS}
399+
LINK_FLAGS ${XRAY_LINK_FLAGS}
400+
LINK_LIBS ${XRAY_LINK_LIBS}
401+
DEFS ${XRAY_COMMON_DEFINITIONS}
402+
OBJECT_LIBS RTXrayPROFILING
403+
PARENT_TARGET xray)
404+
405+
if (${arch} IN_LIST XRAY_DSO_SUPPORTED_ARCH)
406+
# TODO: Only implemented for X86 at the moment
407+
add_compiler_rt_object_libraries(RTXrayDSO
408+
ARCHS ${arch}
409+
SOURCES ${XRAY_DSO_SOURCES} ${${arch}_DSO_SOURCES}
410+
ADDITIONAL_HEADERS ${XRAY_IMPL_HEADERS}
411+
CFLAGS ${XRAY_CFLAGS}
412+
DEFS ${XRAY_COMMON_DEFINITIONS_DSO}
413+
DEPS ${XRAY_DEPS})
414+
# DSO runtime archive
415+
add_compiler_rt_runtime(clang_rt.xray-dso
416+
STATIC
417+
ARCHS ${arch}
418+
CFLAGS ${XRAY_CFLAGS}
419+
LINK_FLAGS ${XRAY_LINK_FLAGS}
420+
LINK_LIBS ${XRAY_LINK_LIBS}
421+
DEFS ${XRAY_COMMON_DEFINITIONS}
422+
OBJECT_LIBS RTXrayDSO
423+
PARENT_TARGET xray)
424+
endif()
359425
endforeach()
360426
endif() # not Apple
361427

0 commit comments

Comments
 (0)