1 //===-- BinaryHolder.cpp --------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This program is a utility that aims to be a dropin replacement for
12 //===----------------------------------------------------------------------===//
14 #include "BinaryHolder.h"
15 #include "llvm/Object/MachO.h"
16 #include "llvm/Support/WithColor.h"
17 #include "llvm/Support/raw_ostream.h"
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
};
29 static bool isArchive(StringRef Filename
) { return Filename
.endswith(")"); }
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
;
38 StringRef ObjData
= FatData
.substr(It
->getOffset(), It
->getSize());
39 Buffers
.emplace_back(ObjData
, Filename
);
44 Error
BinaryHolder::ArchiveEntry::load(StringRef Filename
,
45 TimestampTy Timestamp
, bool Verbose
) {
46 StringRef ArchiveFilename
= getArchiveAndObjectName(Filename
).first
;
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
);
53 MemBuffer
= std::move(*ErrOrBuff
);
56 WithColor::note() << "loaded archive '" << ArchiveFilename
<< "'\n";
58 // Load one or more archive buffers, depending on whether we're dealing with
60 std::vector
<MemoryBufferRef
> ArchiveBuffers
;
63 object::MachOUniversalBinary::create(MemBuffer
->getMemBufferRef());
65 consumeError(ErrOrFat
.takeError());
66 ArchiveBuffers
.push_back(MemBuffer
->getMemBufferRef());
68 FatBinary
= std::move(*ErrOrFat
);
69 FatBinaryName
= ArchiveFilename
;
71 getMachOFatMemoryBuffers(FatBinaryName
, *MemBuffer
, *FatBinary
);
74 // Finally, try to load the archives.
75 Archives
.reserve(ArchiveBuffers
.size());
76 for (auto MemRef
: ArchiveBuffers
) {
77 auto ErrOrArchive
= object::Archive::create(MemRef
);
79 return ErrOrArchive
.takeError();
80 Archives
.push_back(std::move(*ErrOrArchive
));
83 return Error::success();
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
);
92 MemBuffer
= std::move(*ErrOrBuff
);
95 WithColor::note() << "loaded object.\n";
97 // Load one or more object buffers, depending on whether we're dealing with a
99 std::vector
<MemoryBufferRef
> ObjectBuffers
;
102 object::MachOUniversalBinary::create(MemBuffer
->getMemBufferRef());
104 consumeError(ErrOrFat
.takeError());
105 ObjectBuffers
.push_back(MemBuffer
->getMemBufferRef());
107 FatBinary
= std::move(*ErrOrFat
);
108 FatBinaryName
= Filename
;
110 getMachOFatMemoryBuffers(FatBinaryName
, *MemBuffer
, *FatBinary
);
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
));
121 return Error::success();
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());
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())
139 } else if (Obj
->getArch() == T
.getArch())
142 return errorCodeToError(object::object_error::arch_not_found
);
145 Expected
<const BinaryHolder::ObjectEntry
&>
146 BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename
,
147 TimestampTy Timestamp
,
149 StringRef ArchiveFilename
;
150 StringRef ObjectFilename
;
151 std::tie(ArchiveFilename
, ObjectFilename
) = getArchiveAndObjectName(Filename
);
153 // Try the cache first.
154 KeyTy Key
= {ObjectFilename
, Timestamp
};
157 std::lock_guard
<std::mutex
> Lock(MemberCacheMutex
);
158 if (MemberCache
.count(Key
))
159 return MemberCache
[Key
];
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.
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();
174 return ModTimeOrErr
.takeError();
176 if (Timestamp
!= sys::TimePoint
<>() &&
177 Timestamp
!= ModTimeOrErr
.get()) {
179 WithColor::warning() << "member has timestamp mismatch.\n";
184 WithColor::note() << "found member in archive.\n";
186 auto ErrOrMem
= Child
.getMemoryBufferRef();
188 return ErrOrMem
.takeError();
190 auto ErrOrObjectFile
=
191 object::ObjectFile::createObjectFile(*ErrOrMem
);
192 if (!ErrOrObjectFile
)
193 return ErrOrObjectFile
.takeError();
195 OE
.Objects
.push_back(std::move(*ErrOrObjectFile
));
200 return std::move(Err
);
203 if (OE
.Objects
.empty())
204 return errorCodeToError(errc::no_such_file_or_directory
);
206 std::lock_guard
<std::mutex
> Lock(MemberCacheMutex
);
207 MemberCache
.try_emplace(Key
, std::move(OE
));
208 return MemberCache
[Key
];
211 Expected
<const BinaryHolder::ObjectEntry
&>
212 BinaryHolder::getObjectEntry(StringRef Filename
, TimestampTy Timestamp
) {
214 WithColor::note() << "trying to open '" << Filename
<< "'\n";
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
,
225 ArchiveEntry
&AE
= ArchiveCache
[ArchiveFilename
];
226 auto Err
= AE
.load(Filename
, Timestamp
, Verbose
);
228 ArchiveCache
.erase(ArchiveFilename
);
229 // Don't return the error here: maybe the file wasn't an archive.
230 llvm::consumeError(std::move(Err
));
232 return ArchiveCache
[ArchiveFilename
].getObjectEntry(Filename
, Timestamp
,
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
);
245 ObjectCache
.erase(Filename
);
246 return std::move(Err
);
250 return ObjectCache
[Filename
];
253 void BinaryHolder::clear() {
254 std::lock_guard
<std::mutex
> ArchiveLock(ArchiveCacheMutex
);
255 std::lock_guard
<std::mutex
> ObjectLock(ObjectCacheMutex
);
256 ArchiveCache
.clear();
260 } // namespace dsymutil