Home | History | Annotate | Line # | Download | only in dsymutil
      1 //===-- BinaryHolder.cpp --------------------------------------------------===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 // This program is a utility that aims to be a dropin replacement for
     10 // Darwin's dsymutil.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "BinaryHolder.h"
     15 #include "llvm/Object/MachO.h"
     16 #include "llvm/Support/WithColor.h"
     17 #include "llvm/Support/raw_ostream.h"
     18 
     19 namespace llvm {
     20 namespace dsymutil {
     21 
     22 static std::pair<StringRef, StringRef>
     23 getArchiveAndObjectName(StringRef Filename) {
     24   StringRef Archive = Filename.substr(0, Filename.rfind('('));
     25   StringRef Object = Filename.substr(Archive.size() + 1).drop_back();
     26   return {Archive, Object};
     27 }
     28 
     29 static bool isArchive(StringRef Filename) { return Filename.endswith(")"); }
     30 
     31 static std::vector<MemoryBufferRef>
     32 getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem,
     33                          object::MachOUniversalBinary &Fat) {
     34   std::vector<MemoryBufferRef> Buffers;
     35   StringRef FatData = Fat.getData();
     36   for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End;
     37        ++It) {
     38     StringRef ObjData = FatData.substr(It->getOffset(), It->getSize());
     39     Buffers.emplace_back(ObjData, Filename);
     40   }
     41   return Buffers;
     42 }
     43 
     44 Error BinaryHolder::ArchiveEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
     45                                        StringRef Filename,
     46                                        TimestampTy Timestamp, bool Verbose) {
     47   StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
     48 
     49   // Try to load archive and force it to be memory mapped.
     50   auto ErrOrBuff = (ArchiveFilename == "-")
     51                        ? MemoryBuffer::getSTDIN()
     52                        : VFS->getBufferForFile(ArchiveFilename, -1, false);
     53   if (auto Err = ErrOrBuff.getError())
     54     return errorCodeToError(Err);
     55 
     56   MemBuffer = std::move(*ErrOrBuff);
     57 
     58   if (Verbose)
     59     WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n";
     60 
     61   // Load one or more archive buffers, depending on whether we're dealing with
     62   // a fat binary.
     63   std::vector<MemoryBufferRef> ArchiveBuffers;
     64 
     65   auto ErrOrFat =
     66       object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
     67   if (!ErrOrFat) {
     68     consumeError(ErrOrFat.takeError());
     69     ArchiveBuffers.push_back(MemBuffer->getMemBufferRef());
     70   } else {
     71     FatBinary = std::move(*ErrOrFat);
     72     FatBinaryName = std::string(ArchiveFilename);
     73     ArchiveBuffers =
     74         getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
     75   }
     76 
     77   // Finally, try to load the archives.
     78   Archives.reserve(ArchiveBuffers.size());
     79   for (auto MemRef : ArchiveBuffers) {
     80     auto ErrOrArchive = object::Archive::create(MemRef);
     81     if (!ErrOrArchive)
     82       return ErrOrArchive.takeError();
     83     Archives.push_back(std::move(*ErrOrArchive));
     84   }
     85 
     86   return Error::success();
     87 }
     88 
     89 Error BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
     90                                       StringRef Filename, TimestampTy Timestamp,
     91                                       bool Verbose) {
     92   // Try to load regular binary and force it to be memory mapped.
     93   auto ErrOrBuff = (Filename == "-")
     94                        ? MemoryBuffer::getSTDIN()
     95                        : VFS->getBufferForFile(Filename, -1, false);
     96   if (auto Err = ErrOrBuff.getError())
     97     return errorCodeToError(Err);
     98 
     99   if (Filename != "-" && Timestamp != sys::TimePoint<>()) {
    100     llvm::ErrorOr<vfs::Status> Stat = VFS->status(Filename);
    101     if (!Stat)
    102       return errorCodeToError(Stat.getError());
    103     if (Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(
    104                          Stat->getLastModificationTime()))
    105       WithColor::warning() << Filename
    106                            << ": timestamp mismatch between object file ("
    107                            << Stat->getLastModificationTime()
    108                            << ") and debug map (" << Timestamp << ")\n";
    109   }
    110 
    111   MemBuffer = std::move(*ErrOrBuff);
    112 
    113   if (Verbose)
    114     WithColor::note() << "loaded object.\n";
    115 
    116   // Load one or more object buffers, depending on whether we're dealing with a
    117   // fat binary.
    118   std::vector<MemoryBufferRef> ObjectBuffers;
    119 
    120   auto ErrOrFat =
    121       object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
    122   if (!ErrOrFat) {
    123     consumeError(ErrOrFat.takeError());
    124     ObjectBuffers.push_back(MemBuffer->getMemBufferRef());
    125   } else {
    126     FatBinary = std::move(*ErrOrFat);
    127     FatBinaryName = std::string(Filename);
    128     ObjectBuffers =
    129         getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
    130   }
    131 
    132   Objects.reserve(ObjectBuffers.size());
    133   for (auto MemRef : ObjectBuffers) {
    134     auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef);
    135     if (!ErrOrObjectFile)
    136       return ErrOrObjectFile.takeError();
    137     Objects.push_back(std::move(*ErrOrObjectFile));
    138   }
    139 
    140   return Error::success();
    141 }
    142 
    143 std::vector<const object::ObjectFile *>
    144 BinaryHolder::ObjectEntry::getObjects() const {
    145   std::vector<const object::ObjectFile *> Result;
    146   Result.reserve(Objects.size());
    147   for (auto &Object : Objects) {
    148     Result.push_back(Object.get());
    149   }
    150   return Result;
    151 }
    152 Expected<const object::ObjectFile &>
    153 BinaryHolder::ObjectEntry::getObject(const Triple &T) const {
    154   for (const auto &Obj : Objects) {
    155     if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
    156       if (MachO->getArchTriple().str() == T.str())
    157         return *MachO;
    158     } else if (Obj->getArch() == T.getArch())
    159       return *Obj;
    160   }
    161   return errorCodeToError(object::object_error::arch_not_found);
    162 }
    163 
    164 Expected<const BinaryHolder::ObjectEntry &>
    165 BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename,
    166                                            TimestampTy Timestamp,
    167                                            bool Verbose) {
    168   StringRef ArchiveFilename;
    169   StringRef ObjectFilename;
    170   std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename);
    171 
    172   // Try the cache first.
    173   KeyTy Key = {ObjectFilename, Timestamp};
    174 
    175   {
    176     std::lock_guard<std::mutex> Lock(MemberCacheMutex);
    177     if (MemberCache.count(Key))
    178       return MemberCache[Key];
    179   }
    180 
    181   // Create a new ObjectEntry, but don't add it to the cache yet. Loading of
    182   // the archive members might fail and we don't want to lock the whole archive
    183   // during this operation.
    184   ObjectEntry OE;
    185 
    186   for (const auto &Archive : Archives) {
    187     Error Err = Error::success();
    188     for (auto Child : Archive->children(Err)) {
    189       if (auto NameOrErr = Child.getName()) {
    190         if (*NameOrErr == ObjectFilename) {
    191           auto ModTimeOrErr = Child.getLastModified();
    192           if (!ModTimeOrErr)
    193             return ModTimeOrErr.takeError();
    194 
    195           if (Timestamp != sys::TimePoint<>() &&
    196               Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(
    197                                ModTimeOrErr.get())) {
    198             if (Verbose)
    199               WithColor::warning()
    200                   << *NameOrErr
    201                   << ": timestamp mismatch between archive member ("
    202                   << ModTimeOrErr.get() << ") and debug map (" << Timestamp
    203                   << ")\n";
    204             continue;
    205           }
    206 
    207           if (Verbose)
    208             WithColor::note() << "found member in archive.\n";
    209 
    210           auto ErrOrMem = Child.getMemoryBufferRef();
    211           if (!ErrOrMem)
    212             return ErrOrMem.takeError();
    213 
    214           auto ErrOrObjectFile =
    215               object::ObjectFile::createObjectFile(*ErrOrMem);
    216           if (!ErrOrObjectFile)
    217             return ErrOrObjectFile.takeError();
    218 
    219           OE.Objects.push_back(std::move(*ErrOrObjectFile));
    220         }
    221       }
    222     }
    223     if (Err)
    224       return std::move(Err);
    225   }
    226 
    227   if (OE.Objects.empty())
    228     return errorCodeToError(errc::no_such_file_or_directory);
    229 
    230   std::lock_guard<std::mutex> Lock(MemberCacheMutex);
    231   MemberCache.try_emplace(Key, std::move(OE));
    232   return MemberCache[Key];
    233 }
    234 
    235 Expected<const BinaryHolder::ObjectEntry &>
    236 BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) {
    237   if (Verbose)
    238     WithColor::note() << "trying to open '" << Filename << "'\n";
    239 
    240   // If this is an archive, we might have either the object or the archive
    241   // cached. In this case we can load it without accessing the file system.
    242   if (isArchive(Filename)) {
    243     StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
    244     std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
    245     if (ArchiveCache.count(ArchiveFilename)) {
    246       return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp,
    247                                                           Verbose);
    248     } else {
    249       ArchiveEntry &AE = ArchiveCache[ArchiveFilename];
    250       auto Err = AE.load(VFS, Filename, Timestamp, Verbose);
    251       if (Err) {
    252         ArchiveCache.erase(ArchiveFilename);
    253         // Don't return the error here: maybe the file wasn't an archive.
    254         llvm::consumeError(std::move(Err));
    255       } else {
    256         return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp,
    257                                                             Verbose);
    258       }
    259     }
    260   }
    261 
    262   // If this is an object, we might have it cached. If not we'll have to load
    263   // it from the file system and cache it now.
    264   std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
    265   if (!ObjectCache.count(Filename)) {
    266     ObjectEntry &OE = ObjectCache[Filename];
    267     auto Err = OE.load(VFS, Filename, Timestamp, Verbose);
    268     if (Err) {
    269       ObjectCache.erase(Filename);
    270       return std::move(Err);
    271     }
    272   }
    273 
    274   return ObjectCache[Filename];
    275 }
    276 
    277 void BinaryHolder::clear() {
    278   std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex);
    279   std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex);
    280   ArchiveCache.clear();
    281   ObjectCache.clear();
    282 }
    283 
    284 } // namespace dsymutil
    285 } // namespace llvm
    286