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

Check for conflicting @ccallable name before JIT registration #55813

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

topolarity
Copy link
Member

@topolarity topolarity commented Sep 19, 2024

Fixes #54878. For pkgimages with conflicting @ccallable definitions:

$ cat Foo/src/Foo.jl
module Foo
Base.@ccallable function foo()::Cint
    return 0
end
end
$ cat Bar/src/Bar.jl
module Bar
Base.@ccallable function foo()::Cint
    return 0
end
end

This turns the existing segfault into a warning, as on 1.9:

julia> using Foo, Bar
WARNING: @ccallable was already defined for this method name

@topolarity topolarity added backport 1.10 Change should be backported to the 1.10 release backport 1.11 Change should be backported to release-1.11 labels Sep 19, 2024
This turns the existing segfault into a warning, as on 1.9:
```julia
julia> using Foo, Bar
WARNING: @ccallable was already defined for this method name
```

Resolves JuliaLang#54878
@vchuravy
Copy link
Member

vchuravy commented Sep 20, 2024

Should we instead turn it into a proper error? Especially since we are deleting an assert?

@vchuravy vchuravy added the needs tests Unit tests are required for this change label Sep 20, 2024
@topolarity
Copy link
Member Author

Should we instead turn it into a proper error?

I think we should do that, but also make @ccallable register w/ the JIT much less eagerly.

These functions aren't actually available to dlsym etc., so I'm not sure why/whether we even need to do this JIT registration unless you're actually in imaging mode (and also trying to build a monolithic sysimage?)

Especially since we are deleting an assert?

The assert was just wrong - it assumed there is only one (pkg/sys)image loaded system-wide and therefore naming conflicts cannot occur.

@KristofferC KristofferC mentioned this pull request Sep 24, 2024
30 tasks
@vchuravy
Copy link
Member

These functions aren't actually available to

I think so that

julia/test/llvmcall.jl

Lines 154 to 177 in c3af4fc

@ccallable Cvoid function jl_the_callback()
global didcall
didcall = true
nothing
end
@test_throws(ErrorException("@ccallable was already defined for this method name"),
@eval @ccallable Cvoid jl_the_callback(not_the_method::Int) = "other")
# Make sure everything up until here gets compiled
@test jl_the_callback() === nothing
@test jl_the_callback(1) == "other"
didcall = false
function do_the_call()
llvmcall(
("""declare void @jl_the_callback()
define void @entry() #0 {
0:
call void @jl_the_callback()
ret void
}
attributes #0 = { alwaysinline }
""", "entry"),Cvoid,Tuple{})
end
do_the_call()
@test didcall
work. Which you can use for other fun shenanigans.

KristofferC added a commit that referenced this pull request Sep 25, 2024
Backported PRs:
- [x] #55773 <!-- Add compat entry for `Base.donotdelete` -->
- [x] #41244 <!-- Fix shell `cd` error when working dir has been deleted
-->
- [x] #55795 <!-- fix #52986, regression in `@doc` of macro without REPL
loaded -->
- [x] #55829 <!-- [Dates] Make test more robust against non-UTC
timezones -->
- [x] #55641 <!-- fall back to slower stat filesize if optimized
filesize fails -->
- [x] #55744 <!-- fix #45494, error in ssa conversion with complex type
decl -->
- [x] #55783 <!-- use `inferencebarrier` instead of `invokelatest` for
1-arg `@assert` -->
- [x] #55739 <!-- Add `invokelatest` barrier to `string(...)` in
`@assert` -->

Need manual backport:
- [ ] #55798 <!-- Broadcast binary ops involving strided triangular -->

Contains multiple commits, manual intervention needed:
- [ ] #55509 <!-- Fix cong implementation to be properly random and not
just cycling. -->
- [ ] #55569 <!-- Add a docs section about loading/precomp/ttfx time
tuning -->
- [ ] #55824 <!-- Replace regex package module checks with actual code
checks -->

Non-merged PRs with backport label:
- [ ] #55845 <!-- privatize annotated string API, take two -->
- [ ] #55828 <!-- Fix some corner cases of `isapprox` with unsigned
integers -->
- [ ] #55813 <!-- Check for conflicting `@ccallable` name before JIT
registration -->
- [ ] #55743 <!-- doc: heap snapshot viewing -->
- [ ] #55741 <!-- Change annotations to use a NamedTuple -->
- [ ] #55534 <!-- Set stdlib sources as read-only during installation
-->
- [ ] #55499 <!-- propagate the terminal's `displaysize` to the
`IOContext` used by the REPL -->
- [ ] #55458 <!-- Allow for generically extracting unannotated string
-->
- [ ] #55457 <!-- Make AnnotateChar equality consider annotations -->
- [ ] #55453 <!-- Privatise the annotations API, for StyledStrings -->
- [ ] #55355 <!-- relocation: account for trailing path separator in
depot paths -->
- [ ] #55220 <!-- `isfile_casesensitive` fixes on Windows -->
- [ ] #55169 <!-- `propertynames` for SVD respects private argument -->
- [ ] #54457 <!-- Make `String(::Memory)` copy -->
- [ ] #53957 <!-- tweak how filtering is done for what packages should
be precompiled -->
- [ ] #51479 <!-- prevent code loading from lookin in the versioned
environment when building Julia -->
- [ ] #50813 <!-- More doctests for Sockets and capitalization fix -->
- [ ] #50157 <!-- improve docs for `@inbounds` and
`Base.@propagate_inbounds` -->
@topolarity
Copy link
Member Author

Hmm yeah - feels like that behavior ought to be opt-in though

@vchuravy
Copy link
Member

julia> Base.@ccallable Cvoid function jl_the_callback()
                global didcall
                didcall = true
                nothing
            end

julia> function call()
           ccall("extern jl_the_callback", llvmcall, Nothing, ())
       end
call (generic function with 1 methods)

julia> call()

julia> didcall
true

Another variant how this is used (since I did encounter this myself again)

julia> Base.@ccallable Cvoid function jl_the_callback()
                global didcall
                didcall = true
                nothing
            end

julia> Base.@ccallable Cvoid function jl_the_callback()
                global didcall
                didcall = true
                nothing
            end
ERROR: @ccallable was already defined for this method name
Stacktrace:
 [1] _ccallable(rt::Type, sigt::Type)
   @ Base ./c.jl:513
 [2] macro expansion
   @ c.jl:544 [inlined]
 [3] top-level scope
   @ REPL[2]:1

Really, that's the same error we want to give here, and that the error you would get in a systemimage.
Of course with pkgimages this is annoying since we mostly are done loading.

@topolarity
Copy link
Member Author

Of course with pkgimages this is annoying since we mostly are done loading.

Throwing the error isn't really the annoying part imo - it's that it's not always avoiding a real problem:

  1. If you just wanted a ccallable for PackageCompiler workflows, then having a warning / error is annoying because your code works just fine without the ccallable (but you have no way to turn it off)
  2. If you wanted a ccallable for llvmcall then you're forced to fight over a single namespace, which is only composable with a lot of user discipline to include unique prefixes.

Which is why I think it might be better to separate these two use cases

@topolarity
Copy link
Member Author

For my use case, I wanted to test side-by-side two versions of a package for use with PackageCompiler.

I ended up writing a terrible wrapper to save all the ccallable registrations, and then defer their registration until an explicit point in time that I can activate in the PackageCompiler build

Otherwise it was impossible to import both modules without warnings / errors

@vchuravy
Copy link
Member

If you wanted a ccallable for llvmcall then you're forced to fight over a single namespace, which is only composable with a lot of user discipline to include unique prefixes.

Yeah, but that is the universe we live in. Even if I want those packages for use in PackageCompiler I need to use prefixes to ensure that my names are unique.

The question is "could we come up with a better namespace strategy than the user"? Maybe, we could automatically add the package (module?, FQN?) as a prefix. Even for your use-case you likely had to do some shenaigans like changing the UUID and renaming a package to MyPackagev1 for both version to be loaded into the same Julia process at the same time.

@topolarity
Copy link
Member Author

Even if I want those packages for use in PackageCompiler I need to use prefixes to ensure that my names are unique.

Yeah, for PackageCompiler you're specifically exporting for the linker, so we're forced to accept the realities of C namespacing

But only when you're actually building the sysimage - so for my use case, I can load vA and vB just fine in an interactive session

The question is "could we come up with a better namespace strategy than the user"? Maybe, we could automatically add the package (module?, FQN?) as a prefix.

I think it's pretty much equivalent to the gensym problem - but I have to admit I don't know how much we try to make gensym unique across pkgimages

@KristofferC KristofferC mentioned this pull request Sep 30, 2024
39 tasks
KristofferC added a commit that referenced this pull request Oct 1, 2024
Backported PRs:
- [x] #55849 <!-- Mmap: fix grow! for non file IOs -->
- [x] #55863 <!-- Update TaskLocalRNG docstring according to #49110 -->
- [x] #54433 <!-- Root globals in toplevel exprs -->
- [x] #55828 <!-- Fix some corner cases of `isapprox` with unsigned
integers -->
- [x] #55890 <!-- Profile: fix order of fields in heapsnapshot & improve
formatting -->
- [x] #55884 <!-- inference: add missing `TypeVar` handling for
`instanceof_tfunc` -->
- [x] #55881 <!-- Install terminfo data under /usr/share/julia -->
- [x] #55909 <!-- do not intentionally suppress errors in precompile
script from being reported or failing the result -->
- [x] #55355 <!-- relocation: account for trailing path separator in
depot paths -->
- [x] #55917 <!-- fix rawbigints OOB issues -->
- [x] #55892 <!-- TOML: Avoid type-pirating `Base.TOML.Parser` -->
- [x] #55798 <!-- Broadcast binary ops involving strided triangular -->
- [x] #55919 <!-- Limit `@inbounds` to indexing in the dual-iterator
branch in `copyto_unaliased!` -->

Contains multiple commits, manual intervention needed:
- [ ] #54009 <!-- allow extensions to trigger from packages in [deps]
-->
- [ ] #55509 <!-- Fix cong implementation to be properly random and not
just cycling. -->
- [ ] #55569 <!-- Add a docs section about loading/precomp/ttfx time
tuning -->
- [ ] #55824 <!-- Replace regex package module checks with actual code
checks -->

Non-merged PRs with backport label:
- [ ] #55932 <!-- REPL: make UndefVarError aware of imported modules -->
- [ ] #55910 <!-- Prevent extensions from blocking parallel
pre-compilation -->
- [ ] #55908 <!-- add logic to prefer loading modules that are already
loaded -->
- [ ] #55886 <!-- irrationals: restrict assume effects annotations to
known types -->
- [ ] #55871 <!-- lowering: don't reverse handler order in
`(pop-handler-list ...)` -->
- [ ] #55870 <!-- fix infinite recursion in `promote_type` for
`Irrational` -->
- [ ] #55867 <!-- update `hash` doc string: `widen` not required any
more -->
- [ ] #55851 <!-- [REPL] Fix #55850 by using `safe_realpath` instead of
`abspath` in `projname` -->
- [ ] #55813 <!-- Check for conflicting `@ccallable` name before JIT
registration -->
- [ ] #55743 <!-- doc: heap snapshot viewing -->
- [ ] #55741 <!-- Change annotations to use a NamedTuple -->
- [ ] #55534 <!-- Set stdlib sources as read-only during installation
-->
- [ ] #55499 <!-- propagate the terminal's `displaysize` to the
`IOContext` used by the REPL -->
- [ ] #55458 <!-- Allow for generically extracting unannotated string
-->
- [ ] #55457 <!-- Make AnnotateChar equality consider annotations -->
- [ ] #55220 <!-- `isfile_casesensitive` fixes on Windows -->
- [ ] #55169 <!-- `propertynames` for SVD respects private argument -->
- [ ] #54457 <!-- Make `String(::Memory)` copy -->
- [ ] #53957 <!-- tweak how filtering is done for what packages should
be precompiled -->
- [ ] #51479 <!-- prevent code loading from lookin in the versioned
environment when building Julia -->
- [ ] #50813 <!-- More doctests for Sockets and capitalization fix -->
- [ ] #50157 <!-- improve docs for `@inbounds` and
`Base.@propagate_inbounds` -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport 1.10 Change should be backported to the 1.10 release backport 1.11 Change should be backported to release-1.11 needs tests Unit tests are required for this change
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Segfault when loading conflicting @ccallable names from multiple pkgimages
2 participants