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>> BinaryHolder::GetMemoryBuffersForFile( 45 StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) { 46 if (Verbose) 47 outs() << "trying to open '" << Filename << "'\n"; 48 49 // Try that first as it doesn't involve any filesystem access. 50 if (auto ErrOrArchiveMembers = GetArchiveMemberBuffers(Filename, Timestamp)) 51 return *ErrOrArchiveMembers; 52 53 // If the name ends with a closing paren, there is a huge chance 54 // it is an archive member specification. 55 if (Filename.endswith(")")) 56 if (auto ErrOrArchiveMembers = 57 MapArchiveAndGetMemberBuffers(Filename, Timestamp)) 58 return *ErrOrArchiveMembers; 59 60 // Otherwise, just try opening a standard file. If this is an 61 // archive member specifiaction and any of the above didn't handle it 62 // (either because the archive is not there anymore, or because the 63 // archive doesn't contain the requested member), this will still 64 // provide a sensible error message. 65 auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(Filename); 66 if (auto Err = ErrOrFile.getError()) 67 return Err; 68 69 changeBackingMemoryBuffer(std::move(*ErrOrFile)); 70 if (Verbose) 71 outs() << "\tloaded file.\n"; 72 73 auto ErrOrFat = object::MachOUniversalBinary::create( 74 CurrentMemoryBuffer->getMemBufferRef()); 75 if (!ErrOrFat) { 76 consumeError(ErrOrFat.takeError()); 77 // Not a fat binary must be a standard one. Return a one element vector. 78 return std::vector<MemoryBufferRef>{CurrentMemoryBuffer->getMemBufferRef()}; 79 } 80 81 CurrentFatBinary = std::move(*ErrOrFat); 82 CurrentFatBinaryName = Filename; 83 return getMachOFatMemoryBuffers(CurrentFatBinaryName, *CurrentMemoryBuffer, 84 *CurrentFatBinary); 85 } 86 87 ErrorOr<std::vector<MemoryBufferRef>> BinaryHolder::GetArchiveMemberBuffers( 88 StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) { 89 if (CurrentArchives.empty()) 90 return make_error_code(errc::no_such_file_or_directory); 91 92 StringRef CurArchiveName = CurrentArchives.front()->getFileName(); 93 if (!Filename.startswith(Twine(CurArchiveName, "(").str())) 94 return make_error_code(errc::no_such_file_or_directory); 95 96 // Remove the archive name and the parens around the archive member name. 97 Filename = Filename.substr(CurArchiveName.size() + 1).drop_back(); 98 99 std::vector<MemoryBufferRef> Buffers; 100 Buffers.reserve(CurrentArchives.size()); 101 102 for (const auto &CurrentArchive : CurrentArchives) { 103 Error Err = Error::success(); 104 for (auto Child : CurrentArchive->children(Err)) { 105 if (auto NameOrErr = Child.getName()) { 106 if (*NameOrErr == Filename) { 107 auto ModTimeOrErr = Child.getLastModified(); 108 if (!ModTimeOrErr) 109 return errorToErrorCode(ModTimeOrErr.takeError()); 110 if (Timestamp != sys::TimePoint<>() && 111 Timestamp != ModTimeOrErr.get()) { 112 if (Verbose) 113 outs() << "\tmember had timestamp mismatch.\n"; 114 continue; 115 } 116 if (Verbose) 117 outs() << "\tfound member in current archive.\n"; 118 auto ErrOrMem = Child.getMemoryBufferRef(); 119 if (!ErrOrMem) 120 return errorToErrorCode(ErrOrMem.takeError()); 121 Buffers.push_back(*ErrOrMem); 122 } 123 } 124 } 125 if (Err) 126 return errorToErrorCode(std::move(Err)); 127 } 128 129 if (Buffers.empty()) 130 return make_error_code(errc::no_such_file_or_directory); 131 return Buffers; 132 } 133 134 ErrorOr<std::vector<MemoryBufferRef>> 135 BinaryHolder::MapArchiveAndGetMemberBuffers( 136 StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) { 137 StringRef ArchiveFilename = Filename.substr(0, Filename.find('(')); 138 139 auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename); 140 if (auto Err = ErrOrBuff.getError()) 141 return Err; 142 143 if (Verbose) 144 outs() << "\topened new archive '" << ArchiveFilename << "'\n"; 145 146 changeBackingMemoryBuffer(std::move(*ErrOrBuff)); 147 std::vector<MemoryBufferRef> ArchiveBuffers; 148 auto ErrOrFat = object::MachOUniversalBinary::create( 149 CurrentMemoryBuffer->getMemBufferRef()); 150 if (!ErrOrFat) { 151 consumeError(ErrOrFat.takeError()); 152 // Not a fat binary must be a standard one. 153 ArchiveBuffers.push_back(CurrentMemoryBuffer->getMemBufferRef()); 154 } else { 155 CurrentFatBinary = std::move(*ErrOrFat); 156 CurrentFatBinaryName = ArchiveFilename; 157 ArchiveBuffers = getMachOFatMemoryBuffers( 158 CurrentFatBinaryName, *CurrentMemoryBuffer, *CurrentFatBinary); 159 } 160 161 for (auto MemRef : ArchiveBuffers) { 162 auto ErrOrArchive = object::Archive::create(MemRef); 163 if (!ErrOrArchive) 164 return errorToErrorCode(ErrOrArchive.takeError()); 165 CurrentArchives.push_back(std::move(*ErrOrArchive)); 166 } 167 return GetArchiveMemberBuffers(Filename, Timestamp); 168 } 169 170 ErrorOr<const object::ObjectFile &> 171 BinaryHolder::getObjfileForArch(const Triple &T) { 172 for (const auto &Obj : CurrentObjectFiles) { 173 if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) { 174 if (MachO->getArchTriple().str() == T.str()) 175 return *MachO; 176 } else if (Obj->getArch() == T.getArch()) 177 return *Obj; 178 } 179 180 return make_error_code(object::object_error::arch_not_found); 181 } 182 183 ErrorOr<std::vector<const object::ObjectFile *>> 184 BinaryHolder::GetObjectFiles(StringRef Filename, 185 sys::TimePoint<std::chrono::seconds> Timestamp) { 186 auto ErrOrMemBufferRefs = GetMemoryBuffersForFile(Filename, Timestamp); 187 if (auto Err = ErrOrMemBufferRefs.getError()) 188 return Err; 189 190 std::vector<const object::ObjectFile *> Objects; 191 Objects.reserve(ErrOrMemBufferRefs->size()); 192 193 CurrentObjectFiles.clear(); 194 for (auto MemBuf : *ErrOrMemBufferRefs) { 195 auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemBuf); 196 if (!ErrOrObjectFile) 197 return errorToErrorCode(ErrOrObjectFile.takeError()); 198 199 Objects.push_back(ErrOrObjectFile->get()); 200 CurrentObjectFiles.push_back(std::move(*ErrOrObjectFile)); 201 } 202 203 return std::move(Objects); 204 } 205 } 206 } 207