The choice between one or the other has a direct impact on program size, portability and maintenance

the extensions:

  • .a (static library) (linux/unix) (archive) (“ar rcs” command)
  • .so (dynamic library) (linux/unix) (gcc for example) (Shared Object)
  • .dll (dynamic library) (windows) (Dynamic-Link Library)
  • .lib (static library) (windows)

moment when your code is linked to the executable program:

  • Compile/Link Time (Static Linking) (static library)
  • Runtime (Dynamic Linking) (dynamic library)

In a dynamic library, the executable depends on the .so file to run; if it is missing, the program fails.

  • The linker copies all the library code that is needed into the final executable (static library)
  • The executable contains only a reference (a pointer) to the library. The code is loaded into memory by the operating system when the program starts (dynamic library)

features

  • Size and Memory Usage
    • static library
      • The executable is larger because it includes the library code
    • dynamic library
      • The executable is smaller because it only has references. The library code is loaded into system memory only once and is shared by all programs that use it, saving disk space and RAM
  • Maintenance and Updates
    • static library
      • To fix a bug or update the functionality of a library, you must recompile and redistribute all executables that use that library
    • dynamic library
      • You can update the library simply by replacing the .so file without having to recompile the executables (as long as the interface, or ABI, is maintained). This is ideal for security patches and updates
    • Portability
      • static library
        • Excellent portability. The executable is self-contained, and you can move it to another system that doesn’t have the library installed
      • dynamic library
        • Poor portability. The target system must have the library installed and in a known location for the program to work. This sometimes leads to the dreaded “DLL Hell” on Windows, or problems with LD_LIBRARY_PATH on Linux if the versions don’t match or the path is incorrect

I have 2 directories

  • A
  • B

There are 2 projects contained under the same directory, A is the library and B uses A

A and B use the same directory structure:

  • src: where all the source code files (.c) go
  • includes: function definitions (contracts), which use other .h files
  • bin: where all the binaries or partially binaries go, .o, executables, .so, .a, that is, everything that is needed to put together the final executable.

We create the .o files of the library (we reference the .h files with “include” directory) (in A location)

gcc -c src/file.c -o bin/library.o -fPIC -I include

This means that the code is created in such a way that it can be loaded and executed at any memory address without needing to be modified by the operating system when it is loaded

By using -fPIC, the generated object code (file.o) uses relative addresses (relative to itself, not absolute), which allows it to be moved (“relocated”) in memory without problems

-fPIC is a key instruction: Generates position-independent code for use in a shared library

-fPIC transforms file.c into a module ready to be a flexible and relocatable component of a dynamic library

From the .o we create a .so (shared-object) that is, a dynamic library (in A location)

gcc -shared bin/library.o -o bin/dynamiclibrary.so -install_name dynamiclibrary.so

The new .so will be created in the /bin directory along with the other .o files

at the root of the project that uses the library (in B location)

gcc -c src/main.c -o bin/main.o -I ../A/include

Note that with this in the include you only put the name of the .h file and not its path, you specify that in the gcc command

that will compile and create a .o (in B location)

gcc bin/main.o -o bin/executable ../A/bin/dynamiclibrary.so \
-Wl,-rpath,@loader_path/../../A/bin

This links the .o of the main program with the .so of the library (dynamic library)

Note that up to this point we have only used the gcc command

If you are on macOS, you are missing one more step than forcing the links if there is an incorrect path (I’m on macOS) (in B location)

install_name_tool -change dynamiclibrary.so \
@loader_path/../../A/bin/dynamiclibrary.so \
bin/executable

You can then run the final executable by going to /bin

./executable


example working

A is librariesc

B is uselibraryc

B use to A

https://gitlab.com/com.leibnix/shared-library-c


If you noticed for the gcc command I use the flags for GCC:

  • -Wl,-rpath,@loader_path
    • Runtime Library Search Path
    • involves the linker
    • Wl means “Wrapper for the Linker”
    • Tells the GCC compiler: “Don’t interpret the next argument, pass it directly to the linker.” (The linker is the tool that puts all the compiled code together to create the final file)
    • -rpath stands for “Runtime Path”
      • It is a special instruction embedded within the final executable or library. It specifies a list of directories where the operating system’s dynamic loader should search for the necessary shared libraries when the program is executed
    • @loader_path
      • Dynamic Variable
      • is a special variable (common on macOS, similar to $ORIGIN on Linux)
      • Resolves to the directory where the library or executable being loaded is located
    • When using -Wl, -rpath, @loader_path together
      • you tell the compiler: When you need to find a dependency, first look for it in the same directory where I am right now (@loader_path)
    • The location of the bookstore becomes relative and not absolute
    • This is very useful for creating portable application packages
    • If you package your executable application along with all its libraries (.so or .dylib) into a single folder called MyApp, this option ensures that the application will find its libraries correctly, no matter where the user moves the MyApp folder
  • -fPIC
    • Position-Independent Code
    • You tell the compiler to generate machine code that does not depend on being loaded into a specific memory address
      • Normal executables: The code is compiled assuming a fixed memory location
      • Shared (or dynamic) libraries: These libraries are loaded into the memory space of many different programs and must be able to be located at any available address assigned to them by the operating system
    • Analogy
      • Without -fPIC: The manual says, “The boss’s desk is at memory address 1000.” If the library is loaded elsewhere, that address 1000 is no longer correct and the program fails
      • With -fPIC: The manual says, “The boss’s desk is 30 meters from the library entrance.” No matter where you load the library, the instructions are correct because they are relative to your own starting point
    • -fPIC is vital for shared libraries, as it ensures that code can be loaded and executed anywhere in program memory without causing conflicts