call

Тема в разделе "LANGS.C", создана пользователем Dukales, 5 июл 2009.

  1. Dukales

    Dukales New Member

    Публикаций:
    0
    Регистрация:
    5 июл 2009
    Сообщения:
    199
    В разрабатываемой программке есть компилятор из строки - математического выражения в непосредственно исполняемый код. функция получается такая, что не изменяет EBX. сразу за кодом возврата пушатся временно параметры, которые переполняли бы стек FPU (ну, если выражение сложное), потом, конечно, забираются из стека так, что к ret уже ESP указывает на то же место, что и после call. воспринимает два параметра в EAX и EDX (_fastcall Borland). эти регистры не изменяются кодом функции тоже. Так вот: мне нужно иметь возможность из этой функции вызывать любую функцию типа double _stdcall f(void), которая статически присутствует уже. Для этого нужно добавить call. Есть много разных call-ов:
    E8 cw CALL rel16 Call near, relative, displacement relative to next instruction
    E8 cd CALL rel32 Call near, relative, displacement relative to next instruction
    FF /2 CALL r/m16 Call near, absolute indirect, address given in r/m16
    FF /2 CALL r/m32 Call near, absolute indirect, address given in r/m32
    9A cd CALL ptr16:16 Call far, absolute, address given in operand
    9A cp CALL ptr16:32 Call far, absolute, address given in operand
    FF /3 CALL m16:16 Call far, absolute indirect, address given in m16:16
    FF /3 CALL m16:32 Call far, absolute indirect, address given in m16:32
    Мне нужен absolute, address given in operand. Операнд у меня есть - это &f - адрес точки входа. Никакие регистры явно я не хочу портить вообще, так что asm {mov EAX, offset f; call EAX;} меня не интересуют; как и вариант asm {push EAX, mov EAX, offset f; call EAX; pop EAX;} - этот ввиду своей неоптимальности (функция - целевая в задаче оптимизации).
    Я попробовал вариант с опкодом 9A CALL ptr16:32:
    __emit__(0x9A, &f, 0x00, 0x00);
    плохо понимая, что есть "16:" и заполняя его нулями получаю ошибку.
    Можно использовать конечно лишнюю переменную:
    void * pf = &f;
    и делать asm {call dword ptr [pf];}. Но из-за приступа перфекционизма и пытливости (надо же разобраться до конца!) не хочу создавать эту переменную. Да и call с address given in operand наверное быстрее выполняется, чем, если читать адрес из памяти (чуть-чуть :) ).
    Подскажите как сформировать опкод? Идеален был бы вариант CALL ptr32.
     
  2. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    Может быть, таки нужен "call near, relative, displacement relative to next instruction"? Пример:
    Код (Text):
    1. 1000:    db E8  34 12 00 00     ; call Destination. Destination == 1000 + 5 + 1234.
    2. ...
    3. 2239:    ; Destination
    "16:" определяет селектор. В твоём случае этот формат не нужен.
     
  3. Dukales

    Dukales New Member

    Публикаций:
    0
    Регистрация:
    5 июл 2009
    Сообщения:
    199
    нет. не этот. код функции может быть перемещён или копирован (это я говорю из общности - я не могу предположить как я буду модернезировать эту программку в дальнейшем) и relative изменится. Селектор как я понял означает, что адрес считается из комбинации EIP и этого селектора. У меня возникает ассоциация по этому поводу только такая: CS:EIP. При отладке я подсмотрел, что перед вызовом {__emit__(0x9A, &f, 0x00, 0x00);} CS = 0x001B. Делаю пробник так:
    __emit__(0x9A, &f, 0x1B, 0x00);
    - всё получается. Но вот вопрос тогда вот в чём: селектор получается что-то "выбирает", - судя по названию. То есть мы можем CS изменить и будет AV (ну или не мы - какая-нибудь функция, неизвестно как устроенная). Вот мне интересно, могу ли я рассчитывать на то, что CS в ходе исполнения программы не изменится? Позапускал программку несколько раз - одно и то же значение вроде-бы...
     
  4. Dukales

    Dukales New Member

    Публикаций:
    0
    Регистрация:
    5 июл 2009
    Сообщения:
    199
    всё. вопрос исчерпан. по тикам с этими селекторами значительно проигрывает вариант варианту с переменной
    FF /2 CALL r/m32 Call near, absolute indirect, address given in r/m32
     
  5. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    А, тогда да. Разве что хранить вместе с функцией смещения, по которым при перемещении фиксить адреса.


    Формально – нет. Фактически же вся 32-битная линейка NT использует селектор 1B для определения кодового сегмента, AFAIK. Но, опять-таки, если жёстко зашить селектор 1B, то будет крах при выполнении на x64 – тот сегмент там доступен только для нулевого кольца, да и не исполняемый он к тому же.
     
  6. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Код (Text):
    1.     call Delta
    2.  Delta:
    3.     add dword ptr [esp],(offset Return - offset Delta)
    4.     push BranchAddress
    5.     ret
    6.  Return:
     
  7. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    Clerk
    Не пойдёт. Один хочет создавать сверхпроизводительную функцию, а push'n'ret полностью пригнёт вверх предсказания возвратов. Теоретически; замеров мы не делали.
     
  8. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Sol_Ksacap
    Пусть разбавят другими инструкциями, тогда простоев не будет.
    Инструкции которые могут измянь кодовый селектор инициируют множество проверок, что сказывается на времени, поэтому наверно межсегментное ветвление не подходит.
    Тогда самый оптимальный вариант - модификация кода на лету, вычисляйте смещение динамически и записывайте его при формировании кода. В таком случае код будет не перемещаемым.
    Как показывает практика, в юзермоде при включённом планировании подобная оптимизация не имеет смысла.
     
  9. Dukales

    Dukales New Member

    Публикаций:
    0
    Регистрация:
    5 июл 2009
    Сообщения:
    199
    Статью нашёл http://www.insidepro.com/kk/116r.shtml. Знаток пишет, что есть некий опкод "EB YYh YYh YYh YYh" и это "непосредственный CALL". У меня этот опкод расшифровывается как jmp (signed char)YYh (первый) - всего 2 байта опкод - и дальше всякая белебирда. В чём Крис ошибся? или я ошибся?
     
  10. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Это опечатка, имеется ввиду относительный вызов E8 rel32

    Вот эта фраза тоже сомнительна:
    т.к. в современных процах рулит предсказание переходов и начиная со второго вызова функции проц будет переходить по ранее запомненному в BTB адресу не дожидаясь начала\завершения чтения [addr], а потеря 0.3-1 такта на само чтение [addr] это мелочь по сравнению даже с call+ret, не говоря уже о теле функции