Shared library (file .so di Linux, .dylib di macOS, .dll di Windows) adalah kode library yang dimuat ke memori hanya sekali oleh OS dan dipakai bersama oleh banyak executable. Berbeda dengan static library yang menyalin kode ke setiap executable, shared library tetap terpisah dari executable dan dimuat saat program dimulai (atau bahkan saat fungsi pertama dipanggil). Pada chapter ini kita akan membuat shared library dengan flag -fPIC dan -shared, menggunakannya dari program utama, serta memahami peran LD_LIBRARY_PATH dan -Wl,-rpath.

Apa itu Position-Independent Code #

Shared library dimuat OS ke alamat memori yang tidak diketahui saat kompilasi. Karena itu, semua kode di shared library harus position-independent — tidak boleh mengandung alamat absolut. Flag -fPIC menyuruh GCC menghasilkan kode yang hanya memakai alamat relatif terhadap instruction pointer (RIP-relative addressing di x86-64).

Library kode kita tetap sama dengan chapter sebelumnya — perbedaannya hanya pada flag kompilasi:

c
/* mylib.h */
#ifndef MYLIB_H
#define MYLIB_H

int tambah(int a, int b);
int kurang(int a, int b);

#endif
c
/* mylib.c */
#include "mylib.h"

int tambah(int a, int b) {
    return a + b;
}

int kurang(int a, int b) {
    return a - b;
}

Membuat Shared Library #

Langkah pertama adalah mengompilasi mylib.c dengan flag -fPIC, lalu men-link dengan flag -shared:

bash
gcc -std=c11 -Wall -Wextra -fPIC -c mylib.c -o mylib.o
gcc -shared -o libmylib.so mylib.o
file libmylib.so

Output yang Dihasilkan:

text
libmylib.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=5babb0c350b6472ba93fb262baa2cb709c0f91a1, not stripped

Kata kunci shared object menandakan file ini adalah shared library. Bandingkan dengan object file biasa yang berlabel relocatable, atau executable yang berlabel executable.

Sebagai alternatif, kita bisa menyatukan kedua langkah di atas menjadi satu perintah:

bash
gcc -std=c11 -Wall -Wextra -fPIC -shared -o libmylib.so mylib.c

Mari kita telaah flag-flag yang dipakai di atas:

  1. -fPIC (Position-Independent Code): menyuruh GCC menghasilkan kode mesin yang memakai alamat relatif terhadap instruction pointer. Tanpa flag ini, kode di shared library akan berisi alamat absolut yang salah saat library dimuat ke alamat acak oleh OS.
  2. -shared: menyuruh linker menghasilkan shared library alih-alih executable. Linker tidak menyertakan C runtime (crt0.o, crti.o) karena library tidak punya entry point _start.
  3. -o libmylib.so: nama file output. Konvensi Linux: shared library berawalan lib dan berakhiran .so (shared object).

Menggunakan Shared Library dari Program #

Program utama sama seperti chapter Static Library:

c
#include <stdio.h>
#include "mylib.h"

int main(void) {
    printf("5 + 3 = %d\n", tambah(5, 3));
    printf("10 - 4 = %d\n", kurang(10, 4));
    return 0;
}

Kompilasi dan link dengan flag -L. -lmylib — GCC akan mencari libmylib.so di direktori saat ini:

bash
gcc -std=c11 -Wall -Wextra -L. main.c -lmylib -o app

Saat kita mencoba menjalankan app tanpa pengaturan tambahan, runtime dynamic linker tidak bisa menemukan libmylib.so:

bash
./app

Output yang Dihasilkan:

text
./app: error while loading shared libraries: libmylib.so: cannot open shared object file: No such file or directory

Error ini muncul karena runtime dynamic linker (ld-linux-x86-64.so.2) mencari shared library hanya di path standar (/lib, /usr/lib, dll) — bukan direktori kerja saat ini. Ada dua solusi umum.

Solusi 1: Variabel LD_LIBRARY_PATH #

Cara cepat: set variabel environment LD_LIBRARY_PATH agar memuat direktori library:

bash
LD_LIBRARY_PATH=. ./app

Output yang Dihasilkan:

text
5 + 3 = 8
10 - 4 = 6

Verifikasi dengan ldd:

bash
LD_LIBRARY_PATH=. ldd app

Output yang Dihasilkan:

text
	linux-vdso.so.1 (0x00007ffe95fdd000)
	libmylib.so => ./libmylib.so (0x00007f2208b19000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2208916000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f2208b25000)

Baris libmylib.so => ./libmylib.so menandakan linker runtime menemukan library di direktori saat ini. Pendekatan LD_LIBRARY_PATH cocok untuk pengembangan, tetapi tidak disarankan untuk produksi karena variabel ini mempengaruhi seluruh proses yang dijalankan user.

Solusi 2: RPATH dengan -Wl,-rpath #

Cara lebih permanen: tanam path library ke executable dengan flag -Wl,-rpath. Flag -Wl, meneruskan argumen berikutnya ke linker ld (bukan ke GCC):

bash
gcc -std=c11 -Wall -Wextra -L. -Wl,-rpath=. main.c -lmylib -o app_rpath
./app_rpath

Output yang Dihasilkan:

text
5 + 3 = 8
10 - 4 = 6

Sekarang app_rpath bisa dijalankan tanpa LD_LIBRARY_PATH karena path . sudah tertanam di section .dynamic executable. Perintah ldd memperlihatkan ini:

bash
ldd app_rpath

Output yang Dihasilkan:

text
	linux-vdso.so.1 (0x00007fff54378000)
	libmylib.so => ./libmylib.so (0x00007f1ffce11000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1ffcc0e000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f1ffce1d000)

Memeriksa Simbol Ekspor Shared Library #

Perintah nm -D menampilkan dynamic symbol table shared library — daftar simbol yang diekspor untuk dipakai executable lain:

bash
nm -D libmylib.so | grep -E "tambah|kurang"

Output yang Dihasilkan:

text
000000000000110d T kurang
00000000000010f9 T tambah

Flag -D penting karena shared library punya dua symbol table: satu internal (static, seperti object file biasa) dan satu dynamic (yang dilihat runtime linker). Tanpa -D, nm menampilkan symbol table internal yang berisi lebih banyak simbol lokal — tidak semua diekspor.

Perbandingan dengan Static Library #

AspekStatic Library (.a)Shared Library (.so)
FormatArchive berisi object fileELF shared object
Ukuran executableBesar (kode library tertanam)Kecil (hanya referensi)
Dependency runtimeTidak adaMembutuhkan file .so saat dijalankan
Update libraryPerlu re-link executableCukup ganti file .so
Memory usageSetiap proses punya copy kodeSatu copy di memori untuk semua proses
Compile flag-c lalu ar rcs-fPIC -c lalu -shared
Link flag-L. -l<name>-L. -l<name> -Wl,-rpath=...
DistribusiSelf-containedPerlu disertakan file .so