Belajar Assembly - Data Structures: Arrays, Strings & Structs
Episode 11 of 23

Belajar Assembly - Data Structures: Arrays, Strings & Structs

Mengelola data di assembly: peran section .data vs .bss, deklarasi db/dw/dd/dq, array dengan times dan indexing [arr + i*size], string null-terminated dengan loop, implementasi manual strlen dan strcpy, serta layout struct dengan pengaturan padding dan alignment.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Sejauh ini program kalian hanya menangani konstanta dan teks pendek. Episode 11 adalah titik di mana kalian mulai mengelola data nyata: array, string, dan struct — struktur data yang setiap hari kalian pakai di C tanpa berpikir dua kali. Di assembly, semuanya menjadi nyata: kalian melihat byte, alamat, dan layout memori secara langsung.

Mengapa ini penting? Dua alasan. Pertama, sebagian besar codebase assembly dunia nyata adalah library pengolahan data dan format binary. Kedua, memahami layout memori di level byte adalah bekal wajib sebelum masuk ke reverse engineering (16) dan exploit (17) — dua bidang yang seluruhnya tentang memahami bagaimana data diletakkan.

.data vs .bss

Kedua section ini menyimpan data, tapi dengan karakter berbeda:

Aspeksection .datasection .bss
Isinilai awal diisi di build timenilai awal nol (diisi di load time)
Ukuran executableikut bertambahtidak (hanya deklarasi ukuran)
Cocok untukkonstanta, string, tabelbuffer, array besar
Direktifdb/dw/dd/dq, timesresb/resw/resd/resq, times
.data vs .bss
section .data
    greeting db "Halo", 0
    numbers  dd 10, 20, 30, 40     ; array 4 x 4 byte
 
section .bss
    buffer   resb 4096             ; 4096 byte nol
    counters resq 100              ; 100 qword nol

Tip

Gunakan .bss untuk buffer besar yang isinya tidak perlu ditentukan di awal — executable kalian tetap kecil karena .bss tidak menyimpan byte di file, hanya janji "sediakan N byte nol saat program dimuat".

Array dan Indexing

Array di assembly hanyalah blok memori berurutan. Elemen ke-i dari array dengan elemen berukuran size berada di alamat base + i*size — persis addressing mode indexed yang kita bahas di episode 5:

Array 64-bit dan indexing
section .data
    arr dq 100, 200, 300, 400, 500   ; 5 elemen, masing-masing 8 byte
    arr_len equ 5
 
section .text
; rax = arr[i] dengan i di rcx
    lea rbx, [arr]
    mov rax, [rbx + rcx*8]           ; elemen ke-rcx (8 byte per elemen)

Contoh lengkap: menjumlahkan semua elemen array.

Jumlahkan array
section .data
    arr   dq 10, 20, 30, 40, 50
    arr_len equ 5
 
section .text
    global _start
_start:
    mov rcx, arr_len      ; counter = jumlah elemen
    xor rax, rax          ; sum = 0
    lea rbx, [arr]        ; rbx = alamat awal array
sum_loop:
    add rax, [rbx]        ; sum += arr[i]
    add rbx, 8            ; maju ke elemen berikutnya
    loop sum_loop         ; dec rcx; jnz sum_loop
 
    ; hasil di rax = 150 — konversi ke desimal (buku) atau syscall exit
    mov rax, 60
    mov rdi, 150          ; exit code 150 (cukup sebagai bukti)
    syscall

Cek dengan ./sum && echo $?150. (Untuk mencetak angka desimal dari register, kita perlu konversi digit — materi yang umum di episode selanjutnya; sebagai pengganti, exit code di sini berfungsi sebagai bukti hitungan.)

String Null-Terminated

Di C, string adalah array char yang diakhiri 0. Konsep yang sama berlaku di assembly:

Deklarasi string
section .data
    s1 db "hello", 0
    s2 db 'h', 'e', 'l', 'l', 'o', 0    ; sama persis
    s3 db "abc", 0, "def", 0            ; dua string dalam satu blok

Panjang string tidak disimpan — ia dihitung dengan berjalan sampai byte nol. Mari implementasikan strlen dan strcpy sendiri:

strlen

strlen
; input : rdi = alamat string null-terminated
; output: rax = panjang string (tanpa null terminator)
strlen:
    xor rax, rax          ; panjang = 0
.loop:
    cmp byte [rdi + rax], 0
    je  .done
    inc rax
    jmp .loop
.done:
    ret

strcpy

strcpy
; input : rdi = alamat tujuan, rsi = alamat sumber
; output: -
strcpy:
    xor rax, rax
.copy:
    mov cl, [rsi + rax]   ; ambil byte sumber
    mov [rdi + rax], cl   ; tulis ke tujuan
    test cl, cl           ; byte nol?
    jz  .done
    inc rax
    jmp .copy
.done:
    ret

Warning

Perhatikan bahwa strcpy dan strlen tidak tahu batas buffer — mereka berhenti hanya karena byte nol. Jika buffer tujuan terlalu kecil, strcpy menulis melampaui batas: ini persis buffer overflow yang kita eksploitasi di episode 17. Fungsi C modern seperti strncpy/strlcpy hadir justru untuk mencegah pola berbahaya ini.

Optimasi yang dilakukan CPU modern

Perhatikan bahwa implementasi di atas memproses satu byte per iterasi. CPU modern memakai instruksi seperti REPNE SCASB atau bahkan SIMD (episode 12) untuk strlen yang memeriksa 16 byte sekaligus. Sejak era strlen glibc memakai SSE2, itulah contoh nyata mengapa "hitung byte demi byte" jarang ditemukan di library produksi.

Struct: Layout dan Alignment

Struct di assembly adalah soal menghitung offset. Definisi field menentukan tata letak byte:

Struct manual (tanpa padding)
; struct Person {
;   int age;        // offset 0, 4 byte
;   long id;        // offset 4, 8 byte  ← TIDAK ALIGNED!
;   char name[8];   // offset 12, 8 byte
; }

Versi di atas salah secara alignment: field id (8-byte) berada di offset 4 — tidak kelipatan 8. Kebanyakan arsitektur mengharuskan field diakses pada offset kelipatan ukurannya, atau CPU harus bekerja ekstra (misal di ARM64, akses misaligned bisa crash). Compiler menambahkan padding:

Struct dengan padding (benar)
; struct Person {
;   int  age;      // offset 0
;   /* 4 byte padding */
;   long id;       // offset 8
;   char name[8];  // offset 16
; };                // total 24 byte

Aturan alignment paling umum: field tipe N-byte harus berada di offset kelipatan N. Untuk struct bertingkat, offset dihitung relatif terhadap awal struct, dan total ukuran struct biasanya dibulatkan ke kelipatan alignment terbesarnya.

Note

Saat membaca format binary dari disk atau jaringan (misal header PNG, protokol network), kalian sering perlu tahu padding persis — dan di sinilah C bisa mengecoh: sizeof(struct) sering lebih besar dari jumlah field karena padding. Menggunakan __attribute__((packed)) menghapus padding untuk format disk, tetapi mengorbankan kecepatan akses.

Penutup

Pada episode 11 ini, kalian telah menguasai pengelolaan data di assembly.

Inti yang harus dibawa pulang:

  • .data untuk nilai awal di build time; .bss untuk buffer nol yang hemat ruang file.
  • Array = blok memori berurutan; elemen ke-i di [base + i*size] (indexed addressing).
  • String null-terminated: panjang tidak disimpan, dicari lewat byte nol.
  • strlen/strcpy manual menunjukkan mengapa buffer overflow terjadi.
  • Struct: offset field dihitung manual; hormati alignment (padding) agar akses cepat dan benar.

Di episode 12 selanjutnya, kita menaikkan level komputasi: SSE/AVX & floating point — dari x87 (FPU stack) ke SSE2 (xmm, addsd/mulsd), SIMD packed operations, register ymm/zmm pada AVX, hingga contoh vektorisasi nyata yang membuat kalian melihat mengapa CPU modern bisa memproses 8 angka sekaligus. Siapkan array kita dari episode ini — kita akan menjumlahkannya dengan cara SIMD!

Belajar Assembly - Data Structures: Arrays, Strings & Structs | Belajar Assembly