Repository: JuliaLang/julia
File: test/ccall.jl (Julia)

Code before:
    return Base.get_world_counter()
end
let llvm = sprint(code_llvm, world_counter, ())
    # check that we got a reasonable value for the world age
    @test (world_counter() != 0) && (world_counter() != -1)
    # no call to the runtime should be left over
    @test !occursin("call i64", llvm)
    # the world age should be -1 in generated functions (or other pure contexts)
    @test (generated_world_counter() == reinterpret(UInt, -1))
end

function gc_safe_ccall()
    # jl_rand is marked as JL_NOTSAFEPOINT
    @ccall gc_safe=true jl_rand()::UInt64
end

let llvm = sprint(code_llvm, gc_safe_ccall, ())
    # check that the call works
    @test gc_safe_ccall() isa UInt64
    # check for the gc_safe store
    @test occursin("store atomic i8 2", llvm)
end

@testset "jl_dlfind and dlsym" begin
    # We shouldn't be able to call libc functions through libccalltest
    @test_throws ErrorException ccall((:sqrt, libccalltest), Cdouble, (Cdouble,), 2.0)
    # Test that jl_dlfind finds things in the expected places.
    @test ccall(:jl_dlfind, Int, (Cstring,), "doesnotexist") == 0       # not found (RTLD_DEFAULT)
    @static if !Sys.iswindows()
        @test ccall(:jl_dlfind, Int, (Cstring,), "main") == 1               # JL_EXE_LIBNAME
    end
    @test ccall(:jl_dlfind, Int, (Cstring,), "jl_gc_safepoint") == 2    # JL_LIBJULIA_DL_LIBNAME
    @test ccall(:jl_dlfind, Int, (Cstring,), "ijl_gc_small_alloc") == 3 # JL_LIBJULIA_INTERNAL_DL_LIBNME
    @test ccall(:jl_dlfind, Int, (Cstring,), "malloc") ∉ (1, 2, 3)      # Either 0 or msvcrt.dll on Windows
    let hdl = Libdl.dlopen(libccalltest, Libdl.RTLD_GLOBAL)
        try
            @static if Sys.iswindows()
                @test_throws ErrorException ccall(:get_c_int, Cint, ())
            else
                @test ccall(:get_c_int, Cint, ()) isa Cint
            end
        finally
            Libdl.dlclose(hdl)
        end
    end
end

module Test57749
using Test, Zstd_jll
const prefix = "Zstd version: "
const sym = :ZSTD_versionString

Review comment (bug):
An equivalent C program works:
```julia
julia> cc = `cc`;

julia> program = raw"""
       #define _GNU_SOURCE
       #include <dlfcn.h>
       #include <stdio.h>
       #include <stdlib.h>

       int main(void) {
           void *h = dlopen("./libccalltest.so", RTLD_LAZY);
           if (!h) {
               fprintf(stderr, "dlopen failed: %s\n", dlerror());
               return 1;
           }

           dlerror(); // clear any existing error

           // function pointer type for sqrt(double) -> double
           typedef double (*sqrt_fn)(double);

           sqrt_fn my_sqrt = (sqrt_fn)dlsym(h, "sqrt");
           const char *err = dlerror();
           if (err) {
               fprintf(stderr, "dlsym failed: %s\n", err);
               dlclose(h);
               return 1;
           }

           double x = 2.0;
           double y = my_sqrt(x);
           printf("sqrt(%g) = %.17g\n", x, y);

           dlclose(h);
           return 0;
       }
       """;

julia> run(pipeline(`$(cc) -x c -fPIC -shared -o libccalltest.so -lm -`; stdin=devnull));

julia> run(pipeline(`$(cc) -x c -L. -lccalltest -o test -`; stdin=IOBuffer(program)));

julia> run(`./test`);
sqrt(2) = 1.4142135623730951

julia> @ccall "./libccalltest.so".sqrt(2.0::Cdouble)::Cdouble
ERROR: could not load symbol "sqrt":
dlsym(0x392efeb41, sqrt): symbol not found
Stacktrace:
 [1] top-level scope
   @ ./REPL[10]:1
```
So yes, we shouldn't be stricter than what normal C applications can do.  Thanks for fixing it!

Code after:
    return Base.get_world_counter()
end
let llvm = sprint(code_llvm, world_counter, ())
    # check that we got a reasonable value for the world age
    @test (world_counter() != 0) && (world_counter() != -1)
    # no call to the runtime should be left over
    @test !occursin("call i64", llvm)
    # the world age should be -1 in generated functions (or other pure contexts)
    @test (generated_world_counter() == reinterpret(UInt, -1))
end

function gc_safe_ccall()
    # jl_rand is marked as JL_NOTSAFEPOINT
    @ccall gc_safe=true jl_rand()::UInt64
end

let llvm = sprint(code_llvm, gc_safe_ccall, ())
    # check that the call works
    @test gc_safe_ccall() isa UInt64
    # check for the gc_safe store
    @test occursin("store atomic i8 2", llvm)
end

@testset "jl_dlfind and dlsym" begin
    # Test that jl_dlfind finds things in the expected places.
    @test ccall(:jl_dlfind, Int, (Cstring,), "doesnotexist") == 0       # not found (RTLD_DEFAULT)
    @static if !Sys.iswindows()
        @test ccall(:jl_dlfind, Int, (Cstring,), "main") == 1               # JL_EXE_LIBNAME
    end
    @test ccall(:jl_dlfind, Int, (Cstring,), "jl_gc_safepoint") == 2    # JL_LIBJULIA_DL_LIBNAME
    @test ccall(:jl_dlfind, Int, (Cstring,), "ijl_gc_small_alloc") == 3 # JL_LIBJULIA_INTERNAL_DL_LIBNME
    @test ccall(:jl_dlfind, Int, (Cstring,), "malloc") ∉ (1, 2, 3)      # Either 0 or msvcrt.dll on Windows
    let hdl = Libdl.dlopen(libccalltest, Libdl.RTLD_GLOBAL)
        try
            @static if Sys.iswindows()
                @test_throws ErrorException ccall(:get_c_int, Cint, ())
            else
                @test ccall(:get_c_int, Cint, ()) isa Cint
            end
        finally
            Libdl.dlclose(hdl)
        end
    end
end

module Test57749
using Test, Zstd_jll
const prefix = "Zstd version: "
const sym = :ZSTD_versionString
get_zstd_version() = prefix * unsafe_string(ccall((sym, libzstd), Cstring, ()))
@test startswith(get_zstd_version(), "Zstd")