BinaryHolder.cpp 9.8 KB

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