【问题标题】:Find the Delphi source line that matches an offset in a DLL查找与 DLL 中的偏移量匹配的 Delphi 源代码行
【发布时间】:2012-03-19 15:23:39
【问题描述】:

我试图找出我的 Java 应用程序崩溃的原因。其实是JVM的crash,是通过JNI调用原生库引起的。

这是我在生成的hs_err_pidxxxx.log 中看到的内容:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_INT_DIVIDE_BY_ZERO (0xc0000094) at pc=0x4fa19409, pid=1456, tid=4068
#
# JRE version: 6.0_30-b12
# Java VM: Java HotSpot(TM) Client VM (20.5-b03 mixed mode windows-x86 )
# Problematic frame:
# C  [JCustomOpc.dll+0x9409]
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

---------------  T H R E A D  ---------------

Current thread (0x4ab1c400):  JavaThread "opc_service" daemon [_thread_in_native, id=4068, stack(0x4f200000,0x4f250000)]

siginfo: ExceptionCode=0xc0000094

Registers:
EAX=0x00000000, EBX=0x00000000, ECX=0x4f24f958, EDX=0x80000000
ESP=0x4f24f93c, EBP=0x4f24f940, ESI=0x4f24f9a4, EDI=0x52d396f4
EIP=0x4fa19409, EFLAGS=0x00010286

Top of Stack: (sp=0x4f24f93c)
0x4f24f93c:   4f24f98a 4f24f970 4fa1968a 52220000
0x4f24f94c:   5042f418 4f24f9a4 4b1ba6e8 00000004
0x4f24f95c:   52d396f4 4ab1c528 4f24f9a8 4f24f9a6
0x4f24f96c:   4f24f9a4 4f24f98c 4fa197cc 4f24f98a
0x4f24f97c:   52220000 5042f418 00000036 4f24f9a8
0x4f24f98c:   4ab33d1c 4fa6388f 52220000 5042f418
0x4f24f99c:   4ab1c528 4ab33d24 00000008 6d92f61f
0x4f24f9ac:   4ab1c528 4ab33d1c 00000022 0000000a 

Instructions: (pc=0x4fa19409)
0x4fa193e9:   db 89 c1 dd 45 08 d8 8b 88 21 a7 4f 83 ec 08 df
0x4fa193f9:   3c 24 9b 58 5a 09 d2 79 11 f7 da f7 d8 83 da 00
0x4fa19409:   f7 b3 8c 21 a7 4f f7 d8 eb 06 f7 b3 8c 21 a7 4f
0x4fa19419:   05 5a 95 0a 00 89 11 89 41 04 5b 5d c2 08 00 55 


Register to memory mapping:

EAX=0x00000000 is an unknown value
EBX=0x00000000 is an unknown value
ECX=0x4f24f958 is pointing into the stack for thread: 0x4ab1c400
EDX=0x80000000 is an unknown value
ESP=0x4f24f93c is pointing into the stack for thread: 0x4ab1c400
EBP=0x4f24f940 is pointing into the stack for thread: 0x4ab1c400
ESI=0x4f24f9a4 is pointing into the stack for thread: 0x4ab1c400
EDI=0x52d396f4 is an unknown value


Stack: [0x4f200000,0x4f250000],  sp=0x4f24f93c,  free space=318k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [JCustomOpc.dll+0x9409]
C  [JCustomOpc.dll+0x968a]
C  [JCustomOpc.dll+0x97cc]

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
J  javafish.clients.opc.JOpc.getDownloadGroupNative()Ljavafish/clients/opc/component/OpcGroup;
J  fr.def.iss.vd2.mod_opc_service.JEasyFacade.getPossiblyChangedGroups()Ljava/util/Collection;
J  fr.def.iss.vd2.mod_opc_service.OpcServiceImpl.updateState()V
j  fr.def.iss.vd2.mod_opc_service.OpcServiceImpl.access$3000(Lfr/def/iss/vd2/mod_opc_service/OpcServiceImpl;)V+1
j  fr.def.iss.vd2.mod_opc_service.OpcServiceImpl$5.handlOpcEvents()V+103
j  fr.def.iss.vd2.mod_opc_service.OpcServiceImpl$Worker.run()V+156
j  java.lang.Thread.run()V+11
v  ~StubRoutines::call_stub

所以,它表明JCustomOpc.dll 中有一个被零除。 JCustomOpc.dll 是一个第三方库,用 Delphi 编写,由我们的团队使用 Borland Delphi pro 7.0 编译。我现在要做的是在 Delphi 源代码中找到这种被零除的位置。

我主要是一名 Java 程序员,我不习惯调试本机代码。所以,我关注了tutorial on JVM crash analysis

我按照说明进行操作:

> dumpbin /headers JCustomOpc.dll
...
OPTIONAL HEADER VALUES
             10B magic #
            2.25 linker version
           60800 size of code
           12400 size of initialized data
               0 size of uninitialized data
           616A8 RVA of entry point
            1000 base of code
           62000 base of data
          400000 image base
...

因此,图像基数为 400000,根据教程,我希望除法指令位于偏移量 409409。

我们用反汇编来看看:

> dumpbin /exports /disasm JCustomOpc.dll
  ...
  004093E0: C3                 ret
  004093E1: 8D 40 00           lea         eax,[eax]
  004093E4: 55                 push        ebp
  004093E5: 8B EC              mov         ebp,esp
  004093E7: 53                 push        ebx
  004093E8: 31 DB              xor         ebx,ebx
  004093EA: 89 C1              mov         ecx,eax
  004093EC: DD 45 08           fld         qword ptr [ebp+8]
  004093EF: D8 8B 88 21 46 00  fmul        dword ptr [ebx+00462188h]
  004093F5: 83 EC 08           sub         esp,8
  004093F8: DF 3C 24           fistp       qword ptr [esp]
  004093FB: 9B                 wait
  004093FC: 58                 pop         eax
  004093FD: 5A                 pop         edx
  004093FE: 09 D2              or          edx,edx
  00409400: 79 11              jns         00409413
  00409402: F7 DA              neg         edx
  00409404: F7 D8              neg         eax
  00409406: 83 DA 00           sbb         edx,0
  00409409: F7 B3 8C 21 46 00  div         eax,dword ptr [ebx+0046218Ch]
  0040940F: F7 D8              neg         eax
  00409411: EB 06              jmp         00409419
  00409413: F7 B3 8C 21 46 00  div         eax,dword ptr [ebx+0046218Ch]
  00409419: 05 5A 95 0A 00     add         eax,0A955Ah
  0040941E: 89 11              mov         dword ptr [ecx],edx
  00409420: 89 41 04           mov         dword ptr [ecx+4],eax
  00409423: 5B                 pop         ebx
  00409424: 5D                 pop         ebp
  00409425: C2 08 00           ret         8
  ...

409409 确实是 div 指令。

我对汇编知之甚少,但这段代码似乎在操纵浮点值并进行整数除法。我搜索了 Delphi 源代码,但没有发现任何看起来像算术运算的东西。

我很确定有一种可靠的方法可以找到与该程序集匹配的 Delphi 函数,但我不知道该怎么做。我有这个 dll 的全部源代码,并且我可以完全控制如何编译它。

delphi 源 (.pas) 生成编译文件 (.dcu),也许这些文件可以帮助解决我的问题,但我不知道如何处理它们。链接器有一个选项,用于创建映射文件。它确实创建了一个 JCustomOpc.map 文件,该文件是一个包含大量符号和偏移量的文本文件。但偏移量与 9404 附近的任何值都不匹配。

我必须补充一点,此崩溃发生在我们客户的机器上,但无法在我们的机器上重现。此外,实际上不可能在客户的机器上进行一些测试,所以我有点坚持只在hs_err_pid file 上工作。

我现在该怎么做才能找到有问题的源代码行在哪里?

编辑

这里是generated detailed map file

【问题讨论】:

  • 您必须知道 DLL 加载地址才能将感兴趣的地址映射到图像中的 VA。
  • 我会将 Jedi JCL Debug 或 MadExcept 添加到您的 dll 中,围绕您导出的每个 DLL 函数执行一个大的 try-catch 块,并转储包括整个堆栈跟踪的异常日志。您绝对不应该允许访问违规或异常再次引发到托管 DLL 的应用程序中。

标签: java delphi debugging assembly java-native-interface


【解决方案1】:

您需要构建 DLL 并输出详细的地图文件。链接器选项中的设置控制映射文件输出。该映射文件将为您提供 DLL 中每个函数的起始地址,并且应该很容易从那里计算出来。

如果您可以在安装了 Delphi 的开发机器上重现该错误,另一种选择是使用 Delphi 调试器简单地调试 DLL。当引发异常时,这将中断,您将获得更多信息,而不仅仅是麻烦函数的标识。

【讨论】:

  • 我生成了详细的地图文件(请参阅我帖子中的编辑),但我无法找到 9409 附近的偏移量。
  • 最有可能的是 .DLL 链接的一些库代码。您确实选择了一个详细的映射文件,并在其中编译了所有调试信息。我认为您最好专注于获取堆栈回溯并忘记定位这个单个二进制地址,即使它有效,您也错过了您需要找到问题的大部分调用堆栈上下文。
  • 查看标题为“Publics by Value”的部分。它们按地址顺序列出。对于 exe,您需要从报告的偏移量中减去 0x1000 以获得与映射文件中的值匹配的值。我不记得了,但你可能需要也可能不需要对 DLL 做同样的事情。请记住,当您使用详细地图进行重建时,您现在拥有不同的 DLL。因此异常可能发生在这个新 DLL 中的不同地址。
  • 好的,我相信你需要减去 0x1000,所以在这个例子中你正在寻找 0x8409。但是,您现在有一个新的 DLL,因此您必须确保找到新 DLL 引发异常的地址。
  • 还有一件事。确实确保您的 DLL 被编码以捕获所有异常。您不能让它们传播到模块边界之外。很糟糕的形式。去向你的 Delphi 开发同事摇你的手指。
【解决方案2】:

您可以在 delphi 中加载您的 dll 项目,然后在 Run/Parameters 菜单中将您的 java 应用程序定义为主机。然后按 F9。当错误触发时,delphi IDE 应该突出显示有问题的源代码行。不要忘记在调试模式下进行此操作,并且可能会选中“使用调试 DCU”,以防它在 RTL 中发生。

【讨论】:

    【解决方案3】:

    编译器优化会给您在将程序集直接映射到 Delphi 中的 Pascal 源代码时遇到一些麻烦,因为不太可能存在“真正的”简单关联。但是,如果 dll 是内部生成的,您可以让源团队创建一个内置调试信息/符号的。这应该允许您附加调试器并提供更多人类可接受的诊断。但这仍然可能是一个艰难的过程。

    【讨论】:

    • 同意。尽管 OP 要求“如何使用 MAP 文件执行此操作”,但我一直发现 DLL 的实际运行时地址(可能会被重新定位)并将其与 MAP 文件相关联是徒劳的。
    • @WarrenP 你总能找到 DLL 的加载位置。而且你总是可以选择一个基地址来避免重定位。
    • @Michael 为 DLL 生成一个映射文件来标识每个函数的开始位置是很简单的。你的另一点很好。只需使用调试信息构建并从 Delphi IDE 进行调试。
    • @DavidHeffernan 诚然有一些猜想,因为它特别适用于 delphi。虽然在我的 C++ Builder 时代尝试做类似的事情时,我已经看到了一些关于优化的令人惊讶的事情;)
    • 我真的不明白你的意思。如果您有详细的地图和实际的基本地址,那么您就可以开始了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多