[TargetVersion] Only enable on RISC-V and AArch64 (#115991)
[llvm-project.git] / clang / tools / scan-build-py / lib / libear / ear.c
blob323251181a45317babfdeaf5c9a595ec9291eac6
1 /* -*- coding: utf-8 -*-
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 */
7 /**
8 * This file implements a shared library. This library can be pre-loaded by
9 * the dynamic linker of the Operating System (OS). It implements a few function
10 * related to process creation. By pre-load this library the executed process
11 * uses these functions instead of those from the standard library.
13 * The idea here is to inject a logic before call the real methods. The logic is
14 * to dump the call into a file. To call the real method this library is doing
15 * the job of the dynamic linker.
17 * The only input for the log writing is about the destination directory.
18 * This is passed as environment variable.
21 // NOLINTNEXTLINE
22 #include "config.h"
24 #include <dlfcn.h>
25 #include <pthread.h>
26 #include <stdarg.h>
27 #include <stddef.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
33 #if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP
34 #include <spawn.h>
35 #endif
37 #if defined HAVE_NSGETENVIRON
38 #include <crt_externs.h>
39 #else
40 extern char **environ;
41 #endif
43 #define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR"
44 #ifdef APPLE
45 #define ENV_FLAT "DYLD_FORCE_FLAT_NAMESPACE"
46 #define ENV_PRELOAD "DYLD_INSERT_LIBRARIES"
47 #define ENV_SIZE 3
48 #else
49 #define ENV_PRELOAD "LD_PRELOAD"
50 #define ENV_SIZE 2
51 #endif
53 #define DLSYM(TYPE_, VAR_, SYMBOL_) \
54 union { \
55 void *from; \
56 TYPE_ to; \
57 } cast; \
58 if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) { \
59 perror("bear: dlsym"); \
60 exit(EXIT_FAILURE); \
61 } \
62 TYPE_ const VAR_ = cast.to;
64 typedef char const *bear_env_t[ENV_SIZE];
66 static int bear_capture_env_t(bear_env_t *env);
67 static int bear_reset_env_t(bear_env_t *env);
68 static void bear_release_env_t(bear_env_t *env);
69 static char const **bear_update_environment(char *const envp[],
70 bear_env_t *env);
71 static char const **bear_update_environ(char const **in, char const *key,
72 char const *value);
73 static char **bear_get_environment();
74 static void bear_report_call(char const *fun, char const *const argv[]);
75 static char const **bear_strings_build(char const *arg, va_list *ap);
76 static char const **bear_strings_copy(char const **const in);
77 static char const **bear_strings_append(char const **in, char const *e);
78 static size_t bear_strings_length(char const *const *in);
79 static void bear_strings_release(char const **);
81 static bear_env_t env_names = {ENV_OUTPUT, ENV_PRELOAD
82 #ifdef ENV_FLAT
84 ENV_FLAT
85 #endif
88 static bear_env_t initial_env = {0, 0
89 #ifdef ENV_FLAT
92 #endif
95 static int initialized = 0;
96 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
98 static void on_load(void) __attribute__((constructor));
99 static void on_unload(void) __attribute__((destructor));
101 #ifdef HAVE_EXECVE
102 static int call_execve(const char *path, char *const argv[],
103 char *const envp[]);
104 #endif
105 #ifdef HAVE_EXECVP
106 static int call_execvp(const char *file, char *const argv[]);
107 #endif
108 #ifdef HAVE_EXECVPE
109 static int call_execvpe(const char *file, char *const argv[],
110 char *const envp[]);
111 #endif
112 #ifdef HAVE_EXECVP2
113 static int call_execvP(const char *file, const char *search_path,
114 char *const argv[]);
115 #endif
116 #ifdef HAVE_EXECT
117 static int call_exect(const char *path, char *const argv[], char *const envp[]);
118 #endif
119 #ifdef HAVE_POSIX_SPAWN
120 static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
121 const posix_spawn_file_actions_t *file_actions,
122 const posix_spawnattr_t *restrict attrp,
123 char *const argv[restrict],
124 char *const envp[restrict]);
125 #endif
126 #ifdef HAVE_POSIX_SPAWNP
127 static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
128 const posix_spawn_file_actions_t *file_actions,
129 const posix_spawnattr_t *restrict attrp,
130 char *const argv[restrict],
131 char *const envp[restrict]);
132 #endif
134 /* Initialization method to Captures the relevant environment variables.
137 static void on_load(void) {
138 pthread_mutex_lock(&mutex);
139 if (!initialized)
140 initialized = bear_capture_env_t(&initial_env);
141 pthread_mutex_unlock(&mutex);
144 static void on_unload(void) {
145 pthread_mutex_lock(&mutex);
146 bear_release_env_t(&initial_env);
147 initialized = 0;
148 pthread_mutex_unlock(&mutex);
151 /* These are the methods we are try to hijack.
154 #ifdef HAVE_EXECVE
155 int execve(const char *path, char *const argv[], char *const envp[]) {
156 bear_report_call(__func__, (char const *const *)argv);
157 return call_execve(path, argv, envp);
159 #endif
161 #ifdef HAVE_EXECV
162 #ifndef HAVE_EXECVE
163 #error can not implement execv without execve
164 #endif
165 int execv(const char *path, char *const argv[]) {
166 bear_report_call(__func__, (char const *const *)argv);
167 char *const *envp = bear_get_environment();
168 return call_execve(path, argv, envp);
170 #endif
172 #ifdef HAVE_EXECVPE
173 int execvpe(const char *file, char *const argv[], char *const envp[]) {
174 bear_report_call(__func__, (char const *const *)argv);
175 return call_execvpe(file, argv, envp);
177 #endif
179 #ifdef HAVE_EXECVP
180 int execvp(const char *file, char *const argv[]) {
181 bear_report_call(__func__, (char const *const *)argv);
182 return call_execvp(file, argv);
184 #endif
186 #ifdef HAVE_EXECVP2
187 int execvP(const char *file, const char *search_path, char *const argv[]) {
188 bear_report_call(__func__, (char const *const *)argv);
189 return call_execvP(file, search_path, argv);
191 #endif
193 #ifdef HAVE_EXECT
194 int exect(const char *path, char *const argv[], char *const envp[]) {
195 bear_report_call(__func__, (char const *const *)argv);
196 return call_exect(path, argv, envp);
198 #endif
200 #ifdef HAVE_EXECL
201 #ifndef HAVE_EXECVE
202 #error can not implement execl without execve
203 #endif
204 int execl(const char *path, const char *arg, ...) {
205 va_list args;
206 va_start(args, arg);
207 char const **argv = bear_strings_build(arg, &args);
208 va_end(args);
210 bear_report_call(__func__, (char const *const *)argv);
211 char *const *envp = bear_get_environment();
212 int const result = call_execve(path, (char *const *)argv, envp);
214 bear_strings_release(argv);
215 return result;
217 #endif
219 #ifdef HAVE_EXECLP
220 #ifndef HAVE_EXECVP
221 #error can not implement execlp without execvp
222 #endif
223 int execlp(const char *file, const char *arg, ...) {
224 va_list args;
225 va_start(args, arg);
226 char const **argv = bear_strings_build(arg, &args);
227 va_end(args);
229 bear_report_call(__func__, (char const *const *)argv);
230 int const result = call_execvp(file, (char *const *)argv);
232 bear_strings_release(argv);
233 return result;
235 #endif
237 #ifdef HAVE_EXECLE
238 #ifndef HAVE_EXECVE
239 #error can not implement execle without execve
240 #endif
241 // int execle(const char *path, const char *arg, ..., char * const envp[]);
242 int execle(const char *path, const char *arg, ...) {
243 va_list args;
244 va_start(args, arg);
245 char const **argv = bear_strings_build(arg, &args);
246 char const **envp = va_arg(args, char const **);
247 va_end(args);
249 bear_report_call(__func__, (char const *const *)argv);
250 int const result =
251 call_execve(path, (char *const *)argv, (char *const *)envp);
253 bear_strings_release(argv);
254 return result;
256 #endif
258 #ifdef HAVE_POSIX_SPAWN
259 int posix_spawn(pid_t *restrict pid, const char *restrict path,
260 const posix_spawn_file_actions_t *file_actions,
261 const posix_spawnattr_t *restrict attrp,
262 char *const argv[restrict], char *const envp[restrict]) {
263 bear_report_call(__func__, (char const *const *)argv);
264 return call_posix_spawn(pid, path, file_actions, attrp, argv, envp);
266 #endif
268 #ifdef HAVE_POSIX_SPAWNP
269 int posix_spawnp(pid_t *restrict pid, const char *restrict file,
270 const posix_spawn_file_actions_t *file_actions,
271 const posix_spawnattr_t *restrict attrp,
272 char *const argv[restrict], char *const envp[restrict]) {
273 bear_report_call(__func__, (char const *const *)argv);
274 return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp);
276 #endif
278 /* These are the methods which forward the call to the standard implementation.
281 #ifdef HAVE_EXECVE
282 static int call_execve(const char *path, char *const argv[],
283 char *const envp[]) {
284 typedef int (*func)(const char *, char *const *, char *const *);
286 DLSYM(func, fp, "execve");
288 char const **const menvp = bear_update_environment(envp, &initial_env);
289 int const result = (*fp)(path, argv, (char *const *)menvp);
290 bear_strings_release(menvp);
291 return result;
293 #endif
295 #ifdef HAVE_EXECVPE
296 static int call_execvpe(const char *file, char *const argv[],
297 char *const envp[]) {
298 typedef int (*func)(const char *, char *const *, char *const *);
300 DLSYM(func, fp, "execvpe");
302 char const **const menvp = bear_update_environment(envp, &initial_env);
303 int const result = (*fp)(file, argv, (char *const *)menvp);
304 bear_strings_release(menvp);
305 return result;
307 #endif
309 #ifdef HAVE_EXECVP
310 static int call_execvp(const char *file, char *const argv[]) {
311 typedef int (*func)(const char *file, char *const argv[]);
313 DLSYM(func, fp, "execvp");
315 bear_env_t current_env;
316 bear_capture_env_t(&current_env);
317 bear_reset_env_t(&initial_env);
318 int const result = (*fp)(file, argv);
319 bear_reset_env_t(&current_env);
320 bear_release_env_t(&current_env);
322 return result;
324 #endif
326 #ifdef HAVE_EXECVP2
327 static int call_execvP(const char *file, const char *search_path,
328 char *const argv[]) {
329 typedef int (*func)(const char *, const char *, char *const *);
331 DLSYM(func, fp, "execvP");
333 bear_env_t current_env;
334 bear_capture_env_t(&current_env);
335 bear_reset_env_t(&initial_env);
336 int const result = (*fp)(file, search_path, argv);
337 bear_reset_env_t(&current_env);
338 bear_release_env_t(&current_env);
340 return result;
342 #endif
344 #ifdef HAVE_EXECT
345 static int call_exect(const char *path, char *const argv[],
346 char *const envp[]) {
347 typedef int (*func)(const char *, char *const *, char *const *);
349 DLSYM(func, fp, "exect");
351 char const **const menvp = bear_update_environment(envp, &initial_env);
352 int const result = (*fp)(path, argv, (char *const *)menvp);
353 bear_strings_release(menvp);
354 return result;
356 #endif
358 #ifdef HAVE_POSIX_SPAWN
359 static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
360 const posix_spawn_file_actions_t *file_actions,
361 const posix_spawnattr_t *restrict attrp,
362 char *const argv[restrict],
363 char *const envp[restrict]) {
364 typedef int (*func)(pid_t *restrict, const char *restrict,
365 const posix_spawn_file_actions_t *,
366 const posix_spawnattr_t *restrict, char *const *restrict,
367 char *const *restrict);
369 DLSYM(func, fp, "posix_spawn");
371 char const **const menvp = bear_update_environment(envp, &initial_env);
372 int const result =
373 (*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp);
374 bear_strings_release(menvp);
375 return result;
377 #endif
379 #ifdef HAVE_POSIX_SPAWNP
380 static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
381 const posix_spawn_file_actions_t *file_actions,
382 const posix_spawnattr_t *restrict attrp,
383 char *const argv[restrict],
384 char *const envp[restrict]) {
385 typedef int (*func)(pid_t *restrict, const char *restrict,
386 const posix_spawn_file_actions_t *,
387 const posix_spawnattr_t *restrict, char *const *restrict,
388 char *const *restrict);
390 DLSYM(func, fp, "posix_spawnp");
392 char const **const menvp = bear_update_environment(envp, &initial_env);
393 int const result =
394 (*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp);
395 bear_strings_release(menvp);
396 return result;
398 #endif
400 /* this method is to write log about the process creation. */
402 static void bear_report_call(char const *fun, char const *const argv[]) {
403 static int const GS = 0x1d;
404 static int const RS = 0x1e;
405 static int const US = 0x1f;
407 if (!initialized)
408 return;
410 pthread_mutex_lock(&mutex);
411 const char *cwd = getcwd(NULL, 0);
412 if (0 == cwd) {
413 perror("bear: getcwd");
414 pthread_mutex_unlock(&mutex);
415 exit(EXIT_FAILURE);
417 char const *const out_dir = initial_env[0];
418 size_t const path_max_length = strlen(out_dir) + 32;
419 char filename[path_max_length];
420 if (-1 ==
421 snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) {
422 perror("bear: snprintf");
423 pthread_mutex_unlock(&mutex);
424 exit(EXIT_FAILURE);
426 FILE *fd = fopen(filename, "a+");
427 if (0 == fd) {
428 perror("bear: fopen");
429 pthread_mutex_unlock(&mutex);
430 exit(EXIT_FAILURE);
432 fprintf(fd, "%d%c", getpid(), RS);
433 fprintf(fd, "%d%c", getppid(), RS);
434 fprintf(fd, "%s%c", fun, RS);
435 fprintf(fd, "%s%c", cwd, RS);
436 size_t const argc = bear_strings_length(argv);
437 for (size_t it = 0; it < argc; ++it) {
438 fprintf(fd, "%s%c", argv[it], US);
440 fprintf(fd, "%c", GS);
441 if (fclose(fd)) {
442 perror("bear: fclose");
443 pthread_mutex_unlock(&mutex);
444 exit(EXIT_FAILURE);
446 free((void *)cwd);
447 pthread_mutex_unlock(&mutex);
450 /* update environment assure that children processes will copy the desired
451 * behaviour */
453 static int bear_capture_env_t(bear_env_t *env) {
454 int status = 1;
455 for (size_t it = 0; it < ENV_SIZE; ++it) {
456 char const *const env_value = getenv(env_names[it]);
457 char const *const env_copy = (env_value) ? strdup(env_value) : env_value;
458 (*env)[it] = env_copy;
459 status &= (env_copy) ? 1 : 0;
461 return status;
464 static int bear_reset_env_t(bear_env_t *env) {
465 int status = 1;
466 for (size_t it = 0; it < ENV_SIZE; ++it) {
467 if ((*env)[it]) {
468 setenv(env_names[it], (*env)[it], 1);
469 } else {
470 unsetenv(env_names[it]);
473 return status;
476 static void bear_release_env_t(bear_env_t *env) {
477 for (size_t it = 0; it < ENV_SIZE; ++it) {
478 free((void *)(*env)[it]);
479 (*env)[it] = 0;
483 static char const **bear_update_environment(char *const envp[],
484 bear_env_t *env) {
485 char const **result = bear_strings_copy((char const **)envp);
486 for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it)
487 result = bear_update_environ(result, env_names[it], (*env)[it]);
488 return result;
491 static char const **bear_update_environ(char const *envs[], char const *key,
492 char const *const value) {
493 // find the key if it's there
494 size_t const key_length = strlen(key);
495 char const **it = envs;
496 for (; (it) && (*it); ++it) {
497 if ((0 == strncmp(*it, key, key_length)) && (strlen(*it) > key_length) &&
498 ('=' == (*it)[key_length]))
499 break;
501 // allocate a environment entry
502 size_t const value_length = strlen(value);
503 size_t const env_length = key_length + value_length + 2;
504 char *env = malloc(env_length);
505 if (0 == env) {
506 perror("bear: malloc [in env_update]");
507 exit(EXIT_FAILURE);
509 if (-1 == snprintf(env, env_length, "%s=%s", key, value)) {
510 perror("bear: snprintf");
511 exit(EXIT_FAILURE);
513 // replace or append the environment entry
514 if (it && *it) {
515 free((void *)*it);
516 *it = env;
517 return envs;
519 return bear_strings_append(envs, env);
522 static char **bear_get_environment() {
523 #if defined HAVE_NSGETENVIRON
524 return *_NSGetEnviron();
525 #else
526 return environ;
527 #endif
530 /* util methods to deal with string arrays. environment and process arguments
531 * are both represented as string arrays. */
533 static char const **bear_strings_build(char const *const arg, va_list *args) {
534 char const **result = 0;
535 size_t size = 0;
536 for (char const *it = arg; it; it = va_arg(*args, char const *)) {
537 result = realloc(result, (size + 1) * sizeof(char const *));
538 if (0 == result) {
539 perror("bear: realloc");
540 exit(EXIT_FAILURE);
542 char const *copy = strdup(it);
543 if (0 == copy) {
544 perror("bear: strdup");
545 exit(EXIT_FAILURE);
547 result[size++] = copy;
549 result = realloc(result, (size + 1) * sizeof(char const *));
550 if (0 == result) {
551 perror("bear: realloc");
552 exit(EXIT_FAILURE);
554 result[size++] = 0;
556 return result;
559 static char const **bear_strings_copy(char const **const in) {
560 size_t const size = bear_strings_length(in);
562 char const **const result = malloc((size + 1) * sizeof(char const *));
563 if (0 == result) {
564 perror("bear: malloc");
565 exit(EXIT_FAILURE);
568 char const **out_it = result;
569 for (char const *const *in_it = in; (in_it) && (*in_it); ++in_it, ++out_it) {
570 *out_it = strdup(*in_it);
571 if (0 == *out_it) {
572 perror("bear: strdup");
573 exit(EXIT_FAILURE);
576 *out_it = 0;
577 return result;
580 static char const **bear_strings_append(char const **const in,
581 char const *const e) {
582 size_t size = bear_strings_length(in);
583 char const **result = realloc(in, (size + 2) * sizeof(char const *));
584 if (0 == result) {
585 perror("bear: realloc");
586 exit(EXIT_FAILURE);
588 result[size++] = e;
589 result[size++] = 0;
590 return result;
593 static size_t bear_strings_length(char const *const *const in) {
594 size_t result = 0;
595 for (char const *const *it = in; (it) && (*it); ++it)
596 ++result;
597 return result;
600 static void bear_strings_release(char const **in) {
601 for (char const *const *it = in; (it) && (*it); ++it) {
602 free((void *)*it);
604 free((void *)in);