Skip to content

Commit 7937fe1

Browse files
committed
[ORC][llvm-jitlink] Add support for forced loading of archive members.
This patch adds support for forced loading of archive members, similar to the behavior of the -all_load and -ObjC options in ld64. To enable this, the StaticLibraryDefinitionGenerator class constructors are extended with a VisitMember callback that is called on each member file in the archive at generator construction time. This callback can be used to unconditionally add the member file to a JITDylib at that point. To test this the llvm-jitlink utility is extended with -all_load (all platforms) and -ObjC (darwin only) options. Since we can't refer to symbols in the test objects directly (these would always cause the member to be linked in, even without the new flags) we instead test side-effects of force loading: execution of constructors and registration of Objective-C metadata. rdar://134446111
1 parent 4c2c177 commit 7937fe1

File tree

10 files changed

+180
-13
lines changed

10 files changed

+180
-13
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include <Foundation/Foundation.h>
2+
3+
@interface Foo : NSObject
4+
@end
5+
6+
@implementation Foo
7+
@end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: rm -rf %t && mkdir -p %t
2+
// RUN: %clang -c -o %t/EmptyClassFoo.o %S/Inputs/EmptyClassFoo.m
3+
// RUN: ar r %t/libFooClass.a %t/EmptyClassFoo.o
4+
// RUN: %clang -c -o %t/force-objc.o %s
5+
// RUN: %llvm_jitlink -ObjC %t/force-objc.o -L%t -lFooClass
6+
//
7+
// REQUIRES: system-darwin && host-arch-compatible
8+
9+
id objc_getClass(const char *name);
10+
11+
int main(int argc, char *argv[]) {
12+
// Return succeess if we find Foo, error otherwise.
13+
return objc_getClass("Foo") ? 0 : 1;
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
extern "C" int x;
2+
3+
namespace {
4+
5+
struct Init {
6+
public:
7+
Init() { x = 1; }
8+
};
9+
10+
Init SetX;
11+
12+
} // namespace
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Check that the -all_load flag to llvm-jitlink causes all objects from
2+
// archives to be loaded, regardless of whether or not they're referenced.
3+
//
4+
// RUN: rm -rf %t && mkdir -p %t
5+
// RUN: %clangxx -c -o %t/SetX.o %S/Inputs/SetGlobalIntXInConstructor.cpp
6+
// RUN: ar r %t/libSetX.a %t/SetX.o
7+
// RUN: %clang -c -o %t/all_load.o %s
8+
// RUN: %llvm_jitlink -all_load %t/all_load.o -L%t -lSetX
9+
//
10+
// REQUIRES: system-darwin && host-arch-compatible
11+
12+
int x = 0;
13+
14+
int main(int argc, char *argv[]) { return x == 1 ? 0 : 1; }

compiler-rt/test/orc/lit.cfg.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
if config.host_arch == "x86_64h" and config.target_arch == "x86_64":
1616
host_arch_compatible = True
17+
if host_arch_compatible:
18+
config.available_features.add("host-arch-compatible")
1719
config.test_target_is_host_executable = (
1820
config.target_os == config.host_os and host_arch_compatible
1921
)
@@ -71,9 +73,10 @@ def build_invocation(compile_flags):
7173
(lli + " -jit-kind=orc -jit-linker=jitlink -orc-runtime=" + orc_rt_path),
7274
)
7375
)
76+
config.substitutions.append(("%ar", "ar"))
7477

7578
# Default test suffixes.
76-
config.suffixes = [".c", ".cpp", ".S", ".ll", ".test"]
79+
config.suffixes = [".c", ".cpp", ".m", ".S", ".ll", ".test"]
7780

7881
# Exclude Inputs directories.
7982
config.excludes = ["Inputs"]

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,25 +268,37 @@ class DynamicLibrarySearchGenerator : public DefinitionGenerator {
268268
/// the containing object being added to the JITDylib.
269269
class StaticLibraryDefinitionGenerator : public DefinitionGenerator {
270270
public:
271-
// Interface builder function for objects loaded from this archive.
271+
/// Interface builder function for objects loaded from this archive.
272272
using GetObjectFileInterface =
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)>;
279+
280+
/// A VisitMembersFunction that unconditionally loads all object files from
281+
/// the archive.
282+
/// Archive members that are not valid object files will be skipped.
283+
static VisitMembersFunction loadAllObjectFileMembers(ObjectLayer &L,
284+
JITDylib &JD);
285+
276286
/// Try to create a StaticLibraryDefinitionGenerator from the given path.
277287
///
278288
/// This call will succeed if the file at the given path is a static library
279289
/// or a MachO universal binary containing a static library that is compatible
280290
/// with the ExecutionSession's triple. Otherwise it will return an error.
281291
static Expected<std::unique_ptr<StaticLibraryDefinitionGenerator>>
282292
Load(ObjectLayer &L, const char *FileName,
293+
VisitMembersFunction VisitMembers = VisitMembersFunction(),
283294
GetObjectFileInterface GetObjFileInterface = GetObjectFileInterface());
284295

285296
/// Try to create a StaticLibrarySearchGenerator from the given memory buffer
286297
/// and Archive object.
287298
static Expected<std::unique_ptr<StaticLibraryDefinitionGenerator>>
288299
Create(ObjectLayer &L, std::unique_ptr<MemoryBuffer> ArchiveBuffer,
289300
std::unique_ptr<object::Archive> Archive,
301+
VisitMembersFunction VisitMembers = VisitMembersFunction(),
290302
GetObjectFileInterface GetObjFileInterface = GetObjectFileInterface());
291303

292304
/// Try to create a StaticLibrarySearchGenerator from the given memory buffer.
@@ -298,6 +310,7 @@ class StaticLibraryDefinitionGenerator : public DefinitionGenerator {
298310
/// with the ExecutionSession's triple. Otherwise it will return an error.
299311
static Expected<std::unique_ptr<StaticLibraryDefinitionGenerator>>
300312
Create(ObjectLayer &L, std::unique_ptr<MemoryBuffer> ArchiveBuffer,
313+
VisitMembersFunction VisitMembers = VisitMembersFunction(),
301314
GetObjectFileInterface GetObjFileInterface = GetObjectFileInterface());
302315

303316
/// Returns a list of filenames of dynamic libraries that this archive has

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class MachOUniversalBinary;
2828

2929
namespace orc {
3030

31+
class JITDylib;
32+
class ObjectLayer;
33+
3134
/// Check that the given buffer contains a MachO object file compatible with the
3235
/// given triple.
3336
/// ObjIsSlice should be set to true if Obj is a slice of a universal binary
@@ -72,6 +75,20 @@ getMachOSliceRangeForTriple(object::MachOUniversalBinary &UB, const Triple &TT);
7275
Expected<std::pair<size_t, size_t>>
7376
getMachOSliceRangeForTriple(MemoryBufferRef UBBuf, const Triple &TT);
7477

78+
/// For use with StaticLibraryDefinitionGenerators.
79+
class ForceLoadMachOArchiveMembers {
80+
public:
81+
ForceLoadMachOArchiveMembers(ObjectLayer &L, JITDylib &JD, bool ObjCOnly)
82+
: L(L), JD(JD), ObjCOnly(ObjCOnly) {}
83+
84+
Error operator()(MemoryBufferRef MemberBuf);
85+
86+
private:
87+
ObjectLayer &L;
88+
JITDylib &JD;
89+
bool ObjCOnly;
90+
};
91+
7592
} // namespace orc
7693
} // namespace llvm
7794

llvm/lib/ExecutionEngine/Orc/ExecutionUtils.cpp

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -273,27 +273,58 @@ Error DynamicLibrarySearchGenerator::tryToGenerate(
273273
return JD.define(absoluteSymbols(std::move(NewSymbols)));
274274
}
275275

276+
StaticLibraryDefinitionGenerator::VisitMembersFunction
277+
StaticLibraryDefinitionGenerator::loadAllObjectFileMembers(ObjectLayer &L,
278+
JITDylib &JD) {
279+
return [&](MemoryBufferRef Buf) -> Error {
280+
switch (identify_magic(Buf.getBuffer())) {
281+
case file_magic::elf_relocatable:
282+
case file_magic::macho_object:
283+
case file_magic::coff_object:
284+
return L.add(JD, MemoryBuffer::getMemBuffer(Buf));
285+
default:
286+
return Error::success();
287+
}
288+
};
289+
}
290+
276291
Expected<std::unique_ptr<StaticLibraryDefinitionGenerator>>
277292
StaticLibraryDefinitionGenerator::Load(
278-
ObjectLayer &L, const char *FileName,
293+
ObjectLayer &L, const char *FileName, VisitMembersFunction VisitMembers,
279294
GetObjectFileInterface GetObjFileInterface) {
280295

281296
const auto &TT = L.getExecutionSession().getTargetTriple();
282297
auto Linkable = loadLinkableFile(FileName, TT, LoadArchives::Required);
283298
if (!Linkable)
284299
return Linkable.takeError();
285300

286-
return Create(L, std::move(Linkable->first), std::move(GetObjFileInterface));
301+
return Create(L, std::move(Linkable->first), std::move(VisitMembers),
302+
std::move(GetObjFileInterface));
287303
}
288304

289305
Expected<std::unique_ptr<StaticLibraryDefinitionGenerator>>
290306
StaticLibraryDefinitionGenerator::Create(
291307
ObjectLayer &L, std::unique_ptr<MemoryBuffer> ArchiveBuffer,
292-
std::unique_ptr<object::Archive> Archive,
308+
std::unique_ptr<object::Archive> Archive, VisitMembersFunction VisitMembers,
293309
GetObjectFileInterface GetObjFileInterface) {
294310

295311
Error Err = Error::success();
296312

313+
if (VisitMembers) {
314+
for (auto Child : Archive->children(Err)) {
315+
if (auto ChildBuf = Child.getMemoryBufferRef()) {
316+
if (auto Err2 = VisitMembers(*ChildBuf))
317+
return std::move(Err2);
318+
} else {
319+
// We silently allow non-object archive members. This matches the
320+
// behavior of ld.
321+
consumeError(ChildBuf.takeError());
322+
}
323+
}
324+
if (Err)
325+
return std::move(Err);
326+
}
327+
297328
std::unique_ptr<StaticLibraryDefinitionGenerator> ADG(
298329
new StaticLibraryDefinitionGenerator(
299330
L, std::move(ArchiveBuffer), std::move(Archive),
@@ -308,6 +339,7 @@ StaticLibraryDefinitionGenerator::Create(
308339
Expected<std::unique_ptr<StaticLibraryDefinitionGenerator>>
309340
StaticLibraryDefinitionGenerator::Create(
310341
ObjectLayer &L, std::unique_ptr<MemoryBuffer> ArchiveBuffer,
342+
VisitMembersFunction VisitMembers,
311343
GetObjectFileInterface GetObjFileInterface) {
312344

313345
auto B = object::createBinary(ArchiveBuffer->getMemBufferRef());
@@ -319,7 +351,7 @@ StaticLibraryDefinitionGenerator::Create(
319351
return Create(L, std::move(ArchiveBuffer),
320352
std::unique_ptr<object::Archive>(
321353
static_cast<object::Archive *>(B->release())),
322-
std::move(GetObjFileInterface));
354+
std::move(VisitMembers), std::move(GetObjFileInterface));
323355

324356
// If this is a universal binary then search for a slice matching the given
325357
// Triple.
@@ -341,7 +373,7 @@ StaticLibraryDefinitionGenerator::Create(
341373
return Archive.takeError();
342374

343375
return Create(L, std::move(ArchiveBuffer), std::move(*Archive),
344-
std::move(GetObjFileInterface));
376+
std::move(VisitMembers), std::move(GetObjFileInterface));
345377
}
346378

347379
return make_error<StringError>(Twine("Unrecognized file type for ") +

llvm/lib/ExecutionEngine/Orc/MachO.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "llvm/ADT/ScopeExit.h"
1212
#include "llvm/BinaryFormat/MachO.h"
13+
#include "llvm/ExecutionEngine/Orc/Layer.h"
1314
#include "llvm/Object/MachOUniversal.h"
1415
#include "llvm/Support/FileSystem.h"
1516

@@ -228,5 +229,39 @@ getMachOSliceRangeForTriple(MemoryBufferRef UBBuf, const Triple &TT) {
228229
return getMachOSliceRangeForTriple(**UB, TT);
229230
}
230231

232+
Error ForceLoadMachOArchiveMembers::operator()(MemoryBufferRef MemberBuf) {
233+
if (!ObjCOnly)
234+
return L.add(JD, MemoryBuffer::getMemBuffer(MemberBuf));
235+
236+
// We need to check whether this archive member contains any Objective-C
237+
// or Swift metadata.
238+
239+
auto Obj = object::ObjectFile::createObjectFile(MemberBuf);
240+
if (!Obj) {
241+
// We silently ignore invalid files.
242+
consumeError(Obj.takeError());
243+
return Error::success();
244+
}
245+
246+
if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&**Obj)) {
247+
// Load the object if any recognized special section is present.
248+
for (auto Sec : MachOObj->sections()) {
249+
auto SegName =
250+
MachOObj->getSectionFinalSegmentName(Sec.getRawDataRefImpl());
251+
if (auto SecName = Sec.getName()) {
252+
if (*SecName == "__objc_classlist" || *SecName == "__objc_protolist" ||
253+
*SecName == "__objc_clsrolist" || *SecName == "__objc_catlist" ||
254+
*SecName == "__objc_catlist2" || *SecName == "__objc_nlcatlist" ||
255+
(SegName == "__TEXT" && (*SecName).starts_with("__swift") &&
256+
*SecName != "__swift_modhash"))
257+
return L.add(JD, MemoryBuffer::getMemBuffer(MemberBuf));
258+
} else
259+
return SecName.takeError();
260+
}
261+
}
262+
263+
return Error::success();
264+
}
265+
231266
} // End namespace orc.
232267
} // End namespace llvm.

llvm/tools/llvm-jitlink/llvm-jitlink.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
3131
#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
3232
#include "llvm/ExecutionEngine/Orc/LoadLinkableFile.h"
33+
#include "llvm/ExecutionEngine/Orc/MachO.h"
3334
#include "llvm/ExecutionEngine/Orc/MachOPlatform.h"
3435
#include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h"
3536
#include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h"
@@ -265,6 +266,17 @@ static cl::opt<std::string>
265266
OverrideTriple("triple", cl::desc("Override target triple detection"),
266267
cl::init(""), cl::cat(JITLinkCategory));
267268

269+
static cl::opt<bool> AllLoad("all_load",
270+
cl::desc("Load all members of static archives"),
271+
cl::init(false), cl::cat(JITLinkCategory));
272+
273+
static cl::opt<bool> ForceLoadObjC(
274+
"ObjC",
275+
cl::desc("Load all members of static archives that implement "
276+
"Objective-C classes or categories, or Swift structs, "
277+
"classes or extensions"),
278+
cl::init(false), cl::cat(JITLinkCategory));
279+
268280
static ExitOnError ExitOnErr;
269281

270282
static LLVM_ATTRIBUTE_USED void linkComponents() {
@@ -1957,10 +1969,9 @@ static Error addLibraries(Session &S,
19571969
});
19581970

19591971
// 3. Process library loads.
1960-
auto AddArchive = [&](const char *Path, const LibraryLoad &LL)
1972+
auto AddArchive = [&](JITDylib &JD, const char *Path, const LibraryLoad &LL)
19611973
-> Expected<std::unique_ptr<StaticLibraryDefinitionGenerator>> {
1962-
unique_function<Expected<MaterializationUnit::Interface>(
1963-
ExecutionSession & ES, MemoryBufferRef ObjBuffer)>
1974+
StaticLibraryDefinitionGenerator::GetObjectFileInterface
19641975
GetObjFileInterface;
19651976
switch (LL.Modifier) {
19661977
case LibraryLoad::Standard:
@@ -1970,8 +1981,17 @@ static Error addLibraries(Session &S,
19701981
GetObjFileInterface = getObjectFileInterfaceHidden;
19711982
break;
19721983
}
1984+
1985+
StaticLibraryDefinitionGenerator::VisitMembersFunction VisitMembers;
1986+
if (AllLoad)
1987+
VisitMembers = StaticLibraryDefinitionGenerator::loadAllObjectFileMembers(
1988+
S.ObjLayer, JD);
1989+
else if (S.ES.getTargetTriple().isOSBinFormatMachO() && ForceLoadObjC)
1990+
VisitMembers = ForceLoadMachOArchiveMembers(S.ObjLayer, JD, true);
1991+
19731992
auto G = StaticLibraryDefinitionGenerator::Load(
1974-
S.ObjLayer, Path, std::move(GetObjFileInterface));
1993+
S.ObjLayer, Path, std::move(VisitMembers),
1994+
std::move(GetObjFileInterface));
19751995
if (!G)
19761996
return G.takeError();
19771997

@@ -2009,7 +2029,7 @@ static Error addLibraries(Session &S,
20092029
}
20102030

20112031
if (LL.IsPath) {
2012-
auto G = AddArchive(LL.LibName.c_str(), LL);
2032+
auto G = AddArchive(JD, LL.LibName.c_str(), LL);
20132033
if (!G)
20142034
return createFileError(LL.LibName, G.takeError());
20152035
JD.addGenerator(std::move(*G));
@@ -2065,7 +2085,7 @@ static Error addLibraries(Session &S,
20652085
}
20662086
case file_magic::archive:
20672087
case file_magic::macho_universal_binary: {
2068-
auto G = AddArchive(LibPath.data(), LL);
2088+
auto G = AddArchive(JD, LibPath.data(), LL);
20692089
if (!G)
20702090
return G.takeError();
20712091
JD.addGenerator(std::move(*G));

0 commit comments

Comments
 (0)