2009-07-28 13 views
13

Yaz araştırmamın bir kısmı için çekirdek işine giriyorum. Belirli RTT hesaplamalarında TCP'ye değişiklikler yapmak istiyoruz. Benim yapmak istediğim, tcp_input.c'deki işlevlerden birinin çözünürlüğünü, dinamik olarak yüklenmiş bir çekirdek modülü tarafından sağlanan bir işleve dönüştürmektir. Bunun, değişikliği geliştirip dağıtabileceğimiz hızı geliştireceğini düşünüyorum.Linux kernel işlevini bir modülle değiştirebilir miyim?

İlgilendiğim işlev statik olarak bildirildi, ancak çekirdeği statik olmayan ve EXPORT_SYMBOL tarafından dışa aktarılan işlevle yeniden derledim. Bu, fonksiyonun şimdi çekirdeğin diğer modülleri/bölümleri için erişilebilir olduğu anlamına gelir. Bunu "cat/proc/kallsyms" ile doğruladım.

Şimdi sembol adresini başlangıçtan dinamik olarak yüklenen işlevime yeniden yazabilen bir modül yüklemek istiyorum. Benzer şekilde, modül boşaltılacağı zaman, orijinal adresi geri yükler. Bu uygun bir yaklaşım mı? Bunun nasıl daha iyi uygulanabileceğine dair önerileriniz var mı?

Teşekkürler!

aynı

Düzenleme Overriding functionality with modules in Linux kernel olarak: Bu benim nihai yaklaşımdı
.
aşağıdaki işlevi göz önüne alındığında, (geçersiz kılmak için istediği ve dışa değildir):

static void internal_function(void) 
{ 
    // do something interesting 
    return; 
} 

şöyle değiştirin:

static void internal_function_original(void) 
{ 
    // do something interesting 
    return; 
} 

static void (*internal_function)(void) = &internal_function_original; 
EXPORT_SYMBOL(internal_function); 

Bu (beklenen işlevsel tanımlayıcısını bunun yerine bir işlev işaretçisi yeniden tanımlamakta Orijinal uygulamaya işaret eden benzer bir şekilde çağrılabilir. EXPORT_SYMBOL(), adresi global olarak erişilebilir kılar, böylece bir modülden (veya başka bir çekirdek konumundan) değişiklik yapabiliriz.

Şimdi aşağıdaki form ile bir çekirdek modülü yazabilirsiniz:

static void (*original_function_reference)(void); 
extern void (*internal_function)(void); 

static void new_function_implementation(void) 
{ 
    // do something new and interesting 
    // return 
} 

int init_module(void) 
{ 
    original_function_reference = internal_function; 
    internal_function   = &new_function_implementation; 
    return 0; 
} 

void cleanup_module(void) 
{ 
    internal_function = original_function_reference; 
} 

Bu modül dinamik olarak yüklenen sürümü ile orijinal uygulanmasını değiştirir. Boşaltma üzerine, orijinal referans (ve uygulama) geri yüklenir. Benim özel durumumda, TCP'deki RTT için yeni bir tahminci sağladım. Bir modül kullanarak, çekirdeği yeniden derlemek ve yeniden başlatmak zorunda kalmadan, küçük ince ayarlamalar yapabilir ve testleri yeniden başlatabilirim.

cevap

7

Çalışacağından emin değilim - Değiştirmek istediğiniz işlevin dahili çağrıları için sembol çözünürlüğünün, modülünüzün yüklediği zamana kadar yapılmış olduğunu düşünüyorum. Bunun yerine, varolan işlevi yeniden adlandırarak kodu değiştirebilir ve ardından işlevin orijinal adıyla bir global işlev işaretçisi oluşturabilirsin. Fonksiyon göstergesini dahili fonksiyonun adresine göre başlatınız, böylece mevcut kod değiştirilmeden çalışacaktır. Global işlev işaretçisinin sembolünü dışa aktarın, modülünüz sadece modül yükü ve boşaltma süresinde atama ile değerini değiştirebilir.

+2

Genel bir kanca eklemeyi önerdiğiniz rotayı bitirdim. Tam olarak ihtiyacım olanı uygulamak ve sağlamak çok kolaydı. Sembol çözünürlüğü ile ilgili bilgiler için teşekkürler. Sembol tablosuna nasıl ve ne zaman erişildiğini kesin olarak açıklayan bir kaynak bulamadım (her fonksiyon çağrısında veya sadece bağlantıda). Bu yararlı bir tavsiyeydi. –

2

Bence istediğin şey Kprobe.

Kafenin bahsettiği bir başka yöntem de, orijinal rutine kanca eklemek ve modülde kancayı kaydettirmek/silmektir.

+0

Kprobe ilginç ve kullanışlı bir araç gibi görünüyor. Bağlantı için teşekkürler. Farklı bir rota aldığım halde, bunun etkili bir yaklaşım olabileceğini düşünüyorum. –

3

ksplice kullanmayı deneyebilirsiniz - statik olmayan yapmanız bile gerekmez.

+0

Bu ücretsiz değil. FOSS alternatifi var mı –

3

keresinde çekirdek fonksiyonu yerine onu kendi işlevini sokulan bir uçak kaçırma modülünün kavramının kanıtı yaptı. Yeni kernel tacing mimarisinin çok benzer bir sistem kullandığını görüyorum.

benim özel işlevine işaret eden bir atlama ile kod bayt ilk çift üzerine yazarak çekirdekte kendi işlevi enjekte etti. Gerçek fonksiyon çağrılır gelmez, onun yerine benim fonksiyonum atlar. Eğer bu tür şeyler denemeye çekirdek panik ve diğer felaket olaylar için dikkat edeceğiz zaman


#include <linux/module.h> 
#include <linux/kernel.h> 

#define CODESIZE 12 

static unsigned char original_code[CODESIZE]; 
static unsigned char jump_code[CODESIZE] = 
    "\x48\xb8\x00\x00\x00\x00\x00\x00\x00\x00" /* movq $0, %rax */ 
    "\xff\xe0"           /* jump *%rax */ 
     ; 
/* FILL THIS IN YOURSELF */ 
int (*real_printk)(char * fmt, ...) = (int (*)(char *,...))0xffffffff805e5f6e; 

int hijack_start(void); 
void hijack_stop(void); 
void intercept_init(void); 
void intercept_start(void); 
void intercept_stop(void); 
int fake_printk(char *, ...); 


int hijack_start() 
{ 
    real_printk(KERN_INFO "I can haz hijack?\n"); 
    intercept_init(); 
    intercept_start(); 

    return 0; 
} 

void hijack_stop() 
{ 
    intercept_stop(); 
    return; 
} 

void intercept_init() 
{ 
    *(long *)&jump_code[2] = (long)fake_printk; 
    memcpy(original_code, real_printk, CODESIZE); 

    return; 
} 

void intercept_start() 
{ 
    memcpy(real_printk, jump_code, CODESIZE); 
} 

void intercept_stop() 
{ 
    memcpy(real_printk, original_code, CODESIZE); 
} 

int fake_printk(char *fmt, ...) 
{ 
    int ret; 
    intercept_stop(); 
    ret = real_printk(KERN_INFO "Someone called printk\n"); 
    intercept_start(); 
    return ret; 
} 

module_init(hijack_start); 
module_exit(hijack_stop); 

ben uyarıyorum. Bunu sanallaştırılmış bir ortamda yapmanızı öneririm. Bu bir süre önce yazdığım bir konsept kanıtıdır, hala çalıştığından emin değilim.

Bu gerçekten kolay ilke, ama çok etkili. Elbette, gerçek bir çözüm, üzerine yazarken kimsenin fonksiyonu çağırmayacağından emin olmak için kilitleri kullanırdı.

İyi eğlenceler!