Skip to content

Commit bb41fc6

Browse files
committed
[ORC-RT][ORC][MachO] Add executor-side symbol tables to MachO platform support.
Adds symbol tables to the JITDylibState struct in the ORC runtime MachOPlatformRuntimeState class. This table will hold the addresses of materialized symbols (registered by a new JITLink pass in MachOPlatform), allowing these to be looked up in the executor without an IPC request to the controller. The old lookup-symbols callback (made by the runtime in response to dlsym lookups) is replaced with a push-symbols callback that can trigger materialization of requested symbols. Holding a symbol table on the executor side should make repeat calls to dlsym (and other symbol lookup operations) cheaper since the IPC to trigger materialization happens at most once per symbol. It should also enable us (at some point in the future) to symbolicate backtraces in JIT'd code even if the controller process is gone (e.g. detached or crashed). The trade-off for this is increased memory consumption in the executor and larger JIT'd data transfers (since symbol names are now transferred to the executor unconditionally, even though they may never be used).
1 parent 7ec4f60 commit bb41fc6

File tree

6 files changed

+477
-77
lines changed

6 files changed

+477
-77
lines changed

compiler-rt/lib/orc/macho_platform.cpp

Lines changed: 249 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "macho_platform.h"
14+
#include "bitmask_enum.h"
1415
#include "common.h"
1516
#include "debug.h"
1617
#include "error.h"
@@ -34,7 +35,7 @@ using namespace __orc_rt::macho;
3435

3536
// Declare function tags for functions in the JIT process.
3637
ORC_RT_JIT_DISPATCH_TAG(__orc_rt_macho_push_initializers_tag)
37-
ORC_RT_JIT_DISPATCH_TAG(__orc_rt_macho_symbol_lookup_tag)
38+
ORC_RT_JIT_DISPATCH_TAG(__orc_rt_macho_push_symbols_tag)
3839

3940
struct objc_image_info;
4041
struct mach_header;
@@ -148,6 +149,16 @@ struct TLVDescriptor {
148149
};
149150

150151
class MachOPlatformRuntimeState {
152+
public:
153+
// Used internally by MachOPlatformRuntimeState, but made public to enable
154+
// serialization.
155+
enum class MachOExecutorSymbolFlags : uint8_t {
156+
None = 0,
157+
Weak = 1U << 0,
158+
Callable = 1U << 1,
159+
ORC_RT_MARK_AS_BITMASK_ENUM(/* LargestValue = */ Callable)
160+
};
161+
151162
private:
152163
struct AtExitEntry {
153164
void (*Func)(void *);
@@ -256,11 +267,17 @@ class MachOPlatformRuntimeState {
256267
IntervalMap<char *, UnwindSections, IntervalCoalescing::Disabled>;
257268

258269
struct JITDylibState {
270+
271+
using SymbolTableMap =
272+
std::unordered_map<std::string_view,
273+
std::pair<ExecutorAddr, MachOExecutorSymbolFlags>>;
274+
259275
std::string Name;
260276
void *Header = nullptr;
261277
bool Sealed = false;
262278
size_t LinkedAgainstRefCount = 0;
263279
size_t DlRefCount = 0;
280+
SymbolTableMap SymbolTable;
264281
std::vector<JITDylibState *> Deps;
265282
AtExitsVector AtExits;
266283
const objc_image_info *ObjCImageInfo = nullptr;
@@ -296,6 +313,14 @@ class MachOPlatformRuntimeState {
296313
Error deregisterJITDylib(void *Header);
297314
Error registerThreadDataSection(span<const char> ThreadDataSection);
298315
Error deregisterThreadDataSection(span<const char> ThreadDataSection);
316+
Error registerObjectSymbolTable(
317+
ExecutorAddr HeaderAddr,
318+
const std::vector<std::tuple<ExecutorAddr, ExecutorAddr,
319+
MachOExecutorSymbolFlags>> &Entries);
320+
Error deregisterObjectSymbolTable(
321+
ExecutorAddr HeaderAddr,
322+
const std::vector<std::tuple<ExecutorAddr, ExecutorAddr,
323+
MachOExecutorSymbolFlags>> &Entries);
299324
Error registerObjectPlatformSections(
300325
ExecutorAddr HeaderAddr, std::optional<UnwindSectionInfo> UnwindSections,
301326
std::vector<std::pair<std::string_view, ExecutorAddrRange>> Secs);
@@ -306,7 +331,7 @@ class MachOPlatformRuntimeState {
306331
const char *dlerror();
307332
void *dlopen(std::string_view Name, int Mode);
308333
int dlclose(void *DSOHandle);
309-
void *dlsym(void *DSOHandle, std::string_view Symbol);
334+
void *dlsym(void *DSOHandle, const char *Symbol);
310335

311336
int registerAtExit(void (*F)(void *), void *Arg, void *DSOHandle);
312337
void runAtExits(std::unique_lock<std::mutex> &JDStatesLock,
@@ -321,8 +346,42 @@ class MachOPlatformRuntimeState {
321346
JITDylibState *getJITDylibStateByHeader(void *DSOHandle);
322347
JITDylibState *getJITDylibStateByName(std::string_view Path);
323348

324-
Expected<ExecutorAddr> lookupSymbolInJITDylib(void *DSOHandle,
325-
std::string_view Symbol);
349+
/// Requests materialization of the given symbols. For each pair, the bool
350+
/// element indicates whether the symbol is required (true) or weakly
351+
/// referenced (false).
352+
Error requestPushSymbols(JITDylibState &JDS,
353+
span<std::pair<std::string_view, bool>> Symbols);
354+
355+
/// Visits the symbol table for the JITDylib associated with DSOHandle.
356+
/// Visitor should be callable as
357+
///
358+
/// void (size_t,
359+
/// std::optional<std::pair<ExecutorAddr, MachOExecutorSymbolFlags>>)
360+
///
361+
/// The visitor function will be called for each element of the Symbols, but
362+
/// in an arbitrary order. The first argument of the callback will indicate
363+
/// the index of the result. The second argument will be std::nullopt (if the
364+
/// symbol at the given index was not present in the symbol table), or a
365+
/// pair containing the symbol's address and flags.
366+
///
367+
/// This function will remove all elements of Symbols that are found, leaving
368+
/// only the symbols that were not. This allows it to dovetail with
369+
/// requestPushSymbols, enabling the following idiom:
370+
///
371+
/// ...
372+
/// visitSymbolAddrs(DSO, Symbols);
373+
/// if (!Symbols.empty()) {
374+
/// requestPushSymbols(DSO, Symbols);
375+
/// visitSymbolAddrs(DSO, Symbols);
376+
/// for (auto &Sym : Symbols) {
377+
/// -- handle symbols that were not found --
378+
/// }
379+
/// }
380+
///
381+
template <typename VisitorFn>
382+
void visitSymbolAddrs(JITDylibState &JDS,
383+
std::vector<std::pair<std::string_view, bool>> &Symbols,
384+
VisitorFn &&Visit);
326385

327386
bool lookupUnwindSections(void *Addr, unw_dynamic_unwind_sections &Info);
328387

@@ -366,6 +425,47 @@ class MachOPlatformRuntimeState {
366425
std::map<const char *, size_t> ThreadDataSections;
367426
};
368427

428+
} // anonymous namespace
429+
430+
namespace __orc_rt {
431+
432+
class SPSMachOExecutorSymbolFlags;
433+
434+
template <>
435+
class SPSSerializationTraits<
436+
SPSMachOExecutorSymbolFlags,
437+
MachOPlatformRuntimeState::MachOExecutorSymbolFlags> {
438+
private:
439+
using UT = std::underlying_type_t<
440+
MachOPlatformRuntimeState::MachOExecutorSymbolFlags>;
441+
442+
public:
443+
static size_t
444+
size(const MachOPlatformRuntimeState::MachOExecutorSymbolFlags &SF) {
445+
return sizeof(UT);
446+
}
447+
448+
static bool
449+
serialize(SPSOutputBuffer &OB,
450+
const MachOPlatformRuntimeState::MachOExecutorSymbolFlags &SF) {
451+
return SPSArgList<UT>::serialize(OB, static_cast<UT>(SF));
452+
}
453+
454+
static bool
455+
deserialize(SPSInputBuffer &IB,
456+
MachOPlatformRuntimeState::MachOExecutorSymbolFlags &SF) {
457+
UT Tmp;
458+
if (!SPSArgList<UT>::deserialize(IB, Tmp))
459+
return false;
460+
SF = static_cast<MachOPlatformRuntimeState::MachOExecutorSymbolFlags>(Tmp);
461+
return true;
462+
}
463+
};
464+
465+
} // namespace __orc_rt
466+
467+
namespace {
468+
369469
MachOPlatformRuntimeState *MachOPlatformRuntimeState::MOPS = nullptr;
370470

371471
Error MachOPlatformRuntimeState::create() {
@@ -492,6 +592,48 @@ Error MachOPlatformRuntimeState::deregisterThreadDataSection(
492592
return Error::success();
493593
}
494594

595+
Error MachOPlatformRuntimeState::registerObjectSymbolTable(
596+
ExecutorAddr HeaderAddr,
597+
const std::vector<std::tuple<ExecutorAddr, ExecutorAddr,
598+
MachOExecutorSymbolFlags>> &Entries) {
599+
600+
std::lock_guard<std::mutex> Lock(JDStatesMutex);
601+
auto *JDS = getJITDylibStateByHeader(HeaderAddr.toPtr<void *>());
602+
if (!JDS) {
603+
std::ostringstream ErrStream;
604+
ErrStream << "Could not register object platform sections for "
605+
"unrecognized header "
606+
<< HeaderAddr.toPtr<void *>();
607+
return make_error<StringError>(ErrStream.str());
608+
}
609+
610+
for (auto &[NameAddr, SymAddr, Flags] : Entries)
611+
JDS->SymbolTable[NameAddr.toPtr<const char *>()] = {SymAddr, Flags};
612+
613+
return Error::success();
614+
}
615+
616+
Error MachOPlatformRuntimeState::deregisterObjectSymbolTable(
617+
ExecutorAddr HeaderAddr,
618+
const std::vector<std::tuple<ExecutorAddr, ExecutorAddr,
619+
MachOExecutorSymbolFlags>> &Entries) {
620+
621+
std::lock_guard<std::mutex> Lock(JDStatesMutex);
622+
auto *JDS = getJITDylibStateByHeader(HeaderAddr.toPtr<void *>());
623+
if (!JDS) {
624+
std::ostringstream ErrStream;
625+
ErrStream << "Could not register object platform sections for "
626+
"unrecognized header "
627+
<< HeaderAddr.toPtr<void *>();
628+
return make_error<StringError>(ErrStream.str());
629+
}
630+
631+
for (auto &[NameAddr, SymAddr, Flags] : Entries)
632+
JDS->SymbolTable.erase(NameAddr.toPtr<const char *>());
633+
634+
return Error::success();
635+
}
636+
495637
Error MachOPlatformRuntimeState::registerObjectPlatformSections(
496638
ExecutorAddr HeaderAddr, std::optional<UnwindSectionInfo> UnwindInfo,
497639
std::vector<std::pair<std::string_view, ExecutorAddrRange>> Secs) {
@@ -687,15 +829,51 @@ int MachOPlatformRuntimeState::dlclose(void *DSOHandle) {
687829
return 0;
688830
}
689831

690-
void *MachOPlatformRuntimeState::dlsym(void *DSOHandle,
691-
std::string_view Symbol) {
692-
auto Addr = lookupSymbolInJITDylib(DSOHandle, Symbol);
693-
if (!Addr) {
694-
DLFcnError = toString(Addr.takeError());
695-
return 0;
832+
void *MachOPlatformRuntimeState::dlsym(void *DSOHandle, const char *Symbol) {
833+
std::lock_guard<std::mutex> Lock(JDStatesMutex);
834+
auto *JDS = getJITDylibStateByHeader(DSOHandle);
835+
if (!JDS) {
836+
std::ostringstream ErrStream;
837+
ErrStream << "In call to dlsym, unrecognized header address " << DSOHandle;
838+
DLFcnError = ErrStream.str();
839+
return nullptr;
840+
}
841+
842+
std::string MangledName("_");
843+
MangledName += Symbol;
844+
std::vector<std::pair<std::string_view, bool>> Symbols;
845+
Symbols.push_back({MangledName, false});
846+
847+
ExecutorAddr Result;
848+
using ElemResult =
849+
std::optional<std::pair<ExecutorAddr, MachOExecutorSymbolFlags>>;
850+
851+
// Try to resolve the symbol in the local symbol tables.
852+
visitSymbolAddrs(*JDS, Symbols, [&](size_t Idx, ElemResult E) {
853+
if (E)
854+
Result = E->first;
855+
});
856+
857+
// Return early if we found it.
858+
if (Symbols.empty())
859+
return Result.toPtr<void *>();
860+
861+
// Otherwise call back to the controller to try to request that the symbol
862+
// be materialized.
863+
if (auto Err = requestPushSymbols(*JDS, {Symbols.data(), Symbols.size()})) {
864+
DLFcnError = toString(std::move(Err));
865+
return nullptr;
696866
}
697867

698-
return Addr->toPtr<void *>();
868+
// Try another local resolution.
869+
visitSymbolAddrs(*JDS, Symbols, [&](size_t Idx, ElemResult E) {
870+
if (E)
871+
Result = E->first;
872+
});
873+
874+
// At this point Result has either been set (if we found the symbol) or is
875+
// still null (if we didn't). Either way it's the right value.
876+
return Result.toPtr<void *>();
699877
}
700878

701879
int MachOPlatformRuntimeState::registerAtExit(void (*F)(void *), void *Arg,
@@ -774,17 +952,35 @@ MachOPlatformRuntimeState::getJITDylibStateByName(std::string_view Name) {
774952
return nullptr;
775953
}
776954

777-
Expected<ExecutorAddr>
778-
MachOPlatformRuntimeState::lookupSymbolInJITDylib(void *DSOHandle,
779-
std::string_view Sym) {
780-
Expected<ExecutorAddr> Result((ExecutorAddr()));
781-
if (auto Err = WrapperFunction<SPSExpected<SPSExecutorAddr>(
782-
SPSExecutorAddr, SPSString)>::call(&__orc_rt_macho_symbol_lookup_tag,
783-
Result,
784-
ExecutorAddr::fromPtr(DSOHandle),
785-
Sym))
955+
Error MachOPlatformRuntimeState::requestPushSymbols(
956+
JITDylibState &JDS, span<std::pair<std::string_view, bool>> Symbols) {
957+
Error OpErr = Error::success();
958+
if (auto Err = WrapperFunction<SPSError(
959+
SPSExecutorAddr, SPSSequence<SPSTuple<SPSString, bool>>)>::
960+
call(&__orc_rt_macho_push_symbols_tag, OpErr,
961+
ExecutorAddr::fromPtr(JDS.Header), Symbols)) {
962+
cantFail(std::move(OpErr));
786963
return std::move(Err);
787-
return Result;
964+
}
965+
return OpErr;
966+
}
967+
968+
template <typename VisitorFn>
969+
void MachOPlatformRuntimeState::visitSymbolAddrs(
970+
JITDylibState &JDS, std::vector<std::pair<std::string_view, bool>> &Symbols,
971+
VisitorFn &&Visit) {
972+
973+
std::vector<std::pair<std::string_view, bool>> RemainingSymbols;
974+
975+
for (size_t Idx = 0; Idx != Symbols.size(); ++Idx) {
976+
auto I = JDS.SymbolTable.find(Symbols[Idx].first);
977+
if (I != JDS.SymbolTable.end())
978+
Visit(Idx, I->second);
979+
else
980+
RemainingSymbols.push_back(Symbols[Idx]);
981+
}
982+
983+
Symbols = std::move(RemainingSymbols);
788984
}
789985

790986
// eh-frame registration functions.
@@ -1193,6 +1389,38 @@ __orc_rt_macho_register_object_platform_sections(char *ArgData,
11931389
.release();
11941390
}
11951391

1392+
ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult
1393+
__orc_rt_macho_register_object_symbol_table(char *ArgData, size_t ArgSize) {
1394+
using SymtabContainer = std::vector<
1395+
std::tuple<ExecutorAddr, ExecutorAddr,
1396+
MachOPlatformRuntimeState::MachOExecutorSymbolFlags>>;
1397+
return WrapperFunction<SPSError(
1398+
SPSExecutorAddr, SPSSequence<SPSTuple<SPSExecutorAddr, SPSExecutorAddr,
1399+
SPSMachOExecutorSymbolFlags>>)>::
1400+
handle(ArgData, ArgSize,
1401+
[](ExecutorAddr HeaderAddr, SymtabContainer &Symbols) {
1402+
return MachOPlatformRuntimeState::get()
1403+
.registerObjectSymbolTable(HeaderAddr, Symbols);
1404+
})
1405+
.release();
1406+
}
1407+
1408+
ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult
1409+
__orc_rt_macho_deregister_object_symbol_table(char *ArgData, size_t ArgSize) {
1410+
using SymtabContainer = std::vector<
1411+
std::tuple<ExecutorAddr, ExecutorAddr,
1412+
MachOPlatformRuntimeState::MachOExecutorSymbolFlags>>;
1413+
return WrapperFunction<SPSError(
1414+
SPSExecutorAddr, SPSSequence<SPSTuple<SPSExecutorAddr, SPSExecutorAddr,
1415+
SPSMachOExecutorSymbolFlags>>)>::
1416+
handle(ArgData, ArgSize,
1417+
[](ExecutorAddr HeaderAddr, SymtabContainer &Symbols) {
1418+
return MachOPlatformRuntimeState::get()
1419+
.deregisterObjectSymbolTable(HeaderAddr, Symbols);
1420+
})
1421+
.release();
1422+
}
1423+
11961424
ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult
11971425
__orc_rt_macho_deregister_object_platform_sections(char *ArgData,
11981426
size_t ArgSize) {

compiler-rt/lib/orc/simple_packed_serialization.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,13 @@ class TrivialSPSSequenceSerialization<SPSElementTagT, std::vector<T>> {
281281
static constexpr bool available = true;
282282
};
283283

284+
/// Trivial span<T> -> SPSSequence<SPSElementTagT> serialization.
285+
template <typename SPSElementTagT, typename T>
286+
class TrivialSPSSequenceSerialization<SPSElementTagT, span<T>> {
287+
public:
288+
static constexpr bool available = true;
289+
};
290+
284291
/// Trivial SPSSequence<SPSElementTagT> -> std::vector<T> deserialization.
285292
template <typename SPSElementTagT, typename T>
286293
class TrivialSPSSequenceDeserialization<SPSElementTagT, std::vector<T>> {

0 commit comments

Comments
 (0)