Frederic Riss | 5a0743e | 2015-01-05 21:29:28 +0000 | [diff] [blame] | 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" |
Frederic Riss | 15562966 | 2015-07-24 06:41:04 +0000 | [diff] [blame] | 16 | #include "llvm/Object/MachO.h" |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 17 | #include "llvm/Support/WithColor.h" |
Frederic Riss | 5a0743e | 2015-01-05 21:29:28 +0000 | [diff] [blame] | 18 | #include "llvm/Support/raw_ostream.h" |
| 19 | |
| 20 | namespace llvm { |
| 21 | namespace dsymutil { |
| 22 | |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 23 | static std::pair<StringRef, StringRef> |
| 24 | getArchiveAndObjectName(StringRef Filename) { |
| 25 | StringRef Archive = Filename.substr(0, Filename.find('(')); |
| 26 | StringRef Object = Filename.substr(Archive.size() + 1).drop_back(); |
| 27 | return {Archive, Object}; |
| 28 | } |
| 29 | |
| 30 | static bool isArchive(StringRef Filename) { return Filename.endswith(")"); } |
| 31 | |
Frederic Riss | fa7f7bd | 2015-07-24 06:41:11 +0000 | [diff] [blame] | 32 | static std::vector<MemoryBufferRef> |
| 33 | getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem, |
| 34 | object::MachOUniversalBinary &Fat) { |
| 35 | std::vector<MemoryBufferRef> Buffers; |
| 36 | StringRef FatData = Fat.getData(); |
| 37 | for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End; |
| 38 | ++It) { |
| 39 | StringRef ObjData = FatData.substr(It->getOffset(), It->getSize()); |
| 40 | Buffers.emplace_back(ObjData, Filename); |
| 41 | } |
| 42 | return Buffers; |
| 43 | } |
| 44 | |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 45 | Error BinaryHolder::ArchiveEntry::load(StringRef Filename, |
| 46 | TimestampTy Timestamp, bool Verbose) { |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 47 | StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first; |
| 48 | |
| 49 | // Try to load archive and force it to be memory mapped. |
| 50 | auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename, -1, false); |
| 51 | if (auto Err = ErrOrBuff.getError()) |
| 52 | return errorCodeToError(Err); |
| 53 | |
Jonas Devlieghere | d36457d | 2018-06-29 17:11:34 +0000 | [diff] [blame] | 54 | MemBuffer = std::move(*ErrOrBuff); |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 55 | |
| 56 | if (Verbose) |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 57 | WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n"; |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 58 | |
| 59 | // Load one or more archive buffers, depending on whether we're dealing with |
| 60 | // a fat binary. |
| 61 | std::vector<MemoryBufferRef> ArchiveBuffers; |
| 62 | |
| 63 | auto ErrOrFat = |
Jonas Devlieghere | d36457d | 2018-06-29 17:11:34 +0000 | [diff] [blame] | 64 | object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef()); |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 65 | if (!ErrOrFat) { |
| 66 | consumeError(ErrOrFat.takeError()); |
Jonas Devlieghere | d36457d | 2018-06-29 17:11:34 +0000 | [diff] [blame] | 67 | ArchiveBuffers.push_back(MemBuffer->getMemBufferRef()); |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 68 | } else { |
| 69 | FatBinary = std::move(*ErrOrFat); |
| 70 | FatBinaryName = ArchiveFilename; |
| 71 | ArchiveBuffers = |
Jonas Devlieghere | d36457d | 2018-06-29 17:11:34 +0000 | [diff] [blame] | 72 | getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary); |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 73 | } |
| 74 | |
| 75 | // Finally, try to load the archives. |
| 76 | Archives.reserve(ArchiveBuffers.size()); |
| 77 | for (auto MemRef : ArchiveBuffers) { |
| 78 | auto ErrOrArchive = object::Archive::create(MemRef); |
| 79 | if (!ErrOrArchive) |
| 80 | return ErrOrArchive.takeError(); |
| 81 | Archives.push_back(std::move(*ErrOrArchive)); |
| 82 | } |
| 83 | |
| 84 | return Error::success(); |
| 85 | } |
| 86 | |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 87 | Error BinaryHolder::ObjectEntry::load(StringRef Filename, bool Verbose) { |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 88 | // Try to load regular binary and force it to be memory mapped. |
| 89 | auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(Filename, -1, false); |
| 90 | if (auto Err = ErrOrBuff.getError()) |
| 91 | return errorCodeToError(Err); |
| 92 | |
Jonas Devlieghere | d36457d | 2018-06-29 17:11:34 +0000 | [diff] [blame] | 93 | MemBuffer = std::move(*ErrOrBuff); |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 94 | |
| 95 | if (Verbose) |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 96 | WithColor::note() << "loaded object.\n"; |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 97 | |
| 98 | // Load one or more object buffers, depending on whether we're dealing with a |
| 99 | // fat binary. |
| 100 | std::vector<MemoryBufferRef> ObjectBuffers; |
| 101 | |
| 102 | auto ErrOrFat = |
Jonas Devlieghere | d36457d | 2018-06-29 17:11:34 +0000 | [diff] [blame] | 103 | object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef()); |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 104 | if (!ErrOrFat) { |
| 105 | consumeError(ErrOrFat.takeError()); |
Jonas Devlieghere | d36457d | 2018-06-29 17:11:34 +0000 | [diff] [blame] | 106 | ObjectBuffers.push_back(MemBuffer->getMemBufferRef()); |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 107 | } else { |
| 108 | FatBinary = std::move(*ErrOrFat); |
| 109 | FatBinaryName = Filename; |
| 110 | ObjectBuffers = |
Jonas Devlieghere | d36457d | 2018-06-29 17:11:34 +0000 | [diff] [blame] | 111 | getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary); |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 112 | } |
| 113 | |
| 114 | Objects.reserve(ObjectBuffers.size()); |
| 115 | for (auto MemRef : ObjectBuffers) { |
| 116 | auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef); |
| 117 | if (!ErrOrObjectFile) |
| 118 | return ErrOrObjectFile.takeError(); |
| 119 | Objects.push_back(std::move(*ErrOrObjectFile)); |
| 120 | } |
| 121 | |
| 122 | return Error::success(); |
| 123 | } |
| 124 | |
| 125 | std::vector<const object::ObjectFile *> |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 126 | BinaryHolder::ObjectEntry::getObjects() const { |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 127 | std::vector<const object::ObjectFile *> Result; |
| 128 | Result.reserve(Objects.size()); |
| 129 | for (auto &Object : Objects) { |
| 130 | Result.push_back(Object.get()); |
| 131 | } |
| 132 | return Result; |
| 133 | } |
| 134 | Expected<const object::ObjectFile &> |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 135 | BinaryHolder::ObjectEntry::getObject(const Triple &T) const { |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 136 | for (const auto &Obj : Objects) { |
| 137 | if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) { |
| 138 | if (MachO->getArchTriple().str() == T.str()) |
| 139 | return *MachO; |
| 140 | } else if (Obj->getArch() == T.getArch()) |
| 141 | return *Obj; |
| 142 | } |
| 143 | return errorCodeToError(object::object_error::arch_not_found); |
| 144 | } |
| 145 | |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 146 | Expected<const BinaryHolder::ObjectEntry &> |
| 147 | BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename, |
| 148 | TimestampTy Timestamp, |
| 149 | bool Verbose) { |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 150 | StringRef ArchiveFilename; |
| 151 | StringRef ObjectFilename; |
| 152 | std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename); |
| 153 | |
| 154 | // Try the cache first. |
| 155 | KeyTy Key = {ObjectFilename, Timestamp}; |
| 156 | |
| 157 | { |
| 158 | std::lock_guard<std::mutex> Lock(MemberCacheMutex); |
| 159 | if (MemberCache.count(Key)) |
| 160 | return MemberCache[Key]; |
| 161 | } |
| 162 | |
| 163 | // Create a new ObjectEntry, but don't add it to the cache yet. Loading of |
| 164 | // the archive members might fail and we don't want to lock the whole archive |
| 165 | // during this operation. |
| 166 | ObjectEntry OE; |
| 167 | |
| 168 | for (const auto &Archive : Archives) { |
| 169 | Error Err = Error::success(); |
| 170 | for (auto Child : Archive->children(Err)) { |
| 171 | if (auto NameOrErr = Child.getName()) { |
| 172 | if (*NameOrErr == ObjectFilename) { |
| 173 | auto ModTimeOrErr = Child.getLastModified(); |
| 174 | if (!ModTimeOrErr) |
| 175 | return ModTimeOrErr.takeError(); |
| 176 | |
| 177 | if (Timestamp != sys::TimePoint<>() && |
| 178 | Timestamp != ModTimeOrErr.get()) { |
| 179 | if (Verbose) |
| 180 | WithColor::warning() << "member has timestamp mismatch.\n"; |
| 181 | continue; |
| 182 | } |
| 183 | |
| 184 | if (Verbose) |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 185 | WithColor::note() << "found member in archive.\n"; |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 186 | |
| 187 | auto ErrOrMem = Child.getMemoryBufferRef(); |
| 188 | if (!ErrOrMem) |
| 189 | return ErrOrMem.takeError(); |
| 190 | |
| 191 | auto ErrOrObjectFile = |
| 192 | object::ObjectFile::createObjectFile(*ErrOrMem); |
| 193 | if (!ErrOrObjectFile) |
| 194 | return ErrOrObjectFile.takeError(); |
| 195 | |
| 196 | OE.Objects.push_back(std::move(*ErrOrObjectFile)); |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | if (Err) |
| 201 | return std::move(Err); |
| 202 | } |
| 203 | |
| 204 | if (OE.Objects.empty()) |
| 205 | return errorCodeToError(errc::no_such_file_or_directory); |
| 206 | |
| 207 | std::lock_guard<std::mutex> Lock(MemberCacheMutex); |
| 208 | MemberCache.try_emplace(Key, std::move(OE)); |
| 209 | return MemberCache[Key]; |
| 210 | } |
| 211 | |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 212 | Expected<const BinaryHolder::ObjectEntry &> |
| 213 | BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) { |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 214 | if (Verbose) |
| 215 | WithColor::note() << "trying to open '" << Filename << "'\n"; |
| 216 | |
| 217 | // If this is an archive, we might have either the object or the archive |
| 218 | // cached. In this case we can load it without accessing the file system. |
| 219 | if (isArchive(Filename)) { |
| 220 | StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first; |
| 221 | std::lock_guard<std::mutex> Lock(ArchiveCacheMutex); |
| 222 | if (ArchiveCache.count(ArchiveFilename)) { |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 223 | return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp, |
| 224 | Verbose); |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 225 | } else { |
| 226 | ArchiveEntry &AE = ArchiveCache[ArchiveFilename]; |
| 227 | auto Err = AE.load(Filename, Timestamp, Verbose); |
| 228 | if (Err) { |
| 229 | ArchiveCache.erase(ArchiveFilename); |
| 230 | // Don't return the error here: maybe the file wasn't an archive. |
| 231 | llvm::consumeError(std::move(Err)); |
| 232 | } else { |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 233 | return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp, |
| 234 | Verbose); |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 235 | } |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | // If this is an object, we might have it cached. If not we'll have to load |
| 240 | // it from the file system and cache it now. |
| 241 | std::lock_guard<std::mutex> Lock(ObjectCacheMutex); |
| 242 | if (!ObjectCache.count(Filename)) { |
| 243 | ObjectEntry &OE = ObjectCache[Filename]; |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 244 | auto Err = OE.load(Filename, Verbose); |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 245 | if (Err) { |
| 246 | ObjectCache.erase(Filename); |
| 247 | return std::move(Err); |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | return ObjectCache[Filename]; |
| 252 | } |
| 253 | |
Jonas Devlieghere | 9f33bbfe | 2018-06-29 16:51:52 +0000 | [diff] [blame] | 254 | void BinaryHolder::clear() { |
Jonas Devlieghere | 4d3e6c1 | 2018-06-29 16:50:41 +0000 | [diff] [blame] | 255 | std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex); |
| 256 | std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex); |
| 257 | ArchiveCache.clear(); |
| 258 | ObjectCache.clear(); |
| 259 | } |
| 260 | |
Jonas Devlieghere | 3ad0c5a | 2018-02-22 11:32:51 +0000 | [diff] [blame] | 261 | } // namespace dsymutil |
| 262 | } // namespace llvm |