Removed the use of the environ variable and instead use the environment array
[oota-llvm.git] / tools / gccld / gccld.cpp
index 1fd1b741fac6347e4afc9b54022f23118591d43c..1712f0b36d1a979ed95d01a6485ef090eaa5743e 100644 (file)
 #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> 
@@ -65,6 +65,9 @@ namespace {
   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"));
@@ -78,8 +81,7 @@ namespace {
 
 // 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;
 }
 
 
@@ -315,8 +317,145 @@ static int PrintAndReturn(const char *progname, const std::string &Message,
   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;
@@ -434,19 +573,96 @@ int main(int argc, char **argv) {
   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;