Skip to content

Commit 7cb740f

Browse files
sebastiankreutzerDanielCChen
authored andcommitted
[XRay] Add support for instrumentation of DSOs on x86_64 (llvm#90959)
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. @petrhosek @jplehr
1 parent 3d2f275 commit 7cb740f

22 files changed

+1215
-144
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
@@ -2946,6 +2946,11 @@ def fxray_selected_function_group :
29462946
HelpText<"When using -fxray-function-groups, select which group of functions to instrument. Valid range is 0 to fxray-function-groups - 1">,
29472947
MarshallingInfoInt<CodeGenOpts<"XRaySelectedFunctionGroup">, "0">;
29482948

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

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

clang/include/clang/Driver/XRayArgs.h

Lines changed: 4 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,9 @@ class XRayArgs {
3536
llvm::opt::ArgStringList &CmdArgs, types::ID InputType) const;
3637

3738
bool needsXRayRt() const { return XRayInstrument && XRayRT; }
39+
bool needsXRayDSORt() const {
40+
return XRayInstrument && XRayRT && XRayShared;
41+
}
3842
llvm::ArrayRef<std::string> modeList() const { return Modes; }
3943
XRayInstrSet instrumentationBundle() const { return InstrumentationBundle; }
4044
};

clang/lib/Driver/ToolChains/CommonArgs.cpp

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

16151615
bool tools::addXRayRuntime(const ToolChain&TC, const ArgList &Args, ArgStringList &CmdArgs) {
1616-
if (Args.hasArg(options::OPT_shared))
1617-
return false;
1618-
1619-
if (TC.getXRayArgs().needsXRayRt()) {
1616+
if (Args.hasArg(options::OPT_shared)) {
1617+
if (TC.getXRayArgs().needsXRayDSORt()) {
1618+
CmdArgs.push_back("--whole-archive");
1619+
CmdArgs.push_back(TC.getCompilerRTArgString(Args, "xray-dso"));
1620+
CmdArgs.push_back("--no-whole-archive");
1621+
return true;
1622+
}
1623+
} else if (TC.getXRayArgs().needsXRayRt()) {
16201624
CmdArgs.push_back("--whole-archive");
16211625
CmdArgs.push_back(TC.getCompilerRTArgString(Args, "xray"));
16221626
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,
67+
options::OPT_fno_xray_shared, 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)
79+
<< "-fxray-shared" << "-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: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,31 +93,74 @@ enum XRayPatchingStatus {
9393
FAILED = 3,
9494
};
9595

96-
/// This tells XRay to patch the instrumentation points. See XRayPatchingStatus
96+
/// This tells XRay to patch the instrumentation points in all currently loaded objects. See XRayPatchingStatus
9797
/// 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 possible
109+
/// 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 for possible
118+
/// 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
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 of the corresponding function. We return 0 if we encounter any error, even if 0 may be a valid function
114133
/// address.
115134
extern uintptr_t __xray_function_address(int32_t FuncId);
116135

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

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