Geçen bölümde initramfs imajı oluşturmayı ve test etmeyi anlatmıştık. Bu bölümde minimal kök dizin (rootfs) oluşturmayı ve static ve dynamic dosyalar arasındaki farkları anlatacağız.

Static ve Dynamic derleme

Static bir dosya herhangi bir bağımlılığa ihtiyaç duymazken dynamic derlenmiş bir dosyanın bağımlılıkları bulunur. Bir dosyanın dynamic olup olmadığını anlamak için ldd komutu kullanılır.

$ ldd busybox
	not a dynamic executable
$ ldd /bin/bash
	linux-vdso.so.1 (0x00007ffd63d6a000)
	libreadline.so.8 => /lib64/libreadline.so.8 (0x00007f6121182000)
	libtinfo.so.6 => /lib64/libtinfo.so.6 (0x00007f6121141000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f6120f71000)
	libtinfow.so.6 => /lib64/libtinfow.so.6 (0x00007f6120f30000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f61212d2000)

Bütün dynamic derlemiş dosyalar libc.so.6 dosyasına ve ld-linux-x86-64.so.2 interpreter dosyasına ihtiyaç duyar. Bunun dışında kalanlar uygulamanın kullandığı ek kütüphanelerden gelen bağımlılıklardır. Linux adğıtımlarındaki bağımlılık ağacı bunlar dikkate alınarak oluşturulur.

Şimdi aşağıdaki gibi bir C kaynak kodumuz olsun ve bu kaynak kodu derleyelim.

#include <stdio.h>
int main(){
    puts("Hello Word");
    return 0;
}
# dynamic derleyelim
gcc -o main.dynamic main.c
# static derleyelim
gcc -o main.static main.c -static

ldd komutu ile her ikisini de kontrol edebilirsiniz.

Static dosyaları boyutu daha yüksektir çünkü gerekli olan tüm kütüphaneler tek bir dosyaya eklendiği için boyutunun artmasına neden olur. Bu yüzden dağıtımlarda genellikle static yerine dynamic derleme tercih edilir.

Minimal rootfs oluşturma

Şimdi dynamic derlenmiş bir dosyamızdan rootfs oluşturalım. Bunun için öncelikle dosyamızı boş bir dizine kopyalayalım ve file komutunu kullanarak interpreter adını öğrenelim.

$ file main
main: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped

Burada /lib64/ld-linux-x86-64.so.2 dosyası bizim dizinimizde de aynı yerde bulunmalıdır. Bu sebeple lib64 dizini açalım ve içerisine kopyalayalım.

$ mkdir lib64
$ install /lib64/ld-linux-x86-64.so.2 lib64/

Linux dağıtımlarında /lib dizini bulunur. Bizdeki lib64 dizinini buraya sembolik bağ atabiliriz.

$ ln -s lib64 lib

Şimdi de ldd komutunu kullanarak bağımlılıklarını bulalım ve onları da kopyalayalım.

$ ldd main
	linux-vdso.so.1 (0x00007ffd1efe6000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f0af6a13000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f0af6bfe000)
# interpreter zaten kopyalandığı için ikinci kere kopyalamıyoruz.
# linux-vdso.so.1 sistemde dosya olarak bulunmaz. linux çekirdeğinden gelir. Onu da kopyalamıyoruz.
# Bu durumda sadece libc.so.6 dosyamızı kopyalamamız gerekiyor.
$ install /lib64/libc.so.6 lib64

Şimdi rootfs oluşturmuş olduk. Test etmek için chroot <rootfs> /main komutunu kullanabiliriz.

$ chroot . /main
Hello Word

Gördüğünüz gibi minimal rootfs elde ettik ve çalıştırdık. İstersek rootfs dizinimizi initramfs haline getirebiliriz. Derlediğimiz dosyayı /init dosyası yerine koyalım ve tekrar paketleyelim. Aşağıdaki gibi kernel panic hatası verecektir. Bunun sebebi sistemin ana sürecinin (pid 1) kapatılmamasının gerekliliğidir.

Kernel panic

Initramfs içerisindeki init linux dağıtımlarında sistemin kurulu olduğu bölümü bağlayıp içerisindeki servis yöneticisini başlatmak için kullanılır. Servis yöneticileri ise asla kapanmamak üzere tasarlanmıştır. Biz bu örneğimizdeki C kodunu aşağıdaki gibi değiştirerek kapanmasını engelleyebiliriz.

#include <stdio.h>
int main(){
    puts("Hello Word");
    while(1);
    return 0;
}

Şimdi tekrar derleyip initramfs imajımızı paketleyelim.

Linux çekirdeği ile Hello World yazdırma

Gördüğünüz gibi kodumuz çıkış yapmadığı için kernel panic hatası almıyoruz.