JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.41k stars 5.45k forks source link

Bug in `ccall(:printf)` on macos: "%s" gives `(null)`. But differs between downloaded binary and source builds #52766

Closed NHDaly closed 8 months ago

NHDaly commented 8 months ago

Calling printf through ccall, which is weird because it's a varargs function, seems to have an issue in source builds (and julia-up installed builds) of julia on macos.

It seems to incorrectly be getting a nullptr for the "b" argument:

$ ~/src/julia/julia --startup=no -q
julia> VERSION
v"1.11.0-DEV.1167"

julia> ccall(:printf, Cvoid, (Cstring, Cstring), "a %s\n", "b")
a (null)

$ julia +1.9.3 --startup=no -q
julia> VERSION
v"1.9.3"

julia> ccall(:printf, Cvoid, (Cstring, Cstring), "a %s\n", "b")
a (null)

$ /Applications/Julia-1.9.app/Contents/Resources/julia/bin/julia --startup=no -q
julia> VERSION
v"1.9.3"

julia> ccall(:printf, Cvoid, (Cstring, Cstring), "a %s\n", "b")
a b

But as you can see, if you start julia through the downloaded binary release, it works fine! 😱

Could this have something to do with which version of some dependency (like libc?) is being linked? Thanks

NHDaly commented 8 months ago

CC: @kuszmaul

giordano commented 8 months ago

You have to use @ccall with varargs functions with different types: https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/#@ccall-/-@cfunction-argument-translation-guide

KristofferC commented 8 months ago

Here all args are Cstring so this applies I think (taken from the table a bit down in https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/#ccall-interface):

image

If you do use different arg types then you need @ccall (ref https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/#Variadic-function-calls).

gbaraldi commented 8 months ago

Is the generated assembly different? Also is it x86 or aarch64?

giordano commented 8 months ago

Here all args are Cstring so this applies I think (taken from the table a bit down in https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/#ccall-interface):

image

If you do use different arg types then you need @ccall (ref https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/#Variadic-function-calls).

julia> ccall(:printf, Cvoid, (Cstring, Cstring), "a %s\n", "b")
a (null)

julia> @ccall printf("a = %s\n"::Cstring; "b"::Cstring)::Cint;
a = b

julia> versioninfo()
Julia Version 1.11.0-DEV.1206
Commit aeba6d19d4 (2024-01-05 18:23 UTC)
Platform Info:
  OS: macOS (arm64-apple-darwin21.6.0)
  CPU: 8 × Apple M1
  WORD_SIZE: 64
  LLVM: libLLVM-15.0.7 (ORCJIT, apple-m1)
KristofferC commented 8 months ago

@giordano, yeah.. you didn't use the vararg syntax?

julia> ccall(:printf, Cvoid, (Cstring, Cstring),    "a %s\n", "b")
a (null)

julia> ccall(:printf, Cvoid, (Cstring, Cstring...), "a %s\n", "b")
a b
giordano commented 8 months ago

I just copied the code in the first message

KristofferC commented 8 months ago

Ok, why?

You said

You have to use @ccall with varargs functions with different types:

Here, there is only Cstring so you do not need to use @ccall.

kuszmaul commented 8 months ago

I think the problem is just a documentation error in https://docs.julialang.org/en/v1/base/base/#Base.finalizer

In the instructions for finalizer, it shows an example of calling jl_safe_printf

bad:

ccall(:jl_safe_printf, Cvoid, (Cstring, Cstring), "Finalizing %s.", repr(x))

when it should have been

good:

ccall(:jl_safe_printf, Cvoid, (Cstring, Cstring...), "Finalizing %s.", "b")

One could use @ccall when the variadic arguments aren't all the same: good:

@ccall jl_safe_printf("Finalizing %s %d.\n"::Cstring; "b"::Cstring, 5::Cint)::Int

Note the ; when the variadic argument start.

NHDaly commented 8 months ago

Thanks all, great to know. @kuszmaul: Any chance you can send a docs PR for the above fix?

NHDaly commented 8 months ago

Also IMO the ccall docstring should really mention the varargs pattern, which it currently does not.

Screenshot 2024-01-08 at 1 00 06 PM