From a52faa1fcb7b54026ecfbef573d05568846e1aba Mon Sep 17 00:00:00 2001
From: Florian Apolloner <florian@apolloner.eu>
Date: Mon, 6 Mar 2017 21:16:42 +0100
Subject: [PATCH] file: Add READ_ELF command to parse ELF binaries

Leave it undocumented for now because we intend to use it internally and
it cannot be made available everywhere.
---
 Source/cmFileCommand.cxx                | 69 +++++++++++++++++++++++++
 Source/cmFileCommand.h                  |  1 +
 Tests/RunCMake/file/READ_ELF-result.txt |  1 +
 Tests/RunCMake/file/READ_ELF-stderr.txt |  2 +
 Tests/RunCMake/file/READ_ELF.cmake      |  2 +
 Tests/RunCMake/file/RunCMakeTest.cmake  |  1 +
 6 files changed, 76 insertions(+)
 create mode 100644 Tests/RunCMake/file/READ_ELF-result.txt
 create mode 100644 Tests/RunCMake/file/READ_ELF-stderr.txt
 create mode 100644 Tests/RunCMake/file/READ_ELF.cmake

diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 91cecb359f..957b4cb233 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -42,6 +42,10 @@
 #include "cmFileLockResult.h"
 #endif
 
+#if defined(CMAKE_USE_ELF_PARSER)
+#include "cmELF.h"
+#endif
+
 class cmSystemToolsFileTime;
 
 // Table of permissions flags.
@@ -166,6 +170,9 @@ bool cmFileCommand::InitialPass(std::vector<std::string> const& args,
   if (subCommand == "RPATH_REMOVE") {
     return this->HandleRPathRemoveCommand(args);
   }
+  if (subCommand == "READ_ELF") {
+    return this->HandleReadElfCommand(args);
+  }
   if (subCommand == "RELATIVE_PATH") {
     return this->HandleRelativePathCommand(args);
   }
@@ -2177,6 +2184,68 @@ bool cmFileCommand::HandleRPathCheckCommand(
   return true;
 }
 
+bool cmFileCommand::HandleReadElfCommand(std::vector<std::string> const& args)
+{
+  if (args.size() < 4) {
+    this->SetError("READ_ELF must be called with at least three additional "
+                   "arguments.");
+    return false;
+  }
+
+  cmCommandArgumentsHelper argHelper;
+  cmCommandArgumentGroup group;
+
+  cmCAString readArg(&argHelper, "READ_ELF");
+  cmCAString fileNameArg(&argHelper, CM_NULLPTR);
+
+  cmCAString rpathArg(&argHelper, "RPATH", &group);
+  cmCAString runpathArg(&argHelper, "RUNPATH", &group);
+  cmCAString errorArg(&argHelper, "CAPTURE_ERROR", &group);
+
+  readArg.Follows(CM_NULLPTR);
+  fileNameArg.Follows(&readArg);
+  group.Follows(&fileNameArg);
+  argHelper.Parse(&args, CM_NULLPTR);
+
+  if (!cmSystemTools::FileExists(fileNameArg.GetString(), true)) {
+    std::ostringstream e;
+    e << "READ_ELF given FILE \"" << fileNameArg.GetString()
+      << "\" that does not exist.";
+    this->SetError(e.str());
+    return false;
+  }
+
+#if defined(CMAKE_USE_ELF_PARSER)
+  cmELF elf(fileNameArg.GetCString());
+
+  if (!rpathArg.GetString().empty()) {
+    if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
+      std::string rpath(se_rpath->Value);
+      std::replace(rpath.begin(), rpath.end(), ':', ';');
+      this->Makefile->AddDefinition(rpathArg.GetString(), rpath.c_str());
+    }
+  }
+  if (!runpathArg.GetString().empty()) {
+    if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) {
+      std::string runpath(se_runpath->Value);
+      std::replace(runpath.begin(), runpath.end(), ':', ';');
+      this->Makefile->AddDefinition(runpathArg.GetString(), runpath.c_str());
+    }
+  }
+
+  return true;
+#else
+  std::string error = "ELF parser not available on this platform.";
+  if (errorArg.GetString().empty()) {
+    this->SetError(error);
+    return false;
+  } else {
+    this->Makefile->AddDefinition(errorArg.GetString(), error.c_str());
+    return true;
+  }
+#endif
+}
+
 bool cmFileCommand::HandleInstallCommand(std::vector<std::string> const& args)
 {
   cmFileInstaller installer(this);
diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h
index 319864c873..2d82a23b2c 100644
--- a/Source/cmFileCommand.h
+++ b/Source/cmFileCommand.h
@@ -53,6 +53,7 @@ protected:
   bool HandleRelativePathCommand(std::vector<std::string> const& args);
   bool HandleCMakePathCommand(std::vector<std::string> const& args,
                               bool nativePath);
+  bool HandleReadElfCommand(std::vector<std::string> const& args);
   bool HandleRPathChangeCommand(std::vector<std::string> const& args);
   bool HandleRPathCheckCommand(std::vector<std::string> const& args);
   bool HandleRPathRemoveCommand(std::vector<std::string> const& args);
diff --git a/Tests/RunCMake/file/READ_ELF-result.txt b/Tests/RunCMake/file/READ_ELF-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/file/READ_ELF-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file/READ_ELF-stderr.txt b/Tests/RunCMake/file/READ_ELF-stderr.txt
new file mode 100644
index 0000000000..7b328049bd
--- /dev/null
+++ b/Tests/RunCMake/file/READ_ELF-stderr.txt
@@ -0,0 +1,2 @@
+.*file READ_ELF must be called with at least three additional arguments\.
+.*file READ_ELF given FILE "XXX" that does not exist\.
diff --git a/Tests/RunCMake/file/READ_ELF.cmake b/Tests/RunCMake/file/READ_ELF.cmake
new file mode 100644
index 0000000000..cd02c9b623
--- /dev/null
+++ b/Tests/RunCMake/file/READ_ELF.cmake
@@ -0,0 +1,2 @@
+file(READ_ELF XXX)
+file(READ_ELF XXX RPATH YYY)
diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake
index 7497544132..3f3c0da3a9 100644
--- a/Tests/RunCMake/file/RunCMakeTest.cmake
+++ b/Tests/RunCMake/file/RunCMakeTest.cmake
@@ -25,6 +25,7 @@ run_cmake(LOCK-error-no-timeout)
 run_cmake(LOCK-error-timeout)
 run_cmake(LOCK-error-unknown-option)
 run_cmake(LOCK-lowercase)
+run_cmake(READ_ELF)
 run_cmake(GLOB)
 run_cmake(GLOB_RECURSE)
 # test is valid both for GLOB and GLOB_RECURSE
-- 
GitLab