Win32、Win64、および OSX 32 用にコンパイルして正常に動作する (XE2) Delphi/アセンブリ コードがいくつかあります。ただし、Linux で動作させる必要があるため、FPC バージョンのコンパイルを検討しています (これまでのところ、Win32 /64、Linux32/64)。
概して、それはうまく機能しますが、私がうまくいかなかったのはSystem
、次のような Delphi ユニット関数への呼び出し/ジャンプです。
jmp System.@FillChar
これは FPC Win32/Linux32 では望ましい効果があるように見えますが、FPC Win64/Linux64 では例外で失敗します。(私はプラットフォーム間の呼び出し規約の違いに精通しているので、それが理由だとは思いません。)
x64 プラットフォームの FPC でこれを行う正しい方法は何ですか?
[Edit1] --- David のコメントに応えて、問題を説明する単純化されたプログラムを次に示します (少なくとも、正確にそうなることを願っています)。
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
この 2 つによってコンパイルされたバージョンでレジスタの配置が異なる場合があります。解決策は、この違いを回避することです。これを行う 1 つの方法は、上記の 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 で機能するようになりました。