MCJIT and recursive finalization


I ran into a problem migrating cling (finally!) to MCJIT: When an
("outer") MCJIT's finalization /
llvm::RuntimeDyldImpl::resolveExternalSymbols() is called and a symbol
is not known, cling can help by loading the suitable library and
providing the symbol.

It compiles the relevant C++ header as part of loading the library. This
compilation emits symbols through the MCJIT. That "inner" MCJIT'ing then
needs to finalize to resolve its symbols and to run the initializers. My
problem is that this also finalizes the outer MCJIT, e.g. marking *all*
memory pages as non-writable + executable - which in turn makes the
outer MCJIT bomb as soon as it tries to write the newly determined
relocation address to (now protected) memory.

Here is what I guess I'm looking for:
- outer resolveExternalSymbols() has a missing symbol,
- we compile and emit into a new "memory section"
- we finalize only that section
- we run static init of that secion
- we pass that now resolved symbol address to the outer MCJIT.

I tried to have the LinkingMemManager's ClientMM stack memory managers
upon recursion, so each of them can operate on their own memory regions.
But that seems to mess up the section counts / symbol offset tables :frowning:

What would you recommend me to do?

Cheers, Axel.

+Lang, owner of JITs

Hi Axel,

You could use multiple MCJIT instances, each with its own RTDyldMemoryManager instance. That would allow you to selectively finalize memory.

Alternatively you could avoid finalizing the inner MCJIT (I don’t think that’s necessary for resolving symbols), and defer the initializer calls until after you’ve finalized the outer one?

Possibly of interest: I came across the same problem while working on the new Orc APIs, and there I opted for a version of the first solution: Each Module actually gets its own RTDyldMemoryManager instance, so they can be finalized independently.