gpt4 book ai didi

delphi - 使用 ASM 调用对象方法 - 第 2 部分

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

这个问题基于previous ,但这仅供引用。

我已经设法让它工作,但是,我发现了一些我不清楚的东西,所以如果有人能解释以下行为,那就太棒了。

我有以下类(class):

type
TMyObj = class
published
procedure testex(const s: string; const i: integer);
end;

procedure TMyObj.testex(const s: string; const i: integer);
begin
ShowMessage(s + IntToStr(i));
end;

以及以下两个过程:

procedure CallObjMethWorking(AMethod: TMethod; const AStrValue: string; const AIntValue: Integer);
begin
asm
PUSH DWORD PTR AIntValue;
PUSH DWORD PTR AStrValue;
CALL AMethod.Code;
end;
end;

procedure CallObjMethNOTWorking(AInstance, ACode: Pointer; const AStrValue: string; const AIntValue: Integer);
begin
asm
MOV EAX, AInstance;
PUSH DWORD PTR AIntValue;
PUSH DWORD PTR AStrValue;
CALL ACode;
end;
end;

为了测试工作版本,需要调用以下命令:

procedure ...;
var
LObj: TMyObj;
LMethod: TMethod;
LStrVal: string;
LIntVal: Integer;
begin
LObj := TMyObj.Create;
try
LMethod.Data := Pointer( LObj );
LMethod.Code := LObj.MethodAddress('testex');

LStrVal := 'The year is:' + sLineBreak;
LIntVal := 2012;

CallObjMethWorking(LMethod, LStrVal, LIntVal);
finally
LObj.Free;
end; // tryf
end;

为了测试工作版本:

procedure ...;
var
LObj: TMyObj;
LCode: Pointer;
LData: Pointer;
LStrVal: string;
LIntVal: Integer;
begin
LObj := TMyObj.Create;
try
LData := Pointer( LObj );
LCode := LObj.MethodAddress('testex');

LStrVal := 'The year is:' + sLineBreak;
LIntVal := 2012;

CallObjMethNOTWorking(LData, LCode, LStrVal, LIntVal);
finally
LObj.Free;
end; // tryf
end;

最后一个问题:为什么 CallObjMethNOTWorking 不工作,而 CallObjMethWorking 工作?我猜测编译器对待 TMethod 的方式有些特殊...但由于我的汇编知识有限,我无法理解它。

如果有人能向我解释一下,我将不胜感激,谢谢!

最佳答案

Henrick Hellström 的 answer 是正确的,我注意到您的问题被标记为 Delphi 2010,因此仅涉及 Win32。但是,您可能有兴趣了解如果继续使用 Win64 (Delphi >= XE2),情况会是什么样子,因此我在 Henrick 的代码中添加了一个示例 Win64 版本:

procedure CallObjMeth(AInstance, ACode: Pointer; const AStrValue: string; const AIntValue: Integer); stdcall;
asm
{$IFDEF CPU386}
MOV EAX, AInstance;
MOV EDX, DWORD PTR AStrValue;
MOV ECX, DWORD PTR AIntValue;
{$IFDEF MACOS}
//On MacOSX32 ESP = #######Ch here
SUB ESP, 0Ch
{$ENDIF}
CALL ACode;
{$IFDEF MACOS}
ADD ESP, 0Ch // restoring stack
{$ENDIF}
{$ENDIF}
{$IFDEF CPUX64}{$IFDEF WIN64} // <- see comments
.NOFRAME //Disable stack frame generation
//MOV RCX, AInstance {RCX} //<- not necessary because AInstance already is in RCX
MOV R10, ACode {RDX}
MOV RDX, AStrValue {R8}
MOV R8D, AIntValue {R9D}
SUB RSP, 28h //Set up stack shadow space and align stack: 4*8 bytes for 4 params + 8 bytes bytes for alignment
{$IFNDEF DO_NOT_TEST_STACK_ALIGNMENT}
MOVDQA XMM5, [RSP] //Ensure that RSP is aligned to DQWORD boundary -> exception otherwise
{$ENDIF}
CALL R10 //ACode
ADD RSP, 28h //Restore stack
{$ENDIF}{$ENDIF}
end;

需要做一些解释性说明:

1) ASM 语句:在 Delphi XE2 x64 中,没有混合使用 pascal 和 asm 代码,因此编写汇编代码的唯一方法是在一个例程中由单个 asm..end block 组成,没有 begin..end。请注意,32 位 asm 代码周围的 begin..end 也确实有影响。具体来说,您强制生成堆栈帧并让编译器制作函数参数的本地副本。 (如果您首先使用汇编,您可能不希望编译器这样做。)

2) 调用约定:在 Win64 上,只有一个调用约定。像 registerstdcall 这样的东西实际上是没有意义的;都是一样的,Microsoft's Win64 calling convention 。本质上是这样的:参数在RCXRDXR8R9寄存器中传递(和/或XMM0-XMM4,返回RAX/XMM0中的值。大于64位的值通过引用传递。

被调用的函数可以使用:RAX、RCX、RDX、R8-R11、ST(0)-ST(7)、XMM0-XMM5、YMM0-YMM5、YMM6H-YMM15H,并且必须保留RBX、RSI、RDI、RBP、R12-R15、XMM6-XMM15。在适当的情况下,被调用的函数需要发出 CLD/EMMS/VZEROUPPER 指令将 CPU 恢复到预期状态。

3) 对齐和阴影空间重要的是,每个函数在堆栈上都有自己的影子空间,即使没有参数并且无论被调用的函数是否实际接触它,这至少是 4 个 QWORD 参数的堆栈空间。此外,在每个函数调用的位置(在每个 CALL 语句处),RSP 预计为 16 字节对齐(对于 ESP 也是如此)顺便说一句,在 MacOSX32 上)。这通常会导致类似这样的结果:sub rsp, ##;调用$$; add rsp, ## 构造,其中 ## 是要调用函数的 (QWORD) 参数的总和,加上用于对齐 RSP 的可选 8 个字节。请注意,CALL 处的 RSP 对齐会在函数输入时导致 RSP = ###8h(因为 CALL code> 将返回地址放入堆栈中),因此假设在您之前没有人弄乱 RSP,您可以预期它会是这样。

在提供的示例中,SSE2 MOVDQA 指令用于测试 RSP 的对齐情况。 (XMM5用作目标寄存器,因为它可以自由修改,但不能包含任何函数参数数据)。

4)假设这里的代码假设编译器不会插入代码来更改RSP。在某些情况下,这可能不正确,因此请小心做出这种假设。

5) 异常处理 Win64 中的异常处理有点复杂,应该由编译器正确完成(上面的示例代码没有这样做)。为了允许编译器执行此操作,理想情况下您的代码应使用新的 BASM 指令/伪指令 .PARAMS.PUSHNV.SAVENVAllen Bauer here所述。在正确(错误)的情况下,否则可能会发生不好的事情。

关于delphi - 使用 ASM 调用对象方法 - 第 2 部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9454054/

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