Chandler Carruth 2946cd7010 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

605 lines
18 KiB
C

/* -*- coding: utf-8 -*-
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
/**
* This file implements a shared library. This library can be pre-loaded by
* the dynamic linker of the Operating System (OS). It implements a few function
* related to process creation. By pre-load this library the executed process
* uses these functions instead of those from the standard library.
*
* The idea here is to inject a logic before call the real methods. The logic is
* to dump the call into a file. To call the real method this library is doing
* the job of the dynamic linker.
*
* The only input for the log writing is about the destination directory.
* This is passed as environment variable.
*/
#include "config.h"
#include <stddef.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <pthread.h>
#if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP
#include <spawn.h>
#endif
#if defined HAVE_NSGETENVIRON
# include <crt_externs.h>
#else
extern char **environ;
#endif
#define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR"
#ifdef APPLE
# define ENV_FLAT "DYLD_FORCE_FLAT_NAMESPACE"
# define ENV_PRELOAD "DYLD_INSERT_LIBRARIES"
# define ENV_SIZE 3
#else
# define ENV_PRELOAD "LD_PRELOAD"
# define ENV_SIZE 2
#endif
#define DLSYM(TYPE_, VAR_, SYMBOL_) \
union { \
void *from; \
TYPE_ to; \
} cast; \
if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) { \
perror("bear: dlsym"); \
exit(EXIT_FAILURE); \
} \
TYPE_ const VAR_ = cast.to;
typedef char const * bear_env_t[ENV_SIZE];
static int bear_capture_env_t(bear_env_t *env);
static int bear_reset_env_t(bear_env_t *env);
static void bear_release_env_t(bear_env_t *env);
static char const **bear_update_environment(char *const envp[], bear_env_t *env);
static char const **bear_update_environ(char const **in, char const *key, char const *value);
static char **bear_get_environment();
static void bear_report_call(char const *fun, char const *const argv[]);
static char const **bear_strings_build(char const *arg, va_list *ap);
static char const **bear_strings_copy(char const **const in);
static char const **bear_strings_append(char const **in, char const *e);
static size_t bear_strings_length(char const *const *in);
static void bear_strings_release(char const **);
static bear_env_t env_names =
{ ENV_OUTPUT
, ENV_PRELOAD
#ifdef ENV_FLAT
, ENV_FLAT
#endif
};
static bear_env_t initial_env =
{ 0
, 0
#ifdef ENV_FLAT
, 0
#endif
};
static int initialized = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static void on_load(void) __attribute__((constructor));
static void on_unload(void) __attribute__((destructor));
#ifdef HAVE_EXECVE
static int call_execve(const char *path, char *const argv[],
char *const envp[]);
#endif
#ifdef HAVE_EXECVP
static int call_execvp(const char *file, char *const argv[]);
#endif
#ifdef HAVE_EXECVPE
static int call_execvpe(const char *file, char *const argv[],
char *const envp[]);
#endif
#ifdef HAVE_EXECVP2
static int call_execvP(const char *file, const char *search_path,
char *const argv[]);
#endif
#ifdef HAVE_EXECT
static int call_exect(const char *path, char *const argv[],
char *const envp[]);
#endif
#ifdef HAVE_POSIX_SPAWN
static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *restrict attrp,
char *const argv[restrict],
char *const envp[restrict]);
#endif
#ifdef HAVE_POSIX_SPAWNP
static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *restrict attrp,
char *const argv[restrict],
char *const envp[restrict]);
#endif
/* Initialization method to Captures the relevant environment variables.
*/
static void on_load(void) {
pthread_mutex_lock(&mutex);
if (!initialized)
initialized = bear_capture_env_t(&initial_env);
pthread_mutex_unlock(&mutex);
}
static void on_unload(void) {
pthread_mutex_lock(&mutex);
bear_release_env_t(&initial_env);
initialized = 0;
pthread_mutex_unlock(&mutex);
}
/* These are the methods we are try to hijack.
*/
#ifdef HAVE_EXECVE
int execve(const char *path, char *const argv[], char *const envp[]) {
bear_report_call(__func__, (char const *const *)argv);
return call_execve(path, argv, envp);
}
#endif
#ifdef HAVE_EXECV
#ifndef HAVE_EXECVE
#error can not implement execv without execve
#endif
int execv(const char *path, char *const argv[]) {
bear_report_call(__func__, (char const *const *)argv);
char * const * envp = bear_get_environment();
return call_execve(path, argv, envp);
}
#endif
#ifdef HAVE_EXECVPE
int execvpe(const char *file, char *const argv[], char *const envp[]) {
bear_report_call(__func__, (char const *const *)argv);
return call_execvpe(file, argv, envp);
}
#endif
#ifdef HAVE_EXECVP
int execvp(const char *file, char *const argv[]) {
bear_report_call(__func__, (char const *const *)argv);
return call_execvp(file, argv);
}
#endif
#ifdef HAVE_EXECVP2
int execvP(const char *file, const char *search_path, char *const argv[]) {
bear_report_call(__func__, (char const *const *)argv);
return call_execvP(file, search_path, argv);
}
#endif
#ifdef HAVE_EXECT
int exect(const char *path, char *const argv[], char *const envp[]) {
bear_report_call(__func__, (char const *const *)argv);
return call_exect(path, argv, envp);
}
#endif
#ifdef HAVE_EXECL
# ifndef HAVE_EXECVE
# error can not implement execl without execve
# endif
int execl(const char *path, const char *arg, ...) {
va_list args;
va_start(args, arg);
char const **argv = bear_strings_build(arg, &args);
va_end(args);
bear_report_call(__func__, (char const *const *)argv);
char * const * envp = bear_get_environment();
int const result = call_execve(path, (char *const *)argv, envp);
bear_strings_release(argv);
return result;
}
#endif
#ifdef HAVE_EXECLP
# ifndef HAVE_EXECVP
# error can not implement execlp without execvp
# endif
int execlp(const char *file, const char *arg, ...) {
va_list args;
va_start(args, arg);
char const **argv = bear_strings_build(arg, &args);
va_end(args);
bear_report_call(__func__, (char const *const *)argv);
int const result = call_execvp(file, (char *const *)argv);
bear_strings_release(argv);
return result;
}
#endif
#ifdef HAVE_EXECLE
# ifndef HAVE_EXECVE
# error can not implement execle without execve
# endif
// int execle(const char *path, const char *arg, ..., char * const envp[]);
int execle(const char *path, const char *arg, ...) {
va_list args;
va_start(args, arg);
char const **argv = bear_strings_build(arg, &args);
char const **envp = va_arg(args, char const **);
va_end(args);
bear_report_call(__func__, (char const *const *)argv);
int const result =
call_execve(path, (char *const *)argv, (char *const *)envp);
bear_strings_release(argv);
return result;
}
#endif
#ifdef HAVE_POSIX_SPAWN
int posix_spawn(pid_t *restrict pid, const char *restrict path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *restrict attrp,
char *const argv[restrict], char *const envp[restrict]) {
bear_report_call(__func__, (char const *const *)argv);
return call_posix_spawn(pid, path, file_actions, attrp, argv, envp);
}
#endif
#ifdef HAVE_POSIX_SPAWNP
int posix_spawnp(pid_t *restrict pid, const char *restrict file,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *restrict attrp,
char *const argv[restrict], char *const envp[restrict]) {
bear_report_call(__func__, (char const *const *)argv);
return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp);
}
#endif
/* These are the methods which forward the call to the standard implementation.
*/
#ifdef HAVE_EXECVE
static int call_execve(const char *path, char *const argv[],
char *const envp[]) {
typedef int (*func)(const char *, char *const *, char *const *);
DLSYM(func, fp, "execve");
char const **const menvp = bear_update_environment(envp, &initial_env);
int const result = (*fp)(path, argv, (char *const *)menvp);
bear_strings_release(menvp);
return result;
}
#endif
#ifdef HAVE_EXECVPE
static int call_execvpe(const char *file, char *const argv[],
char *const envp[]) {
typedef int (*func)(const char *, char *const *, char *const *);
DLSYM(func, fp, "execvpe");
char const **const menvp = bear_update_environment(envp, &initial_env);
int const result = (*fp)(file, argv, (char *const *)menvp);
bear_strings_release(menvp);
return result;
}
#endif
#ifdef HAVE_EXECVP
static int call_execvp(const char *file, char *const argv[]) {
typedef int (*func)(const char *file, char *const argv[]);
DLSYM(func, fp, "execvp");
bear_env_t current_env;
bear_capture_env_t(&current_env);
bear_reset_env_t(&initial_env);
int const result = (*fp)(file, argv);
bear_reset_env_t(&current_env);
bear_release_env_t(&current_env);
return result;
}
#endif
#ifdef HAVE_EXECVP2
static int call_execvP(const char *file, const char *search_path,
char *const argv[]) {
typedef int (*func)(const char *, const char *, char *const *);
DLSYM(func, fp, "execvP");
bear_env_t current_env;
bear_capture_env_t(&current_env);
bear_reset_env_t(&initial_env);
int const result = (*fp)(file, search_path, argv);
bear_reset_env_t(&current_env);
bear_release_env_t(&current_env);
return result;
}
#endif
#ifdef HAVE_EXECT
static int call_exect(const char *path, char *const argv[],
char *const envp[]) {
typedef int (*func)(const char *, char *const *, char *const *);
DLSYM(func, fp, "exect");
char const **const menvp = bear_update_environment(envp, &initial_env);
int const result = (*fp)(path, argv, (char *const *)menvp);
bear_strings_release(menvp);
return result;
}
#endif
#ifdef HAVE_POSIX_SPAWN
static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *restrict attrp,
char *const argv[restrict],
char *const envp[restrict]) {
typedef int (*func)(pid_t *restrict, const char *restrict,
const posix_spawn_file_actions_t *,
const posix_spawnattr_t *restrict,
char *const *restrict, char *const *restrict);
DLSYM(func, fp, "posix_spawn");
char const **const menvp = bear_update_environment(envp, &initial_env);
int const result =
(*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp);
bear_strings_release(menvp);
return result;
}
#endif
#ifdef HAVE_POSIX_SPAWNP
static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *restrict attrp,
char *const argv[restrict],
char *const envp[restrict]) {
typedef int (*func)(pid_t *restrict, const char *restrict,
const posix_spawn_file_actions_t *,
const posix_spawnattr_t *restrict,
char *const *restrict, char *const *restrict);
DLSYM(func, fp, "posix_spawnp");
char const **const menvp = bear_update_environment(envp, &initial_env);
int const result =
(*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp);
bear_strings_release(menvp);
return result;
}
#endif
/* this method is to write log about the process creation. */
static void bear_report_call(char const *fun, char const *const argv[]) {
static int const GS = 0x1d;
static int const RS = 0x1e;
static int const US = 0x1f;
if (!initialized)
return;
pthread_mutex_lock(&mutex);
const char *cwd = getcwd(NULL, 0);
if (0 == cwd) {
perror("bear: getcwd");
exit(EXIT_FAILURE);
}
char const * const out_dir = initial_env[0];
size_t const path_max_length = strlen(out_dir) + 32;
char filename[path_max_length];
if (-1 == snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) {
perror("bear: snprintf");
exit(EXIT_FAILURE);
}
FILE * fd = fopen(filename, "a+");
if (0 == fd) {
perror("bear: fopen");
exit(EXIT_FAILURE);
}
fprintf(fd, "%d%c", getpid(), RS);
fprintf(fd, "%d%c", getppid(), RS);
fprintf(fd, "%s%c", fun, RS);
fprintf(fd, "%s%c", cwd, RS);
size_t const argc = bear_strings_length(argv);
for (size_t it = 0; it < argc; ++it) {
fprintf(fd, "%s%c", argv[it], US);
}
fprintf(fd, "%c", GS);
if (fclose(fd)) {
perror("bear: fclose");
exit(EXIT_FAILURE);
}
free((void *)cwd);
pthread_mutex_unlock(&mutex);
}
/* update environment assure that chilren processes will copy the desired
* behaviour */
static int bear_capture_env_t(bear_env_t *env) {
int status = 1;
for (size_t it = 0; it < ENV_SIZE; ++it) {
char const * const env_value = getenv(env_names[it]);
char const * const env_copy = (env_value) ? strdup(env_value) : env_value;
(*env)[it] = env_copy;
status &= (env_copy) ? 1 : 0;
}
return status;
}
static int bear_reset_env_t(bear_env_t *env) {
int status = 1;
for (size_t it = 0; it < ENV_SIZE; ++it) {
if ((*env)[it]) {
setenv(env_names[it], (*env)[it], 1);
} else {
unsetenv(env_names[it]);
}
}
return status;
}
static void bear_release_env_t(bear_env_t *env) {
for (size_t it = 0; it < ENV_SIZE; ++it) {
free((void *)(*env)[it]);
(*env)[it] = 0;
}
}
static char const **bear_update_environment(char *const envp[], bear_env_t *env) {
char const **result = bear_strings_copy((char const **)envp);
for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it)
result = bear_update_environ(result, env_names[it], (*env)[it]);
return result;
}
static char const **bear_update_environ(char const *envs[], char const *key, char const * const value) {
// find the key if it's there
size_t const key_length = strlen(key);
char const **it = envs;
for (; (it) && (*it); ++it) {
if ((0 == strncmp(*it, key, key_length)) &&
(strlen(*it) > key_length) && ('=' == (*it)[key_length]))
break;
}
// allocate a environment entry
size_t const value_length = strlen(value);
size_t const env_length = key_length + value_length + 2;
char *env = malloc(env_length);
if (0 == env) {
perror("bear: malloc [in env_update]");
exit(EXIT_FAILURE);
}
if (-1 == snprintf(env, env_length, "%s=%s", key, value)) {
perror("bear: snprintf");
exit(EXIT_FAILURE);
}
// replace or append the environment entry
if (it && *it) {
free((void *)*it);
*it = env;
return envs;
}
return bear_strings_append(envs, env);
}
static char **bear_get_environment() {
#if defined HAVE_NSGETENVIRON
return *_NSGetEnviron();
#else
return environ;
#endif
}
/* util methods to deal with string arrays. environment and process arguments
* are both represented as string arrays. */
static char const **bear_strings_build(char const *const arg, va_list *args) {
char const **result = 0;
size_t size = 0;
for (char const *it = arg; it; it = va_arg(*args, char const *)) {
result = realloc(result, (size + 1) * sizeof(char const *));
if (0 == result) {
perror("bear: realloc");
exit(EXIT_FAILURE);
}
char const *copy = strdup(it);
if (0 == copy) {
perror("bear: strdup");
exit(EXIT_FAILURE);
}
result[size++] = copy;
}
result = realloc(result, (size + 1) * sizeof(char const *));
if (0 == result) {
perror("bear: realloc");
exit(EXIT_FAILURE);
}
result[size++] = 0;
return result;
}
static char const **bear_strings_copy(char const **const in) {
size_t const size = bear_strings_length(in);
char const **const result = malloc((size + 1) * sizeof(char const *));
if (0 == result) {
perror("bear: malloc");
exit(EXIT_FAILURE);
}
char const **out_it = result;
for (char const *const *in_it = in; (in_it) && (*in_it);
++in_it, ++out_it) {
*out_it = strdup(*in_it);
if (0 == *out_it) {
perror("bear: strdup");
exit(EXIT_FAILURE);
}
}
*out_it = 0;
return result;
}
static char const **bear_strings_append(char const **const in,
char const *const e) {
size_t size = bear_strings_length(in);
char const **result = realloc(in, (size + 2) * sizeof(char const *));
if (0 == result) {
perror("bear: realloc");
exit(EXIT_FAILURE);
}
result[size++] = e;
result[size++] = 0;
return result;
}
static size_t bear_strings_length(char const *const *const in) {
size_t result = 0;
for (char const *const *it = in; (it) && (*it); ++it)
++result;
return result;
}
static void bear_strings_release(char const **in) {
for (char const *const *it = in; (it) && (*it); ++it) {
free((void *)*it);
}
free((void *)in);
}