Mercurial > hg > CbC > CbC_llvm
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 |