Header Dosyalarının Yerleri ve Search Path Ayarları

Bu yazıda şu sorular üzerinden gideceğim:

  • C’de Header dosyalarının varsayılan yerleri neresidir. Kullanıcının header dosyaları nerelerde olabilir?
  • Header dosyaları nasıl bulunur?
  • Header dosyasının belirttiği object file nasıl bulunur?
  • Object File‘ların oluşturduğu kütüphaneler(.so, .dll) nasıl bulunur ve burada gerekli olan bir bileşen belleğe nasıl yüklenir?

Header dosyaları

GCC Dökümantasyonunda Libc – Using the Library – Header Files kısmından alıntı yaparak başlayalım:

C Programları tarafından kullanılan kütüphaneler iki kısımdan oluşur. Bunlar: tipleri, makroları, değişken ve fonksiyon bildirimlerini(declaration) içeren header dosyaları ile fonksiyonların ve değişkenlerin tanımlamalarını içeren(definition) gerçek kütüphane veya arşivlerdir.

GNU C Kütüphanesindeki bileşenleri kullanabilmek için, programların kaynak kodlarının, ilgili bileşene referans veren header dosyalarını içerdiğinden emin olmamız gerekir. Compiler, derleme aşamasında bu referansları işler. Program derlendikten sonra, Linker, bu referansları arşiv dosyalarının birinde bulunan gerçek tanımlamalar(.o dosyası) ile değiştirir(çözümler – resolve).

NOT: Yukarıdaki cümleden şunları çıkarabiliriz:

  • Header dosyalarının yerlerini bulan C Preprocessor programıdır. (Dolaylı olarak yaptığım bir çıkarım.)
  • Header dosyalarının belirttiği object file‘ların yerini bulup derlenecek olan kaynak kodun üreteceği object file ile birleştiren ise Linker programıdır.

GNU CPP Manual‘de Header Files kısmında yazılı açıklamalardan gidersek;

Header dosyalarının iki amacı vardır:

  • User Header Files, kullanıcının bir programını oluşturan kaynak kodlar arasındaki arayüzlerdir. #include "file" yapısı kullanılır.
  • System Header Files, işletim sistemi parçalarına arayüz belirtir. Bunları ekleyerek, sistem çağrısı yapabilmek ya da bu çağrılarla ilgili C Kütüphane bileşenlerini çağırabilmek için gerekli bildirim ve tanımlamaları kodumuzda belirtmiş oluruz. #include <file> yapısı kullanılır. file ismiyle belirtilen dosyayı, sistem dizinleri listesinde arar. Bu liste bir standarttır. Bu listeye yeni bir dizin eklemek için -I bayrağı kullanılabilir.

NOT: Burada bir listeden ve -I bayrağından(parametresinden) bahsediliyor. Böylelikle yazının başında sorduğum ilk iki soruya cevap almaya başladık.

Bu sistem dizinleri listesindeki dizinler ve -I bayrağı ile belirtilerek eklenen dizinler, search path kavramını ortaya çıkarır. Buna ileride değineceğim.

Devam edelim,

-I parametrenin nasıl kullanılacağını anlatan Invocation kısmına referans veriliyor. Burada,

Genelde, C Preprocessor‘unu özellikle çağırmazsınız; C Compiler‘ını çağırdığınızda, derleme aşamasına geçmeden önce zaten otomatik olarak çağrılır. Ancak, C Preprocessor‘unu bazen kendi başına çağırmak da gerekebilir.

diye söze giriliyor. Buradan, daha önce de belirttiğim gibi, header dosyalarının yerlerini C Preprocessor‘un bulduğunu anlarız. C Preprocessor‘unu çağırmak için cpp kullanılır.

Preprocessor‘u gcc ya da cpp ile çağırırsanız, öncelikle derleyici sürücüsü(compiler driver) çalıştırılır. Bu programın amacı, parametreler ile birlikte girilecek olan komutu, bu komutta belirtilen gerçek işi yapacak olan program çağrılarına dönüştürmektir.

cpp için belirtilen komut parametrelerinin hepsi gcc için de kullanılabilir ve aynı anlamdadır. Ancak, C Compiler‘ı, çıktı dosyasını(output file) belirtmek için farklı kurallar kullanır.
C Preprocessor‘ı iki argümana sahiptir. Bu argümanlar, sırasıyla infile ve outfile‘ı belirtir. infile‘da belirtilen dosyalar include dosyalarıdır. Bu dosyalar birleştirilerek belirtilen outfile‘a yazılır.

Parametre Yapısı

-I parametresinden sonra yazılacak olan argümanlar -Ifoo şeklinde birleştirilerek ya da -I foo şeklinde ayrı belirtilerek yazılabilir.
Birçok parametre birden fazla harften oluşabilir (-undef gibi). Bu nedenle ayrı parametreler(mesela -d -M) gruplanarak yazılamaz (-dM gibi).

Search Path

GNU GCC Manual Search Path‘te yazılı bilgilerden devam edelim:

GCC -dolaylı olarak cpp denmek isteniyor- , header dosyaları için çeşitli farklı yerleri arar.

Buradan sonra ilk iki sorunun cevabına daha çok yaklaşıyoruz;

Normal bir UNIX Sisteminde, aksi belirtilmedikçe, #include <file>‘da belirtilen bir file, aşağıdaki dizinlerde aranır:

/usr/local/include
*libdir*/gcc/*target*/version/include
/usr/*target*/include
/usr/include

Yani, bu dizinler header dosylarının varsayılan dizinleri, sistem dizinleridir.
GCC, kurulu olduğu sisteme göre derlenmek üzere ayarlanmıştır. Yukarıdaki, target, bu sistemi belirtir. Kullandığım sistemde, bu, i486-linux-gnu.
Yukarıdaki, libdir, Standard C Library’nin kurulu olduğu dizindir. Kullandığım sistemde(ubuntu), lifeinbeta@lifeinbeta:~$ locate /gcc diye aratarak /usr/lib/gcc‘de olduğunu buldum.

Search path ile ilgili parametreler öncelikle -I, -nostdinc, -include, -isystem ve -iquote dir. Bunlara sırayla bakalım:

  • -I *dir* ve -iquote *dir*: Bundan zaten bahsetmiştim. Kullanıcının kendi kütüphanelerini oluşturmak için yazdığı header dosyaları için belirtilir. Sistem dizinleri listesinden önce belirtilen dizinlerde arama yapılır. Bu, hem #include <file> hem de #include "file" ile belirtilen dosyalar için bu anlama gelirken; -iquote ile , adından da anlışılacağı gibi, kodda sadece #include "file" ile belirtilen dosyalar için belirtilen dizinde arama yapılır. #include <file> dosyaları için bu parametreyle search path‘e eklenen dizinde arama yapmaz.
  • -isystem *dir*: -I ile belirtilen dizinleri standart sistem dizinlerinden biriymiş gibi göstermek amacıyla kullanılır. GCC Manual System Headers kısmında açıklanan amaçlardan ve nedenlerden dolayı böyle bir şeye ihtiyaç duyulabilir. İşletim sistemi ve libc arasında arayüz belirten header dosyarı normal header dosyalarından farklıdır. GCC, standart sistem header‘larına farklı davranır.
  • -nostdinc: cpp‘ye Header dosylarının bulunduğu sistem dizinlerinde(varsayılan dizinler) arama yapmamasını söyler. Sadece -I ya da -iquote parametreleri ile belirtilen dizinlerde arama yapılır. Libc olmadan program yazmak yazısında -nostdlib den bahsetmiştim. Bu parametre Linker yani ld‘nin kullandığı bir parametreydi. Bu parametre ise daha cpp aşamasında iken libc‘nin include dosyalarının dikkate alınmamasını söylemek için kullanılır. Bu yüzden bu seçenek, işletim sistemi kernel‘ını derlerken kullanılabilir ya da standart C kütüphanesini kullanmayan programları yazarken kullanılabilir.
  • -include *file*: Kodda, #include "file" şeklinde yazılmış gibi .c dosyasının başına ekler. #include "file"‘da belirtilen file aranmaya başlanırken önce .c‘nin bulunduğu dizin aranır eğer burada bulunamazsa varsayılan dizinlere bakılır. Bu parametre ile önce varsayılan dizinlere bakılır sonra .c‘nin bulunduğu dizine bakılır.

Uygulama ve Açıklamalar

lifeinbeta@lifeinbeta:~$ cd /usr/lib/gcc
lifeinbeta@lifeinbeta:/usr/lib/gcc$ ls
aotcompile.py  aotcompile.pyc  classfile.py  classfile.pyc  i486-linux-gnu
lifeinbeta@lifeinbeta:/usr/lib/gcc$ cd i486-linux-gnu
lifeinbeta@lifeinbeta:/usr/lib/gcc/i486-linux-gnu$ ls
4.4  4.4.3
lifeinbeta@lifeinbeta:/usr/lib/gcc/i486-linux-gnu$ cd 4.4.3
lifeinbeta@lifeinbeta:/usr/lib/gcc/i486-linux-gnu/4.4.3$ ls
cc1          crtend.o       include        libgomp.a           libsupc++.a
cc1plus      crtendS.o      include-fixed  libgomp.so          SYSCALLS.c.X
collect2     crtfastmath.o  libgcc.a       libgomp.spec
crtbegin.o   crtprec32.o    libgcc_eh.a    libssp_nonshared.a
crtbeginS.o  crtprec64.o    libgcc_s.so    libstdc++.a
crtbeginT.o  crtprec80.o    libgcov.a      libstdc++.so
lifeinbeta@lifeinbeta:/usr/lib/gcc/i486-linux-gnu/4.4.3$ cd include
lifeinbeta@lifeinbeta:/usr/lib/gcc/i486-linux-gnu/4.4.3/include$ ls
ammintrin.h     float.h            mm_malloc.h  stdbool.h    wmmintrin.h
avxintrin.h     immintrin.h        nmmintrin.h  stddef.h     x86intrin.h
bmmintrin.h     iso646.h           omp.h        stdfix.h     xmmintrin.h
cpuid.h         mm3dnow.h          pmmintrin.h  tmmintrin.h
cross-stdarg.h  mmintrin-common.h  smmintrin.h  unwind.h
emmintrin.h     mmintrin.h         stdarg.h     varargs.h

Yukarıda, header dosyalarının bulunduğu standart sistem dizinlerinden biri olan *libdir*/gcc/*target*/version/include dizinin içeriği görünüyor. Buradaki header dosyaları, GCC – derleyici uygulamasının, kendi işlevleri için kullandığı kütüphanelere arayüz sağlayan dosyalardır. Burada ayrıca libc‘de standart olarak belirtilmeyen ama GNU‘nun libc‘yi gerçeklerken ek olarak koyduğu fonksiyonların gerçeklemelerine arayüz sağlayan header dosyaları da vardır. GCC, libc‘nin bütün header’larını kullanabilir. Detaylı bilgi için buraya bakılabilir.

lifeinbeta@lifeinbeta:/usr/local/include$ ls
lifeinbeta@lifeinbeta:/usr/local/include$ cd /usr
lifeinbeta@lifeinbeta:/usr$ ls
bin  games  include  lib  lib64  local  sbin  share  src
lifeinbeta@lifeinbeta:/usr$ cd include
lifeinbeta@lifeinbeta:/usr/include$ ls
aio.h           expat_external.h  libexslt         nl_types.h   sysexits.h
aliases.h       expat.h           libgen.h         nss.h        syslog.h
...
err.h           jpegint.h         netinet          stdio.h      xen
errno.h         jpeglib.h         netipx           stdlib.h     xlocale.h
error.h         langinfo.h        netiucv          string.h     xorg
...
...
...

Yukarıda, bahsettiğim standart sistem dizinlerinden sadece /usr/include da header dosyaları bulundu. Bu dosyaların bir özeti gösteriliyor.

Bu yazı yine uzadı gitti 🙂 Neyse ki markdown‘la kolayca yazabildim. 😛 Böylelikle, yıllarca(!) aklımın bir köşesinde takılı kalan bazı noktaları temizledim. Ayrıca, bu yazı sadece hobi olarak yazılmış bir yazı değil. Bir Nerd işi de değil. Sadece, herhangi büyük bir C uygulaması ile uğraşırken ya da harici bir C kütüphanesini kullanırken kafam rahat olsun diye 🙂 yazdığım bir yazı. Neyse, son iki soru kaldı. Onları bir sonraki yazımda, Linker‘dan bahsederken cevaplayacağım.

GCC Ortam Değişkenleri

CPATH
C_INCLUDE_PATH
CPLUS_INCLUDE_PATH
OBJC_INCLUDE_PATH

Yukarıdaki Ortam Değişkenlerinin değeri, terminalden;

lifeinbeta@lifeinbeta:~$ echo $C_INCLUDE_PATH

lifeinbeta@lifeinbeta:~$ echo $CPATH

lifeinbeta@lifeinbeta:~$ echo $OBJC_INCLUDE_PATH

şeklinde yazılarak görülebilir.


LİNKLER

Advertisements

1 Comment

Filed under gcc, linking

One response to “Header Dosyalarının Yerleri ve Search Path Ayarları

  1. Pingback: C – Linker ve Nesne Dosyaları | Taha Yavuz Bodur weblogging..

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s