Skip to content

Commit fddb417

Browse files
committed
[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
1 parent 7e5a187 commit fddb417

File tree

5 files changed

+82
-22
lines changed

5 files changed

+82
-22
lines changed

llvm/include/llvm/InterfaceStub/ELFObjHandler.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "llvm/InterfaceStub/ELFStub.h"
1717
#include "llvm/Object/ELFObjectFile.h"
1818
#include "llvm/Object/ELFTypes.h"
19+
#include "llvm/Support/FileSystem.h"
1920

2021
namespace llvm {
2122

@@ -35,8 +36,10 @@ Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf);
3536
/// @param FilePath File path for writing the ELF binary.
3637
/// @param Stub Source ELFStub to generate a binary ELF stub from.
3738
/// @param OutputFormat Target ELFType to write binary as.
39+
/// @param WriteIfChanged Whether or not to preserve timestamp if
40+
/// the output stays the same.
3841
Error writeBinaryStub(StringRef FilePath, const ELFStub &Stub,
39-
ELFTarget OutputFormat);
42+
ELFTarget OutputFormat, bool WriteIfChanged = false);
4043

4144
} // end namespace elfabi
4245
} // end namespace llvm

llvm/lib/InterfaceStub/ELFObjHandler.cpp

+27-12
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "llvm/Support/FileOutputBuffer.h"
1818
#include "llvm/Support/MathExtras.h"
1919
#include "llvm/Support/MemoryBuffer.h"
20+
#include "llvm/Support/Process.h"
2021

2122
using llvm::MemoryBufferRef;
2223
using llvm::object::ELFObjectFile;
@@ -663,8 +664,25 @@ buildStub(const ELFObjectFile<ELFT> &ElfObj) {
663664
/// @param FilePath File path for writing the ELF binary.
664665
/// @param Stub Source ELFStub to generate a binary ELF stub from.
665666
template <class ELFT>
666-
static Error writeELFBinaryToFile(StringRef FilePath, const ELFStub &Stub) {
667+
static Error writeELFBinaryToFile(StringRef FilePath, const ELFStub &Stub,
668+
bool WriteIfChanged) {
667669
ELFStubBuilder<ELFT> Builder{Stub};
670+
// Write Stub to memory first.
671+
std::vector<uint8_t> Buf(Builder.getSize());
672+
Builder.write(Buf.data());
673+
674+
if (WriteIfChanged) {
675+
if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
676+
MemoryBuffer::getFile(FilePath)) {
677+
// Compare Stub output with existing Stub file.
678+
// If Stub file unchanged, abort updating.
679+
if ((*BufOrError)->getBufferSize() == Builder.getSize() &&
680+
!memcmp((*BufOrError)->getBufferStart(), Buf.data(),
681+
Builder.getSize()))
682+
return Error::success();
683+
}
684+
}
685+
668686
Expected<std::unique_ptr<FileOutputBuffer>> BufOrError =
669687
FileOutputBuffer::create(FilePath, Builder.getSize());
670688
if (!BufOrError)
@@ -674,13 +692,10 @@ static Error writeELFBinaryToFile(StringRef FilePath, const ELFStub &Stub) {
674692
"` for writing");
675693

676694
// Write binary to file.
677-
std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufOrError);
678-
Builder.write(Buf->getBufferStart());
695+
std::unique_ptr<FileOutputBuffer> FileBuf = std::move(*BufOrError);
696+
memcpy(FileBuf->getBufferStart(), Buf.data(), Buf.size());
679697

680-
if (Error E = Buf->commit())
681-
return E;
682-
683-
return Error::success();
698+
return FileBuf->commit();
684699
}
685700

686701
Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf) {
@@ -705,15 +720,15 @@ Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf) {
705720
// This function wraps the ELFT writeELFBinaryToFile() so writeBinaryStub()
706721
// can be called without having to use ELFType templates directly.
707722
Error writeBinaryStub(StringRef FilePath, const ELFStub &Stub,
708-
ELFTarget OutputFormat) {
723+
ELFTarget OutputFormat, bool WriteIfChanged) {
709724
if (OutputFormat == ELFTarget::ELF32LE)
710-
return writeELFBinaryToFile<ELF32LE>(FilePath, Stub);
725+
return writeELFBinaryToFile<ELF32LE>(FilePath, Stub, WriteIfChanged);
711726
if (OutputFormat == ELFTarget::ELF32BE)
712-
return writeELFBinaryToFile<ELF32BE>(FilePath, Stub);
727+
return writeELFBinaryToFile<ELF32BE>(FilePath, Stub, WriteIfChanged);
713728
if (OutputFormat == ELFTarget::ELF64LE)
714-
return writeELFBinaryToFile<ELF64LE>(FilePath, Stub);
729+
return writeELFBinaryToFile<ELF64LE>(FilePath, Stub, WriteIfChanged);
715730
if (OutputFormat == ELFTarget::ELF64BE)
716-
return writeELFBinaryToFile<ELF64BE>(FilePath, Stub);
731+
return writeELFBinaryToFile<ELF64BE>(FilePath, Stub, WriteIfChanged);
717732
llvm_unreachable("invalid binary output target");
718733
}
719734

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## Test writing unchanged content to ELF Stub file with --write-if-changed flag.
2+
3+
# RUN: llvm-elfabi %s --output-target=elf64-little %t
4+
# RUN: touch -m -d "1970-01-01 00:00:00" %t
5+
# RUN: llvm-elfabi %s --output-target=elf64-little %t --write-if-changed
6+
# RUN: ls -l %t | FileCheck %s
7+
8+
--- !tapi-tbe
9+
TbeVersion: 1.0
10+
Arch: x86_64
11+
NeededLibs:
12+
- libc.so.6
13+
Symbols:
14+
bar: { Type: Object, Size: 42 }
15+
baz: { Type: TLS, Size: 3 }
16+
plus: { Type: Func }
17+
...
18+
19+
# CHECK: {{[[:space:]]1970}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
## Test writing unchanged content to TBE file with --write-if-changed flag.
2+
3+
# RUN: llvm-elfabi --elf %p/Inputs/gnu_hash.so --emit-tbe=%t
4+
# RUN: touch -m -d "1970-01-01 00:00:00" %t
5+
# RUN: llvm-elfabi --elf %p/Inputs/gnu_hash.so --emit-tbe=%t --write-if-changed
6+
# RUN: ls -l %t | FileCheck %s
7+
8+
# CHECK: {{[[:space:]]1970}}

llvm/tools/llvm-elfabi/llvm-elfabi.cpp

+24-9
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,37 @@ cl::opt<ELFTarget> BinaryOutputTarget(
5757
clEnumValN(ELFTarget::ELF64BE, "elf64-big",
5858
"64-bit big-endian ELF stub")));
5959
cl::opt<std::string> BinaryOutputFilePath(cl::Positional, cl::desc("output"));
60+
cl::opt<bool> WriteIfChanged(
61+
"write-if-changed",
62+
cl::desc("Write the output file only if it is new or has changed."));
6063

6164
/// writeTBE() writes a Text-Based ELF stub to a file using the latest version
6265
/// of the YAML parser.
6366
static Error writeTBE(StringRef FilePath, ELFStub &Stub) {
67+
// Write TBE to memory first.
68+
std::string TBEStr;
69+
raw_string_ostream OutStr(TBEStr);
70+
Error YAMLErr = writeTBEToOutputStream(OutStr, Stub);
71+
if (YAMLErr)
72+
return YAMLErr;
73+
OutStr.flush();
74+
75+
if (WriteIfChanged) {
76+
if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
77+
MemoryBuffer::getFile(FilePath)) {
78+
// Compare TBE output with existing TBE file.
79+
// If TBE file unchanged, abort updating.
80+
if ((*BufOrError)->getBuffer() == TBEStr)
81+
return Error::success();
82+
}
83+
}
84+
// Open TBE file for writing.
6485
std::error_code SysErr;
65-
66-
// Open file for writing.
6786
raw_fd_ostream Out(FilePath, SysErr);
6887
if (SysErr)
6988
return createStringError(SysErr, "Couldn't open `%s` for writing",
7089
FilePath.data());
71-
// Write file.
72-
Error YAMLErr = writeTBEToOutputStream(Out, Stub);
73-
if (YAMLErr)
74-
return YAMLErr;
75-
90+
Out << TBEStr;
7691
return Error::success();
7792
}
7893

@@ -153,8 +168,8 @@ int main(int argc, char *argv[]) {
153168
if (BinaryOutputTarget.getNumOccurrences() == 0)
154169
fatalError(createStringError(errc::not_supported,
155170
"no binary output target specified."));
156-
Error BinaryWriteError =
157-
writeBinaryStub(BinaryOutputFilePath, *TargetStub, BinaryOutputTarget);
171+
Error BinaryWriteError = writeBinaryStub(
172+
BinaryOutputFilePath, *TargetStub, BinaryOutputTarget, WriteIfChanged);
158173
if (BinaryWriteError)
159174
fatalError(std::move(BinaryWriteError));
160175
}

0 commit comments

Comments
 (0)