#include "BinaryHolder.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
namespace dsymutil {
static std::pair<StringRef, StringRef>
getArchiveAndObjectName(StringRef Filename) {
StringRef Archive = Filename.substr(0, Filename.rfind('('));
StringRef Object = Filename.substr(Archive.size() + 1).drop_back();
return {Archive, Object};
}
static bool isArchive(StringRef Filename) { return Filename.endswith(")"); }
static std::vector<MemoryBufferRef>
getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem,
object::MachOUniversalBinary &Fat) {
std::vector<MemoryBufferRef> Buffers;
StringRef FatData = Fat.getData();
for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End;
++It) {
StringRef ObjData = FatData.substr(It->getOffset(), It->getSize());
Buffers.emplace_back(ObjData, Filename);
}
return Buffers;
}
Error BinaryHolder::ArchiveEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
StringRef Filename,
TimestampTy Timestamp, bool Verbose) {
StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
auto ErrOrBuff = (ArchiveFilename == "-")
? MemoryBuffer::getSTDIN()
: VFS->getBufferForFile(ArchiveFilename, -1, false);
if (auto Err = ErrOrBuff.getError())
return errorCodeToError(Err);
MemBuffer = std::move(*ErrOrBuff);
if (Verbose)
WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n";
std::vector<MemoryBufferRef> ArchiveBuffers;
auto ErrOrFat =
object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
if (!ErrOrFat) {
consumeError(ErrOrFat.takeError());
ArchiveBuffers.push_back(MemBuffer->getMemBufferRef());
} else {
FatBinary = std::move(*ErrOrFat);
FatBinaryName = std::string(ArchiveFilename);
ArchiveBuffers =
getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
}
Archives.reserve(ArchiveBuffers.size());
for (auto MemRef : ArchiveBuffers) {
auto ErrOrArchive = object::Archive::create(MemRef);
if (!ErrOrArchive)
return ErrOrArchive.takeError();
Archives.push_back(std::move(*ErrOrArchive));
}
return Error::success();
}
Error BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
StringRef Filename, TimestampTy Timestamp,
bool Verbose) {
auto ErrOrBuff = (Filename == "-")
? MemoryBuffer::getSTDIN()
: VFS->getBufferForFile(Filename, -1, false);
if (auto Err = ErrOrBuff.getError())
return errorCodeToError(Err);
if (Filename != "-" && Timestamp != sys::TimePoint<>()) {
llvm::ErrorOr<vfs::Status> Stat = VFS->status(Filename);
if (!Stat)
return errorCodeToError(Stat.getError());
if (Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(
Stat->getLastModificationTime()))
WithColor::warning() << Filename
<< ": timestamp mismatch between object file ("
<< Stat->getLastModificationTime()
<< ") and debug map (" << Timestamp << ")\n";
}
MemBuffer = std::move(*ErrOrBuff);
if (Verbose)
WithColor::note() << "loaded object.\n";
std::vector<MemoryBufferRef> ObjectBuffers;
auto ErrOrFat =
object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
if (!ErrOrFat) {
consumeError(ErrOrFat.takeError());
ObjectBuffers.push_back(MemBuffer->getMemBufferRef());
} else {
FatBinary = std::move(*ErrOrFat);
FatBinaryName = std::string(Filename);
ObjectBuffers =
getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
}
Objects.reserve(ObjectBuffers.size());
for (auto MemRef : ObjectBuffers) {
auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef);
if (!ErrOrObjectFile)
return ErrOrObjectFile.takeError();
Objects.push_back(std::move(*ErrOrObjectFile));
}
return Error::success();
}
std::vector<const object::ObjectFile *>
BinaryHolder::ObjectEntry::getObjects() const {
std::vector<const object::ObjectFile *> Result;
Result.reserve(Objects.size());
for (auto &Object : Objects) {
Result.push_back(Object.get());
}
return Result;
}
Expected<const object::ObjectFile &>
BinaryHolder::ObjectEntry::getObject(const Triple &T) const {
for (const auto &Obj : Objects) {
if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
if (MachO->getArchTriple().str() == T.str())
return *MachO;
} else if (Obj->getArch() == T.getArch())
return *Obj;
}
return errorCodeToError(object::object_error::arch_not_found);
}
Expected<const BinaryHolder::ObjectEntry &>
BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename,
TimestampTy Timestamp,
bool Verbose) {
StringRef ArchiveFilename;
StringRef ObjectFilename;
std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename);
KeyTy Key = {ObjectFilename, Timestamp};
std::lock_guard<std::mutex> Lock(MemberCacheMutex);
if (MemberCache.count(Key))
return *MemberCache[Key].get();
auto OE = std::make_unique<ObjectEntry>();
for (const auto &Archive : Archives) {
Error Err = Error::success();
for (auto Child : Archive->children(Err)) {
if (auto NameOrErr = Child.getName()) {
if (*NameOrErr == ObjectFilename) {
auto ModTimeOrErr = Child.getLastModified();
if (!ModTimeOrErr)
return ModTimeOrErr.takeError();
if (Timestamp != sys::TimePoint<>() &&
Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(
ModTimeOrErr.get())) {
if (Verbose)
WithColor::warning()
<< *NameOrErr
<< ": timestamp mismatch between archive member ("
<< ModTimeOrErr.get() << ") and debug map (" << Timestamp
<< ")\n";
continue;
}
if (Verbose)
WithColor::note() << "found member in archive.\n";
auto ErrOrMem = Child.getMemoryBufferRef();
if (!ErrOrMem)
return ErrOrMem.takeError();
auto ErrOrObjectFile =
object::ObjectFile::createObjectFile(*ErrOrMem);
if (!ErrOrObjectFile)
return ErrOrObjectFile.takeError();
OE->Objects.push_back(std::move(*ErrOrObjectFile));
}
}
}
if (Err)
return std::move(Err);
}
if (OE->Objects.empty())
return errorCodeToError(errc::no_such_file_or_directory);
MemberCache[Key] = std::move(OE);
return *MemberCache[Key];
}
Expected<const BinaryHolder::ObjectEntry &>
BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) {
if (Verbose)
WithColor::note() << "trying to open '" << Filename << "'\n";
if (isArchive(Filename)) {
StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
if (ArchiveCache.count(ArchiveFilename)) {
return ArchiveCache[ArchiveFilename]->getObjectEntry(Filename, Timestamp,
Verbose);
} else {
auto AE = std::make_unique<ArchiveEntry>();
auto Err = AE->load(VFS, Filename, Timestamp, Verbose);
if (Err) {
llvm::consumeError(std::move(Err));
} else {
ArchiveCache[ArchiveFilename] = std::move(AE);
return ArchiveCache[ArchiveFilename]->getObjectEntry(
Filename, Timestamp, Verbose);
}
}
}
std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
if (!ObjectCache.count(Filename)) {
auto OE = std::make_unique<ObjectEntry>();
auto Err = OE->load(VFS, Filename, Timestamp, Verbose);
if (Err)
return std::move(Err);
ObjectCache[Filename] = std::move(OE);
}
return *ObjectCache[Filename];
}
void BinaryHolder::clear() {
std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex);
std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex);
ArchiveCache.clear();
ObjectCache.clear();
}
} }