Index: test/tools/llvm-objcopy/strip-preserve-time.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/strip-preserve-time.test @@ -0,0 +1,70 @@ +# Note: ls -lu prints the accessed timestamp, ls -l prints the modified timestamp + +# Preserve dates when stripping to an output file. +# RUN: yaml2obj %s > %t.1.o +# RUN: touch -a -t 199505050555.55 %t.1.o +# RUN: touch -m -t 199705050555.55 %t.1.o +# RUN: llvm-strip -p %t.1.o -o %t-preserved.1.o +# RUN: ls -lu %t-preserved.1.o | FileCheck %s --check-prefix=CHECK-PRESERVE-ATIME +# RUN: ls -l %t-preserved.1.o | FileCheck %s --check-prefix=CHECK-PRESERVE-MTIME + +# Preserve dates available via objcopy interface as well. +# RUN: yaml2obj %s > %t.2.o +# RUN: touch -a -t 199505050555.55 %t.2.o +# RUN: touch -m -t 199705050555.55 %t.2.o +# RUN: llvm-objcopy -p %t.2.o %t-preserved.2.o +# RUN: ls -lu %t-preserved.2.o | FileCheck %s --check-prefix=CHECK-PRESERVE-ATIME +# RUN: ls -l %t-preserved.2.o | FileCheck %s --check-prefix=CHECK-PRESERVE-MTIME + +# Preserve dates when stripping in place. +# RUN: yaml2obj %s > %t.3.o +# RUN: touch -a -t 199505050555.55 %t.3.o +# RUN: touch -m -t 199705050555.55 %t.3.o +# RUN: llvm-strip -p %t.3.o +# RUN: ls -lu %t.3.o | FileCheck %s --check-prefix=CHECK-PRESERVE-ATIME +# RUN: ls -l %t.3.o | FileCheck %s --check-prefix=CHECK-PRESERVE-MTIME + +# Without -p set, don't preserve dates. +# RUN: yaml2obj %s > %t.4.o +# RUN: touch -a -t 199505050555.55 %t.4.o +# RUN: touch -m -t 199705050555.55 %t.4.o +# RUN: llvm-strip %t.4.o +# RUN: ls -lu %t.4.o | FileCheck %s --check-prefix=CHECK-NO-PRESERVE-ATIME +# RUN: ls -l %t.4.o | FileCheck %s --check-prefix=CHECK-NO-PRESERVE-MTIME + +# Preserve dates in archives. +# RUN: yaml2obj %s > %t.5.o +# RUN: rm -f %t.a +# RUN: llvm-ar crs %t.a %t.5.o +# RUN: touch -a -t 199505050555.55 %t.a +# RUN: touch -m -t 199705050555.55 %t.a +# RUN: llvm-strip -p %t.a +# RUN: ls -lu %t.a | FileCheck %s --check-prefix=CHECK-PRESERVE-ATIME +# RUN: ls -l %t.a | FileCheck %s --check-prefix=CHECK-PRESERVE-MTIME + +# Preserve dates in split DWO files. +# RUN: cp %p/Inputs/dwarf.dwo %t-input.dwo +# RUN: touch -a -t 199505050555.55 %t-input.dwo +# RUN: touch -m -t 199705050555.55 %t-input.dwo +# RUN: llvm-objcopy -p -split-dwo=%t-dwo %t-input.dwo %t-nondwo +# RUN: ls -lu %t-dwo | FileCheck %s --check-prefix=CHECK-PRESERVE-ATIME +# RUN: ls -l %t-dwo | FileCheck %s --check-prefix=CHECK-PRESERVE-MTIME +# RUN: ls -lu %t-nondwo | FileCheck %s --check-prefix=CHECK-PRESERVE-ATIME +# RUN: ls -l %t-nondwo | FileCheck %s --check-prefix=CHECK-PRESERVE-MTIME + +# CHECK-PRESERVE-ATIME: 1995 +# CHECK-PRESERVE-MTIME: 1997 + +# CHECK-NO-PRESERVE-ATIME-NOT: 1995 +# CHECK-NO-PRESERVE-MTIME-NOT: 1997 + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] Index: tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- tools/llvm-objcopy/ObjcopyOpts.td +++ tools/llvm-objcopy/ObjcopyOpts.td @@ -22,6 +22,12 @@ defm split_dwo : Eq<"split-dwo">, MetaVarName<"dwo-file">, HelpText<"Equivalent to extract-dwo on the input file to , then strip-dwo on the input file">; + +def preserve_dates : Flag<[ "-", "--" ], "preserve-dates">, + HelpText<"Preserve access and modification timestamps">; + +def p : Flag<[ "-" ], "p">, Alias; + defm add_gnu_debuglink : Eq<"add-gnu-debuglink">, MetaVarName<"debug-file">, HelpText<"Add a .gnu_debuglink for ">; Index: tools/llvm-objcopy/StripOpts.td =================================================================== --- tools/llvm-objcopy/StripOpts.td +++ tools/llvm-objcopy/StripOpts.td @@ -11,6 +11,11 @@ MetaVarName<"output">, HelpText<"Write output to ">; +def preserve_dates : Flag<[ "-", "--" ], "preserve-dates">, + HelpText<"Preserve access and modification timestamps">; + +def p : Flag<[ "-" ], "p">, Alias; + def strip_all : Flag<["-", "--"], "strip-all">, HelpText<"Remove non-allocated sections other than .gnu.warning* sections">; Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- tools/llvm-objcopy/llvm-objcopy.cpp +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -35,6 +35,7 @@ #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include @@ -161,6 +162,7 @@ bool DiscardAll = false; bool OnlyKeepDebug = false; bool KeepFileSymbols = false; + bool PreserveDates = false; }; using SectionPred = std::function; @@ -675,17 +677,44 @@ reportError(Config.OutputFilename, std::move(E)); } +static void restoreDateOnFile(StringRef Filename, + const sys::fs::file_status &Stat) { + int FD; + + if (auto EC = sys::fs::openFileForWrite(Filename, FD)) + reportError(Filename, EC); + + if (auto EC = sys::fs::setLastAccessAndModificationTime( + FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) + reportError(Filename, EC); + + if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) + reportError(Filename, EC); +} + static void executeElfObjcopy(const CopyConfig &Config) { + sys::fs::file_status Stat; + if (Config.PreserveDates) + if (auto EC = sys::fs::status(Config.InputFilename, Stat)) + reportError(Config.InputFilename, EC); + Expected> BinaryOrErr = createBinary(Config.InputFilename); if (!BinaryOrErr) reportError(Config.InputFilename, BinaryOrErr.takeError()); - if (Archive *Ar = dyn_cast(BinaryOrErr.get().getBinary())) - return executeElfObjcopyOnArchive(Config, *Ar); + if (Archive *Ar = dyn_cast(BinaryOrErr.get().getBinary())) { + executeElfObjcopyOnArchive(Config, *Ar); + } else { + FileBuffer FB(Config.OutputFilename); + executeElfObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB); + } - FileBuffer FB(Config.OutputFilename); - executeElfObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB); + if (Config.PreserveDates) { + restoreDateOnFile(Config.OutputFilename, Stat); + if (!Config.SplitDWO.empty()) + restoreDateOnFile(Config.SplitDWO, Stat); + } } // ParseObjcopyOptions returns the config and sets the input arguments. If a @@ -780,6 +809,8 @@ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) Config.SymbolsToKeep.push_back(Arg->getValue()); + Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates); + return Config; } @@ -834,6 +865,8 @@ for (auto Arg : InputArgs.filtered(STRIP_keep_symbol)) Config.SymbolsToKeep.push_back(Arg->getValue()); + Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates); + return Config; }