Laboratuvarlar / uygulama
"Derleme farklarını ve optimizasyon seviyelerini incelemek"
Üç derlemeyle optimizasyon farkını davranış değişikliğinden ayır.
Bu sayfada
- 1. Deney Kodunun Hazırlanması
- 2. Üç Farklı Optimizasyon ile Derleme
- 3. Disassembly Karşılaştırması (objdump)
- A. -O0 Seviyesi (Doğrudan Yığın Tabanlı, Yavaş)
- B. -O2 Seviyesi (Yazmaç Odaklı, Inlining)
- C. -Os Seviyesi (En Az Bayt, Kompakt Kod)
- 4. Analist Açısından Alınacak Dersler
- İncelemeyi Derinleştir
- Kaynaklar
Tersine mühendislik sırasında kaynak kodun mimarisi ile disassembler'da görülen makine kodu arasındaki en büyük sapma derleyici optimizasyonlarından kaynaklanır. Aynı C fonksiyonu, farklı derleme bayraklarıyla derlendiğinde tanınmaz derecede farklı montaj kodları üretebilir.
Bu laboratuvar; aynı C kaynak dosyasını üç farklı optimizasyon seviyesinde (-O0, -O2, -Os) derleyerek fonksiyon içi genişletme (inlining), döngü açma (loop unrolling), yazmaç tahsisi ve çerçeve işaretçisi (frame pointer) farklarını objdump ile adım adım analiz eder.
1. Deney Kodunun Hazırlanması#
Basit bir matematiksel hesaplama ve yardımcı fonksiyon içeren opt.c dosyası:
#include <stdio.h>
static int kare_al(int sayi) {
return sayi * sayi;
}
int seri_topla(int n) {
int toplam = 0;
for (int i = 0; i < n; i++) {
toplam += kare_al(i);
}
return toplam;
}
int main(void) {
int sonuc = seri_topla(10);
printf("Sonuc: %d\n", sonuc);
return 0;
}
2. Üç Farklı Optimizasyon ile Derleme#
Programı üç farklı bayrak setiyle derleyin:
# 1. Hata Ayıklama / Optimizasyonsuz Derleme:
gcc -Wall -Wextra -O0 -g opt.c -o opt_O0
# 2. Standart Üretim / Yüksek Hız Optimizasyonu:
gcc -Wall -Wextra -O2 opt.c -o opt_O2
# 3. Boyut Odaklı Optimizasyon (Gömülü Sistemler):
gcc -Wall -Wextra -Os opt.c -o opt_Os
Dosya ve kod bölümü boyutlarını size komutuyla karşılaştırın:
size opt_O0 opt_O2 opt_Os
Çıktı:
text data bss dec hex filename
2045 600 8 2653 a5d opt_O0
1685 592 8 2285 8ed opt_O2
1550 592 8 2150 866 opt_Os
3. Disassembly Karşılaştırması (objdump)#
Her üç dosyanın seri_topla fonksiyonunu objdump -d -M intel ile inceleyin:
A. -O0 Seviyesi (Doğrudan Yığın Tabanlı, Yavaş)#
objdump -d -M intel --no-show-raw-insn opt_O0 | grep -A 25 "<seri_topla>:"
Karakteristik özellikler:
push rbp; mov rbp, rspçerçeve işaretçisi kurulur.- Her değişken yerel yığında saklanır (
[rbp-0x4],[rbp-0x8]). - Her döngü adımında
kare_aliçin açık bircalltalimatı verilir. Değişkenler her seferinde bellekten okunup tekrar belleğe yazılır.
B. -O2 Seviyesi (Yazmaç Odaklı, Inlining)#
objdump -d -M intel --no-show-raw-insn opt_O2 | grep -A 25 "<seri_topla>:"
Karakteristik özellikler:
- Frame pointer atılmıştır (
-fomit-frame-pointer):push rbpyoktur;RBPgenel amaçlı bir yazmaç gibi kullanılır. - Inlining (İçiçe Genişletme):
kare_alfonksiyonuna hiçbircallyapılmaz! Çarpma işlemi (imul) doğrudan döngünün içine gömülmüştür. - Tüm sayaçlar ve ara toplamlar işlemci yazmaçlarında (
EAX,EDX,ECX) tutulur; RAM erişimi sıfıra indirilmiştir.
C. -Os Seviyesi (En Az Bayt, Kompakt Kod)#
objdump -d -M intel --no-show-raw-insn opt_Os | grep -A 20 "<seri_topla>:"
Karakteristik özellikler:
- Döngü açma (unrolling) kesinlikle yapılmaz.
- Talimat bayt uzunlukları minimum olan opkodlar tercih edilir (
xor eax, eaxvsmov eax, 0). - Fonksiyon hizalama boşlukları (
nopdolguları) en aza indirilir.
4. Analist Açısından Alınacak Dersler#
- Fonksiyon Yokluğu Yanılgısı: Ghidra veya IDA'da kaynak koddaki bir yardımcı fonksiyonun adını bulamıyorsanız, o fonksiyon silinmemiş;
-O2veya-O3tarafından üst fonksiyona inline edilmiş olabilir. - Yığın Değişkenleri: Tersine mühendislik yaparken yerel değişkenleri daima
[RBP-X]adresinde aramak-O0alışkanlığıdır. Optimize kodda değişkenler yaşam döngüleri boyunca yazmaçlar arasında yer değiştirir (register allocation).
İncelemeyi Derinleştir#
Sürümler arasındaki optimizasyon ve kod değişikliklerini grafiksel olarak karşılaştırmak için İkili fark analizi rehberini inceleyin. Çalışma anında işlemcinin en çok hangi fonksiyonlarda zaman harcadığını görmek için Perf ile yürütme analizi konusuna dönün.