哪个本机函数导致 JNI 代码中的 EXCEPTION_ACCESS_VIOLATION?
Posted
技术标签:
【中文标题】哪个本机函数导致 JNI 代码中的 EXCEPTION_ACCESS_VIOLATION?【英文标题】:Which native function causes EXCEPTION_ACCESS_VIOLATION in JNI code? 【发布时间】:2012-11-10 20:51:48 【问题描述】:我正在尝试使用由 libgdx android Java 开发框架 (gdx-bullet) 包装的子弹物理库,并在一段时间的随机工作后出现 JVM 崩溃或“纯虚拟方法调用”崩溃。
其中一些会生成 hs_err_pidXXXX.log 文件,这些文件通常包含:
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0aa0c217, pid=7956, tid=7440
#
# JRE version: 7.0_05-b05
# Java VM: Java HotSpot(TM) Client VM (23.1-b03 mixed mode, sharing windows-x86 )
# Problematic frame:
# C [gdx-bullet.dll+0x1c217]
Current thread (0x04af2800): JavaThread "LWJGL Application" [_thread_in_native, id=7440, stack(0x04d70000,0x04dc0000)]
siginfo: ExceptionCode=0xc0000005, reading address 0x6572fc0f
Registers:
EAX=0x0073f370, EBX=0x0073f480, ECX=0x0073f484, EDX=0x6572fc07
ESP=0x04dbf3c0, EBP=0x04dbf400, ESI=0x0073f120, EDI=0x04dbf3f0
EIP=0x0aa0c217, EFLAGS=0x00010206
Instructions: (pc=0x0aa0c217)
0x0aa0c217: ff 52 08 f3 0f 10 05 0c f0 ba 0a f3 0f 10 4c 24
Register to memory mapping:
EDX=0x6572fc07 is an unknown value
Stack: [0x04d70000,0x04dc0000], sp=0x04dbf3c0, free space=316k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C [gdx-bullet.dll+0x1c217]
C 0x38cffed8
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j com.badlogic.gdx.physics.bullet.gdxBulletJNI.btDiscreteDynamicsWorld_stepSimulation__SWIG_1(JLcom/badlogic/gdx/physics/bullet/btDiscreteDynamicsWorld;FI)I+0
j com.badlogic.gdx.physics.bullet.btDiscreteDynamicsWorld.stepSimulation(FI)I+7
我被告知这可能是 Java GC 解除分配 Java 代码中不再引用但项目符号本机代码仍需要的对象的问题。
我查看了我的代码,但没有发现这种情况,这并不意味着它们不存在。我可以寻找更长的时间,但我认为如果我继续采用这种方法,我需要学习如何自己调试这种情况。
于是我在 gdx-bullet.dll 上运行了 dumpbin.exe,发现如下:
6AB80000 image base (6AB80000 to 6BD4FFFF)
然后我添加了 0x6AB80000 + 0x1c217 = 0x6AB9C217 并在 dumpbin.exe 反汇编中查找:
6AB9C206: 8B 10 mov edx,dword ptr [eax]
6AB9C208: 89 6C 24 0C mov dword ptr [esp+0Ch],ebp
6AB9C20C: 89 7C 24 08 mov dword ptr [esp+8],edi
6AB9C210: 89 4C 24 04 mov dword ptr [esp+4],ecx
6AB9C214: 89 04 24 mov dword ptr [esp],eax
6AB9C217: FF 52 08 call dword ptr [edx+8]
6AB9C21A: F3 0F 10 05 0C F0 movss xmm0,dword ptr ds:[6AD3F00Ch]
D3 6A
6AB9C222: F3 0F 10 4C 24 30 movss xmm1,dword ptr [esp+30h]
6AB9C228: 80 7E 2C 00 cmp byte ptr [esi+2Ch],0
6AB9C22C: F3 0F 5C C8 subss xmm1,xmm0
这一切都很好,但是关于我被困在哪里,因为我不知道 [edx+8] 的位置。
我有使用(roughly this one)的子弹源代码。
我安装了 windbg.exe 并设法让 userdump.exe 生成一个 javaw.dmp 文件,但不知道在其中查找什么以及如何查找。我尝试使用“r”命令找出 rdx 的内容,但它是 0x0,而不是 hs_err_pid 文件,它是一些随机值。
我找到了一些build scripts,但我怀疑我是否可以向它们添加“包含调试信息”标志,然后让它们及时工作。
我该怎么做才能确定是哪个特定的本地方法出现了问题?
如果我知道这一点,那么我可以查看它的源代码并了解我传递给它的错误参数或它需要的 GC 释放了哪些对象。
【问题讨论】:
您是否考虑到 RVA != VA?您将需要一个 .map 文件用于手动或适当的 PDB 文件,以便由 WinDbg 执行自动查找。我肯定会首先建议。问题是您的构建可能仍然不同,例如调试与发布。否则,最后的手段是学习 RCE 并使用 IDA 或 OllyDbg 之类的东西将反汇编映射到源代码。相当繁琐的方法。 不,我上次编程汇编是在 15 年前,那是实模式,我几乎无法掌握“RVA!= VA”的概念。我想我运气不好。 此时 [edx+8] 无效,但您可以通过查看 [edx+8] 之前有效的值来了解调用的内容。您可以通过在 gdx-bullet.dll+0x1c217 处设置断点来执行此操作,然后再使用bu gdx-bullet+0x1c217
重现崩溃。这将在调用指令处中断,然后您可以发出t
以进入被调用的函数。现在,如果调用指令是为了调用阳光下的所有东西,那么这种技术将无济于事。但如果它是为了调用函数来告诉子弹必须行进多远,那么你很幸运。
马克,这是一个有趣的想法,我会在接下来的情况下尝试。谢谢!
不确定这是否会有所帮助,但您可以尝试附加一个调试器并将其设置为该错误的第二次机会异常,并以这种方式进行实时调试,这通常会给您带来更好的机器状态的想法。
【参考方案1】:
指针可能为时已晚,在您的情况下,发生访问冲突是因为访问了在本机代码中已取消分配内存的指针。在这种情况下,如果试图写入该指针指向的内存,如果释放的块还没有被内存管理重新分配,它可能会实际工作几次。我在 Windows 中遇到过这种情况,最终程序在退出之前给出了 Heap Overrun 错误,并且从 Java 端抛出了上述错误。它工作时间短的原因是因为一旦内存块被取消分配,它不会立即被 Windows 分配,而是当它需要重新分配时,Windows 检查内存块的头字段并发现它是损坏,因此出现堆溢出错误。
如果本机代码是由您编写的,您可以添加跟踪并找到问题,在包装的库中,您可能没有在 Java 端保留对库的引用,或者包装器中存在问题。
【讨论】:
以上是关于哪个本机函数导致 JNI 代码中的 EXCEPTION_ACCESS_VIOLATION?的主要内容,如果未能解决你的问题,请参考以下文章
JNI - 在本机 cpp 回调函数中使用 RxJava 的奇怪行为