#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/Scalar.h"
#include "Support/FileUtilities.h"
+#include "Support/SystemUtils.h"
#include "Support/CommandLine.h"
#include "Support/Signals.h"
+#include "Config/unistd.h"
#include <fstream>
#include <memory>
#include <set>
#include <algorithm>
-#include <sys/types.h> // For FileExists
-#include <sys/stat.h>
namespace {
cl::list<std::string>
LinkAsLibrary("link-as-library", cl::desc("Link the .bc files together as a"
" library, not an executable"));
+ cl::opt<bool>
+ Native("native", cl::desc("Generate a native binary instead of a shell script"));
+
// Compatibility options that are ignored, but support by LD
cl::opt<std::string>
CO3("soname", cl::Hidden, cl::desc("Compatibility option: ignored"));
// FileExists - Return true if the specified string is an openable file...
static inline bool FileExists(const std::string &FN) {
- struct stat StatBuf;
- return stat(FN.c_str(), &StatBuf) != -1;
+ return access(FN.c_str(), F_OK) != -1;
}
return 1;
}
+//
+//
+// Function: copy_env()
+//
+// Description:
+// This function takes an array of environment variables and makes a
+// copy of it. This copy can then be manipulated any way the caller likes
+// without affecting the process's real environment.
+//
+// Inputs:
+// envp - An array of C strings containing an environment.
+//
+// Outputs:
+// None.
+//
+// Return value:
+// NULL - An error occurred.
+// Otherwise, a pointer to a new array of C strings is returned. Every string
+// in the array is a duplicate of the one in the original array (i.e. we do
+// not copy the char *'s from one array to another).
+//
+static char **
+copy_env (char ** envp)
+{
+ // The new environment list
+ char ** newenv;
+
+ // The number of entries in the old environment list
+ int entries;
+
+ //
+ // Count the number of entries in the old list;
+ //
+ for (entries = 0; envp[entries] != NULL; entries++)
+ {
+ ;
+ }
+
+ //
+ // Add one more entry for the NULL pointer that ends the list.
+ //
+ ++entries;
+
+ //
+ // If there are no entries at all, just return NULL.
+ //
+ if (entries == 0)
+ {
+ return NULL;
+ }
+
+ //
+ // Allocate a new environment list.
+ //
+ if ((newenv = new (char *) [entries]) == NULL)
+ {
+ return NULL;
+ }
+
+ //
+ // Make a copy of the list. Don't forget the NULL that ends the list.
+ //
+ entries = 0;
+ while (envp[entries] != NULL)
+ {
+ newenv[entries] = strdup (envp[entries]);
+ ++entries;
+ }
+ newenv[entries] = NULL;
+
+ return newenv;
+}
+
+
+//
+// Function: remove_env()
+//
+// Description:
+// Remove the specified environment variable from the environment array.
+//
+// Inputs:
+// name - The name of the variable to remove. It cannot be NULL.
+// envp - The array of environment variables. It cannot be NULL.
+//
+// Outputs:
+// envp - The pointer to the specified variable name is removed.
+//
+// Return value:
+// None.
+//
+// Notes:
+// This is mainly done because functions to remove items from the environment
+// are not available across all platforms. In particular, Solaris does not
+// seem to have an unsetenv() function or a setenv() function (or they are
+// undocumented if they do exist).
+//
+static void
+remove_env (const char * name, char ** envp)
+{
+ // Pointer for scanning arrays
+ register char * p;
+
+ // Index for selecting elements of the environment array
+ register int index;
-int main(int argc, char **argv) {
+ for (index=0; envp[index] != NULL; index++)
+ {
+ //
+ // Find the first equals sign in the array and make it an EOS character.
+ //
+ p = strchr (envp[index], '=');
+ if (p == NULL)
+ {
+ continue;
+ }
+ else
+ {
+ *p = '\0';
+ }
+
+ //
+ // Compare the two strings. If they are equal, zap this string.
+ // Otherwise, restore it.
+ //
+ if (!strcmp (name, envp[index]))
+ {
+ *envp[index] = '\0';
+ }
+ else
+ {
+ *p = '=';
+ }
+ }
+
+ return;
+}
+
+
+int main(int argc, char **argv, char ** envp) {
cl::ParseCommandLineOptions(argc, argv, " llvm linker for GCC\n");
std::string ErrorMessage;
Out.close();
if (!LinkAsLibrary) {
- // Output the script to start the program...
- std::ofstream Out2(OutputFilename.c_str());
- if (!Out2.good())
- return PrintAndReturn(argv[0], "error opening '" + OutputFilename +
- "' for writing!");
- Out2 << "#!/bin/sh\nlli -q -abort-on-exception $0.bc $*\n";
- Out2.close();
+ //
+ // If the user wants to generate a native executable, compile it from the
+ // bytecode file.
+ //
+ // Otherwise, create a script that will run the bytecode through the JIT.
+ //
+ if (Native)
+ {
+ //
+ // Remove these environment variables from the environment of the
+ // programs that we will execute. It appears that GCC sets these
+ // environment variables so that the programs it uses can configure
+ // themselves identically.
+ //
+ // However, when we invoke GCC below, we want it to use its normal
+ // configuration. Hence, we must sanitize it's environment.
+ //
+ char ** clean_env = copy_env (envp);
+ if (clean_env == NULL)
+ {
+ return PrintAndReturn (argv[0], "Failed to duplicate environment");
+ }
+ remove_env ("LIBRARY_PATH", clean_env);
+ remove_env ("COLLECT_GCC_OPTIONS", clean_env);
+ remove_env ("GCC_EXEC_PREFIX", clean_env);
+ remove_env ("COMPILER_PATH", clean_env);
+ remove_env ("COLLECT_GCC", clean_env);
+
+ //
+ // Run LLC to convert the bytecode file into assembly code.
+ //
+ char * cmd[8];
+ std::string AssemblyFile = OutputFilename + ".s";
+
+ cmd[0] = (char *) "llc";
+ cmd[1] = (char *) "-f";
+ cmd[2] = (char *) "-o";
+ cmd[3] = (char *) AssemblyFile.c_str();
+ cmd[4] = (char *) RealBytecodeOutput.c_str();
+ cmd[5] = (char *) NULL;
+ if ((ExecWait (cmd, clean_env)) == -1)
+ {
+ return PrintAndReturn (argv[0], "Failed to compile bytecode");
+ }
+
+ //
+ // Run GCC to assemble and link the program into native code.
+ //
+ // Note:
+ // We can't just assemble and link the file with the system assembler
+ // and linker because we don't know where to put the _start symbol.
+ // GCC mysteriously knows how to do it.
+ //
+ cmd[0] = (char *) "gcc";
+ cmd[1] = (char *) "-o";
+ cmd[2] = (char *) OutputFilename.c_str();
+ cmd[3] = (char *) AssemblyFile.c_str();
+ cmd[4] = (char *) NULL;
+ if ((ExecWait (cmd, clean_env)) == -1)
+ {
+ return PrintAndReturn (argv[0], "Failed to link native code file");
+ }
+
+ //
+ // The assembly file is no longer needed. Remove it, but do not exit
+ // if we fail to unlink it.
+ //
+ if (((access (AssemblyFile.c_str(), F_OK)) != -1) &&
+ ((unlink (AssemblyFile.c_str())) == -1))
+ {
+ std::cerr << "Warning: Failed to unlink " << AssemblyFile << "\n";
+ }
+ }
+ else
+ {
+ // Output the script to start the program...
+ std::ofstream Out2(OutputFilename.c_str());
+ if (!Out2.good())
+ return PrintAndReturn(argv[0], "error opening '" + OutputFilename +
+ "' for writing!");
+ Out2 << "#!/bin/sh\nlli -q $0.bc $*\n";
+ Out2.close();
+ }
// Make the script executable...
MakeFileExecutable (OutputFilename);
- // Make the bytecode file directly executable in LLEE as well
+ // Make the bytecode file readable and directly executable in LLEE as well
MakeFileExecutable (RealBytecodeOutput);
+ MakeFileReadable (RealBytecodeOutput);
}
return 0;