How do you reference a symbol from a specific module in a fortran shared object library with the ccall utility?

When I have a “simple” fortran program with a singular module, I can reference the symbols simply by copying the symbol name from the result of the nm command:

x = ccall((:__mod_julfort_MOD_dot, "path/to/my/lib.so"),
            Float64,
            (Ref{Int64}, Ptr{Float64}, Ptr{Float64}),
            l, arrx, arry)

However, I have a larger library with many different modules, so nm instead returns something like

00000000000ef4e0 T __my_mod_name.my_MOD_function_name

Simply copying __my_mod_name.my_MOD_function_name doesn’t work because the . is not a recognized syntax, and replacing . with _ like

x = ccall((:__my_mod_name_my_MOD_function_name, "path/to/my/lib.so"),
               ...

Returns the error

Error: ErrorException("could not load symbol \"__my_mod_name_my_MOD_function_name\":\n../path/to/my/lib.so: undefined symbol: __my_mod_name_my_MOD_function_name")

How do I correctly call this function using ccall?

I don’t have a Fortran module at hand for trying this out, but perhaps try with

Symbol("__my_mod_name.my_MOD_function_name")

?

This returns the error

Error: TypeError(Symbol("ccall: first argument not a pointer or valid constant expression"), "", Ptr, (Symbol("__my_mod_name.my_MOD_function_name"), "path/to/my/lib.so"))

Can you please show the code you’re using?

I am using the library DAMASK, which I compiled into a shared object library and where I created a new subroutine in the file phase_mechanical_plastic_phenopowerlaw.f90 called test_routine:

module subroutine test_routine(ph,group)
  integer         :: ph
  character(len=*) :: group
  print *, "test", ph, group
  ph = ph+2
end subroutine test_routine

My julia file looks like this:

function main()
    try
        err = ccall((Symbol("__phase.mechanical_MOD_test_routine"), "../DAMASK/lib/libDAMASK_grid.so"), 
                        Cvoid, 
                        (Int64,Ptr{UInt8}), 
                        3, "asd")
    catch e
        println("Error: ", e)
    end
end
main()

Uhm, I wonder if it doesn’t work because ccall doesn’t like the dynamic call to Symbol.

Does

using Libdl
sym = Libdl.dlsym(Libdl.dlopen("../DAMASK/lib/libDAMASK_grid.so"), Symbol("__phase.mechanical_MOD_test_routine"))
function main()
    try
        err = ccall(sym, 
                        Cvoid, 
                        (Int64,Ptr{UInt8}), 
                        3, "asd")
    catch e
        println("Error: ", e)
    end
end
main()

work any better?

1 Like

Your solution works and julia is now able to enter the fortran subroutine I defined. This is how the final working version looked, replacing the string with another int for simplicity:

using Libdl
sym = Libdl.dlsym(Libdl.dlopen("../DAMASK/lib/libDAMASK_grid.so"), Symbol("__phase.mechanical_MOD_test_routine"))
function main()
    try
        a::Int64 = 3
        b::Int64 = 4
        err = ccall(sym, 
                        Cvoid, 
                        (Ref{Int64},Ref{Int64}), 
                        a, b)
    catch e
        println("Error: ", e)
    end
end
main()
2 Likes

Small nitpick: you don’t need to type-annotate the local variables a and b, also, it’d be better to avoid non-constant global variables (this is a mistake I made in my post above). I’d slightly simplify your code to:

using Libdl
function main()
    sym = Libdl.dlsym(Libdl.dlopen("../DAMASK/lib/libDAMASK_grid.so"), Symbol("__phase.mechanical_MOD_test_routine"))
    try
        a = 3
        b = 4
        err = ccall(sym, 
                    Cvoid, 
                    (Ref{Int64},Ref{Int64}), 
                    a, b)
    catch e
        println("Error: ", e)
    end
end
main()