Description:
On macOS I am not able to link a C program with libmysqlclient.dylib using rpath. For example:
"main.c":
#include <stdio.h>
#include <mysql.h>
#include <stdlib.h>
int main() {
printf("MySQL client version: %s\n", mysql_get_client_info());
return 0;
}
Before I compile the above program I check in the shell:
$ mysql_config --variable=pkglibdir
/usr/local/mysql-8.0.26-macos11-arm64/lib
$ mysql_config_orig --cflags
-I/usr/local/mysql-8.0.26-macos11-arm64/include
$ mysql_config_orig --libs
-L/usr/local/mysql-8.0.26-macos11-arm64/lib -lmysqlclient -lssl -lcrypto -lresolv
Now I compile main.c like this:
$ libdir=$(mysql_config --variable=pkglibdir); \
cc `mysql_config --cflags --libs` -Wl,-rpath,$libdir main.c -o main
Then I try to run the "main" executable:
$ ./main
dyld: Library not loaded: libssl.1.1.dylib
Referenced from: /Users/hakonhaegland/test/main
Reason: image not found
[1] 9667 abort ./main
I check that the libraries exists:
$ ls /usr/local/mysql-8.0.26-macos11-arm64/lib/lib*dylib
/usr/local/mysql-8.0.26-macos11-arm64/lib/libcrypto.1.1.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libprotobuf-lite.dylib
/usr/local/mysql-8.0.26-macos11-arm64/lib/libcrypto.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libprotobuf.3.11.4.dylib
/usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.21.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libprotobuf.dylib
/usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.1.1.dylib
/usr/local/mysql-8.0.26-macos11-arm64/lib/libprotobuf-lite.3.11.4.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.dylib
So the libraries libssl.dylib exists in the libdir, but the problem is that its install name (LC_ID_DYLIB) is not prepended with the magic string `@rpath` that allows the library to be loaded using the `rpath` in the "main" executable. This can be confirmed by using "otool":
$ otool -D /usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.dylib
/usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.dylib:
libssl.1.1.dylib
$ otool -L /usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.dylib
/usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.dylib:
libssl.1.1.dylib (compatibility version 1.1.0, current version 1.1.0)
@loader_path/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)
whereas libmysqlclient.dylib has the correct install name to be used with rpath:
$ otool -D /usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.dylib
/usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.dylib:
@rpath/libmysqlclient.21.dylib
$ otool -L /usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.dylib
/usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.dylib:
@rpath/libmysqlclient.21.dylib (compatibility version 21.0.0, current version 21.0.0)
@loader_path/../lib/libssl.1.1.dylib (compatibility version 1.1.0, current version 1.1.0)
@loader_path/../lib/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0)
/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 904.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)
We also observe from the above output that libmysqlclient.dylib uses the magic "@loader_path" prefix to load libssl.dylib and libcrypto.dylib.
It means that I do not need to specify "-lssl -lcrypto" when I compile "main.cpp", I only need to specify "-lmysqlclient" and when the dynamic loader loads "libmysqlclient.dylib" it will automatically load libssl.dylib and libcrypto.dylib from the "@loader_path" paths relative to the directory where libmysqlclient.dylib is located. I can verify this by recompiling "main.cpp" like this:
$ libdir=$(mysql_config --variable=pkglibdir); \
cc `mysql_config --cflags` -L$libdir -lmysqlclient -Wl,-rpath,$libdir main.c -o main
If I now run the executable "main" it works fine:
$ ./main
MySQL client version: 8.0.26
How to repeat:
See description for how to repeat.
Suggested fix:
I believe "mysql_config --libs" should be patched so that instead of returning:
-L/usr/local/mysql-8.0.26-macos11-arm64/lib -lmysqlclient -lssl -lcrypto -lresolv
it should simply return:
-L/usr/local/mysql-8.0.26-macos11-arm64/lib -lmysqlclient
Here is a patch for "/usr/local/mysql-8.0.26-macos11-arm64/bin/mysql_config" that works for me:
121c121
< libs="$libs -lmysqlclient -lssl -lcrypto -lresolv"
---
> libs="$libs -lmysqlclient"
Description: On macOS I am not able to link a C program with libmysqlclient.dylib using rpath. For example: "main.c": #include <stdio.h> #include <mysql.h> #include <stdlib.h> int main() { printf("MySQL client version: %s\n", mysql_get_client_info()); return 0; } Before I compile the above program I check in the shell: $ mysql_config --variable=pkglibdir /usr/local/mysql-8.0.26-macos11-arm64/lib $ mysql_config_orig --cflags -I/usr/local/mysql-8.0.26-macos11-arm64/include $ mysql_config_orig --libs -L/usr/local/mysql-8.0.26-macos11-arm64/lib -lmysqlclient -lssl -lcrypto -lresolv Now I compile main.c like this: $ libdir=$(mysql_config --variable=pkglibdir); \ cc `mysql_config --cflags --libs` -Wl,-rpath,$libdir main.c -o main Then I try to run the "main" executable: $ ./main dyld: Library not loaded: libssl.1.1.dylib Referenced from: /Users/hakonhaegland/test/main Reason: image not found [1] 9667 abort ./main I check that the libraries exists: $ ls /usr/local/mysql-8.0.26-macos11-arm64/lib/lib*dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libcrypto.1.1.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libprotobuf-lite.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libcrypto.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libprotobuf.3.11.4.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.21.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libprotobuf.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.1.1.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libprotobuf-lite.3.11.4.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.dylib So the libraries libssl.dylib exists in the libdir, but the problem is that its install name (LC_ID_DYLIB) is not prepended with the magic string `@rpath` that allows the library to be loaded using the `rpath` in the "main" executable. This can be confirmed by using "otool": $ otool -D /usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.dylib: libssl.1.1.dylib $ otool -L /usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libssl.dylib: libssl.1.1.dylib (compatibility version 1.1.0, current version 1.1.0) @loader_path/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1) whereas libmysqlclient.dylib has the correct install name to be used with rpath: $ otool -D /usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.dylib: @rpath/libmysqlclient.21.dylib $ otool -L /usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.dylib /usr/local/mysql-8.0.26-macos11-arm64/lib/libmysqlclient.dylib: @rpath/libmysqlclient.21.dylib (compatibility version 21.0.0, current version 21.0.0) @loader_path/../lib/libssl.1.1.dylib (compatibility version 1.1.0, current version 1.1.0) @loader_path/../lib/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0) /usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0) /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 904.4.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1) We also observe from the above output that libmysqlclient.dylib uses the magic "@loader_path" prefix to load libssl.dylib and libcrypto.dylib. It means that I do not need to specify "-lssl -lcrypto" when I compile "main.cpp", I only need to specify "-lmysqlclient" and when the dynamic loader loads "libmysqlclient.dylib" it will automatically load libssl.dylib and libcrypto.dylib from the "@loader_path" paths relative to the directory where libmysqlclient.dylib is located. I can verify this by recompiling "main.cpp" like this: $ libdir=$(mysql_config --variable=pkglibdir); \ cc `mysql_config --cflags` -L$libdir -lmysqlclient -Wl,-rpath,$libdir main.c -o main If I now run the executable "main" it works fine: $ ./main MySQL client version: 8.0.26 How to repeat: See description for how to repeat. Suggested fix: I believe "mysql_config --libs" should be patched so that instead of returning: -L/usr/local/mysql-8.0.26-macos11-arm64/lib -lmysqlclient -lssl -lcrypto -lresolv it should simply return: -L/usr/local/mysql-8.0.26-macos11-arm64/lib -lmysqlclient Here is a patch for "/usr/local/mysql-8.0.26-macos11-arm64/bin/mysql_config" that works for me: 121c121 < libs="$libs -lmysqlclient -lssl -lcrypto -lresolv" --- > libs="$libs -lmysqlclient"