Browse Source

ENH: Added RPATH methods to cmSystemTools

  - RemoveRPath to remove the RPATH from a binary
  - CheckRPath to check for an existing RPATH in a binary
Brad King 17 years ago
parent
commit
b9a5dccc8d
2 changed files with 178 additions and 0 deletions
  1. 170 0
      Source/cmSystemTools.cxx
  2. 8 0
      Source/cmSystemTools.h

+ 170 - 0
Source/cmSystemTools.cxx

@@ -2463,3 +2463,173 @@ bool cmSystemTools::ChangeRPath(std::string const& file,
   return false;
 #endif
 }
+
+//----------------------------------------------------------------------------
+bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg)
+{
+#if defined(CMAKE_USE_ELF_PARSER)
+  unsigned long rpathPosition = 0;
+  unsigned long rpathSize = 0;
+  unsigned long rpathEntryPosition = 0;
+  std::vector<char> bytes;
+  {
+  // Parse the ELF binary.
+  cmELF elf(file.c_str());
+
+  // Get the RPATH or RUNPATH entry from it.
+  cmELF::StringEntry const* se = elf.GetRPath();
+  if(!se)
+    {
+    se = elf.GetRunPath();
+    }
+
+  if(se)
+    {
+    // Store information about the entry.
+    rpathPosition = se->Position;
+    rpathSize = se->Size;
+    rpathEntryPosition = elf.GetDynamicEntryPosition(se->IndexInSection);
+
+    // Get the file range containing the rest of the DYNAMIC table
+    // after the RPATH entry.
+    unsigned long nextEntryPosition =
+      elf.GetDynamicEntryPosition(se->IndexInSection+1);
+    unsigned int count = elf.GetDynamicEntryCount();
+    if(count == 0)
+      {
+      // This should happen only for invalid ELF files where a DT_NULL
+      // appears before the end of the table.
+      if(emsg)
+        {
+        *emsg = "DYNAMIC section contains a DT_NULL before the end.";
+        }
+      return false;
+      }
+    unsigned long nullEntryPosition = elf.GetDynamicEntryPosition(count);
+
+    // Allocate and fill a buffer with zeros.
+    bytes.resize(nullEntryPosition - rpathEntryPosition, 0);
+
+    // Read the part of the DYNAMIC section header that will move.
+    // The remainder of the buffer will be left with zeros which
+    // represent a DT_NULL entry.
+    if(!elf.ReadBytes(nextEntryPosition,
+                      nullEntryPosition - nextEntryPosition,
+                      &bytes[0]))
+      {
+      if(emsg)
+        {
+        *emsg = "Failed to read DYNAMIC section header.";
+        }
+      return false;
+      }
+    }
+  else
+    {
+    // There is no RPATH or RUNPATH anyway.
+    return true;
+    }
+  }
+
+  // Open the file for update.
+  std::ofstream f(file.c_str(),
+                  std::ios::in | std::ios::out | std::ios::binary);
+  if(!f)
+    {
+    if(emsg)
+      {
+      *emsg = "Error opening file for update.";
+      }
+    return false;
+    }
+
+  // Write the new DYNAMIC table header.
+  if(!f.seekp(rpathEntryPosition))
+    {
+    if(emsg)
+      {
+      *emsg = "Error seeking to DYNAMIC table header for RPATH.";
+      }
+    return false;
+    }
+  if(!f.write(&bytes[0], bytes.size()))
+    {
+    if(emsg)
+      {
+      *emsg = "Error replacing DYNAMIC table header.";
+      }
+    return false;
+    }
+
+  // Fill the RPATH string with zero bytes.
+  if(!f.seekp(rpathPosition))
+    {
+    if(emsg)
+      {
+      *emsg = "Error seeking to RPATH position.";
+      }
+    return false;
+    }
+  for(unsigned long i=0; i < rpathSize; ++i)
+    {
+    f << '\0';
+    }
+
+  // Make sure everything was okay.
+  if(f)
+    {
+    return true;
+    }
+  else
+    {
+    if(emsg)
+      {
+      *emsg = "Error writing the empty rpath to the file.";
+      }
+    return false;
+    }
+#else
+  (void)file;
+  (void)emsg;
+  return false;
+#endif
+}
+
+//----------------------------------------------------------------------------
+bool cmSystemTools::CheckRPath(std::string const& file,
+                               std::string const& newRPath)
+{
+#if defined(CMAKE_USE_ELF_PARSER)
+  // Parse the ELF binary.
+  cmELF elf(file.c_str());
+
+  // Get the RPATH or RUNPATH entry from it.
+  cmELF::StringEntry const* se = elf.GetRPath();
+  if(!se)
+    {
+    se = elf.GetRunPath();
+    }
+
+  // Make sure the current rpath contains the new rpath.
+  if(newRPath.empty())
+    {
+    if(!se)
+      {
+      return true;
+      }
+    }
+  else
+    {
+    if(se &&
+       cmSystemToolsFindRPath(se->Value, newRPath) != std::string::npos)
+      {
+      return true;
+      }
+    }
+  return false;
+#else
+  (void)file;
+  (void)newRPath;
+  return false;
+#endif
+}

+ 8 - 0
Source/cmSystemTools.h

@@ -393,6 +393,14 @@ public:
                           std::string const& newRPath,
                           std::string* emsg = 0);
 
+  /** Try to remove the RPATH from an ELF binary.  */
+  static bool RemoveRPath(std::string const& file, std::string* emsg = 0);
+
+  /** Check whether the RPATH in an ELF binary contains the path
+      given.  */
+  static bool CheckRPath(std::string const& file,
+                         std::string const& newRPath);
+
 private:
   static bool s_ForceUnixPaths;
   static bool s_RunCommandHideConsole;