Skip to content

Commit

Permalink
[ORC][ORC-RT] Rewrite the MachO platform to use allocation actions.
Browse files Browse the repository at this point in the history
This patch updates the MachO platform (both the ORC MachOPlatform class and the
ORC-Runtime macho_platform.* files) to use allocation actions, rather than EPC
calls, to transfer the initializer information scraped from each linked object.
Interactions between the ORC and ORC-Runtime sides of the platform are
substantially redesigned to accomodate the change.

The high-level changes in this patch are:

1. The MachOPlatform::setupJITDylib method now calls into the runtime to set up
   a dylib name <-> header mapping, and a dylib state object (JITDylibState).

2. The MachOPlatformPlugin builds an allocation action that calls the
   __orc_rt_macho_register_object_platform_sections and
   __orc_rt_macho_deregister_object_platform_sections functions in the runtime
   to register the address ranges for all "interesting" sections in the object
   being allocated (TLS data sections, initializers, language runtime metadata
   sections, etc.).

3. The MachOPlatform::rt_getInitializers method (the entry point in the
   controller for requests from the runtime for initializer information) is
   replaced by MachOPlatform::rt_pushInitializers. The former returned a data
   structure containing the "interesting" section address ranges, but these are
   now handled by __orc_rt_macho_register_object_platform_sections. The new
   rt_pushInitializers method first issues a lookup to trigger materialization
   of the "interesting" sections, then returns the dylib dependence tree rooted
   at the requested dylib for dlopen to consume. (The dylib dependence tree is
   returned by rt_pushInitializers, rather than being handled by some dedicated
   call, because rt_pushInitializers can alter the dependence tree).

The advantage of these changes (beyond the performance advantages of using
allocation actions) is that it moves more information about the materialized
portions of the JITDylib into the executor. This tends to make the runtime
easier to reason about, e.g. the implementation of dlopen in the runtime is now
recursive, rather than relying on recursive calls in the controller to build a
linear data structure for consumption by the runtime. This change can also make
some operations more efficient, e.g. JITDylibs can be dlclosed and then
re-dlopened without having to pull all initializers over from the controller
again.

In addition to the high-level changes, there are some low-level changes to ORC
and the runtime:

* In ORC, at ExecutionSession teardown time JITDylibs are now destroyed in
reverse creation order. This is on the assumption that the ORC runtime will be
loaded into an earlier dylib that will be used by later JITDylibs. This is a
short-term solution to crashes that arose during testing when the runtime was
torn down before its users. Longer term we will likely destroy dylibs in
dependence order.

* toSPSSerializable(Expected<T> E) is updated to explicitly initialize the T
value, allowing it to be used by Ts that have explicit constructors.

* The ORC runtime now (1) attempts to track ref-counts, and (2) distinguishes
not-yet-processed "interesting" sections from previously processed ones. (1)
is necessary for standard dlopen/dlclose emulation. (2) is intended as a step
towards better REPL support -- it should enable future runtime calls that
run only newly registered initializers ("dlopen_more", "dlopen_additions",
...?).
  • Loading branch information
lhames committed Feb 8, 2022
1 parent f237ab0 commit f9aef47
Show file tree
Hide file tree
Showing 9 changed files with 1,080 additions and 634 deletions.
913 changes: 647 additions & 266 deletions compiler-rt/lib/orc/macho_platform.cpp

Large diffs are not rendered by default.

59 changes: 0 additions & 59 deletions compiler-rt/lib/orc/macho_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,6 @@ ORC_RT_INTERFACE void *__orc_rt_macho_jit_dlsym(void *dso_handle,
namespace __orc_rt {
namespace macho {

struct MachOJITDylibInitializers {
using SectionList = std::vector<ExecutorAddrRange>;

MachOJITDylibInitializers() = default;
MachOJITDylibInitializers(std::string Name, ExecutorAddr MachOHeaderAddress)
: Name(std::move(Name)),
MachOHeaderAddress(std::move(MachOHeaderAddress)) {}

std::string Name;
ExecutorAddr MachOHeaderAddress;
ExecutorAddr ObjCImageInfoAddress;

std::unordered_map<std::string, SectionList> InitSections;
};

class MachOJITDylibDeinitializers {};

using MachOJITDylibInitializerSequence = std::vector<MachOJITDylibInitializers>;

using MachOJITDylibDeinitializerSequence =
std::vector<MachOJITDylibDeinitializers>;

enum dlopen_mode : int {
ORC_RT_RTLD_LAZY = 0x1,
ORC_RT_RTLD_NOW = 0x2,
Expand All @@ -61,43 +39,6 @@ enum dlopen_mode : int {
};

} // end namespace macho

using SPSNamedExecutorAddrRangeSequenceMap =
SPSSequence<SPSTuple<SPSString, SPSExecutorAddrRangeSequence>>;

using SPSMachOJITDylibInitializers =
SPSTuple<SPSString, SPSExecutorAddr, SPSExecutorAddr,
SPSNamedExecutorAddrRangeSequenceMap>;

using SPSMachOJITDylibInitializerSequence =
SPSSequence<SPSMachOJITDylibInitializers>;

/// Serialization traits for MachOJITDylibInitializers.
template <>
class SPSSerializationTraits<SPSMachOJITDylibInitializers,
macho::MachOJITDylibInitializers> {
public:
static size_t size(const macho::MachOJITDylibInitializers &MOJDIs) {
return SPSMachOJITDylibInitializers::AsArgList::size(
MOJDIs.Name, MOJDIs.MachOHeaderAddress, MOJDIs.ObjCImageInfoAddress,
MOJDIs.InitSections);
}

static bool serialize(SPSOutputBuffer &OB,
const macho::MachOJITDylibInitializers &MOJDIs) {
return SPSMachOJITDylibInitializers::AsArgList::serialize(
OB, MOJDIs.Name, MOJDIs.MachOHeaderAddress, MOJDIs.ObjCImageInfoAddress,
MOJDIs.InitSections);
}

static bool deserialize(SPSInputBuffer &IB,
macho::MachOJITDylibInitializers &MOJDIs) {
return SPSMachOJITDylibInitializers::AsArgList::deserialize(
IB, MOJDIs.Name, MOJDIs.MachOHeaderAddress, MOJDIs.ObjCImageInfoAddress,
MOJDIs.InitSections);
}
};

} // end namespace __orc_rt

#endif // ORC_RT_MACHO_PLATFORM_H
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Contains a static initializer and deinitializer registered with
// ___cxa_atexit. dlopen-ing/dlclose-ing will print "constructor" and
// "destructor" respectively.

.section __TEXT,__text,regular,pure_instructions
.build_version macos, 12, 0 sdk_version 12, 0, 1
.globl _deinitializer
.p2align 4, 0x90
_deinitializer:
pushq %rbp
movq %rsp, %rbp
leaq L_str.2(%rip), %rdi
popq %rbp
jmp _puts

.section __TEXT,__StaticInit,regular,pure_instructions
.p2align 4, 0x90
_initializer:
pushq %rbp
movq %rsp, %rbp
leaq L_str(%rip), %rdi
callq _puts
movq _deinitializer@GOTPCREL(%rip), %rdi
leaq _I(%rip), %rsi
leaq ___dso_handle(%rip), %rdx
popq %rbp
jmp ___cxa_atexit

.globl _I
.zerofill __DATA,__common,_I,1,0
.section __DATA,__mod_init_func,mod_init_funcs
.p2align 3
.quad _initializer
.section __TEXT,__cstring,cstring_literals
L_str:
.asciz "constructor"

L_str.2:
.asciz "destructor"

.subsections_via_symbols
119 changes: 119 additions & 0 deletions compiler-rt/test/orc/TestCases/Darwin/x86-64/trivial-jit-dlopen.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Test that __orc_rt_macho_jit_dlopen and __orc_rt_macho_jit_dlclose run
// constructors and destructors as expected.
//
// This test calls dlopen and dlclose twice. We expect the inner calls to be
// no-ops.
//
// RUN: %clang -c -o %t.inits.o %p/Inputs/standalone-ctor-and-cxa-atexit-dtor.S
// RUN: %clang -c -o %t.test.o %s
// RUN: %llvm_jitlink \
// RUN: -alias _dlopen=___orc_rt_macho_jit_dlopen \
// RUN: -alias _dlclose=___orc_rt_macho_jit_dlclose \
// RUN: %t.test.o -jd inits %t.inits.o -lmain | FileCheck %s
//
// CHECK: entering main
// CHECK-NEXT: first dlopen
// CHECK-NEXT: constructor
// CHECK-NEXT: second dlopen
// CHECK-NEXT: first dlclose
// CHECK-NEXT: second dlclose
// CHECK-NEXT: destructor
// CHECK-NEXT: leaving main

.section __TEXT,__text,regular,pure_instructions
.build_version macos, 12, 0 sdk_version 13, 0
.globl _main
.p2align 4, 0x90
_main:

pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
leaq L_str(%rip), %rdi
callq _puts
leaq L_str.9(%rip), %rdi
callq _puts
leaq L_.str.2(%rip), %rdi
movl $1, %esi
callq _dlopen
movl $-1, %r14d
leaq L_str.17(%rip), %r15
testq %rax, %rax
je LBB0_6

movq %rax, %rbx
leaq L_str.11(%rip), %rdi
callq _puts
leaq L_.str.2(%rip), %rdi
movl $1, %esi
callq _dlopen
testq %rax, %rax
je LBB0_6

cmpq %rbx, %rax
je LBB0_4

leaq L_str.18(%rip), %r15
jmp LBB0_6
LBB0_4:
leaq L_str.13(%rip), %rdi
callq _puts
movq %rbx, %rdi
callq _dlclose
cmpl $-1, %eax
je LBB0_6

leaq L_str.14(%rip), %rdi
callq _puts
movq %rbx, %rdi
callq _dlclose
xorl %r14d, %r14d
cmpl $-1, %eax
sete %r14b
leaq L_str.17(%rip), %rax
leaq L_str.15(%rip), %r15
cmoveq %rax, %r15
negl %r14d
LBB0_6:
movq %r15, %rdi
callq _puts
movl %r14d, %eax
addq $8, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq

.section __TEXT,__cstring,cstring_literals
L_.str.2:
.asciz "inits"

L_str:
.asciz "entering main"

L_str.9:
.asciz "first dlopen"

L_str.11:
.asciz "second dlopen"

L_str.13:
.asciz "first dlclose"

L_str.14:
.asciz "second dlclose"

L_str.15:
.asciz "leaving main"

L_str.17:
.asciz "failed"

L_str.18:
.asciz "handles do not match"

.subsections_via_symbols
3 changes: 3 additions & 0 deletions compiler-rt/test/orc/lit.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@ def build_invocation(compile_flags):
# Default test suffixes.
config.suffixes = ['.c', '.cpp', '.S']

# Exclude Inputs directories.
config.excludes = ['Inputs']

if config.host_os not in ['Darwin', 'FreeBSD', 'Linux']:
config.unsupported = True
Loading

0 comments on commit f9aef47

Please sign in to comment.