[llvm] fddb417 - [llvm-elfabi] Add flag to preserve timestamp when output is the same

Haowei Wu via llvm-commits llvm-commits at lists.llvm.org
Tue Dec 29 15:05:56 PST 2020


Author: Haowei Wu
Date: 2020-12-29T14:43:47-08:00
New Revision: fddb41744958d21635a60622cfb4067122810bcc

URL: https://github.com/llvm/llvm-project/commit/fddb41744958d21635a60622cfb4067122810bcc
DIFF: https://github.com/llvm/llvm-project/commit/fddb41744958d21635a60622cfb4067122810bcc.diff

LOG: [llvm-elfabi] Add flag to preserve timestamp when output is the same

This change adds '--write-if-changed' flag to llvm-elfabi tool. When
enabled, llvm-elfabi will not overwrite the existing file if the
content of the file will not be changed, which preserves the
timestamp.

Differential Revision: https://reviews.llvm.org/D92902

Added: 
    llvm/test/tools/llvm-elfabi/preserve-dates-stub.test
    llvm/test/tools/llvm-elfabi/preserve-dates-tbe.test

Modified: 
    llvm/include/llvm/InterfaceStub/ELFObjHandler.h
    llvm/lib/InterfaceStub/ELFObjHandler.cpp
    llvm/tools/llvm-elfabi/llvm-elfabi.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/InterfaceStub/ELFObjHandler.h b/llvm/include/llvm/InterfaceStub/ELFObjHandler.h
index cbb9420cb666..4ec158c1405f 100644
--- a/llvm/include/llvm/InterfaceStub/ELFObjHandler.h
+++ b/llvm/include/llvm/InterfaceStub/ELFObjHandler.h
@@ -16,6 +16,7 @@
 #include "llvm/InterfaceStub/ELFStub.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/Object/ELFTypes.h"
+#include "llvm/Support/FileSystem.h"
 
 namespace llvm {
 
@@ -35,8 +36,10 @@ Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf);
 /// @param FilePath File path for writing the ELF binary.
 /// @param Stub Source ELFStub to generate a binary ELF stub from.
 /// @param OutputFormat Target ELFType to write binary as.
+/// @param WriteIfChanged Whether or not to preserve timestamp if
+///        the output stays the same.
 Error writeBinaryStub(StringRef FilePath, const ELFStub &Stub,
-                      ELFTarget OutputFormat);
+                      ELFTarget OutputFormat, bool WriteIfChanged = false);
 
 } // end namespace elfabi
 } // end namespace llvm

diff  --git a/llvm/lib/InterfaceStub/ELFObjHandler.cpp b/llvm/lib/InterfaceStub/ELFObjHandler.cpp
index e50ebd7b8ba1..40d8afa88ad2 100644
--- a/llvm/lib/InterfaceStub/ELFObjHandler.cpp
+++ b/llvm/lib/InterfaceStub/ELFObjHandler.cpp
@@ -17,6 +17,7 @@
 #include "llvm/Support/FileOutputBuffer.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Process.h"
 
 using llvm::MemoryBufferRef;
 using llvm::object::ELFObjectFile;
@@ -663,8 +664,25 @@ buildStub(const ELFObjectFile<ELFT> &ElfObj) {
 /// @param FilePath File path for writing the ELF binary.
 /// @param Stub Source ELFStub to generate a binary ELF stub from.
 template <class ELFT>
-static Error writeELFBinaryToFile(StringRef FilePath, const ELFStub &Stub) {
+static Error writeELFBinaryToFile(StringRef FilePath, const ELFStub &Stub,
+                                  bool WriteIfChanged) {
   ELFStubBuilder<ELFT> Builder{Stub};
+  // Write Stub to memory first.
+  std::vector<uint8_t> Buf(Builder.getSize());
+  Builder.write(Buf.data());
+
+  if (WriteIfChanged) {
+    if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
+            MemoryBuffer::getFile(FilePath)) {
+      // Compare Stub output with existing Stub file.
+      // If Stub file unchanged, abort updating.
+      if ((*BufOrError)->getBufferSize() == Builder.getSize() &&
+          !memcmp((*BufOrError)->getBufferStart(), Buf.data(),
+                  Builder.getSize()))
+        return Error::success();
+    }
+  }
+
   Expected<std::unique_ptr<FileOutputBuffer>> BufOrError =
       FileOutputBuffer::create(FilePath, Builder.getSize());
   if (!BufOrError)
@@ -674,13 +692,10 @@ static Error writeELFBinaryToFile(StringRef FilePath, const ELFStub &Stub) {
                                  "` for writing");
 
   // Write binary to file.
-  std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufOrError);
-  Builder.write(Buf->getBufferStart());
+  std::unique_ptr<FileOutputBuffer> FileBuf = std::move(*BufOrError);
+  memcpy(FileBuf->getBufferStart(), Buf.data(), Buf.size());
 
-  if (Error E = Buf->commit())
-    return E;
-
-  return Error::success();
+  return FileBuf->commit();
 }
 
 Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf) {
@@ -705,15 +720,15 @@ Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf) {
 // This function wraps the ELFT writeELFBinaryToFile() so writeBinaryStub()
 // can be called without having to use ELFType templates directly.
 Error writeBinaryStub(StringRef FilePath, const ELFStub &Stub,
-                      ELFTarget OutputFormat) {
+                      ELFTarget OutputFormat, bool WriteIfChanged) {
   if (OutputFormat == ELFTarget::ELF32LE)
-    return writeELFBinaryToFile<ELF32LE>(FilePath, Stub);
+    return writeELFBinaryToFile<ELF32LE>(FilePath, Stub, WriteIfChanged);
   if (OutputFormat == ELFTarget::ELF32BE)
-    return writeELFBinaryToFile<ELF32BE>(FilePath, Stub);
+    return writeELFBinaryToFile<ELF32BE>(FilePath, Stub, WriteIfChanged);
   if (OutputFormat == ELFTarget::ELF64LE)
-    return writeELFBinaryToFile<ELF64LE>(FilePath, Stub);
+    return writeELFBinaryToFile<ELF64LE>(FilePath, Stub, WriteIfChanged);
   if (OutputFormat == ELFTarget::ELF64BE)
-    return writeELFBinaryToFile<ELF64BE>(FilePath, Stub);
+    return writeELFBinaryToFile<ELF64BE>(FilePath, Stub, WriteIfChanged);
   llvm_unreachable("invalid binary output target");
 }
 

diff  --git a/llvm/test/tools/llvm-elfabi/preserve-dates-stub.test b/llvm/test/tools/llvm-elfabi/preserve-dates-stub.test
new file mode 100644
index 000000000000..f612fe57aa4d
--- /dev/null
+++ b/llvm/test/tools/llvm-elfabi/preserve-dates-stub.test
@@ -0,0 +1,19 @@
+## Test writing unchanged content to ELF Stub file with --write-if-changed flag.
+
+# RUN: llvm-elfabi %s --output-target=elf64-little %t
+# RUN: touch -m -d "1970-01-01 00:00:00" %t
+# RUN: llvm-elfabi %s --output-target=elf64-little %t --write-if-changed
+# RUN: ls -l %t | FileCheck %s
+
+--- !tapi-tbe
+TbeVersion: 1.0
+Arch: x86_64
+NeededLibs:
+  - libc.so.6
+Symbols:
+  bar: { Type: Object, Size: 42 }
+  baz: { Type: TLS, Size: 3 }
+  plus: { Type: Func }
+...
+
+# CHECK: {{[[:space:]]1970}}

diff  --git a/llvm/test/tools/llvm-elfabi/preserve-dates-tbe.test b/llvm/test/tools/llvm-elfabi/preserve-dates-tbe.test
new file mode 100644
index 000000000000..ec9cf7e1e3d6
--- /dev/null
+++ b/llvm/test/tools/llvm-elfabi/preserve-dates-tbe.test
@@ -0,0 +1,8 @@
+## Test writing unchanged content to TBE file with --write-if-changed flag.
+
+# RUN: llvm-elfabi --elf %p/Inputs/gnu_hash.so --emit-tbe=%t
+# RUN: touch -m -d "1970-01-01 00:00:00" %t
+# RUN: llvm-elfabi --elf %p/Inputs/gnu_hash.so --emit-tbe=%t --write-if-changed
+# RUN: ls -l %t | FileCheck %s
+
+# CHECK: {{[[:space:]]1970}}

diff  --git a/llvm/tools/llvm-elfabi/llvm-elfabi.cpp b/llvm/tools/llvm-elfabi/llvm-elfabi.cpp
index 6fff54fd9355..761c6a80b345 100644
--- a/llvm/tools/llvm-elfabi/llvm-elfabi.cpp
+++ b/llvm/tools/llvm-elfabi/llvm-elfabi.cpp
@@ -57,22 +57,37 @@ cl::opt<ELFTarget> BinaryOutputTarget(
                clEnumValN(ELFTarget::ELF64BE, "elf64-big",
                           "64-bit big-endian ELF stub")));
 cl::opt<std::string> BinaryOutputFilePath(cl::Positional, cl::desc("output"));
+cl::opt<bool> WriteIfChanged(
+    "write-if-changed",
+    cl::desc("Write the output file only if it is new or has changed."));
 
 /// writeTBE() writes a Text-Based ELF stub to a file using the latest version
 /// of the YAML parser.
 static Error writeTBE(StringRef FilePath, ELFStub &Stub) {
+  // Write TBE to memory first.
+  std::string TBEStr;
+  raw_string_ostream OutStr(TBEStr);
+  Error YAMLErr = writeTBEToOutputStream(OutStr, Stub);
+  if (YAMLErr)
+    return YAMLErr;
+  OutStr.flush();
+
+  if (WriteIfChanged) {
+    if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
+            MemoryBuffer::getFile(FilePath)) {
+      // Compare TBE output with existing TBE file.
+      // If TBE file unchanged, abort updating.
+      if ((*BufOrError)->getBuffer() == TBEStr)
+        return Error::success();
+    }
+  }
+  // Open TBE file for writing.
   std::error_code SysErr;
-
-  // Open file for writing.
   raw_fd_ostream Out(FilePath, SysErr);
   if (SysErr)
     return createStringError(SysErr, "Couldn't open `%s` for writing",
                              FilePath.data());
-  // Write file.
-  Error YAMLErr = writeTBEToOutputStream(Out, Stub);
-  if (YAMLErr)
-    return YAMLErr;
-
+  Out << TBEStr;
   return Error::success();
 }
 
@@ -153,8 +168,8 @@ int main(int argc, char *argv[]) {
     if (BinaryOutputTarget.getNumOccurrences() == 0)
       fatalError(createStringError(errc::not_supported,
                                    "no binary output target specified."));
-    Error BinaryWriteError =
-        writeBinaryStub(BinaryOutputFilePath, *TargetStub, BinaryOutputTarget);
+    Error BinaryWriteError = writeBinaryStub(
+        BinaryOutputFilePath, *TargetStub, BinaryOutputTarget, WriteIfChanged);
     if (BinaryWriteError)
       fatalError(std::move(BinaryWriteError));
   }


        


More information about the llvm-commits mailing list