Linking libstdc++ statically

11:21:00 AM 0 Comments

Christopher Baus writes about his problems linking libstdc++ statically. Yes, making C++ binaries that will work properly in different Linux distributions is somewhat painful. The problem is not so much linking libstdc++ statically – it is just a library, after all – but the runtime support required by C++ code in general, to enable features like RTTI and exception handling.
The runtime support code used by different parts of a C++ application needs to be compatible. If one part of the program needs to dynamic_cast or catch objects provided by another, both parts must agree on certain implementation details: how to find vtables, how to unwind the stack, and so on.
For C++ and a few other GCC-supported languages with similar features, such details are specified by a C++ ABI. Whenever the ABI used by GCC changes you'll end up with incompatible libraries produced by the different GCC versions. The same is true for plain C, but the C ABI is much simpler and has been around a lot longer so it's fairly stable.
As far as I know C++ ABI changes have been introduced with every major release of GCC (i.e. those with different first or second version number components). To make matters worse, most major Linux distributions use GCC snapshots and/or patch their GCC versions, making it virtually impossible to know exactly what GCC versions you might be dealing with when you distribute binaries.
Note that this problem cannot, in general, be solved by linking statically. First of all, code compiled against different ABIs is simply not binary compatible. It doesn't matter if you manage to link binary incompatible code together, because it will never work properly. Secondly, the language runtime support typically rely on some data being shared, e.g. to access some kind of lock or global data structure (similar to how C programs need a shared errno).
Shared data implies that whenever more than one part of a program needs the runtime support, and any of those parts is dynamically loaded, the runtime support needs to be loaded dynamically too. Otherwise, the different program parts would end up with copies of the data rather than one shared instance. That's the reason for putting the language runtime support code in a dynamic library by default.
There are many different workarounds and no perfect solution, but a fairly workable compromise is to link all C++ code into an executable while using dynamically loaded C libraries only. This way there is only one part of the program that needs the C++ runtime support mechanisms, which can therefore be linked in statically. You can mix and match statically and dynamically linked C libraries, but no C++ code (or any code using the C++ runtime support) may be linked dynamically if this is to work.
Now, the practical problem people run into when trying to link libstdc++ statically is thatg++, the GCC front-end for compiling and linking C++, adds the proper libraries and start-up code for C++ automatically and will link some of this code dynamically by default:
g++ -o example example.cpp

ldd example
    linux-gate.so.1 =>  (0xffffe000)
    libstdc++.so.6 => /usr/lib/gcc/i686-pc-linux-gnu/3.4.3/libstdc++.so.6 (0xb7f17000)
    libm.so.6 => /lib/libm.so.6 (0xb7ef3000)
    libgcc_s.so.1 => /usr/lib/gcc/i686-pc-linux-gnu/3.4.3/libgcc_s.so.1 (0xb7eea000)
    libc.so.6 => /lib/libc.so.6 (0xb7dd0000)
    /lib/ld-linux.so.2 (0xb7feb000)
The resulting binary links to a shared version of libstdc++, the standard C++ library, and a shared version of libgcc, a GCC runtime support library required for exception handling among other things. This binary won't work on a machine with different versions of those libraries, but since it doesn't need any other dynamically loaded C++ libraries, the incompatibility can be removed by linking libstdc++ and libgcc statically.
Consulting the GCC man page, you'll find the GCC option -static-libgcc mentioned. It makes the compiler link libgcc statically rather than dynamically. Except when it doesn't:
g++ -static-libgcc -o example example.cpp

ldd example
    linux-gate.so.1 =>  (0xffffe000)
    libstdc++.so.6 => /usr/lib/gcc/i686-pc-linux-gnu/3.4.3/libstdc++.so.6 (0xb7f17000)
    libm.so.6 => /lib/libm.so.6 (0xb7ef3000)
    libgcc_s.so.1 => /usr/lib/gcc/i686-pc-linux-gnu/3.4.3/libgcc_s.so.1 (0xb7eea000)
    libc.so.6 => /lib/libc.so.6 (0xb7dd0000)
    /lib/ld-linux.so.2 (0xb7feb000)
What happened here? Remember what I said earlier about not loading any C++ code dynamically? Since we are still linking dynamically to libstdc++, the runtime support code in libgcc must also be linked dynamically. g++ ignored the -static-libgcc flag because linking libgcc statically would not result in a program that works properly. We need to link statically to both libraries, or neither.
You can ask g++ to tell you exactly what steps are involved in linking a C++ program (try the -v flag if you are curious) and invoke a slightly different set of commands in order to link your application with static versions of libstdc++ and libgcc. But integrating that into your own build process is painful, error-prone, and specific to the machine and compiler version you use.
There's no -static-libstdc++ option to go along with -static-libgcc, but you can let the compiler tell you the path to the static libstdc++ library it would use, and let g++look for libraries in a directory where it will only find the static version (in this case our build directory):
ln -s `g++ -print-file-name=libstdc++.a`

g++ -static-libgcc -L. -o example example.cpp

ldd example
    linux-gate.so.1 =>  (0xffffe000)
    libm.so.6 => /lib/libm.so.6 (0xb7ef3000)
    libc.so.6 => /lib/libc.so.6 (0xb7dd0000)
    /lib/ld-linux.so.2 (0xb7feb000)
Once again, for this to work reliably you must not use dynamically loaded C++ code, including code loaded with dlopen. In particular, statically linking the runtime support code is contraindicated when creating dynamically loadable C++ libraries. Depending on your linker it might be possible, but planning to distribute such binaries is still very much an order for a super-sized can of worms.
Update: As of GCC 4.5 there is now a -static-libstdc++ compiler option which does what you'd expect it to do. (Thanks to Tim Hutt for pointing this out.)

Fixing SSH login long delay / slow

9:19:00 PM 0 Comments

For a long time I had a problem with ssh login on a Redhat 6 server – it was taking too long to connect to it, around 30 seconds. Normally it hasn’t been a big issue – after all, you connect once and work for all day as long as you enable server keepalive packets to avoid session timeout.
However when it comes to work with SFTP o GIT it might become annoying. Everytime you sFTP upload or  git push you have to wait 30 seconds again.
This kind of problems are often related to DNS issues but this is not always the case. Following are the most common solutions:

1. Disable reverse IP resolution on SSH server

It turns out there is a setting in OpenSSH that controls whether SSHd should not only resolve remote host names but also check whether the resolved host names map back to remote IPs. Apparently, that setting is enabled by default in OpenSSH. The directiveUseDNS controls this particular behaviour of OpenSSH, and while it is commented in sshd_config (which is the default configuration file for the OpenSSH daemon in most enviornments), as per the man page for sshd_config, the default for UseDNS is set to enabled. Add the following line:
UseDNS no

2. DNS resolver fix for IPv4/IPv6 enabled stacks

It’s a known issue on the Red Hat knowledgebase article DOC-58626, but since it’s closed without login, I’ll share the solution below:
The resolver uses the same socket for the A and AAAA requests. Some hardware mistakenly only sends back one reply. When that happens the client sytem will sit and wait for the second reply. Turning this option on changes this behavior so that if two requests from the same port are not handled correctly it will close the socket and open a new one before sending the second request.
The solution is to add the following line to your /etc/resolv.conf. Just add it all the way at the bottom, as the last line.
options single-request-reopen

3. Disable GSSAPI authentication method

OpenSSH server enables by default the GSSAPI key exchange which allows you to leverage an existing key management infrastructure such as Kerberos or GSI, instead of having to distribute ssh host keys throughout your organisation. With GSSAPI key exchange servers do not need ssh host keys when being accessed by clients with valid credentials.
If you are not using GSSAPI as a authentication mecanism, it might be causing this connection delay.
In my particular case, I ran ssh -v myserver to find out that it was hanging whilst attempting to authenticate with GSSAPI, with the slow section looking like:
....
....
debug2: key: /home/user/.ssh/id_rsa (0xb961d7a8)
debug2: key: /home/user/.ssh/id_dsa ((nil))
debug2: key: /home/user/.ssh/id_ecdsa ((nil))
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug3: start over, passed a different list publickey,gssapi-keyex,gssapi-with-mic,password
debug3: preferred gssapi-keyex,gssapi-with-mic,publickey,keyboard-interactive,password
debug3: authmethod_lookup gssapi-keyex
debug3: remaining preferred: gssapi-with-mic,publickey,keyboard-interactive,password
debug3: authmethod_is_enabled gssapi-keyex
debug1: Next authentication method: gssapi-keyex
debug1: No valid Key exchange context
debug2: we did not send a packet, disable method
debug3: authmethod_lookup gssapi-with-mic
debug3: remaining preferred: publickey,keyboard-interactive,password
debug3: authmethod_is_enabled gssapi-with-mic
debug1: Next authentication method: gssapi-with-mic
debug1: Unspecified GSS failure.  Minor code may provide more information
Credentials cache file '/tmp/krb5cc_1000' not found

debug1: Unspecified GSS failure.  Minor code may provide more information
Credentials cache file '/tmp/krb5cc_1000' not found

debug1: Unspecified GSS failure.  Minor code may provide more information
Turned out that it was stalling after trying gssapi-with-mic authentication method. Had several “Unspecified GSS failure” messages with several seconds delay between them, therefore it was definitely the root cause of long delays.
The fix is simple – disable attempts to use GSS-API by adding the following to /etc/sshd_config (server side) or yout ~/.ssh/ssh_config (client side).
GSSAPIAuthentication no
There is an easy way to check beforehand whether this solution will work. Try to ssh into your server by disabling GSSAPI authentication:
ssh -o GSSAPIAuthentication=no user@yourserver

gcc 链接时动态库和静态库的优先选择

1:23:00 PM 0 Comments

老话题了,不过还是记一笔
先看文件
ModuleA.cpp
1int add(int a, int b)
2{
3    return a + b;
4}
ModuleB.cpp
1int minus(int a, int b)
2{
3    return a - b;
4}
Main.cpp
1#include
2 
3int add(intint);
4int minus(int int);
5 
6int main()
7{
8    printf("%d\n", add(1, 2));
9    printf("%d\n", minus(2, 1));
10    return 0;
11}
Makefile
1all:
2    g++ -c -fPIC Main.cpp
3    g++ -c -fPIC ModuleA.cpp
4    g++ -c -fPIC ModuleB.cpp
5    g++ -shared ModuleA.o ModuleB.o -o libmodule.so
6    g++ -o Run Main.o -L./ -lmodule
7clean:
8    rm *.o *.so Run
记得指定动态库路径
1export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
ldd 确认一下
1$ ldd Run
2/$LIB/libonion.so => /lib64/libonion.so (0x00002b98dea29000)
3libmodule.so => ./libmodule.so (0x00002b98deb39000)
4libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00002b98dec4d000)
5libm.so.6 => /lib64/libm.so.6 (0x00002b98dee4c000)
6libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00002b98defa1000)
7libc.so.6 => /lib64/libc.so.6 (0x00002b98df0ae000)
8libdl.so.2 => /lib64/libdl.so.2 (0x00002b98df2ef000)
9/lib64/ld-linux-x86-64.so.2 (0x00002b98de90d000)
跑起来
1$ ./Run
23
31
再来看静态的方式,修改后的 Makefile
1all:
2    g++ -c -fPIC Main.cpp
3    g++ -c -fPIC ModuleA.cpp
4    g++ -c -fPIC ModuleB.cpp
5    ar -r libmodule.a ModuleA.o ModuleB.o
6    g++ -o Run Main.o -L./ -lmodule
7clean:
8    rm *.o *.a Run
编译出来的已经不依赖动态库了(废话)
1$ ldd Run
2/$LIB/libonion.so => /lib64/libonion.so (0x00002ad9ce948000)
3libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00002ad9cea58000)
4libm.so.6 => /lib64/libm.so.6 (0x00002ad9cec56000)
5libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00002ad9cedac000)
6libc.so.6 => /lib64/libc.so.6 (0x00002ad9ceeb9000)
7libdl.so.2 => /lib64/libdl.so.2 (0x00002ad9cf0f9000)
8/lib64/ld-linux-x86-64.so.2 (0x00002ad9ce82c000)
静态版本的 Run 是 9850 字节,动态是 9954 字节,这。。
下面问题来了,如果静态动态都有,链接器要哪个呢
1all:
2    g++ -c -fPIC Main.cpp
3    g++ -c -fPIC ModuleA.cpp
4    g++ -c -fPIC ModuleB.cpp
5    ar -r libmodule.a ModuleA.o ModuleB.o
6    g++ -shared ModuleA.o ModuleB.o -o libmodule.so
7    g++ -o Run Main.o -L./ -lmodule
8clean:
9    rm *.o *.a *.so Run
答案是动态库
1$ ldd Run
2/$LIB/libonion.so => /lib64/libonion.so (0x00002b2243070000)
3libmodule.so => ./libmodule.so (0x00002b2243180000)
4libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00002b2243294000)
5libm.so.6 => /lib64/libm.so.6 (0x00002b2243493000)
6libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00002b22435e8000)
7libc.so.6 => /lib64/libc.so.6 (0x00002b22436f5000)
8libdl.so.2 => /lib64/libdl.so.2 (0x00002b2243936000)
9/lib64/ld-linux-x86-64.so.2 (0x00002b2242f54000)
可以这样来强制链接静态库
1all:
2    g++ -c -fPIC Main.cpp
3    g++ -c -fPIC ModuleA.cpp
4    g++ -c -fPIC ModuleB.cpp
5    ar -r libmodule.a ModuleA.o ModuleB.o
6    g++ -shared ModuleA.o ModuleB.o -o libmodule.so
7    g++ -o Run Main.o libmodule.a
8clean:
9    rm *.o *.a *.so Run