comparison tools/dsymutil/BinaryHolder.cpp @ 171:66f3bfe93da9

git version 2c4ca6832fa6b306ee6a7010bfb80a3f2596f824
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 11:07:02 +0900
parents
children
comparison
equal deleted inserted replaced
150:1d019706d866 171:66f3bfe93da9
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.endswith(")"); }
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 Error BinaryHolder::ArchiveEntry::load(StringRef Filename,
45 TimestampTy Timestamp, bool Verbose) {
46 StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
47
48 // Try to load archive and force it to be memory mapped.
49 auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename, -1, false);
50 if (auto Err = ErrOrBuff.getError())
51 return errorCodeToError(Err);
52
53 MemBuffer = std::move(*ErrOrBuff);
54
55 if (Verbose)
56 WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n";
57
58 // Load one or more archive buffers, depending on whether we're dealing with
59 // a fat binary.
60 std::vector<MemoryBufferRef> ArchiveBuffers;
61
62 auto ErrOrFat =
63 object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
64 if (!ErrOrFat) {
65 consumeError(ErrOrFat.takeError());
66 ArchiveBuffers.push_back(MemBuffer->getMemBufferRef());
67 } else {
68 FatBinary = std::move(*ErrOrFat);
69 FatBinaryName = ArchiveFilename;
70 ArchiveBuffers =
71 getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
72 }
73
74 // Finally, try to load the archives.
75 Archives.reserve(ArchiveBuffers.size());
76 for (auto MemRef : ArchiveBuffers) {
77 auto ErrOrArchive = object::Archive::create(MemRef);
78 if (!ErrOrArchive)
79 return ErrOrArchive.takeError();
80 Archives.push_back(std::move(*ErrOrArchive));
81 }
82
83 return Error::success();
84 }
85
86 Error BinaryHolder::ObjectEntry::load(StringRef Filename, bool Verbose) {
87 // Try to load regular binary and force it to be memory mapped.
88 auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(Filename, -1, false);
89 if (auto Err = ErrOrBuff.getError())
90 return errorCodeToError(Err);
91
92 MemBuffer = std::move(*ErrOrBuff);
93
94 if (Verbose)
95 WithColor::note() << "loaded object.\n";
96
97 // Load one or more object buffers, depending on whether we're dealing with a
98 // fat binary.
99 std::vector<MemoryBufferRef> ObjectBuffers;
100
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 = Filename;
109 ObjectBuffers =
110 getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
111 }
112
113 Objects.reserve(ObjectBuffers.size());
114 for (auto MemRef : ObjectBuffers) {
115 auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef);
116 if (!ErrOrObjectFile)
117 return ErrOrObjectFile.takeError();
118 Objects.push_back(std::move(*ErrOrObjectFile));
119 }
120
121 return Error::success();
122 }
123
124 std::vector<const object::ObjectFile *>
125 BinaryHolder::ObjectEntry::getObjects() const {
126 std::vector<const object::ObjectFile *> Result;
127 Result.reserve(Objects.size());
128 for (auto &Object : Objects) {
129 Result.push_back(Object.get());
130 }
131 return Result;
132 }
133 Expected<const object::ObjectFile &>
134 BinaryHolder::ObjectEntry::getObject(const Triple &T) const {
135 for (const auto &Obj : Objects) {
136 if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
137 if (MachO->getArchTriple().str() == T.str())
138 return *MachO;
139 } else if (Obj->getArch() == T.getArch())
140 return *Obj;
141 }
142 return errorCodeToError(object::object_error::arch_not_found);
143 }
144
145 Expected<const BinaryHolder::ObjectEntry &>
146 BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename,
147 TimestampTy Timestamp,
148 bool Verbose) {
149 StringRef ArchiveFilename;
150 StringRef ObjectFilename;
151 std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename);
152
153 // Try the cache first.
154 KeyTy Key = {ObjectFilename, Timestamp};
155
156 {
157 std::lock_guard<std::mutex> Lock(MemberCacheMutex);
158 if (MemberCache.count(Key))
159 return MemberCache[Key];
160 }
161
162 // Create a new ObjectEntry, but don't add it to the cache yet. Loading of
163 // the archive members might fail and we don't want to lock the whole archive
164 // during this operation.
165 ObjectEntry OE;
166
167 for (const auto &Archive : Archives) {
168 Error Err = Error::success();
169 for (auto Child : Archive->children(Err)) {
170 if (auto NameOrErr = Child.getName()) {
171 if (*NameOrErr == ObjectFilename) {
172 auto ModTimeOrErr = Child.getLastModified();
173 if (!ModTimeOrErr)
174 return ModTimeOrErr.takeError();
175
176 if (Timestamp != sys::TimePoint<>() &&
177 Timestamp != ModTimeOrErr.get()) {
178 if (Verbose)
179 WithColor::warning() << "member has timestamp mismatch.\n";
180 continue;
181 }
182
183 if (Verbose)
184 WithColor::note() << "found member in archive.\n";
185
186 auto ErrOrMem = Child.getMemoryBufferRef();
187 if (!ErrOrMem)
188 return ErrOrMem.takeError();
189
190 auto ErrOrObjectFile =
191 object::ObjectFile::createObjectFile(*ErrOrMem);
192 if (!ErrOrObjectFile)
193 return ErrOrObjectFile.takeError();
194
195 OE.Objects.push_back(std::move(*ErrOrObjectFile));
196 }
197 }
198 }
199 if (Err)
200 return std::move(Err);
201 }
202
203 if (OE.Objects.empty())
204 return errorCodeToError(errc::no_such_file_or_directory);
205
206 std::lock_guard<std::mutex> Lock(MemberCacheMutex);
207 MemberCache.try_emplace(Key, std::move(OE));
208 return MemberCache[Key];
209 }
210
211 Expected<const BinaryHolder::ObjectEntry &>
212 BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) {
213 if (Verbose)
214 WithColor::note() << "trying to open '" << Filename << "'\n";
215
216 // If this is an archive, we might have either the object or the archive
217 // cached. In this case we can load it without accessing the file system.
218 if (isArchive(Filename)) {
219 StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
220 std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
221 if (ArchiveCache.count(ArchiveFilename)) {
222 return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp,
223 Verbose);
224 } else {
225 ArchiveEntry &AE = ArchiveCache[ArchiveFilename];
226 auto Err = AE.load(Filename, Timestamp, Verbose);
227 if (Err) {
228 ArchiveCache.erase(ArchiveFilename);
229 // Don't return the error here: maybe the file wasn't an archive.
230 llvm::consumeError(std::move(Err));
231 } else {
232 return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp,
233 Verbose);
234 }
235 }
236 }
237
238 // If this is an object, we might have it cached. If not we'll have to load
239 // it from the file system and cache it now.
240 std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
241 if (!ObjectCache.count(Filename)) {
242 ObjectEntry &OE = ObjectCache[Filename];
243 auto Err = OE.load(Filename, Verbose);
244 if (Err) {
245 ObjectCache.erase(Filename);
246 return std::move(Err);
247 }
248 }
249
250 return ObjectCache[Filename];
251 }
252
253 void BinaryHolder::clear() {
254 std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex);
255 std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex);
256 ArchiveCache.clear();
257 ObjectCache.clear();
258 }
259
260 } // namespace dsymutil
261 } // namespace llvm