Many users of Visual C ++ have encountered connection errors such as lnk2005: symbol already defined and lnk1169: one or more multiply defined symbols found, and it is usually encountered when using a third-party library. Some may be unaware of this problem, while some may be unaware of it, so this article attempts to thoroughly solve all kinds of questions about it.
As we all know, the process from C/C ++ source code to executable files goes through two phases: (1) the compiler compiles the source file into assembly code, and then the assembler (compiler) the default Suffix of the target file compiled by the VC compiler is. (2) The linker links the target files (or some libraries) together to generate a complete executable file.
When compiling the source file, the compiler divides the global symbol of the source file into two types: strong and weak, the assembler then encodes the strong and weak information and saves it in the symbol table of the target file. So what is strength? The compiler considers that the function and initialized global variables are both strong symbols, while uninitialized global variables become weak symbols. For example, there is a source file:
/***********************************/
Extern int errorno;
Int Buf [2] = {1, 2 };
Int * P;
Int main (){
Return 0;
}
/***********************************/
Here, main and Buf are strong symbols, P is weak symbols, and errorno is not strong or weak, because it is only the declaration of external variables.
With the concept of strong and weak symbols, we can see how the linker handles and selects the global symbols that have been defined multiple times:
Rule 1: do not allow strong symbols to be defined multiple times (that is, different target files cannot contain strong symbols of the same name );
Rule 2: If a symbol is a strong symbol in a target file and is weak in other files, select a strong symbol;
Rule 3: If a symbol is weak in all target files, select any one of them;
Multiple Target files can repeatedly define functions with the same name and initialized global variables. Otherwise, two Link errors, lnk2005 and lnk1169, will inevitably occur. However, sometimes we do not find such a redefinition in our own programs, but we also encounter such a link error. Why? Well, the problem is a little complicated. Let me know it slowly.
As we all know, ansi c/C ++ defines a considerable number of standard functions, which are distributed in many different target files, if the target file is directly provided to programmers, they need to know exactly which function exists in which target file, in addition, the target file name can be explicitly specified during the link to successfully generate an executable file, which is obviously a huge burden. Therefore, the C language provides a mechanism to Package Multiple Target files into one file, which is the static library ). When linking, developers only need to specify the library file name, And the linker will automatically search for those applications in the library.IndeedAnd copy (and only copy) the target modules from the database to build the executable files. Almost all C/C ++ development systems package standard functions into a standard library for developers to use (do they not do this ?).
The Library brings convenience to developers, but it is also the root of some chaos. Let's take a look at how the linker parses (RESOLVE) references to the library.
In the symbol resolution phase, the linker scans all target files and library files from left to right in the order they appear in the command line, during this period, it maintains several collections: (1) Collection E is a collection of all target files that will be merged together to form executable files; (2) set U is a set of unparsed symbols (unresolved symbols, such as referenced but not defined symbols); (3) set D is a collection of all the symbols defined in the target file that have previously been added to E. At the beginning, E, U, and D were empty.
(1): For each input file F in the command line, the linker determines whether it is a target file or a library file. If it is a target file, add F to E, add unparsed symbols and defined symbols in F to the U and D sets respectively, and then process the next input file.
(2) if F is a library file, the linker will try to match all unresolved symbols in u with those defined by the target modules in F. If a target module M defines an unparsed symbol in U, then M is added to E, add unparsed symbols and defined symbols in M to the U and D sets respectively. Repeat this process for all target modules in F until a fixed point is reached. At this time, the U and d do not change. The target modules that are not added to F in E are simply discarded, and the linker continues to process the next input file.
(3) If an existing symbol is added to d during processing, or U is not empty after scanning all input files, the linker reports an error and stops the action. Otherwise, it combines all the target files in e to generate executable files.
The editor name of the vcpus is cl.exe, which has the following options related to the standard library:/ml,/MLD,/mt,/MTD,/MD,/MDD. These options tell the compiler application the version of the C standard library to be used. /Ml (default option) corresponds to the standard library (libc) of the single-thread static version. lib);/MT corresponds to the standard library (libcmt. lib). At this time, the compiler will automatically define the _ Mt macro;/MD corresponding to the multi-threaded dll version (imported into msvcrt. lib, DLL is msvcrt. the compiler automatically defines two macros: _ MT and _ DLL. The option D will allow the compiler to automatically define one more _ debug macro, indicating that the debugging version of the corresponding standard library is used. Therefore,/MLD corresponds to the single-thread static standard library (libcd. lib),/MTD corresponds to the multi-threaded static standard library (libcmtd. lib),/MDD corresponds to the debugging version of the multi-threaded DLL standard library (imported to msvcrtd. lib, DLL is msvcrtd. DLL ). Although we did clearly tell the compiler application what version of the standard library we want to use during compilation, when the compiler is finished, how does the linker know who the target files are missing when it's time to start? In order to pass on lovesickness, our compiler has done something secret. There will be a special area in the target file compiled by Cl (if you are concerned about the location of the area in the file, refer to coff and PE file formats) stores information about how the linker works, one of which is called the default Library (default
Library), this information specifies one or more library file names, tell the linker to add them to the input file list during scanning (of course, the order is after the specified input file in the command line ). Here, we will first make a small experiment. Write a simple program and save it as main. C:
/* Main. C */
Int main () {return 0 ;}
Use the following command to compile main. C (what? You never use command lines to compile programs? This ......):
CL/C main. c
/C tells Cl to compile only source files without links. Because/ml is the default option, the preceding command is equivalent to Cl/C/ml main. C. If there is no problem (it's the best thing to do if something goes wrong! Of course, unless your environment variables are not set, you should find the vcvars32.bat file in the bin directory of VC and run it .), A main. OBJ file will appear in the current directory, which is our lovely target file. Open it with a text editor (yes, the text editor, do not be afraid) and search for the "defaultlib" string. You will usually see something like this: "-defaultlib: libc-defaultlib: oldnames ". Aha, that's right. This is the default library information stored in the target file. Our target file clearly specifies two default libraries. One is the standard library libc of the single-threaded static version. lib (this is consistent with the/ml option), and the other is oldnames. lib (to be compatible with Microsoft's previous C/C ++ development systems ).
The chain connector of vcss is link.exe. Because main. OBJ stores the default library information, you can use
Link main. OBJ libc. Lib
Or
Link main. OBJ
To generate the executable file main.exe. These two commands are equivalent. But if you use
Link main. OBJ libcd. lib, the linker will give a warning: "Warning lnk4098: defaultlib" libc "conflicts with use of other libs; Use/nodefaultlib: Library ", because the default values of the standard library version you explicitly specify are different from those of the target file. Generally, ensure that the default standard library versions specified for all target files merged by the linker are consistent. Otherwise, the compiler will give the above warning, the lnk2005 and lnk1169 Link errors sometimes occur and sometimes do not. So when is this sometimes? Well, don't worry. Everything Below is exactly what you want to get to the bottom.
Create a source file named mylib. C. The content is as follows:
/* Mylib. C */
# Include
Void Foo (){
Printf ("% s", "I am from mylib! \ N ");
}
Use
CL/C/MLD mylib. c
Command compilation. Note that the/MLD option specifies libcd. Lib as the default standard library. Lib.exe is a VC command used to package the target file into a library, so we can use
LIB/out: My. Lib mylib. OBJ
Package mylib. OBJ into a library. The output file name is my. Lib. Next, change main. C:
/* Main. C */
Void Foo ();
Int main (){
Foo ();
Return 0;
}
Use
CL/C main. c
Compile and then use
Link main. OBJ my. Lib
. This command can successfully generate main.exe without generating lnk2005 and lnk1169 Link errors. You only get a warning message: "Warning lnk4098: defaultlib" libcd "conflicts with use of other libs; Use/nodefaultlib: Library ". Based on the scan rules described above, we can analyze what the linker has done at this time.
At the beginning, E, U, and D are empty sets. The linker first scans main. OBJ, add it to the E set, add the unparsed Foo to the U, add the main to the D, and because the main. the default standard library of obj is libc. lib, so it is added to the end of the current input file list. Then scan my. lib, because this is a library, it will take all the symbols in the current U (of course, a foo now) and my. all target modules in lib (of course there is only one mylib. OBJ. Result: mylib. OBJ does define Foo, so it is added to E, foo is transferred from u to D, mylib. the printf referenced by obj is added to u. Similarly, mylib. the default standard library specified by obj is libcd. lib, which is also added to the end of the current input file list (in libc. lib ). Constantly iterate on the modules of my. Lib library to match the symbols in U until u and d do not change. Obviously, now we have reached such a fixed point, so we will scan the next input file, which is libc. Lib.
The linker found libc. printf. in OBJ, printf is defined, so printf moves from u to D, while printf. OBJ is added to E. All the symbols defined by OBJ are added to D, and unparsed symbols in OBJ are added to u. The linker will also set the target modules (such as crt0.obj) where some initialization operations will be used by each program and the modules they reference (such as malloc. OBJ, free. (OBJ) is automatically added to E, and U and D are updated to reflect this change. In fact, the unresolved symbols in each target module of the standard library can be defined in other modules of the library. Therefore, when the linker completes libc. Lib processing, the u must be empty. Finally, libcd. Lib is processed. Because the U is empty at this time, the linker will discard all the target modules in it to end scanning, then merge the target modules in E and output executable files.
The above describes an example of a successful link even though different versions of the default standard library are specified for each target module. Next, you will see a miserable failure caused by this rigor.
Modify mylib. C as follows:
# Include
Void Foo (){
// Just a test, don't care about Memory Leak
_ Malloc_dbg (1, _ normal_block, _ file __, _ line __);
}
Among them, _ malloc_dbg is not an ansi c standard library function. It is a debugging version of malloc provided by the VC standard library. It works with related functions to help developers catch various memory errors. To use it, you must define the _ debug macro. Otherwise, the pre-processor will convert it to malloc automatically. Continue to use
CL/C/MLD mylib. c
LIB/out: My. Lib mylib. OBJ
Compile and package. When you use
Link main. OBJ my. Lib
What do we see during the link? My God, a bunch of lnk2005 plus an lnk1169 expensive for "Fatal error", of course, the lnk4098 is also indispensable. Is the linker crazy? No, you have wronged the poor linker. I promise it will keep working with my due diligence.
E, U, and D are empty at the beginning, and the linker scans main. OBJ, add it to e, add Foo to U, add main to D, and add libc. add lib to the end of the current input file list. Then, scan my. Lib, foo from u to D, add _ malloc_dbg to U, and add libcd. lib to the end of the current input file list. Then scan libc. lib. no target module in lib defines _ malloc_dbg (It only exists in the standard library of the debugging version). Therefore, no module is added to E because of _ malloc_dbg, however, each program uses initialization modules (such as crt0.obj) and their referenced modules (such as malloc. OBJ, free. will be automatically added to E, and U and D will be updated to reflect this change. When the linker completes libc. Lib, only the _ malloc_dbg symbol is left in the U. Finally, handle libcd. lib, dbgheap is found. OBJ defines _ malloc_dbg, so dbgheap. OBJ is added to E, and the unresolved symbols in it are added to u. All other symbols defined by OBJ are also added to D. Then, the disaster will come. Previously, symbols such as malloc were already in D (with libc. malloc in Lib. OBJ is added to E), while dbgheap. OBJ defines many cognominal symbols, including malloc, which triggers a redefinition conflict and the linker has to interrupt the work and report errors.
Now we should know that the linker has no responsibility at all, and the responsibility lies with ourselves. We carelessly linked the target file (main. OBJ) with the Library (My. Lib) that is inconsistent with the default standard library version, resulting in a catastrophic event. The solution is simple. Either use the/MLD option to re-compile main. C, or use the/ml option to re-compile mylib. C.
In the above example, we have the source code of library my. Lib (mylib. C), so we can re-compile the source code with different options and package it again. However, if a third-party library is used and the source code is not provided, we only need to change the compilation options of our program to adapt to these libraries. But how do I know the default library specified by the target module in the library? In fact, a small tool of vcpus can be used to complete the task. This is dumpbin.exe. Run the following command
Dumpbin/directives my. Lib
Find the "linker ctictives" Bootstrap information in the output. You will surely find that each such information contains several strings similar to "-defaultlib: XXXX, XXXX represents the default database name specified by the target module.
Knowing the default standard library specified by a third-party library and compiling our application with appropriate options can avoid lnk2005 and lnk1169 Link errors. If you like IDE, you can go to "Project Properties"-> "C/C ++"-> "code generation) "->" Run-Time library "sets the default standard library version of the application, which is the same as the command line option.