1 | // Check that loading libraries with different modes (RTLD_LOCAL/RTLD_GLOBAL) |
2 | // and dependencies on other DSOs work correctly. |
3 | // |
4 | |
5 | // RUN: split-file %s %t |
6 | // |
7 | // Build shared libs with dependencies b->c and e->f |
8 | // RUN: %clangxx_xray -g -fPIC -fxray-instrument -fxray-shared -shared -std=c++11 %t/testliba.cpp -o %t/testliba.so |
9 | // RUN: %clangxx_xray -g -fPIC -fxray-instrument -fxray-shared -shared -std=c++11 %t/testlibc.cpp -o %t/testlibc.so |
10 | // RUN: %clangxx_xray -g -fPIC -fxray-instrument -fxray-shared -shared -std=c++11 %t/testlibb.cpp %t/testlibc.so -o %t/testlibb.so |
11 | // RUN: %clangxx_xray -g -fPIC -fxray-instrument -fxray-shared -shared -std=c++11 %t/testlibd.cpp -o %t/testlibd.so |
12 | // RUN: %clangxx_xray -g -fPIC -fxray-instrument -fxray-shared -shared -std=c++11 %t/testlibf.cpp -o %t/testlibf.so |
13 | // RUN: %clangxx_xray -g -fPIC -fxray-instrument -fxray-shared -shared -std=c++11 %t/testlibe.cpp %t/testlibf.so -o %t/testlibe.so |
14 | // |
15 | // Executable links with a and b explicitly and loads d and e at runtime. |
16 | // RUN: %clangxx_xray -g -fPIC -rdynamic -fxray-instrument -fxray-shared -std=c++11 %t/main.cpp %t/testliba.so %t/testlibb.so -o %t/main.o |
17 | // |
18 | // RUN: XRAY_OPTIONS="patch_premain=true" %run %t/main.o %t/testlibd.so %t/testlibe.so 2>&1 | FileCheck %s |
19 | |
20 | // REQUIRES: target={{(aarch64|x86_64)-.*}} |
21 | |
22 | //--- main.cpp |
23 | |
24 | #include "xray/xray_interface.h" |
25 | |
26 | #include <cstdio> |
27 | #include <dlfcn.h> |
28 | |
29 | [[clang::xray_never_instrument]] void test_handler(int32_t fid, |
30 | XRayEntryType type) { |
31 | printf(format: "called: %d, object=%d, fn=%d, type=%d\n" , fid, (fid >> 24) & 0xFF, |
32 | fid & 0x00FFFFFF, static_cast<int32_t>(type)); |
33 | } |
34 | |
35 | [[clang::xray_always_instrument]] void instrumented_in_executable() { |
36 | printf(format: "instrumented_in_executable called\n" ); |
37 | } |
38 | |
39 | typedef void (*dso_func_type)(); |
40 | |
41 | [[clang::xray_never_instrument]] void *load_dso(const char *path, int mode) { |
42 | void *dso_handle = dlopen(file: path, mode: mode); |
43 | if (!dso_handle) { |
44 | printf(format: "failed to load shared library\n" ); |
45 | char *error = dlerror(); |
46 | if (error) { |
47 | fprintf(stderr, format: "%s\n" , error); |
48 | } |
49 | return nullptr; |
50 | } |
51 | return dso_handle; |
52 | } |
53 | |
54 | [[clang::xray_never_instrument]] void find_and_call(void *dso_handle, |
55 | const char *fn) { |
56 | dso_func_type dso_fn = (dso_func_type)dlsym(handle: dso_handle, name: fn); |
57 | if (!dso_fn) { |
58 | printf(format: "failed to find symbol\n" ); |
59 | char *error = dlerror(); |
60 | if (error) { |
61 | fprintf(stderr, format: "%s\n" , error); |
62 | } |
63 | return; |
64 | } |
65 | dso_fn(); |
66 | } |
67 | |
68 | extern void a(); |
69 | extern void b(); |
70 | |
71 | int main(int argc, char **argv) { |
72 | |
73 | if (argc < 3) { |
74 | printf(format: "Shared library arguments missing\n" ); |
75 | // CHECK-NOT: Shared library arguments missing |
76 | return 1; |
77 | } |
78 | |
79 | const char *dso_path_d = argv[1]; |
80 | const char *dso_path_e = argv[2]; |
81 | |
82 | __xray_set_handler(entry: test_handler); |
83 | |
84 | instrumented_in_executable(); |
85 | // CHECK: called: {{[0-9]+}}, object=0, fn={{[0-9]+}}, type=0 |
86 | // CHECK-NEXT: instrumented_in_executable called |
87 | // CHECK-NEXT: called: {{[0-9]+}}, object=0, fn={{[0-9]+}}, type=1 |
88 | |
89 | a(); |
90 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ1:[0-9]+]], fn=1, type=0 |
91 | // CHECK-NEXT: a called |
92 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ1]], fn=1, type=1 |
93 | |
94 | // Make sure this object ID does not appear again |
95 | // CHECK-NOT: called: {{[0-9]+}}, object=[[OBJ1]] |
96 | |
97 | b(); // b calls c |
98 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ2:[0-9]+]], fn=1, type=0 |
99 | // CHECK-NEXT: b called |
100 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ3:[0-9]+]], fn=1, type=0 |
101 | // CHECK-NEXT: c called |
102 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ3]], fn=1, type=1 |
103 | // CHECK-NOT: called: {{[0-9]+}}, object=[[OBJ3]] |
104 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ2]], fn=1, type=1 |
105 | // CHECK-NOT: called: {{[0-9]+}}, object=[[OBJ2]] |
106 | |
107 | // Now check explicit loading with RTLD_LOCAL |
108 | |
109 | void *dso_handle_d = load_dso(path: dso_path_d, RTLD_LAZY | RTLD_LOCAL); |
110 | void *dso_handle_e = load_dso(path: dso_path_e, RTLD_LAZY | RTLD_LOCAL); |
111 | // CHECK-NOT: failed to load shared library |
112 | |
113 | find_and_call(dso_handle: dso_handle_d, fn: "_Z1dv" ); |
114 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ4:[0-9]+]], fn=1, type=0 |
115 | // CHECK-NEXT: d called |
116 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ4]], fn=1, type=1 |
117 | // CHECK-NOT: called: {{[0-9]+}}, object=[[OBJ4]] |
118 | |
119 | find_and_call(dso_handle: dso_handle_e, fn: "_Z1ev" ); |
120 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ5:[0-9]+]], fn=1, type=0 |
121 | // CHECK-NEXT: e called |
122 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ6:[0-9]+]], fn=1, type=0 |
123 | // CHECK-NEXT: f called |
124 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ6]], fn=1, type=1 |
125 | // CHECK-NOT: called: {{[0-9]+}}, object=[[OBJ6]] |
126 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ5]], fn=1, type=1 |
127 | // CHECK-NOT: called: {{[0-9]+}}, object=[[OBJ5]] |
128 | |
129 | // Unload DSOs |
130 | dlclose(handle: dso_handle_d); |
131 | dlclose(handle: dso_handle_e); |
132 | |
133 | // Repeat test with RTLD_GLOBAL |
134 | dso_handle_d = load_dso(path: dso_path_d, RTLD_LAZY | RTLD_GLOBAL); |
135 | dso_handle_e = load_dso(path: dso_path_e, RTLD_LAZY | RTLD_GLOBAL); |
136 | // CHECK-NOT: failed to load shared library |
137 | |
138 | find_and_call(dso_handle: dso_handle_d, fn: "_Z1dv" ); |
139 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ7:[0-9]+]], fn=1, type=0 |
140 | // CHECK-NEXT: d called |
141 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ7]], fn=1, type=1 |
142 | // CHECK-NOT: called: {{[0-9]+}}, object=[[OBJ7]] |
143 | |
144 | find_and_call(dso_handle: dso_handle_e, fn: "_Z1ev" ); |
145 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ8:[0-9]+]], fn=1, type=0 |
146 | // CHECK-NEXT: e called |
147 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ9:[0-9]+]], fn=1, type=0 |
148 | // CHECK-NEXT: f called |
149 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ9]], fn=1, type=1 |
150 | // CHECK-NOT: called: {{[0-9]+}}, object=[[OBJ9]] |
151 | // CHECK-NEXT: called: {{[0-9]+}}, object=[[OBJ8]], fn=1, type=1 |
152 | // CHECK-NOT: called: {{[0-9]+}}, object=[[OBJ8]] |
153 | |
154 | auto status = __xray_unpatch(); |
155 | printf(format: "unpatching status: %d\n" , static_cast<int32_t>(status)); |
156 | // CHECK-NEXT: unpatching status: 1 |
157 | |
158 | dlclose(handle: dso_handle_d); |
159 | dlclose(handle: dso_handle_e); |
160 | } |
161 | |
162 | //--- libgenmacro.inc |
163 | #include <cstdio> |
164 | // Helper macros to quickly generate libraries containing a single function. |
165 | #define GENERATE_LIB(NAME) \ |
166 | [[clang::xray_always_instrument]] void NAME() { printf(#NAME " called\n"); } |
167 | |
168 | #define GENERATE_LIB_WITH_CALL(NAME, FN) \ |
169 | extern void FN(); \ |
170 | [[clang::xray_always_instrument]] void NAME() { \ |
171 | printf(#NAME " called\n"); \ |
172 | FN(); \ |
173 | } |
174 | |
175 | //--- testliba.cpp |
176 | #include "libgenmacro.inc" |
177 | GENERATE_LIB(a) |
178 | |
179 | //--- testlibb.cpp |
180 | #include "libgenmacro.inc" |
181 | GENERATE_LIB_WITH_CALL(b, c) |
182 | |
183 | //--- testlibc.cpp |
184 | #include "libgenmacro.inc" |
185 | GENERATE_LIB(c) |
186 | |
187 | //--- testlibd.cpp |
188 | #include "libgenmacro.inc" |
189 | GENERATE_LIB(d) |
190 | |
191 | //--- testlibe.cpp |
192 | #include "libgenmacro.inc" |
193 | GENERATE_LIB_WITH_CALL(e, f) |
194 | |
195 | //--- testlibf.cpp |
196 | #include "libgenmacro.inc" |
197 | GENERATE_LIB(f) |
198 | |