Category Archives: C

ar ve binutils


Binutils

Binutils paketindeki programların büyük çoğunluğu, BFD, yani Binary File Descriptor kütüphanesini kullanır. Bu kütüphane, alt-seviyede manipülasyonlar yapmayı sağlar. Yine, çoğu program opcodes kütüphanesini kullanır. Bu kütüphane, makine komutlarını assemble ve disassemble etmeyi sağlar.

Binutils paketindeki kütüphaneler:

lifeinbeta@lifeinbeta:/usr/lib/ldscripts$ locate libbfd
/usr/lib/libbfd-2.20.1-system.20100303.so
lifeinbeta@lifeinbeta:/usr/lib/ldscripts$ locate libopcodes
/usr/lib/libopcodes-2.20.1-system.20100303.so

ar

ar utility, binutils‘deki utility‘lerden biridir.
Binutils‘in içeriği için, Linux From Scratch – Contents of Binutils‘e bakılabilir.

ar, Arşiv Kütüphaneleri/Statik Kütüphaneler üzerinde oluşturma, değiştirme, içindekileri çıkarma(extract) işlemleri yapmak için kullanılır.

man ar  

Description:(Kısaltılmıştır) `ar`, `s` *modifier*'ı ile birlikte kullanıldığında,
oluşturulacak olan arşivde bulunacak olan *relocatable object module*'lerin içinde bulunan *symbol*'lere bir *index* tablosu oluşturur.

Bir Object File‘ın Symbol tablosu, bir programın sembolik tanımlamalarını ve referanslarını locate ve relocate etmek için gerekli olan bilgileri tutar.

Bu index tablosunu listelemek için nm -sya da nm --print-armap kullanılır. Eğer bir arşiv dosyasında bu tablo yoksa, sadece bu tabloyu eklemek için ar utility‘nin içindeki ranlib kullanılabilir.

Şimdi, ar ile ilgili anlattıklarımın uygulamasını yapalım.

Arşiv Dosyasının – Statik kütüphanenin oluşturulması

lifeinbeta@lifeinbeta:~/CSWORKS/C/soexample$ ar -qcv libdummy.a countdown.o factorial.o
a - countdown.o  <- *Verbose* modda olduğumuz için,
a - factorial.o  <- yapılan işlem hakkında bilgiler veriliyor.
lifeinbeta@lifeinbeta:~/CSWORKS/C/soexample$ ls  <- libdummy.a gerçekten oluştu mu, görelim.
countdown.c  factorial.c  factorial.o  main    main.o
countdown.o  factorial.h  libdummy.a   main.c

Arşivdeki Index Tablosunun Görülmesi

lifeinbeta@lifeinbeta:~/CSWORKS/C/soexample$ nm -s libdummy.a

Archive index:  <- arşivdeki index table
countdown in countdown.o                  <- countdown.o'nun içinde countdown fonksiyonu var.
__i686.get_pc_thunk.bx in countdown.o     <- bu fonksiyonun ne yaptığını daha sonra açıklayacağım.[TODO]
countdown_by_adding in countdown.o        <- countdown.o'nun içinde countdown_by_adding fonksiyonu var.
countdown_with_modulo in countdown.o      <- countdown.o'nun içinde countdown_with_modulo fonksiyonu var.
fact in factorial.o                       <- factorial.o'nun içinde fact fonksiyonu var.
__i686.get_pc_thunk.bx in factorial.o     <- bu fonksiyonun ne yaptığını daha sonra açıklayacağım.[TODO]

countdown.o: <- Kütüphanedeki birinci parça, yani relocatable object module 1  <- aşağıda bu object modülündeki sembollere olan indexler gösteriliyor.
         U _GLOBAL_OFFSET_TABLE_    <- bir index entry ile GOT Tablosu belirtiliyor.
00000000 T __i686.get_pc_thunk.bx   <- bir index entry
00000000 T countdown                <- bir index entry ile countdown fonksiyonunu gösteren symbol belirtiliyor.
00000044 T countdown_by_adding      <- bir index entry ile countdown_by_adding fonksiyonunu gösteren symbol belirtiliyor.
0000008e T countdown_with_modulo    <- bir index entry ile countdown_with_modulo fonksiyonunu gösteren symbol belirtiliyor.
         U printf                   <- bir index entry ile printf fonksiyonunu gösteren symbol beliriliyor.

factorial.o: <- Kütüphanedeki ikinci parça, relocatable object module 2
         U _GLOBAL_OFFSET_TABLE_
00000000 T __i686.get_pc_thunk.bx
00000000 T fact

Yukarıda archive index table‘ı görüntüledik.

Index Table‘daki entry‘lerin object module‘ler içindeki symbol‘lere referans verdiğini söyledim. Peki, relocatable objecet file‘lardaki bu sembolleri görelim.

Relocatable Object Module/File‘lardaki Sembol Tablolarının Görülmesi

Bunun için objdump utility‘si, -t parametresi ile kullanılabilir.

lifeinbeta@lifeinbeta:~/CSWORKS/C/soexample$ objdump -t libdummy.a
In archive libdummy.a:

countdown.o:     file format elf32-i386

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 countdown.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .rodata    00000000 .rodata
00000000 l    d  .text.__i686.get_pc_thunk.bx   00000000 .text.__i686.get_pc_thunk.bx
00000000 l    d  .note.GNU-stack    00000000 .note.GNU-stack
00000000 l    d  .comment   00000000 .comment
00000000 l    d  .group 00000000 .group
00000000 g     F .text  00000044 countdown     <- bir sembol
00000000 g     F .text.__i686.get_pc_thunk.bx   00000000 .hidden __i686.get_pc_thunk.bx  <- bir sembol
00000000         *UND*  00000000 _GLOBAL_OFFSET_TABLE_  <- UND a dikkat!
00000000         *UND*  00000000 printf                 <- UND a dikkat!
00000044 g     F .text  0000004a countdown_by_adding  <- diğer bir sembol
0000008e g     F .text  00000054 countdown_with_modulo <- diğer bir sembol

factorial.o:     file format elf32-i386

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 factorial.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .text.__i686.get_pc_thunk.bx   00000000 .text.__i686.get_pc_thunk.bx
00000000 l    d  .note.GNU-stack    00000000 .note.GNU-stack
00000000 l    d  .comment   00000000 .comment
00000000 l    d  .group 00000000 .group
00000000 g     F .text  00000035 fact  <- bir sembol
00000000 g     F .text.__i686.get_pc_thunk.bx   00000000 .hidden __i686.get_pc_thunk.bx  <- diğer bir sembol
00000000         *UND*  00000000 _GLOBAL_OFFSET_TABLE   <- UND'a dikkat!

Görüldüğü gibi, her relocatable object module‘ün bir sembol tablosu var.

Arşiv başlıklarının Görülmesi

Ayrıca, binutils‘deki diğer bir utility olan objdump‘ı -a parametresi ile kullanarak arşiv dosyasındaki relocatable object module‘lerin adları ve formatlarını görebiliriz.

lifeinbeta@lifeinbeta:~/CSWORKS/C/soexample$ objdump -a libdummy.a
In archive libdummy.a:

countdown.o:     file format elf32-i386
rw-r--r-- 1000/1000   1460 Jun  5 00:04 2011 countdown.o

factorial.o:     file format elf32-i386
rw-r--r-- 1000/1000   1028 Jun  5 00:04 2011 factorial.o

Yukarıdaki, -a parametresi, --archive-headers, Displays archive header information anlamındadır.

ar utility‘sinin açıklamasına devam edelim…

Aşağıda, Synopsis‘i kısaltarak yazdım.

SYNOPSIS: ar [-]p[mod] archive member...

Buradaki parametreler:

p: çalıştırılacak olan işlemi belirtir. Burada, bunun için q‘yu kullandım.

mod: Yapılacak olan işlem hakkındaki detaylar belirtilir. Burada, bunun için c ve v‘yi kullandık.

-c: Create, aşiv dosyasını oluşturur.

-v: Verbose, işlem yapılırken verbose modunda yapılmasını ister. Yani, yapılan işlemler hakkında özet bilgi gösterilir.

-q: Quick Append, beliritilen member‘ları yani dosyaları, arşive ekle demek içindir.

Leave a comment

Filed under C, disassembling, gcc

C – Linker ve Nesne Dosyaları


Header Dosyalarının yerleri ve Search Path Ayarları yazısının girişinde sorduğum sorulardan;

  • Bir Header dosyasının belirttiği bir object file nasıl bulunur? Linker bunu nerede arar?

sorularını burada cevaplayacağım. Bunu yaparken, Linker ve Linker parametrelerinden söz edeceğim.

Object File

Stackoverflow‘da Why compile to an object file first? sorusuna verilen cevaplarda önce object code‘a derleme konusunda aşağıdaki noktalara değiniliyor.

Bu olaya, separate compilation denir.

  • Aynı anda, farklı object file‘larda birden fazla kişi çalışabilir.
  • Daha hızlı derleme, aynı dosyaları tekrar tekrar derlemene gerek yoktur.
  • object file‘lar farklı dillerden derlenerek oluşturulmuş olabilir. Daha sonra, Linker ile birleştirilebilirler.
  • separate compilation birçok sistem geneli library‘nin dağıtımını kolaylaştırır. Bunlar static ya da shared library‘ler olabilir. Mesela; işletim sistemi kütüphaneleri, dillerin standart kütüphaneleri, ya da üçüncü parti kütüphaneler.

Yukarıda anlatılanlardan, Make vs. Ant – Build araçları ile konfigürasyon yönetimi yazısında bahsetmiştim. Burada, object file‘ları birleştirmek Linker‘ın işidir.

gcc ile Link işlemini, çeşitli parametreler kullanarak yapabiliriz. Ayrıca, binutils den biri olan ld ile de bu işlemi linker adımı içerisinde yapabiliriz.

GCC ve Linker Parametreleri

Options for Linking‘de link aşamasında kullanılabilecek olan parametreler açıklanıyor.

gcc -c file1.c file2.c

Yukarıdaki yapıdaki -c, compile demektir, bu parametre Derleyicinin kullandığı bir parametredir.  Belirtilen kaynak kodların object filelarını oluşturur. linker bu parametreyi, -S ve -E parametrelerinden biriyle işlem yapıldığında görmezden gelir.

gcc -o exe_dosya file1.o file2.o

Yukarıdaki yapıda, -o (output) parametresi belirtildiği için, belirtilen object file‘lar, Linker tarafından Link edilerek executable oluşturulur. Dolayısıyla, Linker‘ın en temel parametresi, .o uzantılı object file‘dır. Ama çoğu zaman tek başına .o lar ile işlem yapılmaz. Kütüphaneler(shared->.so ya da static->.a) ile işlem yapılır.

C ile LIBC’yi Kullanmadan Program Yazmak yazısında bahsettiğim -nostdlib‘e de değinmek isterim.

gcc -nostdlib -o hello hello.c

-nostdlib Linker‘a, .c dosyası derlendikten sonra oluşan .o dosyasını libc kütüphanesiyle link etmemesini söylüyor. Yoksa bu .o, .c dosyasında #include <stdio.h>, #include <stdlib.h> ... ifadeleri belirtilmemiş bile olsa standart kütüphaneyle link edilirdi. Bu parametre ile .o dosyasına link edilmeyecek olan libc kütüphanesi,

/usr/lib/gcc/i486-linux-gnu/4.4/libgcc.a

dir.

Bununla birlikte hazır bakmışken libgcc ile ilgili başka nerelerde neler var onlara da bakalım.

lifeinbeta@lifeinbeta:/$ sudo find / -name libgcc*
/opt/lampp/lib/libgcc_s.so.1
/usr/share/lintian/overrides/libgcc1
/usr/share/doc/libgcc1
/usr/lib/ure/lib/libgcc3_uno.so
/usr/lib/gcc/i486-linux-gnu/4.4/libgcc_s.so
/usr/lib/gcc/i486-linux-gnu/4.4/libgcc.a
/usr/lib/gcc/i486-linux-gnu/4.4/libgcc_eh.a
/usr/lib/libgccpp.so.1
/usr/lib/libgccpp.so.1.0.2
/var/lib/dpkg/info/libgcc1.md5sums
/var/lib/dpkg/info/libgcc1.shlibs
/var/lib/dpkg/info/libgcc1.postrm
/var/lib/dpkg/info/libgcc1.symbols
/var/lib/dpkg/info/libgcc1.postinst
/var/lib/dpkg/info/libgcc1.list
/lib/libgcc_s.so.1

-llib: Linker, belirtilen object file‘ı, burada belirtilen library ile link eder.

UNIX’te bir kütüphanenin ismi, “liblibrary_name(.ar | .so)” şeklindedir. Link edilmesi istenen kütüphane, -llibname şeklinde değil, -lname şeklinde belirtilmelidir.

gcc parametreleri belirtilirken, -l parametresinin hangi sırada belirtildiği önemlidir. Eğer, foo.o -lname zoo.o şeklinde bir ifade varsa, zoo.o, name kütühanesindeki gerekli .o ile link edilmez. Çünkü, Linker, object file‘ları ve kütüphaneleri, gcc komutunda bunların belirtildiği noktaya gelince arar ve sadece o noktada işlem yapar. Yukarıda, -lname, bütün object file‘lardan sonra belirtilmiş olsaydı, Linker, önce object file‘ları işleyecekti sonra en sonda belirtilen kütüphaneyi arayacaktı ve bulduğunda bunu, önceden farkettiği object file‘lar ile link edebilecekti.

Peki, yukarıda sadece library ismini belirttik. UNIX üzerinde yüzlerce dizin olduğunu hesaba katarsak, Linker belirtilen kütüphaneyi hangi dizinde bulacak? Linker, gerekli olan bir kütüphaneyi, bir standart dizin listesindeki dizinlerde arar. Daha sonra bu kütüphanenin içinden gerekli olan object file‘ın nasıl alınarak nasıl link edildiğini Loaders yazısında anlatacağım.

Library Search Path = Link Path = Load Path

setting search paths‘te ve When should i set LD_LIBRARY_PATH‘te belirtildiği gibi;

Linker, kütüphaneleri sırasıyla /usr/lib/ ve /usr/local/lib/ dizinlerinde arar. Bu dizinler, link edilecek dosyaların aranması için kullanılan, varsayılan dizinlerdir. Bu dizinlere, library search path ya da link path ya da load path denir. Standart C Kütüphanesi olan libgcc, /usr/lib/ altında bulunur ve varsayılan olarak her zaman link edilir. Link işleminde kullanılması gereken harici bir kütüphanenin bu dizin listesine eklenmesi gerekir.

Load Path‘e yeni bir dizin, LD_LIBRARY_PATH ortam değişkeni aracılığıyla eklenebilir(Ancak bu, güvenlik nedeniyle, tavsiye edilmez). Bu değişken, varsayılan dizinleri tutmaz, sadece harici olarak eklenmiş dizinleri tutar. Ortam değişkenine eklemekle, yeni eklenen dizin başka link işlemlerinde de aranması için kalıcı hale getirilmiş olur. Sadece uygulamaya göre, Linker tarafından içinde kütüphane aranacak olan, dizin belirtmek için(tavsiye edilen) -Ldizin parametresi kullanılmalıdır.

Ancak, -L parametresiyle link path‘e her link işlemi için dizin belirtmek istemeyiz. Aynı şekilde, -l parametresiyle link edilmesini istediğimiz library‘leri belirtirken de tekrar tekrar bu işi yapmak istemeyiz. Bunlara alternatif olarak, makefile kullanabiliriz. Aşağıda örnek bir Makefile var.

CC = gcc -Wall
SOURCE = hello.c
FLAGS = -lglut
BINARY = hellogl

all:
        $(CC) $(SOURCE) -o $(BINARY) $(FLAGS)

clean:
        @echo Temizleniyor...
        @rm $(BINARY)
        @echo Temiz!

freeglut3 Kurulumu

Şimdi, freeglut3‘ü sistemimize yükleyelim.

Bunun için, OpenGL Utility Toolkit(freeglut3) ü kurduktan sonra aşağıdaki işlemi yaptım.

lifeinbeta@lifeinbeta:/$ sudo find / -name freeglut*
/usr/share/doc/freeglut3
/var/cache/apt/archives/freeglut3_2.6.0-0ubuntu2_i386.deb
/var/lib/dpkg/info/freeglut3.postinst
/var/lib/dpkg/info/freeglut3.list
/var/lib/dpkg/info/freeglut3.postrm
/var/lib/dpkg/info/freeglut3.shlibs
/var/lib/dpkg/info/freeglut3.md5sums

Henüz .h ve .so dosyları yok. Daha sonra, development libraries and headers for GLUT(freeglut-dev)i kurdum ve aşağıdaki işlemi yaptım.

lifeinbeta@lifeinbeta:/$ sudo find / -name freeglut*
/usr/share/doc/freeglut3
/usr/share/doc/freeglut3-dev
/usr/share/doc/freeglut3-dev/freeglut.html
/usr/share/doc/freeglut3-dev/freeglut_user_interface.html
/usr/share/doc/freeglut3-dev/freeglut_logo.png
/usr/include/GL/freeglut_std.h
/usr/include/GL/freeglut.h
/usr/include/GL/freeglut_ext.h
/var/cache/apt/archives/freeglut3_2.6.0-0ubuntu2_i386.deb
/var/cache/apt/archives/freeglut3-dev_2.6.0-0ubuntu2_i386.deb
/var/lib/dpkg/info/freeglut3-dev.list
/var/lib/dpkg/info/freeglut3.postinst
/var/lib/dpkg/info/freeglut3.list
/var/lib/dpkg/info/freeglut3.postrm
/var/lib/dpkg/info/freeglut3.shlibs
/var/lib/dpkg/info/freeglut3-dev.md5sums
/var/lib/dpkg/info/freeglut3.md5sums

Header dosyaları, /usr/include/ içinde /GL/ dizini oluşturularak buraya konuldu. Neden buraya konduğu konusunda bkz. Header dosyalarının yerleri ve Search Path ayarları. Ancak hala kütüphaneler ortada yok. development libraries … dediğine göre yüklenmiş olmalılar, ama bulamadım. 🙂 How to install freeglut?‘dan bakarak bu kütüphanelerin libglut isminde olduğunu gördüm.

Aşağıdaki işlemi yaptığımda bu dosyaları görebildim.

lifeinbeta@lifeinbeta:/$ sudo find / -name libglut*
/usr/share/doc/libglut3-dev
/usr/lib/libglut.so.3
/usr/lib/libglut.so
/usr/lib/libglut.so.3.9.0
/usr/lib/libglut.a
/var/cache/apt/archives/libglut3-dev_3.7-25_all.deb
/var/lib/dpkg/info/libglut3-dev.list
/var/lib/dpkg/info/libglut3-dev.md5sums

Linkler

Leave a comment

Filed under build, C, gcc, linker

C – Libc ve Runtime


ÖNEMLİ NOT: Bu yazı, alıntılara rağmen, kişisel yorum içermektedir. Bu yazıyı bunu dikkate alarak okuyun. Daha önceden bu konuyla ilgili okuduklarınızı ya da bildiklerinizi değerlendirmek için okumanız daha faydalı olacaktır. Böyle önemli bir konuda kimseyi yanıltmak istemem. Yazıyı okuduktan sonra eksik olan ya da yanlış yaptığım kısımlar için yapılacak yorumlara minnettar olacağım.

Stackoverflow‘da, What is the C Runtime sorunda;

Microsoft runtime library, Windows İşletim Sistemi için programlama yapabilmek için, fonksiyonlar sağlar. Bu fonksiyonlar, C ve C++ dilleri tarafından sağlanmayan birçok programlama işlevini otomatize eder(çağrılabilir halde sağlar).

Peki, mesela libcmt.lib nedir? Bu sağlanan fonksiyonlar hangileridir? C standard library‘sini, C Derleyicisinin bir bir parçası olarak biliyorum; o halde libcmt.lib, C standard library’sindeki fonksiyonların Win32 ortamında çalışabilir halde olan bir gerçeklemesi mi?

Öncelikle bu soruda belirtilen ifadelere bakalım. İlk paragrafta, Microsoft Runtime Library ile sağlandığı söylenen fonksiyonlar, işletim sistemi çağrılarıdır.

C Wrapper Fonksiyonları ve Sistem Çağrıları

Libc‘deki birtakım C fonksiyonları(IO fonksiyonları -> fopen(), open(), fdopen(), open64(), ...; Dizin fonksiyonları -> chdir(), rename(), rmdir(), mkdir(), ...; Diğer fonksiyonlar -> ... ) işletim sistemi çağrılarının Wrapper‘larıdır(BSD Bu fonksiyonların kullandıkları sistem çağrıları BSD UNIX için sırasıyla, IO sistem çağrıları için -> open; Dizin sistem çağrıları için; chdir, rename, rmdir, mkdir şeklindedir.).

  1. Wiki – Wrapper Library
  2. Wİki – System Call
  3. BSD UNIX System Calls List #Çok çok önemli 🙂
  4. Dosya Erişimi ve Dizin İşlemleri için Sistem Çağrıları
  5. UNIX Sistem Çağrılarını ve Libc Kütüphane Fonksiyonlarını Kullanmak

Soruya verilen cevaplardan;

libcmt

libcmt, C standart kütüphanesindeki fonksiyonların, Microsoft‘un C derleyicisi tarafından derlenip oluşturulmuş gerçeklemelerinden birisidir.

Microsoft‘un birçok libc gerçeklemesi(implementation) vardır. Bunlar;

  1. Her zaman statically linked olan single threaded gerçekleme, (libc.lib)
  2. Multi-threaded; (NOT:libcmt‘deki mt buradan gelir.)
    1. statically linked,(libcmt.lib)
    2. dynamically linked,(libcmt.dll)
  3. Bütün bu gerçeklemelerin debug ve release versiyonlarıdır.(libcmtd, libcmt)
C Standard Library API
libc(C Standart Kütüphanesi); bu kütüphanenin ister Win32 gerçeklemesinin olsun, ister UNIX gerçeklemesinin olsun ya da başka bir işletim sisteminin ve/veya donanımın(işlemcinin komut kümesi); bu kütüphaneyi gerçeklerken mutlaka gerçeklemeleri gereken standart fonksiyon arayüzleri(interface) kümesi tanımlar. Bu gerçeklemeleri yapanlar bu standart kütüphaneyi gerçeklemenin yanında ekstra fonksiyonlar da sağlar. Ayrıca bu vendor‘lar, sadece kendi Compiler programları tarafından kullanılması için birkaç fonksiyon daha tanımlayabilirler.

libcmt içerisinde gerçeklenmiş fonksiyonların listesini görebilmek için; Visual Studio Komut Satırında;

libcmt.dll nin bulunduğu dizine geçildikten sonra,
lib -list libcmt.lib
yazıldığında, bu kütüphanedeki fonksiyon gerçeklemelerinin bulunduğu dosyaların (object file -> .o) uzun listesini üretir. Bu listedeki isimler, C Standart Kütüphane API’sindeki fonksiyon isimlerine tamı tamına uymamakla birlikte, benzerler. Bir dosyada birden fazla fonksiyon gerçeklemesi olabilir. Bu yüzden, belli bir .o dosyasının içeriğine bakmak için, önce bu dosya açılır:
lib -extract
dosya açıldıktan sonra içindeki herhangi bir fonksiyon gerçeklemesine,
dumpbin /symbols
ile bakılabilir.

UNIX ortamları için kullanılan derleyicilerden birisi GNU C Compiler(gcc) dır. C standart kütüphanesinin bu derleyici ile gerçeklenmiş hali glibc dir.

Linux From Scratch‘ta(Adım-adım işlemlerle tamamen kaynak koddan kendine ait değiştirilmiş bir Linux sistemini oluşturmak.) glibc kısmında, glibc‘nin kurulumundan, ayarlarından, Dynamic Loader‘ın ayarlanmasından ve glibc‘nin içindeki dosyalardan bahsediliyor.

 

Derleyiciler 

  1. GNU C Compiler – GCC – Dökümantasyonu‘nda GCC ve bileşenleri hakkında bilgiler mevcut. GCC dökümantasyonunda Standard Libraries kısmıda, GNU C kütüphanesinin neredeyse tam olarak C99 standardını desteklediği belirtiliyor. Extensions to the C Language Family kısmında, GNU C, ISO Standard C‘de bulunmayan bir takım dil özellikleri sağlar deniliyor.
  2. lcc-Win32 A Compiler System for Windows: Windows için yapılmış, GCC’ye göre daha basit bir derleyici sistemi. Bu sistem bir Code Generator(Compiler, Assembler, Linker, Resource Compiler ve Librarian-Library Manager), IDE(editor, debugger, makefile generation, resource editor etc.) içerir.
  3. Pelles C: Windows ve Windows Mobile için bir complete development kit. Burada özellikler kısmında, C99 standardını desteklediği belirtiliyor. Yani, bu derleyici C99 specification‘unda belirtilen fonksiyonların gerçeklemelerini içeriyor.

Runtime ve Runtime Library

C ve C++ Runtime‘ları, fonksiyonlar kümesidir. .NET Runtime ise IL interpreter, garbage collector ve dahasıdır.
diye söyleniyor. Bu cevap çok açık değil. Hangi fonksiyonlar(C Runtime Library API‘sinde belirtilenler mi ve bunlardan başka hangileri) açıkça belirtilmiyor. Ayrıca, .NET library‘si denmiyor ve bu ifadeden .NET Runtime‘ının NET Runtime Library’sini içine aldığı ima ediyor. Buradan, Runtime‘ın Runtime Library‘i içerdiği sonucuna varabiliriz.

Diğer bir açıklamada Wikipediaruntime library/run-time system‘a atıf yapılarak,

Runtime library, derleyici tarafından kullanılan özel bir program kütüphanesidir. Bir programın çalışması anında, programlama diline yerleşik olan fonksiyonları(built-in) gerçeklemek için kullanılır. Bu fonksiyonlar; IO fonksiyonları ya da  Memory Management fonksiyonları gibi fonksiyonlardır.
diye söyleniyor. Bu cevap çok daha açık bir cevap. Özellikle, derleyici tarafından kullanılan özel bir program kütüphanesi açıklaması. C’de bir kaynak kod, object file‘a dönüştüğünde  gerçeklenmiştir. Bu işlem için libc kulanıldığına göre Runtime Library, Libc‘yi içerir.

NOT: Bununla birlikte, IO işlemleri ve Memory Management fonksiyonları için işletim sistemine yapılan sistem çağrıları kullanılır. Yukarıda, Microsoft Runtime Library‘nin sağladığı, C ve C++’da olmayan, fonksiyonların sistem çağrıları olduğunu söylemiştim.

Runtime-system(kısaca runtime da denir), belli bir programlama diliyle yazılmış programların çalışmasını desteklemek için tasarlanmış yazılımdır.

Runtime System, temel alt-seviye komutların gerçeklemelerini ayrıca yüksek seviye komutların gerçeklemelerini yapar ve type checkingdebugging, kod üretme ve optimizasyon sağlar. Runtime System‘in bazı servisleri programlama dilinin API‘si ile programcıyı sağlanır ama diğer servisler(task scheduling ve kaynak yönetimi gibi.)

Bu açıklama diğerlerine göre daha net bir açıklama. Çünkü;
Runtime‘ın o dilin derleyici yazılımını içine aldığı, çalıştırılabilir dosyayı çalıştırabilmek için işletim sisteminin birtakım araçlarının bir şekilde kullanıldığı belirtiliyor.

Bu noktadan sonra şu sonuçlara varabiliriz:

  1. Runtime = Runtime Library = Runtime System
  2. Runtime, İşletim Sistemi Çağrılarının bulunduğu işletim sistemi API‘si + Derleyicinin Kendi işlevleri için kullandığı kütüphane + Derleyici Programın kendi amacını yerine getirmek için kullandığı fonksiyonlar + Libc(Standart C Kütüphanesi) fonksiyonlarıdır.
  3. Yukarıdaki cümleden ve yazının genelindekilerden şunu çıkarabiliriz: Derleyici; kendi kütüphanesini, Libc‘yi, işletim sistemi API‘sini kullanarak çalıştırılabilir dosya oluşturur. Bu çalıştırılabilir dosya, işletim sisteminin Loader‘ı tarafından belleğe yüklenir ve işletim sistemi tarafından Process oluşturulur.

Çalıştırılabilir Dosya ve İşletim Sistemi

Stackoverflow‘da Windows ve Linux tarafından üretilmiş olan çalıştırılabilir dosyaların farklı yanları nelerdir? diye soruluyor. Soruda,

Linux‘ta oluşmuş olan bir çalıştırılabilir dosya neden Windows‘ta çalıştırılamıyor? Windows ve Linux‘ta ortak olan işletim sistemi çağrılarını kullanan bir C programı yaparsak, derleyici, Linux ve Windows için farklı binary‘ler mi üretir?

diye soruluyor. Sorunun cevabına geçmeden önce şunu söylemek isterim: Soruda zaten bir çalıştırılabilir dosyanın işletim sistemi tarafından üretildiği söyleniyor. Eğer bir çalıştırılabilir dosya farklı işletim sistemi tarafından üretilmişse, düz bir mantıkla düşünürsek, oluşan çalıştırılabilir dosyalar da farklı olacaktır. Bu ima, tam olarak doğru değil ki zaten sorunun açıklamasında, bunun derleyici tarafından oluşturulduğu belirtiliyor. Ancak, işletim sistemi tarafından  oluşturulmuş diye biraz genel bir tabir kullanılmasının sebebi, işletim sistemi çağrıları kullanılarak oluşturulmuş denmek istenmesidir. Farklı işletim sistemi denerek de, her işletim sistemine özgü farklı işletim sistemi çağrıları denmek isteniyor.

Sorunun cevaplarında,

En büyük problem, işletim sistemi API’leridir. Bir Windows çalıştırılabilir dosyasının process’i oluşturulurken, Windows API‘ı çağrılır. API‘deki fonksiyonlarla işletim sistemine yapılan çağrılar vasıtasıyla; stack ayarlanır, belli bir hafıza bölgesi bu process‘e tahsis edilir. Bir işletim sisteminin API’sindeki çağrılar da o işletim sistemine özgü olduğu için başka işletim sistemlerinde farklıdır. Bu yüzden, bir çalıştırılabilir dosya hem Windows’ta hem de Linux’ta çalışmaz.

Farklı Container formatları kullanırlar; çoğu UNIX çalıştırılabilir dosyası  ELF dosyalarıdır. Windows çalıştırılabilir dosyaları ve DLL‘ler ise PE dosyalarıdır.

diye söyleniyor. Bu işlemler Loader ile ilgilidir. Loader mevzusunu sonraki yazılarımdan birinde ele alacağım.

Güncelleme – Başlangıç: 31.05.2011

İşletim sistemi API’sinden bahsetmişken;

Linux From Scratch – Linux API Headers yazısında Linux API‘sini belirten Header dosyalarının; kısa özetleri, dahil oldukları mantıksal gruplar, bu grupların yerleri anlatılıyor.

Linux API Header‘ları,  glibc‘nin görebilmesini Linux Kernel‘ının API‘sini görebilmesini sağlar. Linux Kernel’ı, sistemin C kütüphanesi(glibc)nin kullanabilmesi için bir API sunar. Bunu, Linux Kernel kaynak kodundaki çeşitli Header dosyaları ile yapar.

Şimdi Linux kaynak kodundan, kullanıcının-görebildiği(user-visibleHeader dosyalarını çıkaralım ve test edelim. Bunlar, kaynak kodda bir yerel alt dizinde bulunur ve kurulumda olması gereken yere kopyalanırlar. Bu işlem esnasında kopyalamanın yapılacağı hedef dizinde önceden var olan dosyalar silinir. Ayrıca, burada kernel geliştiricilerinin kullandığı bazı gizli dosyalar vardır. Kopyalamanın yapılacağı kaynak dizinde, kopyalamadan önce bu dosyalar da silinir.

make headers_check 
make INSTALL_HDR_PATH=dest headers_install 
find dest/include \( -name .install -o -name ..install.cmd \) -delete 
cp -rv dest/include/* /usr/include 

Yukarıdaki son işlemde, hedef dizin

/usr/include

dir. Yani, Linux API Header dosyaları burada bulunur. Ancak, burada belli klasörler içinde mantıksal olarak gruplanmış halde bulunurlar.

Yüklenen Header dosyaları ve bulundukları dizinler:

/usr/include/asm/*.h, 
/usr/include/asm-generic/*.h, 
/usr/include/drm/*.h, 
/usr/include/linux/*.h, 
/usr/include/mtd/*.h, 
/usr/include/rdma/*.h, 
/usr/include/scsi/*.h, 
/usr/include/sound/*.h, 
/usr/include/video/*.h, 
/usr/include/xen/*.h

Yukarıdaki dizin yapısı,

/usr/include/linux/*.h The Linux API Linux Headers

gibidir.

Güncelleme – Bitiş: 31.05.2011

Stackoverflow‘da, Belirli bir işletim sistemine göre değil de belirli işlemci mimarisine göre, bir çalıştırılabilir dosyayı nasıl oluşturabilirim? diye soruluyor. Soruya verilen cevap, Container ve Loader kavramlarına çok iyi bir şekilde değiniyor:

Run” ne demek onun hakkında bir düşünelim… Oluşturulan binary kodları bir şey belleğe yüklemeli. Bu bir İşletim Sistemi özelliği. .exe ya da binary executable file(.exe‘nin UNIX eşdeğeri) ya da bundle ya da her neyse; bunların birçok özelliği işletim sistemine göre biçimlendirilmiştir ki bu sayede işletim sistemi onu belleğe yükleyebilir. I/O rutinleri, OS API‘sini encapsulate eden fonksiyonlardır. Yani OS her yerde.

Burada bahsedilen, çalıştırılabilir dosyaların işletim sistemine göre hazırlanmış formatları ELF ve PE‘dir. “Bu işletim sisteminin bir özelliği” denirken Loader kastediliyor. Bu formatları taslak yapılar olarak, çalıştırılabilir dosyaları da bunların içine koyulan yapılar olarak düşünürsek; bu formatlara Container diyebiliriz.

Eski günlerde, çalıştığımız bilgisayarlarda OS yoktu, C de yoktu. Assembler ve Linker’ları kullanarak büyük binary image‘lar oluşturuyorduk. Bu şekilde bunları makineye yükleyebiliyorduk.

Burada binary image kavramı, çalıştırılabilir dosya kavramının bir başka karşılığı. Container‘a binary image‘ı koyuyoruz diyebiliriz.

Kodu belleğe yükleyebilmek için front panel key‘leri kullanıyorduk ve yükleyeceğimiz kodu okurken punched paper-tape reader a benzeyen kullanışlı bir cihaz kullanıyorduk. Bu, boot linking loader yazılımını yüklerdi. Sonra, bu linking loader bellekte olduğunda tape‘e assembler ile veri yazabilirdik. Kendi device driver’larımızı yazdık ya da kaynak kod biçiminde, punched on paper tapes, kütüphane fonksiyonlarını kullandık. … Sonra, basit API’lere sahip olan basit OS’ler yazdık. Bu basit API‘ler device driver‘lar ve birkaç basit araç olan bir dosya sistemi(file system), bir editör ve bir derleyici yazdık. Bu derleyici Jovial isimli bir dil içindi.

Daha sonra işletim sistemin kullanmadan C++ kodu yazılabileceği belirtiliyor ve bunun için yapılması gerekenler sıralanıyor:
  1. İşlemci chipset‘inin parçaları olan BIOS ve BIOS‘a benzer yapılar hakkında bilgiler öğren. BIOS, ROM üzerine yazılmış olan POST(power-on self-test) işlemini yapan basit sürücüleri yükleyen ve boot block‘u yerleştiren küçük bir işletim sistemidir.
  2. Kendi boot block‘unu yazmayı öğren. Bu, POST işlemi yapıldıktan sonra yüklenecek olan ilk bildiğimiz anlamdaki yazılımdır. Bu şekilde donanım üzerinde kontrolü, işletim sistemine bırakmadan, ele alabilmiş olursunuz.
  3. GRUB, LILO ya da BootCamp‘ın işletim sistemini nasıl başlattığını öğren. Bu programlar yüklendiklerinde artık, kendi programını yükleyebilirsin. Bu durumda donanım üzerinde işletim sistemi değil senin programın çalışıyor olur.
  4. ELF hakkında oku.
  5. Device Driver‘ların nasıl yazıldığını öğren. Eğer işletim sistemi kullanmayacaksan, Device Driver yazmak zorunda kalacaksın.
Bütün bunlarla birlikte, programlama diline yerleşik herhangi bir fonksiyonu kullanmadan, sadece programlama dilinin yazım kuralında belirtilen ifadeleri kullanarak, C ile Standart Kütüphane olan LIBC’yi kullanmadan Program Yazmak yazısında belirttiğim gibi, program yazılabilir. Burada, libc ve işletim sistemi çağrıları kullanılmaz.

Leave a comment

Filed under C