Dua chapter terakhir berdiri di sisi yang sama: program kita dinamis, sehingga binernya ramping, tetapi selalu menuntut library dari luar — dan seluruh teknik rpath, LD_LIBRARY_PATH, dan ldconfig di chapter lalu ada untuk memenuhi tuntutan itu. Ada satu alternatif radikal: saat linking, kita salin seluruh kode library ke dalam badan biner sehingga program berdiri sendiri tanpa pihak luar. Dua strategi ini — static linking dan dynamic linking — bukan soal benar atau salah, melainkan soal menukar keuntungan: ukuran, kemudahan pembaruan keamanan, kecepatan startup, dan bentuk distribusi. Chapter ini membangun keduanya dari source yang sama, mengukurnya secara nyata, lalu merumuskan kapan masing-masing pilihan yang tepat.

Membangun Biner Dinamis dan Statis #

Bahan ujinya utilitas kecil konversi-waktu.cpp — cukup memakai iostream sehingga menarik libstdc++ dan glibc, cukup kecil agar angka pengukurannya mudah dibaca:

cpp
#include <iomanip>
#include <iostream>

// Utilitas kecil untuk uji linking: mengubah durasi detik menjadi
// format jam:menit:detik untuk beberapa nilai tetap.
void cetakDurasi(int totalDetik) {
    int jam    = totalDetik / 3600;
    int menit  = (totalDetik % 3600) / 60;
    int detik  = totalDetik % 60;
    std::cout << std::setw(6) << totalDetik << " detik  =  "
              << jam << ':'
              << std::setw(2) << std::setfill('0') << menit << ':'
              << std::setw(2) << detik
              << std::setfill(' ') << '\n';
}

int main() {
    std::cout << "Konversi durasi\n";
    cetakDurasi(59);
    cetakDurasi(3661);
    cetakDurasi(86399);
    return 0;
}

Output yang Dihasilkan:

text
Konversi durasi
    59 detik  =  0:00:59
  3661 detik  =  1:01:01
 86399 detik  =  23:59:59

Bedah singkat: Baris 9-11 memecah durasi dengan pembagian dan sisa pembagian integer — matematika murni yang identik di semua konfigurasi build. Baris 13-14 memakai std::setfill('0') agar menit dan detik selalu dua digit, lalu mengembalikan karakter pengisi ke spasi supaya baris berikutnya tidak ikut terpengaruh. Kini build dua varian dari source yang sama dan bandingkan ukurannya:

bash
g++ -std=c++23 -Wall -Wextra -O2 konversi-waktu.cpp -o konversi-waktu
g++ -std=c++23 -Wall -Wextra -O2 -static konversi-waktu.cpp -o konversi-waktu-static
ls -l konversi-waktu konversi-waktu-static | awk '{print $5, $9}'

Output yang Dihasilkan:

text
16648 konversi-waktu
2272688 konversi-waktu-static

Flag -static memerintahkan linker menyalin isi versi statis dari setiap library yang dibutuhkan — libc.a, libstdc++.a, dan kawan-kawannya — ke dalam biner. Hasilnya 2.272.688 byte melawan 16.648 byte: sekitar 136 kali lebih besar, karena seluruh mesin iostream, allocator, dan dukungan locale ikut dikemas. Angka pastinya berubah menurut versi compiler, tetapi ordo besarnya tidak: biner statis C++ umumnya berukuran mega byte meski programnya sepele. Uji fungsionalnya tetap sama persis — kedua varian mencetak output identik di atas, karena linking tidak mengubah semantik program.

Mengenali Biner Statis #

Perubahan paling penting tidak terlihat dari ukuran, melainkan dari header ELF:

bash
file konversi-waktu-static
ldd konversi-waktu-static
readelf -l konversi-waktu-static | grep -ci interpreter

Output yang Dihasilkan:

text
konversi-waktu-static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=d92337141159d02d553c89ab67bf887e6051d232, for GNU/Linux 3.2.0, not stripped
not a dynamic executable
0

Tiga bukti dalam satu jalan: file melaporkan statically linked dan tidak lagi menyebut interpreter; ldd menjawab not a dynamic executable — pesan itu keluar lewat stderr dengan exit code 1, wajar karena biner ini memang tidak punya daftar dependensi untuk ditelusuri; dan hitungan interpreter pada program header bernilai 0, artinya entri PT_INTERP hilang. Konsekuensinya menggantikan seluruh mekanisme chapter lama: kernel langsung mengeksekusi biner tanpa melibatkan dynamic linker, sehingga tidak ada pencarian library, tidak ada cache ld.so.cache, dan tidak ada libkalkulator.so yang bisa tertinggal.

AspekDynamic linkingStatic linking
Ukuran binerRamping; kode library dipakai bersamaMega byte; semua dikemas
Pembaruan keamanan glibcIkut pembaruan sistem, tanpa kompilasi ulangTerkunci di biner; setiap patch menuntut rebuild dan redistribusi
Bentuk distribusiBanyak berkas; butuh rpath/paketSatu berkas mandiri
StartupSedikit lebih lambat: loader, relokasi, simbolLangsung tereksekusi
Konsumsi memoriSatu salinan .so dibagi ke semua prosesSetiap proses membawa salinannya sendiri
Ekosistem pluginAlami: dlopen memuat .so saat runtimeTidak bisa memuat kode eksternal dengan cara itu

Dua baris tabel itu layak didalam. Pembaruan keamanan adalah argumen terkuat sisi dinamis: ketika CVE glibc ditemukan dan distro merilis patch, semua program dinamis sembuh segera setelah apt upgrade — sementara setiap biner statis menyalin versi glibc saat dibangun dan hanya berganti setelah dibangun ulang. Sebaliknya, pada biner dinamis versi library di mesin tujuan adalah faktor di luar kendali kita; itulah sumber kelas bug “jalan di mesin saya” yang dibahas chapter pertama, dan biner statis menghapus kelas itu sekaligus dengan membawa dunianya sendiri.

Kapan Memilih yang Mana #

  • Alat CLI mandiri bergaya “satu berkas”: statis — unduh, chmod +x, jalan; tanpa tanya dependensi.
  • Image container minimal (basis kosong): statis — biner jalan di image tanpa libc sama sekali.
  • Paket distro resmi (.deb/.rpm): dinamis — biarkan sistem mengelola library dan patch keamanannya.
  • Aplikasi server berumur panjang di infrastruktur terkelola: dinamis — tim keamanan Anda berterima kasih saat glibc di-patch.
  • Arsitektur plugin (editor, DAW, game engine): dinamis — statis justru menutup pintu dlopen.
  • Library pihak ketiga berlisensi LGPL: catat lisensinya; LGPL mengizinkan penautan statis dengan kewajiban memberi cara bagi penerima untuk menautkan ulang versi library yang diperbarui.

Statis atau dinamis, biner yang dikirim ke pengguna tidak boleh memikul beban yang tidak perlu: tabel simbol, informasi debug, dan pemeriksaan assert pengembangan semua itu layak dibersihkan sebelum rilis. Chapter berikutnya menyusun checklist release build yang melakukan persis pembersihan itu.

Referensi: Link Options — GCC Online Docs .