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