C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/category_11397492.html 出于调试对比的目的,测试同事需要在其机器上运行Debug版本的软件,结果双击Debug版本exe主程序后报出了多个错误。本文详细介绍一下这些问题的排查过程。
1、问题描述
近日出于调试对比的目的,测试同事的机器上要运行Debug版本的软件,于是我们开发这边将Debug版本的文件打包给测试同事,但同事双击exe主程序后报出了多个错误。今天我们就来介绍一下这些问题的排查解决过程,并详细介绍下与问题相关的细节和知识点。
2、启动时报缺少运行时库
测试同事双击exe主程序首先报找不到vcruntime140d.dll(140d中的d,是debug的意思,即Debug版本)运行时库的问题,如下所示:
于是开发的同事将Debug版本的运行时库发给测试同事,直接覆盖到exe的目录中,拷贝的Debug版本库如下:
Debug版本的程序在开发的机器上运行是没问题的,是因为开发机器上都安装了Visual Studio,安装Visual Studio时会将运行时库拷贝到系统路径中,所以开发这边运行exe主程序时可以搜索到运行时库,所以运行没问题。
测试同事将库拷贝到exe程序的目录中,结果还是有问题,弹出找不到Release版本的msvc100.dll运行时库,估计是有些底层库用的是Release版本的。
我们的程序主要用VS2010和VS2017编译的,其中msvc100.dll中的100对应的就是VS2010,vcruntime140d.dll中的140对应的是VS2017。
3、拷贝运行时库后,启动报0xC000007B错误
开发同事让测试同事在系统盘中搜一下msvc100.dll库,拷贝到exe的目录中。测试同事到系统路径中找到msvc100.dll,拷贝到exe目录中,再次运行exe主程序,不再报找不到运行时库的问题了,但还是报错,会报如下的错误:
错误提示中给出了错误代码0xC000007B,始终查不出来问题,于是找我去看看到底怎么回事。
以前遇到过0xC0000022错误码,对应STATUS_ACCESS_DENIED宏:
即访问被拒绝。出现访问被拒绝,可能有两个原因,一是没有足够的权限(比如需要管理员权限),二是要操作的文件被占用,访问被拒绝。之前写过一个排查0xC0000022的启动失败案例,可以查看这篇文章:
C++程序启动时报“0xc0000022”无法启动的错误https://blog.csdn.net/chenlycly/article/details/1208776100xC000007B错误码之前没遇到过,需要搞清楚是什么意思,于是**将STATUS_ACCESS_DENIED串输入到VS2017中,然后go到该定义该宏的头文件中**,看看能不能搜到0xC000007B错误码的定义,看看其具体是什么含义。
将0xC000007B拷贝到VS2017中居然不识别,没法go到定义处。打开上述文章,以前写文章的时候机器装的win7系统,现在机器换成了win10,按照文章中记录的头文件路径,到现在的win10系统上找,居然没找到。
在C:\Program Files (x86)\Windows Kits路径中找不到10.0的文件夹,只能看到一个8.1文件夹,于是点击去,也找到ntstatus.h头文件,和文章中的路径是类似的:
C:\Program Files (x86)\Windows Kits\8.1\Include\shared\ntstatus.h
在头文件中找到了0xC000007B错误码的含义:
0xC000007B值对应的宏为STATUS_INVALID_IMAGE_FORMAT,无效的二进制文件格式,可能是二进制文件的版本与操作系统不一致(比如64位程序不能运行在32位系统上),也可能是二进制文件中包含错误(比如二进制文件内容损坏或者被篡改)。
4、尝试用Windbg启动exe,看Windbg能否捕捉到相关线索
想尝试使用windbg去启动exe,看看windbg能否捕捉到导致异常报错的线索。打开windbg,点击菜单栏中的File->Open Executable...,然后进入目标exe路径,选中exe即启动exe程序,已启动就弹出了报错提示框:
Windbg并没有感知到具体的异常。看来还是程序启动时,系统的程序加载模块检测到问题,并不是目标程序代码有问题。所以此处并没找到线索。
5、尝试到计算机管理中去查看系统应用程序日志,看是否有相关记录
按照以往排查问题的经验,如果系统检测到异常了,可能会有相关日志记录的,于是计算机管理中去查看系统应用程序日志,看看能否找到线索。查看系统应用程序的日志的具体操作步骤为:
右键点击我的电脑,在弹出的右键菜单中点击“管理”,打开计算机管理窗口,然后依次点击系统工具->事件查看器->Windows日志->应用程序,就可以看到应用程序日志列表了,如下所示:
但应用程序日志列表中并没有看到相关的记录,所以也没找到线索。
6、继续分析引发0xC000007B错误原因
根据0xC000007B错误码的原因的说明,可能是二进制文件损坏了,难道是在文件传输的过程中有文件被损坏了?于是让开发同事重新编译了一个代码,重新发了新的压缩包过来,结果还是报0xC000007B错误。**同样的版本,在开发机器上是可以运行的,那两边的差异应该主要还是在系统运行时库上。**
于是在开发同事的机器上先将Debug版本程序跑起来,然后使用Process Explorer查看Debug版本程序依赖的系统运行时库的目录。打开Process Explorer,在进程列表中点击目标进程,下方就会显示该目标进程加载的所有dll,目标进程引用的运行时库如下
按照上述界面中显示的路径,到路径中找到这些库:(把所有可能用到的库都带上)
然后将这些dll发到测试同事那边,覆盖后就不崩溃了,那应该就是运行时库版本不一致导致的,会不会之前开发同事提供的运行时库版本是64位的,与我们当前32位主程序不匹配?
32位二进制文件不能和64二进制文件混用的!
一个Windows exe程序依赖一堆操作系统的系统dll库,可能会依赖开发者自己编写的一些dll库。对于32位exe或者dll,不能引用64位dll,只能引用32位的dll;对于64位exe或dll,不能引用32位dll,只能引用64位dll。
7、复盘出问题时所做的操作,最终确定问题
后来我们又去找测试同事沟通,测试同事说当时开发同事提供的库不全,还少了一个msvcr100.dll,开发同事让其直接在系统盘中搜索一下msvcr100.dll,找到后将之覆盖到Debug版本的exe程序目录中。当即感觉可能就是我们上面猜测的问题。
测试同事重复了当时的操作,到系统盘中去搜索msvcr100.dll,看到了如下的搜索记录:
仔细一看,测试机器上只有C:\Windows\system32目录下有这个库,测试同事拷贝的就是这个库。这就是问题所在了,C:\Windows\system32目录下存放的都是64位库,放到32位的exe主程序目录中,肯定是不匹配的,这就是导致0xC000007B错误的原因了,至此终于真相大白了。
8、对于程序位数和操作系统位数不一致的详细说明
8.1、64位程序的限制
64位程序不能运行在32位操作系统中,只能运行在64位系统中。因为64程序是按64位地址进行寻址的,32位操作系统不支持。
8.2、32位二进制文件不能和64二进制文件混用
32位二进制文件不能和64二进制文件混用。一个Windows exe程序依赖一堆操作系统的系统dll库,可能会依赖开发者自己编写的一些dll库。对于32位exe或者dll,不能引用64位dll,只能引用32位的dll;对于64位exe或dll,不能引用32位dll,只能引用64位dll。
如果exe主程序是32位的,则其使用的底层库都必须编译32位版本的。
8.3、32位程序可以运行在64位系统中
32位程序可以运行在64位系统中,64位系统对32位程序是兼容的。Windows程序底层必然会使用到系统dll库,64位Windows系统为了同时兼容32程序和64位程序,系统库都提供了两个版本,64位的系统dll库放置在目录C:\Windows\System32中,32位的系统dll库则放置在目录C:\Windows\SysWOW64中。
8.4、32位程序和64位程序甚至在注册表路径上也有区别
甚至注册表的有的路径对32位程序和64位程序也有区分,比如存放64位程序卸载信息的注册表路径为:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
而存放32位程序卸载信息的注册表路径中会多出一个WOW6432Node节点
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall
下面我们以腾讯QQ的安装注册表信息为例,打开控制面板中已安装的程序列表:
已安装的腾讯QQ在界面中显示的信息,均保存在注册表中,对应注册表的路径为:
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall{052CFB79-9D62-42E3-8A15-DE66C2C97C3E}
注册表中保存的所有信息如下:
上图中的InstallLocation节点存放的是QQ的安装路径:D:\Program Files (x86)\Tencent\QQ。因为QQ是32位程序,所以在注册表节点中有个WOW6432Node节点。
8.5、32位程序和64位程序的虚拟内存大小
用户双击启动32位exe程序时,系统把32位程序依赖的所有dll库加载到进程空间中,最后再将exe主程序启动起来,系统会给进程分配2^32=4GB的虚拟地址内存,对于Windows系统,一般内核态和用户态各占2GB的大小,对于Linux系统,一般用户态占3GB,内核态占1GB。用户态内存是供应用程序代码使用的,内核态内存则是供操作系统内核使用的。
对于程序而言,这个用户态的虚拟内存,在运行exe程序时,exe依赖的所有dll文件都会加载到地址空间中,这些二进制文件占用的内存时代码段的内存,都是从进程的虚拟内存中划拨的。如果当前应用程序是大型程序,会占用大量的虚拟内存空间,2GB的用户态虚拟内存可能就不够用了。而系统在加载64位程序时,系统会分配2^64=2^32*4GB,会分配很大的虚拟地址空间,基本是用不完的。
8.6、到底是开发32位程序还是64位程序?
这个需要分情况来看的。对于Windows客户端软件,主要看操作系统的位数,现在大部分人都用win10系统了,win10系统基本都是64位的,但还有少部分人在用win7系统,其中有部分win7系统还是32位的,因为32位程序才能兼容所有的系统,可以在所有系统上运行,所以Windows客户端软件都目前基本都选择开发32位版本。但32位程序只有2GB的用户态内存,对于大型软件来说,有限的内存可能不太够用,这是个头疼的事情。
对于服务器侧的软件,大多数使用的是Linux系统,也有一部分人使用Windows Server服务器系统,但服务器系统一般都是软件开发商自行配置提供给用户的(服务器设备时软件开发上提供的,内置在服务器设备中的操作系统是开发商选定的,即自行控制的),肯定都是选用64位操作系统,所以对于服务器侧的软件,一般都开发64位的,都会有充足的虚拟内存可供使用。
9、总结
问题一开始毫无头绪,随着排查的慢慢展开与逐步推进,逐渐找到线索并最终定位原因。在排查问题的过程,经历的多个细节,很有参考价值,所以在此给大家分享出来。细节出真知,我会争取写出更多有质量的文章,尽可能地多讲一些细节,详细梳理一下相关知识点。
最后,感谢大家一直以来的支持与认可,我会持续输出更多的原创文章。
版权归原作者 dvlinker 所有, 如有侵权,请联系我们删除。