Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linking with .dylib library from the command line using clang

Tags:

c

macos

clang

dylib

I'm trying to write a C program using a library. The library has provided a include/ and lib/ directory with header files and .dylib files, respectively.

My file is in the same directory as include/ and lib/. I'm trying to compile it with the following command:

clang -I"./include/" -L"./lib/" -lcsfml-graphics -lcsfml-window test.c

However, when I go to run my program, I get the following error:

% ./a.out 
dyld: Library not loaded: @rpath/libcsfml-graphics.2.4.dylib
  Referenced from: ~/src/CSFML-2.4-osx-clang/./a.out
  Reason: image not found
zsh: abort      ./a.out

What is the correct way to compile using these libraries? I'd prefer to simply use the command line as I'm writing a small program, without having to set up Xcode, etc.

% ls -l lib/
-rwxr-xr-x@ 1  staff   50296 Mar  1  2017 libcsfml-audio.2.4.0.dylib*
lrwxr-xr-x@ 1  staff      26 Mar  1  2017 libcsfml-audio.2.4.dylib@ -> libcsfml-audio.2.4.0.dylib
lrwxr-xr-x@ 1  staff      24 Mar  1  2017 libcsfml-audio.dylib@ -> libcsfml-audio.2.4.dylib
-rwxr-xr-x@ 1  staff  163680 Mar  1  2017 libcsfml-graphics.2.4.0.dylib*
lrwxr-xr-x@ 1  staff      29 Mar  1  2017 libcsfml-graphics.2.4.dylib@ -> libcsfml-graphics.2.4.0.dylib
lrwxr-xr-x@ 1  staff      27 Mar  1  2017 libcsfml-graphics.dylib@ -> libcsfml-graphics.2.4.dylib
-rwxr-xr-x@ 1  staff   67272 Mar  1  2017 libcsfml-network.2.4.0.dylib*
lrwxr-xr-x@ 1  staff      28 Mar  1  2017 libcsfml-network.2.4.dylib@ -> libcsfml-network.2.4.0.dylib
lrwxr-xr-x@ 1  staff      26 Mar  1  2017 libcsfml-network.dylib@ -> libcsfml-network.2.4.dylib
like image 878
Joe the Person Avatar asked Jan 28 '23 06:01

Joe the Person


1 Answers

It seems that after fixing rpath with clang -I"./include/" -L"./lib/" -lcsfml-graphics -lcsfml-window -Wl,-rpath,"@executable_path/lib" test.c you're still missing SFML libraries. I've just checked that CSFML depends on SFML, and based on your ls -l lib/ listing, SFML is clearly absent in lib/. My guess is that after fixing rpath you may not have noticed that the absent dependency has changed to @rpath/libsfml-graphics.2.4.dylib instead of @rpath/libcsfml-graphics.2.4.dylib. Please, download SFML libraries and put them into lib/.

Now to your question, how to build on macOS from the command line. Your building steps are correct, so I suppose the difficult part is how the dependencies are searched by dyld, not how they're linked with ld.

A short theory.

There are 4 ways a binary (an executable or a dynamic library) references its dependencies:

  • by an absolute or relative path (the latter is relative to your working directory);
  • by @executable_path, expanding to the path of the executable which is the root of the dependency tree (in case you have recursive dependencies);
  • by @loader_path, expanding to the path of the executable or a library for which the dependency is searched, i.e. it's the path of the direct parent in the dependency tree;
  • by @rpath, which is substituted in turn by each rpath found in your binary, and this is the most flexible way as you can search in several directories by having multiple rpaths.

Now the question is how to ensure the dependencies are correctly referenced. As @mattmilten pointed out, there are 2 methods for that:

  • make your build system/build script/manual build commands ensure the references are correct;
  • use install_name_tool to correct the broken references.

Making it automatic during the build.

In order for the first method to work you need to make sure the identification names of the dependency libraries are correct. Suppose you're linking a binary against some library libA.dylib. The identification name of libA.dylib is the default reference which will be used by the linker (ld) when building your binary. You can find it by looking at the first line of otool -L libA.dylib (or alternatively at the LC_ID_DYLIB section of otool -l libA.dylib). In case of libcsfml-graphics.2.4.dylib it's @rpath/libcsfml-graphics.2.4.dylib and it was passed to a.out on linking, that's why you're seeing it in the error message when dyld fails to meet it.

Setting a correct identification name is a responsibility of the libA.dylib authors. It is set according to their expectations as to where libA.dylib could be placed (using @rpath is a good choice) as well as their versioning scheme (in case of CSFML version 2.4.0 could be replaced with 2.4.x without losing binary compatibility). If you're building libA.dylib yourself you can set it with -install_name param (e.g. clang -Wl,-dylib -Wl,-install_name,@executable_path/libA.dylib -o libA.dylib liba.c). If it's a 3rd party library you can change it with install_name_tool -id @rpath/libA.dylib libA.dylib.

Identification name, to the best of my knowledge, is only used during linking and not used when loading a binary, so if you prefer the second method of fixing incorrect references with install_name_tool you can ignore it.

Manual fixing.

Fixing the references with install_name_tool is simple but tedious. You'll probably need a script when there are a lot of incorrect references. All you need is the following set of commands (the binary placeholder should obviously be replaced with an actual binary name):

  • install_name_tool -change @executable_path/libA.dylib @rpath/libA.dylib binary to change reference @executable_path/libA.dylib -> @rpath/libA.dylib;
  • install_name_tool -add_rpath @executable_path/lib binary to add @executable_path/lib to rpaths;
  • install_name_tool -delete_rpath @executable_path/lib binary to remove @executable_path/lib from rpaths;
  • otool -l binary to check existing rpaths (just look at the LC_RPATH sections).
like image 183
miadz Avatar answered Feb 05 '23 17:02

miadz