Assembly 这是不是;“不应该发生”;使AMD Fusion CPU错误崩溃?
我的公司已经开始有很多客户打电话进来,因为我们的程序因为他们系统上的访问违规而崩溃 崩溃发生在SQLite 3.6.23.1中,我们将其作为应用程序的一部分发布。(为了使用与应用程序其余部分相同的VC++库,我们提供了一个自定义构建,但它是SQLite代码。) 当Assembly 这是不是;“不应该发生”;使AMD Fusion CPU错误崩溃?,assembly,crash,x86,windbg,amd-processor,Assembly,Crash,X86,Windbg,Amd Processor,我的公司已经开始有很多客户打电话进来,因为我们的程序因为他们系统上的访问违规而崩溃 崩溃发生在SQLite 3.6.23.1中,我们将其作为应用程序的一部分发布。(为了使用与应用程序其余部分相同的VC++库,我们提供了一个自定义构建,但它是SQLite代码。) 当pcache1Fetch执行call 00000000时发生崩溃,如WinDbg callstack所示: 0b50e5c4 719f9fad 06fe35f0 00000000 000079ad 0x0 0b50e5d8 719f92
pcache1Fetch
执行call 00000000
时发生崩溃,如WinDbg callstack所示:
0b50e5c4 719f9fad 06fe35f0 00000000 000079ad 0x0
0b50e5d8 719f9216 058d1628 000079ad 00000001 SQLite_Interop!pcache1Fetch+0x2d [sqlite3.c @ 31530]
0b50e5f4 719fd581 000079ad 00000001 0b50e63c SQLite_Interop!sqlite3PcacheFetch+0x76 [sqlite3.c @ 30651]
0b50e61c 719fff0c 000079ad 0b50e63c 00000000 SQLite_Interop!sqlite3PagerAcquire+0x51 [sqlite3.c @ 36026]
0b50e644 71a029ba 0b50e65c 00000001 00000e00 SQLite_Interop!getAndInitPage+0x1c [sqlite3.c @ 40158]
0b50e65c 71a030f8 000079ad 0aecd680 071ce030 SQLite_Interop!moveToChild+0x2a [sqlite3.c @ 42555]
0b50e690 71a0c637 0aecd6f0 00000000 0001edbe SQLite_Interop!sqlite3BtreeMovetoUnpacked+0x378 [sqlite3.c @ 43016]
0b50e6b8 71a109ed 06fd53e0 00000000 071ce030 SQLite_Interop!sqlite3VdbeCursorMoveto+0x27 [sqlite3.c @ 50624]
0b50e824 71a0db76 071ce030 0b50e880 071ce030 SQLite_Interop!sqlite3VdbeExec+0x14fd [sqlite3.c @ 55409]
0b50e850 71a0dcb5 0b50e880 21f9b4c0 00402540 SQLite_Interop!sqlite3Step+0x116 [sqlite3.c @ 51744]
0b50e870 00629a30 071ce030 76897ff4 70f24970 SQLite_Interop!sqlite3_step+0x75 [sqlite3.c @ 51806]
C代码的相关行为:
if( createFlag==1 ) sqlite3BeginBenignMalloc();
编译器内联sqlite3BeginBenignMalloc
,定义为:
typedef struct BenignMallocHooks BenignMallocHooks;
static SQLITE_WSD struct BenignMallocHooks {
void (*xBenignBegin)(void);
void (*xBenignEnd)(void);
} sqlite3Hooks = { 0, 0 };
# define wsdHooksInit
# define wsdHooks sqlite3Hooks
SQLITE_PRIVATE void sqlite3BeginBenignMalloc(void){
wsdHooksInit;
if( wsdHooks.xBenignBegin ){
wsdHooks.xBenignBegin();
}
}
这方面的大会是:
719f9f99 mov esi,dword ptr [esp+1Ch]
719f9f9d cmp esi,1
719f9fa0 jne SQLite_Interop!pcache1Fetch+0x2d (719f9fad)
719f9fa2 mov eax,dword ptr [SQLite_Interop!sqlite3Hooks (71a7813c)]
719f9fa7 test eax,eax
719f9fa9 je SQLite_Interop!pcache1Fetch+0x2d (719f9fad)
719f9fab call eax ; *** CRASH HERE ***
719f9fad mov ebx,dword ptr [esp+14h]
登记册包括:
eax=00000000 ebx=00000001 ecx=000013f0 edx=fffffffe esi=00000001 edi=00000000
eip=00000000 esp=0b50e5c8 ebp=000079ad iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
如果eax
为0(即0),则应通过test eax,eax
设置零标志,但它不是零。由于未设置零标志,je
不会跳转,然后应用程序在尝试执行调用eax(00000000)
时崩溃
更新:eax
此处应始终为0,因为在我们的代码构建中未设置sqlite3Hooks.xBenignBegin
。我可以使用定义的SQLite\u ommit\u BUILTIN\u TEST
重建SQLite,这将在代码中启用\define sqlite3BeginBenignMalloc()
,并完全忽略此代码路径。这可能会解决问题,但感觉不像是“真正的”修复;什么能阻止它在其他代码路径中发生
到目前为止,共同的因素是所有客户都在运行“Windows 7 Home Premium 64位(6.1,Build 7601)Service Pack 1”,并具有以下CPU之一(根据DxDiag):
- AMD A6-3400M APU,带Radeon(tm)高清图形(4个CPU),~1.4GHz
- AMD A8-3500M APU,带Radeon(tm)高清图形(4个CPU),~1.5GHz
- AMD A8-3850 APU,带Radeon(tm)高清图形(4个CPU),~2.9GHz
.time
报告崩溃前1米30到7米的用户时间。我在最初的帖子中忽略了一个事实(可能与这个问题有关),那就是这个应用程序是多线程的,CPU和I/O的使用率都很高。默认情况下,应用程序生成四个工作线程,并发布80%以上的CPU使用率(SQLite代码中存在一些I/O和互斥锁阻塞),直到崩溃。我将应用程序修改为只使用两个线程,但它仍然崩溃(尽管需要更长的时间)。我现在正在运行一个只有一个线程的测试,它还没有崩溃
还要注意的是,这似乎不是一个纯粹的CPU负载问题;我可以在系统上无错误地运行Prime95,它会将CPU温度提高到>70℃,而我的应用程序在运行时温度几乎不会超过50℃
8月16日更新:稍微干扰说明会使问题“消失”。对于eax示例,将内存加载(mov-eax,dword-ptr[SQLite\u Interop!sqlite3Hooks(71a7813c)]
替换为xor-eax,eax
)可以防止崩溃。修改原始C代码以向if(createFlag==1)
语句添加额外的检查,从而更改编译代码中各种跳转的相对偏移量(以及testeax、eax
和call eax
语句的位置),并且似乎可以防止出现问题
到目前为止,我发现的最奇怪的结果是将719f9fa0
处的jne
更改为两条nop
指令(这样,无论createFlag
/esi
的值是多少,控制总是落在测试eax,eax
指令上)允许程序在不崩溃的情况下运行。我有点担心为if(wsdHooks.xBenignBegin)
生成的代码不是很通用。它假设唯一的真值是1
,而实际上它应该测试任何非零值。尽管如此,MSVC有时还是让人困惑。可能没什么。没关系:这些说明适用于C
未显示的代码
假设eflagZ
位是清晰的,而EAX
位是零,则代码没有通过执行指令到达这里
719f9fa7 test eax,eax
必须从其他地方跳转到以下指令(719f9fa9 je SQLite_Interop!pcache1Fetch+0x2d
),甚至是调用
指令本身
另一个复杂问题是,对于x86系列,无效的跳转目标(如JE
指令的第二个字节)通常会对相当多的指令执行不受干扰(无故障)的操作,通常最终会恢复到正确的指令对齐方式。换句话说,您可能不希望跳转到这些指令的开头:跳转可能在它们的字节中间,导致执行不显著的操作,如add[al+ebp],al
,这些操作往往不会被注意到
我预测异常不会命中测试
指令处的断点。找到这些原因的唯一方法是