开发者

C/C++ Static vs dynamic libraries example

开发者 https://www.devze.com 2023-03-12 22:59 出处:网络
I\'m learning about static and dynamic libraries. So far I understand why I would need a dynamic library. In case something is changing it\'s good to plug in a newer version and all the applications w

I'm learning about static and dynamic libraries. So far I understand why I would need a dynamic library. In case something is changing it's good to plug in a newer version and all the applications will update automatically without even noticing.

a) Gre开发者_运维技巧at for plugins, b) multiple apps using the same library and c) maintenance when you need to correct errors.

However, why would anyone use a static library? I mean what's the advantage? Does sb have an example so I can understand it better? Is it to make a product proprietary?

EDIT: Due to the confusion in the comments. I understand what a static library is, and I also know the difference between a dynamic library. It was just beyond me why anyone would use a static library instead of just the source itself. I think I'm now starting to understand that a static library offers the following advantages:

a) better code maintenance b) faster compiling times


There is another difference between static and dynamic libraries which may become important in some situations, I am surprised that nobody mentions that.

  • When static library is linked, the symbols (e.g. function names) are resolved during the linkage (compile) time, so a call to a library function is resolved to the direct call to an address in the final executable.

  • With dynamic library, this happens during the run-time, when the library is loaded into the process space (often during process start-up). The symbols must be mapped into the process's address space. Depending on the number of symbols, which can be surprisingly large, and number of libraries loaded at the start-up, the delay can be quite tangible.

There is this excellent in-depth guide on dynamic libraries on Linux - How To Write Shared Libraries. It is way too detailed for most of us, but even skimming through it gives you many surprising insights. For instance, it says that in release 1.0 of OpenOffice it had to do more than 1.5 million of string comparisons during the launch!

A way to get a feeling of that is to set LD_DEBUG to symbols, and LD_DEBUG_OUTPUT to some file, run a program and look at the file to see the activity that goes on on startup.


Compilers can do all sorts of additional optimizations with static libraries that they cannot do with dynamic libraries. For example, a compiler can delete unused functions from static libraries. It wouldn't know to do that in a dynamic library. But there are even more advanced optimizations. The compiler can pull code from a static library function into the main program, which will eliminate function calls. Very smart compilers can do even more. The sky is really the limit with static libraries, but dynamic libraries make much of this much harder or impossible.

Probably the more practical reason however, is that static linking is the default for most library compilers so many people just end up using it. To create a dynamic library, you normally have to create an additional file which exposes certain functions. Although the file tends to be relatively simple, if you don't take the time to do it, then your libraries all end up being static.

As mentioned in another post, managing dependencies with static libraries tends to be easier simply because you have everything under your control. You may not know what dll/so is installed on the user's system.


A static library is basically a ZIP of object files. The only advantage it has over just distributing the object files is that it's a single file that encompasses your whole library. Users can use your headers and the lib to build their applications.

Because it's just a ZIP of object files, anything the compiler does to object files also works with static libraries, for example, dead code elimination and whole program optimization (also called Link-Time Code Generation). The compiler won't include bits of the shared library in the final program that are unused, unlike dynamic libraries.

With some build systems, it makes link seams easier too. E.g. for MSVC++, I'll often have a "production" EXE project, a "testing" EXE project, and put the common stuff in a static library. That way, I don't have to rebuild all the common stuff when I do builds.


1.) Shared libraries require position independent code (-fpic or -fPIC for libraries) and position independent code requires setup. This makes for larger code; for example:

*Part of this is due to compiler inefficiencies as discussed here

long realfoo(long, long);
long foo(long x, long y){
    return realfoo(x,y);
}
//static
foo:
  jmp realfoo #
//shared (-fpic code)
foo:
  pushl %ebx #
  call __x86.get_pc_thunk.bx #
  addl $_GLOBAL_OFFSET_TABLE_, %ebx # tmp87,
  subl $16, %esp #,
  pushl 28(%esp) # y
  pushl 28(%esp) # x
  call realfoo@PLT #
  addl $24, %esp #,
  popl %ebx #
  ret
__x86.get_pc_thunk.bx:
  movl (%esp), %ebx #,
  ret
  1. Using the previous example, the realfoo() could be considered for inlining in a static build if proper optimization is enabled and supported. This is because the compiler has direct access to the object in the archive file (libfoo.a). You can think of the .a file as a pseudo-directory containing the individual object files.

  2. The "not having to recompile the whole binary for a bug in a library" cuts both ways. If your binary doesn't use the offending bug, it was probably compiled out of the static binary and replacing a shared library with code from the latest trunk with the fix may introduce (multiple) other as yet unreported bugs.

  3. Initial startup time. Although many shared library proponents will suggest that using shared libraries will reduce startup times due to other programs already using them and (sometimes) smaller binary size. In practice, this is rarely true with the exception of some really basic X11 apps. Anecdotally, my startup time for X went down to about 1/10th of a second from over 5s by switching to a static build with musl-libc and tinyX11 from the stock glibc+X11 shared. In practice, most static binaries end up starting faster because they don't need to initialize all the (possibly unused) symbols in every dependent library. Furthermore, subsequent calls to the same binary get the same preloading benefits as the shared libraries.

  4. Use a dynamic library when the library is bad for static builds. For example gnu-libc (aka glibc) is notoriously bad at static builds with a basic hello world weighing in at close to 1Mb while musl-libc, diet-libc and uclibc all build hello world at around 10kb. On top of that glibc will omit some key (mostly network related) functionality in static builds.

  5. Use a shared library if the library relies on "plugins" for key functionality; gtk icon loading and a few other features for instance used to be buildable with the plugins builtin, but for the longest time that has been broken, so the only recourse is to use shared libraries. Some C libraries won't support loading arbitrary libraries (musl for example) unless they are built as a shared library, so if you need to load a library on the fly as a "plugin" or "module", you may at least need the C library to be shared. A workaround for static builds is to use function pointers instead and a better, more stable GUI toolkit for the specific case of GTK. If you are building an extendable programming language like perl or python, you're going to need shared library capabilities for your optimized plugins to be written in a compiled language.

  6. Use a shared library if you absolutely need to use a library with a license that is incompatible with static building. (AGPL, GPL, LGPL without a static link clause) ... and of course if/when you don't have the source code.

  7. Use static builds when you want more aggressive optimization. Many well-seasoned libraries like libX11 were written when cdecl was the primary calling convention. Consequently many X11 functions take a boatload of parameter in the same order as the struct that the function manipulates (rather than just a pointer to the struct)... which makes sense with the cdecl calling convention since theoretically you could just move the stack pointer and call the function. However this breaks down as soon as you use some number of registers for parameter passing. With static builds, some of these unintentional consequences can be mitigated via inlining and link time optimization. This is just one example. There are many many other optimizations that crop up as function boundaries are erased.

  8. Memory security can go either way. With static binaries, you aren't susceptible to LD_PRELOAD attacks (a static binary may only have 1 symbol - the entry point), but shared (and static-pie if supported) can have address space randomization that is only available to static builds on the few architectures that support position independent executables (though most common architecture do support PIE now).

  9. Shared libraries can (sometimes) produce smaller binaries, so if you are using shared libraries that will be on the system anyhow, you can reduce package size (and thus server loads) a bit. However if you are going to have to ship the shared libraries anyhow, the combined size of the libraries and the binary will always be larger than the static binary barring some crazy aggressive inlining. A good compromise is to use shared common system libraries (for instance libc, X11, zlib, png, glib and gtk/qt/other-default-toolkit) while using static libraries for uncommon dependencies or libraries specific to your package. The chrome browser does this to some extent. A good way to determine the shared/static threshold in your target Linux distro is to iterate over */bin and */sbin in the default install with ldd or objdump -x and parse the output through sort and uniq to determine a good cutoff. If you are distributing multiple binaries that use most of the same libraries a bunch of static builds will increase bloat, but you can consider using a multicall binary (such as in busybox, toybox, mupdf or netpbm)

  10. Use static builds if you want to avoid "DLL hell" or for cross distro compatibility. A static binary built in one distro should work on any other distro that is reasonably up to date (mostly kernel versions to prevent trying to use unsupported syscalls).

For more info on the advantages of static linking see: stali (short for static linux)

For some shared library propaganda from the former maintainer of glibc, see Ulrich Drepper's "Static Linking Considered Harmful" Though roughly half of the problems with static linking that he mentions are glibc-specific problems (diet, musl and uclibc don't have the same problems).

If you think static libraries don't quite go far enough toward enabling full optimizations, check out Sean Barrett's list of single file libraries. These can often be configured to enable all functions to be static so that the binary can be built as a single compilation unit. This enables several optimizations that can't even be achieved with link time optimization, but you better have an IDE that supports code folding.


Static libraries good then you want tiny package without any problems with conflicting dll's. Also time to load and initialize libraries reduced a lot with static linking.

But as disadvantage you can notice some size increase of binary.

Static librarys on WIKI


If you need a number of small programs, which use only a very small, but sometimes slightly different part of a huge library (it usually happens with large open-source libraries), it might be better to not build a large number of small dynamic libraries, as they will become hard to manage. In this case, it might be a good idea to statically link only the parts you need.


With dynamic libraries if all the libraries aren't present then the application doesn't run. So if you have a partition that holds your libraries and it becomes unavailable then so does the application. If an application has static libraries then the libraries are always present so there is nothing to prevent the application from working. This generally helps in case you are staring your system in maintenance mode.

On a Solaris system for example, commands that you may need to run in the event that some partitions may not be present are stored under /sbin. sbin is short for static binaries. If a partition is unavailable these apps will still work.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号