2013-06-23 116 views
3

假設存在一個庫函數(無法修改),它接受一個回調(函數指針)作爲它的參數,這個函數將在未來的某個時刻被調用。我的問題:是否有一種方法可以將額外的數據與函數指針一起存儲,以便在調用回調函數時可以檢索額外的數據。該計劃在c。在c函數指針中存儲額外的數據

例如:

// callback's type, no argument 
typedef void (*callback_t)(); 

// the library function    
void regist_callback(callback_t cb);  

// store data with the function pointer 
callback_t store_data(callback_t cb, int data); 

// retrieve data within the callback 
int retrieve_data(); 

void my_callback() { 
    int a; 
    a = retrieve_data(); 
    // do something with a ... 
} 

int my_func(...) { 
    // some variables that i want to pass to my_callback 
    int a; 

    // ... regist_callback may be called multiple times 
    regist_callback(store_data(my_callback, a)); 
    // ... 
} 

問題是因爲callback_t接受任何參數。我的想法是每次生成一小段asm代碼以填充regist_callback,當它被調用時,它可以找到真正的回調函數和它的數據並將其存儲在堆棧(或一些未使用的寄存器)上,然後跳轉到真實回調,並在回調中,可以找到數據。

僞代碼:

typedef struct { 
    // some asm code knows the following is the real callback 
    char trampoline_code[X]; 
    callback_t real_callback; 
    int data;  
} func_ptr_t; 

callback_t store_data(callback_t cb, int data) { 
    // ... malloc a func_ptr_t 
    func_ptr_t * fpt = malloc(...); 

    // fill the trampoline_code, different machine and 
    // different calling conversion are different 
    // ... 

    fpt->real_callback = cb; 
    fpt->data = data; 

    return (callback_t)fpt; 
} 

int retrieve_data() { 
    // ... some asm code to retrive data on stack (or some register) 
    // and return 
} 

是否合理?以前有沒有爲這個問題做過工作?

+0

爲什麼不讓store_date()在全局映射中存儲原函數的地址以及對'a'的引用並通過'retrieve_data()'檢索? – alk

+0

如果多次註冊相同的回調,當回調被調用時,它不知道哪一個回調。 – jayven

+0

是的,這個用例不能用這種方式來覆蓋。但是,您可以通過爲每個同時註冊的實例實現包裝來解決此問題。或者只是使用cb函數地址的一些未使用的低位(或甚至高位)位來添加子索引... - 8字節指針很寬;-) – alk

回答

2

不幸的是,隨着時間的推移,您可能會被禁止在越來越多的系統中執行您的蹦牀,因爲執行數據是利用安全漏洞的常見方式。

我首先向圖書館的作者報告錯誤。每個人都應該知道比提供沒有私人數據參數的回調接口更好。

有這樣的限制會讓我三次考慮圖書館是否是可重入的。我建議確保一次只能有一個未完成的調用,並將回調參數存儲在全局變量中。

如果您認爲該庫適合使用,那麼可以通過編寫不同的回調蹦牀來擴展此範圍,每個回調蹦牀都引用它們自己的全局數據,並將其包裝在一些管理API中。

+0

你是對的,但我只是cuirious :-) – jayven

+0

那麼,如果你沒有被禁止執行你的結構,你的組件蹦牀只需要對你的真正的處理程序執行一次調用。然後,您的處理程序可以檢查通話的返回地址,以找出結構的位置。 – sh1

+0

你的意思是jmp嗎?還是打個電話? – jayven