Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Показано с 1 по 3 из 3
  1. #1
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±

    Узнаём имя нативной функции (AMX)

    Недавно (буквально сегодня, на момент написания сей статьи) меня попросили поделиться способом того, как во время выполнения нативной функции узнать её название.
    Самым простым вариантом может показаться использование встроенной константы __func__, но название функции в исходном коде на C/C++ может не соответствовать названию, под которым эта функция регистрируется в AMX.
    Не зря говорят: самое простое не всегда самое правильное.

    Самым логичным решением будет узнать название функции из AMX, но сделать это не так-то просто, поскольку в AMX не предусмотрено функций для таких целей, и все манипуляции с информацией о скрипте придётся делать самостоятельно.
    Для тех, кому интересно, я разъясню алгоритм получения названия, а заодно несколько базовых принципов работы AMX, которые попутно понадобятся.

     (Не)много теории

    Секция кода в AMX представляет собой массив ячеек - одна ячейка на код операции и ещё по одной на аргументы опкода (есть ещё упакованные опкоды, в которых код операции и 1-й аргумент умещаются в одну ячейку, но в SA:MP используется старая версия AMX, в которой таких опкодов нет).
    Нативная функция может быть вызвана с помощью нескольких разных опкодов:
    1. SYSREQ.c - вызывает функцию по указанному индексу для таблицы нативных функций (индекс передаётся в виде аргумента опкода). Имеет 1 аргумент.
    2. SYSREQ.d - вызывает нативную функцию по её физическому адресу. Компилятор не генерирует этот опкод - вместо этого интерпретатор заменяет опкоды SYSREQ.c на SYSREQ.d во время загрузки скрипта, чтобы ускорить процесс вызова нативных функций (проще вызвать функцию по заранее известному адресу, чем при каждом вызове заново выискивать этот адрес по индексу в таблице). Имеет 1 аргумент.
    3. SYSREQ.n - эквивалентен комбинации опкодов SYSREQ.c и STACK (эти опкоды часто используются вместе, один для вызова функции, другой - для высвобождения места в стеке, использовавшегося для передачи аргументов функции). Комбинированием двух инструкций в одну макроинструкцию достигается большая компактность кода и производительность. Имеет 2 аргумента - индекс нативной функции и кол-во байт, высвобождаемых в стеке. Впервые появился в Pawn 3.2.
    4. SYSREQ.nd - эквивалентен комбинации SYSREQ.d + STACK. Впервые появился в Pawn 3.2.
    5. SYSREQ.pri - вызывает функцию по индексу, записанному в регистре PRI. Имеет 0 аргументов. Удалён из набора инструкций в версии Pawn 4.0. Когда нативная функция вызвана с помощью этого опкода, значение регистра PRI не сохраняется в структуре AMX и нельзя получить актуальное значение этого регистра. Иными словами, при таком варианте нельзя узнать ни индекс функции, ни её физический адрес, поэтому здесь этот вариант рассматриваться не будет.

    Во время выполнения нативной функции регистр CIP (amx->cip) указывает на опкод, следующий после SYSREQ*.
    Также в структуре AMX есть поле flags, в котором указываются флаги скрипта, сигнализирующие о присутствии отладочной информации, зашифрованности скрипта и т.д.
    Один из этих флагов - AMX_FLAG_SYSREQN - говорит о том, что в скрипте вместо SYSREQ.c и SYSREQ.d используются только макроинструкции SYSREQ.n и SYSREQ.nd.
    Стоит учесть, что этот флаг, а также опкоды SYSREQ.n и SYSREQ.nd существуют только в Pawn 3.2 и более поздних версиях (в SA:MP этого нет, т.к. там используется версия 3.0).

    Сначала нужно узнать, с помощью какого именно опкода была вызвана нативная функция. Поскольку CIP указывает на следующий опкод после текущего, нужно узнать адрес предыдущего опкода - это и будет нужный нам опкод SYSREQ*.
    Если в скрипте был установлен флаг AMX_FLAG_SYSREQN, то для вызова нативных функций используются только опкоды SYSREQ.n и SYSREQ.nd, имеющие по 2 аргумента, а значит от CIP нужно отнять размер одного опкода и двух аргументов, т.е. размер (1 + 2) ячеек => (3 * sizeof(cell)) байт.
    Иначе, если флаг AMX_FLAG_SYSREQN не установлен, для вызова нативных функций используются только инструкции SYSREQ.c и SYSREQ.d, имеющие 1 аргумент, и от CIP нужно отнять размер 1 + 1 ячеек => (2 * sizeof(cell)) байт.

    После того, как адрес инструкции получен, считаем код операции из секции кода.
    Если это операция SYSREQ.c или SYSREQ.n - берём индекс функции из 1-го аргумента опкода и по этому индексу из таблицы нативных функций получаем запись с информацией о нужной нативной функции (AMX_FUNCSTUB).
    Если же имеем дело с опкодом SYSREQ.d или SYSREQ.nd - из 1-го аргумента получаем физический адрес функции и выискиваем запись с таким же адресом среди всех записей в таблице нативных функций.

    Когда нужная запись с информацией о нативной функции найдена, дело остаётся за малым - по атрибуту nameofs получить физический адрес, по которому расположена строка с названием функции.
    Совсем не сложно (нет).


    Реализацию вышеописанного алгоритма можно найти здесь:
    Пример использования:
    PHP код:
    #include "pluginutils.h"

    // ...

    static cell AMX_NATIVE_CALL n_GetThisFunctionName(AMX *amx, const cell *params)
    {
        (
    void)params;
        const 
    char *func_name pluginutils::GetCurrentNativeFunctionName(amx);
        
    printf("Function name: %s", (func_name != NULL) ? func_name "(unknown)");
        return 
    1;

    P.S.: Используйте на свой страх и риск. Я не предоставляю никаких гарантий того, что эта функция будет работать правильно, и не несу ответственности за возможный ущерб в результате её использования (в том числе неправильного). Эта функция чисто экспериментальная, и хотя я сделал так, чтобы она работала на всех версиях Pawn от 3.0 до 4.0, проверялась она только при работе с 4.0.


    Автор: Daniel_Cortez

    Специально для Pro-Pawn.ru
    Копирование данной статьи на других ресурсах без разрешения автора запрещено!
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  2. 4 пользователя(ей) сказали cпасибо:
    BadPawn (20.11.2016) Desulaid (20.11.2016) oukibt (01.06.2020) VVWVV (19.11.2016)
  3. #2
    Аватар для BadPawn
    Пользователь

    Статус
    Оффлайн
    Регистрация
    15.01.2016
    Адрес
    Приморье, Спасск-Дальний
    Сообщений
    165
    Репутация:
    7 ±
    Это жесть. ) Спасибо

  4. #3
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Обновил статью. Функция GetCurrentNativeFunctionName теперь работает с Pawn 3.2 и 3.0, как под Windows, так и под Linux, и теперь её можно использовать в плагинах для SA-MP. К сожалению, не получилось сделать так, чтобы функция работала в 4.0 с альтернативными ядрами AMX (в том числе с оптимизированным для GCC), т.к. там нет адекватного способа получить таблицу переходов. Тем не менее, в Pawn 4.0 функция работает со стандартным ядром (ANSI C version).
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

 

 

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •