Is it possible to link libstdc++ statically in Mac OSX 10.6? - c++

I am trying to run my C++ program on other Mac OSX machines which may have an older copy of libstdc++, but have all the other tools. I tried to follow this approach, also mentioned in this SO question, even though it discusses a linux setup. I have small program try.cpp:
#include <iostream>
int main() {
int a = 10;
std::cout << a << '\n';
return 1;
}
Obviously, if I just compile it, I get
$ /usr/bin/g++ try.cpp
$ otool -L a.out
a.out:
/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)
I understand the dependency on libSystem.B.dylib, and we can leave that aside. To try to get rid of libstdc++, I try this:
$ /usr/bin/g++ try.cpp /usr/lib/libstdc++-static.a
$ otool -L a.out
a.out:
/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)
So, I try
$ ln /usr/lib/libstdc++-static.a .
$ /usr/bin/g++ try.cpp -L.
$ otool -L a.out
a.out:
/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)
or,
$ /usr/bin/g++ try.cpp -L. -lstdc++-static
$ otool -L a.out
a.out:
/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)
Finally, this works:
$ /usr/bin/gcc try.cpp -L. -lstdc++-static
$ otool -L a.out
a.out:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.0)
Is this alright? (To use gcc to link C++ programs with libstdc++). I've heard somewhere that g++ is actually a script that uses gcc and libstdc++ to compile C++ programs. If that is the case, and we use it correctly, it should be ok.
However, I am actually using the macport compiler and a more complicated program, for which gcc generates some warnings, while it is C++ compliant. Something to the effect of:
ld: warning: std::basic_stringbuf<char, std::char_traits<char>, std::allocator<char> >::~basic_stringbuf() has different visibility (hidden) in /opt/local/lib/gcc44/libstdc++.a(sstream-inst.o) and (default) in /var/folders/2u/2uLPtE+3HMi-BQIEfFVbSE+++TU/-Tmp-//ccoE2rqh.o
This suggests that we shouldnt be using gcc for c++ compilations. So to sum up, the questions are:
How to link libstdc++ statically
If g++ doesn't do it, is it ok to use gcc and supply the libstdc++ manually? Then why the visibility warnings?
If neither of the two approaches work because of the visibility problems in the compiled libraries, why not use libstdc++ source files (sstream.h, list.h, vector.c) etc and just include them in the compilation. Even though this will make the compilation slow, it might be useful for certain applications. It might even lead to better optimization!

It sounds like you just want to target earlier Mac OS X versions, which can be done without statically linking with libstdc++. I think the GCC that ships with Xcode targets the host environment by default. However, it can handle a special flag called -mmacosx-version-min to change the target environment. If you provide this with the target OS X version number then it will automatically create binaries compatible with that version of Mac OS X.
#include <iostream>
int main(void)
{
std::cout << "Hello world!" << std::endl;
return 0;
}
Compile like this:
g++ -mmacosx-version-min=10.4 test.cpp
I compiled this program twice, once with the flag and once without, and then I copied both binaries to a Mac running 10.4. The one compiled with the flag executed properly, however the one compiled without the flag said “Bad CPU type in executable” (despite the fact that it was compiled on an identical machine just running a later version of OS X).
Some of the headers have macro guards that prevent you from using functions/classes introduced in 10.5 or 10.6 if you have specified 10.4 as a minimum target (I'm not sure about C++ headers, but the Cocoa, Foundation, AppKit etc. framework headers definitely do).

This is a stretch for my knowledge but I see few responses here so!
GCC is a compiler driver that will also drive the linker. g++ to my understanding is more of just a compiler. So to get G++ to build properly I believe you need to build the object files and link them manually. Of the top of my head I can't say how to do this as I'm IDE brain damaged at the moment.
As to the error you have seen that might be due to the wrong files being linked in. I'm on my iPhone right now so I'm not about to dechiper the error message you printed. I'm not a fan at all of MacPorts so don't be surprised that, that installation is screwed up. First though make sure you are using the MacPorts libs with the MacPorts compiler.
In the end I have no doubt that you can do what you want to do. You will need to however start reading Make files and more of the documentation of the GCC tool set. Focus on building and linking files into programs. You might want to find a small open source program tha builds nicely on a mac and look at the Make files there.
Of course finding a good C++ based project to learn from is not easy. I would however reccomend installing LLVM & CLang especially considering the new rev is suppose to be C++ ready. Obviously a different set of tools but CLang may resolve your issues or at least give you better debugging info. Maybe somebody can chime in with an opensource C++ project with simple clean make files. The closest I've seen lately is a project called HeeksCAD.
In the end when building anything non trivial you end up needing more than just GCC. A lot of that is taken care of with IDEs these days, however I'm not certain if XCode can be properly configured to do what you want.

Related

Why do g++ binaries have a significantly slower startup time than clang++?

Whenever I compile code using g++, I've noticed that when I run the binary repeatedly, it runs significantly slower than when I compile with clang++.
test.cpp
#include <iostream>
int main() {
std::cout << "Hello World!" << '\n';
}
script.sh
for ((i=0; i<2000; ++i)); do
./a.out
done
Compiling using g++:
g++-11 -O2 test.cpp
time bash script.sh -> 10.32s user 5.37s system 88% cpu 17.751 total
Compiling using clang++:
clang++ -O2 test.cpp
time bash script.sh -> 1.42s user 1.50s system 69% cpu 4.223 total
This is extremely annoying as I need to use g++, but I also need to speed up the binary to make it easier to stress test my code, so an explanation or fix would be welcome.
Note: GCC (11.3, via homebrew), clang (13.1, apple/homebrew (both seem to be the same))
this problem doesn't apply to gcc, only g++
It turns out that this is because g++ links the executable to libstdc++ by default.
The example can be reduced to
// test.cpp
int main() {}
With linking and loading libstdc++:
❯ g++ test.cpp && otool -L a.out
a.out:
/opt/homebrew/opt/gcc/lib/gcc/11/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.29.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.100.3)
❯ ls -lh /opt/homebrew/opt/gcc/lib/gcc/11/libstdc++.6.dylib
-rw-r--r-- 1 vainman wheel 2.3M Jul 4 01:59 /opt/homebrew/opt/gcc/lib/gcc/11/libstdc++.6.dylib
❯ time bash script.sh
bash script.sh 6.27s user 3.58s system 94% cpu 10.468 total
Without linking and loading libstdc++:
❯ g++ -nodefaultlibs test.cpp -lSystem && otool -L a.out
a.out:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.100.3)
❯ time bash script.sh
bash script.sh 0.35s user 0.96s system 72% cpu 1.804 total
clang++ also tries linking to /usr/lib/libc++.1.dylib but the file doesn't seem exists.
❯ clang++ test.cpp && otool -L a.out
a.out:
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1300.23.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.100.3)
❯ ls /usr/lib/libc++.1.dylib
ls: /usr/lib/libc++.1.dylib: No such file or directory
Update: Found macOS Big Sur 11.0.1 Release Notes:
New in macOS Big Sur 11.0.1, the system ships with a built-in dynamic linker cache of all system-provided libraries. As part of this change, copies of dynamic libraries are no longer present on the filesystem. Code that attempts to check for dynamic library presence by looking for a file at a path or enumerating a directory will fail. Instead, check for library presence by attempting to dlopen() the path, which will correctly check for the library in the cache. (62986286)

undefined reference to `log2f#GLIBC_2.27' undefined reference to `logf#GLIBC_2.27'

I'am trying to a run a sample code from pardiso website but end up with this error.
I installed the lapack package from http://www.netlib.org/lapack/
gcc pardiso_sym.c -L /home/sree/ -lpardiso600-GNU800-X86-64 -llapack -lgfortran -fopenmp -lm -ldl
error:
/home/sree//libpardiso600-GNU800-X86-64.so: undefined reference to `log2f#GLIBC_2.27'
/home/sree//libpardiso600-GNU800-X86-64.so: undefined reference to `logf#GLIBC_2.27'
collect2: error: ld returned 1 exit status
I know this question is quite old but anyway:
First of all - the error you are getting is a linker error and is seems like it cannot resolve a reference to a function defined in glibc.
The version given here for glibc (the GNU C library) is 2.27.
I would now suspect that the version used by GCC when trying to compile pardiso_sym.c was lower than the specified version of glibc - thus the error.
You can find a nice thread about checking the version of glibc used by different gcc compilers here.
That said - different gcc compilers might use different versions of glibc for linking. You could now either specifically try and link a proper version of glibc (like described here) or - probably more feasible - try and update your glibc version.
The described pardiso packages were also compiled with gcc 8.0 but there is a pardiso version available compiled using the gcc 7.2. Both versions also link agains different glibc versions and it might already be feasible to use libpardiso600-GNU720-X86-64.so.
In addition I'd also use a gcc version that is higher equal than the one used to compile pardiso so you might want to upgrade gcc too.
Edit:
Originally Pardiso 6.0 (and, for that matter also 6.2) was deployed as either libpardiso600-GNU720-X86-64.so or libpardiso600-GNU800-X86-64.so both available under its Download-Link.
As promised, here comes the summary of what I learned:
The Pardiso library (like all shared libraries) remembers which glibc version was used when it was compiled. Occasionally glibc may be older on your system than what Pardiso expects, leading to the link errors described by OP. You can check the version of your glibc by running ldd --version.
I applied for a free time-limited academic license, that gave me a personal download link. As #Flusslauf pointed out, one has the choice of 2 versions for the Linux 64-bit platform. As of today (2021-03-03) the two versions available to me were: libpardiso600-GNU720-X86-64.so (note: GNU720 and not GNU729, but that's a minor difference) and libpardiso600-GNU800-X86-64.so. The latter didn't work for me for the reasons explained above. So I compiled one of the little example programs from the Pardiso website in the directory containing the former Pardiso library as follows:
cd /path/to/pardiso
gcc pardiso_sym.c -L/usr/lib64 -L. -lpardiso600-GNU720-X86-64 \
-llapack -lblas -fopenmp -lpthread -lm -ldl -o psym
export LD_LIBRARY_PATH=/path/to/pardiso:${LD_LIBRARY_PATH}
export OMP_NUM_THREADS=2
export PARDISOLICMESSAGE=1
./psym
The -L/usr/lib64 was necessary to find LAPACK and BLAS on the machine I used. The -L. tells GCC to look for the Pardiso library in the current directory (/path/to/pardiso where I compiled the test program). After compilation, add to LD_LIBRARY_PATH the Pardiso library location, ask for 2 OpenMP threads and silence the Pardiso license message.
Oh, one last thing: don't forget to copy your license file to your home directory! :-)

Link only libc, and not libc++ in C++

Is it possible, with both clang and gcc, in a portable way to not link libstdc++ or libc++, but still link libc and use all the features of C++ (well, only classes).
-nodefaultlibs doesn't seem to work after my first test (tested on OS X), it does not link libstdc++, but it also doesn't link with libSystem, which is required.
The best, and most portable solution is, to just not link any default libraries, and link everything manually.
Adding:
-nodefaultlibs -lc
will not link anything, and afterwards just link libc. Now you've got C with Classes.
You can verify that with either otool -L (on OS X) or ldd (on Linux).
On OSX the output now is:
$ otool -L ./test
test:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
instead of
$ otool -L ./test
./test:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)

Link whole program statically

I have an application in c++ ported from Windows to Linux, everything worked ok, but...
Our customer what that application running on Debian 3.1 (sarge), I cannot force the gcc version on the target system and I prefer to use new gcc (there are some c++11 constructs, which I'd like to preserve). I want to make executable for now for tests and .so file in future.
I decide to compile my procect statically.
when I run:
g++ -static -o prog obj/sublib1/file1.o obj/sublib1/file2.o obj/sublib2/file1.o obj/sublib2/file2.o (...) -L../somedir -s -lsomestaticlib
I get en error:
/usr/lib/gcc/i586-suse-linux/4.8/../../../../i586-suse-linux/bin/ld: cannot find -lm
/usr/lib/gcc/i586-suse-linux/4.8/../../../../i586-suse-linux/bin/ld: cannot find -lc
The system is OpenSuse 13.1 32bit, uname -a:
Linux linux-zfaz.site 3.11.6-4-desktop #1 SMP PREEMPT Wed Oct 30 18:04:56 UTC 2013 (e6d4a27) i686 i686 i386 GNU/Linux
The problem is probably with math library and C library. The dynamic version of both libraries are in /lib directory.
(probably doesn't matter: I was trying to build it using code::blocks, but when problem occurred I've moved to terminal)
Do I need to install static version of these libraries? How?
If you're using a recent version of g++, the option -static-libstdc++ should be all you need. This will ensure that the g++ libraries are linked statically, but that the system libraries (for which there usually isn't a static version) are linked dynamically. (Don't use the -static in this case.)
You need to install the glibc-devel-static package, though if applicable the answer of #jameskanze is a better option.

loading dynamic library path error in mac

I am now building a dynamic library and an command line illustration program that uses this dynamic library. The library and the illustration program are in the same folder:
/user/xxx/develop/debug/libdynamic.dylib
/user/xxx/develop/debug/illustration
When the illustration program can work very well in my computer, I send the illustration program as well as the dynamic library to my colleague and he will run the illustration program in his computer. However, every time he runs the illustration program in the command window, the program also reminds that it can not load the libdynamic.dylib as it tries to find the library in /user/xxx/develop/debug/, which is unavailable in my colleague's computer. I was wondering what I should do. Many thanks.
EDIT:
The output using otool for the illustration program is as follows:
/Users/xxx/develop/debug/libdynamic.dylib (compatibility version 0.0.0, current version 0.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 744.18.0)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 56.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
You need to tell illustration where to find libdynamic.dylib which you can do post-build using install_name_tool (manpage). You'll want to set the new path to #executable_path/libdynamic.dylib, with (something like):
$ install_name_tool -change /user/xxx/develop/debug/libdynamic.dylib \
#executable_path/libdynamic.dylib \
/user/xxx/develop/debug/illustration
(the exact old name value to pass to install_name_tool will depend on what it's currently set to, which can be determined using otool -L /user/xxx/develop/debug/illustration).
One way to avoid this nonsense (and the way I do it myself) is to use the -install_name linker option:
$(BINDIR)/libdynamic.dylib: $(OBJS)
$(CXX) -dynamiclib -current_version $(MAJOR_MINOR_VERSION) \
-compatibility_version $(MAJOR_MINOR_VERSION) \
-install_name #executable_path/libdynamic.dylib \
$(LDFLAGS) -o $# $(OBJS) $(LIBS)
(Makefile fragment taken from here).