2013-06-23 3 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

К сожалению, с течением времени вам, вероятно, будет запрещено выполнять ваш батут во все большем количестве систем, поскольку выполнение данных является довольно распространенным способом использования уязвимостей безопасности.

Я начал бы сообщать об ошибке автору библиотеки. Все должны знать лучше, чем предлагать интерфейс обратного вызова без частного параметра данных.

Имея такое ограничение, я бы дважды подумал о том, является ли библиотека реентерабельной. Я хотел бы предложить, чтобы вы могли иметь только один вызов за раз, и сохранить параметр обратного вызова в глобальной переменной.

Если вы считаете, что библиотека подходит для использования, вы можете расширить ее, написав n различные батуты обратного вызова, каждый из которых ссылается на свои собственные глобальные данные и обертывает их в каком-то управляющем API.

+0

Вы правы, но я просто cuirious :-) – jayven

+0

Ну, если вам не запретили выполнять вашу структуру, ваш батут сборки должен выполнить только один вызов вашего реального обработчика. Затем ваш обработчик может проверить адрес возврата вызова, чтобы узнать, где находится структура. – sh1

+0

Вы имеете в виду jmp? Или вызов? – jayven

Смежные вопросы