我有一些 Delphi/汇编代码可以为 Win32、Win64 和 OSX 32 编译和工作正常(XE2)。但是,由于我需要它在 Linux 上工作,我一直在研究编译它的 FPC 版本(到目前为止,Win32 /64,Linux32/64)。
总的来说,它运行良好,但我无法开始工作的一件事是调用/跳转到 DelphiSystem
单元函数,如下所示:
jmp System.@FillChar
这似乎对 FPC Win32/Linux32 产生了预期的效果,但在 FPC Win64/Linux64 上失败并出现异常。(我对平台之间的调用约定差异非常熟悉,所以不要认为这是原因。)
在 x64 平台的 FPC 上执行此操作的正确方法是什么?
[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 System.@FillChar
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 System.@FillChar
上面的内容可以消除 x64 上的异常。
解决方案(感谢 FPK):
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 System.@FillChar
end;
这不是很漂亮,但它现在适用于 Delphi 和 FPC 的 Win/Linux 32/64。