Topik ini membahas empat section bawaan NASM, yaitu .text, .data, .rodata, dan .bss, beserta izin baca, tulis, dan eksekusi yang menyertai masing-masingnya. Pemeriksaan dilakukan pada satu berkas objek kecil yang memuat keempatnya, lalu pada executable hasil penautan. Perbedaan ukuran berkas dan ukuran memori pada .bss serta akibat melanggar izin memori ditunjukkan dengan keluaran perkakas binutils yang asli.
Empat section bawaan#
Section adalah bagian dari berkas objek yang menampung byte dengan peruntukan serupa, dan pemisahan inilah dasar penetapan izin memori ketika program dijalankan. Direktif section .text menampung kode instruksi, section .data menampung data terinisialisasi yang boleh diubah, section .rodata menampung konstanta yang tidak boleh diubah, sedangkan section .bss memesan ruang kosong yang diisi nol oleh loader, yaitu bagian sistem operasi yang memuat program ke memori. Nama keempat section tersebut bukan kesepakatan semata, melainkan konvensi yang dikenali linker dan skrip penautan bawaannya, sehingga setiap section berakhir pada segmen dengan izin yang tepat.
| Section | Isi | Izin memori |
|---|---|---|
| .text | kode instruksi | baca dan eksekusi |
| .data | data terinisialisasi yang boleh diubah | baca dan tulis |
| .rodata | konstanta yang tetap | baca saja |
| .bss | ruang kosong yang diisi nol | baca dan tulis |
Izin ditetapkan per halaman memori (page), satuan pelindungan memori yang dikelola prosesor. Setelah program dimuat, instruksi boleh membaca dan mengeksekusi halaman .text tetapi tidak boleh menulisnya, halaman .rodata hanya boleh dibaca, sedangkan halaman .data dan .bss boleh dibaca sekaligus ditulis. Selain keempat section bawaan tersebut, NASM mengizinkan section kustom melalui direktif section yang diberi kualifier, misalnya section kustom write, dan bentuk ini jarang diperlukan pada program biasa.
Pembacaan header section#
Ukuran, offset berkas, dan penanda setiap section dapat diperiksa dengan objdump -h. Berkas berikut memuat keempat section sekaligus, sehingga keluarannya memperlihatkan semuanya dalam satu tabel. Program ini sengaja tidak menggunakan data yang didefinisikannya, karena tugasnya hanya membentuk struktur section.
default rel
section .text
global _start
_start:
mov eax, 60 ; syscall exit
xor edi, edi ; kode keluar 0
syscall
section .data
nilai: dq 42 ; terinisialisasi, boleh diubah
section .rodata
pesan: db "data tetap", 10
section .bss
buffer: resb 8192 ; ruang kosong, diisi nol saat dimuat# rakit berkas objek tanpa menautkannya
nasm -f elf64 section-empat.asm -o section-empat.o
# tampilkan header setiap section beserta penandanya
objdump -h section-empat.osection-empat.o: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000009 0000000000000000 0000000000000000 00000240 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000008 0000000000000000 0000000000000000 00000250 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .rodata 0000000b 0000000000000000 0000000000000000 00000260 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .bss 00002000 0000000000000000 0000000000000000 00000270 2**2
ALLOCKolom Size menyatakan panjang section dalam heksadesimal, sedangkan File off menyatakan posisi awalnya di dalam berkas. Kolom VMA pada berkas objek masih seragam 0000000000000000 karena alamat final baru ditetapkan ketika penautan dilakukan, sehingga yang relevan pada tahap ini hanyalah Size, File off, dan penandanya. Penanda di bawah setiap baris menjelaskan sifat section lebih lanjut. CONTENTS berarti byte section benar-benar tersimpan di dalam berkas, sedangkan ALLOC berarti ruangnya dialokasikan di memori ketika program dimuat. Section .text, .data, dan .rodata memuat kedua penanda tersebut, sedangkan .bss hanya ALLOC karena tidak menyimpan satu byte pun. Nilai 0x2000 pada .bss berarti 8192 byte, sedangkan 0x0b pada .rodata berarti 11 byte untuk teks “data tetap” beserta karakter baris barunya.
Ukuran berkas dan ukuran memori#
Ukuran .bss yang besar tidak membuat berkas objek membengkak karena byte-nya memang tidak pernah disimpan. Perintah stat menampilkan ukuran kedua berkas secara langsung untuk menunjukkan ketimpangan tersebut.
# ukuran berkas sumber dan berkas objek yang dihasilkannya
stat -c '%s byte %n' section-empat.asm section-empat.o338 byte section-empat.asm
976 byte section-empat.oSeluruh berkas objek hanya 976 byte, padahal .bss di dalamnya memesan 8192 byte ruang memori. Loader memetakan halaman untuk .bss lalu mengisi halaman tersebut dengan nol, tanpa membaca apa pun dari berkas. Pola ini cocok untuk buffer besar yang isinya memang dimulai dari nol, misalnya buffer pembacaan berkas. Buffer semacam ini lazim ditempatkan pada .bss justru karena keadaan awalnya memang nol, sehingga tidak ada byte yang perlu disalin dari berkas pada saat program dimuat. Dengan begitu, waktu muat program tetap singkat sekalipun ruang yang dipesannya besar. Konsekuensinya, variabel pada .bss tidak boleh diberi nilai awal selain nol, sehingga nilai awal lain harus ditempatkan pada .data.
Izin segmen dan pelanggarannya#
Setelah penautan, section yang serupa digabungkan menjadi segment, yaitu rentang memori yang dipetakan dengan satu set izin. Perintah readelf -lW menampilkan program header yang memuat daftar segmen tersebut, dan kolom Flg pada tabelnya menyatakan izin yang digunakan kernel untuk memetakan setiap segmen.
# tautkan menjadi executable, lalu lihat izin tiap segmen
ld section-empat.o -o section-empat
readelf -lW section-empat | grep -E 'LOAD|Type' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x000120 0x000120 R 0x1000
LOAD 0x001000 0x0000000000401000 0x0000000000401000 0x000009 0x000009 R E 0x1000
LOAD 0x002000 0x0000000000402000 0x0000000000402000 0x00000b 0x00000b R 0x1000
LOAD 0x00200c 0x000000000040300c 0x000000000040300c 0x000008 0x00200c RW 0x1000Terdapat empat baris LOAD. Baris pertama berisi header ELF itu sendiri dengan izin baca saja. Baris kedua bertanda R E memuat .text yang boleh dibaca dan dieksekusi. Baris ketiga bertanda R memuat .rodata yang hanya boleh dibaca. Baris keempat bertanda RW menggabungkan .data dan .bss, dengan FileSiz 0x8 dan MemSiz 0x200c karena sebagian besar isinya adalah .bss yang tidak tersimpan di berkas.
Pelanggaran izin dapat dibuktikan dengan program yang menulis ke .rodata.
# program yang menulis byte ke section .rodata
cat > tulis-rodata.asm <<'EOF'
default rel
section .rodata
pesan: db "teks tetap", 10
section .text
global _start
_start:
mov byte [pesan], 88
mov eax, 60
xor edi, edi
syscall
EOF
nasm -f elf64 tulis-rodata.asm -o tulis-rodata.o
ld tulis-rodata.o -o tulis-rodata
# jalankan lewat sh agar pesan kernelnya terlihat
sh -c ./tulis-rodata
echo "exit=$?"Segmentation fault
exit=139Instruksi mov byte [pesan], 88 berusaha menulis ke halaman .rodata yang hanya boleh dibaca. Prosesor menolak akses tersebut dan mengirim sinyal SIGSEGV kepada proses, sehingga program dihentikan sebelum baris exit sempat dijalankan. Shell melaporkan “Segmentation fault” beserta kode keluar 139, yaitu 128 ditambah nomor sinyal 11. Kode keluar semacam ini bersifat tetap dan dapat digunakan sebagai penanda kuat bahwa sebuah alamat berada pada halaman yang tidak boleh ditulis.
Ringkasan#
- Section .text, .data, .rodata, dan .bss memisahkan kode, data terinisialisasi, konstanta, dan ruang kosong berisi nol, masing-masing dengan izin baca, tulis, dan eksekusi yang berbeda.
- Penanda CONTENTS dan ALLOC pada
objdump -hmembedakan byte yang tersimpan di berkas dari ruang yang hanya dialokasikan di memori. - .bss tidak menambah ukuran berkas karena loader mengisinya dengan nol saat program dimuat.
- Izin ditetapkan per segmen pada program final, dan penulisan ke .rodata berakhir dengan SIGSEGV serta kode keluar 139.
Referensi#
- Assembler Directives — NASM — Direktif SECTION beserta section bawaan yang dikenali NASM.
- objdump — GNU Binutils — Opsi -h untuk menampilkan header section beserta penandanya.
- System V Application Binary Interface AMD64 — GitLab — Spesifikasi program header dan pelindungan halaman memori untuk Linux x86-64.