What Is Inside .Lib File of Static Library, Statically Linked Dynamic Library and Dynamically Linked Dynamic Library

What is inside .lib file of Static library, Statically linked dynamic library and dynamically linked dynamic library?

For a static library, the .lib file contains all the code and data for the library. The linker then identifies the bits it needs and puts them in the final executable.

For a dynamic library, the .lib file contains a list of the exported functions and data elements from the library, and information about which DLL they came from. When the linker builds the final executable then if any of the functions or data elements from the library are used then the linker adds a reference to the DLL (causing it to be automatically loaded by Windows), and adds entries to the executable's import table so that a call to the function is redirected into that DLL.

You don't need a .lib file to use a dynamic library, but without one you cannot treat functions from the DLL as normal functions in your code. Instead you must manually call LoadLibrary to load the DLL (and FreeLibrary when you're done), and GetProcAddress to obtain the address of the function or data item in the DLL. You must then cast the returned address to an appropriate pointer-to-function in order to use it.

DLL and LIB files - what and why?

There are static libraries (LIB) and dynamic libraries (DLL) - but note that .LIB files can be either static libraries (containing object files) or import libraries (containing symbols to allow the linker to link to a DLL).

Libraries are used because you may have code that you want to use in many programs. For example if you write a function that counts the number of characters in a string, that function will be useful in lots of programs. Once you get that function working correctly you don't want to have to recompile the code every time you use it, so you put the executable code for that function in a library, and the linker can extract and insert the compiled code into your program. Static libraries are sometimes called 'archives' for this reason.

Dynamic libraries take this one step further. It seems wasteful to have multiple copies of the library functions taking up space in each of the programs. Why can't they all share one copy of the function? This is what dynamic libraries are for. Rather than building the library code into your program when it is compiled, it can be run by mapping it into your program as it is loaded into memory. Multiple programs running at the same time that use the same functions can all share one copy, saving memory. In fact, you can load dynamic libraries only as needed, depending on the path through your code. No point in having the printer routines taking up memory if you aren't doing any printing. On the other hand, this means you have to have a copy of the dynamic library installed on every machine your program runs on. This creates its own set of problems.

As an example, almost every program written in 'C' will need functions from a library called the 'C runtime library, though few programs will need all of the functions. The C runtime comes in both static and dynamic versions, so you can determine which version your program uses depending on particular needs.

Differences between static libraries and dynamic libraries ignoring how they are used by the linker/loader

It's a pity the terms static library and dynamic library are
both of the form ADJECTIVE library, because it perpetually leads programmers
to think that they denote variants of the essentially the same kind of thing.
This is almost as misleading as the thought that a badminton court and a supreme court
are essentially the same kind of thing. In fact it's far more misleading,
since nobody actually suffers from thinking that a badminton court and a supreme
court are essentially the same kind of thing.

Can someone throw some light on the differences between the contents of static and shared library files?

Let's use examples. To push back against the badminton court / supreme court fog
I'm going to use more accurate technical terms. Instead of static library I'll say ar archive, and instead of dynamic library I'll say
dynamic shared object, or DSO for short.

What an ar archive is

I'll make an ar archive starting with these three files:

foo.c

#include <stdio.h>

void foo(void)
{
puts("foo");
}

bar.c

#include <stdio.h>

void bar(void)
{
puts("bar");
}

limerick.txt

There once was a young lady named bright
Whose speed was much faster than light
She set out one day
In a relative way
And returned on the previous night.

I'll compile those two C source into Position Independent object files:

$ gcc -c -Wall -fPIC foo.c
$ gcc -c -Wall -fPIC bar.c

There's no need for object files destined for an ar archive to be compiled with
-fPIC. I just want these ones compiled that way.

Then I'll create an ar archive called libsundry.a containing the object files foo.o and bar.o,
plus limerick.txt:

$ ar rcs libsundry.a foo.o bar.o limerick.txt

An ar archive is created, of course, with ar,
the GNU general-purpose archiver. So it is not created by the linker. No linkage
happens. Here's how ar reports the contents of the archive:

$ ar -t libsundry.a 
foo.o
bar.o
limerick.txt

Here's what the limerick in the archive looks like:

$ rm limerick.txt
$ ar x libsundry.a limerick.txt; cat limerick.txt
There once was a young lady named bright
Whose speed was much faster than light
She set out one day
In a relative way
And returned on the previous night.

Q. What's the point of putting two object files and an ASCII limerick into the same ar archive?

A. To show that I can. To show that an ar archive is just a bag of files.

Let's see what file makes of libsundry.a.

$ file libsundry.a 
libsundry.a: current ar archive

Now I'll write a couple of programs that use libsundry.a in their linkage.

fooprog.c

extern void foo(void);

int main(void)
{
foo();
return 0;
}

Compile, link and run that one:

$ gcc -c -Wall fooprog.c
$ gcc -o fooprog fooprog.o -L. -lsundry
$ ./fooprog
foo

That's hunky dory. The linker apparently wasn't bothered by the presence of
an ASCII limerick in libsundry.a.

The reason for that is the linker didn't even try to link limerick.txt
into the program. Let's do the linkage again, this time with a diagnostic option
that will show us exactly what input files are linked:

$ gcc -o fooprog fooprog.o -L. -lsundry -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o
fooprog.o
(./libsundry.a)foo.o
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o

Lots of default libraries and object files there, but the only object
files we have created that the linker consumed are:

fooprog.o
(./libsundry.a)foo.o

All that the linker did with ./libsundry.a was take foo.o out of
the bag and link it in the program
. After linking fooprog.o into the program,
it needed to find a definition for foo.
It looked in the bag. It found the definition in foo.o, so it took foo.o from
the bag and linked it in the program. In linking fooprog,

gcc -o fooprog fooprog.o -L. -lsundry

is exactly the same linkage as:

$ gcc -o fooprog fooprog.o foo.o

What does file say about fooprog?

$ file fooprog
fooprog: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), \
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, \
for GNU/Linux 2.6.32, BuildID[sha1]=32525dce7adf18604b2eb5af7065091c9111c16e,
not stripped

Here's my second program:

foobarprog.c

extern void foo(void);
extern void bar(void);

int main(void)
{
foo();
bar();
return 0;
}

Compile, link and run:

$ gcc -c -Wall foobarprog.c
$ gcc -o foobarprog foobarprog.o -L. -lsundry
$ ./foobarprog
foo
bar

And here's the linkage again with -trace:

$ gcc -o foobarprog foobarprog.o -L. -lsundry -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o
foobarprog.o
(./libsundry.a)foo.o
(./libsundry.a)bar.o
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o

So this time, our object files that the linker consumed were:

foobarprog.o
(./libsundry.a)foo.o
(./libsundry.a)bar.o

After linking foobarprog.o into the program, it needed to find definitions for foo and bar.
It looked in the bag. It found definitions respectively in foo.o and bar.o, so it took them from
the bag and linked them in the program. In linking foobarprog,

gcc -o foobarprog foobarprog.o -L. -lsundry

is exactly the same linkage as:

$ gcc -o foobarprog foobarprog.o foo.o bar.o

Summing all that up. An ar archive is just a bag of files. You can use
an ar archive to offer to the linker a bunch of object files from which to
pick the ones that it needs to continue the linkage. It will take those object files
out of the bag and link them into the output file. It has absolutely no other
use for the bag. The bag contributes nothing at all to the linkage.

The bag just makes your life a little simpler by sparing you the need to know
exactly what object files you need for a particular linkage. You only need
to know: Well, they're in that bag.

What a DSO is

Let's make one.

foobar.c

extern void foo(void);
extern void bar(void);

void foobar(void)
{
foo();
bar();
}

We'll compile this new source file:

$ gcc -c -Wall -fPIC foobar.c

and then make a DSO using foobar.o and re-using libsundry.a

$ gcc -shared -o libfoobar.so foobar.o -L. -lsundry -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtbeginS.o
foobar.o
(./libsundry.a)foo.o
(./libsundry.a)bar.o
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/lib/x86_64-linux-gnu/libc.so.6
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/usr/lib/gcc/x86_64-linux-gnu/5/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o

That has made the DSO libfoobar.so. Notice: A DSO is made by the linker. It
is linked just like a program is linked. The linkage of libfoopar.so looks very much
like the linkage of foobarprog, but the addition of the option
-shared instructs the linker to produce a DSO rather than a program. Here we see that our object
files consumed by the linkage were:

foobar.o
(./libsundry.a)foo.o
(./libsundry.a)bar.o

ar does not understand a DSO at all:

$ ar -t libfoobar.so 
ar: libfoobar.so: File format not recognised

But file does:

$ file libfoobar.so 
libfoobar.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), \
dynamically linked, BuildID[sha1]=16747713db620e5ef14753334fea52e71fb3c5c8, \
not stripped

Now if we relink foobarprog using libfoobar.so instead of libsundry.a:

$ gcc -o foobarprog foobarprog.o -L. -lfoobar -Wl,-trace,--rpath=$(pwd)
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o
foobarprog.o
-lfoobar (./libfoobar.so)
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o

we see

foobarprog.o
-lfoobar (./libfoobar.so)

that ./libfoobar.so itself was linked. Not some object files "inside it". There
aren't any object files inside it. And how this has
contributed to the linkage can be seen in the dynamic dependencies of the program:

$ ldd foobarprog
linux-vdso.so.1 => (0x00007ffca47fb000)
libfoobar.so => /home/imk/develop/so/scrap/libfoobar.so (0x00007fb050eeb000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb050afd000)
/lib64/ld-linux-x86-64.so.2 (0x000055d8119f0000)

The program has come out with runtime dependency on libfoobar.so. That's what linking a DSO does.
We can see this runtime dependency is satisfied. So the program will run:

$ ./foobarprog
foo
bar

just the same as before.

The fact that a DSO and a program - unlike an ar archive - are both products
of the linker suggests that a DSO and a program are variants of the essentially the same kind of thing.
The file outputs suggested that too. A DSO and a program are both ELF binaries
that the OS loader can map into a process address space. Not just a bag of files.
An ar archive is not an ELF binary of any kind.

The difference between a program-type ELF file and non-program-type ELF lies in the different values
that the linker writes into the ELF Header structure and Program Headers
structure of the ELF file format. These differences instruct the OS loader to
initiate a new process when it loads a program-type ELF file, and to augment
the process that it has under construction when it loads a non-program ELF file. Thus
a non-program DSO gets mapped into the process of its parent program. The fact that a program
initiates a new process requires that a program shall have single default entry point
to which the OS will pass control: that entry point is the mandatory main function
in a C or C++ program. A non-program DSO, on the other hand, doesn't need a single mandatory entry point. It can be entered through any of the global functions it exports by function calls from the
parent program.

But from the point of view of file structure and content, a DSO and a program
are very similar things. They are files that can be components of a process.
A program must be the initial component. A DSO can be a secondary component.

It is still common for the further distinction to be made: A DSO must consist entirely
of relocatable code (because there's no knowing at linktime where the loader may need to
place it in a process address space), whereas a program consists of absolute code,
always loaded at the same address. But in fact its quite possible to link a relocatable
program:

$ gcc -pie -o foobarprog foobarprog.o -L. -lfoobar -Wl,--rpath=$(pwd)

That's what -pie (Position Independent Executable) does here. And then:

$ file foobarprog
foobarprog: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), ....

file will say that foobarprog is a DSO, which it is, although it is
also still a program:

$ ./foobarprog
foo
bar

And PIE executables are catching on. In Debian 9 and derivative distros (Ubuntu 17.04...) the GCC toolchain
builds PIE programs by default.

If you hanker for detailed knowledge of the ar and ELF file
formats, here are details of the ar format
and here are details of the ELF format.

why not have a single type of library file accompanied by compiler flags which
indicate how the library should be linked (static vs dynamic)?

The choice between dynamic and static linkage is already fully controllable by
commandline linkage options, so there's no need abandon either ar archives or DSOs or to invent a another kind
of library to achieve this. If the linker couldn't use ar archives the way it does,
that would be a considerable inconvenience. And of course if the linker couldn't link
DSOs we'd back to the operating systems stone-age.

Why shared library linked with static library behaves like it was linked with dynamic one?

Shared libraries and static libraries are very different beasts.

A static library is literally an archive file, similar to a .tar file, whose contents are object files. Typically these are augmented with an index of the external symbols defined in the object files. During static linking, the linker extracts some or all of those object files from the library and links those with any other specified objects, pretty much as if the extracted object files had been specified directly. There is no inherent distinction between external symbols based on which object provided them, or on whether that object was drawn from a (static) library.

Shared libraries are integrated objects, very much along the lines of programs. They can be executable programs. There are no individual object files within. In shared-library formats that feature position-independent code, all external symbols provided by the library must be position-independent.

When you link a static library into a shared one, you are asking the linker to include the contents of some or all of the object files from the static library in the resulting shared library. That's the only way it can work. The external symbols resolved from the static library will be external symbols in the shared one, too, so references to them need to be relative, including from other functions in the shared lib.

Are lib files exclusively statically linked or do they need to be compiled specifically (VS2015)

In Windows, dll files (dynamically linked libraries) need to be in the same directory as the application or on the search path. lib files (static libraries) need to be statically linked during linking (the last step of building the application). It's common in Windows so have a library come with both a dll and lib file. In this case, the lib file is an import library containing the information needed to easily link to the dll.

Place the dll file where your application will be built and statically link with the lib file. Go to 'Project->Properties->Link->Input->Additional Dependencies' and 'Project->Properties->Link->General->Additional Library Directories' to specify the static libraries you want to link.

Edit: It seems I misunderstood the question. The question is how to recompile a dynamic library as a static library. You need the source code of the library you are using along with it's Visual Studio Project file. Open the library and in `Project->Properties->General->Configuration Type' change it from Dynamic Library to Static Library.

Beware that Dynamic Library uses the Linker group of properties while the Static Library uses the Librarian group of properties. Changing between these types may cause the project to drop essential linker flags options. Since every library is different, I can't predict what you will have to do work around this. Make sure to backup the project file so you can see the original options and flags.

When to use dynamic vs. static libraries

Static libraries increase the size of the code in your binary. They're always loaded and whatever version of the code you compiled with is the version of the code that will run.

Dynamic libraries are stored and versioned separately. It's possible for a version of the dynamic library to be loaded that wasn't the original one that shipped with your code if the update is considered binary compatible with the original version.

Additionally dynamic libraries aren't necessarily loaded -- they're usually loaded when first called -- and can be shared among components that use the same library (multiple data loads, one code load).

Dynamic libraries were considered to be the better approach most of the time, but originally they had a major flaw (google DLL hell), which has all but been eliminated by more recent Windows OSes (Windows XP in particular).

Why cant you statically link dynamic libraries?

Why is this the case?

Most linkers (AIX linker is a notable exception) discard information in the process of linking.

For example, suppose you have foo.o with foo in it, and bar.o with bar in it. Suppose foo calls bar.

After you link foo.o and bar.o together into a shared library, the linker merges code and data sections, and resolves references. The call from foo to bar becomes CALL $relative_offset. After this operation, you can no longer tell where the boundary between code that came from foo.o and code that came from bar.o was, nor the name that CALL $relative_offset used in foo.o -- the relocation entry has been discarded.

Suppose now you want to link foobar.so with your main.o statically, and suppose main.o already defines its own bar.

If you had libfoobar.a, that would be trivial: the linker would pull foo.o from the archive, would not use bar.o from the archive, and resolve the call from foo.o to bar from main.o.

But it should be clear that none of above is possible with foobar.so -- the call has already been resolved to the other bar, and you can't discard code that came from bar.o because you don't know where that code is.

On AIX it's possible (or at least it used to be possible 10 years ago) to "unlink" a shared library and turn it back into an archive, which could then be linked statically into a different shared library or a main executable.

If foo.o and bar.o are linked into a foobar.so, wouldn't it make sense that the call from foo to bar is always resolved to the one in bar.o?

This is one place where UNIX shared libraries work very differently from Windows DLLs. On UNIX (under common conditions), the call from foo to bar will resolve to the bar in main executable.

This allows one to e.g. implement malloc and free in the main a.out, and have all calls to malloc use that one heap implementation consistently. On Windows you would have to always keep track of "which heap implementation did this memory come from".

The UNIX model is not without disadvantages though, as the shared library is not a self-contained mostly hermetic unit (unlike a Windows DLL).

Why would you want to resolve it to another bar from main.o?

If you don't resolve the call to main.o, you end up with a totally different program, compared to linking against libfoobar.a.



Related Topics



Leave a reply



Submit