У меня есть код Delphi/сборки, который компилируется и работает отлично (XE2) для Win32, Win64 и OSX 32. Однако, поскольку мне нужно, чтобы он работал в Linux, я был глядя на компиляцию его версий FPC (до сих пор Win32/64, Linux32/64).Абонентские вызовы функций системного блока на FreePascal x64
По большому счету, он хорошо работает, но одна вещь, которую я не смог попасть на работу являются звонки/перескакивает на Delphi System
единицы функции, такие как, например:
jmp [email protected]
Это, как представляется, имеют желаемый эффект на FPC Win32/Linux32, но сбой с исключением на FPC Win64/Linux64. (Я хорошо знаком с развязкой вызывающих конвенций среди платформ, поэтому не думаю, что это причина.)
Каков правильный способ сделать это на FPC для платформ x64?
[Edit1] --- В ответ на комментарий Дэвида, вот упрощенная программа, которая иллюстрирует эту проблему (по крайней мере, я надеюсь, что он делает это точно):
program fpcx64example;
{$IFDEF FPC}
{$MODE DELPHI}
{$ASMMODE INTEL}
{$ELSE}
{$APPTYPE CONSOLE}
{$ENDIF}
procedure FillMemCall (p: pointer; len: longword; val: byte);
asm
// this function and the System function have the same parameters
// in the same order -- they are already in their proper places here
jmp [email protected]
end;
function MakeString (c: AnsiChar; len: longword): AnsiString;
begin
Setlength (Result, len);
if len > 0 then FillMemCall (PAnsiChar(Result), len, byte(c));
end;
begin
try
writeln (MakeString ('x',10));
except
writeln ('Exception!');
end;
end.
компилировать с FPC : [Win32:] fpc.exe fpcx64example.dpr
, [Win64:] ppcrossx64.exe fpcx64example.dpr
, [linux32:] fpc.exe -Tlinux -XPi386-linux- -FD[path]\FPC\bin\i386-linux fpcx64example.dpr
, [Linux64:] ppcrossx64.exe -Tlinux -XPx86_64-linux- -FD[FPCpath]\bin\x86_64-linux fpcx64example.dpr
.
Прекрасно работает с Delphi (Win32/64). Для FPC удаление jmp [email protected]
выше избавляет от исключения на x64.
Раствор (Благодаря ФПК):
Delphi и FPC не создают фреймов стека для функций в соответствии с теми же самыми условиями, так что RSP
регистр может иметь различное выравнивание в версиях, составленных два. Решение состоит в том, чтобы избежать этой разницы. Один из способов сделать это так, для примера FillMemCall выше, будет выглядеть, например:
{$IFDEF CPU64} {$DEFINE CPUX64} {$ENDIF} // for Delphi compatibility
procedure FillMemCall (p: pointer; len: longword; val: byte);
{$IFDEF FPC} nostackframe; {$ENDIF} //Force same FPC behaviour as in Delphi
asm
{$IFDEF CPUX64}
{$IFNDEF FPC} .NOFRAME {$ENDIF} // To make it explicit (Delphi)...
// RSP = ###0h at the site of the last CALL instruction, so
// since the return address (QWORD) was pushed onto the stack by CALL,
// it must now be ###8h -- if nobody touched RSP.
movdqa xmm0, dqword ptr [rsp + 8] // <- Testing RSP misalignment -- this will crash if not aligned to DQWORD boundary
{$ENDIF}
jmp [email protected]
end;
Это не совсем красиво, но теперь он работает на Win/Linux 32/64 как для Delphi и FPC.
Некоторые могут задаться вопросом, почему, когда он имеет те же параметры, что и FillChar в том же порядке, FillMemCell существует вообще. Почему бы просто не назвать FillChar? –
Кажется, что проблема с реализацией FillChar или Writeln в x64, поскольку она вызывает исключение, если вы вызываете ее напрямую без функции MakeString. –
['StringOfChar'] (http://www.freepascal.org/docs-html/rtl/system/stringofchar.html), похоже, делает то, что делает ваша функция. –