Сериализировать указатель функции в C и сохранить его в файл? - PullRequest
0 голосов
/ 16 апреля 2020

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

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

Я предполагаю, что возможно сделать это C, поскольку он позволяет вам делать практически все, но я могу что-то упустить, могу ли я сделать это без системных вызовов вообще? Или, если нет, какой самый простой способ сделать это в posix?

Функции предоставляются при создании регистра или создании новых вторичных индексов:

registerHandler* createAndOpenRecordFile(
int overwrite, char *filename, int keyPos, fn_keyCompare userCompare, fn_serialize userSerialize, fn_deserialize userDeserialize, int type, ...)

И сохраняются как указатели функций:

typedef void* (*fn_serialize)(void*);
typedef void* (*fn_deserialize)(void*);
typedef int (*fn_keyCompare) (const void *, const void *);

typedef struct {
...
fn_serialize encode;
fn_deserialize decode;
fn_keyCompare compare;
} registerHandler;

1 Ответ

1 голос
/ 20 апреля 2020

Хотя ваша логика c имеет какой-то смысл, вещи намного, намного более сложные, чем это. Мой ответ будет содержать большинство комментариев, уже сделанных здесь, только в форме ответа ...

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

, что подводит нас к следующей точке , Учитывая, что ваш вопрос помечен posix, POSIX-совместимого способа загрузки кода в указанный c адрес не существует, есть MAP_FIXED, но он не будет работать, если вы не напишите свой собственный динамический c компоновщик , Почему это имеет значение? потому что код сборки функции может ссылаться на начальный адрес функции, по разным причинам, наиболее заметной из которых является то, что сама функция дает свой собственный адрес в качестве аргумента другой функции.

Что фактически приводит нас к следующей точке , Если сериализованная функция вызывает другие функции, вам также придется их сериализовать. Но это самая легкая часть. Трудная часть состоит в том, что функция переходит в середину другой функции, а не вызывает другую функцию, что может произойти, например, в результате оптимизации хвостового вызова. Это означает, что вы должны сериализовать все, к чему функция переходит (рекурсивно), но если функция переходит на 0x00000000ff173831, сколько байтов вы сериализуете с этого адреса?

В этом отношении, как вы узнаете, когда какая-нибудь функция заканчивается переносимым способом?

Еще хуже, вы даже гарантируете, что эта функция непрерывна в памяти? Конечно, все существующие, здравомыслящие аппаратные диспетчеры памяти ОС и аппаратные архитектуры делают его непрерывным в памяти, но гарантированно он будет таким через 1 год?

Еще одна проблема: что, если пользователь передает другую функцию на основе чего-то динамического c? т.е. если переменная окружения X равна true, нам нужна функция x(), в противном случае мы хотим y()?

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

Но мы поговорим о безопасности. Предполагая, что вам больше не требуется, чтобы пользователь указывал вам код, который мог содержать ошибку, исправленную в новой версии, вы будете продолжать использовать версию с ошибкой, пока пользователь не запомнит «refre sh». «ваши структуры данных с новым кодом.

И когда я говорю« ошибка »выше, вы должны прочитать« уязвимость безопасности ». Если уязвимая функция, которую вы сериализуете, запускает оболочку или действительно ссылается на что-то вне процессов, она становится постоянным эксплойтом.

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

Самый очевидный способ сделать это - попросить его передать имя файла библиотеки, которую вы затем откроете с помощью dlopen() .

Еще один способ сделать это - передать что-то вроде Lua или JavaScript строки и встроить механизм для выполнения этих строк в виде кода.

Еще один способ - передать пути к исполняемые файлы и выполняйте их, когда необходимо обработать данные. Это , что git делает .

Но, вероятно, вам следует просто потребовать, чтобы пользователь всегда передавал эти функции. Будьте проще.

...