Why a Static Library Can Depend on a Shared a Library

Can a C++ Static Library link to shared library?

Static libraries are not linked. They are just a collection of object files (*.obj or *.o) that are archived together into a library file (kind of like a tar/zip file) to make it easier for the linker to find the symbols it needs.

A static lib can call functions that are not defined (but are only declared in a header file), as it is only compiled. Then when you link an exe or dll that uses the static lib you will have to link with another library that provides the called from the static lib but not defined in it.

If you want to the linker to automatically link other libraries Stephen's suggestion will work and is used by very reputable libraries like boost and stlport. To do this put the pragma in the main header file for the static library. You should include the static library and its dependants.

However IMO this feature is really meant for library writers, where the library is in the system library path so the linker will easily find it. Also in the case of boost and stlport they use this feature to support multiple version of the same libraries with options defined with #defines where different options require different versions of the library to be linked. This means that users are less likely to configure boost one way and link with a library configured another.

My preference for application code is to explicitly link the required parts.

Why a static library can depend on a shared a library?

This claim

a static library cannot depend on a shared library in Linux

is completely wrong.

A static library is simply a collection of object files.

If you can have code using imports from a shared library that compiles to an .o file, you can collect those .o files into a library and now you have a library that uses imports from a shared library.

The only real difference that a static library makes, compared to linking all the object code directly, is that the library contains an index of symbols that the linker uses to decide which object files inside the library need to be linked. Whereas object files directly passed to the linker are always linked in. This has important ramifications for global initializer behavior and very little else.

Difference between static and shared libraries?

Shared libraries are .so (or in Windows .dll, or in OS X .dylib) files. All the code relating to the library is in this file, and it is referenced by programs using it at run-time. A program using a shared library only makes reference to the code that it uses in the shared library.

Static libraries are .a (or in Windows .lib) files. All the code relating to the library is in this file, and it is directly linked into the program at compile time. A program using a static library takes copies of the code that it uses from the static library and makes it part of the program. [Windows also has .lib files which are used to reference .dll files, but they act the same way as the first one].

There are advantages and disadvantages in each method:

  • Shared libraries reduce the amount of code that is duplicated in each program that makes use of the library, keeping the binaries small. It also allows you to replace the shared object with one that is functionally equivalent, but may have added performance benefits without needing to recompile the program that makes use of it. Shared libraries will, however have a small additional cost for the execution of the functions as well as a run-time loading cost as all the symbols in the library need to be connected to the things they use. Additionally, shared libraries can be loaded into an application at run-time, which is the general mechanism for implementing binary plug-in systems.

  • Static libraries increase the overall size of the binary, but it means that you don't need to carry along a copy of the library that is being used. As the code is connected at compile time there are not any additional run-time loading costs. The code is simply there.

Personally, I prefer shared libraries, but use static libraries when needing to ensure that the binary does not have many external dependencies that may be difficult to meet, such as specific versions of the C++ standard library or specific versions of the Boost C++ library.

Why are shared and static libraries different things?

So I see lots of answers talking about why you would want to use shared libraries instead of static libraries, but I think your question is why they are even distinct things nowadays, i.e. why isn't it possible to use a shared library as a static library and pull what you need out of it at build time?

Here are some reasons. Some of these are historical - keep in mind that something as fundamental as binary formats changes very slowly in computer systems.

Compiled Differently

Code can be compiled either to be dependent on the address it sits at (position-dependent) or independent (position-independent). This affects things like loads of global constants, function calls, etc. Position-dependent code needs fixups if it isn't loaded at the address it expects, i.e. the loader has to go over the code and actually change offsets.

For executables, this isn't a problem. An executable is the first thing that is loaded into the address space, so it will always be loaded at the same address. You generally don't need any fixups. But a shared library is used by different executables, by different processes. Multiple libraries can conflict: if they expect to be at overlapping address ranges, one will have to budge. When it does, and it is position-dependent, it needs to be fixed by the loader. But now you have process-specific changes in the library code, which means the code can't be shared (at runtime) with other processes anymore. You lose one of the big benefits of shared libraries.

If the shared library uses position-independent code (PIC), it doesn't need fixups. So PIC is good for shared libraries. On the other hand, PIC is slower on some architectures (notably x86, but not x64), so compiling executables as PIC is a waste of resources.

Executables were therefore usually compiled as position-dependent code, while shared libraries were compiled as position-independent code. If you used shared libraries as sources for code directly pulled into executables, you get PIC. If you want PDC, you need a separate code repository, and that's a static library.

Of course, on most modern architectures, PIC isn't less efficient than PDC, and security techniques like address space randomization make it useful to compile executables as PIC too, so this is more of a historical reason than a current one.

Contain Different Things

But there's another, more current reason for separating static and shared libraries, and that's link-time optimization.

Basically, the more information an optimizer has about a program, the better it can reason about it. Classical optimizer worked on a per-module basis: compile a .c file, optimize it, generate object code. The linker took all the object files and merged them together. This means that the optimizer can only reason about one module at a time. It cannot look into the called functions that are outside the module in order to reason about them, or even simply inline them.

In modern toolchains, however, the compiler often works differently. Instead of compiling and optimizing a module and then producing object code, it takes a module, produces an intermediate form, possibly optimizes it a bit, and then puts the intermediate form into the object file. The linker, instead of just merging object files and resolving references, actually merges the intermediate representation and then invokes the optimizer and code generator on the merged form. With much more information available, the optimizer can do a vastly better job.

This intermediate representation is more detailed, more faithful to the original code than machine code. You want this for your compilation process. You don't want to ship it to the customer, because it is much bigger, and if you use a closed-source model also because it is much easier to reverse-engineer. Moreover, there's no point in shipping it, because the loader doesn't understand it, and you don't want to re-optimize and recompile your program at startup time anyway (JIT languages aside).

Thus, a shared library contains real object code. A static library, on the other hand, is a good container for intermediate code, because it is consumed by the linker. This is a key difference between static and shared libraries.

Linkage Model

Finally, we have another semi-historical reason: linkage.

Linkage defines how a symbol (a variable or function name) is visible outside a code unit. The C language defines two linkages: internal (not visible outside the compilation unit, i.e. static) and external (visible to the whole program, i.e. extern). You generally have a lot of externally visible symbols.

Shared libraries, however, have their symbols resolved at load time, and this should be fast. Fewer symbols means lookup in the symbol table is faster. Of course this was more relevant when computers were slower, but it still can have a noticeable effect. It also affects the size of the libraries.

Therefore, object file specifications used by the operating systems (ELF for *nix, PE/COFF for Windows) defined separate visibilities for shared libraries. Instead of making everything that's external in C visible, you have the option to specify the visible functions explicitly. (In Windows, only things annotated as __declspec(dllexport), or listed in a .def file are exported from a DLL. In Linux, everything extern is exported by default, but you can use __attribute__((visibility("hidden"))) to not do that, or you can specify the -fvisibility=hidden command line switch or the visibility pragma to override the default.)

The end result is that a shared library throws away all symbol information except for the exported functions.

A static library has no need to throw away any symbol information. What's more, you don't want to do that, because carefully specifying which functions are exported and which aren't is some work, and you don't want to have to do that work unless necessary. If you're using static libraries, it isn't necessary.

So a shippable shared library should minimize its exported symbols in order to be fast and small. This makes it less useful as a code repository for static linking, where you may want a greater selection of functions to link in, especially once the interface functions get inlined (see link-time optimization above).

Can a dynamic library depend on a static library in C and vice-versa?

From gcc link options:

-llibrary

-l library

...

It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified. Thus, ‘foo.o -lz bar.o’ searches library ‘z’ after file foo.o but before bar.o. If bar.o refers to functions in ‘z’, those functions may not be loaded.

Try:

gcc main.c -lB -lA -L. -I. -o EXE

How do static libraries do linking to dependencies?

Static linking is just copying the whole items (functions, constants, etc) into the resulting executable. If a static library's code contains references to some shared library items, these references will become dependencies in the resulting executable. The same holds if you link a library instead of executable.

This thread discusses how it happens in Linux.

Shared library with statically linked dependencies

Will the static version of library (lstatic) loaded by lshared.so and the dynamic version (lstatic.so) conflict.

Possibly.

Will they share any global state.

Possibly.

The answers depend on how exactly lshared.so is built (which symbols it exports), and how the main helloworld binary is linked.

The answers also tend to be somewhat complicated, and may depend on inlining and other optimizations, and possibly on compiler versions.

It is best to avoid doing that by using shared version of lstatic.so, where all the answers become simple.

Static library depending on shared library

I don't know about Solaris, so assume everything in my answer here applies to Linux only (although pkg-config should be usable on Solaris too).

First off, there's no supported way for a static library to pull in a link-time dependency. Sorry. Most libraries use something like pkg-config for this - that is, when you build, you add to the compiler command line:

gcc `pkg-config --cflags your-library` [...]

and when you link:

gcc `pkg-config --libs your-library` [...]

Note that the --libs parameter, in this case, would produce something like -ldl -lyourlib as output. The --cflags parameter may produce no output, or it may add some include paths.

If you absolutely need it to work with just a -lyourlib, though, and you don't mind being tied to a unsupported and unstable interface in glibc... Well, libdl is just a thin wrapper over some routines in the dynamic linker, via an undocumented vtable. If you look at the source under the dlfcn/ directory of the version of glibc in use, you should be able to replicate what it does.

HOWEVER, the interface between libdl and ld-linux is PRIVATE and UNSTABLE - it may change at any glibc release, including minor and bugfix releases. ONLY do this if you control the version of glibc deployed, and are prepared to rebuild your application when necessary. Also note that if your library is not LGPL itself, then using a private API like this may have licensing issues; not sure how things stand with this for the LGPL.



Related Topics



Leave a reply



Submit