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

Rework dlsym(), dlopen(), jl_dlsym() and jl_load_dynamic_library() APIs #28888

Merged
merged 8 commits into from
Aug 29, 2018

Conversation

staticfloat
Copy link
Member

  • Remove jl_dlsym_e() to instead be rolled into jl_dlsym() with a throw_err parameter, similar to jl_load_dynamic_library_().

  • Fix jl_dlsym incorrectly assumes a NULL return is an error #28881 by having Libdl.dlsym() return nothing on missing symbol, rather than C_NULL.

  • Change dlopen() to mimic dlsym() so that a negative result returns nothing. While not necessary in the same way as in the dlsym() case (there are no valid NULL dlopen() results) the consistency is worth it.

@staticfloat staticfloat changed the title Rework Libdl.dlsym() and jl_dlsym(). Rework Libdl.dlsym(), Libdl.dlopen(), jl_dlsym() and jl_load_dynamic_library() APIs Aug 24, 2018
@staticfloat staticfloat changed the title Rework Libdl.dlsym(), Libdl.dlopen(), jl_dlsym() and jl_load_dynamic_library() APIs Rework dlsym(), dlopen(), jl_dlsym() and jl_load_dynamic_library() APIs Aug 24, 2018
src/dlload.c Outdated
/* Next, check for errors. On Windows, a NULL pointer means the symbol
* was not found. On everything else, we can have NULL symbols, so we check
* for non-NULL returns from dlerror(). */
const char * err = jl_dlerror();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big deal, but we don't really need to do this work on windows unless we know there's a failure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I originally had it split out but it was enough extra code that I liked it better this way. Optimizing for the programmer, not the program, I suppose.

src/dlload.c Outdated
{
#ifdef _OS_WINDOWS_
CHAR reason[256];
static CHAR reason[256];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes this no longer thread safe, which is probably fine if we only do it in the error case, but if we do it in the success case as well, I'm worried the error message will get overwritten by the success of a different thread.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've fixed this by slapping __thread in front of it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, we can't assume that that's universally available.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was taking my inspiration from support/dirname.c. Can I do:

#  if !defined(_COMPILER_MICROSOFT_)
    static __thread char reason[256];
# else
    static __declspec(thread) char reason[256];
# endif

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you really need to, you can use our own tls... There're already platform dependent members in there so it shouldn't be a huge problem even though it won't look as nice and will certainly needs cross references in the comments.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because there seems to be some disagreement within the Julia codebase, I have taken the liberty of adding #define JL_THREAD_LOCAL to julia.h which emulates what is held within src/support/dirname.c. If this shouldn't be done (and we should instead use the more complex TLS implementation in src/threading.c), it's probably best to transition both jl_dlerror() and dirname() to use that new stuff instead.

@Keno
Copy link
Member

Keno commented Aug 25, 2018

Do you want to separate out the .c change from the julia changes to make that part eligible for backporting and use from BinaryProvider?

src/ccall.cpp Outdated
@@ -1490,13 +1491,11 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs)
};
#define is_libjulia_func(name) _is_libjulia_func((uintptr_t)&(name), #name)

static auto ptls_getter = &jl_get_ptls_states;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the change? This completely defeated the purpose of the old code, which is clearly stated in the comment..............

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because I have been dealing with the dynamic (runtime) linker too much, so when it said "can cause linker issues" I naively thought that meant at runtime when the symbol actually gets used. I didn't realize this caused issues at compile-time link. :P

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are welcome to change the comment in whichever way you want so that anyone that have read that comment won't change this code without testing the compiler configure combination again.....

@staticfloat
Copy link
Member Author

staticfloat commented Aug 26, 2018

With regards to splitting out the .c changes and the .jl changes; I don't really see how they can be changed; the .jl changes are dependent upon the .c changes, and the .c changes are not backwards-compatible.

That's okay though; because BP doesn't strictly need this; the libstdc++ version detection is future-proofing against a Julia version that doesn't have libgfortran linked into it, which doesn't exist yet.

@staticfloat staticfloat force-pushed the sf/dlsym branch 4 times, most recently from d517d3a to cac021c Compare August 26, 2018 23:43
@staticfloat
Copy link
Member Author

ERROR: could not load symbol "SymRefreshModuleList":
The operation completed successfully.

^^ Due to a missing semicolon. Impressive.

@staticfloat staticfloat force-pushed the sf/dlsym branch 3 times, most recently from 119d60d to 29398e6 Compare August 27, 2018 06:49
@staticfloat
Copy link
Member Author

staticfloat commented Aug 27, 2018

Huzzah! We are passing all tests. If I could get a thumbs up or thumbs down on the API exposed here, then we can make the final decision.

I have purposefully not put in deprecation warnings for the *_e() methods in Julia, mostly because it would be very annoying for no real benefit at the moment. However it is something that should eventually be done, perhaps once we are closer to 2.0.

@Keno
Copy link
Member

Keno commented Aug 28, 2018

Ok by me. @yuyichao ?

Copy link
Contributor

@yuyichao yuyichao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. The ccall ptls getter lookup is correct but is just very confusing though....

// directly accessing the address of an ifunc can cause compile-time linker issues
// on some configurations (e.g. AArch64 + -Bsymbolic-functions), so we guard the
// `&jl_get_ptls_states` within this `#ifdef` guard, and use a more roundabout
// method involving `jl_dlsym()` on Linux platforms instead.
#ifdef _OS_LINUX_
Copy link
Contributor

@yuyichao yuyichao Aug 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually makes the static useless. The performance isn't super critical here so if you want to just get rid of the static that's fine. Otherwise you can write it as.

static auto ptls_getter = [] {
#ifdef _OS_LINUX_
    jl_ptls_t (*p)(void);
    jl_dlsym(....);
    return p;
#else
    return jl_get_ptls_states;
#endif
}();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, that's available even all the way back to GCC 4.8.5. I'm amazed. And yes, you're right this completely defeats static, but I like this fancy lambda initialization. I'm gonna go with that since compiler support is good enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is standard C++11 feature and we require C++11 so that shouldn't be very surprising......

{
#ifdef _OS_WINDOWS_
CHAR reason[256];
static JL_THREAD_LOCAL CHAR reason[256];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change needed but note that the "more complex" tls implementation we have should actually be simpler and faster.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simpler than just writing JL_THREAD_LOCAL? How would I use it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simpler in the code generated that is.

* Remove `jl_dlsym_e()` to instead be rolled into `jl_dlsym()` with a `throw_err` parameter, similar to `jl_load_dynamic_library()`.

* Fix #28881 by having `Libdl.dlsym()` return `nothing` on missing symbol, rather than `C_NULL`.
This changes `dlopen()` to mimic `dlsym()` so that a negative result
returns `nothing`.  While not necessary in the same way as in the
`dlsym()` case (there are no valid `NULL` `dlopen()` results) the
consistency is worth it.
@staticfloat
Copy link
Member Author

When this goes green, I'm going to merge. Thanks for the shepherding, Keno and Yichao!

@vtjnash
Copy link
Member

vtjnash commented Aug 29, 2018

I'm not sure that I like the new API any better than the old, but I suppose that's not very helpful feedback.

@staticfloat staticfloat merged commit fc27b48 into master Aug 29, 2018
@staticfloat
Copy link
Member Author

Feel free to suggest a better API in a new PR. I’m going to sit with his one for now.

@staticfloat staticfloat deleted the sf/dlsym branch August 29, 2018 04:25
@staticfloat
Copy link
Member Author

The behavioral change of returning nothing instead of just C_NULL should probably be documented somewhere, but NEWS.md is empty. Should this be added to that file?

@KristofferC
Copy link
Member

Seems like the correct place to me.

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.

5 participants