Как динамически распределить код функции? - PullRequest
0 голосов
/ 14 января 2019

На языке Си обычный маршрут для указателей на функции в качестве обратных вызовов из некоторой библиотеки состоит в том, чтобы включить указатель void* для контекста пользователя:

void (*fp)(void* ctx);

Позволяет библиотеке вызывать обратный вызов с контекстом ctx.

Допустим, я использую библиотеку, которая не включает указатель контекста в обратных вызовах. Мне понадобится один обратный вызов для каждого контекста. Каков наиболее переносимый способ динамического выделения указателей на функции в C для обеспечения обратных вызовов? Как мне malloc() код функции, который можно вызвать из библиотеки?

Например:

typedef void (*my_fp_t)(char);

my_fp_t fp = (my_fp_t) malloc(sizeof(...));
init_function_ptr(fp, "Hello, there");
my_library_callback(fp);
...

void my_library_callback(my_fp_t fp) {
  fp('a'); // prints "Hello, there"
}

Ответы [ 2 ]

0 голосов
/ 14 января 2019

Я думаю, что получил ответ.

Реальная проблема заключается в получении указателя контекста void* внутри обратного вызова, который не получает его. И я не могу использовать (в принципе) глобальные переменные, потому что потенциально вызывается много обратных вызовов, и я не могу определить, какая именно.

Я мог бы пойти по тому же маршруту, что и JIT VM: генерировать машинный код динамически для каждого возможного значения void * ctx, например:

void do_stuff(void* ctx) {
  // Finally have the ctx!
}

void* my_ctx;
my_fn_ptr p = allocate_function_that_calls_f_with_ctx(do_stuff, ctx);
library_register_callback_fn(p);

Здесь p - указатель на функцию с сигнатурой void (*)(void), которая при вызове по очереди вызывает f(ctx).

Однако это только для обратного вызова для библиотеки, и количество обратных вызовов невелико. Затем я мог бы написать набор функций, каждая из которых извлекает пустое значение * из таблицы, каждая из которых имеет свой индекс:

void do_stuff_0(void) { do_stuff(do_stuff_TABLE[0]); }
void do_stuff_1(void) { do_stuff(do_stuff_TABLE[1]); }
void do_stuff_2(void) { do_stuff(do_stuff_TABLE[2]); }
...

Я могу написать какой-нибудь простой скрипт, который записывает в новый файл .c все функции, которые идут от 0 до пары тысяч. Затем мне нужно будет отследить, какие функции уже зарегистрированы, а какие нет, и предоставить некоторый механизм для выбора следующих доступных и освобождения тех, которые больше не нужны.

0 голосов
/ 14 января 2019

Предполагая, что вы хотите выделить действительный код функции, а не указатель функции. Как это делается при написании загрузчиков и тому подобного:

  • Будьте абсолютно уверены в базовом соглашении о вызовах, используемом компилятором и ABI. Вам нужно будет точно знать, какие регистры и / или места в кадре стека, где все идет после вызова функции и возврата.
  • Создайте код на ассемблере, либо написав функцию в компиляторе C и аккуратно скопировав / вставив сборку, либо вручную написав функцию на ассемблере.
  • Перевести ассемблер на коды операций.
  • Убедитесь, что есть кусок памяти, где вы можете хранить данные и выполнять код. Это также зависит от настройки ABI и MMU.
  • Узнайте, как выделить память в этом пользовательском сегменте. Обычно включает в себя некоторые скрипты компоновщика и различные нестандартные ключевые слова. #pragma __declspec __attribute__ блабла с сахаром сверху. Сильно зависит от компилятора.
  • Выделите память для этой области в виде необработанного массива uint8_t func [n], содержащего необработанные коды OP в шестнадцатеричном формате.
  • Отключите строгие псевдонимы и другие подобные вещи преобразования злых указателей. Убедитесь, что ваш компилятор имеет определенное нестандартное расширение при переходе от указателей объектов к указателям на функции.
  • Позвоните по коду через ((func_ptr)func) ().

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...