From 9e283e98682b1a2ae24f7caba8f61b3d47cea3fd Mon Sep 17 00:00:00 2001 From: JKRT_CLAUDE <247156613+SVAGEN26@users.noreply.github.com> Date: Sat, 13 Jun 2026 21:37:11 +0200 Subject: [PATCH 1/2] Pre-load OMC shared libraries by absolute path in topological order The prebuilt .so files shipped in the libs-v0.1.0 release have RUNPATH baked to the original build host (/home/johti17/Projects/OpenModelica/ build_cmake/...), so ld.so cannot resolve inter-library DT_NEEDED entries on consumer machines. The previous __init__ only dlopen'd libModelicaCallbacks, libModelicaIO and libModelicaExternalC, leaving libOpenModelicaRuntimeC, libomcgc, libModelicaMatIO, libModelicaStandardTables and libSimulationRuntimeC unresolved. As a result loading libModelicaStandardTables.so fails with "libModelicaIO.so: cannot open shared object file" whenever an external Modelica function is invoked, even though libModelicaIO.so sits next to it on disk. The ENV["LD_LIBRARY_PATH"] mutation in the old __init__ is ineffective on Linux because glibc's ld.so caches LD_LIBRARY_PATH at process start. Dlopen every shipped library by absolute path with RTLD_GLOBAL in a fixed topological order. ld.so reuses the already-loaded library when it sees the same SONAME on a dependent load, so DT_NEEDED references are satisfied without relying on RUNPATH or LD_LIBRARY_PATH. The libModelicaCallbacks shim is still loaded first so its symbols win global resolution and override the default OMC setjmp/longjmp-based error handlers. Verified with the libs-v0.1.0 zip on Linux: dlopen of libModelicaStandardTables.so now succeeds where it previously failed with the same error observed in JKRT/OM.jl#44 CI. Refs JKRT/OM.jl#44 Co-Authored-By: JKRT_CLAUDE <247156613+SVAGEN26@users.noreply.github.com> --- src/OMRuntimeExternalC.jl | 68 +++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/src/OMRuntimeExternalC.jl b/src/OMRuntimeExternalC.jl index d9f9816..6b9fe85 100644 --- a/src/OMRuntimeExternalC.jl +++ b/src/OMRuntimeExternalC.jl @@ -34,37 +34,49 @@ function locateSharedParserLibrary(directoryToSearchIn, libraryName, relativeDir nothing end +#= Topological load order for the OMC shared libraries shipped with this + package. Each entry only references SONAMEs of entries that appear before + it, so dlopen'ing them in this sequence with RTLD_GLOBAL satisfies every + DT_NEEDED reference for the dependent libraries. libModelicaCallbacks is + first so its symbols win global resolution and override the default OMC + setjmp/longjmp-based error handlers. =# +const _LIB_LOAD_ORDER = ( + "libModelicaCallbacks", + "libomcgc", + "libOpenModelicaRuntimeC", + "libModelicaMatIO", + "libModelicaIO", + "libModelicaStandardTables", + "libModelicaExternalC", + "libSimulationRuntimeC", +) + function __init__() - try - if installedLibPath !== nothing - local libdir = splitdir(installedLibPath)[1] - if Sys.iswindows() - #= On Windows, add to PATH for DLL search =# - Base._setenv("PATH", ENV["PATH"] * ";" * libdir) - else - #= On Linux/macOS, add to both DL_LOAD_PATH and LD_LIBRARY_PATH =# - push!(Libdl.DL_LOAD_PATH, libdir) - #= Also set LD_LIBRARY_PATH for library dependencies loaded by the system linker =# - local ldpath = get(ENV, "LD_LIBRARY_PATH", "") - ENV["LD_LIBRARY_PATH"] = isempty(ldpath) ? libdir : libdir * ":" * ldpath + if installedLibPath === nothing + @warn "OMRuntimeExternalC: shared libraries not found. Simulations that use external Modelica functions will fail." + return nothing + end + local libdir = splitdir(installedLibPath)[1] + push!(Libdl.DL_LOAD_PATH, libdir) + if Sys.iswindows() + #= Windows resolves DLL dependencies via PATH at LoadLibrary time. =# + ENV["PATH"] = libdir * ";" * get(ENV, "PATH", "") + end + #= The prebuilt .so files have RUNPATH baked to the original build host, + so ld.so cannot resolve inter-library DT_NEEDED entries on its own. + Pre-load every dependency by absolute path in topological order with + RTLD_GLOBAL; ld.so reuses the already-loaded library when it sees the + same SONAME on a dependent load. =# + local ext = Sys.iswindows() ? ".dll" : (Sys.isapple() ? ".dylib" : ".so") + for name in _LIB_LOAD_ORDER + local p = joinpath(libdir, name * ext) + if isfile(p) + try + Libdl.dlopen(p, Libdl.RTLD_GLOBAL | Libdl.RTLD_LAZY) + catch err + @warn "OMRuntimeExternalC: failed to preload $name" path=p exception=err end end - #= Load the Julia-compatible ModelicaCallbacks shim FIRST with RTLD_GLOBAL - so it overrides the OMC setjmp/longjmp-based error handlers. - Then load the other libraries with RTLD_GLOBAL so their symbols are - visible to dlsym (used by the safe_* wrappers in the callbacks shim). =# - if installedLibPathlibModelicaCallbacks !== nothing - Libdl.dlopen(installedLibPathlibModelicaCallbacks, Libdl.RTLD_GLOBAL) - end - if installedLibPathlibModelicaIO !== nothing - Libdl.dlopen(installedLibPathlibModelicaIO, Libdl.RTLD_GLOBAL) - end - if installedLibPathlibModelicaExternalC !== nothing - Libdl.dlopen(installedLibPathlibModelicaExternalC, Libdl.RTLD_GLOBAL) - end - catch - @warn "Failed to setup the environment correctly. Make sure that you have the correct shared libraries installed." - @warn "NOTE: If your Modelica model uses certain external functions your simulation might fail." end nothing end From 4783de00b1de512634bd15149aac8da2a5d1df74 Mon Sep 17 00:00:00 2001 From: JKRT_CLAUDE <247156613+SVAGEN26@users.noreply.github.com> Date: Sat, 13 Jun 2026 22:02:01 +0200 Subject: [PATCH 2/2] Bump version to 0.3.2 Co-Authored-By: JKRT_CLAUDE <247156613+SVAGEN26@users.noreply.github.com> --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b1fae66..abbeb1c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "OMRuntimeExternalC" uuid = "ada38df0-e20e-11ed-02f3-2b4b19c1ec8a" authors = ["Adrian Pop ", "John Tinnerholm "] -version = "0.3.1" +version = "0.3.2" [deps] CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193"