Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14 pmbaty 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
*/
6
 
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.
12
 *
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.
16
 *
17
 * The only input for the log writing is about the destination directory.
18
 * This is passed as environment variable.
19
 */
20
 
21
// NOLINTNEXTLINE
22
#include "config.h"
23
 
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>
32
 
33
#if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP
34
#include <spawn.h>
35
#endif
36
 
37
#if defined HAVE_NSGETENVIRON
38
#include <crt_externs.h>
39
#else
40
extern char **environ;
41
#endif
42
 
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
52
 
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;
63
 
64
typedef char const *bear_env_t[ENV_SIZE];
65
 
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 **);
80
 
81
static bear_env_t env_names = {ENV_OUTPUT, ENV_PRELOAD
82
#ifdef ENV_FLAT
83
                               ,
84
                               ENV_FLAT
85
#endif
86
};
87
 
88
static bear_env_t initial_env = {0, 0
89
#ifdef ENV_FLAT
90
                                 ,
91
 
92
#endif
93
};
94
 
95
static int initialized = 0;
96
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
97
 
98
static void on_load(void) __attribute__((constructor));
99
static void on_unload(void) __attribute__((destructor));
100
 
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
133
 
134
/* Initialization method to Captures the relevant environment variables.
135
 */
136
 
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);
142
}
143
 
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);
149
}
150
 
151
/* These are the methods we are try to hijack.
152
 */
153
 
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);
158
}
159
#endif
160
 
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);
169
}
170
#endif
171
 
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);
176
}
177
#endif
178
 
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);
183
}
184
#endif
185
 
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);
190
}
191
#endif
192
 
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);
197
}
198
#endif
199
 
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);
209
 
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);
213
 
214
  bear_strings_release(argv);
215
  return result;
216
}
217
#endif
218
 
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);
228
 
229
  bear_report_call(__func__, (char const *const *)argv);
230
  int const result = call_execvp(file, (char *const *)argv);
231
 
232
  bear_strings_release(argv);
233
  return result;
234
}
235
#endif
236
 
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);
248
 
249
  bear_report_call(__func__, (char const *const *)argv);
250
  int const result =
251
      call_execve(path, (char *const *)argv, (char *const *)envp);
252
 
253
  bear_strings_release(argv);
254
  return result;
255
}
256
#endif
257
 
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);
265
}
266
#endif
267
 
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);
275
}
276
#endif
277
 
278
/* These are the methods which forward the call to the standard implementation.
279
 */
280
 
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 *);
285
 
286
  DLSYM(func, fp, "execve");
287
 
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;
292
}
293
#endif
294
 
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 *);
299
 
300
  DLSYM(func, fp, "execvpe");
301
 
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;
306
}
307
#endif
308
 
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[]);
312
 
313
  DLSYM(func, fp, "execvp");
314
 
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);
321
 
322
  return result;
323
}
324
#endif
325
 
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 *);
330
 
331
  DLSYM(func, fp, "execvP");
332
 
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);
339
 
340
  return result;
341
}
342
#endif
343
 
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 *);
348
 
349
  DLSYM(func, fp, "exect");
350
 
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;
355
}
356
#endif
357
 
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);
368
 
369
  DLSYM(func, fp, "posix_spawn");
370
 
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;
376
}
377
#endif
378
 
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);
389
 
390
  DLSYM(func, fp, "posix_spawnp");
391
 
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;
397
}
398
#endif
399
 
400
/* this method is to write log about the process creation. */
401
 
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;
406
 
407
  if (!initialized)
408
    return;
409
 
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);
416
  }
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);
425
  }
426
  FILE *fd = fopen(filename, "a+");
427
  if (0 == fd) {
428
    perror("bear: fopen");
429
    pthread_mutex_unlock(&mutex);
430
    exit(EXIT_FAILURE);
431
  }
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);
439
  }
440
  fprintf(fd, "%c", GS);
441
  if (fclose(fd)) {
442
    perror("bear: fclose");
443
    pthread_mutex_unlock(&mutex);
444
    exit(EXIT_FAILURE);
445
  }
446
  free((void *)cwd);
447
  pthread_mutex_unlock(&mutex);
448
}
449
 
450
/* update environment assure that children processes will copy the desired
451
 * behaviour */
452
 
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;
460
  }
461
  return status;
462
}
463
 
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]);
471
    }
472
  }
473
  return status;
474
}
475
 
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;
480
  }
481
}
482
 
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;
489
}
490
 
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;
500
  }
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);
508
  }
509
  if (-1 == snprintf(env, env_length, "%s=%s", key, value)) {
510
    perror("bear: snprintf");
511
    exit(EXIT_FAILURE);
512
  }
513
  // replace or append the environment entry
514
  if (it && *it) {
515
    free((void *)*it);
516
    *it = env;
517
    return envs;
518
  }
519
  return bear_strings_append(envs, env);
520
}
521
 
522
static char **bear_get_environment() {
523
#if defined HAVE_NSGETENVIRON
524
  return *_NSGetEnviron();
525
#else
526
  return environ;
527
#endif
528
}
529
 
530
/* util methods to deal with string arrays. environment and process arguments
531
 * are both represented as string arrays. */
532
 
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);
541
    }
542
    char const *copy = strdup(it);
543
    if (0 == copy) {
544
      perror("bear: strdup");
545
      exit(EXIT_FAILURE);
546
    }
547
    result[size++] = copy;
548
  }
549
  result = realloc(result, (size + 1) * sizeof(char const *));
550
  if (0 == result) {
551
    perror("bear: realloc");
552
    exit(EXIT_FAILURE);
553
  }
554
  result[size++] = 0;
555
 
556
  return result;
557
}
558
 
559
static char const **bear_strings_copy(char const **const in) {
560
  size_t const size = bear_strings_length(in);
561
 
562
  char const **const result = malloc((size + 1) * sizeof(char const *));
563
  if (0 == result) {
564
    perror("bear: malloc");
565
    exit(EXIT_FAILURE);
566
  }
567
 
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);
574
    }
575
  }
576
  *out_it = 0;
577
  return result;
578
}
579
 
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);
587
  }
588
  result[size++] = e;
589
  result[size++] = 0;
590
  return result;
591
}
592
 
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;
598
}
599
 
600
static void bear_strings_release(char const **in) {
601
  for (char const *const *it = in; (it) && (*it); ++it) {
602
    free((void *)*it);
603
  }
604
  free((void *)in);
605
}