embedding: julia repl as a shared library

171 views
Skip to first unread message

Joosep Pata

unread,
Jul 24, 2016, 1:21:45 PM7/24/16
to julia...@googlegroups.com
Hi,

I'd like to compile ui/repl.c into a shared library so that I could dlopen julia after some other initialization procedures that would otherwise conflict with the LLVM linked to julia.

I succeeded in doing that on OSX using:

~~~
diff --git a/ui/Makefile b/ui/Makefile
+julia-release: $(build_bindir)/julia$(EXE) $(build_private_libdir)/librepl.$(SHLIB_EXT)
...
+$(build_private_libdir)/librepl.$(SHLIB_EXT): $(OBJS)
+ @$(call PRINT_LINK, $(CXXLD) -shared $(CXXFLAGS) $(CXXLDFLAGS) $(LINK_FLAGS) $(SHIPFLAGS) $^ -o $@ -L$(build_private_libdir) -L$(build_libdir) -L$(build_shlibdir)
~~~

so I can call julia dynamically as
~~~
my_init(); // initalize stuff that hides its LLVM symbols after loading
...
void* handle_julia = dlopen(LIBJULIAREPL, RTLD_NOW | RTLD_GLOBAL);
...
typedef int (*t_jl_main)(int, char**);
t_jl_main jl_main = (t_jl_main)dlsym(handle_julia, "main");
return jl_main(argc, argv);
~~~

On linux, I get strange linker errors:
`/usr/bin/ld: repl.o: relocation R_X86_64_TPOFF32 against `tls_states.12084' can not be used when making a shared object; recompile with -fPIC`

As far as I can tell, julia uses fPIC throughout. Has anyone encountered something like this before? Google links to some old gcc bugs and a go linker issue but it's not evident if there is a fix.

Cheers,
Joosep

Yichao Yu

unread,
Jul 24, 2016, 1:55:00 PM7/24/16
to Julia Users
On Sun, Jul 24, 2016 at 1:21 PM, Joosep Pata <joose...@gmail.com> wrote:
> Hi,
>
> I'd like to compile ui/repl.c into a shared library so that I could dlopen julia after some other initialization procedures that would otherwise conflict with the LLVM linked to julia.

You should **NOT** compile `ui/repl.c` since it will fail as you saw.
You should just use `libjulia.so`, why is it not enough? `ui/repl.c`
is just a very thin wrapper and should have nothing to do with LLVM or
whatever conflict you saw.

Joosep Pata

unread,
Jul 24, 2016, 2:37:28 PM7/24/16
to julia-users
I'd like to not re-implement all the REPL boiler-plate, like
~~~
ios_puts("\njulia> ", ios_stdout);
            ios_flush(ios_stdout);
            line = ios_readline(ios_stdin);
~~~
and so on.

In effect, I want to launch the usual julia REPL, but call some of my own initialization procedures before julia_init.
My motivation is that I want to call an external library that dies horribly due to the LLVM symbols present if loaded after julia_init is called, see also https://github.com/JuliaLang/julia/issues/12644.
The only way I've managed to do it is to recompile the julia binary, I figured I could re-use the repl code by just dynamically loading it.

Yichao Yu

unread,
Jul 24, 2016, 2:39:48 PM7/24/16
to Julia Users
On Sun, Jul 24, 2016 at 2:37 PM, Joosep Pata <joose...@gmail.com> wrote:
> I'd like to not re-implement all the REPL boiler-plate, like
> ~~~
> ios_puts("\njulia> ", ios_stdout);
> ios_flush(ios_stdout);
> line = ios_readline(ios_stdin);
> ~~~
> and so on.

That's not the repl and you don't need to implement that.

>
> In effect, I want to launch the usual julia REPL, but call some of my own

Which is **NOT** in the repl.c

Yichao Yu

unread,
Jul 24, 2016, 2:43:54 PM7/24/16
to Julia Users
On Sun, Jul 24, 2016 at 2:39 PM, Yichao Yu <yyc...@gmail.com> wrote:
> On Sun, Jul 24, 2016 at 2:37 PM, Joosep Pata <joose...@gmail.com> wrote:
>> I'd like to not re-implement all the REPL boiler-plate, like
>> ~~~
>> ios_puts("\njulia> ", ios_stdout);
>> ios_flush(ios_stdout);
>> line = ios_readline(ios_stdin);
>> ~~~
>> and so on.
>
> That's not the repl and you don't need to implement that.

The only thing you need to do to get a repl after initialization is to
call `Base._start()`. Simply `jl_eval_string("Base._start()")` should
work.

Joosep Pata

unread,
Jul 24, 2016, 3:12:16 PM7/24/16
to julia-users
Right, thanks for the tip. To confirm: `ui/repl.c` is still the code that gets compiled to the julia(-debug) binary, right?
If I call "Base._start()" via libjulia, I still need to reproduce the usual argv logic of the julia binary.
I'll just patch `repl.c` to my needs then without changing the linking, it's dirty but seems better that re-implementing.

Yichao Yu

unread,
Jul 24, 2016, 3:18:52 PM7/24/16
to Julia Users
On Sun, Jul 24, 2016 at 3:12 PM, Joosep Pata <joose...@gmail.com> wrote:
> Right, thanks for the tip. To confirm: `ui/repl.c` is still the code that
> gets compiled to the julia(-debug) binary, right?

Yes.

> If I call "Base._start()" via libjulia, I still need to reproduce the usual
> argv logic of the julia binary.
> I'll just patch `repl.c` to my needs then without changing the linking, it's
> dirty but seems better that re-implementing.

Sure, if you think literally two lines of code much worse than a dirty
patching then go for it.

Yichao Yu

unread,
Jul 24, 2016, 3:20:09 PM7/24/16
to Julia Users
On Sun, Jul 24, 2016 at 3:18 PM, Yichao Yu <yyc...@gmail.com> wrote:
> On Sun, Jul 24, 2016 at 3:12 PM, Joosep Pata <joose...@gmail.com> wrote:
>> Right, thanks for the tip. To confirm: `ui/repl.c` is still the code that
>> gets compiled to the julia(-debug) binary, right?
>
> Yes.
>
>> If I call "Base._start()" via libjulia, I still need to reproduce the usual
>> argv logic of the julia binary.
>> I'll just patch `repl.c` to my needs then without changing the linking, it's
>> dirty but seems better that re-implementing.
>
> Sure, if you think literally two lines of code much worse than a dirty
> patching then go for it.

Sorry, 3 lines, to be more precise.

Yichao Yu

unread,
Jul 25, 2016, 11:12:04 AM7/25/16
to Julia Users
On Sun, Jul 24, 2016 at 3:19 PM, Yichao Yu <yyc...@gmail.com> wrote:
> On Sun, Jul 24, 2016 at 3:18 PM, Yichao Yu <yyc...@gmail.com> wrote:
>> On Sun, Jul 24, 2016 at 3:12 PM, Joosep Pata <joose...@gmail.com> wrote:
>>> Right, thanks for the tip. To confirm: `ui/repl.c` is still the code that
>>> gets compiled to the julia(-debug) binary, right?
>>
>> Yes.
>>
>>> If I call "Base._start()" via libjulia, I still need to reproduce the usual
>>> argv logic of the julia binary.
>>> I'll just patch `repl.c` to my needs then without changing the linking, it's
>>> dirty but seems better that re-implementing.

I was under the impression that by "patch repl.c" you mean to patch it
somehow so that you can compile as a shared library, that will be very
bad and is intentionally not supported.
If you are talking about adding your llvm initialization stuff in this
file and compile it still as an executable and if you current goal is
to get a julia binary that does not confuse LLVM then I think that's
the best way to do it and the approach itself is not "dirty" at all
(apart from possibly dirty things you need to do to "unconfuse" LLVM,
which you need to do anyway, independent of where you do it).

Joosep Pata

unread,
Jul 25, 2016, 11:18:40 AM7/25/16
to julia...@googlegroups.com

> I was under the impression that by "patch repl.c" you mean to patch it
> somehow so that you can compile as a shared library, that will be very
> bad and is intentionally not supported.
> If you are talking about adding your llvm initialization stuff in this
> file and compile it still as an executable and if you current goal is
> to get a julia binary that does not confuse LLVM then I think that's
> the best way to do it and the approach itself is not "dirty" at all
> (apart from possibly dirty things you need to do to "unconfuse" LLVM,
> which you need to do anyway, independent of where you do it).

Yes, that's what I wanted to do (re-compile the julia binary with my preinit code), sorry for not being clear. If only 3 (or some N<10) lines of code was needed to make a fully functional julia binary using libjulia, I suppose repl.c would be a bit shorter as well.

In fact, I found the way to implement what I needed also via dlopening libjulia [1] as you suggested, but having a non-standard location for the julia binary (wrt. julia source tree) is a real pain, so I think I'll just go with the patch-and-recompile-binary approach.

Thanks again for the clarifications!

[1] https://github.com/jpata/ROOT.jl/blob/cxx/src/ui.cc

Yichao Yu

unread,
Jul 25, 2016, 11:24:18 AM7/25/16
to Julia Users
On Mon, Jul 25, 2016 at 11:18 AM, Joosep Pata <joose...@gmail.com> wrote:
>
>> I was under the impression that by "patch repl.c" you mean to patch it
>> somehow so that you can compile as a shared library, that will be very
>> bad and is intentionally not supported.
>> If you are talking about adding your llvm initialization stuff in this
>> file and compile it still as an executable and if you current goal is
>> to get a julia binary that does not confuse LLVM then I think that's
>> the best way to do it and the approach itself is not "dirty" at all
>> (apart from possibly dirty things you need to do to "unconfuse" LLVM,
>> which you need to do anyway, independent of where you do it).
>
> Yes, that's what I wanted to do (re-compile the julia binary with my preinit code), sorry for not being clear. If only 3 (or some N<10) lines of code was needed to make a fully functional julia binary using libjulia, I suppose repl.c would be a bit shorter as well.

Well, this IS actually the case. Most of the logic in `repl.c` are
actually not necessary for a working julia binary. Many of them (in
terms of line count) are actually only needed during (or even only for
debugging) bootstrapping. In another word, `true_main` should
basically only contain one `jl_set_ARGS` and one `jl_eval_string` if
it doesn't need to support bootstrapping.
Reply all
Reply to author
Forward
0 new messages