Differences between static and dynamic libraries in C

Julian Villegas
Basics Full Stack Engineering
7 min readDec 17, 2019
Source: https://unix.stackexchange.com/questions/402949/do-we-need-c-libraries-when-running-a-program

In programming, a “library is a collection of pre-compiled pieces of code that can be reused in a program. Libraries simplify life for programmers, in that they provide reusable functions, routines, classes, data structures and so on
which they can be reused in the programs”.

How do they work?

Every library consists of two parts: a header file and the actual code file. The header file, normally denoted by a .h suffix, contains information about the library that programs using it need to know. In general, the header file contains constants and types, along with prototypes for functions available in the library. i.e.

Example of header file wit all prototypes, source: Own

After that, you have to create the source code in a file .c and include the header created. ie.

Example of include header in a source code file, source: own

By last, you need to know how to compile and get the output for the executable file. Please review my previous story of Steps of compilation in C

Brief description of each library

There are two Linux C/C++ library types which can be created:

  1. Static libraries (.a): Library of object code which is linked with, and becomes part of the application.
  2. Dynamically linked shared object libraries (.so): There is only one form of this library but it can be used in two ways.
  • Dynamically linked at run time. The libraries must be available during compile/link phase. The shared objects are not included into the executable component but are tied to the execution.
  • Dynamically loaded/unloaded and linked during execution (i.e. browser plug-in) using the dynamic linking loader system functions.

Static Libraries : A Static library or statically-linked library is a set of routines, external functions and variables which are resolved in a caller at compile-time and copied into a target application by a compiler, linker, or binder, producing an object file and a stand-alone executable. This executable and the process of compiling it are both known as a static build of the program. Historically, libraries could only be static.

Shared libraries. Are .so (or in Windows .dll, or in OS X .dylib) files.
These are linked dynamically simply including the address of the library (whereas static linking is a waste of space). Dynamic linking links the libraries at the run-time. Thus, all the functions are in a special place in memory space, and every program can access them, without having multiple copies of them.

How to create and how to use libraries in Linux?

1. Static libraries:

Step 1: Compile your Library code(.c files) into an object code

gcc -Wall -pedantic -Werror -Wextra -c *.c

Step 2: Create a Static Library from all “.o” files using command ar

ar -rc liball.a *.o

This tells ar to create an archive (option c) and to insert the objects, replacing older files where needed (option r).

Step 3: Next step is to create index for the library using ranlib command. ranlib makes a header in the library with the symbols of the object file contents.This helps the compiler to quickly reference symbols.

$ ranlib liball.a

Step 4: We have now created a static library libholberton.a. Now let us use the static library by invoking it as part of the compilation and linking process when creating a program executable. Incase of gcc we use following flags to create static library.

gcc main.c -L. lholbertonschool -o main

‘-L’ option - this flag tells the linker that libraries might be found in the given directory ('.', refering to the current directory).

Finally, when you execute the file, the output will show all the logic implemented in the functions called in different libraries. i.e ./main

2. Shared libraries

Step 1: Compile all the C files in your current directory using the below command

gcc -Wall -fPIC -c *.c

  • -Wall: include warnings. See man page for warnings specified.
  • -fPIC: Compiler directive to output position independent code, a characteristic required by shared libraries. Also see “-fpic”.
Object code generated by source files

Step 2: Now we have the object codes .o in our current directory. We need to convert this into a dynamic(shared) object file which can be linked with other objects to form an executable. This is accomplished using the following command.

gcc -shared -Wl,-soname,liball.so -o liball.so *.o

  • -shared: Produce a shared object which can then be linked with other objects to form an executable.
  • -Wl,options: Pass options to linker.
    In this example the options to be passed on to the linker are: “-soname liball.so”. The name passed with the “-o” option is passed to gcc.
  • Option -o: Output of operation. In this case the name of the shared object to be output will be “liball.so”
shared library created(blue wrapped)

Now, is time to list symbols from object files with nm command.

nm -D liball.so

  • -D means — the symbol is in the initialized data section.
Listing symbols

Step 3: To test and compile the shared library, we have to create a source file (main) where it reuse functions included at header file.

Example of main function with an own prototype (_strlen)

gcc -Wall -pedantic -Werror -Wextra -L. 0-main.c -lall -o len

Step 4: List dependencies. The shared library dependencies of the executable can be listed with the command: ldd name-of-executable

list dependencies

The second line, with says “not found” is a problem and whe have to fix is resolving dependencies when linking the library libname-of-lib.so:

Step 5: Add the library path to the environment variable to fix run-time dependency: export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

Since Dynamic Library is linked during runtime, we obviously need to make this file available during runtime. The dynamic linker searches standard paths available in the LD_LIBRARY_PATH and also searches in system cache (ldconfig) for dynamic libraries. So our next step is to add our working directory to the LD_LIBRARY_PATH environment variable so that the linker can find our library file. The below command is used for this.

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/lib

export PATH

Now we are ready to run our binary file! the address is located for our shared library in environment variable. Use “./len”

Differences between static and dynamic 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].

Advantages and drawbacks of each of them

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.
  • In shared libraries, no need to recompile the executable.

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.
  • Executable file will have to be recompiled if any changes were applied to external files.

REFERENCES

--

--

Julian Villegas
Basics Full Stack Engineering

Professional System Engineer and Student at Holberton School Colombia