diff --git a/tools/llee/ExecveHandler.c b/tools/llee/ExecveHandler.c new file mode 100644 index 00000000000..947b2fcdbd0 --- /dev/null +++ b/tools/llee/ExecveHandler.c @@ -0,0 +1,92 @@ +//===-- ExecveHandler.c - Replaces execve() to run LLVM files -------------===// +// +// This file implements a replacement execve() to spawn off LLVM programs to run +// transparently, without needing to be (JIT-)compiled manually by the user. +// +//===----------------------------------------------------------------------===// + +#include "SysUtils.h" +#include +#include +#include +#include +#include + +/* + * This is the expected header for all valid LLVM bytecode files. + * The first four characters must be exactly this. + */ +static const char llvmHeader[] = "llvm"; + +/* + * The type of the execve() function is long and boring, but required. + */ +typedef int(*execveTy)(const char*, char *const[], char *const[]); + +/* + * This method finds the real `execve' call in the C library and executes the + * given program. + */ +int executeProgram(const char *filename, char *const argv[], char *const envp[]) +{ + /* + * Find a pointer to the *real* execve() function starting the search in the + * next library and forward, to avoid finding the one defined in this file. + */ + char *error; + execveTy execvePtr = (execveTy) dlsym(RTLD_NEXT, "execve"); + if ((error = dlerror()) != NULL) { + fprintf(stderr, "%s\n", error); + return -1; + } + + /* Really execute the program */ + return execvePtr(filename, argv, envp); +} + +/* + * This replacement execve() function first checks the file to be executed + * to see if it is a valid LLVM bytecode file, and then either invokes our + * execution environment or passes it on to the system execve() call. + */ +int execve(const char *filename, char *const argv[], char *const envp[]) +{ + /* Open the file, test to see if first four characters are "llvm" */ + char header[4]; + FILE *file = fopen(filename, "r"); + /* Check validity of `file' */ + if (errno) { return errno; } + /* Read the header from the file */ + size_t headerSize = strlen(llvmHeader) - 1; // ignore the NULL terminator + size_t bytesRead = fread(header, sizeof(char), headerSize, file); + fclose(file); + if (bytesRead != headerSize) { + return EIO; + } + if (!strncmp(llvmHeader, header, headerSize)) { + /* + * This is a bytecode file, so execute the JIT with the program and + * parameters. + */ + unsigned argvSize, idx; + for (argvSize = 0, idx = 0; argv[idx] && argv[idx][0]; ++idx) + ++argvSize; + char **LLIargs = (char**) malloc(sizeof(char*) * (argvSize+2)); + char *LLIpath = FindExecutable("lli"); + if (!LLIpath) { + fprintf(stderr, "Cannot find path to `lli', exiting.\n"); + return -1; + } + LLIargs[0] = LLIpath; + for (idx = 0; idx != argvSize; ++idx) + LLIargs[idx+1] = argv[idx]; + LLIargs[argvSize + 1] = '\0'; + /* + for (idx = 0; idx != argvSize+2; ++idx) + printf("LLI args[%d] = \"%s\"\n", idx, LLIargs[idx]); + */ + return executeProgram(LLIpath, LLIargs, envp); + } + executeProgram(filename, argv, envp); + return 0; +} diff --git a/tools/llee/Makefile b/tools/llee/Makefile new file mode 100644 index 00000000000..aabbed635b1 --- /dev/null +++ b/tools/llee/Makefile @@ -0,0 +1,21 @@ +LEVEL = ../.. +include $(LEVEL)/Makefile.config + +SRCS = ExecveHandler.c SysUtils.c + +OBJS = $(SRCS:%.c=%.o) +SO = execve.so + +all: $(SO) execve_test + +%.o: %.c + gcc -g -I../../include -D_GNU_SOURCE $< -c -o $@ + +$(SO): $(OBJS) + gcc -g -shared -ldl -rdynamic $(OBJS) -o $@ + +execve_test: execve_test.c + gcc -g $< -o $@ + +clean: + rm -f $(OBJS) $(SO) diff --git a/tools/llee/SysUtils.c b/tools/llee/SysUtils.c new file mode 100644 index 00000000000..1c3aa85e5c6 --- /dev/null +++ b/tools/llee/SysUtils.c @@ -0,0 +1,81 @@ +//===- SystemUtils.h - Utilities to do low-level system stuff --*- C++ -*--===// +// +// This file contains functions used to do a variety of low-level, often +// system-specific, tasks. +// +//===----------------------------------------------------------------------===// + +#include "SysUtils.h" +#include "Config/sys/types.h" +#include "Config/sys/stat.h" +#include "Config/fcntl.h" +#include "Config/sys/wait.h" +#include "Config/unistd.h" +#include "Config/errno.h" +#include +#include + +/// isExecutableFile - This function returns true if the filename specified +/// exists and is executable. +/// +bool isExecutableFile(const char *ExeFileName) { + struct stat Buf; + if (stat(ExeFileName, &Buf)) + return false; // Must not be executable! + + if (!(Buf.st_mode & S_IFREG)) + return false; // Not a regular file? + + if (Buf.st_uid == getuid()) // Owner of file? + return Buf.st_mode & S_IXUSR; + else if (Buf.st_gid == getgid()) // In group of file? + return Buf.st_mode & S_IXGRP; + else // Unrelated to file? + return Buf.st_mode & S_IXOTH; +} + +/// FindExecutable - Find a named executable in the directories listed in $PATH. +/// If the executable cannot be found, returns NULL. +/// +char *FindExecutable(const char *ExeName) { + /* Try to find the executable in the path */ + const char *PathStr = getenv("PATH"); + if (PathStr == 0) return ""; + + // Now we have a colon separated list of directories to search... try them... + unsigned PathLen = strlen(PathStr); + while (PathLen) { + /* Find the next colon */ + const char *Colon = strchr(PathStr, ':'); + + /* Check to see if this first directory contains the executable... */ + unsigned DirLen = Colon ? (Colon-PathStr) : strlen(PathStr); + char *FilePath = alloca(sizeof(char) * (DirLen+1+strlen(ExeName)+1)); + unsigned i, e; + for (i = 0; i != DirLen; ++i) + FilePath[i] = PathStr[i]; + FilePath[i] = '/'; + for (i = 0, e = strlen(ExeName); i != e; ++i) + FilePath[DirLen + 1 + i] = ExeName[i]; + FilePath[DirLen + 1 + i] = '\0'; + if (isExecutableFile(FilePath)) + return strdup(FilePath); /* Found the executable! */ + + /* If Colon is NULL, there are no more colon separators and no more dirs */ + if (!Colon) break; + + /* Nope, it wasn't in this directory, check the next range! */ + PathLen -= DirLen; + PathStr = Colon; + while (*PathStr == ':') { /* Advance past colons */ + PathStr++; + PathLen--; + } + + /* Advance past the colon */ + ++Colon; + } + + // If we fell out, we ran out of directories in PATH to search, return failure + return NULL; +} diff --git a/tools/llee/SysUtils.h b/tools/llee/SysUtils.h new file mode 100644 index 00000000000..89b647970e3 --- /dev/null +++ b/tools/llee/SysUtils.h @@ -0,0 +1,28 @@ +/*===- sysutils.h - Utilities to do low-level system stuff -------*- C -*--===*\ + * * + * This file contains functions used to do a variety of low-level, often * + * system-specific, tasks. * + * * +\*===----------------------------------------------------------------------===*/ + +#ifndef SYSUTILS_H +#define SYSUTILS_H + +typedef unsigned bool; +enum { false = 0, true = 1 }; + +/* + * isExecutableFile - This function returns true if the filename specified + * exists and is executable. + */ +bool isExecutableFile(const char *ExeFileName); + +/* + * FindExecutable - Find a named executable, giving the argv[0] of program + * being executed. This allows us to find another LLVM tool if it is built into + * the same directory, but that directory is neither the current directory, nor + * in the PATH. If the executable cannot be found, return an empty string. + */ +char *FindExecutable(const char *ExeName); + +#endif diff --git a/tools/llee/llee b/tools/llee/llee new file mode 100755 index 00000000000..7a3ea959ede --- /dev/null +++ b/tools/llee/llee @@ -0,0 +1 @@ +exec env LD_PRELOAD=`pwd`/execve.so $*