Reputation: 16321
I am messing around with a test project, lets call it mytest
, it has a .cpp and a .h file, the contents are not really important - imagine it contains a few simple hello_world()
type functions...
So, I was making a generic makefile to compile this into the various library outputs where an ls -l
on my output folder gives:
libmytest.a
libmytest.so -> libmytest.so.1.0
libmytest.so.1 -> libmytest.so.1.0
libmytest.so.1.0
All good so far, my shared / static libraries are created.
Now I have a make install
target in my make file, which basically copies the header to /usr/local/include
and all of these library files to /usr/local/lib
Then I made another test cpp file called usertest.cpp
(sorry for the not-very-imaginative/descriptive names), which links to the library files.
I compiled in various ways:
g++ -Wall -Werror -I. -lmytest
g++ -Wall -Werror -I. -lmytest -static
Then I deleted the libmytest.so* files so I only had the libmytest.a library file in /usr/local/lib
Then I did the same test:
g++ -Wall -Werror -I. -lmytest
g++ -Wall -Werror -I. -lmytest -static
Finally I deleted the libmytest.a file and copied back the .so files so I only had the libmytest.so* library files in /usr/local/lib
Then I did the same test:
g++ -Wall -Werror -I. -lmytest
g++ -Wall -Werror -I. -lmytest -static
The file size results(in bytes) are:
1. 7736 - Makes sense, all libs dynamically linked
2. 19674488 - Makes sense, all libs statically linked
3. 64908 - hmm... not really sure why
4. 19674488 - Makes sense, same as 2.
5. 7736 - Makes sense, same as 1.
6. failed - Makes sense, no .so files!
I have three files sizes, the small (7736) is fully dynamically linked. The large is statically linked.... what is this medium one (64908)? So I have questions:
Note all outputs run fine and call functions from the library.
Upvotes: 3
Views: 5229
Reputation: 61137
For 1. I assume the system looks for .so libraries first and .a libraries second?
That's roughly right, but read on.
For 3. What happened here? - is it dynamically linking the system libs but when it sees my .a lib it dynamically links it?
A static library cannot be dynamically linked: it is statically linked. The shared ( = dynamic) system libraries are linked, assuming that the system libraries that the linker finds and prefers are in fact shared libraries.
By default, the linkage option -lmytest
directs the linker to search for an input file called libmytest.so
(shared library)
or libmytest.a
(static library), first in the search directories you have specified in the commandline with
the -Ldirname
option, in the order specified, and then in its default search directories, in the configured order.
It stops searching when it finds either of those files in one of those directories. If it finds both of them in
the same directory then it selects the shared library, libmytest.so
. The selected file, if any, is input to the linkage.
If the search is unsuccessful the linker gives an error: cannot find -lmytest
.
This default behaviour can be changed by the option -static
. If it appears anywhere in the commandline, the linker
ignores all shared libraries: then -lmytest
can only be satisfied by finding libmytest.a
, and static system libraries must also be found.
/usr/local/lib
is one of the linker's default search directories. So when you execute:
g++ -Wall -Werror -I. -lmytest
in the scenario (3) where, /usr/local/lib/libmytest.a
is found by the linker and /usr/local/lib/libmytest.so
is not,
libmytest.a
satisfies -lmytest
and is input to the linkage. The linker's default preference for shared libraries is unaffected.
The contribution that the linkage of libmytest.a
makes to the size of the executable is not obvious.
A static library - quite unlike a shared library - is not an ELF binary that the linker has produced. It is
ar archive
of object files, produced by ar
: it is a bag of files just that
happen to be object files.
By default, when an ar
archive is input to the linker, it looks in the bag to find any object files that
provide definitions for any undefined symbol references that have accrued from object files already
linked into the output file (program or shared library) when the archive was inspected. If it finds any
such object files, it extracts them from the archive and links them into the output file, exactly as if they
had been individually listed in the commandline and the archive not mentioned at all. Except as a bag from which
object files may be selected, the archive contributes nothing to the linkage.
Thus, if there are N
object files in libmytest.a
, inputting that archive to a linkage might
contribute between 0 and N
object files to the output file, depending on what undefined references into members of
that set of object files accrue earlier in the linkage, and which object files provide definitions for those
references.
And even if you know exactly which object files in libmytest.a
will be required in your linkage, you cannot
conclude that the sum of their sizes will be added to the size of the output file. An object file is
partitioned into sections by the compiler, a section being the smallest unit of input and output that the linker
recognizes. By default the linker will retain an input section for output only if that section provides the linker's selected definition of some symbol that the linkage must define. If an input section is of no such use, the
linker will just discard it. So, even if an object file is linked, the linker might omit redundant sections
within it from the output file.
The behaviour of the -l | --library
linker option is documented in 2.1 Command Line Options
of the GNU ld
manual
Upvotes: 5
Reputation: 8851
Most probably libmytest.a
is not the one, who plays major role in the binary size increase, but bigger standard libraries (that explains why the size didn't grow much in 3.).
You can investigate all the dynamic dependencies of your binary using ldd
:
ldd a.out
(and which of them are disappearing after using -static
).
Upvotes: 1