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
.ends_with(")"); }
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 BinaryHolder::BinaryHolder(IntrusiveRefCntPtr
<vfs::FileSystem
> VFS
,
45 BinaryHolder::Options Opts
)
46 : VFS(VFS
), Opts(Opts
) {}
48 Error
BinaryHolder::ArchiveEntry::load(IntrusiveRefCntPtr
<vfs::FileSystem
> VFS
,
50 TimestampTy Timestamp
, Options Opts
) {
51 StringRef ArchiveFilename
= getArchiveAndObjectName(Filename
).first
;
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
);
60 MemBuffer
= std::move(*ErrOrBuff
);
63 WithColor::note() << "loaded archive '" << ArchiveFilename
<< "'\n";
65 // Load one or more archive buffers, depending on whether we're dealing with
67 std::vector
<MemoryBufferRef
> ArchiveBuffers
;
70 object::MachOUniversalBinary::create(MemBuffer
->getMemBufferRef());
72 consumeError(ErrOrFat
.takeError());
73 ArchiveBuffers
.push_back(MemBuffer
->getMemBufferRef());
75 FatBinary
= std::move(*ErrOrFat
);
76 FatBinaryName
= std::string(ArchiveFilename
);
78 getMachOFatMemoryBuffers(FatBinaryName
, *MemBuffer
, *FatBinary
);
81 // Finally, try to load the archives.
82 Archives
.reserve(ArchiveBuffers
.size());
83 for (auto MemRef
: ArchiveBuffers
) {
84 auto ErrOrArchive
= object::Archive::create(MemRef
);
86 return ErrOrArchive
.takeError();
87 Archives
.push_back(std::move(*ErrOrArchive
));
90 return Error::success();
93 Error
BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr
<vfs::FileSystem
> VFS
,
94 StringRef Filename
, TimestampTy Timestamp
,
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
);
103 if (Opts
.Warn
&& Filename
!= "-" && Timestamp
!= sys::TimePoint
<>()) {
104 llvm::ErrorOr
<vfs::Status
> Stat
= VFS
->status(Filename
);
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";
115 MemBuffer
= std::move(*ErrOrBuff
);
118 WithColor::note() << "loaded object.\n";
120 // Load one or more object buffers, depending on whether we're dealing with a
122 std::vector
<MemoryBufferRef
> ObjectBuffers
;
125 object::MachOUniversalBinary::create(MemBuffer
->getMemBufferRef());
127 consumeError(ErrOrFat
.takeError());
128 ObjectBuffers
.push_back(MemBuffer
->getMemBufferRef());
130 FatBinary
= std::move(*ErrOrFat
);
131 FatBinaryName
= std::string(Filename
);
133 getMachOFatMemoryBuffers(FatBinaryName
, *MemBuffer
, *FatBinary
);
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
));
144 return Error::success();
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());
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())
162 } else if (Obj
->getArch() == T
.getArch())
165 return errorCodeToError(object::object_error::arch_not_found
);
168 Expected
<const BinaryHolder::ObjectEntry
&>
169 BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename
,
170 TimestampTy Timestamp
,
172 StringRef ArchiveFilename
;
173 StringRef ObjectFilename
;
174 std::tie(ArchiveFilename
, ObjectFilename
) = getArchiveAndObjectName(Filename
);
175 KeyTy Key
= {ObjectFilename
, Timestamp
};
177 // Try the cache first.
178 std::lock_guard
<std::mutex
> Lock(MemberCacheMutex
);
179 if (MemberCache
.count(Key
))
180 return *MemberCache
[Key
];
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
>();
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();
194 return ModTimeOrErr
.takeError();
196 if (Timestamp
!= sys::TimePoint
<>() &&
197 Timestamp
!= std::chrono::time_point_cast
<std::chrono::seconds
>(
198 ModTimeOrErr
.get())) {
202 << ": timestamp mismatch between archive member ("
203 << ModTimeOrErr
.get() << ") and debug map (" << Timestamp
209 WithColor::note() << "found member in archive.\n";
211 auto ErrOrMem
= Child
.getMemoryBufferRef();
213 return ErrOrMem
.takeError();
215 auto ErrOrObjectFile
=
216 object::ObjectFile::createObjectFile(*ErrOrMem
);
217 if (!ErrOrObjectFile
)
218 return ErrOrObjectFile
.takeError();
220 OE
->Objects
.push_back(std::move(*ErrOrObjectFile
));
225 return std::move(Err
);
228 if (OE
->Objects
.empty())
229 return errorCodeToError(errc::no_such_file_or_directory
);
231 MemberCache
[Key
] = std::move(OE
);
232 return *MemberCache
[Key
];
235 Expected
<const BinaryHolder::ObjectEntry
&>
236 BinaryHolder::getObjectEntry(StringRef Filename
, TimestampTy Timestamp
) {
238 WithColor::note() << "trying to open '" << Filename
<< "'\n";
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
,
250 auto AE
= std::make_unique
<ArchiveEntry
>();
251 auto Err
= AE
->load(VFS
, Filename
, Timestamp
, Opts
);
253 // Don't return the error here: maybe the file wasn't an archive.
254 llvm::consumeError(std::move(Err
));
256 ArchiveCache
[ArchiveFilename
] = std::move(AE
);
257 return ArchiveCache
[ArchiveFilename
]->getObjectEntry(Filename
,
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
);
271 return std::move(Err
);
272 ObjectCache
[Filename
] = std::move(OE
);
275 return *ObjectCache
[Filename
];
278 void BinaryHolder::clear() {
279 std::lock_guard
<std::mutex
> ArchiveLock(ArchiveCacheMutex
);
280 std::lock_guard
<std::mutex
> ObjectLock(ObjectCacheMutex
);
281 ArchiveCache
.clear();
285 void BinaryHolder::eraseObjectEntry(StringRef Filename
) {
287 WithColor::note() << "erasing '" << Filename
<< "' from cache\n";
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
);
298 std::lock_guard
<std::mutex
> Lock(ObjectCacheMutex
);
299 ObjectRefCounter
[Filename
]--;
300 if (ObjectRefCounter
[Filename
] == 0)
301 ObjectCache
.erase(Filename
);
304 } // namespace dsymutil