Program yang memakai pustaka eksternal — zlib untuk kompresi, OpenSSL untuk kriptografi — menuntut dua informasi saat dibangun: di mana file header (zlib.h) berada, dan pustaka mana yang harus ditautkan linker. Menuliskan flag -I/usr/include/... dan -lz secara hafalan berhasil di satu mesin lalu gagal di mesin lain, karena tiap distribusi menaruh pustaka di lokasi berbeda. Alat pkg-config menghapus tebak-tebakan itu: satu kueri, semua flag siap tempel. Alat ini terpasang di sistem latihan, jadi seluruh contoh berikut dijalankan sungguhan dengan pustaka zlib yang benar-benar ada.
Masalah Klasik Linking #
Kita mulai dari program kecil yang mengompresi 256 byte data memakai zlib, lalu sengaja membangunnya tanpa memberi tahu compiler soal pustakanya:
cat > salin.c << 'AKHIR'
#include <stdio.h>
#include <string.h>
#include <zlib.h>
int main(void)
{
char asal[256];
memset(asal, 'a', sizeof asal);
unsigned char hasil[256];
uLongf panjang = sizeof hasil;
if (compress(hasil, &panjang, (Bytef *)asal, sizeof asal) != Z_OK)
return 1;
printf("asli: %zu byte, terkompresi: %lu byte\n",
sizeof asal, (unsigned long)panjang);
return 0;
}
AKHIR
gcc -c salin.c -o salin.o && echo "kompilasi object: OK"
gcc -o salin salin.o 2>&1Output yang Dihasilkan:
kompilasi object: OK
/usr/bin/ld: salin.o: in function `main':
salin.c:(.text+0x4d): undefined reference to `compress'
collect2: error: ld returned 1 exit statusKegagalan ini memisahkan dua tahap yang berbeda. Baris gcc -c sukses karena stdio.h maupun zlib.h ditemukan di path default /usr/include — tahap kompilasi hanya butuh deklarasi. Yang gagal adalah tahap linking: object file salin.o memanggil fungsi compress yang dideklarasikan zlib, tetapi tidak ada implementasinya di antara file yang diserahkan ke linker. Pesan undefined reference to 'compress' adalah cara linker berkata: “kamu memanggil fungsi ini, tapi aku tidak tahu di mana tubuhnya”. Implementasi itu ada di /usr/lib/x86_64-linux-gnu/libz.so — kita hanya belum memberi flag -lz agar linker mencarinya.
pkg-config: Penyedia Flag yang Pasti #
Daripada menghafal lokasi tiap pustaka, kita tanyakan langsung ke pkg-config:
pkg-config --modversion zlib
pkg-config --cflags --libs zlib
pkg-config --exists zlib && echo "zlib dikenal pkg-config"Output yang Dihasilkan:
1.3.1
-lz
zlib dikenal pkg-configTiga kueri itu adalah seluruh API yang kita butuhkan sehari-hari:
--modversion zlib: versi pustaka yang ditemukan — berguna saat program menuntut zlib minimal versi tertentu.--cflags --libs zlib: flag siap tempel untuk gcc; hasilnya hanya-lzkarena header zlib memang berada di path default, sehingga bagian-Ikosong.--exists zlib: uji keberadaan tanpa mencetak apa pun — inilah yang dipakai scriptconfigureuntuk memeriksa dependensi sebelum menulis Makefile.
Kompilasi dengan Flag dari pkg-config #
Output pkg-config dirancang untuk dimakan substitusi perintah — pola $(pkg-config ...) menempelkan flag tepat di tempatnya:
cat > salin.c << 'AKHIR'
#include <stdio.h>
#include <string.h>
#include <zlib.h>
int main(void)
{
char asal[256];
memset(asal, 'a', sizeof asal);
unsigned char hasil[256];
uLongf panjang = sizeof hasil;
if (compress(hasil, &panjang, (Bytef *)asal, sizeof asal) != Z_OK)
return 1;
printf("asli: %zu byte, terkompresi: %lu byte\n",
sizeof asal, (unsigned long)panjang);
return 0;
}
AKHIR
gcc -c salin.c -o salin.o
gcc -o salin salin.o $(pkg-config --libs zlib)
./salin
objdump -p salin | grep NEEDEDOutput yang Dihasilkan:
asli: 256 byte, terkompresi: 12 byte
NEEDED libz.so.1
NEEDED libc.so.6Alur bangun-run-periksa itu berjalan mulus: kompresi memangkas 256 byte berisi huruf a berulang menjadi 12 byte, dan objdump -p membedah isi binary untuk menampilkan entri NEEDED — daftar soname pustaka yang dicatat linker. Dari sini terlihat bahwa dependensi zlib ikut tertanam di dalam binary; menyertakan $(shell pkg-config --cflags --libs zlib) pada variabel CFLAGS/LDLIBS di Makefile — pola yang kita bangun di chapter Make dan Build Automation
— membuat proyek multi-file memakai flag yang sama otomatis.
Anatomi File .pc dan PKG_CONFIG_PATH #
Dari mana pkg-config tahu semua itu? Dari file metadata berekstensi .pc yang dipasang tiap pustaka ke /usr/lib/<arsitektur>/pkgconfig/. File itu teks biasa — kita bisa membuatnya sendiri untuk SDK lokal, lalu mengarahkan PKG_CONFIG_PATH ke foldernya:
mkdir -p sdk
cat > sdk/teman.pc << 'AKHIR'
prefix=/opt/sdk-teman
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: teman
Description: SDK fiktif untuk latihan pkg-config
Version: 1.2.3
Cflags: -I${includedir}
Libs: -L${libdir} -lteman
AKHIR
PKG_CONFIG_PATH=sdk pkg-config --modversion teman
PKG_CONFIG_PATH=sdk pkg-config --cflags --libs temanOutput yang Dihasilkan:
1.2.3
-I/opt/sdk-teman/include -L/opt/sdk-teman/lib -lteman Bedah isi file teman.pc:
prefixsampaiincludedir: variabel internal yang menunjuk lokasi pemasangan SDK;${prefix}di baris lain merujuk nilainya, persis variabel Makefile.Name,Description,Version: metadata untuk manusia dan untuk--modversion.Cflags: flag untuk tahap kompilasi —-Imenambah lokasi header.Libs: flag untuk tahap linking —-Lmenambah lokasi pustaka,-lmenautkanlibteman.PKG_CONFIG_PATH=sdk: variabel lingkungan perintah-perintah yang memperluas lokasi pencarian.pc— tanpa itu,temantidak akan ditemukan karenapkg-confighanya melihat direktori sistem.
Pola ini menjelaskan kenapa proyek dengan dependensi lokal cukup menyimpan SDK di folder proyek: .pc-nya ditemukan lewat PKG_CONFIG_PATH, dan seluruh flag konsisten di mana pun proyek dibangun.
Sisi Runtime: ld.so, ldconfig, dan rpath #
pkg-config menyelesaikan separuh cerita — sisa pekerjaan terjadi setiap kali program dijalankan. Loader dinamis ld.so membaca entri NEEDED binary lalu mencari libz.so.1 di beberapa tempat berurutan: path tertanam rpath di binary, variabel LD_LIBRARY_PATH, lalu cache /etc/ld.so.cache. Alat ldd memperlihatkan hasil resolusinya; sebagai contoh tampilan, inilah wujud tipikal ldd salin di mesin 64-bit:
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f8e2a3d9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8e2a200000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8e2a400000)Angka heksadesimal di ujung adalah alamat pemuatan yang berubah tiap eksekusi — yang stabil adalah pemetaan nama ke path. Cache yang dicari ld.so dibangun oleh ldconfig dari /lib, /usr/lib, dan direktori tambahan di /etc/ld.so.conf.d/; setelah memasang pustaka baru ke lokasi sistem, sudo ldconfig menyegarkannya. Urutan pencarian dan opsi-opsinya dirinci di ld.so(8)
dan ldconfig(8)
.
Untuk aplikasi yang membawa pustakanya sendiri — misalnya dipasang di /opt/aplikasi/lib — urutan pertama tadi menjadi solusi: flag -Wl,-rpath,/opt/aplikasi/lib saat linking menanam path itu ke binary, sehingga program berjalan tanpa LD_LIBRARY_PATH dan tanpa menyentuh direktori sistem. Paksaan terakhir seperti ini jarang diperlukan; pola bersihnya tetap pustaka dari package manager, pkg-config sebagai juru bicara flag, dan cache ldconfig sebagai peta runtime.
Dengan flag yang benar, program akhirnya terbangun dan berjalan — namun berjalan bukan berarti benar. Ketika hasilnya salah atau program tiba-tiba mati, kita pindah ke ranah yang berbeda: mengintip isi proses yang sedang berjalan. Chapter berikutnya tentang gdb, strace, dan ltrace dimulai dari situ.