Kaynak dosyasından binary dosyasına dönüşüm. Bir C programının derlenme aşamaları - 3 & 4 (Assembly & Linking)

Serbay Özkan
4 min readJul 29, 2020

--

Bir önceki yazımda Compilation aşamasından bahsetmiştim. Bu aşamada translation unit assembly koduna çevrilmekteydi. Bu yazımda da Assembly ve Linking sürecinden bahsedeceğim.

Assembly sürecinde girdi olarak assembly code (.s) alınıp çıktı olarak da machine code (.o veya .obj) üretilmektedir. Bu üretilen dosyaya Relocatable Object File ismi verilmektedir. Bütün işlemci mimarilerinin kendine özel assembler’i bulunmaktadır. Buradaki assembler aslında gcc’nin bir parçası olarak geliştirilmemiştir. Bir sistemde gcc yüklü olmasa bile assembler var olabilmektedir.

Assembly Süreci

Unix türevi işletim sistemlerinde as adında assembler aracı bulunmaktadır ve bu araç sayesinde assembly kodu relocatable object dosyasına dönüştürülmektedir. Diyelim ki ayni bilgisayar üzerinde iki tane farklı unix türevi işletim sistemi mevcut. Bu ikisinde yüklü olan assembler birbirinden farklı olabilir. Yani bu iki işletim sistemi aynı donanım üzerinde çalışsa da ürettikleri object dosyaları birbirinden farklı olabilmektedir.

Assembly süreci sonunda üretilen dosya çalıştırılabilir (executable) bir dosya değildir ve sadece makine seviyesinde talimatlar içermektedir.

Assembler aşamasının aşağıda belirttiğim üç farklı şekilde de başlatılabilmesi mümkündür. Bunlardan birincisi direk as aracı ile diğerleri de gcc’nin -c komut satır argümanı iledir. Kullanım olarak ikinci ve üçüncü seçenekler daha fazla önerilmektedir.

as debug.s -o debug.ogcc -c debug.cgcc -c debug.c -o debug.o

Yukarıda belirttiğim üç komut da aynı işlevi gerçekleştirmektedir ve sonunda debug.o adında relocatable object dosyası oluşmaktadır. Bu dosya artık okunabilir (human-readable) dosya değildir çünkü içerisinde text içerikler barındırmamaktadır ve yapısı da tamamen binary içeriğe dönüşmüştür. Şimdiye kadar bahsettiğim tüm aşamalar aslında tek bir kaynak (source) dosyasının derlenme aşamalarını içermekteydi. Eğer sistemde birden fazla kaynak dosyası mevcut ise aynı işlemler tüm kaynak dosyaları için tekrarlanmaktadır.

Elde edilen bu geçici object dosyasıyla oynamak isterseniz Linux’un nm, readelf ve objdump gibi yardımcı uygulamaları mevcut. Örneğin readelf utility ile istediğiniz relocatable object dosyası içerisinde yer alan sembol tablolarına erişebilmeniz mümkündür.

readelf -s debug.oSymbol table '.symtab' contains 13 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS debug.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 6
9: 0000000000000000 69 FUNC GLOBAL DEFAULT 1 main
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts

Ya da örneğin çoğu IDE’de disassembly penceresi bulunur. objdump ile istediğiniz relocatable object dosyasını disassembly edebilmeniz mümkündür.

objdump -d debug.odebug.o:     file format elf64-x86-64Disassembly of section .text:0000000000000000 <main>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # b <main+0xb>
b: b8 00 00 00 00 mov $0x0,%eax
10: e8 00 00 00 00 callq 15 <main+0x15>
15: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 1c <main+0x1c>
1c: e8 00 00 00 00 callq 21 <main+0x21>
21: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 28 <main+0x28>
28: b8 00 00 00 00 mov $0x0,%eax
2d: e8 00 00 00 00 callq 32 <main+0x32>
32: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 39 <main+0x39>
39: e8 00 00 00 00 callq 3e <main+0x3e>
3e: b8 00 00 00 00 mov $0x0,%eax
43: 5d pop %rbp
44: c3 retq

Daha önceden de belirttiğim gibi assembler aşamasında elde edilen dosya executable bir dosya değildir. Bu yüzden bu dosyayı nihai haline ulaştırabilmek için son aşama olan linking sürecinin de tamamlanması gerekmektedir.

Linking sürecinde girdi olarak relocatable object file (.o veya .obj) alınıp çıktı olarak da executable object file (.out veya .exe) üretilmektedir.

Linking Süreci

Unix türevi işletim sistemlerinde ld aracı varsayılan linker olarak görev yapmaktadır. Assembler ve linker compiler’dan bağımsız olarak da çalışabilmektedir.

Linker’in tam olarak görevi elimizde olan bir veya birden fazla relocatable object dosyalarını ve varsa statik kütüphaneleri (.a veya .lib) bir araya getirerek bize nihai bir çalıştırılabilir object dosyası üretmektir.

gcc -o debug.out debug.o

Yukarı komut satırı ile debug.o relocatable object dosyası debug.out adı ile executable bir dosyaya dönüştürülmektedir. Bu aşamada elimizde sadece tek bir .o dosyası değil birden fazla da dosya bulunabilirdi. Bu durumda da debug.o yanında diğer dosyaların ismi yazılarak bu komut satırı yine işletilebilirdi.

Bu aşamadan sonra artık debug.out adında çalıştılabilir bir dosya üretilmiştir. Komut satırından aşağıdaki gibi çalıştırıldığı zaman artık debug.c ve debug.h dosyalarımızın nihai çıktılarını da görebiliyor olacağız. İlgili komut ve çıktısı aşağıda belirtilmiştir.

./debug.outDEBUG: Logging is started
DEBUG: Program is started

Böylelikle bir C programının binary dosyaya dönüşene kadar ki serüveni bu noktada bitmiş bulunmaktadır.

Diğer yazılarımda görüşmek üzere…

--

--

No responses yet