从零开始,学习windows编程(6)--改换entry,link错误的简单分析

还是那个hello.c程序,我们将其小修改一下,来开始今天的话题。

1 #include <stdio.h>
2
3  int myentry()
4 {
5 printf("hello world");
6 return0;
7 }

可以看到,我将原来main的位置换成了myentry,这会有什么结果发生呢?

D:\test>cl /c hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

hello.c

OK,没有问题,生成了hello.obj。

D:\test>link hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

LIBC.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
hello.exe : fatal error LNK1120: 1 unresolved externals

link过程出现了问题,报了一个LINK2001错误。这个错误在MSDN上是这么说的:

unresolved external symbol "symbol"

Code references something (such as a function, variable, or label) that the linker can't find in the libraries and object files.

Possible causes

  • What the code asks for doesn't exist (the symbol is spelled incorrectly or uses the wrong case, for example).
  • The code asks for the wrong thing (you are using mixed versions of the libraries, some from one version of the product, others from another version).

This error message is followed by fatal error LNK1120.

具体原因,其实结合前面几篇的知识,就可以很容易的推理出来。如果大家有兴趣,可以试着先推理一下,再看下面的分析过程,这样理解更深刻一些。

对于LINK错误,应该算是C/C++特有的,而且比较麻烦,难以理解和解决的一类错误。C语言还好一些,到了C++中,LINK错误常常会带一些“乱码”符号,就更让初次接触到的童鞋们摸不到头脑了。正好借这一个小例子,来看一下思路。

首先,我们使用cl从hello.c生成了hello.obj文件,由于没有加上/Zl选项,所以生成的hello.obj文件还是带有两个defaultlib的,一个为libc.lib,还有一个oldnames.lib。这样,在LINK的时候,会将hello.obj与libc.lib进行链接,而libc.lib是很多obj合在一起形成的,里面有crt0.c生成的名为crt0.obj文件。回想我们上次看到的crt0.c的源码文件,里面函数为mainCRTStartup,在mainCRTStartup函数中调用到main函数了,main函数这个external symbol没有找到(其他函数如_heap_init。。。看来都是找到了),所以报出来一个LNK2001错误,告诉用户,main这个symbol没有找到,您把它忘哪儿啦。

相关扩展

link时使用/entry选项选定入口

D:\test>cl /c hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

hello.c

D:\test>link /entry:myentry hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

LIBC.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
hello.exe : fatal error LNK1120: 1 unresolved externals

其他不变,在link的时候加上/entry选项,结果还是和原来一样。因此,可以确定,link过程需要将所有的符号找到其所,而不像编译过程可以“打马虎眼”,即使是有一些external的也可以不理。另外,不管这个函数有没有“使用”到,都必须被link程序找到。这里的“使用”意思为执行过程中,执行到它。可以再看一个例子:

1 #include <stdio.h>
2
3  int myentry()
4 {
5 printf("hello world");
6 return0;
7 }
8
9 int main()
10 {
11 myentry();
12 }
13
14 int test()
15 {
16 nofunc();
17 return0;
18 }

从上面的代码可以看到,在默认编译链接的情况下,test函数是没有被调用到的,而其中的nofunc函数是没有定义的。我们来对其做一下编译链接。

D:\test>cl /c hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

hello.c

D:\test>link hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

hello.obj : error LNK2001: unresolved external symbol _nofunc
hello.exe : fatal error LNK1120: 1 unresolved externals

可以看到,link的时候,找不到nofunc这个symbol,从而报错。

这也是C/C++的一个比其他语言麻烦的地方,有编译时、链接时和运行时的划分,这样子清晰了,但是概念也多了,如果再加上隐藏了一些东西的实现和原理,理解起来就更不容易了。

使用/Zl选项

如果对myentry代码使用/Zl选项。

D:\test>cl /c /Zl hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

hello.c

D:\test>link hello.obj
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

LINK : fatal error LNK1561: entry point must be defined

此时,则出现LNK1561错误。MSDN上对应的解释为:

entry point must be defined

The linker did not find an entry point. You may have intended to link as a DLL, in which case you should link with the /DLL option. You may have also forgotten to specify the name of the entry point; link with the /ENTRY option.

Otherwise, you should include a main, wmain, WinMain, or wMain function in your code.

If you using LIB and intend to build a .dll, one reason for this error is that you supplied a .def file. If so, remove the .def file from the build.

因为/Zl去掉了defaultlib,所以link直接就是hello.obj,而不会去链接libc.lib,因为默认定义的entry symbol为mainCRTStartup,此时没有libc.lib,里面的mainCRTStartup也就没有,所以会提示出没有定义entry point错误。

Way1:

照MSDN上面的解释,我们再来将main函数使用上,然后做编译/Zl,以及链接工作会怎样呢?

首先将myentry改为main。之后:

D:\test>cl /c /Zl hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

hello.c

D:\test>link hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

hello.obj : error LNK2001: unresolved external symbol _printf
LINK : error LNK2001: unresolved external symbol _mainCRTStartup
hello.exe : fatal error LNK1120: 2 unresolved externals

此时的错误又不相同,这里出现寻找不到printf和mainCRTStartup的链接错误。

Way2:

将/entry选项指定为myentry。

D:\test>cl /c /Zl hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

hello.c

D:\test>link /entry:myentry hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

LINK : fatal error LNK1221: a subsystem can't be inferred and must be defined

又出现一个新错误,LNK1221。来看看解释:

a subsystem can’t be inferred and must be defined

The linker does not have enough information to infer which subsystem you will target your application.

To fix this error, use the /SUBSYSTEM option.

看来link的时候,还可以根据搜到的symbol以及定义的entry来自动判断编译出来的目标应用的subsystem,关于subsystem,似乎只有windows下面有这个编译选项,加上来再看一下。

D:\test>link /entry:myentry /subsystem:console hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

hello.obj : error LNK2001: unresolved external symbol _printf
hello.exe : fatal error LNK1120: 1 unresolved externals

OK,当加上之后,也出现了LNK2001错误,比上面要少一个_mainCRTStartup,还需要一个_printf。

Way3:

而综合上面两种方法,将way1和way2合起来使用,使用main函数代替掉myentry的源码,然后将main设置entry point。

D:\test>cl /c /Zl hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

hello.c

D:\test>link /entry:main hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

hello.obj : error LNK2001: unresolved external symbol _printf
hello.exe : fatal error LNK1120: 1 unresolved externals

此时的结果与way2相同,都是找不到printf这个symbol,而printf的实现的确是在libc.lib中的。

解决上面的问题很简单,只要链接libc.lib即可(当然,对于运行时还是有问题的,具体看上一篇的执行结果)。而不指定entry,同时使用main,出现的结果有mainCRTStartup链接不到的提示,说明link的时候链接器找到main,也从它的认识,认为入口为mainCRTStartup,但是找不到该symbol,所以报了这么一个错误。这也是常常让初学者摸不到头脑的一个错误。

参考:

  1. http://msdn.microsoft.com/en-us/library/f6xx1b1z%28v=vs.71%29.aspx
  2. http://msdn.microsoft.com/en-us/library/ky737ya4%28v=VS.71%29.aspx
  3. http://msdn.microsoft.com/en-us/library/aa235394%28v=vs.60%29.aspx
  4. http://delxu.wordpress.com/2008/11/02/windows%E7%9A%84%E7%8E%AF%E5%A2%83%E5%AD%90%E7%B3%BB%E7%BB%9F/
  5. http://hi.baidu.com/cyclone/blog/item/96624a90fb4ca081a977a4db.html

posted on 2011-06-20 13:24  cnyao  阅读(3615)  评论(7编辑  收藏  举报