Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[loader]: Re-export symbols for C embedding, rename to libjulia-internal #38160

Merged
merged 1 commit into from
Dec 2, 2020

Conversation

staticfloat
Copy link
Member

We first rename libjulia to libjulia-internal, and libjulialoader
to libjulia so as to reduce confusion from the outside; libjulia is
the entrypoint for all usecases, and libjulia-internal just so happens
to be the chunk that contains all the actual code and linkage to other
libraries.

To support C linking against libjulia in a natural way, we must
re-export our symbols from libjulia-internal. In order to do this we
create function trampolines from libjulia to libjulia-internal and
we define all symbols within libjulia to be filled in by the
initialization routines of libjulia-internal.

The function and data symbols are now maintained in lists stored in
src/jl_exported_*.list. These lists are consumed by cli/Makefile
where bindings are generated for them to be re-exported. The function
trampolines are all of the form:

__attribute__((visibility("hidden"))) void * jl_foo_addr;
JL_DLLEXPORT JL_NAKED void jl_foo(void) { __asm__("jmpq *%0" : : "a"(jl_foo_addr) :); };

where the jl_foo_addr is initialized within loader_export_symbols():

jl_foo_addr = dlsym(libjulia_internal, "jl_foo");

The data symbols are merely defined as void * pointers within the
loader, then imported into libjulia-internal by the C linker. This
backwards-dependency is slightly confusing, but it allows the symbols to
live within libjulia while being initialized by libjulia-internal.
At first glance it appears to be a circular dependency, however since
libjulia loads libjulia-internal through dlopen() first, then
libjulia-internal finds libjulia already loaded, the circular
dependency is a non-issue. C code that wishes to link against
libjulia should do so directly, and the values of all symbols, while
initially being NULL, will be set to their correct values after
initialization through load_libjulia_internal() is complete.

@staticfloat
Copy link
Member Author

@yuyichao I requested your review because I believe this was a central issue with regards to your objections to the libjulialoader PR. With this change we should support direct C linking/embedding again.

@JeffBezanson I re-named libjulia to libjulia-internal, and libjulialoader to libjulia. Let me know if there's something you don't like.

@vtjnash I think we're going to need to come up with function trampoline analogues that can work on any assembler. So far it looks like linux32 doesn't like jmpq. Perhaps we should use jmpl on x86?

@yuyichao
Copy link
Contributor

With this change we should support direct C linking/embedding again.

No, as I said, the caller is still required to provide more information than before.
(and as an aside the tls handling is still wrong)

@staticfloat staticfloat force-pushed the sf/reexport_symbols branch 11 times, most recently from 800948d to 04bb53b Compare November 20, 2020 18:25
@staticfloat
Copy link
Member Author

Looks like this is ready to merge. Keno and I confirmed that you can get unwind info from within a trampoline by segfaulting within the trampoline and seeing the correct unwind trace (despite the fact that the trampoline function itself was referred to as an unknown function).

…rnal`

We first rename `libjulia` to `libjulia-internal`, and `libjulialoader`
to `libjulia` so as to reduce confusion from the outside; `libjulia` is
the entrypoint for all usecases, and `libjulia-internal` just so happens
to be the chunk that contains all the actual code and linkage to other
libraries.

To support C linking against `libjulia` in a natural way, we must
re-export our symbols from `libjulia-internal`.  In order to do this we
create function trampolines from `libjulia` to `libjulia-internal` and
we define all symbols within `libjulia` to be filled in by the
initialization routines of `libjulia-internal`.

The function and data symbols are now maintained in lists stored in
`src/jl_exported_*.inc`.  These lists are consumed by `cli/Makefile`
where bindings are generated for them to be re-exported.  The function
trampolines are all naked functions that contain an architecture-
appropriate `jmp` instruction to `jl_*_addr` symbols, which are
initialized within `loader_export_symbols()` to point to the actual
function addresses within `libjulia-internal`.

The data symbols are merely defined as `void *` pointers within the
loader, then imported into `libjulia-internal` by the C linker.  This
backwards-dependency is slightly confusing, but it allows the symbols to
live within `libjulia` while being initialized by `libjulia-internal`.
At first glance it appears to be a circular dependency, however since
`libjulia` loads `libjulia-internal` through `dlopen()` first, then
`libjulia-internal` finds `libjulia` already loaded, the circular
dependency is a non-issue.  C code that wishes to link against
`libjulia` should do so directly, and the values of all symbols, while
initially being NULL, will be set to their correct values after
initialization through `load_libjulia_internal()` is complete.
@staticfloat
Copy link
Member Author

Green flush! Let's go.

@staticfloat staticfloat merged commit 2e3364e into master Dec 2, 2020
@staticfloat staticfloat deleted the sf/reexport_symbols branch December 2, 2020 23:56
staticfloat pushed a commit that referenced this pull request Dec 7, 2020
The soname was changed in #38160 to be libjulia.so.1.6 instead of
libjulia.so.1, but probably not on purpose.

This contradicts what is written in the comment in Make.inc and it breaks
e.g. libcxxwrap_julia_jll which is linked against libjulia.so.1.
See JuliaPackaging/Yggdrasil#2233
@GunnarFarneback
Copy link
Contributor

The embedding tests work now! There should be no extra information needed, we now use dlpath() equivalents to get the path of libjulia.so and build our relative paths to dependencies off of that.

They work with Julia in the build tree but not with the nightly binaries. More specifically, running

cd /tmp
wget https://julialangnightlies-s3.julialang.org/bin/linux/x64/julia-latest-linux64.tar.gz
tar xvfz julia-latest-linux64.tar.gz
cd ~/julia/test/embedding/
mkdir build
JULIA=/tmp/julia-6ddc7f1ecc/bin/julia BIN=build make
./build/embedding

I get

ERROR: could not load library "/tmp/julia-6ddc7f1ecc/lib/julia/../bin/../lib/julia/sys.so"
/tmp/julia-6ddc7f1ecc/lib/julia/../bin/../lib/julia/sys.so: cannot open shared object file: No such file or directory

@nalimilan
Copy link
Member

@staticfloat I get a similar error as @GunnarFarneback above when trying to build the Fedora RPM package for Julia 1.6.0-beta. Any idea about how we can fix out of tree builds? (More precisely, I get an error when trying to load openlibm.)

@staticfloat
Copy link
Member Author

Milan, can you open a new issue with all the relevant info please? Thanks!

@nalimilan
Copy link
Member

Sure: see #39523.

ElOceanografo pushed a commit to ElOceanografo/julia that referenced this pull request May 4, 2021
…#38748)

The soname was changed in JuliaLang#38160 to be libjulia.so.1.6 instead of
libjulia.so.1, but probably not on purpose.

This contradicts what is written in the comment in Make.inc and it breaks
e.g. libcxxwrap_julia_jll which is linked against libjulia.so.1.
See JuliaPackaging/Yggdrasil#2233
NHDaly added a commit that referenced this pull request Jan 2, 2022
Previously the `@snoopl` functionality from SnoopCompile wasn't unit
tested at all, and so it was broken in #38160,
which changed the requirements for exporting C functions from the
julia-internal shared lib.

This commit restores the functionality by exporting it correctly, and
also adds a unit test for snoopl, to make sure it isn't broken in the
future. Sorry that we didn't test it in the first place! :)
vchuravy pushed a commit that referenced this pull request Jan 2, 2022
* Fix SnoopCompile's `snoopl` macro and add test.

Previously the `@snoopl` functionality from SnoopCompile wasn't unit
tested at all, and so it was broken in #38160,
which changed the requirements for exporting C functions from the
julia-internal shared lib.

This commit restores the functionality by exporting it correctly, and
also adds a unit test for snoopl, to make sure it isn't broken in the
future. Sorry that we didn't test it in the first place! :)
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Feb 22, 2022
* Fix SnoopCompile's `snoopl` macro and add test.

Previously the `@snoopl` functionality from SnoopCompile wasn't unit
tested at all, and so it was broken in JuliaLang#38160,
which changed the requirements for exporting C functions from the
julia-internal shared lib.

This commit restores the functionality by exporting it correctly, and
also adds a unit test for snoopl, to make sure it isn't broken in the
future. Sorry that we didn't test it in the first place! :)
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Mar 8, 2022
* Fix SnoopCompile's `snoopl` macro and add test.

Previously the `@snoopl` functionality from SnoopCompile wasn't unit
tested at all, and so it was broken in JuliaLang#38160,
which changed the requirements for exporting C functions from the
julia-internal shared lib.

This commit restores the functionality by exporting it correctly, and
also adds a unit test for snoopl, to make sure it isn't broken in the
future. Sorry that we didn't test it in the first place! :)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants