Static library adalah kumpulan object file yang dibungkus dalam satu file archive berformat .a. Saat linker menemukan referensi ke simbol di static library, linker menyalin object file yang relevan dari archive ke dalam executable final. Hasilnya: executable mandiri yang tidak lagi membutuhkan file .a saat dijalankan. Pada chapter ini kita akan membuat static library sederhana dengan ar rcs, memeriksa isinya, dan menggunakannya dari program utama dengan dua gaya command line GCC.

Membuat Static Library #

Kode library kita terdiri dari header mylib.h dan implementasi mylib.c:

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;
}

Langkah pertama adalah mengompilasi mylib.c menjadi object file dengan flag -c. Selanjutnya, kita membungkus object file tersebut menjadi archive .a dengan ar rcs:

bash
gcc -std=c11 -Wall -Wextra -c mylib.c -o mylib.o
ar rcs libmylib.a mylib.o
ls -l libmylib.a

Output yang Dihasilkan:

text
-rw-rw-r-- 1 z z 1362 libmylib.a

Mari kita telaah perintah ar rcs libmylib.a mylib.o:

  1. ar: archiver dari GNU Binutils — program yang membuat dan memodifikasi archive .a.
  2. r (replace): bila mylib.o sudah ada di archive, ganti; bila belum ada, tambahkan. Flag r membuat perintah idempoten — aman dipanggil berulang.
  3. c (create): bila archive libmylib.a belum ada, buat archive baru tanpa menampilkan pesan peringatan. Tanpa c, ar akan mengeluh bila file tidak ada.
  4. s (symbol index): tulis indeks simbol di awal archive. Indeks ini yang dibaca linker untuk cepat menemukan object file mana di archive yang berisi simbol tertentu. Tanpa s, linker tidak bisa memakai archive sebagai library (archive menjadi kumpulan object file biasa, bukan library).

Memeriksa Isi Archive #

Perintah ar -t menampilkan daftar member di dalam archive:

bash
ar -t libmylib.a

Output yang Dihasilkan:

text
mylib.o

Perintah nm juga menerima file archive dan menampilkan symbol table tiap member:

bash
nm libmylib.a

Output yang Dihasilkan:

text

mylib.o:
0000000000000014 T kurang
0000000000000000 T tambah

Header kosong diikuti nama member (mylib.o:) menandakan bahwa nm menampilkan simbol per member. Simbol tambah dan kurang muncul dengan huruf T (global, section .text) sehingga bisa dirujuk dari file lain.

Menggunakan Static Library dari Program Utama #

Program utama main.c meng-include header library dan memanggil fungsinya:

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;
}

Cara pertama untuk men-link library adalah menyebut path file .a secara langsung:

bash
gcc -std=c11 -Wall -Wextra main.c libmylib.a -o app
./app

Output yang Dihasilkan:

text
5 + 3 = 8
10 - 4 = 6

Cara kedua memakai konvensi -L<path> -l<name>, yang lebih lazim di Makefile dan CMakeLists.txt:

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

Output yang Dihasilkan:

text
5 + 3 = 8
10 - 4 = 6

Mari kita telaah flag yang dipakai di command line kedua:

  1. -L.: tambahkan direktori . (direktori saat ini) ke daftar pencarian library. Tanpa flag ini, GCC hanya mencari library di /usr/lib, /usr/lib/x86_64-linux-gnu, dan direktori bawaan lainnya — bukan di direktori kerja kita.
  2. -lmylib: link dengan library bernama libmylib. GCC menerjemahkan mylib menjadi libmylib.a (atau libmylib.so) dan mencari di daftar direktori -L. Konvensi lib<name>.a menyembunyikan prefix lib dan ekstensi .a dari command line, membuat script build lebih bersih.
  3. Urutan main.c ... -lmylib: GCC memproses argumen dari kiri ke kanan. main.c dibaca terlebih dahulu — linker mencatat bahwa main.c merujuk tambah dan kurang (undefined). Baru kemudian -lmylib diproses — linker mencari di archive dan menemukan definisi tambah serta kurang. Bila urutan dibalik (-lmylib main.c), linker akan memproses library lebih dulu, menemukan tidak ada referensi yang perlu diresolve, lalu membuang object file dari archive — akibatnya undefined reference.

Static Library dengan Banyak Modul #

Library nyata sering berisi banyak object file. Kita bisa menambahkan modul baru dengan ar rcs:

bash
# Update mylib.h dengan deklarasi baru
cat > mylib.h <<'EOF'
#ifndef MYLIB_H
#define MYLIB_H

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

#endif
EOF

# Implementasi fungsi baru di file terpisah
cat > mylib2.c <<'EOF'
#include "mylib.h"

int kali(int a, int b) {
    return a * b;
}
EOF

gcc -std=c11 -Wall -Wextra -c mylib2.c -o mylib2.o
ar rcs libmylib.a mylib.o mylib2.o
ar -t libmylib.a

Output yang Dihasilkan:

text
mylib.o
mylib2.o

Sekarang archive berisi dua member. Karena flag s dipakai, indeks simbol sudah diperbarui otomatis untuk mencakup simbol di mylib2.o. Linker dapat menemukan tambah di mylib.o dan kali di mylib2.o tanpa campur tangan manual.

Karakteristik Static Library #

Ciri khas static library adalah kode library menjadi bagian dari executable. Mari kita verifikasi dengan ldd dan nm:

bash
ldd app

Output yang Dihasilkan:

text
	linux-vdso.so.1 (0x00007ffecbb7e000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc070ef1000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fc0710fb000)

Tidak ada dependensi ke libmylib.a — kode tambah dan kurang sudah disalin ke executable. Verifikasi dengan nm:

bash
nm app | grep -E "tambah|kurang"

Output yang Dihasilkan:

text
00000000000011a2 T kurang
000000000000118e T tambah

Simbol tambah dan kurang sekarang ada di section .text executable app dengan alamat absolut (0x118e dan 0x11a2). Inilah inti static linking: linker menyalin object file yang relevan dari archive ke executable, dan executable bisa didistribusikan tanpa file .a di sistem target.