[dsymutil] Check archive members timestamps.
authorFrederic Riss <friss@apple.com>
Wed, 22 Jul 2015 23:24:00 +0000 (23:24 +0000)
committerFrederic Riss <friss@apple.com>
Wed, 22 Jul 2015 23:24:00 +0000 (23:24 +0000)
The debug map contains the timestamp of the object files in references.
We do not check these in the general case, but it's really useful if
you have archives where different versions of an object file have been
appended. This allows llvm-dsymutil to find the right one.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@242965 91177308-0d34-0410-b5e6-96231b3b80d8

test/tools/dsymutil/archive-timestamp.test [new file with mode: 0644]
test/tools/dsymutil/yaml-object-address-rewrite.test
tools/dsymutil/BinaryHolder.cpp
tools/dsymutil/BinaryHolder.h
tools/dsymutil/DebugMap.cpp
tools/dsymutil/DebugMap.h
tools/dsymutil/DwarfLinker.cpp
tools/dsymutil/MachODebugMapParser.cpp

diff --git a/test/tools/dsymutil/archive-timestamp.test b/test/tools/dsymutil/archive-timestamp.test
new file mode 100644 (file)
index 0000000..11add6d
--- /dev/null
@@ -0,0 +1,24 @@
+# RUN: llvm-dsymutil -no-output -v -oso-prepend-path=%p -y %s 2>&1 | FileCheck %s
+
+# This is the archive member part of basic-archive.macho.x86_64 debug map with corrupted timestamps.
+
+# CHECK: warning: {{.*}}libbasic.a(basic2.macho.x86_64.o): {{[Nn]o}} such file
+# CHECK: warning: {{.*}}libbasic.a(basic3.macho.x86_64.o): {{[Nn]o}} such file
+
+---
+triple:          'x86_64-unknown-unknown-macho'
+objects:
+  - filename:        '/Inputs/libbasic.a(basic2.macho.x86_64.o)'
+    timestamp:       141869239
+    symbols:
+      - { sym: _foo, objAddr: 0x0000000000000020, binAddr: 0x0000000100000ED0, size: 0x00000050 }
+      - { sym: _private_int, objAddr: 0x0000000000000560, binAddr: 0x0000000100001004, size: 0x00000000 }
+      - { sym: _inc, objAddr: 0x0000000000000070, binAddr: 0x0000000100000F20, size: 0x00000017 }
+      - { sym: _baz, objAddr: 0x0000000000000310, binAddr: 0x0000000100001000, size: 0x00000000 }
+  - filename:        '/Inputs/libbasic.a(basic3.macho.x86_64.o)'
+    timestamp:       418692393
+    symbols:
+      - { sym: _val, objAddr: 0x0000000000000004, binAddr: 0x0000000100001008, size: 0x00000000 }
+      - { sym: _bar, objAddr: 0x0000000000000020, binAddr: 0x0000000100000F40, size: 0x00000050 }
+      - { sym: _inc, objAddr: 0x0000000000000070, binAddr: 0x0000000100000F90, size: 0x00000019 }
+...
index dcb39be891cd79f053a9d761e2cf9362cedc4363..73697d4a6e5b8043cffb5e76fda5a4018575f915 100644 (file)
@@ -8,9 +8,11 @@
 # CHECK-NEXT: triple:{{.*}}'x86_64-unknown-unknown-macho'
 # CHECK-NEXT: objects:
 # CHECK-NEXT: filename:{{.*}}/Inputs/basic1.macho.x86_64.o
+# CHECK-NEXT: timestamp: 0
 # CHECK-NEXT: symbols:
 # CHECK-NEXT: sym: _main, objAddr: 0x0000000000000000, binAddr: 0x0000000100000EA0, size: 0x00000024
 # CHECK-NEXT: filename:{{.*}}/Inputs/./libbasic.a(basic2.macho.x86_64.o)'
+# CHECK-NEXT: timestamp: 0
 # CHECK-NEXT: symbols:
 # CHECK-DAG:   sym: _foo, objAddr: 0x0000000000000020, binAddr: 0x0000000100000ED0, size: 0x00000050
 # CHECK-DAG:   sym: _private_int, objAddr: 0x0000000000000560, binAddr: 0x0000000100001004, size: 0x00000000
@@ -18,6 +20,7 @@
 # CHECK-DAG:   sym: _baz, objAddr: 0x0000000000000310, binAddr: 0x0000000100001000, size: 0x00000000
 # CHECK-NOT: { sym:
 # CHECK-NEXT: filename:{{.*}}/Inputs/./libbasic.a(basic3.macho.x86_64.o)'
+# CHECK-NEXT: timestamp: 0
 # CHECK-NEXT: symbols:
 # CHECK-DAG:   sym: _val, objAddr: 0x0000000000000004, binAddr: 0x0000000100001008, size: 0x00000000
 # CHECK-DAG:   sym: _bar, objAddr: 0x0000000000000020, binAddr: 0x0000000100000F40, size: 0x00000050
index ad66105bc249d8811877d39156719dd6965efde6..49fe901b41950112b5d3f0722ed51c9309246c11 100644 (file)
@@ -19,18 +19,20 @@ namespace llvm {
 namespace dsymutil {
 
 ErrorOr<MemoryBufferRef>
-BinaryHolder::GetMemoryBufferForFile(StringRef Filename) {
+BinaryHolder::GetMemoryBufferForFile(StringRef Filename,
+                                     sys::TimeValue Timestamp) {
   if (Verbose)
     outs() << "trying to open '" << Filename << "'\n";
 
   // Try that first as it doesn't involve any filesystem access.
-  if (auto ErrOrArchiveMember = GetArchiveMemberBuffer(Filename))
+  if (auto ErrOrArchiveMember = GetArchiveMemberBuffer(Filename, Timestamp))
     return *ErrOrArchiveMember;
 
   // If the name ends with a closing paren, there is a huge chance
   // it is an archive member specification.
   if (Filename.endswith(")"))
-    if (auto ErrOrArchiveMember = MapArchiveAndGetMemberBuffer(Filename))
+    if (auto ErrOrArchiveMember =
+            MapArchiveAndGetMemberBuffer(Filename, Timestamp))
       return *ErrOrArchiveMember;
 
   // Otherwise, just try opening a standard file. If this is an
@@ -50,7 +52,8 @@ BinaryHolder::GetMemoryBufferForFile(StringRef Filename) {
 }
 
 ErrorOr<MemoryBufferRef>
-BinaryHolder::GetArchiveMemberBuffer(StringRef Filename) {
+BinaryHolder::GetArchiveMemberBuffer(StringRef Filename,
+                                     sys::TimeValue Timestamp) {
   if (!CurrentArchive)
     return make_error_code(errc::no_such_file_or_directory);
 
@@ -64,6 +67,12 @@ BinaryHolder::GetArchiveMemberBuffer(StringRef Filename) {
   for (const auto &Child : CurrentArchive->children()) {
     if (auto NameOrErr = Child.getName())
       if (*NameOrErr == Filename) {
+        if (Timestamp != sys::TimeValue::PosixZeroTime() &&
+            Timestamp != Child.getLastModified()) {
+          if (Verbose)
+            outs() << "\tmember had timestamp mismatch.\n";
+          continue;
+        }
         if (Verbose)
           outs() << "\tfound member in current archive.\n";
         return Child.getMemoryBufferRef();
@@ -74,7 +83,8 @@ BinaryHolder::GetArchiveMemberBuffer(StringRef Filename) {
 }
 
 ErrorOr<MemoryBufferRef>
-BinaryHolder::MapArchiveAndGetMemberBuffer(StringRef Filename) {
+BinaryHolder::MapArchiveAndGetMemberBuffer(StringRef Filename,
+                                           sys::TimeValue Timestamp) {
   StringRef ArchiveFilename = Filename.substr(0, Filename.find('('));
 
   auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename);
@@ -90,12 +100,12 @@ BinaryHolder::MapArchiveAndGetMemberBuffer(StringRef Filename) {
   CurrentArchive = std::move(*ErrOrArchive);
   CurrentMemoryBuffer = std::move(*ErrOrBuff);
 
-  return GetArchiveMemberBuffer(Filename);
+  return GetArchiveMemberBuffer(Filename, Timestamp);
 }
 
 ErrorOr<const object::ObjectFile &>
-BinaryHolder::GetObjectFile(StringRef Filename) {
-  auto ErrOrMemBufferRef = GetMemoryBufferForFile(Filename);
+BinaryHolder::GetObjectFile(StringRef Filename, sys::TimeValue Timestamp) {
+  auto ErrOrMemBufferRef = GetMemoryBufferForFile(Filename, Timestamp);
   if (auto Err = ErrOrMemBufferRef.getError())
     return Err;
 
index 04871b5d585501fa2abb6a0876f3178c2b878784..84c07ce0c4bccd859144abc303706f209b87992f 100644 (file)
@@ -19,6 +19,7 @@
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/TimeValue.h"
 
 namespace llvm {
 namespace dsymutil {
@@ -46,12 +47,14 @@ class BinaryHolder {
   /// This function performs no system calls, it just looks up a
   /// potential match for the given \p Filename in the currently
   /// mapped archive if there is one.
-  ErrorOr<MemoryBufferRef> GetArchiveMemberBuffer(StringRef Filename);
+  ErrorOr<MemoryBufferRef> GetArchiveMemberBuffer(StringRef Filename,
+                                                  sys::TimeValue Timestamp);
 
   /// \brief Interpret Filename as an archive member specification,
   /// map the corresponding archive to memory and return the
   /// MemoryBufferRef corresponding to the described member.
-  ErrorOr<MemoryBufferRef> MapArchiveAndGetMemberBuffer(StringRef Filename);
+  ErrorOr<MemoryBufferRef>
+  MapArchiveAndGetMemberBuffer(StringRef Filename, sys::TimeValue Timestamp);
 
   /// \brief Return the MemoryBufferRef that holds the memory
   /// mapping for the given \p Filename. This function will try to
@@ -61,7 +64,8 @@ class BinaryHolder {
   /// The returned MemoryBufferRef points to a buffer owned by this
   /// object. The buffer is valid until the next call to
   /// GetMemoryBufferForFile() on this object.
-  ErrorOr<MemoryBufferRef> GetMemoryBufferForFile(StringRef Filename);
+  ErrorOr<MemoryBufferRef> GetMemoryBufferForFile(StringRef Filename,
+                                                  sys::TimeValue Timestamp);
 
 public:
   BinaryHolder(bool Verbose) : Verbose(Verbose) {}
@@ -72,11 +76,15 @@ public:
   ///
   /// Calling this function invalidates the previous mapping owned by
   /// the BinaryHolder.
-  ErrorOr<const object::ObjectFile &> GetObjectFile(StringRef Filename);
+  ErrorOr<const object::ObjectFile &>
+  GetObjectFile(StringRef Filename,
+                sys::TimeValue Timestamp = sys::TimeValue::PosixZeroTime());
 
   /// \brief Wraps GetObjectFile() to return a derived ObjectFile type.
   template <typename ObjectFileType>
-  ErrorOr<const ObjectFileType &> GetFileAs(StringRef Filename) {
+  ErrorOr<const ObjectFileType &>
+  GetFileAs(StringRef Filename,
+            sys::TimeValue Timestamp = sys::TimeValue::PosixZeroTime()) {
     auto ErrOrObjFile = GetObjectFile(Filename);
     if (auto Err = ErrOrObjFile.getError())
       return Err;
index e5cc87b3f3181edc09cfa21db71f62bec364bb5b..24dedfcb673d4b2dba24ef07c679cacf29ead50c 100644 (file)
@@ -20,8 +20,9 @@ namespace dsymutil {
 
 using namespace llvm::object;
 
-DebugMapObject::DebugMapObject(StringRef ObjectFilename)
-    : Filename(ObjectFilename) {}
+DebugMapObject::DebugMapObject(StringRef ObjectFilename,
+                               sys::TimeValue Timestamp)
+    : Filename(ObjectFilename), Timestamp(Timestamp) {}
 
 bool DebugMapObject::addSymbol(StringRef Name, uint64_t ObjectAddress,
                                uint64_t LinkedAddress, uint32_t Size) {
@@ -58,8 +59,9 @@ void DebugMapObject::print(raw_ostream &OS) const {
 void DebugMapObject::dump() const { print(errs()); }
 #endif
 
-DebugMapObject &DebugMap::addDebugMapObject(StringRef ObjectFilePath) {
-  Objects.emplace_back(new DebugMapObject(ObjectFilePath));
+DebugMapObject &DebugMap::addDebugMapObject(StringRef ObjectFilePath,
+                                            sys::TimeValue Timestamp) {
+  Objects.emplace_back(new DebugMapObject(ObjectFilePath, Timestamp));
   return *Objects.back();
 }
 
@@ -121,11 +123,12 @@ namespace yaml {
 
 // Normalize/Denormalize between YAML and a DebugMapObject.
 struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO {
-  YamlDMO(IO &io) {}
+  YamlDMO(IO &io) { Timestamp = 0; }
   YamlDMO(IO &io, dsymutil::DebugMapObject &Obj);
   dsymutil::DebugMapObject denormalize(IO &IO);
 
   std::string Filename;
+  sys::TimeValue::SecondsType Timestamp;
   std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries;
 };
 
@@ -141,6 +144,7 @@ void MappingTraits<dsymutil::DebugMapObject>::mapping(
     IO &io, dsymutil::DebugMapObject &DMO) {
   MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO);
   io.mapRequired("filename", Norm->Filename);
+  io.mapOptional("timestamp", Norm->Timestamp);
   io.mapRequired("symbols", Norm->Entries);
 }
 
@@ -192,6 +196,7 @@ void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping(
 MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO(
     IO &io, dsymutil::DebugMapObject &Obj) {
   Filename = Obj.Filename;
+  Timestamp = Obj.getTimestamp().toEpochTime();
   Entries.reserve(Obj.Symbols.size());
   for (auto &Entry : Obj.Symbols)
     Entries.push_back(std::make_pair(Entry.getKey(), Entry.getValue()));
@@ -224,7 +229,9 @@ MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) {
     }
   }
 
-  dsymutil::DebugMapObject Res(Path);
+  sys::TimeValue TV;
+  TV.fromEpochTime(Timestamp);
+  dsymutil::DebugMapObject Res(Path, TV);
   for (auto &Entry : Entries) {
     auto &Mapping = Entry.second;
     uint64_t ObjAddress = Mapping.ObjectAddress;
index d0edbabb404bae8b57427676add85f52c30f817f..29347ed0f5051405511efe005e6748e804669f16 100644 (file)
@@ -29,6 +29,7 @@
 #include "llvm/Support/ErrorOr.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/TimeValue.h"
 #include "llvm/Support/YAMLTraits.h"
 #include <vector>
 
@@ -89,7 +90,8 @@ public:
 
   /// This function adds an DebugMapObject to the list owned by this
   /// debug map.
-  DebugMapObject &addDebugMapObject(StringRef ObjectFilePath);
+  DebugMapObject &addDebugMapObject(StringRef ObjectFilePath,
+                                    sys::TimeValue Timestamp);
 
   const Triple &getTriple() const { return BinaryTriple; }
 
@@ -139,6 +141,8 @@ public:
 
   llvm::StringRef getObjectFilename() const { return Filename; }
 
+  sys::TimeValue getTimestamp() const { return Timestamp; }
+
   iterator_range<StringMap<SymbolMapping>::const_iterator> symbols() const {
     return make_range(Symbols.begin(), Symbols.end());
   }
@@ -150,9 +154,10 @@ public:
 private:
   friend class DebugMap;
   /// DebugMapObjects can only be constructed by the owning DebugMap.
-  DebugMapObject(StringRef ObjectFilename);
+  DebugMapObject(StringRef ObjectFilename, sys::TimeValue Timestamp);
 
   std::string Filename;
+  sys::TimeValue Timestamp;
   StringMap<SymbolMapping> Symbols;
   DenseMap<uint64_t, DebugMapEntry *> AddressToMapping;
 
@@ -167,12 +172,14 @@ private:
 public:
   DebugMapObject &operator=(DebugMapObject RHS) {
     std::swap(Filename, RHS.Filename);
+    std::swap(Timestamp, RHS.Timestamp);
     std::swap(Symbols, RHS.Symbols);
     std::swap(AddressToMapping, RHS.AddressToMapping);
     return *this;
   }
   DebugMapObject(DebugMapObject &&RHS) {
     Filename = std::move(RHS.Filename);
+    Timestamp = std::move(RHS.Timestamp);
     Symbols = std::move(RHS.Symbols);
     AddressToMapping = std::move(RHS.AddressToMapping);
   }
index bbe5cfbb1c1d633a7a4f509fb39089d33c907864..ba136d1d96fbc91e920f108b9385eed49a5da555 100644 (file)
@@ -3027,7 +3027,8 @@ bool DwarfLinker::link(const DebugMap &Map) {
 
     if (Options.Verbose)
       outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n";
-    auto ErrOrObj = BinHolder.GetObjectFile(Obj->getObjectFilename());
+    auto ErrOrObj =
+        BinHolder.GetObjectFile(Obj->getObjectFilename(), Obj->getTimestamp());
     if (std::error_code EC = ErrOrObj.getError()) {
       reportWarning(Twine(Obj->getObjectFilename()) + ": " + EC.message());
       continue;
index 6c9fa9b5132518397649e89bb918aa9a3d0b2743..8f3ca6ca0045a5126ebab9a4717b6b6359c8c57e 100644 (file)
@@ -55,7 +55,7 @@ private:
   const char *CurrentFunctionName;
   uint64_t CurrentFunctionAddress;
 
-  void switchToNewDebugMapObject(StringRef Filename);
+  void switchToNewDebugMapObject(StringRef Filename, sys::TimeValue Timestamp);
   void resetParserState();
   uint64_t getMainBinarySymbolAddress(StringRef Name);
   void loadMainBinarySymbols();
@@ -84,13 +84,15 @@ void MachODebugMapParser::resetParserState() {
 /// Create a new DebugMapObject. This function resets the state of the
 /// parser that was referring to the last object file and sets
 /// everything up to add symbols to the new one.
-void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename) {
+void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename,
+                                                    sys::TimeValue Timestamp) {
   resetParserState();
 
   SmallString<80> Path(PathPrefix);
   sys::path::append(Path, Filename);
 
-  auto MachOOrError = CurrentObjectHolder.GetFileAs<MachOObjectFile>(Path);
+  auto MachOOrError =
+      CurrentObjectHolder.GetFileAs<MachOObjectFile>(Path, Timestamp);
   if (auto Error = MachOOrError.getError()) {
     Warning(Twine("cannot open debug object \"") + Path.str() + "\": " +
             Error.message() + "\n");
@@ -98,7 +100,7 @@ void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename) {
   }
 
   loadCurrentObjectFileSymbols();
-  CurrentDebugMapObject = &Result->addDebugMapObject(Path);
+  CurrentDebugMapObject = &Result->addDebugMapObject(Path, Timestamp);
 }
 
 static Triple getTriple(const object::MachOObjectFile &Obj) {
@@ -144,8 +146,11 @@ void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
   const char *Name = &MainBinaryStrings.data()[StringIndex];
 
   // An N_OSO entry represents the start of a new object file description.
-  if (Type == MachO::N_OSO)
-    return switchToNewDebugMapObject(Name);
+  if (Type == MachO::N_OSO) {
+    sys::TimeValue Timestamp;
+    Timestamp.fromEpochTime(Value);
+    return switchToNewDebugMapObject(Name, Timestamp);
+  }
 
   // If the last N_OSO object file wasn't found,
   // CurrentDebugMapObject will be null. Do not update anything