【问题标题】:How to get address of Delphi code label in another function?如何在另一个函数中获取 Delphi 代码标签的地址?
【发布时间】:2014-12-19 09:13:02
【问题描述】:

我正在尝试将一些代码从 Delphi 5 迁移到 Delphi XE7-WIN64。 该方案是最新的 Delphi 不允许混合汇编和 Delphi 代码。 我也是asm新手。

原代码:

function TclDbgHelpStackTracer.GetSymbolSearchPath(): string;
var
  sPath: array[0..MAX_PATH] of char;
  mbi: MEMORY_BASIC_INFORMATION;
  pProc: Pointer;
label l1;
begin
  asm
    mov eax, offset l1
    mov pProc, eax
  end;
l1:
  Result := '';
  if (GetEnvironmentVariable(SYMBOL_PATH) <> '') then
Result := GetEnvironmentVariable(SYMBOL_PATH) + ';';
  if (GetEnvironmentVariable(ALTERNATE_SYMBOL_PATH) <> '') then
    Result := Result + GetEnvironmentVariable(ALTERNATE_SYMBOL_PATH) + ';';
  if (GetEnvironmentVariable('SystemRoot') <> '') then
    Result := Result + GetEnvironmentVariable('SystemRoot') + ';';

VirtualQuery(pProc, mbi, sizeof(mbi));
GetModuleFileName(Cardinal(mbi.AllocationBase), sPath, MAX_PATH);
StrRScan(sPath, '\')^ := #0;
Result := Result + sPath + ';';

GetModuleFileName(0, sPath, MAX_PATH);
StrRScan(sPath, '\')^ := #0;
Result := Result + sPath;
end;

以上代码在Delphi XE7-WIN32中工作 但是你可以看到上面的 sn-p 有嵌套的 asm 块 Delphi XE7-WIN64 要求过程/函数只包含 asm 或 pascal 代码。 所以我把它改成如下:

{$IFDEF WIN64}
procedure AsmProc(pProc: Pointer);
asm
mov eax, offset l1
mov pProc, eax
end;
{$ENDIF}

function TclDbgHelpStackTracer.GetSymbolSearchPath(): string;
var
  sPath: array[0..MAX_PATH] of char;
  mbi: MEMORY_BASIC_INFORMATION;
  pProc: Pointer;
label l1;
begin
{$IFDEF WIN32}
  asm
    mov eax, offset l1
    mov pProc, eax
  end;
{$ELSE}
    AsmProc(pProc);
{$ENDIF}
l1:
  Result := '';
  if (GetEnvironmentVariable(SYMBOL_PATH) <> '') then
                Result := GetEnvironmentVariable(SYMBOL_PATH) + ';';
  if (GetEnvironmentVariable(ALTERNATE_SYMBOL_PATH) <> '') then
                Result := Result + GetEnvironmentVariable(ALTERNATE_SYMBOL_PATH) + ';';
  if (GetEnvironmentVariable('SystemRoot') <> '') then
                Result := Result + GetEnvironmentVariable('SystemRoot') + ';';

  VirtualQuery(pProc, mbi, sizeof(mbi));
  GetModuleFileName(Cardinal(mbi.AllocationBase), sPath, MAX_PATH);
  StrRScan(sPath, '\')^ := #0;
  Result := Result + sPath + ';';

  GetModuleFileName(0, sPath, MAX_PATH);
  StrRScan(sPath, '\')^ := #0;
  Result := Result + sPath;
end;

现在麻烦开始了。 l1 是代码标签,它的地址(下一条可执行语句的地址)被移动到 eax 然后使指针 pProc 指向该地址。 然后在

中使用指针pProc
  VirtualQuery(pProc, mbi, sizeof(mbi));

问题是如何传递l1的地址?或者有没有其他方法可以做到这一点?

【问题讨论】:

  • 去掉asm会得到启示。它还有助于开始检查错误并停止截断指针。在进入 64 位世界之前,您必须认识到指针是 64 位宽的。
  • 正如@rob-kennedy 在我的(现已删除)答案下评论的那样,您可以通过使用全局HInstance 变量而不是Cardinal(mbi.AllocationBase) 并抛出pProc 和程序集来实现相同的目的代码离开
  • @xmojmr 我没有删除你的答案。 :( 我将在星期一尝试修复,因为我是 OOO。
  • 而且我似乎也丢失了@rob-kennedy 的评论。
  • 我自己删除了答案,因为它只是部分有用。这是 Rob 的评论:“此代码调用 VirtualQuery 来获取基内存地址。它假定它将是整个 DLL 的基地址,并且它进一步假定 i> 基地址可以代表模块句柄。它将“句柄”传递给 GetModuleFileName 以获取 DLL 的名称。但是 DLL 在全局 HInstance 变量中已经有一个句柄。那里根本不需要打电话给VirtualQuery。”

标签: delphi assembly delphi-5 delphi-xe7


【解决方案1】:

问题中的代码使提取包含执行代码的模块的名称真正成为一顿饭。你只需要这个:

function TclDbgHelpStackTracer.GetSymbolSearchPath(): string;

  function GetEnvPath(const Name: string): string;
  var
    Value: string;
  begin
    Value := GetEnvironmentVariable(Name);
    if Value <> '' then
      Result := Value + ';'
    else
      Result := '';
  end;

  function GetModulePath(Module: HMODULE): string;
  begin
    Result := ExtractFileDir(GetModuleName(Module));
  end;

begin
  Result :=
    GetEnvPath(SYMBOL_PATH) +
    GetEnvPath(ALTERNATE_SYMBOL_PATH) +
    GetEnvPath('SystemRoot') +
    GetModulePath(HInstance) + ';' +
    GetModulePath(0);
end;

坦率地说,您问题中的asm 代码很奇怪。没有必要那样做。所有代码都试图做的是找到已经作为全局 HInstance 变量可用的模块句柄。

作为一般规则,您应该尽量避免使用asm。它使代码的可移植性降低并且更难理解。有时使用asm 是正确的选择。这不是那些场合之一。

【讨论】:

  • 感谢您的回答。看起来很完美。将在星期一检查并接受它作为答案。
猜你喜欢
  • 2011-04-15
  • 2021-11-14
  • 1970-01-01
  • 2022-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多