Skip to content

Commit 6fa8657

Browse files
authored
[ORC] Refactor visit-members in StaticLibraryDefinitionGenerator. (#141546)
This refactor was motivated by two bugs identified in out-of-tree builds: 1. Some implementations of the VisitMembersFunction type (often used to implement special loading semantics, e.g. -all_load or -ObjC) were assuming that buffers for archive members were null-terminated, which they are not in general. This was triggering occasional assertions. 2. Archives may include multiple members with the same file name, e.g. when constructed by appending files with the same name: % llvm-ar crs libfoo.a foo.o % llvm-ar q libfoo.a foo.o % llvm-ar t libfoo.a foo.o foo.o While confusing, these members may be safe to link (provided that they're individually valid and don't define duplicate symbols). In ORC however, the archive member name may be used to construct an ORC initializer symbol, which must also be unique. In that case the duplicate member names lead to a duplicate definition error even if the members define unrelated symbols. In addition to these bugs, StaticLibraryDefinitionGenerator had grown a collection of all member buffers (ObjectFilesMap), a BumpPtrAllocator that was redundantly storing synthesized archive member names (these are copied into the MemoryBuffers created for each Object, but were never freed in the allocator), and a set of COFF-specific import files. To fix the bugs above and simplify StaticLibraryDefinitionGenerator this patch makes the following changes: 1. StaticLibraryDefinitionGenerator::VisitMembersFunction is generalized to take a reference to the containing archive, and the index of the member within the archive. It now returns an Expected<bool> indicating whether the member visited should be treated as loadable, not loadable, or as invalidating the entire archive. 2. A static StaticLibraryDefinitionGenerator::createMemberBuffer method is added which creates MemoryBuffers with unique names of the form `<archive-name>[<index>](<member-name>)`. This defers construction of member names until they're loaded, allowing the BumpPtrAllocator (with its redundant name storage) to be removed. 3. The ObjectFilesMap (symbol name -> memory-buffer-ref) is replaced with a SymbolToMemberIndexMap (symbol name -> index) which should be smaller and faster to construct. 4. The 'loadability' result from VisitMemberFunctions is now taken into consideration when building the SymbolToMemberIndexMap so that members that have already been loaded / filtered out can be skipped, and do not take up any ongoing space. 5. The COFF ImportedDynamicLibraries member is moved out into the COFFImportFileScanner utility, which can be used as a VisitMemberFunction. This fixes the bugs described above; and should lower memory consumption slightly, especially for archives with many files and / or symbol where most files are eventually loaded.
1 parent 7462da1 commit 6fa8657

File tree

18 files changed

+345
-115
lines changed

18 files changed

+345
-115
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===-------------- COFF.h - COFF format utilities --------------*- C++ -*-===//
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+
// Contains utilities for load COFF relocatable object files.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_EXECUTIONENGINE_ORC_COFF_H
14+
#define LLVM_EXECUTIONENGINE_ORC_COFF_H
15+
16+
#include "llvm/Support/Error.h"
17+
#include "llvm/Support/MemoryBuffer.h"
18+
19+
#include <set>
20+
#include <string>
21+
22+
namespace llvm {
23+
24+
namespace object {
25+
class Archive;
26+
} // namespace object
27+
28+
namespace orc {
29+
30+
class COFFImportFileScanner {
31+
public:
32+
COFFImportFileScanner(std::set<std::string> &ImportedDynamicLibraries)
33+
: ImportedDynamicLibraries(ImportedDynamicLibraries) {}
34+
Expected<bool> operator()(object::Archive &A, MemoryBufferRef MemberBuf,
35+
size_t Index) const;
36+
37+
private:
38+
std::set<std::string> &ImportedDynamicLibraries;
39+
};
40+
41+
} // namespace orc
42+
} // namespace llvm
43+
44+
#endif // LLVM_EXECUTIONENGINE_ORC_MACHO_H

llvm/include/llvm/ExecutionEngine/Orc/COFFPlatform.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class COFFPlatform : public Platform {
139139
COFFPlatform(
140140
ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD,
141141
std::unique_ptr<StaticLibraryDefinitionGenerator> OrcRuntimeGenerator,
142+
std::set<std::string> DylibsToPreload,
142143
std::unique_ptr<MemoryBuffer> OrcRuntimeArchiveBuffer,
143144
std::unique_ptr<object::Archive> OrcRuntimeArchive,
144145
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,

llvm/include/llvm/ExecutionEngine/Orc/ExecutionUtils.h

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -273,16 +273,33 @@ class StaticLibraryDefinitionGenerator : public DefinitionGenerator {
273273
unique_function<Expected<MaterializationUnit::Interface>(
274274
ExecutionSession &ES, MemoryBufferRef ObjBuffer)>;
275275

276-
/// Callback for visiting archive members at construction time.
277-
/// Con be used to pre-load archive members.
278-
using VisitMembersFunction = unique_function<Error(MemoryBufferRef)>;
276+
/// Callback for visiting archive members at construction time. Can be used
277+
/// to pre-load members.
278+
///
279+
/// Callbacks are provided with a reference to the underlying archive, a
280+
/// MemoryBufferRef covering the bytes for the given member, and the index of
281+
/// the given member.
282+
///
283+
/// Implementations should return true if the given member file should be
284+
/// loadable via the generator, false if it should not, and an Error if the
285+
/// member is malformed in a way that renders the archive itself invalid.
286+
///
287+
/// Note: Linkers typically ignore invalid files within archives, so it's
288+
/// expected that implementations will usually return `false` (i.e.
289+
/// not-loadable) for malformed buffers, and will only return an
290+
/// Error in exceptional circumstances.
291+
using VisitMembersFunction = unique_function<Expected<bool>(
292+
object::Archive &, MemoryBufferRef, size_t)>;
279293

280294
/// A VisitMembersFunction that unconditionally loads all object files from
281295
/// the archive.
282296
/// Archive members that are not valid object files will be skipped.
283297
static VisitMembersFunction loadAllObjectFileMembers(ObjectLayer &L,
284298
JITDylib &JD);
285299

300+
static std::unique_ptr<MemoryBuffer>
301+
createMemberBuffer(object::Archive &A, MemoryBufferRef BufRef, size_t Index);
302+
286303
/// Try to create a StaticLibraryDefinitionGenerator from the given path.
287304
///
288305
/// This call will succeed if the file at the given path is a static library
@@ -313,32 +330,22 @@ class StaticLibraryDefinitionGenerator : public DefinitionGenerator {
313330
VisitMembersFunction VisitMembers = VisitMembersFunction(),
314331
GetObjectFileInterface GetObjFileInterface = GetObjectFileInterface());
315332

316-
/// Returns a list of filenames of dynamic libraries that this archive has
317-
/// imported. This class does not load these libraries by itself. User is
318-
/// responsible for making sure these libraries are available to the JITDylib.
319-
const std::set<std::string> &getImportedDynamicLibraries() const {
320-
return ImportedDynamicLibraries;
321-
}
322-
323333
Error tryToGenerate(LookupState &LS, LookupKind K, JITDylib &JD,
324334
JITDylibLookupFlags JDLookupFlags,
325335
const SymbolLookupSet &Symbols) override;
326336

327337
private:
328-
StaticLibraryDefinitionGenerator(ObjectLayer &L,
329-
std::unique_ptr<MemoryBuffer> ArchiveBuffer,
330-
std::unique_ptr<object::Archive> Archive,
331-
GetObjectFileInterface GetObjFileInterface,
332-
Error &Err);
333-
Error buildObjectFilesMap();
338+
StaticLibraryDefinitionGenerator(
339+
ObjectLayer &L, std::unique_ptr<MemoryBuffer> ArchiveBuffer,
340+
std::unique_ptr<object::Archive> Archive,
341+
GetObjectFileInterface GetObjFileInterface,
342+
DenseMap<SymbolStringPtr, size_t> SymbolToMemberIndexMap);
334343

335344
ObjectLayer &L;
336345
GetObjectFileInterface GetObjFileInterface;
337-
std::set<std::string> ImportedDynamicLibraries;
338346
std::unique_ptr<MemoryBuffer> ArchiveBuffer;
339347
std::unique_ptr<object::Archive> Archive;
340-
DenseMap<SymbolStringPtr, MemoryBufferRef> ObjectFilesMap;
341-
BumpPtrAllocator ObjFileNameStorage;
348+
DenseMap<SymbolStringPtr, size_t> SymbolToMemberIndexMap;
342349
};
343350

344351
/// A utility class to create COFF dllimport GOT symbols (__imp_*) and PLT

llvm/include/llvm/ExecutionEngine/Orc/MachO.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define LLVM_EXECUTIONENGINE_ORC_MACHO_H
1515

1616
#include "llvm/ExecutionEngine/Orc/LoadLinkableFile.h"
17+
#include "llvm/Object/Archive.h"
1718
#include "llvm/Support/Error.h"
1819
#include "llvm/Support/MemoryBuffer.h"
1920
#include "llvm/TargetParser/Triple.h"
@@ -22,6 +23,7 @@ namespace llvm {
2223

2324
namespace object {
2425

26+
class Archive;
2527
class MachOUniversalBinary;
2628

2729
} // namespace object
@@ -81,7 +83,8 @@ class ForceLoadMachOArchiveMembers {
8183
ForceLoadMachOArchiveMembers(ObjectLayer &L, JITDylib &JD, bool ObjCOnly)
8284
: L(L), JD(JD), ObjCOnly(ObjCOnly) {}
8385

84-
Error operator()(MemoryBufferRef MemberBuf);
86+
Expected<bool> operator()(object::Archive &A, MemoryBufferRef MemberBuf,
87+
size_t Index);
8588

8689
private:
8790
ObjectLayer &L;

llvm/lib/ExecutionEngine/Orc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ endif()
88

99
add_llvm_component_library(LLVMOrcJIT
1010
AbsoluteSymbols.cpp
11+
COFF.cpp
1112
COFFVCRuntimeSupport.cpp
1213
COFFPlatform.cpp
1314
CompileOnDemandLayer.cpp

llvm/lib/ExecutionEngine/Orc/COFF.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===------------------ COFF.cpp - COFF format utilities ------------------===//
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+
#include "llvm/ExecutionEngine/Orc/COFF.h"
10+
#include "llvm/Object/Binary.h"
11+
12+
#define DEBUG_TYPE "orc"
13+
14+
namespace llvm::orc {
15+
16+
Expected<bool> COFFImportFileScanner::operator()(object::Archive &A,
17+
MemoryBufferRef MemberBuf,
18+
size_t Index) const {
19+
// Try to build a binary for the member.
20+
auto Bin = object::createBinary(MemberBuf);
21+
if (!Bin) {
22+
// If we can't then consume the error and return false (i.e. not loadable).
23+
consumeError(Bin.takeError());
24+
return false;
25+
}
26+
27+
// If this is a COFF import file then handle it and return false (not
28+
// loadable).
29+
if ((*Bin)->isCOFFImportFile()) {
30+
ImportedDynamicLibraries.insert((*Bin)->getFileName().str());
31+
return false;
32+
}
33+
34+
// Otherwise the member is loadable (at least as far as COFFImportFileScanner
35+
// is concerned), so return true;
36+
return true;
37+
}
38+
39+
} // namespace llvm::orc

llvm/lib/ExecutionEngine/Orc/COFFPlatform.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/ExecutionEngine/Orc/COFFPlatform.h"
10+
1011
#include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h"
12+
#include "llvm/ExecutionEngine/Orc/COFF.h"
1113
#include "llvm/ExecutionEngine/Orc/DebugUtils.h"
1214
#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"
1315
#include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h"
@@ -170,8 +172,10 @@ COFFPlatform::Create(ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD,
170172
if (!GeneratorArchive)
171173
return GeneratorArchive.takeError();
172174

175+
std::set<std::string> DylibsToPreload;
173176
auto OrcRuntimeArchiveGenerator = StaticLibraryDefinitionGenerator::Create(
174-
ObjLinkingLayer, nullptr, std::move(*GeneratorArchive));
177+
ObjLinkingLayer, nullptr, std::move(*GeneratorArchive),
178+
COFFImportFileScanner(DylibsToPreload));
175179
if (!OrcRuntimeArchiveGenerator)
176180
return OrcRuntimeArchiveGenerator.takeError();
177181

@@ -207,8 +211,9 @@ COFFPlatform::Create(ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD,
207211
Error Err = Error::success();
208212
auto P = std::unique_ptr<COFFPlatform>(new COFFPlatform(
209213
ObjLinkingLayer, PlatformJD, std::move(*OrcRuntimeArchiveGenerator),
210-
std::move(OrcRuntimeArchiveBuffer), std::move(RuntimeArchive),
211-
std::move(LoadDynLibrary), StaticVCRuntime, VCRuntimePath, Err));
214+
std::move(DylibsToPreload), std::move(OrcRuntimeArchiveBuffer),
215+
std::move(RuntimeArchive), std::move(LoadDynLibrary), StaticVCRuntime,
216+
VCRuntimePath, Err));
212217
if (Err)
213218
return std::move(Err);
214219
return std::move(P);
@@ -376,6 +381,7 @@ bool COFFPlatform::supportedTarget(const Triple &TT) {
376381
COFFPlatform::COFFPlatform(
377382
ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD,
378383
std::unique_ptr<StaticLibraryDefinitionGenerator> OrcRuntimeGenerator,
384+
std::set<std::string> DylibsToPreload,
379385
std::unique_ptr<MemoryBuffer> OrcRuntimeArchiveBuffer,
380386
std::unique_ptr<object::Archive> OrcRuntimeArchive,
381387
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,
@@ -401,10 +407,6 @@ COFFPlatform::COFFPlatform(
401407
}
402408
VCRuntimeBootstrap = std::move(*VCRT);
403409

404-
std::set<std::string> DylibsToPreload;
405-
for (auto &Lib : OrcRuntimeGenerator->getImportedDynamicLibraries())
406-
DylibsToPreload.insert(Lib);
407-
408410
auto ImportedLibs =
409411
StaticVCRuntime ? VCRuntimeBootstrap->loadStaticVCRuntime(PlatformJD)
410412
: VCRuntimeBootstrap->loadDynamicVCRuntime(PlatformJD);

llvm/lib/ExecutionEngine/Orc/COFFVCRuntimeSupport.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "llvm/ExecutionEngine/Orc/COFFVCRuntimeSupport.h"
1010

11+
#include "llvm/ExecutionEngine/Orc/COFF.h"
1112
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
1213
#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"
1314
#include "llvm/Support/VirtualFileSystem.h"
@@ -81,12 +82,14 @@ Error COFFVCRuntimeBootstrapper::loadVCRuntime(
8182
auto LoadLibrary = [&](SmallString<256> LibPath, StringRef LibName) -> Error {
8283
sys::path::append(LibPath, LibName);
8384

84-
auto G = StaticLibraryDefinitionGenerator::Load(ObjLinkingLayer,
85-
LibPath.c_str());
85+
std::set<std::string> NewImportedLibraries;
86+
auto G = StaticLibraryDefinitionGenerator::Load(
87+
ObjLinkingLayer, LibPath.c_str(),
88+
COFFImportFileScanner(NewImportedLibraries));
8689
if (!G)
8790
return G.takeError();
8891

89-
llvm::append_range(ImportedLibraries, (*G)->getImportedDynamicLibraries());
92+
llvm::append_range(ImportedLibraries, NewImportedLibraries);
9093

9194
JD.addGenerator(std::move(*G));
9295

0 commit comments

Comments
 (0)