Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Building Static and Shared Libraries with GCC on Linux

Tech 2

Static libraries bundle multiple object files into a single archive, while shared libraries defer symbol resolution until load or runtime. Both forms are comon on Linux and can be produced with GCC and standard binutils.

Inspecting a system static library

Static archives are regullar files with the .a suffix, typically created with ar. For example, glibc’s static C runtime archive can be examined to see which objects it includes.

List members and look for fread-related objects:

$ ar -t "$(gcc -print-file-name=libc.a)" | grep fread

Locate objects related to printf using objdump:

$ objdump -t "$(gcc -print-file-name=libc.a)" | grep 'printf\.o'

These commansd reveal object members such as iofread.o, vprintf.o, printf.o, etc., all packaged into libc.a.

Dynamic vs. static linking with a tiny program

hello.c:

#include <stdio.h>

int main(void) {
    printf("Hello, C!\n");
    return 0;
}

Default build (typically dynamically linked):

$ gcc hello.c -o hello
$ ls -lh hello

Static build (links in libc and friends, producing a much larger binary):

$ gcc -static hello.c -o hello
$ ls -lh hello

The statically linked executable will be significantly larger because library code is embedded into the binary.

Creating static and shared libraries with GCC

Source files

ops.h:

#ifndef OPS_H
#define OPS_H

void set_base(int value);
int  add_to(int value);

#endif

ops.c:

#include <stdio.h>
#include "ops.h"

static int g_base;

void set_base(int value) {
    g_base = value;
}

int add_to(int value) {
    return g_base + value;
}

__attribute__((constructor)) static void lib_init(void) {
    printf("libcalcutil: init\n");
    g_base = 0;
}

__attribute__((destructor)) static void lib_fini(void) {
    printf("libcalcutil: fini\n");
}

solver.h:

#ifndef SOLVER_H
#define SOLVER_H

int ultimate_answer(void);

#endif

solver.c:

#include "ops.h"
#include "solver.h"

int ultimate_answer(void) {
    set_base(20);
    return add_to(22); // 42
}

main.c:

#include <stdio.h>
#include "ops.h"
#include "solver.h"

int main(void) {
    set_base(5);
    printf("5 + 7 = %d\n", add_to(7));
    printf("And the answer is: %d\n", ultimate_answer());
    return 0;
}

Directory layout

$ mkdir -p build/static build/shared
$ tree -L 2
.
├── build
│   ├── shared
│   └── static
├── main.c
├── ops.c
├── ops.h
├── solver.c
└── solver.h

Compile object files

Common object for the main program:

$ gcc -c main.c -o build/main.o

Static library objects:

$ gcc -c ops.c    -o build/static/ops.o
$ gcc -c solver.c -o build/static/solver.o

Shared library objects (position-independent):

$ gcc -fPIC -c ops.c    -o build/shared/ops.o
$ gcc -fPIC -c solver.c -o build/shared/solver.o

Build a static library and link

Create the archive:

$ ar rcs build/static/libcalcutil.a build/static/ops.o build/static/solver.o

Link statically againsst the archive:

$ gcc build/main.o -Lbuild/static -lcalcutil -o build/app-static

Run:

$ ./build/app-static
libcalcutil: init
5 + 7 = 12
And the answer is: 42
libcalcutil: fini

Build a shared library and link

Create the shared object:

$ gcc -shared build/shared/ops.o build/shared/solver.o -o build/shared/libcalcutil.so

Link an executable that uses the shared object:

$ gcc build/main.o -Lbuild/shared -lcalcutil -o build/app-shared

Run (initial attempt may fail if the loader cannot locate the .so):

$ ./build/app-shared
./build/app-shared: error while loading shared libraries: libcalcutil.so: cannot open shared object file: No such file or directory

Point the dynamic loader to the directory containing the library for this shell session:

$ export LD_LIBRARY_PATH="$(pwd)/build/shared:${LD_LIBRARY_PATH}"
$ ./build/app-shared
libcalcutil: init
5 + 7 = 12
And the answer is: 42
libcalcutil: fini

Alternaitvely, place the shared library in a standard search path (system-wide change may require root):

$ sudo mv build/shared/libcalcutil.so /usr/lib/
$ sudo chmod 755 /usr/lib/libcalcutil.so
# Optionally: sudo ldconfig
Tags: GCCLinuxc

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.