brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.5 KiB · 58751bd Raw
303 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This program is a utility that aims to be a dropin replacement for10// 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 19namespace llvm {20namespace dsymutil {21 22static std::pair<StringRef, StringRef>23getArchiveAndObjectName(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 29static bool isArchive(StringRef Filename) { return Filename.ends_with(")"); }30 31static std::vector<MemoryBufferRef>32getMachOFatMemoryBuffers(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 44BinaryHolder::BinaryHolder(IntrusiveRefCntPtr<vfs::FileSystem> VFS,45                           BinaryHolder::Options Opts)46    : VFS(VFS), Opts(Opts) {}47 48Error BinaryHolder::ArchiveEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,49                                       StringRef Filename,50                                       TimestampTy Timestamp, Options Opts) {51  StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;52 53  // Try to load archive and force it to be memory mapped.54  auto ErrOrBuff = (ArchiveFilename == "-")55                       ? MemoryBuffer::getSTDIN()56                       : VFS->getBufferForFile(ArchiveFilename, -1, false);57  if (auto Err = ErrOrBuff.getError())58    return errorCodeToError(Err);59 60  MemBuffer = std::move(*ErrOrBuff);61 62  if (Opts.Verbose)63    WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n";64 65  // Load one or more archive buffers, depending on whether we're dealing with66  // a fat binary.67  std::vector<MemoryBufferRef> ArchiveBuffers;68 69  auto ErrOrFat =70      object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());71  if (!ErrOrFat) {72    consumeError(ErrOrFat.takeError());73    ArchiveBuffers.push_back(MemBuffer->getMemBufferRef());74  } else {75    FatBinary = std::move(*ErrOrFat);76    FatBinaryName = std::string(ArchiveFilename);77    ArchiveBuffers =78        getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);79  }80 81  // Finally, try to load the archives.82  Archives.reserve(ArchiveBuffers.size());83  for (auto MemRef : ArchiveBuffers) {84    auto ErrOrArchive = object::Archive::create(MemRef);85    if (!ErrOrArchive)86      return ErrOrArchive.takeError();87    Archives.push_back(std::move(*ErrOrArchive));88  }89 90  return Error::success();91}92 93Error BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,94                                      StringRef Filename, TimestampTy Timestamp,95                                      Options Opts) {96  // Try to load regular binary and force it to be memory mapped.97  auto ErrOrBuff = (Filename == "-")98                       ? MemoryBuffer::getSTDIN()99                       : VFS->getBufferForFile(Filename, -1, false);100  if (auto Err = ErrOrBuff.getError())101    return errorCodeToError(Err);102 103  if (Opts.Warn && Filename != "-" && Timestamp != sys::TimePoint<>()) {104    llvm::ErrorOr<vfs::Status> Stat = VFS->status(Filename);105    if (!Stat)106      return errorCodeToError(Stat.getError());107    if (Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(108                         Stat->getLastModificationTime()))109      WithColor::warning() << Filename110                           << ": timestamp mismatch between object file ("111                           << Stat->getLastModificationTime()112                           << ") and debug map (" << Timestamp << ")\n";113  }114 115  MemBuffer = std::move(*ErrOrBuff);116 117  if (Opts.Verbose)118    WithColor::note() << "loaded object.\n";119 120  // Load one or more object buffers, depending on whether we're dealing with a121  // fat binary.122  std::vector<MemoryBufferRef> ObjectBuffers;123 124  auto ErrOrFat =125      object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());126  if (!ErrOrFat) {127    consumeError(ErrOrFat.takeError());128    ObjectBuffers.push_back(MemBuffer->getMemBufferRef());129  } else {130    FatBinary = std::move(*ErrOrFat);131    FatBinaryName = std::string(Filename);132    ObjectBuffers =133        getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);134  }135 136  Objects.reserve(ObjectBuffers.size());137  for (auto MemRef : ObjectBuffers) {138    auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef);139    if (!ErrOrObjectFile)140      return ErrOrObjectFile.takeError();141    Objects.push_back(std::move(*ErrOrObjectFile));142  }143 144  return Error::success();145}146 147std::vector<const object::ObjectFile *>148BinaryHolder::ObjectEntry::getObjects() const {149  std::vector<const object::ObjectFile *> Result;150  Result.reserve(Objects.size());151  for (auto &Object : Objects) {152    Result.push_back(Object.get());153  }154  return Result;155}156Expected<const object::ObjectFile &>157BinaryHolder::ObjectEntry::getObject(const Triple &T) const {158  for (const auto &Obj : Objects) {159    if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {160      if (MachO->getArchTriple().str() == T.str())161        return *MachO;162    } else if (Obj->getArch() == T.getArch())163      return *Obj;164  }165  return errorCodeToError(object::object_error::arch_not_found);166}167 168Expected<const BinaryHolder::ObjectEntry &>169BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename,170                                           TimestampTy Timestamp,171                                           Options Opts) {172  StringRef ArchiveFilename;173  StringRef ObjectFilename;174  std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename);175  KeyTy Key = {ObjectFilename, Timestamp};176 177  // Try the cache first.178  std::lock_guard<std::mutex> Lock(MemberCacheMutex);179  if (auto It = MemberCache.find(Key); It != MemberCache.end())180    return *It->second;181 182  // Create a new ObjectEntry, but don't add it to the cache yet. Loading of183  // the archive members might fail and we don't want to lock the whole archive184  // during this operation.185  auto OE = std::make_unique<ObjectEntry>();186 187  for (const auto &Archive : Archives) {188    Error Err = Error::success();189    for (const auto &Child : Archive->children(Err)) {190      if (auto NameOrErr = Child.getName()) {191        if (*NameOrErr == ObjectFilename) {192          auto ModTimeOrErr = Child.getLastModified();193          if (!ModTimeOrErr)194            return ModTimeOrErr.takeError();195 196          if (Timestamp != sys::TimePoint<>() &&197              Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(198                               ModTimeOrErr.get())) {199            if (Opts.Verbose)200              WithColor::warning()201                  << *NameOrErr202                  << ": timestamp mismatch between archive member ("203                  << ModTimeOrErr.get() << ") and debug map (" << Timestamp204                  << ")\n";205            continue;206          }207 208          if (Opts.Verbose)209            WithColor::note() << "found member in archive.\n";210 211          auto ErrOrMem = Child.getMemoryBufferRef();212          if (!ErrOrMem)213            return ErrOrMem.takeError();214 215          auto ErrOrObjectFile =216              object::ObjectFile::createObjectFile(*ErrOrMem);217          if (!ErrOrObjectFile)218            return ErrOrObjectFile.takeError();219 220          OE->Objects.push_back(std::move(*ErrOrObjectFile));221        }222      }223    }224    if (Err)225      return std::move(Err);226  }227 228  if (OE->Objects.empty())229    return errorCodeToError(errc::no_such_file_or_directory);230 231  return *(MemberCache[Key] = std::move(OE));232}233 234Expected<const BinaryHolder::ObjectEntry &>235BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) {236  if (Opts.Verbose)237    WithColor::note() << "trying to open '" << Filename << "'\n";238 239  // If this is an archive, we might have either the object or the archive240  // cached. In this case we can load it without accessing the file system.241  if (isArchive(Filename)) {242    StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;243    std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);244    ArchiveRefCounter[ArchiveFilename]++;245    if (auto It = ArchiveCache.find(ArchiveFilename);246        It != ArchiveCache.end()) {247      return It->second->getObjectEntry(Filename, Timestamp, Opts);248    } else {249      auto AE = std::make_unique<ArchiveEntry>();250      auto Err = AE->load(VFS, Filename, Timestamp, Opts);251      if (Err) {252        // Don't return the error here: maybe the file wasn't an archive.253        llvm::consumeError(std::move(Err));254      } else {255        auto &Cache = ArchiveCache[ArchiveFilename];256        Cache = std::move(AE);257        return Cache->getObjectEntry(Filename, Timestamp, Opts);258      }259    }260  }261 262  // If this is an object, we might have it cached. If not we'll have to load263  // it from the file system and cache it now.264  std::lock_guard<std::mutex> Lock(ObjectCacheMutex);265  ObjectRefCounter[Filename]++;266  if (!ObjectCache.count(Filename)) {267    auto OE = std::make_unique<ObjectEntry>();268    auto Err = OE->load(VFS, Filename, Timestamp, Opts);269    if (Err)270      return std::move(Err);271    ObjectCache[Filename] = std::move(OE);272  }273 274  return *ObjectCache[Filename];275}276 277void 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 284void BinaryHolder::eraseObjectEntry(StringRef Filename) {285  if (Opts.Verbose)286    WithColor::note() << "erasing '" << Filename << "' from cache\n";287 288  if (isArchive(Filename)) {289    StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;290    std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);291    if (--ArchiveRefCounter[ArchiveFilename] == 0)292      ArchiveCache.erase(ArchiveFilename);293    return;294  }295 296  std::lock_guard<std::mutex> Lock(ObjectCacheMutex);297  if (--ObjectRefCounter[Filename] == 0)298    ObjectCache.erase(Filename);299}300 301} // namespace dsymutil302} // namespace llvm303