A linking problem in macOS: no -R option

I need help from people familiar with linker options to fix a linking issue encountered only on macOS: macos 14.2 Compile Gfortran-13.2 using gtk-4-fortran --Error linker mapping · Issue #280 · vmagnin/gtk-fortran · GitHub

The pkgconfig file gtk-fortran.pc includes this line since May 2011:

Libs: -Xlinker -R${libdir} -l${libname}

After installing the gtk-fortran library with sudo make install, the command pkg-config --cflags --libs gtk-4-fortran is therefore expanding to:

[...]
-Xlinker -R/usr/local/lib -lgtk-4-fortran
[...]

But in macOS (using Homebrew), a linking error occurs:

ld: unknown option: -R/usr/local/lib
collect2: error: ld returned 1 exit status

The macOS linker does indeed not recognize the -R option.

In the GNU linker ld manual (GNU Binutils version 2.41), I read:

  • if the -R option is followed by a directory name, rather than a file name, it is treated as the -rpath option.

  • -rpath=dir ¶
    Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects. All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at runtime.

  • -L searchdir
    Add path searchdir to the list of paths that ld will search for archive libraries and ld control scripts.

In the /usr/local/lib directory we can find both the static and dynamic libraries:

libgtk-4-fortran.a
libgtk-4-fortran.dylib

In macOS, replacing -R by -L in the pkgconfig file fixes the linking error (the static library is therefore used instead of the dynamic version).

But I guess we should more precisely replace the line:

Libs: -Xlinker -R${libdir} -l${libname}

by

Libs: -Xlinker -rpath=${libdir} -L${libdir} -l${libname}

to deal both with static and dynamic/shared versions of the library (-rpath is recognized by the macOS linker).

Am I right?

(or should I put only -rpath ?)

The macOS linker ld64 does not accept the GNU ld syntax -Xlinker -rpath=${libdir}, but would accept a space instead of =. In that case -Xlinker must be used twice to pass each part of the option to the linker:

-Xlinker -rpath -Xlinker ${libdir}

But curiously, I had a problem with pkgconfig in Ubuntu with that syntax, the first -Xlinker being not returned (I have instead obtained: -rpath -Xlinker /usr/local/lib).

Happily, there is an alternative syntax with the -Wl option that is correctly treated in all systems:

-Wl,-rpath,${libdir}

Here, there is only one option -Wl with the different parts of the option to pass to the linker separated by commas.

Note that the -rpath option is writting the library path directly inside the executable, as can be verified by this command:

$ readelf --dynamic a.out

Finally, I have also decided to add the missing -L option that “add path searchdir to the list of paths that ld will search for archive [static] libraries and ld control scripts”. The last line of the pkgconfig file is now therefore:

Libs: -L${libdir} -Wl,-rpath,${libdir} -l${libname}

I have pushed it in my dev branch GitHub - vmagnin/gtk-fortran at gtk4-vmagnin where I will prepare the next release (probably in May), and tested linking in macOS, Ubuntu, Fedora, FreeBSD and MSYS2/Windows10.

I am therefore rather confident with this solution, but any comment is welcome as linking is a very special thing and I am not expert!