farkmt2official 1
farkmt2official
kralhakan2009 1
kralhakan2009
Vahsi Uzman 1
Vahsi Uzman
Hikaye Ekle

linux/arch/i386/boot/compressed/head.S

  • Konuyu başlatan Konuyu başlatan badie
  • Başlangıç tarihi Başlangıç tarihi
  • Cevaplar Cevaplar 0
  • Görüntüleme Görüntüleme 258

Ayyıldız2 | 2008 TR Yapısı • 1-99 Orta Emek Destan • Oto Avsız • 10 Temmuz 21:00 HEMEN TIKLA!

Artık bvmlinux içindeyiz! misc.c:decompress_kernel()'in yardımıyla, çekirdek imgemiz linux/vmlinux'u elde etmek için piggy.o'yu açacağız.
Bu dosya saf 32 bitlik açılış kodudur. Önceki iki dosyadan farklı olarak kaynak kodun içinde hiç .code16 deyimi yoktur. Ayrıntılar için belgesine başvurun.
Sıkıştırılmış Çekirdeğin Açılması
Bölüt tanıml***cılarındaki (bölüt seçicisi __KERNEL_CS ve __KERNEL_DS'ye tekabül eden) bölüt taban adresleri 0'a eşittir; bu yüzden, eğer bu iki adresten her ikisi de kullanıldıysa, mantıksal konum adresi (bölüt:konum biçiminde) kendi doğrusal adresine eşit olacaktır. zImage için, CS:EIP şimdi 10:1000 mantıksal adresinde (doğrusal 0x1000 adresi), bzImage ise 10:100000 (doğrusal 0x100000) adresindedir.
Sayfalama etkinleştirilmediği için doğrusal adres fiziksel adres ile özdeştir. Adres konuları için IA-32 Manual (Vol.1. Ch.3.3. Memory Organization, and Vol.3. Ch.3. Protected-Mode Memory Management) ve belgelerine bakınız.
BX=0 ve ESI=INITSEG<<4 olması setup.S'den ileri gelir.

.text///////////////////////////////////////////////////////////////////////////////startup_32(){ cld; cli; DS = ES = FS = GS = __KERNEL_DS; SS:ESP = *stack_start; // user_stack[] sonu, misc.c içinde tanımlı // koru***ı kip etkinleştirildikten sonra // tüm bölüt kaydedicileri yeniden yüklenir // A20'nin gerçekten etkin olup olmadığını kontrol et EAX = 0; do {1: DS:[0] = ++EAX; } while (DS:[0x100000]==EAX); EFLAGS = 0; clear BSS; // _edata'dan _end'e struct moveparams mp; // subl $16,%esp if (!decompress_kernel(&mp, ESI)) { // AX'deki değeri döndür ESI'yi yığıttan geri yükle; EBX = 0; goto __KERNEL_CS:100000; // bkz. linux/arch/i386/kernel/head.S:startup_32 } /* * Yüksek yüklediysek buraya geliriz. * move-in-place rutinini aşağı 0x1000'e taşımamız gerekir * ve sonra yığıttan aldığımız yazmaçlardaki * tampon adresleri ile başlatırız. */3: move_rountine_start..move_routine_end 0x1000'e taşı; // move_routine_start & move_routine_end aşağıda tanımlanmıştır // move_routine_start() parametrelerini hazırla EBX = real mode pointer; // ESI değeri setup.S'den geçer ESI = mp.low_buffer_start; ECX = mp.lcount; EDX = mp.high_buffer_star; EAX = mp.hcount; EDI = 0x100000; cli; // kesme almadığımızdan emin ol. goto __KERNEL_CS:1000; // move_routine_start();}/* Eğer yüksek yüklediysek, yerinde çözülmüş çekirdeği taşımak için * yordam (şablon). Bu PIC kodu ol***ı! *////////////////////////////////////////////////////////////////////////////////move_routine_start(){ mp.low_buffer_start'ı 0x100000'a taşı, mp.lcount bayt, iki adımda: (lcount >> 2) kelime + (lcount & 3) bayt; move/append mp.high_buffer_start, ((mp.hcount + 3) >> 2) kelime // 1 kelime == 4 bayt, 32 bitlik kod/veri anlamında. ESI = EBX; // gerçek kip gösterici, setup.S'deki gibi EBX = 0; goto __KERNEL_CS:100000; // bkz. linux/arch/i386/kernel/head.S:startup_32()move_routine_end:}​

je 1b ve jnz 3f'nin anlamları için belgesine başvurunuz.
_edata ve _end tanımla***arını bulamadınız mı? Sorun değil, onlar "dahili ilintileme betiği" içinde tanımlanmıştır. -T (--script=) seçeneği belirtilmeksizin kullanılırsa, ld bu yerleşik betiği compressed/bvmlinux'u ilintilemek için kullanır. Bu betiği görüntülemek için "ld --verbose" komutunu kullanınız ya da bölümüne bakınız.
-T (--script=), -L (--library-path=) ve --verbose seçimlerinin tarifi için belgesine başvurunuz. Ayrıca "man ld" ve "info ld" de yardımcı olabilir.
piggy.o çözüldü ve kontrol __KERNEL_CS:100000'ye geçirildi, örn.
linux/arch/i386/kernel/head.S:startup_32(). Bakınız .

#define LOW_BUFFER_START 0x2000#define LOW_BUFFER_MAX 0x90000#define HEAP_SIZE 0x3000///////////////////////////////////////////////////////////////////////////////asmlinkage int decompress_kernel(struct moveparams *mv, void *rmode)|-- setup real_mode(=rmode), vidmem, vidport, lines and cols;|-- if (is_zImage) setup_nor***_output_buffer() {| output_data = 0x100000;| free_mem_end_ptr = real_mode;| } else (is_bzImage) setup_output_buffer_if_we_run_high(mv) {| output_data = LOW_BUFFER_START;| low_buffer_end = MIN(real_mode, LOW_BUFFER_MAX) & ~0xfff;| low_buffer_size = low_buffer_end - LOW_BUFFER_START;| free_mem_end_ptr = &end + HEAP_SIZE;| // get mv->low_buffer_start and mv->high_buffer_start| mv->low_buffer_start = LOW_BUFFER_START;| /* To make this program work, we must have| * high_buffer_start > &end+HEAP_SIZE;| * As we will move low_buffer from LOW_BUFFER_START to 0x100000| * (max low_buffer_size bytes) finally, we should have| * high_buffer_start > 0x100000+low_buffer_size; */| mv->high_buffer_start = high_buffer_start| = MAX(&end+HEAP_SIZE, 0x100000+low_buffer_size);| mv->hcount = 0 if (0x100000+low_buffer_size > &end+HEAP_SIZE);| = -1 if (0x100000+low_buffer_size <= &end+HEAP_SIZE);| /* mv->hcount==0 : we need not move high_buffer later,| * as it is already at 0x100000+low_buffer_size.| * Used by close_output_buffer_if_we_run_high() below. */| }|-- makecrc(); // create crc_32_tab[]| puts("Uncompressing Linux... ");|-- gunzip();| puts("Ok, booting the kernel.\n");|-- if (is_bzImage) close_output_buffer_if_we_run_high(mv) {| // get mv->lcount and mv->hcount| if (bytes_out > low_buffer_size) {| mv->lcount = low_buffer_size;| if (mv->hcount)| mv->hcount = bytes_out - low_buffer_size;| } else {| mv->lcount = bytes_out;| mv->hcount = 0;| }| }`-- return is_bzImage; // return value in AX​

end "dahili ilintileme betiği" içinde de tanımlanmıştır.
decompress_kernel() bir asmlinkage değiştiricisine sahiptir. linux/include/linux/linkage.h dosyasında:

#ifdef __cplusplus#define CPP_ASMLINKAGE extern "C"#else#define CPP_ASMLINKAGE#endif#if defined __i386__#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))#elif defined __ia64__#define asmlinkage CPP_ASMLINKAGE __attribute__((syscall_linkage))#else#define asmlinkage CPP_ASMLINKAGE#endif​

asmlinkage makrosu derleyiciyi yığıttaki tüm işlev argümanlarını aktarması için (bazı eniyileştirme yöntemleri bunu değiştirmeye çalışsa bile) zorlayacaktır. Ayrıntılar için (regparm) ve belgelerine bakınız.
gunzip()
decompress_kernel() sadece bzImage için sıkıştırılmış çekirdek imgesini düşük (output_data ile gösterilen) ve yüksek (high_buffer_start ile gösterilen) tamponlara açmak için linux/lib/inflate.c dosyasında tanımlı gunzip() -> inflate() çağrısını yapar.
gzip dosya biçimi içinde belirtilmiştir.
Tablo 3.6. gzip dosya biçimi
BileşenAçılımıBayt s***sıYorumuID1IDentification 1 (1. belirteç)131 (0x1f, \037)ID2IDentification 2 (2. belirteç)1139 (0x8b, \213)[ ]CMCompression Method (Sıkıştırm Yöntemi)18 - "deflate" sıkıştırma yöntemini gösterirFLGFLaGs (Seçenekler)1çoğu durumda 0MTIMEModification TIME (Değişiklik zamanı)4özgün dosyanın değişiklik zamanıXFLeXtra FLags (ek seçenekler)12 - sıkıştırıcı en yavaş algoritm***[ ] kullanır, azami sıkıştırma yaparOSOperating System (İşletim Sistemi)13 - Unixek alanlar--değişken uzunluk, alan FLG ile belirtilir[ ]sıkıştırılmış bloklar--değişken uzunlukCRC32-4sıkıştırılmamış verinin CRC değeriISIZEInput SIZE (Girdi uzunluğu))4sıkıştırılmamış girdi verisi boyunun 2^32 ile bölümünden kalan[ ] ID2 değeri gzip 0.5 için 158 (0x9e, \236) olabilir;

[ ] XFL değeri 4 olduğunda ise sıkıştırıcı en hızlı algoritm*** kullanacaktır.

[ ] FLG biti 0 olduğunda FTEXT, herhangi bir ek alan belirtmez.


Bu dosya biçimi bilgisini gzipli linux/vmlinux'un başlangıcını bulmak için kullanabiliriz.

[root@localhost boot]# hexdump -C /boot/vmlinuz-2.4.20-28.9 | grep '1f 8b 08 00'00004c50 1f 8b 08 00 01 f6 e1 3f 02 03 ec 5d 7d 74 14 55 |.......?...]}t.U|[root@localhost boot]# hexdump -C /boot/vmlinuz-2.4.20-28.9 -s 0x4c40 -n 6400004c40 00 80 0b 00 00 fc 21 00 68 00 00 00 1e 01 11 00 |......!.h.......|00004c50 1f 8b 08 00 01 f6 e1 3f 02 03 ec 5d 7d 74 14 55 |.......?...]}t.U|00004c60 96 7f d5 a9 d0 1d 4d ac 56 93 35 ac 01 3a 9c 6a |......M.V.5..:.j|00004c70 4d 46 5c d3 7b f8 48 36 c9 6c 84 f0 25 88 20 9f |MF\.{.H6.l..%. .|00004c80[root@localhost boot]# hexdump -C /boot/vmlinuz-2.4.20-28.9 | tail -n 400114d40 bd 77 66 da ce 6f 3d d6 33 5c 14 a2 9f 7e fa e9 |.wf..o=.3\...~..|00114d50 a7 9f 7e fa ff 57 3f 00 00 00 00 00 d8 bc ab ea |..~..W?.........|00114d60 44 5d 76 d1 fd 03 33 58 c2 f0 00 51 27 00 |D]v...3X...Q'.|00114d6e​

Yukarıdaki örnekte gzipli dosyanın 0x4c50 adresinde başladığını görebiliriz. "1f 8b 08 00"den önceki dört byte input_len'dir (küçük sonlu olarak 0x0011011e) ve 0x4c50+0x0011011e=0x114d6e değeri bzImage (/boot/vmlinuz-2.4.20-28.9) dosyasının boyuna eşittir.

static uch *inbuf; /* girdi tamponu */static unsigned insize = 0; /* inbuf içindeki geçerli baytlar*/static unsigned inptr = 0; /* inbuf içinde işlenecek sonraki baytın indisi *////////////////////////////////////////////////////////////////////////////////static int gunzip(void){ Girdi tamponunu {ID1, ID2, CM} için kontrol et, şöyle ol***ı: {0x1f, 0x8b, 0x08} (nor*** durum), veya {0x1f, 0x9e, 0x08} (gzip 0.5 için); FLG'yi (seçenek baytı) kontrol et, 1, 5, 6 ve 7. bitler atanma***ı; Ignore {MTIME, XFL, OS}; FLG biti 2,3 ve 4'e karşılık gelen seçimlik yapıları yönet; inflate(); // sıkıştırılmış blokları yönet Validate {CRC32, ISIZE};}​

linux/arch/i386/boot/compressed/misc.c içinde tanımlı get_byte() ilk defa çağırıldığında, girdi tamponunu inbuf=input_data ve insize=input_len olacak şekilde ayarlamak için fill_inbuf() işlevini çağırır. input_data ve input_len sembolleri piggy.o ilintileme betiğinde tanımlanmıştır. Bakınız .
inflate()

// misc.c içindeki bazı önemli tanımla***ar#define WSIZE 0x8000 /* Pencere boyutu en azından 32k ol***ı, * ve ikinin üssü ol***ı */static uch window[WSIZE]; /* Kayan pencere tamponu */static unsigned outcnt = 0; /* çıktı tamponundaki bayt s***sı */// linux/lib/inflate.c#define wp outcnt#define flush_output(w) (wp=(w),flush_window())STATIC unsigned long bb; /* bit tamponu */STATIC unsigned bk; /* bit tamponundaki bit s***sı */STATIC unsigned hufts; /* belleği kullanımı izlemek*/static long free_mem_ptr = (long)&end;///////////////////////////////////////////////////////////////////////////////STATIC int inflate(){ int e; /* son blok seçeneği */ int r; /* sonuç kod */ unsigned h; /* struct huft'un azami belleği */ void *ptr; wp = bb = bk = 0; // sıkıştırılmış blokları birer birer şişir (inflate) do { hufts = 0; gzip_mark() { ptr = free_mem_ptr; }; if ((r = inflate_block(&e)) != 0) { gzip_release() { free_mem_ptr = ptr; }; return r; } gzip_release() { free_mem_ptr = ptr; }; if (hufts > h) h = hufts; } while (!e); /* Çok fazla ileri bakm*** (lookahead) geri al. Sonraki okuma bayt hizalı * olacak böylece son anlamlı bayttaki kullanılmayan bitleri çıkarabileceğiz. */ while (bk >= 8) { bk -= 8; inptr--; } /* çıktı penceresini (window[0..outcnt-1]) çıktı verisine (output_data) yaz, * output_ptr/output_data, crc ve bytes_out'u da buna bağlı olarak güncelle * ve outcnt'yi 0'a ayarla. */ flush_output(wp); /* başarılı olduğunu döndür */ return 0;}​

free_mem_ptr dinamik bellek tahsisi için misc.c:***loc() içinde kullanılır. Sıkıştırılmış her bir bloğu şişirmeden önce, gzip_mark() free_mem_ptr değerini saklar. Şişirmeden sonra gzip_release() bu değeri geri yükleyecektir. Bu inflate_block() içinde ***rılan belleğin serbest bırakılma işlemidir.
dosyaları sıkıştırmak için Lempel-Ziv (LZ77) kodlamasını kullanır. Sıkıştırılmış veri biçimi içinde belirtilmiştir. inflate_block() bit düzeni olarak ele alınabilen sıkıştırılmış blokları şişirir.
Sıkıştırılmış her bir bloğun veri yapısı anahatlarıyla şöyledir:
BFINAL (1 bit) 0 - son blok değil 1 - son blokBTYPE (2 bit) 00 - sıkıştırma yok bayt sınırına kadar kalan bitler; LEN (2 bayt); NLEN (2 bayt, LEN'in tamaml***cısı); data (LEN bayt); 01 - düzeltilmiş Huffman kodu ile sıkıştırılmış { literal (7-9 bitleri, 256 hariç 0..287 kodunu temsil eder); // Bakınız RFC 1951, 3.2.6 paragrafındaki tablo. length (0-5 bitleri, literal > 256 ise 3..258 arasında bir uzunluktur); // Bkz. RFC 1951, 3.2.5 paragrafındaki 1. alfabe tablosu. data (literal < 256 ise literal baytlarının verileri); distance (literal == 257..285 ise 5 artı 0-13 ek bit, 1..32768 arasında bir mesafe belirtir; /* Bakınız RFC 1951, 3.2.5 paragrafındaki 2. alfabe tablosu, * 3.2.6 paragrafındaki deyim değil*/ /* Çıktı akımında "distance" bayt geri git * ve "length" baytı kopyala. */ }* // çok s***da örnek olabilir literal (7 bit, tümü 0, literal == 256, blok sonu belirtir); 10 - Dinamik Huffman koduyla sıkıştırılmış HLIT (5 bit, Literal/Length kodlarının s***sı - 257, 257-286); HDIST (5 bit, Distance kodlarının s***sı - 1, 1-32); HCLEN (4 bit, Code Length kodlarının s***sı - 4, 4 - 19); Code Length dizisi ((HCLEN+4)*3 bit) /* Aşağıdaki 2 alfabe tablosu, önceki Code Length dizisinden üretilen * Huffman kod çözme tablosu kullanılarık çözülecektir. */ Literal/Length alfabesi (HLIT+257 kod) Distance alfabesi (HDIST+1 kod) // Kod çözme tabloları bu alfabe tablolarından oluşturur. /* Aşağıdaki, farklı kod çözme tabloları kullanmak dışında düzeltilmiş * Huffman kodları kısmı ile benzerlik gösterir. */ { literal/length (değişken uzunluk, Literal/Length alfabesine bağımlı); data (literal < 256 ise literal baytlarının verisi); distance (literal == 257..285 ise değişken uzunlukta, Distance alfabesine bağımlı); }* // çok s***da örnek olabilir literal (literal değeri 256, blok sonu anlamında); 11 - reserved (hata)
Dikkat ederseniz Huffman kodları MSB'den başlarken, veri elemanları En-Değersiz-Bit'ten (Least-Significant Bit - LSB) başl***p En-Değerli-Bit'e (Most-Significant Bit - MSB) kadar byte'ları paketler. Ayrıca literal değerleri 286-287 ve distance kodları 30-31'in asla oluşmayacağına dikkat edin.
RFC 1951 ve yukarıdaki veri yapısı elinizdeyken inflate_block() işlevini anlamak çok da zor olmayacaktır. Huffman kodlaması ve alfabe tablosu üretimi için RFC 1951 içindeki ilgili paragraflara başvurunuz.
Daha fazla ayrıntı için linux/lib/inflate.c belgesine, gzip kaynak koduna (bir çok yorum satırı var) ve ilgili başvuru materyallerine bakınız
 

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)

Geri
Üst