[原创]masm32开发包与VC6.0之间存在的Bug的体现及解决办法

小钟 2018-2-8 23:29 1746

概览:本文做了个Demo测试小程序来体现masm32开发包的库与VC6.0存在的一些不兼容的问题,如下即为所描述的问题及列举的对应的解决办法:

1、在RadASM环境下连接VC6.0生成的.obj时,找不到_main符号的问题及解决办法。

2、VC6.0对GetEnvironmentStrings这个函数的ASCII版本和UNICODE版本的宏替换与masm32的不一致的问题及解决办法。


一、Bug的触发:

创建VC6.0工程,声明和定义ShowHello()函数,然后编译生成.obj目标文件:



创建RadASM控制台工程。由于VC6.0编译好的代码都会被生成在.obj里面,所以就能在RadASM工程直接调用.obj里面的函数。将编译好的StdAfx.obj扔到RadASM工程文件夹下,并添加到RadASM工程里面。然后就可以调用了,以下就是调用ShowHell()这个函数。


编译可通过,但是连接的时候报了两个错误:



二、错误一:

提示缺少_main符号,为什么会缺少_main符号?是因为在VC6.0生成的.obj会把LIBCD.LIB一块连接进去(LIBCD.LIB在VC6.0安装目录的VC98下的LIB文件夹里面)。在 LIBCD.LIB中有一个crt0.obj,这个crt0.obj就是VC6的入口代码,我们用IDA打开LIBCD.LIB查看:


双击这个crt0.obj进去后发现,就会看到的即是VC6.0的入口代码:


入口代码做完一些初始化工作后,就调用了main函数


由于连接了LIBCD.LIB,而LIBCD.LIB又有入口函数被连接,而拷贝到RadASM工程下的StdAfx.obj并没有入口函数的定义,于是就报错第一个错误:缺少_main符号。


解决办法一:将RadASM的程序入口改为main:


再连接,第一个错误解决了!


解决办法二:直接定一个的空函数,使RadASM连接时能检测到_main符号:


这个方法同样可以解决第一个错误!


三、错误二:

缺少一个函数定义:__imp__GetEnvironmentStrings

       首先,在VC6.0下我们看看GetEnvironmentStrings这个函数在何时被调用。通过栈回溯定位到VC的mainCRTStartup,然后往下就会看到:是mainCRTStartup调用了这个函数:


关键点:在RadASM连接时会报错,是由于识别不出.obj下的这个函数,即masm32对这个声明的与VC6.0声明的不一致。


问题的体现:

1、我们先看masm32对ASCII与UNICODE版本的实现。打开masm32的include目录,找到kernel32.inc


打开并定位到GetEnvironmentStrings这个函数:


masm32调用该函数时如果是ASCII版,则用GetEnvironmentStringsA宏替换,如果是UNICODE版,则用GetEnvironmentStringsW宏替换。

 2、再看微软对ASCII版本与UNICODE版本的实现,首先我们以举MessageBox为例子:


 但是很不幸的是GetEnvironmentStrings这个函数并不这样子:


这时就发现了,后缀带A的GetEnvironmentStringsA的是ASCII版本,而带W的GetEnvironmentStringsW是UNICODE版。也就是说带GetEnvironmentStringsA的是宏,而GetEnvironmentStrings才是函数。


通过对比发现,问题很明显的体现了,masm32与微软VC6.0对此函数的声明不一致,才会导致上面说的RadASM连接此函数时报的那个错误!!

解决办法一:先修改masm32处的声明:


 修改完保存,但是此时连接依然是不会过的,因为.lib已经打包了kernel32.inc。简洁的解决办法,可直接拿VC6.0下的.lib直接覆盖masm32下的这个.lib,这是一种解决办法。我们就试着将它拷到masm32的.lib目录下:


然后,在RadASM工程下再次连接:


       这样连接就过了。但此时程序依然不能运行,由于我们给RadASM提供的.obj是VC环境下编译的.obj,所以对应的入口函数也应改为mainCRTStartup,即:


此处,再编译、连接、运行,即可看正常运行。



解决办法二:除上面提供的方法外,还有另外的解决办法:将修改后的kernel32.inc重新生成.lib文件替换原来的.lib。我们使用masm32提供的工具inc2l来重新生成kernel32.lib。这个工具在masm32的tool目录下:


将我们修改过的kernel32.inc拷贝进来:


打开命令行,并编译生成kernel32.lib:


将重新生成的kernel32.lib拷贝到masm32目录下的lib文件夹覆盖旧的kernel32.lib:


到了这一步,就算大功告成了。将RadASM再做连接,编译通过了!


运行看到效果:


到此,RadASM连接VC6.0时的两个报错都解决了。


四、尾声:

此博文是在科锐学习期间的日常笔记,心血来潮就发表了此帖子,对此有遗落的地方望各位大佬指出供小弟加以改正。最后感谢看雪提供的平台使大家有相互学习的机会!


上传的附件:
最新回复 (17)
coneco 2018-2-9 21:41
2
学习了,谢谢分享
聖blue 2018-2-9 23:10
3
JoenChen 2018-2-11 08:50
4
这个解决思路不太对吧,  套路不是应该VC这边编译不要带默认库吗?
小钟 2018-2-11 12:39
5

最后于 2018-2-14 18:03 被小钟编辑 ,原因:
小钟 2018-2-11 13:47
6
JoenChen 这个解决思路不太对吧, 套路不是应该VC这边编译不要带默认库吗?
问题的本质,是VC的库与masm32库对这个API声明不一致导致,所以可通过修改两者其一库声明来解决问题,解决办法多种,楼主所说VC编译这边也是可行的
Akkuman 2018-2-13 00:56
7
目前自己倒是在看fasm
JerryOne 2018-2-13 10:50
8
写的很好,但有个问题不解,crt0.c中的main是三个参数的
#ifdef  WPRFLAG
                        __winitenv  =  _wenviron;
                        mainret  =  wmain(__argc,  __wargv,  _wenviron);
#else    /*  WPRFLAG  */
                        __initenv  =  _environ;
                        mainret  =  main(__argc,  __argv,  _environ);
#endif    /*  WPRFLAG  */
但标准的是以下两个
int  main(void)
int  main(int  argc,  char  *agrv[])
链接的时候如何对应,看了看ctr0.c中的内容,有
int  __cdecl  main(int,  char  **,  char  **);                          /*generated  by  compiler*/
是这个编译器产生的三个参数是main调用了我们提供的符合标准的main吗?
如果是的话,链接符号_main有两个,不会因重复导致冲突吗?
求解答
小钟 2018-2-14 17:46
9

最后于 2018-2-14 18:01 被小钟编辑 ,原因:
小钟 2018-2-14 17:48
10

最后于 2018-2-14 18:01 被小钟编辑 ,原因:
小钟 2018-2-14 18:21
11
JerryOne 写的很好,但有个问题不解,crt0.c中的main是三个参数的 #ifdef WPRFLAG __winitenv = _wenviron; ma ...
本文所提供方案不是嵌套一层main调用吧,汇编调用VC提供的obj,汇编这边没声明main符号所以link时识别不出,手动加进去可解决此问题,三个参数的main是标准  不带参数main同样可行
JerryOne 2018-2-16 21:28
12
小钟 本文所提供方案不是嵌套一层main调用吧,汇编调用VC提供的obj,汇编这边没声明main符号所以link时识别不出,手动加进去可解决此问题,三个参数的main是标准 不带参数main同样可行
to    小钟
我的疑惑不是_main符号link时识别问题
我的问题是crt0.obj中已经包含_main强符号(三个参数的main函数),我们提供的(无参数或两个参数)main函数也将产生_main强符号,这不会产生冲突吗
另外,看crt0.c代码,mainCRTStartup调用的是由编译器自动生成的带三个参数的main函数,而我们提供的(无参数或两个参数)main函数是由谁调用的呢
loqich 2018-2-19 17:33
13
JerryOne to 小钟 我的疑惑不是_main符号link时识别问题 我的问题是crt0.obj中已经包含_main强符号(三个参数的main函数),我们提供的(无参数或两个参数)main函数也将产生_ma ...
你搞错了c语言符号和c++符号  main是c语言符号  导出后在就是_main  链接器没法判断有几个参数的,  可变参数函数的原理就在如此
李白nihao 2018-2-23 21:05
14
不错,谢谢分享啦
xyy吸氧羊 2018-2-25 17:32
15
之前还碰到过VS中无法在汇编代码中调用c写的函数和C库函数,不知楼主遇见过吗?
小钟 2018-2-28 19:30
16
也许是你环境没配好吧
小钟 2018-2-28 19:31
17
也许是你环境没配好吧
xyy吸氧羊 2018-3-6 12:28
18
小钟 也许是你环境没配好吧
环境我配过,现在可以在VS中调用Irvine32和C代码中调用汇编函数以及纯汇编代码,但是我说的“在汇编代码中调用c写的函数和C库函数”这个问题我没解决,还请指教
返回