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
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.
33 #if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP
37 #if defined HAVE_NSGETENVIRON
38 #include <crt_externs.h>
40 extern char **environ
;
43 #define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR"
45 #define ENV_FLAT "DYLD_FORCE_FLAT_NAMESPACE"
46 #define ENV_PRELOAD "DYLD_INSERT_LIBRARIES"
49 #define ENV_PRELOAD "LD_PRELOAD"
53 #define DLSYM(TYPE_, VAR_, SYMBOL_) \
58 if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) { \
59 perror("bear: dlsym"); \
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
[],
71 static char const **bear_update_environ(char const **in
, char const *key
,
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
88 static bear_env_t initial_env
= {0, 0
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
));
102 static int call_execve(const char *path
, char *const argv
[],
106 static int call_execvp(const char *file
, char *const argv
[]);
109 static int call_execvpe(const char *file
, char *const argv
[],
113 static int call_execvP(const char *file
, const char *search_path
,
117 static int call_exect(const char *path
, char *const argv
[], char *const envp
[]);
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
]);
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
]);
134 /* Initialization method to Captures the relevant environment variables.
137 static void on_load(void) {
138 pthread_mutex_lock(&mutex
);
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
);
148 pthread_mutex_unlock(&mutex
);
151 /* These are the methods we are try to hijack.
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
);
163 #error can not implement execv without execve
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
);
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
);
180 int execvp(const char *file
, char *const argv
[]) {
181 bear_report_call(__func__
, (char const *const *)argv
);
182 return call_execvp(file
, argv
);
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
);
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
);
202 #error can not implement execl without execve
204 int execl(const char *path
, const char *arg
, ...) {
207 char const **argv
= bear_strings_build(arg
, &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
);
221 #error can not implement execlp without execvp
223 int execlp(const char *file
, const char *arg
, ...) {
226 char const **argv
= bear_strings_build(arg
, &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
);
239 #error can not implement execle without execve
241 // int execle(const char *path, const char *arg, ..., char * const envp[]);
242 int execle(const char *path
, const char *arg
, ...) {
245 char const **argv
= bear_strings_build(arg
, &args
);
246 char const **envp
= va_arg(args
, char const **);
249 bear_report_call(__func__
, (char const *const *)argv
);
251 call_execve(path
, (char *const *)argv
, (char *const *)envp
);
253 bear_strings_release(argv
);
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
);
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
);
278 /* These are the methods which forward the call to the standard implementation.
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
);
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
);
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(¤t_env
);
317 bear_reset_env_t(&initial_env
);
318 int const result
= (*fp
)(file
, argv
);
319 bear_reset_env_t(¤t_env
);
320 bear_release_env_t(¤t_env
);
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(¤t_env
);
335 bear_reset_env_t(&initial_env
);
336 int const result
= (*fp
)(file
, search_path
, argv
);
337 bear_reset_env_t(¤t_env
);
338 bear_release_env_t(¤t_env
);
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
);
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
);
373 (*fp
)(pid
, path
, file_actions
, attrp
, argv
, (char *const *restrict
)menvp
);
374 bear_strings_release(menvp
);
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
);
394 (*fp
)(pid
, file
, file_actions
, attrp
, argv
, (char *const *restrict
)menvp
);
395 bear_strings_release(menvp
);
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;
410 pthread_mutex_lock(&mutex
);
411 const char *cwd
= getcwd(NULL
, 0);
413 perror("bear: getcwd");
414 pthread_mutex_unlock(&mutex
);
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
];
421 snprintf(filename
, path_max_length
, "%s/%d.cmd", out_dir
, getpid())) {
422 perror("bear: snprintf");
423 pthread_mutex_unlock(&mutex
);
426 FILE *fd
= fopen(filename
, "a+");
428 perror("bear: fopen");
429 pthread_mutex_unlock(&mutex
);
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
);
442 perror("bear: fclose");
443 pthread_mutex_unlock(&mutex
);
447 pthread_mutex_unlock(&mutex
);
450 /* update environment assure that children processes will copy the desired
453 static int bear_capture_env_t(bear_env_t
*env
) {
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;
464 static int bear_reset_env_t(bear_env_t
*env
) {
466 for (size_t it
= 0; it
< ENV_SIZE
; ++it
) {
468 setenv(env_names
[it
], (*env
)[it
], 1);
470 unsetenv(env_names
[it
]);
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
]);
483 static char const **bear_update_environment(char *const envp
[],
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
]);
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
]))
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
);
506 perror("bear: malloc [in env_update]");
509 if (-1 == snprintf(env
, env_length
, "%s=%s", key
, value
)) {
510 perror("bear: snprintf");
513 // replace or append the environment entry
519 return bear_strings_append(envs
, env
);
522 static char **bear_get_environment() {
523 #if defined HAVE_NSGETENVIRON
524 return *_NSGetEnviron();
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;
536 for (char const *it
= arg
; it
; it
= va_arg(*args
, char const *)) {
537 result
= realloc(result
, (size
+ 1) * sizeof(char const *));
539 perror("bear: realloc");
542 char const *copy
= strdup(it
);
544 perror("bear: strdup");
547 result
[size
++] = copy
;
549 result
= realloc(result
, (size
+ 1) * sizeof(char const *));
551 perror("bear: realloc");
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 *));
564 perror("bear: malloc");
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
);
572 perror("bear: strdup");
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 *));
585 perror("bear: realloc");
593 static size_t bear_strings_length(char const *const *const in
) {
595 for (char const *const *it
= in
; (it
) && (*it
); ++it
)
600 static void bear_strings_release(char const **in
) {
601 for (char const *const *it
= in
; (it
) && (*it
); ++it
) {