Home | History | Annotate | Download | only in dsymutil
      1 //===-- BinaryHolder.cpp --------------------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This program is a utility that aims to be a dropin replacement for
     11 // Darwin's dsymutil.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "BinaryHolder.h"
     16 #include "llvm/Object/MachO.h"
     17 #include "llvm/Support/raw_ostream.h"
     18 
     19 namespace llvm {
     20 namespace dsymutil {
     21 
     22 static std::vector<MemoryBufferRef>
     23 getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem,
     24                          object::MachOUniversalBinary &Fat) {
     25   std::vector<MemoryBufferRef> Buffers;
     26   StringRef FatData = Fat.getData();
     27   for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End;
     28        ++It) {
     29     StringRef ObjData = FatData.substr(It->getOffset(), It->getSize());
     30     Buffers.emplace_back(ObjData, Filename);
     31   }
     32   return Buffers;
     33 }
     34 
     35 void BinaryHolder::changeBackingMemoryBuffer(
     36     std::unique_ptr<MemoryBuffer> &&Buf) {
     37   CurrentArchives.clear();
     38   CurrentObjectFiles.clear();
     39   CurrentFatBinary.reset();
     40 
     41   CurrentMemoryBuffer = std::move(Buf);
     42 }
     43 
     44 ErrorOr<std::vector<MemoryBufferRef>>
     45 BinaryHolder::GetMemoryBuffersForFile(StringRef Filename,
     46                                       sys::TimeValue Timestamp) {
     47   if (Verbose)
     48     outs() << "trying to open '" << Filename << "'\n";
     49 
     50   // Try that first as it doesn't involve any filesystem access.
     51   if (auto ErrOrArchiveMembers = GetArchiveMemberBuffers(Filename, Timestamp))
     52     return *ErrOrArchiveMembers;
     53 
     54   // If the name ends with a closing paren, there is a huge chance
     55   // it is an archive member specification.
     56   if (Filename.endswith(")"))
     57     if (auto ErrOrArchiveMembers =
     58             MapArchiveAndGetMemberBuffers(Filename, Timestamp))
     59       return *ErrOrArchiveMembers;
     60 
     61   // Otherwise, just try opening a standard file. If this is an
     62   // archive member specifiaction and any of the above didn't handle it
     63   // (either because the archive is not there anymore, or because the
     64   // archive doesn't contain the requested member), this will still
     65   // provide a sensible error message.
     66   auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(Filename);
     67   if (auto Err = ErrOrFile.getError())
     68     return Err;
     69 
     70   changeBackingMemoryBuffer(std::move(*ErrOrFile));
     71   if (Verbose)
     72     outs() << "\tloaded file.\n";
     73 
     74   auto ErrOrFat = object::MachOUniversalBinary::create(
     75       CurrentMemoryBuffer->getMemBufferRef());
     76   if (!ErrOrFat) {
     77     consumeError(ErrOrFat.takeError());
     78     // Not a fat binary must be a standard one. Return a one element vector.
     79     return std::vector<MemoryBufferRef>{CurrentMemoryBuffer->getMemBufferRef()};
     80   }
     81 
     82   CurrentFatBinary = std::move(*ErrOrFat);
     83   CurrentFatBinaryName = Filename;
     84   return getMachOFatMemoryBuffers(CurrentFatBinaryName, *CurrentMemoryBuffer,
     85                                   *CurrentFatBinary);
     86 }
     87 
     88 ErrorOr<std::vector<MemoryBufferRef>>
     89 BinaryHolder::GetArchiveMemberBuffers(StringRef Filename,
     90                                       sys::TimeValue Timestamp) {
     91   if (CurrentArchives.empty())
     92     return make_error_code(errc::no_such_file_or_directory);
     93 
     94   StringRef CurArchiveName = CurrentArchives.front()->getFileName();
     95   if (!Filename.startswith(Twine(CurArchiveName, "(").str()))
     96     return make_error_code(errc::no_such_file_or_directory);
     97 
     98   // Remove the archive name and the parens around the archive member name.
     99   Filename = Filename.substr(CurArchiveName.size() + 1).drop_back();
    100 
    101   std::vector<MemoryBufferRef> Buffers;
    102   Buffers.reserve(CurrentArchives.size());
    103 
    104   for (const auto &CurrentArchive : CurrentArchives) {
    105     Error Err;
    106     for (auto Child : CurrentArchive->children(Err)) {
    107       if (auto NameOrErr = Child.getName()) {
    108         if (*NameOrErr == Filename) {
    109           if (Timestamp != sys::TimeValue::PosixZeroTime() &&
    110               Timestamp != Child.getLastModified()) {
    111             if (Verbose)
    112               outs() << "\tmember had timestamp mismatch.\n";
    113             continue;
    114           }
    115           if (Verbose)
    116             outs() << "\tfound member in current archive.\n";
    117           auto ErrOrMem = Child.getMemoryBufferRef();
    118           if (auto Err = ErrOrMem.getError())
    119             return Err;
    120           Buffers.push_back(*ErrOrMem);
    121         }
    122       }
    123     }
    124     if (Err)
    125       return errorToErrorCode(std::move(Err));
    126   }
    127 
    128   if (Buffers.empty())
    129     return make_error_code(errc::no_such_file_or_directory);
    130   return Buffers;
    131 }
    132 
    133 ErrorOr<std::vector<MemoryBufferRef>>
    134 BinaryHolder::MapArchiveAndGetMemberBuffers(StringRef Filename,
    135                                             sys::TimeValue Timestamp) {
    136   StringRef ArchiveFilename = Filename.substr(0, Filename.find('('));
    137 
    138   auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename);
    139   if (auto Err = ErrOrBuff.getError())
    140     return Err;
    141 
    142   if (Verbose)
    143     outs() << "\topened new archive '" << ArchiveFilename << "'\n";
    144 
    145   changeBackingMemoryBuffer(std::move(*ErrOrBuff));
    146   std::vector<MemoryBufferRef> ArchiveBuffers;
    147   auto ErrOrFat = object::MachOUniversalBinary::create(
    148       CurrentMemoryBuffer->getMemBufferRef());
    149   if (!ErrOrFat) {
    150     consumeError(ErrOrFat.takeError());
    151     // Not a fat binary must be a standard one.
    152     ArchiveBuffers.push_back(CurrentMemoryBuffer->getMemBufferRef());
    153   } else {
    154     CurrentFatBinary = std::move(*ErrOrFat);
    155     CurrentFatBinaryName = ArchiveFilename;
    156     ArchiveBuffers = getMachOFatMemoryBuffers(
    157         CurrentFatBinaryName, *CurrentMemoryBuffer, *CurrentFatBinary);
    158   }
    159 
    160   for (auto MemRef : ArchiveBuffers) {
    161     auto ErrOrArchive = object::Archive::create(MemRef);
    162     if (!ErrOrArchive)
    163       return errorToErrorCode(ErrOrArchive.takeError());
    164     CurrentArchives.push_back(std::move(*ErrOrArchive));
    165   }
    166   return GetArchiveMemberBuffers(Filename, Timestamp);
    167 }
    168 
    169 ErrorOr<const object::ObjectFile &>
    170 BinaryHolder::getObjfileForArch(const Triple &T) {
    171   for (const auto &Obj : CurrentObjectFiles) {
    172     if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
    173       if (MachO->getArchTriple().str() == T.str())
    174         return *MachO;
    175     } else if (Obj->getArch() == T.getArch())
    176       return *Obj;
    177   }
    178 
    179   return make_error_code(object::object_error::arch_not_found);
    180 }
    181 
    182 ErrorOr<std::vector<const object::ObjectFile *>>
    183 BinaryHolder::GetObjectFiles(StringRef Filename, sys::TimeValue Timestamp) {
    184   auto ErrOrMemBufferRefs = GetMemoryBuffersForFile(Filename, Timestamp);
    185   if (auto Err = ErrOrMemBufferRefs.getError())
    186     return Err;
    187 
    188   std::vector<const object::ObjectFile *> Objects;
    189   Objects.reserve(ErrOrMemBufferRefs->size());
    190 
    191   CurrentObjectFiles.clear();
    192   for (auto MemBuf : *ErrOrMemBufferRefs) {
    193     auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemBuf);
    194     if (!ErrOrObjectFile)
    195       return errorToErrorCode(ErrOrObjectFile.takeError());
    196 
    197     Objects.push_back(ErrOrObjectFile->get());
    198     CurrentObjectFiles.push_back(std::move(*ErrOrObjectFile));
    199   }
    200 
    201   return std::move(Objects);
    202 }
    203 }
    204 }
    205