WebAssembly Text Format ile Düşük Seviyeli Programlama
Sistem yöneticisi olarak WebAssembly ile tanışmak biraz tuhaf hissettiriyor ilk başta. “Bu bir web teknolojisi değil mi?” diye soruyorsunuz kendinize. Ama WASM’ın düşük seviyeli çalışma mantığını kavradığınızda, edge computing, serverless ve hatta gömülü sistemler dünyasında neden bu kadar gündeme geldiğini anlıyorsunuz. Bu yazıda WebAssembly Text Format (WAT) üzerinden düşük seviyeli programlamayı ele alacağız ve sysadmin perspektifinden gerçek dünya senaryolarına bakacağız.
WebAssembly Text Format Nedir?
WebAssembly ikili formatta (.wasm) çalışır, ancak bu dosyaları doğrudan okumak neredeyse imkansızdır. WebAssembly Text Format (WAT) ise bu ikili formatın insan tarafından okunabilir metin temsilidir. Tıpkı assembly dilinin makine kodu ile yüksek seviyeli diller arasında köprü kurması gibi, WAT da WASM ikili formatı ile geliştiriciler arasında bir köprü görevi görür.
Bir sistem yöneticisi olarak WAT’ı neden öğrenmelisiniz? Çünkü Cloudflare Workers, Fastly Compute@Edge veya kendi kurduğunuz Wasmtime tabanlı sistemlerde WASM modüllerini debug etmeniz, optimize etmeniz veya güvenlik açısından incelemeniz gerekebilir. Bu noktada WAT bilgisi son derece değerli hale gelir.
WAT, S-expression tabanlı bir sözdizimine sahiptir. Yani Lisp diline benzer bir parantez yapısı kullanır. Bu ilk bakışta korkutucu görünebilir ama mantığını kavradığınızda oldukça sezgisel hale gelir.
Geliştirme Ortamını Hazırlamak
Başlamadan önce gerekli araçları kuralım. WABT (WebAssembly Binary Toolkit) bizim temel aracımız olacak.
# Ubuntu/Debian sistemlerde WABT kurulumu
sudo apt-get update
sudo apt-get install -y wabt
# MacOS'ta Homebrew ile
brew install wabt
# Wasmtime runtime kurulumu (Linux)
curl https://wasmtime.dev/install.sh -sSf | bash
source ~/.bashrc
# Kurulumu doğrulayalım
wat2wasm --version
wasmtime --version
wasm2wat --version
Bu araçlar birlikte çok güçlü bir geliştirme ve debug ortamı oluşturur. wat2wasm WAT dosyalarını WASM ikili formatına dönüştürür, wasm2wat ise tam tersi yönde çalışır. Bir WASM modülünü reverse engineering yapmak istediğinizde wasm2wat hayat kurtarır.
İlk WAT Modülü: Temel Yapı
WAT’ın temel yapısını anlamak için basit bir örnekle başlayalım. Bir modül, WebAssembly’nin en temel birimi olup her şey bir modül içinde tanımlanır.
;; ilk_modul.wat
;; Bu basit bir toplama fonksiyonu içeren WAT modülüdür
(module
;; Fonksiyon tanımı: iki i32 alır, bir i32 döner
(func $topla (param $x i32) (param $y i32) (result i32)
local.get $x ;; x'i stack'e yükle
local.get $y ;; y'yi stack'e yükle
i32.add ;; ikisini topla, sonuç stack'te kalır
)
;; Fonksiyonu dışarıya export et
(export "topla" (func $topla))
)
Bu dosyayı derleyip çalıştıralım:
# WAT'tan WASM'a dönüştür
wat2wasm ilk_modul.wat -o ilk_modul.wasm
# Dosya boyutunu kontrol et
ls -lh ilk_modul.wasm
# Wasmtime ile çalıştır ve test et
wasmtime ilk_modul.wasm --invoke topla 5 3
# Alternatif olarak wasm2wat ile geri dönüştürüp doğrula
wasm2wat ilk_modul.wasm
Stack tabanlı mimariyi anlamak burada kritik. WAT’ta her işlem bir stack üzerinde gerçekleşir. local.get $x komutu x değişkeninin değerini stack’in tepesine iter, i32.add ise stack’ten iki değer çeker, toplar ve sonucu stack’e geri iter.
Veri Tipleri ve Bellek Yönetimi
WebAssembly’de dört temel sayısal tip vardır ve bir sysadmin olarak bunları iyi anlamanız önemlidir çünkü performans optimizasyonu doğrudan bu tiplere bağlıdır.
- i32: 32-bit işaretli tam sayı (en yaygın kullanılan)
- i64: 64-bit işaretli tam sayı (büyük dosya boyutları, zaman damgaları için)
- f32: 32-bit kayan noktalı sayı
- f64: 64-bit kayan noktalı sayı (hassas hesaplamalar için)
Bellek yönetimi WAT’ın en kritik konularından biridir. WebAssembly doğrusal bir bellek modeli kullanır ve bu bellek 64KB’lik “page”ler halinde organize edilir.
;; bellek_yonetimi.wat
;; Bellek ayırma ve string işleme örneği
(module
;; 1 sayfa bellek ayır (64KB)
(memory (export "mem") 1)
;; Bellekte bir sayac tut
(global $sayac (mut i32) (i32.const 0))
;; Belleğe veri yaz
(func $bellek_yaz (param $adres i32) (param $deger i32)
local.get $adres
local.get $deger
i32.store ;; 4 byte yaz
)
;; Bellekten veri oku
(func $bellek_oku (param $adres i32) (result i32)
local.get $adres
i32.load ;; 4 byte oku
)
;; Sayaci artir
(func $sayaci_artir (result i32)
global.get $sayac
i32.const 1
i32.add
global.set $sayac
global.get $sayac
)
(export "bellek_yaz" (func $bellek_yaz))
(export "bellek_oku" (func $bellek_oku))
(export "sayaci_artir" (func $sayaci_artir))
)
Bu bellek modeli, sistem yöneticileri için güvenlik açısından son derece önemlidir. WASM modülleri kendi sandbox bellek alanlarında çalışır ve host sistemin belleğine doğrudan erişemez. Bu, edge computing senaryolarında güvenilmeyen kod çalıştırırken büyük avantaj sağlar.
Kontrol Akışı ve Döngüler
Gerçek dünya senaryolarında döngüler ve koşullu ifadeler kaçınılmazdır. WAT’ın kontrol akışı yapısı, yüksek seviyeli dillerden biraz farklı çalışır.
;; kontrol_akisi.wat
;; Döngü ve koşul örnekleri ile log analizi simülasyonu
(module
(memory 1)
;; Hata sayısını tutan global değişken
(global $hata_sayisi (mut i32) (i32.const 0))
;; 1'den n'e kadar toplama (klasik döngü örneği)
(func $toplam_hesapla (param $n i32) (result i32)
(local $toplam i32)
(local $i i32)
i32.const 0
local.set $toplam
i32.const 1
local.set $i
;; block ve loop ile döngü yapısı
(block $dongu_cikis
(loop $dongu_devam
;; i > n ise döngüden çık
local.get $i
local.get $n
i32.gt_s
br_if $dongu_cikis
;; toplam = toplam + i
local.get $toplam
local.get $i
i32.add
local.set $toplam
;; i = i + 1
local.get $i
i32.const 1
i32.add
local.set $i
;; Döngünün başına dön
br $dongu_devam
)
)
local.get $toplam
)
;; Değerin pozitif, negatif veya sıfır olduğunu kontrol et
;; 1: pozitif, -1: negatif, 0: sıfır
(func $isaretini_bul (param $deger i32) (result i32)
(if (result i32) (i32.gt_s (local.get $deger) (i32.const 0))
(then i32.const 1)
(else
(if (result i32) (i32.lt_s (local.get $deger) (i32.const 0))
(then i32.const -1)
(else i32.const 0)
)
)
)
)
(export "toplam_hesapla" (func $toplam_hesapla))
(export "isaretini_bul" (func $isaretini_bul))
)
# Derle ve test et
wat2wasm kontrol_akisi.wat -o kontrol_akisi.wasm
# Toplam hesapla: 1+2+3+...+100 = 5050 olmalı
wasmtime kontrol_akisi.wasm --invoke toplam_hesapla 100
# İşaret kontrolü test et
wasmtime kontrol_akisi.wasm --invoke isaretini_bul -- -5
wasmtime kontrol_akisi.wasm --invoke isaretini_bul 42
Gerçek Dünya Senaryosu: Log Analizi Modülü
Bir sysadmin olarak en sık yapılan işlemlerden biri log analizi. Cloudflare Workers veya Fastly gibi edge platformlarda WASM modülleri kullanarak log işleme yapabilirsiniz. Aşağıda basit ama işlevsel bir örnek görelim.
;; log_analiz.wat
;; Edge'de çalışan basit HTTP status code analiz modülü
(module
(memory (export "mem") 2) ;; 128KB bellek
;; Sayaçlar için sabit adresler
;; Adres 0: 2xx sayısı
;; Adres 4: 4xx sayısı
;; Adres 8: 5xx sayısı
;; Adres 12: toplam istek sayısı
;; Sayaçları başlat
(func $sayaclari_sifirla
i32.const 0 i32.const 0 i32.store
i32.const 4 i32.const 0 i32.store
i32.const 8 i32.const 0 i32.store
i32.const 12 i32.const 0 i32.store
)
;; HTTP status kodunu işle
(func $status_isle (param $status_kodu i32)
;; Toplam sayacı artır
i32.const 12
i32.const 12
i32.load
i32.const 1
i32.add
i32.store
;; 200-299 aralığı mı?
(block $kontrol_bitti
(if (i32.and
(i32.ge_s (local.get $status_kodu) (i32.const 200))
(i32.lt_s (local.get $status_kodu) (i32.const 300)))
(then
i32.const 0
i32.const 0 i32.load
i32.const 1 i32.add
i32.store
br $kontrol_bitti
)
)
;; 400-499 aralığı mı?
(if (i32.and
(i32.ge_s (local.get $status_kodu) (i32.const 400))
(i32.lt_s (local.get $status_kodu) (i32.const 500)))
(then
i32.const 4
i32.const 4 i32.load
i32.const 1 i32.add
i32.store
br $kontrol_bitti
)
)
;; 500-599 aralığı mı?
(if (i32.and
(i32.ge_s (local.get $status_kodu) (i32.const 500))
(i32.lt_s (local.get $status_kodu) (i32.const 600)))
(then
i32.const 8
i32.const 8 i32.load
i32.const 1 i32.add
i32.store
)
)
)
)
;; Sonuçları oku
(func $basarili_sayisi (result i32) i32.const 0 i32.load)
(func $istemci_hatasi (result i32) i32.const 4 i32.load)
(func $sunucu_hatasi (result i32) i32.const 8 i32.load)
(func $toplam_istek (result i32) i32.const 12 i32.load)
(export "sayaclari_sifirla" (func $sayaclari_sifirla))
(export "status_isle" (func $status_isle))
(export "basarili_sayisi" (func $basarili_sayisi))
(export "istemci_hatasi" (func $istemci_hatasi))
(export "sunucu_hatasi" (func $sunucu_hatasi))
(export "toplam_istek" (func $toplam_istek))
)
Bu modülü derleyip bir bash scriptiyle test edelim:
# Modülü derle
wat2wasm log_analiz.wat -o log_analiz.wasm
# Test scripti oluştur
cat << 'EOF' > test_log_analiz.sh
#!/bin/bash
echo "Log analiz modülü test ediliyor..."
# Sayaçları sıfırla
wasmtime log_analiz.wasm --invoke sayaclari_sifirla
# Simüle edilmiş HTTP istekleri işle
for status in 200 201 200 404 500 200 403 200 502 200; do
wasmtime log_analiz.wasm --invoke status_isle $status 2>/dev/null
done
echo "Başarılı (2xx): $(wasmtime log_analiz.wasm --invoke basarili_sayisi)"
echo "İstemci hatası (4xx): $(wasmtime log_analiz.wasm --invoke istemci_hatasi)"
echo "Sunucu hatası (5xx): $(wasmtime log_analiz.wasm --invoke sunucu_hatasi)"
echo "Toplam istek: $(wasmtime log_analiz.wasm --invoke toplam_istek)"
EOF
chmod +x test_log_analiz.sh
Import ve Export Mekanizmaları
Gerçek dünya uygulamalarında WASM modüllerinin host ortamla iletişim kurması gerekir. Bu noktada import mekanizması devreye girer. Özellikle Wasmtime veya Wasmer ile kendi runtime’ınızı oluşturduğunuzda bu kavramı anlamanız şarttır.
;; import_export.wat
;; Host ortamdan fonksiyon import eden modül örneği
(module
;; Host'tan print fonksiyonu import et
;; "env" modülünden "log_yaz" fonksiyonunu al
(import "env" "log_yaz" (func $host_log (param i32 i32)))
;; Host'tan zaman damgası al
(import "env" "zaman_al" (func $host_zaman (result i64)))
(memory (export "mem") 1)
;; Basit bir işlem yapıp sonucu logla
(func $hesapla_ve_logla (param $a i32) (param $b i32) (result i32)
(local $sonuc i32)
(local $zaman i64)
;; Çarpım hesapla
local.get $a
local.get $b
i32.mul
local.set $sonuc
;; Zaman damgası al (monitoring için)
call $host_zaman
local.set $zaman
;; Sonucu bellekte sakla ve logla
;; (gerçek uygulamada string formatting yapılır)
i32.const 0 ;; bellek adresi
i32.const 8 ;; mesaj uzunluğu
call $host_log
local.get $sonuc
)
(export "hesapla_ve_logla" (func $hesapla_ve_logla))
)
WASM Modüllerini Üretim Ortamında Debug Etmek
Bir production sistemde WASM modülü beklendiği gibi davranmıyorsa ne yaparsınız? İşte burada WAT bilginiz kritik hale gelir.
# Mevcut bir WASM dosyasını WAT formatına çevir
wasm2wat production_modul.wasm -o production_modul.wat
# WAT dosyasını insan okunabilir şekilde göster
cat production_modul.wat | head -100
# WASM dosyasının genel istatistiklerini al
wasm-objdump -h production_modul.wasm
# Tüm export edilen fonksiyonları listele
wasm-objdump -x production_modul.wasm | grep -A 20 "Export"
# Fonksiyon listesini görüntüle
wasm-objdump -d production_modul.wasm | grep "^[0-9a-f]* func"
# WABT'ın validate aracı ile modülün geçerli olup olmadığını kontrol et
wasm-validate production_modul.wasm && echo "Modül geçerli" || echo "Modülde hata var"
# Bellek kullanım analizi
wasm-objdump -x production_modul.wasm | grep -A 5 "Memory"
Bu komutları cron job içine alıp periyodik WASM modül sağlık kontrolü yapabilirsiniz. Özellikle CI/CD pipeline’ınızda her deploy öncesinde wasm-validate çalıştırmak iyi bir pratiktir.
Performans Optimizasyonu İpuçları
WAT seviyesinde yazarken veya üretilmiş WAT kodunu incelerken dikkat etmeniz gereken bazı kritik noktalar var.
- i32 vs i64 tercihi: Mümkün olduğunda i32 kullanın. 32-bit işlemler çoğu platformda daha hızlıdır ve bellek bant genişliğini daha verimli kullanır.
- local değişken sayısını minimize edin: Her local değişken stack frame’de yer kaplar. Gereksiz local değişkenlerden kaçının.
- Bellek erişim düzenini optimize edin: Sıralı (sequential) bellek erişimi, rastgele (random) erişimden çok daha hızlıdır. Cache coherency burada da geçerlidir.
- br_table kullanımı: Çok sayıda if-else yerine br_table kullanmak jump table oluşturur ve büyük switch-case benzeri yapılarda ciddi hız kazancı sağlar.
- Tail call optimizasyonu: Recursive fonksiyonlarda return_call kullanarak stack overflow riskini azaltabilirsiniz.
# wasm-opt ile otomatik optimizasyon (Binaryen araç seti)
# Önce Binaryen'i kur
sudo apt-get install binaryen
# Optimizasyon seviyeleri
wasm-opt -O1 input.wasm -o output_O1.wasm # Basit optimizasyon
wasm-opt -O2 input.wasm -o output_O2.wasm # Orta seviye
wasm-opt -O3 input.wasm -o output_O3.wasm # Agresif optimizasyon
wasm-opt -Oz input.wasm -o output_Oz.wasm # Boyut optimizasyonu
# Boyut karşılaştırması yap
ls -lh input.wasm output_O*.wasm
# Optimizasyon öncesi ve sonrası performans testi
time wasmtime output_O1.wasm --invoke toplam_hesapla 1000000
time wasmtime output_O3.wasm --invoke toplam_hesapla 1000000
Edge Computing Bağlamında Güvenlik Konuları
Sistem yöneticisi perspektifinden WASM’ın en büyük avantajlarından biri sandbox güvenlik modelidir. Ama bu “sıfır risk” anlamına gelmez.
- Bellek sınırları: Her WASM modülü izole bellek alanında çalışır. Ancak modüle izin verilen maksimum bellek miktarını sınırlandırmak önemlidir. Wasmtime’da
--max-wasm-stackve bellek limiti parametrelerini kullanın. - CPU zaman sınırı: Kötü yazılmış veya kötü niyetli bir WASM modülü sonsuz döngüye girebilir. Fuel mekanizmasını veya işletim sistemi düzeyinde zaman sınırlarını devreye alın.
- Import güvenliği: Modülün hangi host fonksiyonlarına erişebildiğine dikkat edin. Sadece gerekli importları expose edin.
- WAT incelemesi: Güvenilmeyen kaynaklardan gelen WASM modüllerini wasm2wat ile WAT formatına çevirip incelemek, tersine mühendislik yapmanın en pratik yoludur.
# Wasmtime ile güvenli çalıştırma örneği
# Bellek limitini 50MB ile sınırla
wasmtime --max-memory-size 52428800 modul.wasm
# WASI yeteneklerini sınırla (sadece okuma izni ver)
wasmtime --dir=/tmp::/sandbox:readonly modul.wasm
# CPU fuel limiti ile çalıştır (Wasmtime API üzerinden)
# Bu özellik programatik API ile kullanılır
# Komut satırından epoch interruption aktif et
wasmtime --epoch-interruption modul.wasm
Sonuç
WAT öğrenmek, tıpkı assembly dili öğrenmek gibi, sizi daha iyi bir sistem yöneticisi yapar. Doğrudan WAT yazmak zorunda kalmayacaksınız çoğu zaman. Ama bir WASM modülünün neden beklenmedik davrandığını anlamak, edge ortamında performans sorunu yaşayan bir servisi debug etmek veya güvenilmeyen bir WASM binary’sini incelemek için bu bilgi paha biçilmez.
Özellikle vurgulamak istediğim pratik noktalar şunlar: wasm2wat aracını bir sistem yöneticisinin toolbox’ına mutlaka ekleyin. WASM modüllerini production’a almadan önce wasm-validate kontrolünü CI/CD pipeline’ına dahil edin. Wasmtime’ın fuel ve bellek limitlerini her zaman kullanın. Ve WAT’ın stack tabanlı çalışma mantığını kavradıktan sonra, bu paradigmanın ne kadar elegance bir tasarım olduğunu fark edeceksiniz.
Edge computing trendi devam ettikçe, WASM ve WAT bilgisi giderek daha fazla değer kazanacak. Cloudflare Workers’ta çalışan bir fonksiyonu optimize etmek, Kubernetes üzerinde Wasmtime ile güvenli workload çalıştırmak veya kendi özel runtime’ınızı oluşturmak için bu temellere ihtiyacınız olacak. Şimdi başlamak için daha iyi bir zaman yok.
