Menyatukan assembly dengan ekosistem C: linking lewat gcc, memanggil printf dan fungsi glibc dari assembly dengan aturan alignment 16-byte, memahami symbol resolution, GOT/PLT, dan position-independent code, serta membangun shared library .so dari kode assembly.

Sejauh ini program assembly kita berjalan tanpa libc — murni syscall. Episode 19 membalik arah: assembly memanggil libc, dan C memanggil assembly, dalam satu binary yang di-link oleh gcc. Inilah bentuk interop yang paling umum di dunia nyata: kode assembly untuk hot path, C/libc untuk segala yang lain.
Mengapa penting? Karena menulis semua string formatting, file I/O, dan networking dengan syscall mentah (seperti episode 10) adalah kerja sia-sia. Dan memahami GOT/PLT — mekanisme di balik pemanggilan fungsi dari shared library — adalah jembatan menuju topik kernel di episode 21 dan analisis binary di episode 16.
Perbedaan kunci dengan program syscall murni: program ini di-link dengan libc, dan _start-nya bukan lagi milik kita (libc menyediakan _start yang memanggil main). Ubah nama entry point:
section .data
fmt db "Nilai: %d", 0x0a, 0
section .text
global main
extern printf
main:
push rbp
mov rbp, rsp
mov rsi, 42 ; argumen ke-2 printf: nilai integer
lea rdi, [rel fmt] ; argumen ke-1 printf: format string
call printf
pop rbp
mov eax, 0 ; return 0 (int main)
retnasm -f elf64 print_printf.asm -o print_printf.o
gcc -no-pie print_printf.o -o app
./appOutput: Nilai: 42.
Important
Aturan alignment 16-byte (episode 9) wajib dipatuhi. Saat main masuk, rsp % 16 == 8. Prologue push rbp membuat rsp % 16 == 0 — persis kondisi yang diharapkan sebelum call printf. Jika kalian melewati prologue (fungsi leaf tanpa push), RSP tetap 8 mod 16 saat call → glibc crash SIGSEGV. Ini bug paling umum pada kode assembly yang memanggil libc.
lea rdi, [rel fmt]?Di episode 3 kita menulis mov rsi, msg (alamat absolut). Dengan gcc -no-pie itu bekerja. Tetapi distro modern default memakai PIE (episode 17), di mana alamat binary di-randomisasi. Untuk kode position-independent, ambil alamat relatif terhadap RIP:
lea rdi, [rel fmt] ; relatif ke RIP → aman untuk PIEDan link tanpa -no-pie:
gcc print_printf.o -o app
./appIni menandai kode assembly kalian siap untuk binary modern yang ber-ASLR penuh.
Saat call printf dieksekusi, ke mana tepatnya ia melompat? Karena printf tinggal di libc (shared library yang dimuat di alamat acak), alamatnya belum diketahui saat link. Mekanisme penyelesaiannya berlapis:
jmp ke GOT.printf yang sebenarnya.Untuk kalian yang bekerja dengan assembly, yang perlu dipahami: call printf melalui PLT/GOT adalah indirection — biaya kecil demi shared library yang fleksibel. objdump -d akan menampilkan call 0x401030 <printf@plt> — alamat PLT, bukan libc.
Kita sudah melihat satu arah (episode 9). Mari lengkapi dengan pembuatan shared library — bentuk interop paling reusable:
section .text
global square
; long square(long x)
square:
mov rax, rdi
imul rax, rax
ret#include <stdio.h>
extern long square(long x);
int main(void) {
printf("%ld\n", square(9));
return 0;
}nasm -f elf64 mylib.asm -o mylib.o
gcc -shared -o libmylib.so mylib.o
gcc main.c -L. -lmylib -o app
LD_LIBRARY_PATH=. ./appOutput: 81. Perhatikan -shared menghasilkan .so, dan -lmylib menghubungkan -L. (direktori ini) ke libmylib.so.
Saat membuat .so, kode di dalamnya harus position-independent karena akan dimuat di alamat acak:
readelf -r libmylib.so
objdump -d libmylib.so | grep -A3 squareKonsekuensi praktis untuk penulis assembly:
lea rdi, [rel symbol] untuk mengakses data/string.GOT jika perlu simbol eksternal (akses data global libc biasanya lewat GOT).rel dan, bila perlu, wrt ..got:extern some_global
mov rax, [rel some_global wrt ..got]Tip
Trik debugging: jalankan readelf -d libmylib.so → perhatikan TEXTREL (teks yang mengandung relokasi — tandanya kode tidak benar-benar PIC) dan NEEDED (dependensi). Binary modern yang sehat seharusnya tanpa TEXTREL, karena mencegah berbagi halaman kode antar proses.
| Situasi | Pilihan |
|---|---|
| Hot path numerik di aplikasi C | Fungsi assembly dipanggil C, di-link statis |
| Library yang dipakai banyak program | Shared library .so dari assembly |
| Program yang butuh bootstrap sebelum libc (bootloader, kernel) | Murni syscall, tanpa libc (episode 3/10/21) |
| Mengambil alih rutin perf-kritis libc | Override simbol (LD_PRELOAD) |
call libc — crash SIGSEGV; jaga aturan 16-byte.lea ... [rel ...].extern di NASM — linker error unresolved symbol.main vs _start — dengan libc gunakan main; _start dipakai hanya tanpa libc.syscall exit dalam program yang memakai libc; biarkan main ret (libc yang memanggil exit, memastikan flush buffer stdio).Pada episode 19 ini, kalian telah menyatukan assembly dengan ekosistem C.
Inti yang harus dibawa pulang:
main + link gcc untuk memakai libc; jaga alignment 16-byte sebelum call.lea ... [rel ...] untuk alamat yang aman di kode PIE..so dari assembly: gcc -shared; gunakan rel untuk PIC.Di episode 20 selanjutnya, kalian diperbarui ke toolchain terbaru: NASM 3.02 (rilis 29 Juni 2026) dengan dukungan instruksi modern APX/AVX10 eksperimental, assembler terintegrasi LLVM/Clang dan llvm-objdump, serta Godbolt (Compiler Explorer) untuk melihat langsung bagaimana compiler menghasilkan assembly. Saatnya memakai semua skill membaca assembly kalian untuk "membaca pikiran" compiler.