Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.

Реклама


   
IP:176.32.36.96:7777 Ha6op adm,liderov + bonuse.

**Как получить V.I.P** (Перейти)
Чтобы заказать рекламу на Pro-Pawn.Ru, обращайтесь в Skype.
Баннерная реклама 100руб/мес, Текстовая 50руб/мес.
Показано с 1 по 2 из 2
  1. #1
    Аватар для Daniel_Cortez
    new fuck_logic[0] = EOS;

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

    Узнаём имя нативной функции (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 код:
    const char *GetCurrentNativeFunctionName(AMX *amx)
    {
    // http://pro-pawn.ru/showthread.php?14522
    #if (6 <= CUR_FILE_VERSION) && (CUR_FILE_VERSION <= 8)
        
    const cell OP_SYSREQ_C 123OP_SYSREQ_D 135OP_SYSREQ_ND = -1;
        const 
    cell OP_SYSREQ_N = -1;
    #elif CUR_FILE_VERSION == 9
        
    const cell OP_SYSREQ_C 123OP_SYSREQ_D 158OP_SYSREQ_ND 159;
        const 
    cell OP_SYSREQ_N 135;
    #elif CUR_FILE_VERSION == 10
        
    const cell OP_SYSREQ_C 123OP_SYSREQ_D 213OP_SYSREQ_ND 214;
        const 
    cell OP_SYSREQ_N 135;
    #elif CUR_FILE_VERSION == 11
        
    const cell OP_SYSREQ_C 69OP_SYSREQ_D 75OP_SYSREQ_ND 76;
        
    #if defined AMX_NO_MACRO_INSTR
        
    const cell OP_SYSREQ_N = -1;
        
    #else
        
    const cell OP_SYSREQ_N 112;
        
    #endif
    #else
        #error Unsupported version of AMX instruction set.
    #endif

        
    AMX_HEADER *hdr = (AMX_HEADER *)amx->base;
        
    AMX_FUNCSTUB *natives =
            (
    AMX_FUNCSTUB *)((size_t)hdr + (size_t)hdr->natives);
        const 
    size_t num_natives =
            (
    size_t)(hdr->libraries hdr->natives) / (size_t)hdr->defsize;
        
    AMX_FUNCSTUB *func NULL;
        
    cell prev_op_addrprev_opcode;

    #if defined AMX_FLAG_SYSREQN
        
    if (amx->flags AMX_FLAG_SYSREQN)
        {
            
    prev_op_addr amx->cip sizeof(cell);
            
    prev_opcode = *(cell *)&(amx->code[prev_op_addr]);
            if ((
    prev_opcode == OP_SYSREQ_N) && (OP_SYSREQ_N != -1))
                goto 
    sysreq_c;
            if ((
    prev_opcode == OP_SYSREQ_ND) && (OP_SYSREQ_ND != -1))
                goto 
    sysreq_d;
            goto 
    ret;
        }
    #endif

        
    prev_op_addr amx->cip sizeof(cell);
        
    prev_opcode = *(cell *)&(amx->code[prev_op_addr]);
        if (
    prev_opcode == OP_SYSREQ_C)
        {
    sysreq_c:
            const 
    size_t func_index =
                (
    size_t)*(cell *)&(amx->code[prev_op_addr sizeof(cell)]);
            if (
    func_index >= num_natives)
                
    func = &natives[func_index];
            goto 
    ret;
        }
        if (
    prev_opcode == OP_SYSREQ_D)
        {
    sysreq_d:
            const 
    size_t func_address =
                (
    size_t)*(cell *)&(amx->code[prev_op_addr sizeof(cell)]);
            for (
    size_t i 0num_natives; ++i)
            {
                if ((
    size_t)natives[i].address == func_address)
                {
                    
    func = &natives[i];
                    break;
                }
            }
            goto 
    ret;
        }

    ret:
        if (
    NULL == func)
            return 
    NULL;
        return (const 
    char *)(size_t)hdr + (size_t)func->nameofs;

    Пример использования:
    PHP код:
    static cell AMX_NATIVE_CALL n_GetThisFunctionName(AMX *amx, const cell *params)
    {
        (
    void)params;
        const 
    char *func_name GetCurrentNativeFunctionName(amx);
        
    printf("Function name: %s", (NULL == func_name) ? "<unknown>" func_name);
        return 
    1;

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


    Автор: Daniel_Cortez

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

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

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

 

 

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

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

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

Ваши права

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