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