Bu ayki yazımda sizlere basit bir C programının geçirdiği evreleri anlatacağım. Bu aşamalar bir kodun derlenme evresinde başlar ve ekrana çıktıyı bastırana kadar sürer. Ben bu aşamaları tek tek inceleyip size aktarmaya çalışacağım.
Bir C kodu derlenirken 4 aşamadan geçer. Bunların ilki ön-işlemci (preprocessor) aşması, sonraki derlenme (compilation) aşaması, bir sonraki çevirme (assembly) aşaması ve sonuncusu da bağlama (linker) aşamasıdır. Şimdi bu evrelere tek tek göz gezdirelim. İlk önce basit bir C programı yazarak başlayalım. Açıklamaları yaparken hep bu kod üzerinde işlemler yapacağım.
#include <stdio.h>
int main()
{
printf(“Merhaba e-bergi okuyucuları :)”);
return 0;
}
Ön-işlemci (Preprocessor) Aşaması
Bu evrede C için ön-işlemci olan “cpp” bir C kodundaki '#' karakteriyle başlayan komutları modifiye eder. Örnegimizde kullandığımız “#include ” komutu ön-işlemciye “stdio.h” kütüphanesinin içeriğini okuyup, bunu programımıza eklemesini söyler. Sonuçta olaşan dosya “.i” uzantılı olur. Bu dosyayı şu komutla elde edebiliriz.
hande@hande-laptop:~/Desktop$ cpp hello.c > hello.i
Sonuçta oluşan dosya da şu şekildedir.
# 1 "hello.c"
# 1 ""
# 1 ""
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 28 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 330 "/usr/include/features.h" 3 4
...
...
typedef unsigned char __u_char;
typedef unsigned short int __u_short;
typedef unsigned int __u_int;
typedef unsigned long int __u_long;
typedef signed char __int8_t;
typedef unsigned char __uint8_t;
typedef signed short int __int16_t;
typedef unsigned short int __uint16_t;
...
...
# 912 "/usr/include/stdio.h" 3 4
# 2 "hello.c" 2
int main()
{
printf(“Merhaba e-bergi okuyucuları :)”);
return 0;
}
Derleme (Compilation) Aşaması
C derleyicisi olan “ccl” bir önceki aşamada oluşturduğumuz “.i” uzantılı dosyayı alır ve onu assembly dilinde yazılmış bir dosya haline getirir. Assembly diline dönüştürülmüş dosyadaki her bir komut aslında bir çeşit düşük-seviye (low-level) makine dili komutudur. Peki bu aşama neden gereklidir? Çünkü, bu sayede değişik dillerde yazılan, farklı derleyicilerde derlenen programlar ortak bir dile dönüştürülür. Mesela, C ve Fortran ile yazılan kodlar tamamen aynı assembly kodunu oluşturabilir. Şimdi de bizim kodumuzu assemblye dönüştürelim. Bunun için bir derleyici gereklidir ve örneğimizde gcc'yi kullanıyoruz.
hande@hande-laptop:~/Desktop$ gcc -Wall -S hello.i > hello.s
Ve oluşan dosya bu formattadır.
.file "hello.c"
.section .rodata
.align 4
.LC0:
.string "Merhaba e-bergi okuyucular\304\261 :)"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $4, %esp
movl $.LC0, (%esp)
call printf
movl $0, %eax
addl $4, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.2.4 (Ubuntu 4.2.4-1ubuntu3)"
.section .note.GNU-stack,"",@progbits
Çevirme (Assembler) Aşaması
Bu aşamada ise oluşturduğumuz “.s” uzantılı dosyamız makine koduna dönüştürülür. Sonuç olarak oluşan dosya “.o” uzantılıdır ve ikili formda oluşturulmuştur. Bu dosyayı herhangi bir editörle açarsak karşımıza anlamsız şeyler çıkar. Ama aslında içinde karakterler değil de, makine dili komutları bulunur. Bu dosyayı şu şekilde elde edebiliriz.
hande@hande-laptop:~/Desktop$ as hello.s -o hello.o
Açmak için ise şu komutu yazmamız yeterlidir.
hande@hande-laptop:~/Desktop$ od -a hello.o
Ve olaşan dosya şu şekildedir.
0000000 del E L F soh soh soh nul nul nul nul nul nul nul nul nul
0000020 soh nul etx nul soh nul nul nul nul nul nul nul nul nul nul nul
0000040 | nul nul nul nul nul nul nul 4 nul nul nul nul nul ( nul
0000060 vt nul bs nul cr L $ eot etx d p del q | U ht
0000100 e Q etx l eot G eot $ nul nul nul nul h | del del
0000120 del 8 nul nul nul nul etx D eot Y ] cr a | C nul
0000140 M e r h a b a sp e - b e r g i sp
0000160 o k u y u c u l a r D 1 sp : ) nul
0000200 nul G C C : sp ( G N U ) sp 4 . 2 .
0000220 4 sp ( U b u n t u sp 4 . 2 . 4 -
0000240 1 u b u n t u 3 ) nul nul . s y m t
0000260 a b nul . s t r t a b nul . s h s t
0000300 r t a b nul . r e l . t e x t nul .
0000320 d a t a nul . b s s nul . r o d a t
0000340 a nul . c o m m e n t nul . n o t e
0000360 . G N U - s t a c k nul nul nul nul nul nul
0000400 nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul
*
0000440 nul nul nul nul us nul nul nul soh nul nul nul ack nul nul nul
0000460 nul nul nul nul 4 nul nul nul + nul nul nul nul nul nul nul
0000500 nul nul nul nul eot nul nul nul nul nul nul nul esc nul nul nul
0000520 ht nul nul nul nul nul nul nul nul nul nul nul l etx nul nul
0000540 dle nul nul nul ht nul nul nul soh nul nul nul eot nul nul nul
0000560 bs nul nul nul % nul nul nul soh nul nul nul etx nul nul nul
0000600 nul nul nul nul ` nul nul nul nul nul nul nul nul nul nul nul
0000620 nul nul nul nul eot nul nul nul nul nul nul nul + nul nul nul
0000640 bs nul nul nul etx nul nul nul nul nul nul nul ` nul nul nul
0000660 nul nul nul nul nul nul nul nul nul nul nul nul eot nul nul nul
0000700 nul nul nul nul 0 nul nul nul soh nul nul nul stx nul nul nul
0000720 nul nul nul nul ` nul nul nul sp nul nul nul nul nul nul nul
...
0001340 etx nul soh nul nul nul nul nul nul nul nul nul nul nul nul nul
0001360 etx nul etx nul nul nul nul nul nul nul nul nul nul nul nul nul
0001400 etx nul eot nul nul nul nul nul nul nul nul nul nul nul nul nul
0001420 etx nul enq nul nul nul nul nul nul nul nul nul nul nul nul nul
0001440 etx nul bel nul nul nul nul nul nul nul nul nul nul nul nul nul
0001460 etx nul ack nul ht nul nul nul nul nul nul nul + nul nul nul
0001500 dc2 nul soh nul so nul nul nul nul nul nul nul nul nul nul nul
0001520 dle nul nul nul nul h e l l o . c nul m a i
0001540 n nul p r i n t f nul nul nul nul dc4 nul nul nul
0001560 soh enq nul nul em nul nul nul stx ht nul nul
0001574
Bağlama (Linker) Aşaması
Yukarıda yazdığım kodda, hatırlarsanız “printf” fonksiyonunu kullanmıştım. Bunun gibi C dilinde daha önceden tanımlanmış hazır fonksiyonlar var. Fakat bunların bir şekilde programımıza entegre edilmesi gerekiyor. İşte bu işlemi bu aşamada yapıyoruz. En son “.o” uzantılı hale gelen programımıza “printf.o” adlı dosyayı bağlıyoruz. Sonunda oluşan dosya artık bizim çalıştırılabilir dosyamız haline geliyor. Tabi ki, bir önceki aşamadaki gibi editörle içini açarsanız görecekleriniz size çok anlamsız gelecektir. Ama asıl neye benzediğini görmek istiyorsanız bir öncekinde kullandığımız komutla görebilirsiniz. Ben şimdi sizin için son aşamayı da gerçekleştirip, dosyanın son halini göstermek istiyorum.
hande@hande-laptop:~/Desktop$ gcc hello.o -o hello
Ve oluşan dosya hepsinden daha uzun ve daha karışık.
0000000 del E L F soh soh soh nul nul nul nul nul nul nul nul nul
0000020 stx nul etx nul soh nul nul nul p stx eot bs 4 nul nul nul
0000040 $ ff nul nul nul nul nul nul 4 nul sp nul bel nul ( nul
0000060 $ nul ! nul ack nul nul nul 4 nul nul nul 4 nul eot bs
0000100 4 nul eot bs ` nul nul nul ` nul nul nul enq nul nul nul
0000120 eot nul nul nul etx nul nul nul dc4 soh nul nul dc4 soh eot bs
0000140 dc4 soh eot bs dc3 nul nul nul dc3 nul nul nul eot nul nul nul
0000160 soh nul nul nul soh nul nul nul nul nul nul nul nul nul eot bs
0000200 nul nul eot bs eot eot nul nul eot eot nul nul enq nul nul nul
0000220 nul dle nul nul soh nul nul nul eot eot nul nul eot dc4 eot bs
0000240 eot dc4 eot bs ff soh nul nul dle soh nul nul ack nul nul nul
0000260 nul dle nul nul stx nul nul nul can eot nul nul can dc4 eot bs
0000300 can dc4 eot bs P nul nul nul P nul nul nul ack nul nul nul
0000320 eot nul nul nul eot nul nul nul ( soh nul nul ( soh eot bs
0000340 ( soh eot bs sp nul nul nul sp nul nul nul eot nul nul nul
0000360 eot nul nul nul Q e t d nul nul nul nul nul nul nul nul
0000400 nul nul nul nul nul nul nul nul nul nul nul nul ack nul nul nul
0000420 eot nul nul nul / l i b / l d - l i n u
0000440 x . s o . 2 nul nul eot nul nul nul dle nul nul nul
0000460 soh nul nul nul G N U nul nul nul nul nul stx nul ul nul
0000500 ack nul nul nul bs nul nul nul etx nul nul nul enq nul nul nul
0000520 soh nul nul nul stx nul nul nul etx nul nul nul nul nul nul nul
0000540 nul nul nul nul nul nul nul nul eot nul nul nul nul nul nul nul
0000560 stx nul nul nul eot nul nul nul soh nul nul nul enq nul nul nul
0000600 nul sp nul sp nul nul nul nul eot nul nul nul - K c @
...
...
0014240 p r i n t f @ @ G L I B C _ 2 .
0014260 0 nul _ _ b s s _ s t a r t nul _ e
0014300 n d nul _ e d a t a nul _ _ i 6 8 6
0014320 . g e t _ p c _ t h u n k . b x
0014340 nul m a i n nul _ i n i t nul
0014354
Sizin de gördüğünüz gibi yazdığımız 5 satırlık kod ne aşamalardan geçerek bu hale geldi. Sonuçta ise şu şekilde çalıştırabileceğimiz bir program elde ettik.
hande@hande-laptop:~/Desktop$ ./hello
Merhaba e-bergi okuyucuları :)
Programı çalıştırdığımızda donanımsal olarak hangi yollardan geçtiğine de bir göz atalım isterseniz. Konsolu ilk açtığımızda bizden bir komut yazmamızı bekler. Biz “./hello” komutunu verdiğimiz zaman, bu karakterler tek tek okunur ve hafızada depolanır. “Enter”a bastığımızda ise konsol artık bu komutun bittiğini anlar ve işlemlere başlar. İlk olarak çalıştırılabilir “hello” dosyasını diskten hafızaya kopyalar. Bundan sonra teker teker komutları uygulamaya başlar. Bu komutlar en sonunda “Merhaba e-bergi okuyucuları :)” cümlesini hafızadan yazmaçlara (register) taşır ve oradan da artık ekrana bastırılır.
Genel olarak bu basit C programının hangi aşamalardan geçtiğine değindim. Daha kompleks programların da çalışma aşamasına gelene kadar geçirdiği aşamalar genel olarak bu şekildedir. Umarım işinize yarayacak bir yazı olmuştur. Bir daha buluşana kadar mutlu kalın :)