gpt4 book ai didi

delphi - FreePascal x64 上系统单元函数的汇编调用

转载 作者:行者123 更新时间:2023-12-03 15:06:47 27 4
gpt4 key购买 nike

我有一些 Delphi/汇编代码,可以在 Win32、Win64 和 OSX 32 上编译并正常工作 (XE2)。但是,由于我需要它在 Linux 上工作,所以我一直在考虑编译它的 FPC 版本(所以目前为止,Win32/64、Linux32/64)。

总的来说,它工作得很好,但我无法开始工作的一件事是调用/跳转到 Delphi System 单元函数,如下所示:

  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[路径]\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。

最佳答案

简短回答:正确的方法是使用调用指令。

长答案:x86-64代码要求堆栈是16字节对齐的,因此FillMemCall在入口点包含编译器生成的sub rsp,8,并在导出处包含add rsp,8(添加其他8个字节/通过调用/返回对删除)。另一方面,Fillchar 是手工编码的汇编程序,并使用 nostackframe 指令,因此它不包含编译器生成的 sub/add 对,一旦 fillchar 离开,堆栈就会困惑,因为 FillChar 之前不包含 add rsp,8 ret 指令。

可能可以使用 FillMemCall 的 nostackframe 指令或在执行 jmp 之前调整堆栈等解决方法,但可能会被任何 future 的编译器更改所破坏。

关于delphi - FreePascal x64 上系统单元函数的汇编调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16559833/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com