Question & Answer
I get unresolved external references when trying to compile and link my program with the CPLEX CallableLibrary. What's wrong?
Unresolved external references occur when the symbol for a function or global
variable is referenced in a program, but none of the object files or libraries
specified in the link step contain a definition for that symbol. While the error
message may initially appear confusing, most unresolved external references
fall into a few distinct categories that are straightforward to correct. So,
an organized approach to categorize them can save significant time and effort.
While the following discussion focuses on linking problems that arise
with the CPLEX Callable Library, many of the tips here may be useful in fixing
linking errors in other applications.
Let's start with some general tactics that can help isolate the source of the linker error.
I. Try to reproduce the error with as simple an example as possible.
In the context of CPLEX, this means try to compile and link one of the simple C or C++ examples (e.g. lpex1.c or ilolpex1.cpp, respectively) and see if the linker error occurs there. If so, you have a smaller example with which to work. If not, focus on the differences between your program and the CPLEX example program as likely sources of the error. Your ILOG CPLEX software distribution includes make files or projects for the example programs you can use to compile and link the the examples.
II. Make use of some helpful tools available on your operating system.
Under Unix, the nm utility allows you to examine symbols in object files and libraries. By applying the nm utility to both the CPLEX Callable Library and the object files that call its functions, you can see if the symbol names associated with the functions are consistent. For example, use the following command to search an object file for the symbol associated with the CPLEX function CPXopenCPLEX():
nm -o lpex1.o | grep CPXopenCPLEX
The resulting output is:
lpex1.o: U CPXopenCPLEX
The output indicates lists the symbol for the CPXopenCPLEX function as it appears in the object file. The 'U' symbol indicates that this function call is not resolved in this object file; the function resides in some other object file or library.
Now, apply the same command to the CPLEX Callable Library libcplex.a.
nm -o libcplex.a | grep CPXopenCPLEX
The resulting output is:
libcplex.a:envlib.o:00000000 T CPXopenCPLEX
By comparing the nm output of libcplex.a and lpex1.o, we see that in this case the function symbol names match. However, if the symbols did not match in some way (e.g. the symbol appeared as CPXopenCPLEX@4 in lpex1.o), that mismatch is the cause of the linker error and must be corrected in order for the program to link properly. Note that a 'T' appears in the output for libcplex.a because, unlike lpex1.o, libcplex.a contains the actual function in question.
Refer to a man page or Unix manual for more information on the nm utility.
Windows based computers typically lack the nm utility. However, you can accomplish similar symbol comparisons using the library manager of your compiler, using your linker to generate a map file, or by simply opening your object file and CPLEX Callable Library from a text editor. Most library managers have an option that enables you to obtain an extensive symbol listing of a library or object file. In some cases you may need to first create a library from your object file. These listings allow you to compare symbol names in a manner similar to the one described above with the nm utility. If not, generate a map file with your linker and examine the symbol names therein. Otherwise, you can often search for symbols with a text editor. Even though the object and library files are binary, many text editors will still allow you to search them for character strings corresponding to symbol names.
III. Determine the type of unresolved symbol.
In many cases, identifying the type of unresolved symbol immediately sheds light on the source of the trouble. Here is a list of the most common types of linker errors, and the appropriate action to either fix the problem or obtain additional information.
* The linker issues unresolved errors for all CPLEX functions in your program.
Possible causes include:
1. Your link statement doesn't properly identify the CPLEX Callable Library. Compare your compile and link statement with the ones in the examples make file that comes with your ILOG CPLEX software. This may help you identify errors or omissions in your compile and link statement.
2. The CPLEX function names in the object files of your program do not match those of the CPLEX library because the compiler has put a prefix or suffix around the function names. Try compiling and linking one of the simple CPLEX example programs that comes with your software distribution. If you can reproduce the error, use the nm or lib utilities as described above to compare the function names. If they don't match, check your compiler's documentation for any options that affect function names. Examples include the Watcom compiler, which by default appends an underscore to the function names (use the -5s option from the compile line or the Pentium Stack based calling convention from the IDE to suppress the underscore), and FORTRAN compilers which frequently translate function names to upper case (most of them support the -U or some similar option that will cause the compiler to generate case sensitive names).
3. If you wrote the calling program, be sure that the function prototypes in the CPLEX header file cplex.h are surrounded by the 'extern "C"' statement. This will occur automatically with CPLEX 6.0 and later, but you will need to use this statement with earlier versions of CPLEX.
4. Your copy of the CPLEX callable library has been corrupted or is of the wrong format. Examples include trying to link to an old SunOS 4 CPLEX Callable Library on a Sun machine running Solaris, or trying to link from Borland C Builder to the import library that comes with your CPLEX distribution instead of one generated from the CPLEX DLL using Borland's implib utility. In such cases, you should be able to reproduce the error with one of the CPLEX example programs. Also, under Unix, the 'file' utility program determines the type of an individual file.
* The linker issues unresolved errors for only certain CPLEX functions in your program.
This type of error occurs much less frequently. Check the spelling of the
that generate the errors. Focus on other differences between the CPLEX functions
in libcplex.a that the linker finds and the ones that generate the errors. Use
the nm utility if necessary.
* The linker issues errors for functions with names such as "pow", "sqrt", "ceil" and "floor". Yet, your program does call any of these functions.
These are functions in the standard math libraries for your C or C++ compiler. You need to add the -lm option to your compile and link statement.
* (Windows only) The linker issues errors involving functions 'main' or 'winmain'.
When you build a Windows program, your compiler typically allows you to specify whether the application is a Console based or Windows based application. Console based applications require a 'main' routine, while Windows based applications require a 'winmain' routine. If you wrote your program to be a Console application but inadvertently specified a Windows application in your project or link statement, the linker expects the main routine to be 'winmain', yet your program specifies 'main' as the main routine. The result is a linker error.
* The linker issues errors for functions with name that are neither in your program nor in the CPLEX Callable Library.
All CPLEX functions begin with a 'CPX' prefix. If you get linker errors that do not have this prefix, are not called by your program, and are not part of the math libraries as mentioned previously, your system or compiler may be improperly configured to find all the libraries needed for your program. You may need to add a directory to your path, add another linker option to enable the compiler to find another library, update an out of date or corrupted library used by your compiler, or modify a compiler or linker environment variable to fix the problem. Once again, the nm utility can help under Unix. For example, suppose you compile and link your program and get an unresolved symbol for a function 'weirdfunc'. You know your own program doesn't call this function, and the CPLEX library doesn't contain it since it lacks the 'CPX' prefix. Therefore, 'weirdfunc' is probably a low level system function called by a system call made either by your program or the CPLEX library. You can find out if this function resides in one of the standard C libraries by going to the directory containing these libraries (typically /usr/lib) and typing the following command:
nm -o *.a | grep weirdfunc
This should help you determine the library containing this function. Also, for symbols like these, searching the web for the unresolved symbol name can often shed some light on the cause of the problem.
So, to summarize, an organized approach to linker errors should help reduce the time spent correcting them. By reproducing the error with as simple an example as possible, making use of all available system tools, and classifying the type of error, most linker errors can be resolved quite quickly.
16 June 2018