1GOTCHA(1) Gotcha GOTCHA(1)
2
3
4
6 gotcha - Gotcha Documentation
7
9 Gotcha is an API that provides function wrapping, interposing a wrapper
10 function between a function and its callsites. Many tools rely on func‐
11 tion wrapping as a fundamental building block. For example, a perfor‐
12 mance analysis tool which wants to measure time an application spends
13 in IO might put wrappers around "read()" and "write()" which trigger
14 stopwatches.
15
16 Tools traditionally implemented function wrapping with the LD_PRELOAD
17 environment variable on glibc-based systems. This environment variable
18 allowed the tool to inject a tool library into the target application.
19 Any functions that the tool library exports, such as a "read()" or
20 "write()" function, will intercept calls to the matching function names
21 from the original application. While powerful, the LD_PRELOAD approach
22 had several limitations:
23
24 • Tool libraries can have challenges matching ABI-compatibility with
25 the application.
26
27 • Multiple tools cannot wrap the same function.
28
29 • The set of wrapped functions are determined at tool build-time and
30 cannot be changed in response to application behavior.
31
32 Gotcha addresses these limitations by providing an API for function
33 wrapping. Tool libraries make wrapping requests to Gotcha that say, for
34 example, “wrap all calls to the read() function with my tool_read()
35 function, and give me a function pointer to the original read().”
36 Gotcha’s API allows tool wrapping decisions to be made at runtime, and
37 it handles cases of multiple tools wrapping the same function. It does
38 not, however, provide any new mechanisms for injecting the tool library
39 into an application. Gotcha-based tools should be added to the applica‐
40 tion at link-time or injected with LD_PRELOAD.
41
42 Gotcha works by rewriting the Global Offset Table (GOT) that links in‐
43 ter-library callsites and variable references to their targets. Be‐
44 cause of this Gotcha cannot wrap intra-library calls (such as a call to
45 a static function in C) or calls in statically-linked binaries. Binary
46 rewriting technology such as DyninstAPI is more appropriate for these
47 use cases.
48
50 This section defines some terms used throughout the document.
51
52 GOT
53 The Global Offset Table, or GOT, is a section of a computer program's
54 (executables and shared libraries) memory used to enable computer pro‐
55 gram code compiled as an ELF file to run correctly, independent of the
56 memory address where the program's code or data is loaded at runtime.
57 More details can be read at GOT Documentation.
58
59 ELF
60 In computing, the Executable and Linkable Format[2] (ELF, formerly
61 named Extensible Linking Format), is a common standard file format for
62 executable files, object code, shared libraries, and core dumps.
63
64 LD_PRELOAD
65 LD_PRELOAD is a powerful and advanced feature in the Linux dynamic
66 linker that allows users to preload shared object files into the ad‐
67 dress space of a process. Read more at LD_PRELOAD Documentation.
68
69 ABI-compatibility
70 An application binary interface (ABI) is an interface between two bi‐
71 nary program modules. An ABI defines how data structures or computa‐
72 tional routines are accessed in machine code, which is a low-level,
73 hardware-dependent format.
74
76 General Limitations
77 Operating system support
78 As the ELF is the file format used for .o object files, binaries,
79 shared libraries and core dumps in Linux. We currently only support
80 Linux OS.
81
82 Intra and Intra-library calls
83 Gotcha works by rewriting the Global Offset Table (GOT) that links in‐
84 ter-library callsites and variable references to their targets. Be‐
85 cause of this Gotcha cannot wrap intra-library calls (such as a call to
86 a static function in C) or calls in statically-linked binaries. Binary
87 rewriting technology such as DyninstAPI is more appropriate for these
88 use cases. Additionally, the function pointer wrapping feature with
89 GOTCHA only applies to function pointers created after wrapping func‐
90 tions. The function pointers created before wrapping would not be
91 wrapped by gotcha.
92
94 This section describes how to build GOTCHA, and what configure time op‐
95 tions are available.
96
97 There are two build options:
98
99 • build GOTCHA with Spack, and
100
101 • build GOTCHA with cmake
102
103
104 ----
105
106
107
108 Build GOTCHA with Spack
109 One may install GOTCHA with Spack. If you already have Spack, make
110 sure you have the latest release. If you use a clone of the Spack de‐
111 velop branch, be sure to pull the latest changes.
112
113 Install Spack
114 $ git clone https://github.com/spack/spack
115 $ # create a packages.yaml specific to your machine
116 $ . spack/share/spack/setup-env.sh
117
118 Use Spack's shell support to add Spack to your PATH and enable use of
119 the spack command.
120
121 Build and Install GOTCHA
122 $ spack install gotcha
123 $ spack load gotcha
124
125 If the most recent changes on the development branch ('dev') of GOTCHA
126 are desired, then do spack install gotcha@develop.
127
128 ATTENTION:
129 The initial install could take a while as Spack will install build
130 dependencies (autoconf, automake, m4, libtool, and pkg-config) as
131 well as any dependencies of dependencies (cmake, perl, etc.) if you
132 don't already have these dependencies installed through Spack or
133 haven't told Spack where they are locally installed on your system
134 (i.e., through a custom packages.yaml). Run spack spec -I gotcha
135 before installing to see what Spack is going to do.
136
137
138 ----
139
140
141
142 Build GOTCHA with CMake
143 Download the latest GOTCHA release from the Releases page or clone the
144 develop branch ('develop') from the GOTCHA repository
145 https://github.com/LLNL/GOTCHA.
146
147 cmake . -B build -DCMAKE_INSTALL_PREFIX=<where you want to install GOTCHA>
148 cmake --build build
149 cmake --install build
150
151
152 ----
153
154
155
157 This section describes how to use the GOTCHA API in an application.
158
159
160 ----
161
162
163
164 Include the GOTCHA Header
165 In C or C++ applications, include gotcha.h.
166
167 #include <gotcha.h>
168
169 Define your Gotcha wrappee
170 Gotcha wrappee enables the application to call the function it wrapped
171 using GOTCHA.
172
173 static gotcha_wrappee_handle_t wrappee_fputs_handle;
174
175 Define your function wrapper
176 The function wrapper for wrapping functions from shared libraries.
177
178 static int fputs_wrapper(const char *str, FILE *f) {
179 // insert clever tool logic here
180 typeof(&fputs_wrapper) wrappee_fputs = gotcha_get_wrappee(wrappee_fputs_handle); // get my wrappee from Gotcha
181 return wrappee_fputs(str, f); //wrappee_fputs was directed to the original fputs by GOTCHA
182 }
183
184 Define GOTCHA bindings
185 GOTCHA works on binding a function name, wrapper function, and wrappee
186 handle. Gotcha works on triplets containing this information.
187
188 struct gotcha_binding_t wrap_actions [] = {
189 { "fputs", fputs_wrapper, &wrappee_fputs_handle },
190 };
191
192 Wrap the binding calls
193 To initiate gotcha with the bindings defined in last step, tools can
194 call the gotcha_wrap function. This function should be called before
195 any interception is expected by the tool. Some popular places for
196 calling this are gnu constructor or the start of main function. The
197 function will always be successful and would never throw error.
198
199 gotcha_error_t gotcha_wrap(wrap_actions,
200 sizeof(wrap_actions)/sizeof(struct gotcha_binding_t), // number of bindings
201 "my_tool_name");
202
203 Multiple gotcha_wrap Caveat
204
205 We allow tools to bind different set of functions to different tool
206 names through multiple gotcha_wrap calls. However, a tool within
207 GOTCHA is designed to layer or prioritize the order of functions bind‐
208 ing same symbol name. For instance, if multiple tools bind the fputs
209 functions, then GOTCHA layers them to call one after the other with the
210 lowest level being the system call. In this case, tools can prioritize
211 which tools go first or second at runtime to determine the wrapper or‐
212 der for GOTCHA. If an tool uses multiple bindings then they have to
213 set priority to different bindings identified using tool_name defined
214 within the same tool.
215
216 ATTENTION:
217 The gotcha_wrap function modifies the gotcha_binding_t wrap_ac‐
218 tions[] provided by the user. GOTCHA does not create a copy of the
219 binding functions and is the responsibility of the user to maintain
220 this binding.
221
222 Set priority of tool binding
223 To set priority of tool within GOTCHA, tools can utilize
224 gotcha_set_priority function. The priority is an integer value with
225 lower values are for inner most call. The lowest layer is the system
226 call followed by GOTCHA layer and finally other tools based on prior‐
227 ity. The API would never fail. If it return GOTCHA_INTERNAL as error
228 then there was issue with memory allocation of tool. If multiple tools
229 have same priority then they are wrapper in FIFO order with the first
230 tool being the inner-most wrapper. Without calling this API the de‐
231 fault priority given to each tool is -1.
232
233 gotcha_error_t gotcha_set_priority(const char* tool_name,
234 int priority);
235
236 Get priority of tool binding
237 This API gets the priority of the tool. This could be default or as as‐
238 signed by the tool.
239
240 gotcha_error_t gotcha_get_priority(const char* tool_name,
241 int *priority);
242
243 Get the wrapped function from GOTCHA stack
244 This API return the wrapped function to call based on the tool's han‐
245 dle. The tools handle is used to locate the next element of the wrap‐
246 per stack and return the function. Returns the ptr of the wrapped
247 function.
248
249 void* gotcha_get_wrappee(gotcha_wrappee_handle_t handle);
250
251 Filter libraries
252 Within GOTCHA, even bound symbol is updated in the GOT table for each
253 shared library loaded within the tool. In some cases, tools might not
254 want to update these symbols on some libraries. For these cases,
255 GOTCHA has a series of filter functions that can assist tools to define
256 which libraries should be updated. CAUTION: this could lead to behav‐
257 iors where calls from these libraries would not be intercepted by
258 GOTCHA wrappers and need to handled by the tool.
259
260 Filter by Name
261 This API allows GOTCHA to include only libraries given specified by the
262 user. This could be a partial match of string contains as defined by
263 strstr function in C.
264
265 void gotcha_filter_libraries_by_name(const char* nameFilter);
266
267 Filter if Last
268 This API allows GOTCHA to include only the last library defined in the
269 linker of the tool.
270
271 void gotcha_only_filter_last();
272
273 Filter by user defined function
274 This API allows users to define a function that selected the libraries
275 that user wants to intercept. The function should take struct
276 link_map* as input and return true if it should be wrapped by GOTCHA.
277 TIP: the library name can be accessed by map->l_name.
278
279 void gotcha_set_library_filter_func(int(*new_func)(struct link_map*));
280
281 Restore default filter of GOTCHA
282 The default filter of gotcha selects all libraries loaded. This func‐
283 tion set the default filter back for GOTCHA.
284
285 void gotcha_restore_library_filter_func();
286
288 This example shows how to use gotcha to wrap the open and fopen libc
289 calls. This example is self-contained, though in typical gotcha work‐
290 flows the gotcha calls would be in a separate library from the applica‐
291 tion.
292
293 The example logs the parameters and return result of every open and
294 fopen call to stderr.
295
296 #include <stdio.h>
297 #include <sys/types.h>
298 #include <sys/stat.h>
299 #include <fcntl.h>
300
301 #include "gotcha/gotcha.h"
302
303 typedef int (*open_fptr)(const char *pathname, int flags, mode_t mode);
304 typedef FILE* (*fopen_fptr)(const char *pathname, const char *mode);
305
306 static gotcha_wrappee_handle_t open_handle;
307 static gotcha_wrappee_handle_t fopen_handle;
308
309 static int open_wrapper(const char *pathname, int flags, mode_t mode) {
310 open_fptr open_wrappee = (open_fptr) gotcha_get_wrappee(open_handle);
311 int result = open_wrappee(pathname, flags, mode);
312 fprintf(stderr, "open(%s, %d, %u) = %d\n",
313 pathname, flags, (unsigned int) mode, result);
314 return result;
315 }
316
317 static FILE *fopen_wrapper(const char *path, const char *mode) {
318 fopen_fptr fopen_wrappee = (fopen_fptr) gotcha_get_wrappee(fopen_handle);
319 FILE *result = fopen_wrappee(path, mode);
320 fprintf(stderr, "fopen(%s, %s) = %p\n",
321 path, mode, result);
322 return result;
323 }
324
325 static gotcha_binding_t bindings[] = {
326 { "open", open_wrapper, &open_handle },
327 { "fopen", fopen_wrapper, &fopen_handle }
328 };
329
330 int main(int argc, char *argv[]) {
331 gotcha_wrap(bindings, 2, "demotool");
332
333 open("/dev/null", O_RDONLY);
334 open("/dev/null", O_WRONLY | O_CREAT | O_EXCL);
335 fopen("/dev/random", "r");
336 fopen("/does/not/exist", "w");
337
338 return 0;
339 }
340
341 The fundamental data structure in the Gotcha API is the gotcha_bind‐
342 ing_t table, which is shown in lines 29-32. This table states that
343 calls to open should be rerouted to call open_wrapper, and similarly
344 for fopen and fopen_wrapper. The original open and fopen functions will
345 still be accessible via the handles open_handle and fopen_handle.
346
347 The binding table is passed to Gotcha on line 36, which specifies there
348 are two entries in the table and that these are part of the “demotool”
349 tool. The open_handle and fopen_handle variables are updated by this
350 call to gotcha_wrap and can now be used to look up function pointers to
351 the original open and fopen calls.
352
353 The subsequent callsites to open and fopen on lines 37-40 are redi‐
354 rected to respectively call open_wrapper and fopen_wrapper on lines
355 14-20 and 22-27. Each of these functions looks up the original open and
356 fopen functions using the gotcha_get_wrappee API call and the open_han‐
357 dle and fopen_handle on lines 15 and 23.
358
359 The wrappers call then call the underlying functions open and fopen
360 functions on lines 16 and 24. The print the parameters and results of
361 these calls on lines 17 and 25 and return.
362
363 Note that this example skips proper error handling for brevity. The
364 call to gotcha_wrap could have failed to find instances of fopen and
365 open in the process, which would have led to an error return. The calls
366 to fprintf on lines 17 and 25 are stomping on the value of errno, which
367 could be set in the open and fopen calls on lines 16 and 24.
368
370 Coding Conventions
371 GOTCHA follows the Google coding style. Please run git clang-format
372 --diff HEAD~1 -q to check your patch for style problems before submit‐
373 ting it for review.
374
375 Styling Code
376 The clang-format tool can be used to apply much of the required code
377 styling used in the project.
378
379 To apply style to the source file foo.c:
380
381 clang-format --style=Google --Werror foo.c
382
383 The .clang-format file specifies the options used for this project. For
384 a full list of available clang-format options, see
385 https://clang.llvm.org/docs/ClangFormat.html.
386
387 Verifying Style Checks
388 To check that uncommitted changes meet the coding style, use the fol‐
389 lowing command:
390
391 git clang-format --diff HEAD~1 -q
392
393 TIP:
394 This command will only check specific changes and additions to files
395 that are already tracked by git. Run the command git add -N [<un‐
396 tracked_file>...] first in order to style check new files as well.
397
398
399 ----
400
401
402
403 Commit Message Format
404 Commit messages for new changes must meet the following guidelines:
405
406 • In 50 characters or less, provide a summary of the change as the
407 first line in the commit message.
408
409 • A body which provides a description of the change. If necessary,
410 please summarize important information such as why the proposed ap‐
411 proach was chosen or a brief description of the bug you are resolv‐
412 ing. Each line of the body must be 72 characters or less.
413
414 An example commit message for new changes is provided below.
415
416 Capitalized, short (50 chars or less) summary
417
418 More detailed explanatory text, if necessary. Wrap it to about 72
419 characters or so. In some contexts, the first line is treated as the
420 subject of an email and the rest of the text as the body. The blank
421 line separating the summary from the body is critical (unless you omit
422 the body entirely); tools like rebase can get confused if you run the
423 two together.
424
425 Write your commit message in the imperative: "Fix bug" and not "Fixed bug"
426 or "Fixes bug." This convention matches up with commit messages generated
427 by commands like git merge and git revert.
428
429 Further paragraphs come after blank lines.
430
431 - Bullet points are okay
432
433 - Typically a hyphen or asterisk is used for the bullet, followed by a
434 single space, with blank lines in between, but conventions vary here
435
436 - Use a hanging indent
437
439 We can never have enough testing. Any additional tests you can write
440 are always greatly appreciated.
441
442 Unit Tests
443 Testing new core features within GOTCHA should be implemented in the
444 test/unit/gotcha_unit_tests.c using the check framework as defined in
445 https://libcheck.github.io/check.
446
447 Create a new test
448 We can create a new test using START_TEST and END_TEST macros.
449
450 START_TEST(sample_test){
451 }
452 END_TEST
453
454 Create a new suite
455 These new tests can be added to new suite with code similar to the fol‐
456 lowing. To add to existing suite, we need use tcase_add_test api to
457 add the test function to the suite.
458
459 Suite* gotcha_sample_suite(){
460 Suite* s = suite_create("Sample");
461 TCase* sample_case = configured_case_create("Basic tests");
462 tcase_add_test(sample_case, sample_test);
463 suite_add_tcase(s, sample_case);
464 return s;
465 }
466
467 Adding suite to runner
468 Within the main function of the test/unit/gotcha_unit_tests.c, the
469 gotcha_sample_suite can be added as follows.
470
471 Suite* sample_suite = gotcha_sample_suite();
472 SRunner* sample_runner = srunner_create(sample_suite);
473 srunner_run_all(sample_suite, CK_NORMAL);
474 num_fails += srunner_ntests_failed(sample_suite);
475
476 Testing tool specific usage of GOTCHA
477 We should use custom test cases where we are testing the API for GOTCHA
478 for specific features such as filtering libraries, main function bind‐
479 ings, etc. These test cases can be stored within the test folder. Look
480 at existing examples such as test/stack and test/dlopen to understand
481 how we can implement these tests.
482
483 Once you add a self containing test case within test, we can add it to
484 the test/CMakeLists.txt.
485
486 • Index
487
488 • Module Index
489
490 • Search Page
491
493 Hariharan Devarajan, David Poliakoff, Matt Legendre
494
496 2023, Lawrence Livermore National Security, LLC
497
498
499
500
5011.0 Nov 18, 2023 GOTCHA(1)