Phi*_*hiS 4 delphi 64-bit assembly freepascal basm
我有一些Delphi /汇编代码为Win32,Win64和OSX 32编译和工作正常(XE2).但是,因为我需要它在Linux上工作,我一直在寻找它的FPC版本(到目前为止,Win32)/64,Linux32/64).
总的来说,它运行良好,但我无法工作的一件事是调用/跳转到Delphi System单元函数,如:
jmp System.@FillChar
Run Code Online (Sandbox Code Playgroud)
这似乎对FPC的Win32/linux32镜像预期的效果,但无法与异常上FPC Win64的/ LINUX64.(我非常熟悉平台之间的调用约定差异,所以不要认为这就是原因.)
在FPC for x64平台上执行此操作的正确方法是什么?
[编辑1] ---回应大卫的评论,这是一个简化的程序,说明了问题(至少我希望它准确地说):
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.
Run Code Online (Sandbox Code Playgroud)
用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;
Run Code Online (Sandbox Code Playgroud)
这不是很漂亮,但现在适用于Delphi和FPC的Win/Linux 32/64.
简短回答:正确的方法是使用调用指令.
答案很长:x86-64代码要求堆栈是16字节对齐的,所以FillMemCall在入口点包含一个编译器生成的子rsp,8和一个add rsp,8在出口处(其他8个字节被添加/删除呼叫/转发对).另一方面,Fillchar是手工编码的汇编程序,并且使用nostackframe指令,因此它不包含编译器生成的子/添加对,并且很快就会填充fillchar,堆栈被搞乱,因为FillChar之前不包含添加rsp,8 ret指令.
解决方法就像在FillMemCall中使用nostackframe指令或在执行jmp之前调整堆栈一样,但可能会受到任何未来编译器更改的影响.