How to static link on OS X - macos

I'm trying to link to a static library on OS X. I used the -static flag in the gcc command but I get the following error message:
ld_classic: can't locate file for: -lcrt0.o
collect2: ld returned 1 exit status
I looked in the man pages and it reads something like:
This option will not work on Mac OS X unless all libraries (including libgcc.a) have also been compiled with -static. Since neither a static version of libSystem.dylib nor crt0.o are provided, this option is not useful to most people.
Is there another way to link to this static library?

In order to link to an archive library (sometimes also called static library), just add it to the link line:
gcc main.o ... -lfoo ...
The linker will search for libfoo.dylib, and then libfoo.a, which is all you need.
If you have both versions of the library, and want to link with an archive version in preference of the dynamic one, just specify the full path to the archive on the link line:
gcc main.o ... /path/to/libfoo.a ...

Regretfully, it's not supported. Some people reported it's possible to manually compile crt0 but nobody confirms it.

A common case is to static link against a third user library while dynamically linking against the system frameworks and libraries, so your users don't need to install third party libs before using your program. If the library is dynamically linked against frameworks (as is often the case), it may still ship with a static .a, but it is not sufficient just to replace -l<libname> with /path/to/libname.a because the .a will not have the dependencies in it. You will also have to dynamically link against those frameworks that your library was using.
For example, say you want to write a program that uses the open source libusb without requiring your user to download and install libusb. Say you have a dynamically linked binary you built with this:
clang -lusb-1.0 main.c -o myprogram
To statically link on OS X, the command looks like this (note the -framework arguments):
clang -framework CoreFoundation -framework IOKit main.c /path/to/libusb-1.0.a -o myprogram
To find what system frameworks and libraries you need to add, look at the third party dylib using otool:
otool -L /usr/local/opt/libusb/lib/libusb-1.0.0.dylib
which shows:
/usr/local/opt/libusb/lib/libusb-1.0.0.dylib:
/usr/local/opt/libusb/lib/libusb-1.0.0.dylib (compatibility version 2.0.0, current version 2.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1348.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.0.0)
You can start by adding the frameworks, followed by the libraries one at a time and you will see the list of undefined reference errors shrink. Note you probably won't need to add every library, because some may be loaded as dependencies for the ones you explicitly added.
If you aren't sure where the dylib exists, build your program the original dynamic way (with -lusb-1.0), and run otool on it:
clang -lusb-1.0 main.c -o myprogram
otool -L myprogram
which gives:
myprogram:
/usr/local/opt/libusb/lib/libusb-1.0.0.dylib (compatibility version 2.0.0, current version 2.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.0.0)
Also, read the license of the library you are linking to.

-Bstatic seems to be a no-op on OS-X Lion - used gcc -v to confirm this.

I've been run into the same issue. Here is an example to work around:
STEP1: create files
myfunc1.c:
#include <stdio.h>
void myfunc1() {
printf( "This is my func1!\n" );
}
myfunc2.c:
#include <stdio.h>
void myfunc2() {
printf( "This is my func2!\n" );
}
and myfunc.c:
#include <stdio.h>
void myfunc1( void );
void myfunc2( void );
int main() {
myfunc1();
myfunc2();
return 0;
}
STEP2: create the lib
gcc -c myfunc1.c myfunc2.c
ar -r libmyfuncs.a myfunc1.o myfunc2.o
STEP3: link
gcc -o myfunc -L. myfunc.c -lmyfuncs
Do not forget to type "-L."; dot indicates the current path.
Hope that helps.

Related

Link against shared library with SONAME

How can I force the gcc linker to link against a given version (soname) of a shared library on the system?
I need this to enforce that the version of openssl that is #include'ed matches the version that is linked, on any system, even if multiple versions of openssl are installed. To find the ABI version, my configure script first compiles and runs a program that extracts the SONAME from the headers:
#include <openssl/opensslv.h>
#include <stdio.h>
int main () {
printf(SHLIB_VERSION_NUMBER);
return 0;
}
The SHLIB_VERSION_NUMBER contains the so version string, e.g. 0.9.8 or 1.0.2k or 1.1.0. But how do I tell gcc to link against this version of libssl or libcrypto rather than just any -lssl?
I tried "in situ" linking, so that instead of linking with gcc main.c -lcrypto we use:
gcc main.c libcrypto.so.1.1.0
However it seems the linker libcrypto.so.1.1.0 cannot be found:
gcc: error: libcrypto.so.1.1.0: No such file or directory
I guess the system only searches in the standard locations when using the -l flag. Is there a better way to make my software link against libcrypto.so.1.1.0?
To select the correct version of the openssl shared libraries use:
gcc main.c -l:libssl.so.1.0.0 -l:libcrypto.so.1.0.0
The key to answering this question is "how do I control ld so that is links the correct version of a shared library?".
The correct way to pass a linker flag (a command line parameter to ld) using the gnu compilers (gcc, g++, gdc, etc...) is to use the normal ld parameter prefixed with "-l". For example -lssl or -L/usr/local/lib.
Edit: As per How to specify the library version to use at link time? you can read the manual for ld with:man ld.

Is it possible to have clang++ link dylib's relatively and not use install_name_tool?

Summary
When linking an executable with a dylib, is install_name_tool the only way to make the dylib path relative to the executable, or is there a way of doing so in clang's linking step?
Setup
Given the following project structure:
- Project Root
| compile.sh
- lib_src
| myprint.cpp
| myprint.h
- main_src
| main.cpp
with these files: https://gist.github.com/JohannesMP/8fa140b60b8ffeb2cae0
Running compile.sh (used instead of a make file for simplicity's sake here) results in the following files:
- Project Root
| main (a unix executable linked to myprint.dylib)
| myprint.dylib (a dynamic library that main uses)
Running the program with ./main while cd'd into the project works fine, but attempting to run it from anywhere else, such as by simply double clicking it, will result in the following error:
dyld: Library not loaded: myprint.dylib
Referenced from: /Users/Jo/Sandbox/libtest/main
Reason: image not found
Trace/BPT trap: 5
When inspecting main with otool I can see that this is because the path for myprint.dylib is not defined in terms of the executable:
$ otool -L main
main:
myprint.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
The general fix
As far as I've been able to find out, the suggested way to fix this is by using install_name_tool -change and modify the path to use #executable_path:
install_name_tool -change myprint.dylib #executable_path/myprint.dylib main
The Question
While the above fix clearly works, it seems somewhat unintuitive to me that there is no way of telling clang to just link main in such a way that it already uses the #executable_path to start with. It seems odd that when compiling a program with an arbitrary number of dylibs, that this has to be done for every single one.
Is this really the only way? Is this what cmake and xcode do under the hood? Is there a reason why we can't just have clang++ link relatively rather than absolutely by default?
I have found some tools that make the process easier, such as the mac dylib bundler, but I'm curious if anyone has any insight as to why it is done this way.
TL;DR
Is there a way to change just this line to avoid having to run install_name_tool -change?
It is indeed possible:
When compiling my library I set its install_name to be in terms of #rpath:
clang++ -dynamiclib lib_src/myprint.cpp -o myprint.dylib -install_name #rpath/myprint.dylib
And then in when compiling the executable I set rpath to be in terms of #executable_path, which can be done in a single step:
clang++ main_src/main.cpp -o main -I ./lib_src myprint.dylib -rpath #executable_path
For a detailed explanation of #rpath and #executable_path please check this wiki: https://wincent.com/wiki/#executable_path,_#load_path_and_#rpath

CUDA Programming and xCode

I'm completely new to Mac OS X and the main reason why I bought my new MacBook Pro R15 Retina Edition is to start studying CUDA API.
But I can't get my project build.
For example, when I create a new CUDA project, I go in the project properties and make the following changes :
Compiler for C/C++/Objective-C : LLVM GCC 4.2
Other Linker Flag : -lcuda, -lcudart
Header Search Path : /usr/local/cuda/include
Library Search Path : /usr/local/cuda/lib
When I start adding the necessary libraries in my code, everything runs fine. My code gets no typing error.
When I build the project, I get linking errors (paths shortened and message broken up for readability):
Ld /my_test_project/Build/Products/Debug/test normal x86_64 cd /Users/Admin/Documents/test setenv MACOSX_DEPLOYMENT_TARGET 10.7 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
-arch x86_64
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk
-L/my_test_project/Build/Products/Debug
-L/usr/local/cuda/lib -F/my_test_project/Build/Products/Debug
-filelist /my_test_project/Build/Intermediates/test.build/Debug/test.build/Objects-normal/x86_64/test.LinkFileList
-mmacosx-version-min=10.7
-lcudart
-lcuda
-o /my_test_project/Build/Products/Debug/test
ld: file not found: #rpath/CUDA.framework/Versions/A/CUDA for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
What's weird, is that when I go to the described path, I see the CUDA executable. The file is there!
When I use the cuComplex.h, which uses the library, every single mathematical expression (such as log, cos, sin, etc...) is an "undefined variable". However, the cuComplex.h library has inside it the library.
The driver, the SDK and toolkit are well installed and everything works like a charm. I can get the SDK examples working. But I think I miss a detail to get the whole thing working.
I have the same problem with CUDA 5.0 and Xcode 4.4.1 installed on Mountain Lion. The reason is that libcuda.dylib links CUDA.framework with rpath, which you can inspect with otool.
otool -L /usr/local/cuda/lib/libcuda.dylib
/usr/local/cuda/lib/libcuda.dylib:
/usr/local/cuda/lib/libcuda.dylib (compatibility version 1.1.0, current version 5.0.17)
#rpath/CUDA.framework/Versions/A/CUDA (compatibility version 1.1.0, current version 5.0.17)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.0)
However, Xcode does not seem to recognize CUDA.framework under /Library/Frameworks. The reason is unknown. You have to add it yourself to your project.
In your project property page, click Build Phases -> Link with Libraries. Select CUDA.framework. Add it using "Add Others" if it does not appear in the list. This way you get it linked.
If you are writing Makefiles, you want to add this to fully automate the linking stage:
-F/Library/Frameworks -framework CUDA
As an alternative that worked better for my setup is to change the path at libcuda like this:
sudo install_name_tool -change #rpath/CUDA.framework/Versions/A/CUDA \
/Library/Frameworks/CUDA.framework/CUDA \
/usr/local/cuda/lib/libcuda.dylib
As usual, don't paste a sudo command into your terminal if you don't understand what it does.
I had the same problem compiling the CUDA 6.5 examples on OS X 10.10, with Xcode 6.1. The problem happened in the examples whose Makefiles contained the line:
ALL_LDFLAGS += -Xlinker -framework -Xlinker CUDA
The fix was to tell the linker about /Library/Frameworks, so that the above was replaced with 2 lines:
ALL_LDFLAGS += -Xlinker -F/Library/Frameworks
ALL_LDFLAGS += -Xlinker -framework -Xlinker CUDA
Presumably your question is answered at this point, but this page is almost literally the only google result for "ld: framework not found CUDA" and hopefully this can save others some time.
I had a similar CUDA linking issue with CUDA 8.0 and OSX 10.12 while compiling the sample files. Adding the following to the make files solved the problem.
LDFLAGS += -Xlinker -F/Library/Frameworks

Linking to a dynamic library on a Mac with full path

I am linking a (Python extension) library that embeds the Matlab engine with the following command (generated using cmake)
c++ -mmacosx-version-min=10.6 -bundle -headerpad_max_install_names -o library.so library.o /Applications/MATLAB_R2009b.app/bin/maci64/libeng.dylib /Applications/MATLAB_R2009b.app/bin/maci64/libmx.dylib -framework Python
resulting in
$ otool -L library.so
library.so:
#loader_path/libeng.dylib (compatibility version 0.0.0, current version 0.0.0)
#loader_path/libmx.dylib (compatibility version 0.0.0, current version 0.0.0)
/System/Library/Frameworks/Python.framework/Versions/2.6/Python (compatibility version 2.6.0, current version 2.6.1)
/opt/local/lib/gcc44/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.13.0)
/opt/local/lib/gcc44/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.0.0)
However, when I try to use the library, I get an error message:
ImportError: dlopen(./library.so, 2): Library not loaded: #loader_path/libmex.dylib
Referenced from: ./library.so
Reason: image not found
I believe the problem stems from the fact that the linker includes the matlab dylib files in the form #loader_path/libeng.dylib rather than using the full path, even though I give the full path to g++. How can I force the linker to use the full path?
I know one solution is to use
export DYLD_LIBRARY_PATH=/Applications/MATLAB_R2009b.app/bin/maci64:$DYLD_LIBRARY_PATH
which is where those library files reside, but I'd like to avoid that as it causes some other problems.
Manually changing the files using install_name_tool
install_name_tool -change "#loader_path/libeng.dylib" "/Applications/MATLAB_R2009b.app/bin/maci64/libeng.dylib" library.so
install_name_tool -change "#loader_path/libmx.dylib" "/Applications/MATLAB_R2009b.app/bin/maci64/libmx.dylib" library.so
I could use this as a temporary fix, but I wonder if there isn't a better solution where the linker is given a setting to use the full paths.
Note that some of the problems with DYLD_LIBRARY_PATH can be prevented by using DYLD_FALLBACK_LIBRARY_PATH instead. This will only be used if the lib cannot be found in the default paths.
Look into the -rpath option to the ld command to control this. You might also be interested in the contents of https://github.com/bimargulies/jni-origin-testbed, which is a demonstration of some relevant technology.
The critical technique here is:
install_name_tool -change libsl2.so "#loader_path/libsl2.so" libsl1.so
You can also use symbolic link !

Compiling a 'static' binary from fortran on mac

I would like to compile fortran code on mac such that it does not depend on any shared library. Ultimately, I want to be able to send the binary to other people and for it to just work (provided the processor has the right architecture). I noticed that g95 and ifort have a -static flag, which does not work on mac.
If I consider the following program:
program test
print *,'hello world'
end program test
and compile it with
ifort -static-libgcc -static-intel test.f90
The resulting binary still depends on a few shared libraries:
valinor:tmp tom$ otool -L a.out
a.out:
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 103.0.0)
/usr/lib/libmx.A.dylib (compatibility version 1.0.0, current version 315.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 124.1.1)
So is there a way to properly compile fortran code such that it does not depend on any shared library on mac?
Static libraries are difficult on Mac OS X by design.
See this post for more information on static binaries on Mac OS X.
In theory, you could build static libraries yourself and link against them. You'd have to fetch the C runtime library from Darwin and compile it, libgcc, and so on, statically. It will work, but it might involve some work.
The short answer is: you don't actually want full static linking on Mac OS X. Linking in static versions of some product-specific libraries is fair and good, but the system shared libraries are just that: shared libraries that you'll always find on any Mac, with guaranteed compatibility from Apple.
PS: You can get rid of libgcc_s by actually specifying -static-libgcc on your commande-line. Not that it's worth doing any way, but...

Resources