Search code examples
rustcobolgnucobol

GnuCOBOL calling Rust: libcob: error: module not found


I want to call Rust from GnuCOBOL. I've copied the code from the first example in Can GnuCOBOL interface with Rust? from Brian Tiffin's GNUCobol FAQ as a test but got an error when running it.

Expected:

$ make -B
rustc --crate-type=dylib called.rs
LD_RUN_PATH=. cobc -xj caller.cob -L. -lcalled
:Hello, world:

Actual:

$ make -B
rustc --crate-type=dylib called.rs
LD_RUN_PATH=. cobc -xj caller.cob -L. -lcalled
libcob: error: module 'hello_rust' not found
make: *** [makefile:5: caller] Error 1

I get the same error after compiling both files from the command line, then using $ ./caller.

The syntax seems correct based on the cobc man page and the linkage sections of the GnuCOBOL manual and Rust reference. I've tried $ ./caller COB_LIBRARY_PATH=. as described in the GnuCOBOL manual, but it doesn't make a difference. The Rust source compiles to a library as expected, but the COBOL doesn't find it.

Using $ cobcrun caller instead displays libcob: error: module 'caller' not found.

This question about a similar error is about statically linking multiple COBOL source files, which works fine, and this question about a similar error seems to be an issue with X"AF", which isn't used here. Statically linking C source with Jay Moseley's C Wrapper for Calling Library Function example works as expected. Statically linking Rust source isn't supported.

Software Versions:

  • Ubuntu 22.04.1 LTS
  • cobc (GnuCOBOL) 3.1.2.0
  • rustc 1.64.0

Solution

  • The issue seems to be that the COBOL caller executable is trying to dynamically load a library named hello_rust.so instead of libcalled.so at runtime.

    • The easy fix without modifying anything is to just create a symlink:

      $ ln -s libcalled.so hello_rust.so
      
    • Alternatively, adding -fstatic to the cobc command should statically link the Rust library at compilation, eliminating the dynamic library runtime calls.

      The example Makefile can be updated to look like this:

      # GnuCOBOL and Rust
      .RECIPEPREFIX = >
      
      caller: caller.cob libcalled.so
      > LD_RUN_PATH=. cobc -fstatic -xj caller.cob -L. -lcalled
      
      libcalled.so: called.rs
      > rustc --crate-type=dylib called.rs
      

    For reference, we can investigate what the executable is doing via strace to see what system calls the COBOL runtime is making -- and in our case -- what files it's failing to find.

    $ strace ./caller 2>&1 | grep hello_rust                                                                                                                                         
    access("./hello_rust.so", R_OK)         = -1 ENOENT (No such file or directory)
    access("/usr/lib64/gnucobol/hello_rust.so", R_OK) = -1 ENOENT (No such file or directory)
    write(2, "module 'hello_rust' not found", 29module 'hello_rust' not found) = 29