Делфи+ассемблер 64-бит: вызов функции

grey702
Дата: 13.08.2019 13:00:51
Нужен совет гуру по встроенному ассемблеру!

Есть метод для вызова внешней функции:
function  ExtCall(StackData: Pointer; Addr: Pointer; StackSize: Integer): HResult;
  asm
      PUSH    EDI
      MOV     EDI,Addr
      SUB     ESP,StackSize  
      MOV     EDX,ESP       // адрес вершины стека пишем в EDX, чтобы далее переместить туда StackSize данных с адреса StackData командой MOVE
      CALL    MOVE          // Move(D, S, n);  // D - EAX (StackData: Pointer); S - EDX (там теперь лежит адрес вершины стека); n - к-во байт (StackSize) - в ЕСХ
      CALL    EDI
      POP     EDI
end;


Addr - адрес функции, которую вызываем. StackData - адрес, по которому в памяти лежат параметры в определенном порядке, как ожидает функция. StackSize - необходимый размер стека.
Все это нормально работает в 32-битном варианте.
Теперь встал вопрос перехода на 64 бита, и, естественно, работать перестало, поскольку соглашение о вызовах другое, и параметры должны быть в регистрах RCX, RDX и т д, а не в стеке.

Вопрос - есть ли возможность скопировать параметры в регистры (исходя из того, что имеем только адрес, по кторому они в памяти), не меняя полностью весь механизм? Я изучал хелпы, нашел вот такую вещь:
.PARAMS <number>
Used when calling external functions to setup the register parameter backing store as per the x64 calling convention as this is not normally done by default. When used, a pseudo-variable, @params, is available for passing stack params to called functions. Use @params as a byte array where the first stack parameter will be @params[32], locations 0-31 represent the 4 register parameters.
Но как пользоваться не понял, описания нормального не нашел.
white_nigger
Дата: 13.08.2019 13:51:18
А чем стандартный Move не угодил?
kealon(Ruslan)
Дата: 13.08.2019 14:13:36
grey702,

читай механизм вызова под x64.
4 первых параметра надо протолкнуть в регистры, но всё равно выделить стек под них
grey702
Дата: 13.08.2019 14:39:27
Читал... там еще дополнительно XMM
Может, нормальную доку про .PARAMS знаете? как это вообще работает?
kealon(Ruslan)
Дата: 13.08.2019 15:03:08
_Den_Z_
Дата: 13.08.2019 16:36:55
grey702
Читал... там еще дополнительно XMM
Может, нормальную доку про .PARAMS знаете? как это вообще работает?


Так вы же сами привели кусок доки, там все и описано.
.PARAMS - устанавливает кол-во переданных параметров в функцию. Инструкция генерирует смещение стека.
@params - позволяет адресоваться к выделенному стеку как к массиву байт. 0-31 первые 4 регистра rcx, rdx, r8, r9.

Судя по доке должно быть что-то вроде:
      .PARAMS 3
      // сохраняет регистры
      MOV     @params[24], RCX
      MOV     @params[16], RDX
      MOV     DWORD(@params[8]), r8d      
      // что-то делает
      ....
     // копирует сохраненные в стеке значения в регистры
      MOV     RCX, @params[24]
      MOV     RDX, @params[16]
      MOV     r8d, DWORD(@params[8])

      CALL    MOVE
kealon(Ruslan)
Дата: 13.08.2019 16:51:36
grey702,

подсмотри как делают в System.Rtti.Invoke - 21943123
grey702
Дата: 15.08.2019 10:07:08
kealon(Ruslan),
Там приходит уже готовый массив параметров... Я попробовал переделать на Invoke, проблема другая - Invoke с var параметрами нормально не работает, надо крутить через указатели, мне переписывать 100500 функций. Так что смотрю опять в сторону ассемблера
kealon(Ruslan)
Дата: 15.08.2019 10:46:16
grey702,

модификатор var это всего лишь ссылка, в вашем оригинале я не вижу какой-то особой обработки ссылок - параметры формируются где-то во вне, т.е. то же самое.

основная мысль такая, вы же stdcall пытаетесь вызывать?
если это стандартные процы, я думаю там не будет значений больше 8 байт, очень какой-то скользкий вопрос с ними
т.е. разбить ваш входной массив на переменные int64 и отправить через invoke для начала
grey702
Дата: 16.08.2019 12:18:25
Пришел я к вот такой конструкции:

{$ifdef win64} // rcx //rdx // r8
function ExtCall(p_StackData: Pointer; p_Addr: Pointer; p_StackSize: NativeUInt): HResult;
asm
SUB RSP, 32+8

MOV [RBP+$210], p_Addr
// stack
SUB RSP, p_StackSize // место в стеке под даные
MOV RCX, p_StackData // указатель на параметры в RCX
MOV RDX, RSP // вершину стека в RВX
MOV R8, p_StackSize // количество байт в R8
CALL MOVE // перемещаем параметры в стек (Move(source, dest, n); // source - RCX, dest - RDX, n - R8)
// get parameters
POP RCX // выталкиваем параметры в регистры - первые 4, остальные остаются в стеке
POP RDX
POP R8
POP R9

CALL [RBP+$210]

ADD RSP, 32+8
end;

На первый вгляд задачу она решает - вызывается функция, про которую знаем только адрес, и адрес, по которому лежат параметры (тип их и количество неизвестно, имеем только их размер для помещения в стек).
В ассемблере я слаб, особенно в 64битном варианте. Если что подскажете - буду очень благодарен)