如何从 Delphi 程序或编译器生成的调试信息中提取局部变量信息(地址和类型)?

Posted

技术标签:

【中文标题】如何从 Delphi 程序或编译器生成的调试信息中提取局部变量信息(地址和类型)?【英文标题】:How do you extract local variable information (address and type) from a Delphi program or the compiler-generated debug info? 【发布时间】:2015-07-11 12:20:07 【问题描述】:

我的目标是:

在 Delphi 编译的 32 位或 64 位 Windows 程序中给定一个暂停线程,遍历堆栈(可行) 给定堆栈条目,枚举每个方法中的局部变量及其值。也就是说,至少找到它们的地址和类型(integer32/64/signed/unsigned、string、float、record、class...),它们的组合可以用来找到它们的值。

第一个很好,这是第二个问题。概括地说,如何在 Delphi 中枚举给定堆栈条目的局部变量?


在低级别,这是我一直在调查的:

RTTI: 并未列出此类有关方法的信息。这不是我实际上认为是一个现实的选择,但无论如何都在这里列出。

调试信息:加载为调试构建生成的调试信息。

地图文件:即使是详细的地图文件(文本格式文件!打开并查看)也不包含局部变量信息。它基本上是地址和源文件行号的列表。非常适合地址到文件和行的相关性,例如排水沟中的蓝点;不太适合获取更多详细信息 远程调试信息(RSM 文件)-no known information 的内容或格式。 TD32/TDS 文件:我目前的研究方向。它们包含许多其他信息中的全局和局部符号。

我在这里遇到的问题是:

没有关于 TD32 文件格式的文档(我可以找到。) 我对它们的大部分了解来自使用它们的 Jedi JCL 代码 (JclTD32.pas),我不确定如何使用该代码,或者那里的结构是否足够广泛以显示本地变量。我很确定它将处理全局符号,但我对本地符号非常不确定。定义了各种各样的常量并且没有格式的文档,要阅读它们的含义,我只能猜测。但是,这些常量及其名称必须来自某个地方。 Source I can find using TDS info 不加载或处理本地符号。

如果这是正确的方法,那么这个问题就变成了“是否有 TDS/TD32 文件格式的文档,是否有任何加载局部变量的代码示例?”

代码示例不是必需的,但可能非常有用,即使它非常少。

【问题讨论】:

我实际上并没有使用 Jedi JCL 单元来访问 TD32 信息 - 我有我自己的专有库,但看起来你需要的所有基本管道都在 JclTD32 中。帕斯但是,我找不到用于访问变量信息的演示代码,但是那里的示例(在 ..\jcl\examples\windows\debug\sourceloc 中)显示了如何从 TD32 数据中获取行号信息,所以你应该能够在此基础上得到你需要的东西。请在这里报告您的发现:) @500-InternalServerError 谢谢。行号信息很简单(甚至在地图文件中)——但您能否提供有关您在 JCL 代码中看到的与本地符号特别相关的任何信息?另外,出于好奇,您的 TD32 专有库是什么,它是发布/公开使用还是仅在内部使用? 它下面的每个过程/函数/方法符号依次包含一个本地符号列表。大多数定义似乎都存在于绝地部队中,但有些定义已被注释掉。我的建议是创建小型测试应用程序并查看符号枚举返回的内容。我拥有的代码是专有的,不适合我发布。无论如何,它都没有涵盖局部变量的主题。但它所基于的信息是半公开的,所以如果你遇到特定的问题,我可能会提供帮助。 tds2pdb (code.google.com/p/map2dbg) 似乎有一个用于 tds 文件的解析器。虽然是 C# 代码。 曾经有一个非正式的文档,是的,但后来 Borland(当时)决定发布一个 dll 来代替访问调试信息,这样他们就可以更改内部格式而不必更新文档。不幸的是,我现在既找不到原始文档也找不到 dll。我建议您联系 Embarcadero 技术支持并询问相关信息。 【参考方案1】:

检查是否有任何调试符号不是二进制的。 也可以使用 GDB(在 Windows 上的一个端口 它)。如果您找到 .dbg 或 .dSYM,那就太好了 文件。它们包含源代码,例如。

gdb> list foo
56 void foo()
57 
58  bar();
59  sighandler_t fnc = signal(SIGHUP, SIG_IGN);
60  raise(SIGHUP);
61  signal(SIGHUP, fnc);
62  baz(fnc);
63 

如果您没有任何调试文件,您可以尝试获取 MinGW 或 Cygwin,并使用 nm(1) (man page)。它将从二进制文件中读取符号名称。它们可能包含一些类型,例如 C++ 类型:

int abc::def::Ghi::jkl(const std::string, int, const void*)

不要忘记添加--demangle 选项,否则你会得到类似的东西:

__ZN11MRasterFont21getRasterForCharacterEh

代替:

MRasterFont::getRasterForCharacter(unsigned char)

【讨论】:

Jakub,感谢您的回答。不幸的是,我可能需要阅读特定的调试格式 - TDS。 Delphi 应用程序未在 Windows 上使用与 GDB 兼容的调试信息进行编译。我也不确定 nm 将如何提供帮助,因为它将依赖于特定的调试文件格式,而这种格式可能不是 Delphi 生成的。还是我误解了你的答案——例如 GDB 可以读取 Delphi 的符号吗? @DavidM,您的评论非常重要。尝试在 Windows 上找到 GNU Binutils 或 GNU 调试器的端口(我只知道 Binutils 端口)。 GDB 有一个 BFD 库。它也用于 Binutils。它允许读取多种文件格式并通过它们的幻数识别它们。如果一切都失败了,请使用名为strings 的工具。它将从任何二进制文件中提取字符串。见man page。这将打印可能但不一定有用的字符串【参考方案2】:

查看http://download.xskernel.org/docs/file%20formats/omf/borland.txt 开放架构手册。它很旧,但也许您会找到有关文件格式的一些相关信息。

【讨论】:

能否请您添加一些上下文,该链接将来可能会损坏。 该链接包含来自 borland 的官方文档,关于 Borland 编译器使用的 OMF 文件格式和 Borland 使用的其他二进制文件格式。几年前,我查看了 TDS 文件格式,似乎有些部分与记录的文件格式兼容。在尝试从 TDS 文件中收集有关局部变量的任何信息时,应使用或参考链接的文档。如果链接断开,我的回答将毫无用处,所需的信息也会丢失。链接的“开放架构手册”是旧 Turbo Pascal 和 C 版本的一部分。

以上是关于如何从 Delphi 程序或编译器生成的调试信息中提取局部变量信息(地址和类型)?的主要内容,如果未能解决你的问题,请参考以下文章

Delphi调试器窗口中为啥变量内容显示不全

如何delphi中写一个程序使用次数限制,到期后需要注册码?

求教在delphi中,如何把一个exe做为res加入到dll中,并在运行时生成exe文件执行

Delphi应用程序的调试使用断点

delphi下如何制作纯dos下能够运行的程序,并非是控制台程序

C语言中编译 生成 调试 测试 运行各是啥意思有啥区别