Label pada assembly diperlakukan sebagai symbol, yaitu nama yang dipasangkan dengan alamat, dan seluruh simbol dikumpulkan dalam symbol table atau tabel simbol setiap berkas objek. Topik ini membahas perbedaan simbol lokal dan global, pembacaan tabel simbol dengan nm, serta relocation, yaitu catatan pada byte yang alamatnya belum dapat diisi assembler dan menunggu penyelesaian oleh linker, program yang menyatukan berkas objek menjadi executable. Seluruh proses diamati pada contoh dua berkas yang ditautkan menjadi satu program, termasuk error yang muncul bila satu simbol tidak tersedia.
Berkas contoh dua modul#
Contoh pada topik ini terdiri atas dua berkas. main.asm berperan sebagai pemanggil, sedangkan aritmetika.asm menyediakan fungsi penjumlahan yang digunakan bersama.
default rel
section .data
angka: dq 24 ; simbol lokal, hanya dikenal di berkas ini
tujuan: dq tambah ; alamat fungsi sebagai data, menunggu relokasi absolut
section .bss
sementara: resb 8
section .text
global _start
extern tambah
_start:
mov rdi, [angka]
mov rsi, 18
call tambah
mov [sementara], rax
mov edi, eax
mov eax, 60 ; syscall exit
syscall ; kode keluar 42default rel
section .text
global tambah
tambah:
mov rax, rdi
add rax, rsi
retBaris extern tambah memberi tahu assembler bahwa nama tambah didefinisikan di berkas lain, sehingga assembler membiarkan byte alamatnya kosong. Baris tujuan: dq tambah menaruh alamat fungsi tersebut sebagai data 64-bit, sebuah pola yang lazim digunakan untuk tabel callback, yaitu daftar alamat fungsi yang dipanggil kemudian. Pemisahan dua berkas ini meniru pembagian kerja yang lazim pada proyek nyata, yaitu satu berkas berisi titik masuk program dan berkas lain berisi rutin yang dapat digunakan kembali. Assembler mengerjakan kedua berkas secara terpisah tanpa membuka isi satu sama lain, sehingga pengetahuan tentang alamat akhir baru muncul pada tahap penautan. Berkas kedua mendefinisikan tambah lalu mengekspornya lewat global tambah. Tanpa baris tersebut, nama tambah tetap menjadi simbol lokal yang tidak terlihat dari berkas lain, dan penautan lintas berkas akan gagal. Status global sendiri tidak mengubah byte program sama sekali, karena perbedaannya hanya pada keterlihatan nama bagi linker pada saat penautan.
Tabel simbol pada nm#
Perkakas nm menampilkan isi tabel simbol setiap berkas objek. Kedua berkas dirakit, lalu simbolnya didaftarkan dengan nm.
# rakit kedua berkas menjadi berkas objek
nasm -f elf64 main.asm -o main.o
nasm -f elf64 aritmetika.asm -o aritmetika.o
# daftar simbol pada masing-masing berkas objek
nm main.o
echo '--- aritmetika.o ---'
nm aritmetika.o0000000000000000 T _start
0000000000000000 d angka
0000000000000000 b sementara
U tambah
0000000000000008 d tujuan
--- aritmetika.o ---
0000000000000000 T tambahKolom kiri berisi alamat simbol, dan hampir semuanya bernilai 0000000000000000 karena berkas objek belum memiliki alamat akhir. Pengecualian hanya pada tujuan yang menempati alamat 0000000000000008, yaitu offset 8 pada .data karena menyusul angka yang berukuran 8 byte. Huruf tengah menyatakan jenisnya, dengan huruf besar untuk simbol global dan huruf kecil untuk simbol lokal. Daftar ini disusun nm menurut nama simbol secara alfabetis, sehingga urutannya tidak mengikuti urutan kemunculan di dalam berkas. T atau t menandai simbol pada section .text, d menandai data pada .data, b menandai ruang pada .bss, sedangkan U singkatan dari undefined dan menandai simbol yang digunakan tetapi tidak didefinisikan di berkas ini. main.o memuat _start sebagai simbol global, angka dan sementara sebagai simbol lokal, serta tambah yang masih kosong. aritmetika.o memuat tambah sebagai simbol global T yang siap digunakan berkas lain.
Relokasi dan tipenya#
Assembler meninggalkan catatan relocation pada setiap byte yang menunggu alamat akhir. Perintah readelf -rW menampilkan catatan tersebut beserta tipenya.
# relokasi yang belum selesai pada main.o
readelf -rW main.oRelocation section '.rela.data' at offset 0x430 contains 1 entry:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000008 0000000800000001 R_X86_64_64 0000000000000000 tambah + 0
Relocation section '.rela.text' at offset 0x450 contains 3 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000003 0000000200000002 R_X86_64_PC32 0000000000000000 .data - 4
000000000000000d 0000000800000002 R_X86_64_PC32 0000000000000000 tambah - 4
0000000000000014 0000000300000002 R_X86_64_PC32 0000000000000000 .bss - 4Relokasi pertama bertipe R_X86_64_64 dan menunggu alamat penuh simbol tambah untuk mengisi 8 byte pada .data, tepat pada nilai yang disimpan oleh tujuan. Tiga relokasi berikutnya bertipe R_X86_64_PC32, tipe untuk operand yang dihitung relatif terhadap RIP. Dua di antaranya merujuk .data dan .bss karena mov rdi, [angka] dan mov [sementara], rax menyebut label lokal yang dirujuk NASM melalui simbol section-nya, sedangkan call tambah merujuk simbol tambah secara langsung. Jumlah entri tersebut juga menunjukkan bahwa assembler hanya membiarkan byte alamat yang benar-benar belum diketahui, sedangkan operan immediate dan register diselesaikan sepenuhnya pada saat perakitan. Kolom Offset menyatakan letak byte yang akan diisi, diukur dari awal section-nya, sehingga tiga entri .rela.text menunjuk tiga titik berbeda pada .text, yaitu offset 3, 13, dan 20 atau 0x3, 0xd, dan 0x14. Angka pada kolom paling kanan adalah addend, penyesuai kecil yang menyebabkan hasil pengisian dihitung dari akhir ruang displacement berukuran 4 byte.
Penyelesaian oleh linker#
Linker menyatukan kedua berkas objek dengan menggabungkan section yang bernama sama, mengalokasikan alamat akhir untuk setiap simbol, lalu mengisi seluruh relokasi. Apabila satu relokasi tidak dapat diselesaikan karena simbolnya tidak tersedia, linker menolak hasilnya, sebagaimana ditunjukkan percobaan menautkan main.o sendirian. Urutan berkas pada baris perintah ld menentukan urutan penggabungan section-nya, tetapi selama setiap simbol dapat diselesaikan, penautan tetap sah.
# menautkan tanpa aritmetika.o, simbol tambah tidak tersedia
ld main.o -o terputus
echo "exit=$?"ld: main.o: in function `tujuan':
main.asm:(.data+0x8): undefined reference to `tambah'
ld: main.o: in function `_start':
main.asm:(.text+0xd): undefined reference to `tambah'
exit=1Pesan error undefined reference muncul dua kali karena dua relokasi terhadap simbol tambah tidak terjawab, satu pada .data dan satu pada .text. Tidak ada executable yang dihasilkan, dan ld berhenti dengan kode keluar 1. Penautan baru berhasil setelah aritmetika.o disertakan.
# penautan lengkap, lalu jalankan programnya
ld main.o aritmetika.o -o lengkap
./lengkap
echo "exit=$?"
# alamat akhir simbol setelah penautan
nm lengkap | grep -E ' (T|d|b|B|D) (_start|tambah|angka|tujuan|sementara|__bss_start|_edata)$'exit=42
0000000000402010 B __bss_start
0000000000402010 D _edata
0000000000401000 T _start
0000000000402000 d angka
0000000000402010 b sementara
0000000000401030 T tambah
0000000000402008 d tujuanProgram berhenti dengan kode keluar 42, hasil penjumlahan 24 dan 18 yang dibawa keluar melalui eax. Daftar simbol pada executable akhir menunjukkan setiap nama kini membawa alamat nyata, dan tidak ada lagi simbol berstatus U karena definisi tambah ditemukan di berkas lain. nm mengurutkan daftarnya menurut nama simbol dan bukan alamatnya, sehingga baris tambah beralamat 0x401030 muncul setelah sementara yang beralamat lebih besar. Simbol yang semula lokal seperti angka juga tercantum dengan alamatnya, tetapi nama tersebut tetap tidak dapat diimpor oleh berkas lain karena statusnya tidak pernah diubah menjadi global.
Ringkasan#
- Simbol adalah nama beserta alamatnya yang dikumpulkan dalam tabel simbol berkas objek, dengan status global atau lokal yang ditetapkan lewat
global. - nm menampilkan alamat, jenis, dan nama simbol, dengan huruf besar untuk simbol global dan U untuk simbol yang belum didefinisikan.
- Relokasi adalah catatan byte yang menunggu alamat akhir, dengan tipe R_X86_64_PC32 untuk operand relatif RIP dan R_X86_64_64 untuk alamat absolut 64-bit.
- Linker menyelesaikan relokasi lintas berkas, sedangkan simbol yang tidak tersedia menyebabkan error undefined reference dan penautan gagal.
Referensi#
- Assembler Directives — NASM — Direktif GLOBAL, EXTERN, dan STATIC untuk keluar masuknya simbol antarmodul.
- nm — GNU Binutils — Daftar huruf jenis simbol yang dicetak nm pada kolom keduanya.
- readelf — GNU Binutils — Opsi -r untuk menampilkan relokasi beserta tipenya.