Linking libstdc++ statically

Linking libstdc++ statically


FROM:  

 

 

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 that g++, 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.

1 June, 2005
<The noobIntel's forbidden fruit>
Feedback
by christopher baus

Johan.  Thanks for addressing this. I understand the problem of using .so's that were compiled against different version of libstdc++.  But this can be avoided by statically linking the entire executable (minus libc).  The end result I believe is better for the user and the maintainer (me).  By statically linking there are no external dependencies that must be satisfied.  Your executable ends up a bit bigger, but in my case it isn't that bad.

What I found extraordinarily frustrating is that -static-libgcc fails silently.  I'm still confused by your solution to use -L/some/path/to/libstdc++/ .  The only way to get gcc to statically link is to physically move the library?
by Johan

It's certainly not the only way, just the easiest way I know of. I only need to make that libstdc++.a symlink a target in my makefile and let g++ know where to look for it. The alternatives generally require quite a few link and library options specified in the right order and I find that tricky to get right.
by Garrett Rooney

Note that linking C++ code statically does have some issues beyond what I've seen you guys discuss.

Specifically, nondeterministic ordering of global object instantiation can be a problem.

Say you've got on global object in one library, and another in another library, and within their constructors they manage to do something such that one depends on the other.

In a statically linked world you can end up in situations where a global object is used before its constructor is actually called, which needless to say is a bad thing.  The root cause is the fact that the order in which the global object constructors is called in is fixed (and arbitrary), and is not dependant on their interdependencies.

In a dynamically linked world, you can get around the problem by forcing the runtime linker to handle the ordering, you wrap object access in a function of some sort, and when that function is called the runtime linker pulls in the other library, which causes the constructor to be called early enough.

I realize this isn't the kind of problem you're trying to solve, but I'm just trying to point out that static linking isn't the end-all be-all solution to all problems in C++ land.
by Johan

There are no guarantees regarding static object initialization order in C++; it's a limitation in the language. If a C++ program or library needs a certain order of initialization to work correctly it's relying on inherently nonportable platform features, and developers had better already be aware that this always come at the cost of fragile code.
by Ken MacLeod

(/me adds another blog to the feed reader, thanks)

While I can understand the desire to release distribution-agnostic binaries -- I mean, if you're going to distribute binaries, why not for as many platforms as possible -- what I don't yet understand is how the "last 20%" is handled: differences in the runtime environment outside the code itself.  Ie. filesystem layout, startup/shutdown scripts, logging, integration, etc.

It would seem to me that there's only a "niche" between programs small enough that can dynamically link against several distributions and programs large enough that the environment outside the executable is just as important.

Am I missing something?
by christopher baus

The RedHat like distributions all basically use the same rc scripts and filesystem layouts, but they have different versions of libstdc++.  That's one reason.  I also like to know exactly what I am linking to.

In Windows this particularily becomes a nightmare since DLLs aren't versioned.

Plus you don't require users to resolve dependencies, which requires root access.
by Mike Hearn

Statically linking libgcc_s is probably not a good idea: for one, every GCC 3.x version provides it and AFAIK it has not changed incompatibly. For another, it gives you a silent dependency on newer versions of glibc (go have a look at the __register_frame_info_bases symbol your binary just acquired).

For the autopackage project, we force dynamic linking of libgcc_s and place it on the system at install time if it's not there.
by Matt Hall

THANK YOU SO MUCH!

You saved me from a project rollout disaster this evening brought on by a conflict between a 32-bit and a 64-bit libgcc.
by Karl Berry

I recently had this same problem, wanting to link libstdc++ statically.  Since this web page was helpful reading, I thought I'd post my solution (so far).  Maybe someone will find it useful.

This was for TeX Live, which includes the lcdf-typetools, which are written in C++. TL distributes binaries. I need not explain the resulting pain to anyone reading here :).

I didn't want to have to set up a separate directory as discussed above, since (because of libc incompatibilities, sigh) mine is not the final build machine. I found that I could get static linking by using gcc instead of g++ as the front-end, as in the following one-line script:

exec gcc "$@" -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic -lm

Then I tell automake to use it, say it's named cxxhack:

CXX=cxxhack configure ...

(well, for TL it's actually several layers away from a direct configure call, but that doesn't matter here.)

Explanation: it turns out that (incredibly annoyingly) g++ does not use the specs file to determine the final link command – the stdc++ part is hardwired into the binary, and no environment variable or runtime configuration can change it, as far as I could see. (gcc-3.4.4/gcc/cp/g++spec.c, line 303 or so). Thus, the idea is to use gcc instead of g++ or c++ for the front-end compiler, and link with -lstdc++ ourselves. g++/c++ automatically link with -lm, so we do that too.

Perhaps there is a cleaner way to alter the lcdf configure setup to achieve the same link line, but I could not find it offhand. I wrote to bug-automake for advice.

Some of the output from configure looked doubtful ("no -lstdc++"), but the binaries seemed to run ... and ldd reports no libstdc++. Hallelujah.

I surmise that this won't work in the face of exceptions or other issues as mentioned here, but I'm hoping it will suffice for us. If anyone has any comments or better suggestions, I'd be grateful to hear. My email is karl@freefriends.org.
by marvind

I tried the following command on RedHat Enterprise Linux 3.0

gcc -o example example.cpp /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/libstdc++.a

(the library path from the output of g++ -print-file-name=libstdc++.a as you pointed out).

The output of ldd is:

[mar@localhost test]ldd example

libc.so.6 => /lib/tls/libc.so.6 (0xb749e000)

/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)

Looks like it linked the gcc version correctly. Note that I did not specify the location. of libgcc at all. So, I am not sure why I have to specify the -Bstatic, etc. I have the libstdc++*.so in the usual location under /usr/lib with libgcc*.so under /lib.

I would appreciate a clarification. Thanks.
by Johan

marvind, you are compiling and linking C++ code with gcc rather than g++. That's not guaranteed to work, for precisely the reasons I mention at the beginning of my post. Even if you get no errors during compiling and linking, your executable may fail unexpectedly when using certain C++ features.
by Tony Feick

Have you had any problems trying to static link libpthread?

Feedback is closed for this entry.

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值