QLINK Documentation File
23 March 2006
QLINK is a DOS linker and analysis tool designed to link together MS-DOS
compatible .OBJ files. It can replace the MS-DOS LINK.EXE program
when producing MS-DOS compatible .EXE and .COM files.
Make a directory (e.g., C:\QLINK), copy the zip file to that
directory, and unzip the files:
If you'll be running QLINK under Windows 3.1x (see below for Win95 instructions),
copy the file WINDPMI.386 to your Windows system directory.
For example, if you installed Windows into the directory C:\WINDOWS,
copy WINDPMI.386 to C:\WINDOWS\SYSTEM. Then edit
your Windows SYSTEM.INI file to insert a line such as the following in
the [386ENH] section:
You should first ensure that no other similar line already appears in
your SYSTEM.INI file. For example, you might already have a line
If this is the case, do not insert another call to the same driver; you
need only one. This VxD does not work with Win9x.
If you'll be running QLINK under Win9x, follow the above procedure using
the file W95DPMI.386 instead of WINDPMI.386.
- One pass linker (using uncommitted
memory in DPMI 1.0)
- Better performance (typically twice as fast as MS LINK, sometimes
ten times faster)
- Handles USE32 segments > 64KB
- Detailed error checking to the point that it becomes a highly valuable
- Detailed error information (e.g., source code line number info (if
in .OBJ file) for fixup overflows)
- Type checking between .OBJ files (if in .OBJ files).
- MS-DOS 3.x or later
- DPMI host which supports DPMI 1.0 calls -- use either 386MAX version
7.0 or later, or Windows 3.1 with a (supplied) VxD from Borland (WINDPMI.386),
or Win95 with a different VxD (W95DPMI.386), or
How To Use
For the most part, just call QLINK instead of LINK or TLINK as appropriate.
Borland users should note that a number of Borland specific Object Module
Formats (OMFs) are not implemented as yet (I'm waiting for the documentation
from Borland). Several MS link switches are not supported as yet
(e.g., /PACKC). If there are switches you particularly
need which are not supported, let me know. For an explanation of
the old linker switches, see your linker manual.
To take advantage of the detailed error processing in QLINK, use the
assembler switches which generate types and line numbers. For MASM
and TASM these switches are /Zd and /Zi.
The order in which segments appear in the executable file depends on
several factors. The first is whether or not the /DOSSEG
switch appears explicitly on the command line or implicitly in a OMF record
in one of the .OBJ files.
If /DOSSEG is specified, the segment order is as follows:
- All segments with a class name ending in 'CODE'
- All other segments not in DGROUP, grouping together segments
with the same class name
- DGROUP segments in the following order:
- Any segments of class 'BEGDATA'
- Any segment not of class 'BEGDATA', 'BSS', or
- Segments of class 'BSS'
- Segments of class 'STACK'
Otherwise, the segment order is as follows:
- All unclassed segments
- All classed segments by class (that is, segments in the same class
appear adjacent to each other).
There are a number of switches specific to QLINK which are documented
in the file QLINK.CFG. These switches control the processing of
error messages from QLINK. All error messages begin with either
Messages which begin with WARN are warnings and do not halt
the linker. Messages which being with FAIL cause the linker
to stop immediately and not continue processing the input files.
If an error message is followed by a name such as FIXOVF or
GRPEXT0 in parentheses, then that error can be controlled by
the switches /I:switch, /W:switch, and
/F:switch, where switch is the name in parentheses
in the error message.
If you wish to ignore this error (meaning the linker takes a default
action and continues processing), use /I:switch.
To warn about an error (meaning an error message is displayed, the linker
takes a default action, and continues processing), use /W:switch.
The default settings for all error messages are described in the file
This same file (QLINK.CFG) is consulted when QLINK begins execution.
Any switches found there (including switches such as /MAP, /LINE,
etc.) are processed before the command line is parsed. Switches
only may be contained in QLINK.CFG, not names of .OBJ files, etc.
Even earlier in the process, the environment variable QLINK=
is consulted, and it too may contain only switches.
Thus the order of processing of switches is first, those contained in
the environment variable QLINK=, then those in the file QLINK.CFG
(first in the current directory, and if not found there in the directory
from which QLINK is loaded), and finally those found on the command line
to QLINK. Switches processed later in the sequence override ones
Occasionally, you want to link together .OBJ modules from different projects
which use different naming conventions. For example, in one project
code segments are in class CODE and in others they are in class
PROG. Previously, you would have to edit the source code,
make the changes, and re-compile. With the Name Substitution feature
of QLINK, it's a snap.
To substitute names on the fly within the .OBJ file, place the switch
/NS before the reference to each .OBJ file whose names are to
be substituted. For example, use /NS:PROG-CODE to tell
QLINK that the name PROG is to be changed to CODE.
Each substitution is effective for all .OBJ files which appear after
it until that substitution (or all substitutions) are halted. Use
the form /NS:name to halt substitutions on name;
use /NS with no arguments to halt all substitutions.
Note that this means that the occurrences of /NS are sensitive
to the position and order in which they appear. Be sure to place
occurrences of /NS before the reference to the .OBJ file
to which they apply.
To swap two symbols in the same file, use (say) /NS:A-B:B-A.
The substitution is made on all references to the name regardless of
context. Thus if you have a file with a segment named PROG
and a class named PROG, substituting CODE for PROG
changes both references.
The full syntax is
; Halt substitution on all names
; Halt substitution on this name
; Substitute the second name for the first name
Switch: '/NS:' Namedef
The keyword /NS may appear in the QLINK environment variable,
the QLINK.CFG configuration file, the automatic response file, and the
QLINK command line.
Frequently Asked Questions
||When I link modules with the MS linker and QLINK, sometimes the
executable files are of very different sizes?
||This can occur if a .LIB is used to resolve external references.
Because there is no rule as to the order in which external refs are
processed, different ordering of these references mean that there
can be different segment boundary alignments which can change the
final executable file size.
In no particular order of importance (nor of expectation of getting done),
the following topics are on my list:
- Support Borland-specific OMFs
- Support MS-specific OMFs (for which I don't have any examples)
- External procedure for symbol processing (instead of reading .MAP
- Allow segment attribute changes per .OBJ file
- Generate Windows compatible .EXEs
- Generate Code view information
- Generate Turbo Debugger information
- Compress .EXE using LZH or some such technique (for Windows executables
- Finish type checking of structures
Please feel free to add to this list.
Please contact the author via Internet e-mail at
QLINK is © Copyright 1994-2006 Qualitas, Inc. All rights reserved.
5.08 23 March 2006
- Fix bug in .LIB symbol compares for case-insensitive
libraries whose symbols contain uppercase letters (thanks
5.07 2 January 2004
- Fix bug which prevents multiple .LIB files from being recognized (thanks
5.06 24 December 2003
- Catch invalid OMF record where an LIDATA record has a zero repeat
5.05 19 June 2003
- Mark COMM variables as USE32 if the segment in which they are defined
(c_common or FAR_BSS) is USE32.
- Load .OBJ files into extended memory instead of low DOS in case there's
not enough room.
- Implement undocumented /KNOWEAS for compatibility with MS linker.
5.04 22 May 2003
- Added more information to USEDIF error message to point to .OBJ file
in which the segment was first defined.
- Added references to DPMIONE as a DPMI 1.0 host under which QLINK
5.03 21 July 2002
- Modified the change in version 5.00 for fixup overflows to treat
the Target Displacement as a signed number and then ignore overflows
if the upper 24- (for byte fixups) or 16-bits (for word fixups) are
5.02 1 July 2002
- Fix bug where a MODEND fixup generating any kind of error causes
the routine which displays the .OBJ file name to fail (thanks to Vladomir
Rodriquez for pointing this out).
5.01 26 June 2002
- Fix bug where .MAP file occasionally not written out.
- Display segment combine type in /MAP:FULL.
- Fix bug in display of line #s for grouped segments which are not first
in the group.
- Append IGNOREd errors to .ERR file if /DEBUG:ERR in effect.
- Added FIXOVF$ switch to ignore fixup overflows in '$$SYMBOLS' segments.
- Fix bug when parsing command line and/or .ARF file if leading '+'
in multiple entry (.OBJ or .LIB) fields.
5.00 26 June 2002
- Change version # to 5 to workaround bug in EXEHDR.
- Fix bug in display of FRMSEG$ message.
- Display error message if not enough memory to enter PM through the
- Fix bug when checking for fixup overflows where the displacement
wasn't added in before the overflow check, thus missing some overflows
(thanks to Vladomir Rodriquez for pointing this out).
1.30 22 June 2002
- Added MTOBJ switch to fail on empty .OBJ files which can occur when
a language translator creates an object file but halts for some reason
before writing anything to it.
1.29 18 April 2002
- Fix bug to change ignore/warn action on OMFUNK to ignore the record.
1.28 25 April 2000
- Fix bug handling weak externs if the symbol is already public.
1.27 18 April 2000
- Implement support for COMDAT records.
- Avoid searching through duplicate library names.
- Handle blank line in ARF file as field marker.
- Allow library directories in libfiles part of the command line.
1.26 10 April 2000
- Extend checking for FRMSEG, FRMSEG0, and FRMSEG$ to the FT01 case.
- Define RELTGT switch to catch the case where a self-relative fixup's
Frame and Target segments are different in the FT00 case.
1.25 7 April 2000
- Extend checking for RELGRP errors in self-relative fixups to the
FT10 and FT11 cases.
- Extend checking for RELGRPX and RELSEGX errors in self-relative fixups
to the FT20 and FT21 cases.
- Extend checking for RELSEG errors in self-relative fixups to the
1.24 4 April 2000
- Extend Name Substitutions to PUBDEF and EXTDEF records (it previously
applied to LNAMES & LLNAMES records only).
- Extend Name Substitutions to .LIB files.
1.23 30 March 2000
- Define FRMSEG$ switch to catch the case where a FRMSEG error occurs
in a fixup segment named '$$SYMBOLS'. This reduces some of the
noise when linking with debugging info. The default action is
to ignore the error.
- Fix bug when an external mixed-case symbol precedes the matching
public declaration of the same symbol in a different case.
1.22 30 March 2000
- Implement /NS keyword to handle name substitutions.
- Implement additional debugging display for fixups via /DEBUG:FIXUP.
1.21 28 March 2000
- Force /NOE as I can't figure out how it works. I thought
I understood it, but now I'm convinced I do not.
1.20 24 March 2000
- Implement /FARCALL.
- Fix bugs when recognizing special class, segment, and group
names (wasn't case-insensitive and was off by one in length when comparing
1.19 22 March 2000
- Define FRMSEG0 switch to reduce the number of spurious FRMSEG
messages in the case where the segment is the first one in the group.
In this case, the fixup value is the same independent of whether the
fixup is segment- or group-relative. The default action is to
ignore FRMSEG0 errors.
1.18 15 March 2000
- Fix bug in self-relative fixups for several Frame vs. Target
cases I never thought could occur until NASM came along.
1.17 2 October 1999
- Change default behavior of ALINDIF to align segments of the same
type according to the actual alignment (which may differ from segment
to segment) instead of enforcing a single alignment across all segments
of the same type. This change mimics the MS-LINK behavior.
Using segments of the same type with different alignment is still a
1.16 8 September 1999
- Fix bug when encountering multiple different segments with stack
combine type (use the first one only).
1.15 6 September 1999
- Fix bug which didn't display an error if a .LIB file was not found.
- Implement switches for BLKDEF, BLKEND, and TYPDEF records instead
of lumping them into OMFIGN. As the default action is to ignore
these records, you don't have to ignore all OMFIGN records just to ignore
1.14 3 September 1999
- Fix bug where default EXE and MAP filenames were not displayed when
using an Automatic Response File.
1.13 25 May 1999
- Fix bug with not generating .MAP file when /MAP specified without
an end-of-field marker.
1.12 16 May 1999
- Fix bug with /DOSSEG segment ordering.
1.11 25 June 1998
- Compare segment and class names case insensitively so as to mimic
MS LINK behavior.
- Fix bug in FIXUPP of Frame Segment, Target External where the wrong
variable was used when checking for FRMSEG errors.
- Round down Frame Base to para boundary before calculating fixups.
1.10 27 April 1998
- Mark THRINV as ignored. Apparently, MSVC 8 (and possibly earlier
versions) set bit 2 in the method field of a THREAD subrecord in a FIXUPP
- Fix bugs with aliased symbols.
- Fix spurious error report with BAKPAT records.
1.09 12 March 1998
- Fix bug in parsing of .LIB file on the command line so that QLINK
no longer asks for more .LIB files if one is specified.
- Implement /OPTHEADER (/OP) to optimize the .EXE
file header by rounding the header up to a paragraph boundary instead
of a 512-byte boundary.
1.08 12 November 1997
- Wrote the VxD W95DPMI.386 which provides the appropriate
DPMI 1.0 functions for QLINK to run under Win95. In particular,
this VxD supplies the needed calls to allocate uncommitted pages.
1.07 8 July 1997
- Add EXTMAT config option to display message if an external
in a module is not referenced by that module. Presumably (but
not always), these references can be deleted from the source file.
1.06 25 June 1995
- If /MAP but no explicit entry in map file field, create
- Fix bug in fixup of far call to extern in a later segment which doesn't
start on a para boundary.
- Fix bug where /NOE didn't work.
- Fix bug where null para at start of _TEXT not handled.
1.05 8 May 1995
- Implement /ONERROR:NOEXE.
- Parse and ignore /NOLOGO.
- Parse and warn /PACKCODE:nnn.
- Use FSA to parse .ARF files.
- Change THRINV from always Fail to Ignore, Warn, Fail.
- Support BAKPAT records.
- Support CEXTDEF records.
- Support LLNAMES records.
- Treat extra fields in ARF file as EOF.
- Implement /NOIGNORECASE.
1.04 25 March 1995
- Allow addition with seg directive, e.g., DW (seg PGROUP)+10h.
Note that MS-LINK doesn't handle this correctly.
- Fix bug in handling of OMF FIXUPP records for Frame &
Target external for self-relative fixups. This format is used
by MASM 6.10, but neither MASM 5.10b nor 6.11a.
1.03 14 February 1995
- Allow options occasionally enabled by some Integrated Development
Environments which turn off unsupported features such as
- Also allow (and signal warning) for unsupported features
1.02 18 November 1994
- Fix bug if QLINK is run w/o DPMI host present.
- Fix misspelling of /NOEXTDICTIONARY.
1.01 26 October 1994
- Fix bug if searching for library file.
- Write out minimal sized .EXE file.
1.00 2 October 1994