Geoff Chappell - Software Analyst
Attempts to build even some startlingly simple projects using common versions of Microsoft’s COFF linker may fail with either of the following error messages (depending on whether the build is of a VxD or not):
LINK : error : Internal error during ParseVxDDefExport
LINK : error : Internal error during ParseAnExport
This internal error means that an exception has occurred after entry to the ParseVxDDefExport or ParseAnExport function but before the linker gets round to recording that it has progressed to some other function. (Strictly speaking, these ParseVxDDefExport and ParseAnExport functions are known by those names only for the purpose of reporting errors. Whether the functions are called by those names in Microsoft’s source code is of no interest here.)
An EXPORTS statement in a module definition file introduces a series of EXPORTS definitions, possibly (and indeed, typically) spread over more than one line. In a valid EXPORTS definition, the first argument extends up to but not including the first space, tab or equals sign. The general idea is that if this first argument is a recognised statement keyword then it is not in fact the first argument of a new definition in the current EXPORTS statement but is instead the start of the next statement.
A special case is made for the VERSION keyword so that an EXPORTS statement may contain a definition that begins with VERSION and which acts to qualify the EXPORTS statement. This feature is meaningful when the target platform for the build is the Motorola 680x0 family (as set through /machine:m68k option on the command line).
Support for this feature as a special case when building for other platforms seems to be an error. When the linker starts parsing an EXPORTS statement, it prepares for this feature only if building for the Motorola machines. However, the linker recognises the special case of a VERSION within an EXPORTS for all machines. A line that the programmer intends as a VERSION statement (that happens to be placed after an EXPORTS statement) is therefore interpreted as a Motorola VERSION within an EXPORTS.
The VERSION definition in an EXPORTS statement for the Motorola machines admits one or two arguments. An empty definition is valid but trivial. If there is to be an argument, then the VERSION tag must be followed immediately by a space or tab. An overview of the syntax is:
Each argument may be preceded by white space. The series of three periods that separates the two arguments may not be preceded by white space. The line must end with the arguments. Even trailing white space is invalid. Each argument has the form:
where i may consist of one or two decimal digits; j, k and m are each one decimal digit; and l is one of the lower-case letters a, b, d or r.
If the line intended as a VERSION statement happens to be valid syntactically as a VERSION within an EXPORTS for the Motorola, then because the linker has not prepared for the Motorola case, there is no structure to receive the results of the parsing and the linker writes through a null pointer when trying to save the VERSION information. This CPU exception is reported as the internal error.
If the line intended as a VERSION statement happens not to be valid syntactically as a VERSION within an EXPORTS for the Motorola, then the consequence is fatal error LNK1118, which is simply a report of the syntax error.
This problem applies to the LINK.EXE version 2.60.5046 supplied with the Windows 95 DDK and to versions 3.00.5270, 3.10.6038 and 4.20.6164 from Microsoft Visual C++ 4.0, 4.1 and 4.2 respectively.
In LINK.EXE version 5.00.7022 supplied with Microsoft Visual C++ version 5.0, the ParseDefExports function has no code for recognising the Motorola VERSION within an EXPORTS, and so the problem goes away.
To side-step this bug, take care not to place a VERSION statement immediately after an EXPORTS statement.
Even now that Microsoft has detected the bug and corrected it for new versions, it should be borne in mind that it is not always possible for programmers to insist on particular versions of development tools. This applies especially when source code is written for others—for instance, when a consultant or contractor starts a project that may thereafter be maintained by the client, or when sample code is written for the instruction of and possible adaptation by unknown readers. In the absence of any good reason to insist on Visual C++ 5.0 (or higher), the greater benefit must come from adopting the work-around as standard practice.