Linux - Syscalls. Linux'ta sistem çağrıları
Bu malzeme, "Sistem Yöneticisi" dergisinde yayınlanan Vladimir Meshkov tarafından makalenin makalesinin bir modifikasyonudur.
Bu matreal, Zhuranala "Sistem Yöneticisi" ile Vladimir Meshkov makalelerinin kopyalarıdır. Bu makaleler aşağıdaki bağlantılara göre bulunabilir. Programların kaynak metinlerinin bazı örnekleri de değiştirildi, geliştirildi, değiştirildi. (Örnek 4.2, biraz farklı bir sistem çağrısına girmem gerektiğinden) URL'ler: http://www.samag.ru/img/uploaded/p.pdf http://www.samag.ru/img/uploaded / A3. PDF.
Sorularım var? Sonra buradayım: [E-posta Korumalı]
- 2. İndirilebilir çekirdek modülü
- 4. LKM'ye dayalı sistem çağrısının görüşü örnekleri
- 4.1 Katalog oluşturmanın yasaklanması
1. Linux mimarisinde genel Wang
En yaygın dalga, iki seviyeli bir sistem modeli görmenizi sağlar. çekirdek<=> Merkezdeki progs (solda) sistemin çekirdeğini buldu. Çekirdek doğrudan, bilgisayarın bir donanım parçası ile etkileşime girer, uygulamalı programları mimarlığın özelliklerinden izole etmek. Çekirdek, uygulamalar tarafından sağlanan bir dizi hizmete sahiptir. Çekirdek hizmetleri arasında giriş / çıkış işlemlerini (dosyaları açın, okuyun, yazın ve yönetin), süreçleri oluşturma ve yönetme, senkronizasyon ve işleme etkileşimi. Tüm uygulamalar sistem çağrıları aracılığıyla çekirdek hizmetleri istemek.İkinci seviye, Linux kullanıcı arayüzünü sağlayan hem sistemik, sistem işlevselliğini ve uygulamasını tanımlayan uygulamalar veya görevlerdir. Bununla birlikte, uygulamaların dış heterojenliğine rağmen, çekirdeğe olan etkileşim şemaları aynıdır.
Çekirdekle etkileşim, standart sistem arama arayüzünden oluşur. Sistem arama arayüzü bir dizi çekirdek hizmettir ve Hizmetler İsteklerinin formatını tanımlar. İşlem, hizmetin sistem aracılığıyla, belirli bir çekirdek prosedürünü, harici olarak günlük olarak kütüphane işlevine benzer şekilde benzer şekilde çağırır. İşlem adına çekirdek bir istek yerine getirir ve gerekli verileri sürece geri döndürür.
Örnekte, program dosyayı açar, verileri ondan okur ve bu dosyayı kapatır. Aynı zamanda, açılış işlemi (açık), okuma (okunur) ve dosyanın kapanması, görevin talebi üzerine çekirdek tarafından yapılır ve (2) işlevi, okuma (2) ve kapanır (2) ) İşlev sistem çağrılarıdır.
/ * Kaynak 1.0 * / #include
- eAX Kayıt, sistem arama numarasıdır. Bu nedenle, bizim durumumuz için, sistem arama numarası 5'tir (bkz. __NR_OPEN).
- eBX kaydı ilk fonksiyon parametresidir (açık () için açılan dosyanın adını içeren bir dize için bir işaretçidir.
- eCX Kayıtlarında - İkinci parametre (dosya erişim hakları)
Doğru yolda olduğumuzdan emin olmak için, LIBC sistem kitaplığındaki açık () işlev kodunu göz önünde bulundurun:
# Gdb -q / lib/libc.so.6 (GDB) Montajcı Dökümü Açık Döküm İçin kod İşlev Açık: 0x000C8080
Ve şimdi sistem arama mekanizmasının değerlendirilmesine geri dönelim. Böylece, çekirdek bir kesme işleyicisi 0x80 - System_Call işlevi çağırır. System_Call, Save_all makroyu kullanarak STEK'te arama parametrelerini içeren kayıtların kopyalarını yerleştirir ve arama komutu istenen sistem fonksiyonuna neden olur. Sistem çağrılarını uygulayan çekirdek fonksiyonlarının işaretçi tablosu SYS_CALL_TABLE Dizisinde bulunur (bkz. Arch / I386 / Kernel / Giriş.s). EAX kaydında bulunan sistem arama numarası bu dizideki bir dizintir. Böylece, EAX'te 5 değeri varsa, SYS_OPEN () çekirdek işlevi neden olur. Neden bir makro save_all ihtiyacınız var? Buradaki açıklama çok basittir. Neredeyse tüm çekirdek sistemi işlevleri C'ye yazıldığından, yığındaki parametrelerini arıyorlar. Ve parametreler, Save_all kullanarak yığın üzerine yerleştirilir! Sistem çağrısı tarafından döndürülen değer EAX kaydında saklanır.
Şimdi sistem çağrısının nasıl kesileceğini öğrenelim. Bu yüklü çekirdek modüllerinin bu mekanizmasında bize yardımcı olacaktır.
2. İndirilebilir çekirdek modülü
Downloadlanabilir çekirdek modülü (genellikle LKM yüklenebilir çekirdek modülünün kabul edildi) - çekirdek alanında gerçekleştirilen program kodu. Ana özellik LKM, tüm sistemi yeniden başlatmaya veya çekirdeğin yeniden derlenmesi gerekmeden dinamik yükleme ve boşaltma yeteneğidir.Her LKM iki temel işlevden oluşur (minimum):
- modül başlatma özelliği. Bellekte LKM yüklerken çağrılır: int init_module (geçersiz) (...)
- modül boşaltma işlevi: Void CleanUp_Module (boşluk) (...)
3. LKM'ye dayalı olarak ele geçirilen sistem çağrısı için algoritma
Bir modülü uygulamak, bir sistem çağrısını yakalamak için, önleme algoritmasını belirlemek gerekir. Algoritma aşağıdakilerdir:- İşaretçiyi restore etmek için orijinal (kaynak) meydan okumaya kaydet
- yeni bir sistem araması uygulayan bir özellik oluşturun
- sYS_CALL_Table sistem çağrısı tablosunda, yedek bir arama yapın, yani karşılık gelen işaretçiyi yeni bir sistem çağrısına yapılandırın
- İşin sonunda (modülü boşaltırken), daha önce kaydedilmiş bir işaretçi kullanarak orijinal sistem çağrısını geri yükleyin.
4. LKM'ye dayalı sistem çağrısının görüşü örnekleri
4.1 Katalog oluşturmanın yasaklanması
Bir dizin oluştururken, SYS_MKDIR çekirdek işlevi denir. Bir parametre olarak, dizinin oluşturduğu dizinin adıyla yer alan dize. İlgili sistem çağrısını engelleyen kodu düşünün. / * Kaynak 4.1 * / #include4.2 Katalogdaki dosya girişini gizleme
Kataloğun içeriğini okumaktan hangi sistem çağrısının sorumlu olduğunu tanımlıyoruz. Bunu yapmak için, geçerli dizinle ilgilenen başka bir test parçası yazın: / * Kaynak 4.2.1 * / #include- d_Reclen - Yazma boyutu
- d_NAME - Dosya Adı
5. Doğrudan Erişim Yöntemi / Dev / KMem Doğrudan Erişim Yöntemi
Önce teorik olarak, çekirdeğin adres alanına doğrudan erişim yöntemini nasıl durdurun ve ardından pratik uygulamaya devam edin.Çekirdek adres alanına doğrudan erişim, cihaz / dev / kmem dosyasını sağlar. Bu dosya, takaslar (takas alanı) dahil olmak üzere tüm mevcut sanal adres alanını görüntüler. KMem dosyasıyla çalışmak için standart sistem fonksiyonları kullanılır - açık (), okuma (), yaz (). Standart yöntemi / dev / KMem'i açmak, bu dosyada bir vardiya olarak ayarlayarak sistemdeki herhangi bir adreye başvurabiliriz. Bu yöntem Silvio Cesare tarafından geliştirilmiştir.
Sistem fonksiyonlarına, fonksiyonun parametrelerini işlemci kayıtlarına ve ardından 0x80 yazılımının kesilmesine yapılan aramayı yükleyerek gerçekleştirilir. Bu kesintinin işleyicisi, System_call işlevi, Arama parametrelerini STEK'te yer alır, SYS_CALL_TABLE tablosundan denilen sistem işlevinin adresini alır ve kontrolü bu adrese aktarır.
Çekirdeğin adres alanına tam erişime sahip olmak, sistem arama tablosunun tüm içeriğini alabiliriz, yani. Tüm sistem fonksiyonlarının adresleri. Herhangi bir sistem mücadelesinin adresini değiştirerek, böylelikle görüşmeyi uygulayarak. Ancak bunun için, masanın adresini veya başka bir deyişle, / dev / KMem dosyasındaki ofset, bu tablonun bulunduğu ofset bilmeniz gerekir.
SYS_CALL_TABLE tablosunun adresini belirlemek için önce System_Call işlevinin adresini hesaplamanız gerekir. Bu özellik kesme işleyicisi olduğundan, kesintilerin korumalı modda nasıl işlendiğine bakalım.
Gerçek modda, kesintiye kaydolurken işlemci, her zaman hafızanın en başında olan ve kesme işlem programlarının arama adreslerini içeren kesme vektörleri tablosu olarak adlandırılır. Korunan modda, analog kesme vektörleri tablosu, güvenli mod işletim sisteminde bulunan kesme tanımlayıcı tablosu (IDT, Kesinti Tanımlayıcı Tablosu). İşlemcinin bu tablonun ile iletişime geçmesi için, adresi IDTR kaydına indirilmelidir (kesme tanımlayıcısı tablosu kaydı, kesme tanımlayıcı tablo kaydı). IDT tablosu, özellikle adreslerinin dahil olduğu kesintisiz işleyicilerin yapıcılarını içerir. Bu tanımlayıcıların ağ geçitleri (valfler) denir. İşlemci, kesmeyi kaydetme, ağ geçidi IDT'den IDT'den alınır, işleyicinin adresini belirler ve kontrolüne iletir.
IDT tablosundan System_call işlevinin adresini hesaplamak için, Intrutt Gateway Int $ 0x80'i ve bundan itibaren - karşılık gelen işleyicinin adresi, yani System_call işlevinin adresi. System_call işlevinde, System_Call_Table tablosuna erişim, ARAMA komutu tarafından gerçekleştirilir.<адрес_таблицы>(,% Ex, 4). / Dev / kmem dosyasında bu komutun bir görünümünü (imzası) bulduktan sonra, sistem arama tablosunun adresini buluruz.
Görünümünü belirlemek için, hata ayıklayıcısını kullanın ve System_Call işlevini sökünüz:
# GDB -Q / USR / SRC / Linux / Vmlinux (GDB) Disas System_Call Montajcı Kodunun Dökümü Fonksiyon System_Call: 0xC0194CBC
Durdurma işlemini gerçekleştiren bir psödokodu düşünün:
Readaddr (old_syscall, scr + sys_call * 4, 4); Writeaddr (NEW_SYSCALL, SCR + SYS_CALL * 4, 4); ReadAddr özelliği, sistem çağrısının adresini sistem çağrısı tablosundan okur ve Old_SyScall değişkenine kaydeder. Sys_call_table tablosundaki her giriş 4 bayt kaplar. İstenilen adres, SCT + SYS_CALL * 4 Flakon / Dev / KMem'deki ofsette bulunur (burada SCT - SYS_CALL_TABLE tablo adres adresi, SYS_CALL - sistem çağrısının sırası numarası). WRiteaddr fonksiyonu, SYS_CALL adresinin yeni_syscall adresinin adresini yazar ve SYS_CALL sistem çağrısına olan tüm erişim bu işlev tarafından hizmet verilecektir.
Bak, her şey basit ve hedef elde edildi. Ancak, kullanıcının adres alanında çalıştığımızı hatırlayalım. Bu adres alanına yeni bir sistem işlevi yerleştirirseniz, bu işlevi ararken, güzel bir hata mesajı alacağız. Bu nedenle çıktı - Yeni bir sistem çağrısı, çekirdeğin adres alanına yerleştirilmelidir. Bunu yapmak için gereklidir: Kernel alanında bir bellek bloğu alın, bu blokta yeni bir sistem çağrısı yapın.
Kumsalloc işlevini kullanarak çekirdek alanındaki belleği seçin. Ancak, çekirdeğin doğrudan fonksiyonuna neden olmak için, kullanıcının adres alanından gelen adres alanından aşağıdaki algoritmayı kullanmak için kullanılamaz:
- sYS_CALL_TABLE tablo adresini bilmek, bazı sistem çağrısının adresini alıyoruz (örneğin, SYS_MKDIR)
- kMALLOC işlevine hitap eden işlevi belirleyin. Bu özellik, çekirdeğin adres alanındaki bellek bloğuna bir işaretçi döndürür. Bu işlevi arayalım Get_Kmalloc
- İlk n bayt sistemini kaydedin Sys_mkdir, burada n Get_kmalloc işlevinin boyutudur.
- İlk n bayt çağrısının üzerine yazma SYS_MKDIR FONKSİYONU GET_KMALLOC
- sYS_MKDIR sistem çağrısına erişim, böylece get_kmalloc işlevini yürütmek için çalışıyoruz
- sYS_MKDIR sistem çağrısının ilk n baytını geri yükleriz
Ancak bu algoritmayı uygulamak için, KMALLOC işlevinin adresine ihtiyacımız var. Birkaç şekilde bulmak mümkündür. En kolay olanı, bu adresi SYSTEM.MAP dosyasından dikkate almak veya GSYİH hata ayıklayıcısının yardımı ile belirlemektir (Print & Kmalloc). Çekirdek çekerde etkinse, KMALLOC adresi GET_KERNEL_SYMS () işlevi kullanılarak tanımlanabilir. Bu seçenek aşağıda tartışılacaktır. Çekirdek modüllerinin desteği eksikse, KMALLOC işlevi adresi KMALLOC çağrı takımı açıcısı tarafından arama yapacak - Sys_call_table tablosu için nasıl yapıldığına benzer.
KMALLOC özelliği iki parametre alır: istenen hafızanın boyutu ve GFP belirticisi. Görünümü aramak için, hata ayıklayıcısını kullanırız ve Kernel'in herhangi bir fonksiyonunu Kernel'in herhangi bir işlevini parçalayın.
# GDB -Q / USR / SRC / Linux / Vmlinux (GDB) DisaS Inter_Module_Register Montajcı Kodunun Dökümü Inter_Module_Register: 0xC01A57B4
Bu amaçla, teorik hesaplamalar ve yukarıdaki yöntemi kullanarak, SYS_MKDIR sistem çağrısını yakalayacaktır.
6. Araç / dev / KMem ile yapılan müdahale örneği
/ * Kaynak 6.0 * / #includeKağıt Sonu / EOP
Kodun tüm bölümlerden çalıştırılması Kernel 2.4.22'de kontrol edildi. Rapor hazırlarken, site malzemeleri kullanılmıştır.En sık, sistem çağrısı kodu ile tanımlanan __nr_xxx numarasıyla /usr/include/asm/Unistd.h.fonksiyondaki Linux çekirdeğinin kaynak kodunda bulunabilir sys_xxx.(). (I386 için çağrı tablosu bulunabilir /usr/src/linux/arch/i386/kernel/entry.s..) Bu kuraldan, esas olarak eski sistem çağrılarının çoğunun yeni ile değiştirilmesi nedeniyle, herhangi bir sistemi olmayan bir şeyle değiştirilir. Parisc, SPARC, SPARC64 ve ALPA gibi öz-emülasyonlu platformlarda, birçok ek sistem çağrısı vardır; MIPS64 için ayrıca 32 bitlik sistem aramalarının tam bir seti vardır.
Zamanla, gerekirse, bazı sistem çağrılarının arayüzünde değişiklikler meydana geldi. Bu tür değişikliklerin nedenlerinden biri, iletilen sistem çağrısının yapılarının veya skaler değerlerinin boyutunu arttırmaktı. Bazı mimarilerdeki bu değişiklikler nedeniyle (yani, eski 32-bit i386'da çeşitli benzer sistem çağrılarının çeşitli grupları (örneğin, kesik.(2) ve truncate64.(2)), aynı görevleri gerçekleştiren, ancak argümanlarının boyutunda farklılık gösterir. (Zaten belirtildiği gibi, uygulamaları etkilemez: Glibc Wolved Fonksiyonları, doğru sistem çağrısını başlatmak için bazı eylemler gerçekleştirir ve bu, eski ikili dosyalar için ABI uyumluluğu sağlar.) Birkaç versiyona sahip sistem çağrılarının örnekleri:
* Şu anda üç tane var Çeşitli versiyonlar uydurmak(2): sys_stat.() (bir yer __Nr_oldstat.), sys_newstat() (bir yer __Nr_stat.) BEN. sys_stat64.() (bir yer __Nr_stat64.), ikincisi şu anda kullanılır. Benzer durum S. lstat.(2) ve fstat.(2). * Benzer şekilde, tanımlanmış __Nr_oldolduname., __Nr_olduname. ve __Nr_uname Aramalar için sys_olduname.(), sys_uname.() BEN. sys_newuname(). * Linux 2.0'da göründü yeni bir versiyon vm86.(2), nükleer prosedürlerin yeni ve eski sürümleri denir sYS_VM86OLD.() BEN. sYS_VM86.(). * Linux 2.4'te yeni bir versiyona sahip getRlimit.(2) Nükleer prosedürlerin yeni ve eski versiyonları denir sys_old_getrlimit.() (bir yer __NR_GETRLIMIT.) BEN. sys_getrlimit.() (bir yer __NR_UGETRLIMIT.). * Linux 2.4'te kullanıcı kimliğini ve 16 ila 32 bit arasındaki grupları artırdı. Bu değişikliği desteklemek için, birkaç sistem araması eklendi (örneğin, chown32.(2), getuid32.(2), getGroups32.(2), setresuid32.(2)), erken aramaları aynı isimlerle kaldıran, ancak "32" soneki olmadan. * Linux 2.4, 32 bit mimarilerindeki uygulamalarda büyük dosyalara (boyut ve ofset 32 \u200b\u200bbit uymaz) için destek eklendi. Bunu yapmak için, dosya üzerinde boyut ve yer değiştirmelerle çalışan sistem çağrılarında değişiklik yapmak gerekliydi. Aşağıdaki sistem çağrıları eklendi: fcntl64.(2), getdents64.(2), stat64.(2), statfs64.(2), truncate64.(2) ve dosya tanımlayıcılarını veya sembolik bağlantıları kullanan analogları. Bu sistem, "stat" aramaları da "stat" aramaları da aranıyor, ancak "64" eki yok.
Sadece 64 bit dosya erişimi ve 32-bit UID / GID (örneğin, Alpha, IA64, S390x, X86-64) olan yeni platformlarda, UID / GID ve dosya erişimi için sistem çağrısının yalnızca bir versiyonu var. Platformlarda (genellikle 32 bit platformlar), * 64 ve * 32 aramaların bulunduğu diğer sürümler modası geçmiştir.
* Aramalar rt_sig * Ek gerçek zamanlı sinyalleri desteklemek için Kernel 2.2'ye eklendi (bkz. sinyal.(7)). Bu sistem, eski sistem çağrılarını aynı isimlerle ortadan kaldırır, ancak "RT_" öneki olmadan. * Sistem zorluklarında seçmek(2) ve mmap(2) Beş veya daha fazla argüman kullanılır, bu da I386'ya argümanları iletme yöntemini belirleme sorununa neden olur. Bunun soruşturmasında, diğer mimarilerde de aradılar sYS_SELECT.() BEN. sys_mmap() karşılık gelir __NR_SELECT. ve __Nr_mmap, i386'da karşılık gelirler old_Select.() BEN. old_mpap() (Argüman bloğuna bir işaretçi kullanan prosedürler). Şu anda artık beşten fazla argüman transferinde bir sorun yok ve __Nr__NewSelect.bu tam olarak karşılık gelir sYS_SELECT.() ve aynı durumla __Nr_mmap2..
Çok fazla Walrus'a kadar, konuşmanın zamanı geldi.
L. Carolle (B. Strawbook'taki alıntı)
İdare yerine.
Genel olarak Linux çekirdeğinin iç aygıtının konusu üzerine, özellikle çeşitli alt sistemleri ve sistem zorlukları, siparişe göre yazılmış ve yeniden yazılmıştır. Muhtemelen, her bir kendi kendine saygılı yazara buna yazmalı, çünkü her bir kendi kendini saygılı programcının kendi dosya yöneticisini yazması gerektiği gibi :) Profesyonel bir IT-RAITER değilim ve genel olarak, paramimi sadece yararına yapıyorum. Öncelikle çalışılanları çok hızlı unutmamak. Ancak, eğer birisi kullanışlı olursa, seyahat notlarımı elbette, elbette sadece mutlu olacağım. Genel olarak, yulaf lapası petrolü bozmaz, bu nedenle kimsenin bahsetmeyecekleri bir şeyi yazmak veya tanımlamak bile mümkün olabilir.
Teori. Sistem aramaları nedir?
Başlatılmamış ne tür (veya işletim sistemi) ne tür bir şey olduğunu açıklarsa, genellikle aşağıdakileri söylerler: Bilgisayarın kendisi bir demir parçasıdır, ancak yazılım, bu, bu demir parçasından faydalanmanın mümkün olduğu için, . Elbette kaba, ancak genel olarak, doğru bir şey. Muhtemelen işletim sistemi ve sistem zorlukları söylerdim. Aslında, farklı işletim sistemi sistemlerinde farklı şekillerde uygulanabilir, bu aralığın sayısı emekli olabilir, ancak bir şekilde veya başka bir şekilde, bir veya başka birinde, sistem arama mekanizması herhangi bir işletim sistemindedir. Her gün kullanıcı açıkça veya açıkça dosyalarla çalışmıyor. Tabii ki, en sevdiği MS Word "E veya Notepad" e'de düzenlemek için bir dosyayı açıkça açabilir ve belki de sadece oyuncuyu çalıştırın, bu arada, bu arada, hangi dosyada depolanır. Aç, açılış yükleyici çalıştırılabilir dosyaları açmalı ve okumalısınız. Buna karşılık, oyuncak da iş sürecinde düzinelerce dosyayı açabilir ve okuyabilir. Doğal olarak, dosyalar yalnızca okunamıyor, aynı zamanda yazmak için de (her zaman değil, ancak burada da hak ve ayrık erişim hakkında değil :)). Bütün bunlar çekirdeğe yöneliyor (mikronik işletim sistemindeki durum farklı olabilir, ancak şimdi tartışmamızın nesnesi - Linux'umuzun itirazına gireceğiz, bu anı görmezden geliyorum). Yeni bir sürecin üretimi de OS çekirdeği tarafından sağlanan hizmettir. Bütün bunlar harika, ne gibi modern işlemciler Gigahertz bantlarının frekanslarında çalışın ve birçok milyon transistörden oluşur, ancak daha sonra ne var? Evet, eğer başka bir mekanizma olmasaydı, hangi özel uygulamaların oldukça sıradan ve aynı anda, gerekli şeyleri ( aslında, bu önemsiz eylemler herhangi bir eksiklikte bir kullanıcı uygulaması değil, işletim sistemi - AVT'nin çekirdeğinde gerçekleştirilir.), o zaman işletim sistemi kendi başına bir şeydi - kesinlikle işe yaramaz ya da aksine, her kullanıcı uygulamasının kendisi olmalıydı işletim sistemiTüm ihtiyaçlarınızı bağımsız olarak hizmet etmek. Şirin, değil mi?
Böylece, bir sistem çağrısının tanımına ilk yaklaşımın tanımına yaklaştık: bir sistem çağrısı, OS Kernel'in ikincisinin talebi üzerine bir kullanıcı uygulaması sağladığı bir hizmettir. Böyle bir servis, dosyanın zaten bahsedilen açılması, oluşturma, okuma, yazma, yeni bir işlem oluşturma, bir işlem tanımlayıcısı (PID), montaj olabilir dosya sistemi, Sonunda sistemi durdur. Gerçek hayatta, sistem çağrıları burada listelendiğinden çok daha fazlasıdır.
Bir sistem çağrısı neye benziyor ve bu nedir? Eh, yukarıda belirtilenlerden, sistem çağrısının, karşılık gelen bir görünüme sahip bir çekirdek alt rutin olduğu açıktır. Win9x / DOS altında programlama deneyimi olanlar, muhtemelen Int 0x21'i tümü (veya en azından bazıları) sayısız fonksiyonunu hatırlatır. Ancak, tüm UNIX sistemi çağrılarıyla ilgili küçük bir özellik var. Anlaşmaya göre, sistem çağrısını uygulayan işlev n argümanları alabilir veya bunları hiç kabul etmiyor olabilir, ancak bir şekilde ya da başka bir şekilde, işlevin int değerini iade etmesi gerekir. Negatif olmayan herhangi bir değer, sistem arama fonksiyonunun başarılı bir şekilde yürütülmesi olarak yorumlanır ve bu nedenle en fazla sistem araması olun. Değer sıfırdan az, hatanın bir özelliğidir ve aynı zamanda bir hata kodu içerir (Hata Kodları, Dahil / ASM-Genel / Errno-Base.h'da tanımlanır ve / asm-jenerik / errno.h başlıkları dahil) ). Sistem çağrıları için Linux ağ geçidinde, yakın zamana kadar bir int 0x80 kesintiye çıktı, Windows'ta (yanılmıyorsam, XP Service Pack 2 versiyonuna kadar), bu ağ geçidi 0x2e kesmesi. Yine, Linux çekirdeğinde, yakın zamana kadar, tüm sistem çağrıları System_Call () işlevi tarafından işlendi. Bununla birlikte, daha sonra ortaya çıktığında, 0x80 ağ geçidi aracılığıyla akan sistemin klasik mekanizması, işlemci performansında önemli bir düşüşe yol açar. Intel pentium. 4. Bu nedenle, sanal dinamik paylaşılan nesnelerin yöntemi Klasik mekanizmayı değiştirmeye (DSO - Dinamik paylaşılan nesne dosyası. Doğru çeviri için sayılmaz, ancak DSO, Windows kullanıcılarının DLL - dinamik olarak indirilebilir ve bileşen kütüphanesi olarak bilindiği biliniyor. ) - vdso. Klasikten yeni bir yöntem arasındaki fark nedir? Başlamak için, 0x80 kapısı boyunca çalışan klasik yöntemle anlıyoruz.
Linux'ta Klasik Sistem Çağrı Servis Mekanizması.
X86 mimarlığında kesintiler.
Yukarıda belirtildiği gibi, kullanıcı uygulamalarına servis yapmanın daha önce 0x80 (int 0x80). IA-32 mimarisinin işletimi, kesme işlemleri tarafından yönetilmektedir (kesinlikle konuşursak, bu, X86'ya dayanan tüm sistemler ile ilgilidir). Belirli bir olay meydana geldiğinde (yeni bir TIK zamanlayıcısı, bazı cihazlarda herhangi bir aktivite, hatalar - sıfıra, vb.), Bir kesme oluşturulur. Kesinti (kesme) olarak adlandırılır, çünkü genellikle kodun normal seyrini keser. Kesintiler, donanım ve yazılımı (donanım ve yazılım kesintileri) bölünmeye kabul edilir. Donanım kesintileri, sistemik ve çevresel cihazlar tarafından üretilen kesintilerdir. Bazı cihazlara ihtiyacınız varsa, OS çekirdeği (cihaz), kesme sorgu satırında (IRQ - kesme istek satırında) bir sinyal oluşturur. Bu, karşılık gelen sinyalin, işlemcinin belirli girişlerinde üretildiği gerçeğine, işlemcinin, talimatların akışını durdurduğu ve daha önce ne olduğunu bulur ve ne yapılması gerekiyor. Donanım, doğası gereği asenkron durdurur. Bu, kesintinin herhangi bir zamanda gerçekleşebileceği anlamına gelir. dışında Çevresel aygıtlarİşlemcinin kendisi kesintiler üretebilir (veya daha kesin, donanım istisnaları - donanım istisnaları - örneğin, zaten sıfırdan bahsedilen bölünme). Bu, işletim sisteminin anormal bir durumun ortaya çıkması üzerine bildirilmesi için yapılır, böylece işletim sistemi böyle bir durumun ortaya çıkmasına cevaben bir tür harekete geçebilir. Kesintiyi işten çıkardıktan sonra, işlemci kesintiye uğramış programın tamamlanmasına geri döner. Kesme, kullanıcı uygulaması tarafından başlatılabilir. Böyle bir kesinti yazılım denir. Yazılım kesintileri, donanımın aksine, senkronize. Bunlar., Koduna neden olan bir kesmeyi ararken, kesilinceye kadar askıya alınır. Kesme işleyicisini terk ederken, yığın içindeki (int) kesildikten sonra istifin üzerine daha önce kaydedilen (kesintiyi çağırırken) kaydedilen uzun mesafeli bir adrese döner. Kesinti işleyicisi, kodun bir ikamet (sürekli hafızada). Kural olarak, bu küçük bir programdır. Her ne kadar Linux çekirdeği hakkında konuşursak, o zaman her zaman çok küçük kesme işleyicisi yoktur. Kesme işleyicisi vektör tarafından belirlenir. Vektör, bu endekle durdurması gereken başlangıç \u200b\u200bkodunun adresinden (segment ve ofset) daha fazla bir şey değildir. Kesintilerle çalışmak, gerçek modda önemli ölçüde farklıdır ve işlemcinin çalışma modunda (korumalı mod) (bu, size burada ve sonra Intel işlemcileri ve bunlarla uyumlu olduğumuzu hatırlatırız). Gerçek (korunmasız) işlemci modunda, kesme işlemcileri, her zaman hafızanın başında depolanan vektörleri tarafından belirlenir. Vektör çizimlerinden istenen adresi seçmek, aynı zamanda kesme numarası olan dizinde oluşur. Kendi işleyicisini kesmek için bir vektörün tanımlanmış bir dizinle üzerine yazmak atanabilir.
Korunan modda, kesme işleyicileri (ağ geçitleri, kapılar veya valfler) artık vektörler tablosunu kullanarak belirlenmez. Bu tablonun yerine, vana tablosu kullanılır veya daha doğru, kesme tablosu - IDT (kesme çizelticileri tablosu). Bu tablo çekirdek tarafından oluşturulur ve adresi IDTR işlemci kaydında saklanır. Bu kayıt doğrudan mevcut değil. Bununla çalışma, yalnızca LIDT / SIDT talimatlarını kullanarak mümkündür. Bunlardan ilki (LIDT), işlenende belirtilen IDTR değerini yükler ve temel adres Kesme Decript Tabloları, İkinci (SIDT), IDTR'de bulunan tablonun adresini belirtilen işlenene saklar. Tablo tanımlayıcı tanımlayıcılarından yapılan segmentle ilgili bir bilgi seçeneği olarak, segment tanımlayıcısının bir bölümünün, korumalı modda kesilmeye hizmet verilmesi de oluşur. Hafıza koruması desteklenir intel İşlemciler CPU I80286 ile başlayarak (şu anda sunulduğu formda değil, eğer sadece 286, eğer 286), bu nedenle Linux bu işlemciler üzerinde çalışamazlar) ve i80386 ve bu nedenle işlemci bağımsız olarak gerekli tüm örnekleri üretir ve Bu nedenle, korunan modun tüm inceliklerine derinleşmesi gerektiği (yani, Linux korumalı modda çalışır) yapmayacağız. Ne yazık ki, ne de yeteneklerin ne de yetenekleri, kesme işlem mekanizmasında uzun süre güvenli bir modda durmamıza izin vermemektedir. Evet, bu makaleyi yazarken bu amaç değildi. Burada H86 aile işlemcilerinin çalışmalarında belirtilen tüm bilgiler oldukça yüzeyseldir ve yalnızca çekirdeğin sistem çağrılarının işletme mekanizmasını biraz daha iyi anlamak için verilir. Doğrudan kodlayıcı kodundan öğrenebileceğiniz bir şey, olsa da, neler olduğunu tam olarak anlamak için, korumalı rejimin ilkelerine aşina olmanız önerilir. Başlangıç \u200b\u200bdeğerlerini dolduran kod kodu (ancak yüklemez!) IDT, ARCH / I386 / Kernel / Head.s: / * * SETUP_IDT * * * Ignore_int, kesme kapılarına işaret eden 256 girişli bir IDT oluşturur. Aslında yüklenmez * IDT - sadece çağrı etkinleştirildikten sonra yapılabilir * ve çekirdek, page_offset'e taşındı. Kesme * Başka bir yerde etkinleştiriliyor, biz göreceli olabiliriz * Her şey yolundayız. * * Uyarı:% ESI Bu fonksiyon boyunca yaşıyor. * / 1.setup_idt: 2. Leave Ignore_int,% EDX 3. MOVL $ (__ Kernel_CS<< 16),%eax 4. movw %dx,%ax /* selector = 0x0010 = cs */ 5. movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ 6. lea idt_table,%edi 7. mov $256,%ecx 8.rp_sidt: 9. movl %eax,(%edi) 10. movl %edx,4(%edi) 11. addl $8,%edi 12. dec %ecx 13. jne rp_sidt 14..macro set_early_handler handler,trapno 15. lea \handler,%edx 16. movl $(__KERNEL_CS << 16),%eax 17. movw %dx,%ax 18. movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ 19. lea idt_table,%edi 20. movl %eax,8*\trapno(%edi) 21. movl %edx,8*\trapno+4(%edi) 22..endm 23. set_early_handler handler=early_divide_err,trapno=0 24. set_early_handler handler=early_illegal_opcode,trapno=6 25. set_early_handler handler=early_protection_fault,trapno=13 26. set_early_handler handler=early_page_fault,trapno=14 28. ret Koddaki çeşitli yorumlar: Verilen kod AT & T Assembler'in çeşitleri üzerine yazılmıştır, bu nedenle asscaten'in her zamanki Intel notasyonundaki bilgisi sadece karıştırılabilir. Operandların sırasındaki en önemli fark. Sipariş Intel Notation için tanımlanırsa - "Batarya"< "источник", то для ассемблера AT&T порядок прямой. Регистры процессора, как правило, должны иметь префикс "%", непосредственные значения (константы) префиксируются символом доллара "$". Синтаксис AT&T традиционно используется в Un*x-системах.
2-4 satırlarında verilen örnekte, tüm varsayılan kesintilerin işleyicisinin adresi ayarlanır. Varsayılan işleyici, hiçbir şey yapmayan Ignore_int işlevidir. Böyle bir fişin varlığı, bu aşamadaki tüm kesintilerin doğru şekilde işlenmesi için gereklidir, çünkü diğerleri henüz artık (TRAD, TRAPS (TRAPS) kodunda biraz daha düşük ayarlanır - Tuzaklar Hakkında, Intel Mimarlık Manuel Referansı ya da böyle bir şey, burada tuzağa dokunmayacağız). 5. satırda, valf tipi ayarlanır. 6. sırada, IDT tablonuzun adresini dizin kaydında indiriyoruz. Masanın her biri 8 bayt, 255 giriş içermelidir. 8-13 satırlarında, tüm tabloyu EAX ve EDX kayıtları tarafından belirlenen aynı değerlerle doldururuz - yani, bu, Ignore_int işleyicisini ifade eden bir kesme valfidir. Hemen aşağıda, tuzakları (tuzaklar) - dizeleri 14-22 yüklemek için bir makro tanımlarız. Yukarıda belirtilen makroyu kullanarak 23-26 numaralı. başarısızlık (14). Parantez içinde, uygun bir acil durum tarafından üretilen sayıları "kesintiler" verilir. İşlemci tipini Kemer / I386 / Kernel / Kafa bölümündeki kontrol etmeden önce, IDT tablosu SETUP_IDT çağrısı ile ayarlanır: / * * Sistemi Başlat 32-bit Kurulumu. "Gerçek" işlemler için 16 bit modunda yapılan şeyleri * yeniden yapmamız gerekiyor. * / 1. SETUP_IDT ... 2. Çağrı Check_x87 3. LGDT Early_GDT_DESCR 4. LIDT IDT_DESCRİşlemcinin tipini (CO) bulduktan ve 3 ve 4 satırlarındaki tüm hazırlık işlemlerini gerçekleştirdikten sonra, çekirdeğin ilk gözeneklerinde kullanılacak GDT ve IDT tablolarını yükleriz.
Sistem çağrıları ve int 0x80.
Kesintilerden sistem zorluklarına geri dönecek. Öyleyse, bir tür hizmet isteyen sürecine hizmet etmek için neler gerekiyor? Başlamak için, en çok etkilenen seviye 0 (Ring 0, CPL \u003d 0), halka 3 (CPL \u003d 3 seviyesi), çünkü çünkü Çekirdek kod, en yüksek turistik mekanlara sahip segmentte bulunur. Ek olarak, işleyici kodu işlemin hizmet edeceği için gereklidir. Bunun için 0x80 ağ geçidinin kullanıldığı içindir. Sistem çağrıları oldukça fazla olmasına rağmen, herkes için tek bir giriş noktası kullanırlar - Int 0x80. KAPA / I386 / KERNEL / TRAPS.C :: Trap_init () işlevini ararken işleyicinin kendisi ayarlanır: void __init trap_init (void) (... set_system_gate (syscall_vector, & system_call); ...)Biz Trap_init () bu çizgi ile daha fazla ilgileniyoruz. Aynı dosyada, yukarıdaki SET_SYSTEM_GATE () fonksiyon koduna bakabilirsiniz: Statik void __init set_system_gate (işaretsiz int n, void * addr) (_SET_GATE (N, DESTAPE_TRAP | DESTAPE_DPL3, ADDR, __KERNEL_CS);)Burada valfin 0x80'i kestiğini (yani, bu değer, Syscall_Vector makrosu tarafından belirleneceğine inanıyorsunuz) görebilirsiniz :)) Nitelikler DPL \u003d 3 seviyesi olan bir tuzak (tuzak) olarak monte edilebilir (Halka 3) , yani Bu kesme, kullanıcı alanından ararken yakalanacaktır. Halka 3'ten Ring 0 T.O'da geçişle ilgili sorun. Çözüldü. _SET_GATE () işlevi, dahil / asm-i386 / desc.h başlık dosyasında tanımlanır. Aşağıdaki meraklılar için, kod, mükemmellik olmadan gösterilir: Statik inline void _SET_GATE (int kapısı, imzasız int tipi, boşluk * ADDR, imzasız kısa seg) (__u32 a, b; pack_gate (& a, & b, (imzasız uzun) addr, seg, tip, 0); write_idt_entry (idt_table) , Gate, A, B);)Trap_init () işlevine dönelim. Init / Main.c'deki Start_Kernel () işlevinden çağrılır. Trap_init () koduna bakarsanız, bu özelliğin, IDT tablosunun bazı değerlerine karşılık geldiği açıktır (Early_Page_Fault, Early_Divide_Rerr, Early_Llegal_Opcode, Early_Protection_Pault) zaten işlemde kullanılacak olanlarla değiştirildi. Çekirdek çalışıyor. Öyleyse, pratikte özüne geldik ve tüm sistem çağrılarının üniform bir şekilde işlendiğini biliyoruz - Int 0x80 Gateway. Int 0x80 için bir işleyici olarak, tekrar olduğu gibi, ARCH / I386 / KERNEL / TRAPS.C :: Trap_INIT () kodunun yukarıdaki çizelgesinden görülür, System_call () işlevini ayarlar.
system_call ().
System_call () işlevinin kodu, Arch / I386 / Kernel / Giriş.s dosyasında ve şöyle görünür: # Sistem çağrısı işleyici saplı girişi (System_call) Ring0_int_frame # "Kullanıcı alanına giremezsiniz. ve böylece testbiye ihtiyacı var ve testb * / testw $ (_ tif_syscall_emu | _tif_syscall_trace | _tif_seccall_trace | _tif_seccomp | _tif_syscall_audit), ti_flags (% ebp) jnz syscall_trace_tentry cmpl $ (nr_syscalls),% eax jae syscall_badsyssysysysysy_call: Call * sys_call_table (,% eax , 4) MOVL% EAX, PT_EAX (% ESP) # iade değerini saklayın ...Kod tamamen gösterilmez. Gördüğünüz gibi, birinci System_Call () bir istifi halka 0'da çalışacak şekilde yapılandırır, EAX üzerinden iletilen değeri yığına kaydeder, tüm kayıtları yığına kaydeder, arayan verilerini alır ve iletilen değerinin olup olmadığını kontrol eder Sistem araması, sistem çağrısı tablosunun sınırlarına gitmez ve daha sonra, EAX olarak iletilen değeri bir argüman olarak, System_Call () egzersizlerini kullanarak, tablonun hangi elemanına EAX'e işaret eden gerçek bir sistem çıkış işleyicisine uygulanır. Şimdi gerçek moddan eski kesici vektörlerin eski tablosunu hatırlayın. Hiçbir şey hatırlatmıyor? Gerçekte, elbette, her şey daha karmaşık. Özellikle, sistem çağrısı, sonuçları çekirdek yığınından kullanıcı yığına kopyalamalı, geri dönüş kodunu ve biraz daha şeyleri geçmelidir. EAX'te belirtilen argüman mevcut bir sistem çağrısına başvurmadığında (değer aralığındadır), Syscall_Badsys etiketine gidin. Burada, EAX'in değeri olması gereken yer değiştirme yığını içinde, -enosys'ın değeri girilir - sistem çağrısı uygulanmaz. Bu yürütme sistemi_call () tamamlandı.
Sistem çağrısı tablosu, ARCH / I386 / KERNEL / SYSCALL_TABLE.S dosyasında bulunur ve oldukça basit bir görünüme sahiptir: Giriş (sys_call_table) .long sys_restart_syscall / * 0 - eski "kurulum ()" Sistem çağrısı, yeniden başlatma için kullanılan * / .long sys_exit .long sys_fork .long sys_read .long sys_write .long sys_open / * 5 * / .long sys_close .long SYS_WAITPID .LONG SYS_CREAT ...Başka bir deyişle, tüm tablo, bu fonksiyonların servis edildiği sistem arama numaralarının sırasına göre bulunan bir dizi işlev adresinden başka bir şey değildir. Tablo, çift motorlu kelimelerin (veya daha fazla seven 32 bit kelimenin) normal dizisidir. Sistem çağrıları servis eden fonksiyonların bir kısmının kodu, platform bağımlı parçada - ARCH / I386 / KERNEL / SYS_I386.C ve KERNEL / SYS.C'deki platforma bağlı olmayan bir parçada bulunur.
Sistem zorlukları ve 0x80 valfinde durum budur.
Linux'ta Yeni Sistem Çağrı İşleme Mekanizması. SYSENTER / SYSEXIT.
Çok hızlı bir şekilde bahsedildiği gibi, Gaita 0x80'e dayanan sistem çağrılarının geleneksel bir işleme yönteminin kullanımının, Intel Pentium işlemcilerindeki performans kaybına itmektir. Bu nedenle, Linus Torvalds, Kernel'de yeni bir mekanizma uyguladı. SYSENTTER / SYSEXIT talimatları ve çekirdeğin verimliliğini Pentium II ve daha yüksek işlemciyle donatılmış makinelerde arttırmak için tasarlanmıştır (SYSENTER / SYSEXIT talimatları tarafından desteklenen Pentium II + Intel işlemcilerindendir). Yeni bir mekanizmanın özü nedir? Garip bir şekilde yeterince, ancak özü aynı kalır. Yürütme değişti. Intel belgelerine göre, Sysenter talimatı "Hızlı Sistem Çağrıları" mekanizmasının bir parçasıdır. Özellikle, bu talimat, bir seviyesinden diğerine hızlı geçiş için optimize edilmiştir. Daha kesin olarak, halka 0 (halka 0, CPL \u003d 0) geçişini hızlandırır. Aynı zamanda, işletim sistemi sysenter talimatını kullanmak için işlemciyi hazırlamalıdır. Bu ayar, işletim sistemi çekirdeğini yüklerken ve başlatırken bir kez yapılır. SySenter'ı ararken, işlemci kayıtlarını daha önce kurulmuş işletim sistemine bağlı kayıtlara göre ayarlar. Özellikle, segment kaydı ve talimat işaretçisinin kaydı kurulur - CS: EIP, yanı sıra yığın segmenti ve Yığın Vertix göstergesi - SS, ESP. Kodun yeni segmentine geçiş ve ofset, halka 3 ila 0'dan gerçekleştirilir.
Sysexit talimatı ters işlem gerçekleştirir. 3. (CPL \u003d 3) içindeki ilgi çekici yerlerden 0 seviyesinden hızlı bir geçiş üretir. Bu durumda, kod segment kaydı, işlemcinin makine bağımlı kayıtında depolanan 16+ CS segment değerine ayarlanır. EIP kaydı, EDX kaydının içeriğine girilir. SS, SS 24'e girilir ve CS değerleri, sysenter talimatının çalışması için bağlamın hazırlanması sırasında makineye bağlı işlemci kaydında makinee bağlı işlemci kaydında kaydedilir. ESP, ECX kaydının içeriğini girdi. SYSENTER / SYSEXIT talimatlarının çalışması için gereken değerler aşağıdaki adreslerde depolanır:
- SYSENTER_CS_MSR 0x174, sistem çağrısı işleyici kodunun girildiği bölüm değerinin girildiği bir kod segmentidir.
- SYSENTER_ESP_MSR 0x175, bir sistem arama işleyicisi için yığının üst kısmına bir işaretçidir.
- SYSENTER_EIP_MSR 0x176 - Kod segmentinin içindeki ofsete işaretçi. Sistem arama işleyicisinin başlangıç \u200b\u200bkodunu belirtir.
Linux'un görev bağlamlarının donanım anahtarlamasını kullanmadığı gerçeğine rağmen, çekirdek, sistemde kurulu olan her işlemci için TSS kaydını ayarlamak zorunda kalır. Bu, işlemcinin kullanıcı modundan çekirdek moduna geçtiğinde, çekirdek yığın adresini TSS'den alır. Ek olarak, I / O bağlantı noktalarına erişimi kontrol etmek için TSS gereklidir. TSS, bağlantı noktalarına bir erişim hakları haritası bulunur. Bu karta dayanarak, giriş / çıkış talimatlarını kullanarak her işlem için bağlantı noktalarına erişimi kontrol etmek mümkün olur. Burada tss-\u003e x86_tss.esp1, çekirdeğin yığını gösterir. __Kernel_CS doğal olarak kodlayıcı kod segmentini gösterir. EIP yer değiştirmesi, SYSENTER_ENTRY () işlevinin adresini gösterir.
SYSENTER_ENTRY () işlevi, Arch / I386 / Kernel / Giriş.s dosyasında tanımlanır ve bu tür: / * SYSENTER_RETURN, VSYSCALL sayfasındaki "SYSENTER" talimatından sonra işaret eder. Sembolü tanımlayan vsyscall-sysentry.s bakın. * / # SYSENTER çağrı işleyici saplı giriş (SYSENTER_ENTRY) CFI_STARTPROC SIMPLE SIMPLE CFI_SIGNAL_FRAME CFI_DEF_CFA ESP, 0 CFI_REGister ESP, EBP MOVL TSSS_SYSENTER_ESP0 (% ESP),% ESP SYSENTER_PAST_ESP: / * * Bu IRQS'i takip etmenize gerek yok: Syscall * Engelli IRQS ve burada doğrudan giriş yaptıktan sonra bunu etkinleştiriyoruz: * / Enable_Tinterraps (CLBR_NONE) Pushl $ (__ user_ds) CFI_ADJust_CFA_OFFSET 4 / * CFI_REL_OFFSET SS, 0 * / Pushl% EBP CFI_ADJUST_CFA_OFFSET 4 CFI_REL_OFFSET ESP, 0 Pushl CFI_ADJust_CFA_OFFSET 4 Pushl $ (__ user_cs ) CFI_ADJUST_CFA_OFFSET 4 / * CFI_REL_OFFSET CS, 0 * / / * * Push Standration_Thread_info () -\u003e SYSENTER_RETURN Yığına. * Tinik bir ofset düzeltmesi gereklidir - 4 * 4, yukarıda itilen 4 kelimenin * anlamına gelir; +8 COMPLE_THREAD "ın ESP0 ayarına karşılık gelir. * / Pushl (Ti_SySenter_return-thread_Size + 8 + 4 * 4) (% ESP) CFI_ADJUST_CFA_OFFSET 4 CFI_REL_OFFSET EIP, 0 / * * Potansiyel altıncı argümanı kullanıcı yığından yükleyin. * Güvenlik hakkında dikkatli olun . * / SPPL $ __ Page_Offset-3,% EBP JAE SYSCALL_FAULT 1: MOVL (% EBP),% EBP .Section __ex_table, "a" .Alng 4 .Long 1b, Syscall_Fault .Previous pushl% eax cfi_adjust_cfa_offset 4 save_All Get_Thread_info (% EBP) / * Not, _TIF_SECCOMP Bit 8'dir ve böylece TestW'ye ihtiyaç duyar ve TestB * / TestW $ (_ TIF_SYSCALL_EMU | _TIF_SECCALL_TRACE | _TIF_SECCOMPL_TRACE | _TIF_SECCOMP | _TIF_SYSCALL_AUDIT), TI_FLAGS (% EBP) JNZ SYSCALL_TRACE_ENTRY CMPL $ (nr_syscalls),% ex Jae Syscall_Badsys * sys_call_table (% eax, 4) movl% eax, pt_eax (% ESP) Disable_Interrupt (clbr_any) trace_irqs_off movl ti_flags (% ebp),% ecx testw $ _TIF_ALLWork_Mask,% cx jne syscall_exit_work / * bir şey değiştirirse Ayrıca SYSEXIT * / MOVL PT_EIP (% ESP),% EDX MOVL PT_OLDESP (% ESP),% ECX XORL% EBP,% EBP TRACE_IRQS_ON 1: MOV PT_FS (% ESP),% FS \u200b\u200benable_interrupt_sysexit cfi_endproc .Pushsection .fixup, "AX" 2: MOVL $ 0, PT_FS (% ESP) JMP 1B .Section __ex_table, "a" .ALIGN 4 .LONG 1B, 2B .POPS ENDPROC (SYSENTER_ENTRY)System_call () durumunda olduğu gibi, ana çalışma çağrısında * SYS_CALL_TABLE (,% EAX, 4) dize yapılır. Belirli bir sistem çağrı işleyicisi burada denir. Böylece, biraz değiştiği görülebilir. Kesinti vektörünün şimdi demir içine tıkanmış ve işlemci, cazibe merkezlerinin bir seviyesinden başka bir değişiklik için daha hızlı bir şekilde daha hızlı yardımcı olur, yalnızca aynı içeriğe sahip yürütmenin bazı detaylarını değiştirmemize yardımcı olur. Doğru, bu değişiklikler bitmiyor. Anlatının neden başladığını hatırla. En başında sanal paylaşılan nesneler hakkında bahsettim. Öyleyse, sistem çağrısının uygulanmasından önce, LIBC kütüphanesinden bir kesme çağrısına benziyordu (kütüphanenin bağlam anahtarlarının sayısını azaltmak için bazı fonksiyonlar yaptığına rağmen), şimdi VDSO sistem çağrısı olabilir. Neredeyse doğrudan, Libc'in katılımı olmadan yapılabilir. Ayrıca doğrudan, yine bir kesme olarak gerçekleştirilebilir. Ancak şimdi arama, dinamik olarak bir bileşen kütüphanesinden (DSO) dışa aktarılan her zamanki fonksiyon olarak istenebilir. Çekirdek yüklenirken, hangi mekanizmanın bu platform için kullanılacağını belirler. Koşullara bağlı olarak, çekirdek giriş noktasını sistem çağrısını gerçekleştiren bir fonksiyona ayarlar. Ayrıca, fonksiyon, Linux-Gate.so.1 kütüphane kullanıcı alanına aktarılır. Linux-Gate.so.1 kütüphane fiziksel olarak diskte mevcut değildir. Eğer koyabilirseniz, çekirdek tarafından öykünür ve sistemin ne kadar çalıştığını tam olarak var. Sistemi durdurursanız, kök FS'yi başka bir sistemden baltalayın, o zaman bu dosyayı kök FS'de bulamazsınız. Aslında, bir çalışma sisteminde bile bulamayacaksınız. Fiziksel olarak, sadece değil. Bu yüzden Linux-Gate.so.1.1 vdso gibi bir şeydir - yani. Sanal dinamik olarak paylaşılan nesne. Çekirdek, dinamik kütüphaneyi bu şekilde her işlemin adres alanında görüntüler. Aşağıdaki komutu çalıştırırsanız, bunun kolay olduğundan emin olun: [E-posta Korumalı]: ~ $ Cat / Proc / Self / Haritalar 08048000-0804C000 R-XP 00000000 08:01 46 / Bin / Cat 0804C000-0804D000 RW-P 00003000 08:01 46 / Bin / Cat 0804D000-0806E000 RW-P 0804D000 00:00 0 ... B7FDF000-B7FE1000 RW-P 00019000 08:01 2066 /Lib/ld-2.5.So BFFD2000-BFFE8000 RW-P BFFD2000 00:00 0 FFFFE000-FFFFFF000 R-XP 00000000 00:00 0Burada en son satır ilginin amacıdır: FFFFE000-FFFFFF000 R-XP 00000000 00:00 0Yukarıdaki örnekten, nesnenin hafızaya girdiği görülebilir, neredeyse adres alanının arka bahçelerinde, tam olarak bir sayfa - 4096 bayt. Başka bir deney çizelim: [E-posta Korumalı]: ~ $ Ldd `Hangi kedi linux-gate.so.1 \u003d\u003e (0xffffe000) libc.so.6 \u003d\u003e / lib/tls/i686/cmov/libc.so.6 (0xB7E87000) / lib / ld-linux .So.2 (0xb7fdf000) [E-posta Korumalı]: ~ $ Ldd `hangi gcc` linux-gate.so.1 \u003d\u003e (0xffffe000) libc.so.6 \u003d\u003e / lib/tls/i686/cmov/libc.so.6 (0xB7E3C000) / lib / ld-linux .So.2 (0xB7F94000) [E-posta Korumalı]:~$ Burada sadece iki başvuru yaptık. Kütüphanenin, işlemin bir adres alanında aynı kalıcı adrese göre görüntüleneceği görülebilir - 0xffffe000. Şimdi bu hafıza sayfasında ne kaydedilenleri görmeye çalışalım ...
Paylaşılan VDSO kodunun kullanıldığı bellek sayfası dökülmesini yapın sonraki program: #include #include #include int ana () () () (char * vdso \u003d 0xffffe000; char * tampon; dosya * f; arabellek \u003d malloc (4096); eğer (! Tampon) çıkış (1); Memcy (Tampon, VDSO, 4096) eğer (! (F \u003d fopen ("test.dump", "w + b"))))) (ücretsiz (tampon); çıkış (1);) fwrite (tampon, 4096, 1, f); f1 (f) ); Ücretsiz (tampon); geri dönüş 0;)Kesinlikle, ekibi kullanarak daha kolay yapılmadan önce, dD if \u003d / proc / self / mem \u003d test.dump bs \u003d 4096 skip \u003d 1048574 sayım \u003d 1Ancak sürüm 2.6.22 veya belki daha önce bile başlayan çekirdekler, artık işlem belleğini artık dosyaya / proc / `pid` / mem. Bu dosya, açıkça, uyumluluk için kaydedilir, ancak daha fazla bilgi içermiyor.
CommSise ve verilen programı çalıştırın. Elde edilen kodu sökmeye çalışalım: [E-posta Korumalı]: ~ / Tmp $ objdump --DisAssemble ./test.dump ./test.dump: Dosya dosyası formatı elf32-i386 Bölümün sökülmesi .Text: FFFFE400<__kernel_vsyscall>: FFFFE400: 51 Push% ECX FFFFE401: 52 Push% EDX FFFFE402: 55 Push% EBP FFFFE403: 89 E5 MOV% ESP,% EBP FFFFE405: 0F 34 SYSENTER ... FFFFFE40E: EB F3 JMP FFFFE403<__kernel_vsyscall+0x3> FFFFE410: 5D POP% EBP FFFFE411: 5A POP% EDX FFFFE412: 59 POP% ECX FFFFE413: C3 RET ... [E-posta Korumalı]: ~ / Tmp $Burada, avuç içi gibi sistem çağrıları için ağ geçidimizdir. İşlem (veya, LIBC Sistem Kütüphanesi), __Kernel_vsyscall işlevini çağırmak, 0xffffe400 (bizim durumumuzda) adrese düşer. Sonra, __KERNEL_VSYSCALL ECX, EDX, EDX kayıtlarının içeriğini kaydeder EDX, EDX kayıtları, EDX kayıtları, ECX ve EDX kayıt kayıtları hakkında daha önce daha önce konuştuk, daha sonra kullanıcının yığını geri yüklemek için kullanılır. Sysenter talimatı yürütülür, "Interptice Interrupt" ve sonuç olarak, SYSENTER_ENTRY'ye bir sonraki geçiş (yukarıya bakınız). 0xffffe40e'deki JMP talimatı, sistem mücadelesini bir dizi 6 argümanla yeniden başlatmak için eklenir (bkz. Http://lkml.org/lkml/2002/12/18/). Sayfada yayınlanan kod, ARCT / I386 / KERNEL / VSYSCALL-ENTERN.S (veya ARCH / I386 / KERNEL / VSYSCALL-INT80.S) Tuzak 0x80 içindir). __Kernel_vsyscall işlevinin adresinin kalıcı olduğunu tespit etmiş olsa da, ancak olmadığı bir görüş var. Genellikle, __kernel_vsyscall () içindeki giriş noktasının konumu, AT_SYSINFO parametresini kullanarak Elf-Auxv vektörü tarafından bulunabilir. Elf-auxv vektörü, başlarken ve program sırasında gerekli çeşitli bilgileri içerdiğinde yığın boyunca işleme iletilen bilgileri içerir. Özellikle bu vektör, sürecin ortamının değişkenlerini, argümanları, vb.
Doğrudan __Kernel_vsyscall işlevine nasıl başvurulacağı konusunda küçük bir örnek: #Dahil etmek.
Sonuçlar.
Aslında, pratik olarak, özel performans, bu FSCF'nin (Hızlı Sistem Çağrı Tesisi) desteğiyle bile, her zaman elde etmek mümkün değildir. Sorun şu ki, böylece işlem nadiren doğrudan çekirdeğe atıfta bulunur. Ve bunun için iyi sebepler var. LIBC kütüphanesini kullanmak, çekirdek versiyonundan bağımsız olarak, programın taşınabilirliğini garanti etmenizi sağlar. Ve çoğu sistem çağrısının gittiği standart sistem kütüphanesinden geçiyor. FSCF'yi destekleyen bir platform için monte edilmiş en son çekirdeği toplamlasanız ve kursanız bile, bu bir verimlilik artışının garantisi değildir. Gerçek şu ki, LIGC.SOSY sistem kitaplığınızın Int 0x80'i kullanmak için düzleştirileceği ve yalnızca GliBC'nin geçişi ile başa çıkabileceğidir. VDSO ve __KERNEL_VSYSCALL arayüzü ve __KERNEL_VSYSCALL, GliBC'de desteklenir mi, dürüstçe cevap vermeyi zor buluyorum.
Bağlantılar.
Manu Garg "s sayfası, http://www.manugarg.com
Scatter / Thougohts Johan Petersson, http://www.trilithium.com/johan/2005/08/linux-gate/ tarafından toplayın.
Eski İyi Anlamak Linux çekirdeği onsuz nerede :)
Ve tabi ki, kaynak Kodları Linux (2.6.22)