Delphi 汇编器/RTTI 专家 : Can I obtain the memory address and type info of the implied Result variable in a function?

标签 delphi rtti basm

考虑这个典型的方法跟踪代码(为了说明而简化):

type
  IMethodTracer = interface
  end;

  TMethodTracer = class(TInterfacedObject, IMethodTracer)
  private
    FName: String;
    FResultAddr: Pointer;
    FResultType: PTypeInfo;
  public
    constructor Create(
      const AName: String;
      const AResultAddr: Pointer = nil;
      const AResultType: PTypeInfo = nil);
    destructor Destroy; override;
  end;

constructor TMethodTracer.Create(
  const AName: String;
  const AResultAddr: Pointer;
  const AResultType: PTypeInfo);
begin
  inherited Create();
  FName := AName;
  FResultAddr := AResultAddr;
  FResultType := AResultType;
  Writeln('Entering ' + FName);
end;

destructor TMethodTracer.Destroy;
var
  lSuffix: String;
  lResVal: TValue;
begin
  lSuffix := '';
  if FResultAddr <> nil then
    begin
      //there's probably a more straight-forward to doing this, without involving TValue:
      TValue.Make(FResultAddr, FResultType, lResVal);
      lSuffix := ' - Result = ' + lResVal.AsString;
    end;
  Writeln('Leaving ' + FName + lSuffix);

  inherited Destroy;
end;

function TraceMethod(
  const AName: String;
  const AResultAddr: Pointer;
  const AResultType: PTypeInfo): IMethodTracer;
begin
  Result := TMethodTracer.Create(AName, AResultAddr, AResultType);
end;

//////

function F1: String;
begin
  TraceMethod('F1', @Result, TypeInfo(String));
  Writeln('Doing some stuff...');
  Result := 'Booyah!';
end;

F1();

这正在按预期工作。输出为:

Entering F1
Doing some stuff...
Leaving F1 - Result = Booyah!

我现在正在寻找一种方法来最大限度地减少调用 TraceMethod() 所需的参数数量,理想情况下允许我完全跳过与 Result 相关的参数。我自己没有汇编程序或堆栈布局的经验,但如果我没有弄错的话,从我看到其他人所做的“魔术”来看,至少隐含魔术 Result 变量的内存地址应该是可以通过某种方式获得,不是吗?也许人们也可以从那里获取其类型信息?

当然,如果甚至可以确定“周围”函数本身的名称,那么就完全不需要将参数传递给 TraceMethod...

我使用的是Delphi XE2,因此所有最近引入的语言/框架功能都可以使用。

在有人提到它之前:我的实际代码已经使用 CodeSite.EnterMethod/ExitMethod 而不是 Writeln 调用。我还知道这个简化的示例无法处理复杂类型并且不执行任何错误处理。

最佳答案

你最好的选择就是直接传入@Result。如果不这样做,则无法保证 Result 甚至地址。返回简单类型(例如IntegerBoolean)的函数将结果放入EAX 寄存器中。如果结果没有理由具有地址,则编译器不会为其分配任何内存。使用表达式@Result强制编译器为其提供一个地址。

不过,仅知道地址并不能获得返回类型。可能有一种方法可以通过 RTTI 来发现它。这将涉及三个步骤:

  1. 从方法名中提取类名。然后你就可以get the RTTI for that type 。这需要方法名称包含明确的类名称(包括单元名称)。

  2. 使用list of methods从该类型中找到该方法的 RTTI。由于名称不一定唯一标识一个方法,这将使情况变得复杂。重载将全部显示相同的名称。 (Rruz 在 how to deal with RTTI of overloaded methods 方法的上下文中显示 Invoke。)此外,从调试信息中获取的方法名称不一定与 RTTI 名称匹配。

    您可以循环遍历类的所有方法,搜索 CodeAddress 的方法,而不是尝试匹配名称。属性与调用者的地址匹配。不过,事实证明,确定如何获取调用者的开始地址(而不是返回地址)比我预期的更难找到。

  3. 获取 the method's return type并使用 Handle属性最终获得您想要的 PTypeInfo 值。

关于Delphi 汇编器/RTTI 专家 : Can I obtain the memory address and type info of the implied Result variable in a function?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10819770/

相关文章:

delphi - 如何将图像嵌入到 EXE 文件中并以幻灯片形式显示

Delphi:如何将所有类字段重置为零值?

delphi - TPersistent + 接口(interface),Delphi

delphi - 如何为枚举 RTTI 字段创建通用 TValue?

delphi - 用于将 8 位扩展为 0 或 1 的 8 个 bool 字节的 Intel x86 汇编优化技术

delphi 用 updateresources 写入数据

delphi - 用于转换菜单的Delphi XE2组件

abap - RTTI:获取字符列的长度

delphi - Delphi 和 FPC 中的裸 64 位 asm 函数

delphi - 交换字变量的字节(低/高)的过程