This is the DJGPP Frequently-Asked Questions List.
Copyright © 1994, 1995, 1996, 1997, 1998, 2000 Eli Zaretskii.
This is the third edition of the FAQ list;
it is consistent with version 2.03 of DJGPP.
This FAQ list may be freely distributed with the DJGPP package or any part thereof, provided that this copyright notice is left intact on all copies.
Make
, or Info
, or ...) Crashes...
as
"?
.exe
file
.exe
files so large?
sys/movedata.h
?
stubify
.
.cc
source
malloc
/free
don't affect virtual memory...
@
.obj
or .lib
code with DJGPP
int86
go32-v2.exe
program?
foo
has modification time in the future"
zoneinfo
directory?
main
function return in a C/C++ program?
stdin
?
In DJGPP (see DJGPP overview), a 32-bit compiler and programming environment, originally written for Unix machines, meet a 16-bit MS-DOS operating system. Programmers who work in this environment have to master a large body of knowledge from both Unix and MS-DOS, especially if they want to use some advanced features, like interrupt handling, directly accessing peripheral devices, etc.
But because the DJGPP project is run by a group of volunteers on their free time, there isn't always enough time (or patience, or money ;-) to produce documentation which will describe all the subtle features and pitfalls a user should know about. The documentation of DJGPP-specific utilities and features is minimal at times, leaving wide space for confusion, in newcomers and veterans alike, and making the DJGPP learning curve steeper than it could be.
This FAQ list is an attempt to take the sting out of that learning curve, by supplying solutions for problems which are known to puzzle DJGPP users. (Another solution would be to pay to DJ Delorie and other people who develop DJGPP to produce more documentation ;-).
Some additional places to look for tutorials and other introductory material about DJGPP are listed below.
One good place to look for DJGPP features that are often overlooked is
the DJGPP Knowledge Base. The
Knowledge Base is also available in Info format; type info
knowledge
from the DOS prompt. In addition, a User's Guide is
being written by several contributors; it is currently available
from the DJGPP server.
You can browse the HTML version of this FAQ list on line at DJ Delorie's Web server. You can also download FAQ in several additional formats and browse it locally.
A previous version of this FAQ was translated into French, and is available via FTP, and also through the Web.
A Japanese translation is available via the Web.
This is Edition 2.30 of the FAQ, last updated 2 February 2000, for DJGPP Version 2.03.
The following master menu lists the major topics in this FAQ list, including all the indices.
Q: Do you really mean I have to read this looongish FAQ list to get my
answers?
Q: I have this problem which I absolutely MUST solve NOW! What do I
do?
A: No, you don't need to read all of the FAQ unless you want to (although this is by all means recommended). The questions in this document are listed, as much as possible, in the order they appear when one goes through getting DJGPP, installing it and using it. To quickly find an answer to your question, first look at the Table of Contents. If that doesn't help, try the indices at the end of this manual. You can look up your question either by program name, or by topic name.
If you don't find anything appropriate through the indices, search this FAQ for words which are pertinent to your problem1.
If searching the FAQ didn't help, try the DJGPP archives search, where you can find reports about similar problems and their solutions. If that doesn't help either, try asking the DJGPP gurus.
For those in a real hurry, here are some pointers to the most important topics in this FAQ list:
Here's a brief description of the necessary steps:
chdir
there. Do NOT
install DJGPP in a directory called \dev
on any drive, or in
any of its subdirectories: it won't work!
*.zip
files preserving the directory structure. On
Windows 9X, use an unzip program which supports long file names. On
Windows NT, use a DOS unzip program that does not support long
file names. A free unzip program unzip32.exe
is available from
the DJGPP archives
which does the Right Thing on both DOS, Windows 9X and Windows NT, so I
recommend using unzip32.exe
to unzip DJGPP.
*.zip
files. You need to unzip them all from the same
directory! I'm told that the Aladdin Expander also automatically
renames directories it extracts if a directory by that name already
exists; this renaming should be disabled as well. If you have ZipMagic
on your machine, disable it, as it also unzips each file into its
directory.
AUTOEXEC.BAT
file and add these two lines:
set PATH=C:\DJGPP\BIN;%PATH% set DJGPP=C:\DJGPP\DJGPP.ENV
If your top DJGPP directory is other than C:\DJGPP
,
change these two lines accordingly!
You can use any text editor to add these two lines. On Windows 95,
right-click on AUTOEXEC.BAT
in Explorer
or in
My Computer
, then select Edit
from the pop-up menu. On
Windows 98, click START
, then choose, successively, Programs,
Accessories, System Tools, System Information, Tools, System
Configuration, and use the AUTOEXEC.BAT
tab to edit the file. On
DOS, type from the command prompt edit c:\autoexec.bat.
On Windows/NT, use My Computer->Properties->Environment
to edit
the default value of PATH
for the DOS box, and to add a new
variable DJGPP
whose value is set to the full pathname of
DJGPP.ENV
, as shown above.
DJGPP.ENV
is on a CD-ROM), you need to set an additional
environment variable, TMPDIR
, and point it to an existing
directory on a writable disk, like this:
set TMPDIR=c:/windows
Without TMPDIR
set, DJGPP programs which create temporary files
will crash when run from a CD-ROM.
Out of environment space
then enlarge the size of the environment available to
COMMAND.COM
. On Windows, add a /E:2048
option to the
command line that the DOS box runs. On DOS, edit CONFIG.SYS
and
add the /E:2048
option on the line that defines the shell, like
this:
SHELL = C:\DOS\COMMAND.COM C:\DOS /E:2048
Your installation is now complete.
Here are several simple commands:
cprog.c
into cprog.exe
:
gcc -o cprog.exe cprog.c
cxxprog.cc
into cxxprog.exe
:
gpp -o cxxprog.exe cxxprog.cc
gcc -c cfile1.c cxxfile2.cc
*.o
object files into myprog.exe
:
gpp -o myprog.exe cfile1.o cxxfile2.o
To compile with optimizations, add the -O2
switch to the command
line. In addition, use of the -Wall
switch is highly
recommended: it turns on many useful diagnostic messages.
The DJGPP User Guide includes a tutorial introduction for first-time programmers.
Use the DJGPP News group or mailing list. For most questions, you will have your answer in a day or two. See the details on how to ask the gurus.
This depends on your hardware and software. Detailed instructions are in system configuration guidelines.
Check out list of required and optional packages.
See subscription instructions. However, it is better to read the comp.os.msdos.djgpp news group if you have access to Usenet News.
See the DJGPP archive search server in this FAQ. The search facility described there is set up by DJ Delorie, and you should use it whenever you have any questions or look for an information on a DJGPP-related subject.
Q: What is DJGPP?
A: DJGPP is a compiler and a set of tools that let you produce 32-bit protected-mode programs which run on MS-DOS/MS-Windows machines.
The originator and principal maintainer of DJGPP is DJ Delorie; that's where the "DJ" in "DJGPP" comes from. However, anybody is welcome and encouraged to contribute.
Programs compiled with DJGPP, and all development tools provided as part of DJGPP, look on the outside like normal DOS programs, and they rely on MS-DOS and BIOS for file I/O and other basic functions such as keyboard input, screen cursor position, etc. However, the bulk of the code in a DJGPP program is 32-bit protected-mode code; DJGPP programs use DPMI (the DOS Protected Mode Interface) to allow DOS/BIOS calls from protected mode. Therefore, any environment that can run DOS programs and provides DPMI services, will run DJGPP programs as well. Environments that are known to be compatible with DJGPP include MS-DOS, Caldera's DR-DOS, NWDOS, FreeDOS, Windows 3.X, 9X and NT, OS/2, and Linux DOSEmu. When DJGPP programs run on Windows 9X and Caldera's DR-DOS, they support long filenames.
It is important to understand that all these environments will treat DJGPP programs as DOS programs which call DPMI services. DJGPP cannot by itself create Win16 or Win32 applications; however, you can use the RSXNT package together with DJGPP to achieve this. See writing Windows applications with DJGPP.
Programs compiled with DJGPP can access all the physical memory on your machine and support virtual memory. All this memory presents a flat address space with no segmentation (you can say goodbye to far and huge pointers and to memory models), and is only limited by the amount of virtual memory supported by the DPMI server in use. A typical DPMI server can provide at least 64MB of virtual memory (if you have enough free disk space).
DJGPP is free: you don't have to pay anything to download and use it, even if you write commercial programs. DJGPP doesn't impose any restrictions on programs that you write and compile with it: you can make them commercial, shareware, freeware, or any other kind. (There are a few minor exceptions to that rule, see (un)restrictions on distribution of DJGPP apps.)
The core of DJGPP is the MS-DOS port of the GNU C/C++ compiler, GCC, and auxiliary utilities, such as assembler, linker, librarian, Make, and a hypertext docs browser. The DJGPP C library was written specifically for DJGPP, mainly by DJ Delorie himself, with help from a small group of volunteers. This core set of utilities and libraries is still actively developed and maintained.
DJGPP presents a set of tools which are remarkably ANSI- and
Posix-compliant2.
GCC complies to ANSI/ISO C Standard; the DJGPP C library is ANSI- and
Posix-compliant (however, a small number of Posix features, like the
fork
system call, are unimplemented); the C++ libraries also
comply to the latest standards; and the GNU development tools used by
DJGPP are all Posix-compliant. As a result, DJGPP tools provide a
complete and coherent Posix layer on top of Microsoft operating systems,
to the degree that even the infamous limitations of DOS and
incompatibilities between DOS/Windows and Unix are almost completely
concealed from users and developers.
Here are some of the tasks that DJGPP is said to be good for:
DJGPP is also used as back-end for programming languages other than
C/C++. ADA, Pascal and Fortran compilers have been ported to MS-DOS
based on DJGPP; GNU Pascal (gpc
) and GNU Fortran (g77
) are
available from the DJGPP archives. The latest GCC releases include
front ends for Java and Chill languages.
Starting from v2.0, DJGPP programs do not need a separate extender program, only a DPMI server to run; DJGPP includes a free 32-bit DPMI server which allows for a 32-bit, 4 GByte flat address space and up to 512 MBytes of virtual memory on plain DOS machines taht lack a DPMI server of their own.
This chapter describes what are the hardware and software which will allow you to use DJGPP. Minimum, "reasonable" and optimal system configurations are listed.
Q: What are the minimum system requirements for using DJGPP?
Q: Will DJGPP run on my brand-new Acme i986DX7/900 PC with a SCSI-III
10-Terabyte disk drive under MulticOS/42 v7.99 operating system?
A: DJGPP requires at least 386SX CPU and between 15 and 35 MB of free disk space (see more details on this below), including space for the software installation and some swap space. A minimum of 64K of free system memory is enough for DJGPP to run with CWSDPMI as your DPMI host (most other DPMI hosts will require much more), but at least 4MB of free extended RAM is recommended for reasonably fast compilation of large source files (8MB for compiling large C++ programs); you might see painfully slow compiles for large sources if you don't have at least that much. If your machine doesn't have a numeric co-processor, you will need to install an emulator to run floating-point code (DJGPP provides such an emulator) or link your applications with a special emulator library (also provided with DJGPP).
DJGPP requires MS-DOS version 3.1 or later; any other operating system is OK if it includes a DPMI server and supports some kind of "DOS box". Environments known to run DJGPP besides native DOS: Windows 3.1 & 3.11 DOS box, OS/2 (including Warp) DOS box, Windows 9X/DOS 7, Windows NT (on Intel CPUs), Novell NWDOS 7, FreeDOS and Caldera's DR-DOS (but several people have found the DPMI services of NWDOS and early versions of Caldera's DR-DOS to be incompatible with DJGPP, so they might need to be turned off and CWSDPMI used instead), and Linux DOSEmu environment.
Note that somebody reported that running Caldera's DR-DOS 7.03 with TaskManager enabled causes redirection of standard error stream to not work. In particular, RHIDE cannot display the compilation errors printed by the compiler. A work-around is to turn TaskManager off when compiling.
One user of Caldera's DR-DOS found that using their virtual disk drive,
VDISK.SYS
, caused strange crashes in DJGPP programs, unless the
memory manager is Caldera's EMM386
(as opposed to
HIMEM
).
Other known problems with latest versions of Caldera's DR-DOS are that Ctrl-<C> and Ctrl-<BREAK> crash the system or require cold reboot; and there are some problems with programs that use the linear frame buffer under VESA 2, and with certain games.
Q: You tell me it will work under OS/2, but I'm experiencing strange
crashes after several compilations ....
Q: DJGPP Make crashes when I run it on OS/2!
Q: When I press Ctrl-<C>, my program is aborted, even
though I've set up a handler for SIGINT
!
A: There was a bug in the DPMI server of the old OS/2 versions,
which was triggered by spawning child processes (like GCC does when it
invokes the various compiler passes). Current versions of OS/2 don't
have that bug, so DJGPP programs should run fine under OS/2. If you
can't make this happen, chances are that your setup is incorrect. One
system parameter that can cause problems with DJGPP (reportedly, Make
crashes if it isn't set correctly) is DPMI_DOS_API
. Setting it
to ENABLED
instead of the default AUTO
should solve the
problem. I'm also told that experimenting with the value of
DPMI_MEMORY_LIMIT
sometimes solves problems on OS/2. Reportedly,
version 4.0 of OS/2 solves problems with DPMI support, so the above is
only required for OS/2 v3.0 or earlier.
One particular problem with OS/2 v3.0 is that RHIDE 1.4 and later exits after the compilation ends. This doesn't happen under OS/2 v4.0, so you should upgrade if you have such problems.
Problems with SIGINT
are due to a known bug in OS/2 VDM: when you
press Ctrl-<C>, VDM catches it ahead of the keyboard interrupt
handler installed by the DJGPP startup code, and aborts the program.
Thus, you cannot prevent your programs from being aborted by installing
a SIGINT
handler.
Programs that use the library function delay
hang if the
<Pause> key is pressed while inside the call to delay
. It is
possible that versions of OS/2 4.0 fixpack 13 or later will correct
this. As a work-around, use usleep
or write a loop which calls
uclock
.
Q: What about Windows NT?
A: Current Windows NT versions support DPMI programs in the DOS box, so DJGPP programs should in general run fine under NT (but see the list of possible problems below).
The DPMI server built into NT (and Windows 9X) loses selectors with each
child program that is invoked by a DJGPP program, so after about two
thousand calls to functions from the spawnXX
family you can see
an error message like this:
Load error: no DPMI selectors
Some versions of NT lose DOS memory instead of selectors, so you might see another error message:
Load error: no DOS memory
These problems are likely to afflict only DJGPP ports of Unix shells
(such as bash
), since no other DJGPP program, not even
Make
, is likely to call so many child programs before it exits.
The only known work-around is to exit the shell every now and then,
because when all the available selectors are exhausted, the DOS box will
crash. I'm told that Make
sometimes fails on long
Makefiles
on Windows 9X, where the selectors are lost at even
higher rate than on NT. If you ever run a very long Makefile
and
see Make
crash, just run Make
again, and it will pick up
where the crashed session has left off.
The long filename API (the special functions of Int 21h which support file names longer than the DOS 8+3 limitation) for DOS box is not supported by current versions of Windows/NT, so you cannot have long filenames there from DJGPP programs. An alpha version of an LFN driver for NT which enables long file name support for DJGPP programs, written by Andrew Crabtree and significantly improved by Wojciech Galazka, can be downloaded the Web.
The popular DJGPP IDE RHIDE needs a -M
switch to work on NT
(to disable the mouse support which will otherwise crash RHIDE).
Version 1.4.7b or RHIDE reportedly solves this problem and allows
the mouse to be used on NT. Also, one user reported that he had to type
rhide twice to enter RHIDE, because the first invocation
immediately exits back to the command-line prompt with no message, if
you don't disable the mouse with -M
.
You might have problems with using the SVGA modes of your video card
under Windows/NT; only standard VGA modes (including mode-X) work. That
is because NT doesn't allow arbitrary direct access to the SVGA
registers, without which it is impossible to recognize the type of the
SVGA and employ its capabilities. For example, a user reported that GRX
functions and the MODETEST.EXE
program thought that only a
standard VGA was installed, whereas he had an S3 card. There is nothing
you can do about this feature of Windows/NT; that is the price you pay
for the stability and protection you get under this OS (a runaway
program that accesses hardware registers can wipe out your disk or wedge
the entire system cold). However, I'm told that Windows/NT 4.0 supports
DirectX which is a method of accessing screen, audio and other
peripherals directly, and the Win32 ports of Allegro and other graphics
packages can use it.
The Allegro library also has problems on NT. One user reports that even switching into the standard 640x480 video mode turns the screen black and kills the machine. Programs that use Allegro to switch into VESA modes usually don't work, since NT doesn't support SVGA graphics modes. In particular, the example programs provided with Allegro print an error message like this:
Error setting 24 bit graphics mode VESA not available
Programs that use the "nearptr" facility of DJGPP to access absolute
memory addresses (e.g., for memory-mapped devices) won't work on NT,
because its DPMI server silently ignores functions that set huge limits
on selectors. Since the default algorithm which allocates memory from
the DPMI server needs to set such huge limit in some rare cases, there's
a small probability that a program will fail or crash even if it doesn't
set selector limits in user code. It is best to use the Unix-style
sbrk
algorithm in programs that run on Windows/NT. See the
library docs for the variable _crt0_startup_flags
where the
_CRT0_FLAG_UNIX_SBRK
bit is explained, for more info on this
issue. If you cannot switch to the Unixy sbrk
(e.g., if you
don't have access to the program's sources), I'm told that sometimes
such problems can be worked around if you run DJGPP programs in a
full-screen session; your mileage may vary.
Another problem on NT is that you cannot install a handler for the
SIGFPE
, SIGINT
, or SIGALRM
signals: if you do, your
program will crash as soon as the signal is generated (in DJGPP
v2.02 and later, FP exceptions are masked by default, so you will need
to unmask them first, otherwise SIGFPE
won't be generated). This
is due to a bug in NT.
Windows/NT makes it impossible to use FP emulation on a machine that has
the FP hardware. If you set 387=n
on NT, the DJGPP startup code
calls the DPMI function to switch on the FP emulation, but NT ignores it
and continues to use the hardware FPU.
Yet another problem with NT is that interrupting some programs with Ctrl-<C> causes Dr. Watson to complain about "Access Violation" (that's NT'ese for GPF) and abort the program; careful inspection of Dr. Watson's logfile seems to indicate that the crash is inside NT's own code which handles the exception deliberately produced by the DJGPP's machinery that translates the Ctrl-<C> keypress into a signal. It seems NT uses the DJGPP stack for some of that processing, which is a no-no inside an exception handler. Sorry, no work-around.
The above-mentioned problems with signals are probably the cause for
another type of calamities on Windows/NT: running a program compiled
with the -pg
option causes it to crash almost immediately due
to--you guessed it--"Access Violation" in NTVDM (that's the NT DOS
Emulator).
Windows/NT comes with its own version of redir.exe
, which serves
a different purpose. If you invoke redir
, and the NT's
winnt\system32
directory is before DJGPP's bin
directory in your PATH
, you will see a message saying "The VDM
Redirector is already loaded". To solve this, rearrange your
PATH
or rename DJGPP's redir.exe
to somethink like
djredir.exe
.
Programs that use the library function delay
may hang if the
<Pause> key is pressed while the program is inside the call to
delay
. The work-around is to use usleep
or write a loop
which calls uclock
.
Another peculiarity of the NT DOS box is that beeping by printing the
\007
character to stdout
or stderr
behaves
strangely. Usually it beeps, but the beep is very long; sometimes, you
get the Windows "ding" sound. It is recommended that you turn on the
"visible bell" feature of the tools that support it, like Emacs and
Less.
Accessing the serial communication ports on NT also has some problems.
Anton Helm says that the first
two invocations of a program that accesses the port behave abnormally;
e.g., the data from the device on the other end of the link doesn't get
fed into your program. After that, the third and subsequent invocations
work correctly, but only if you use COMAND.COM as your shell.
Using the default cmd.exe
leaves the link in a state where you
get the replies from the other device for the n
th invocation of
your program in the n+1
st invocation.
In other words, to make the com port work on NT, you need to open the
DOS box with COMMAND.COM
, run your program and exit it two times,
then invoke the program for the third time and start working.
Some people report that NT servers cause much more problems than NT workstations of the same version and build. It seems that these problems usually mean that NT installation was done incorrectly (maybe it is much harder to get it right with a server than with a workstation?). If you have such problems, try to install a workstation, or re-install the server, and see if that helps. And if you gain some insight as to why servers like DJGPP less than workstations, please tell what you've learned.
The Cygnus Win32 project is another (unrelated to DJGPP) port of GCC and development tools to Windows/NT and Windows 9X platforms, which specifically targets development of Windows programs. See description of the Cygwin project, for more details about the Cygnus ports.
Q: You say it works on Linux, but I seem to be unable to run the
compiler from within Make....
Q: I can run DJGPP on Linux, but Make crashes with SIGFPE on even
the simplest Makefiles!
Q: When I run bash on Linux/DOSEmu, echoing of what I type is very
slow.
A: Versions of Linux which were released before 13 March 1996 need a patch to be able to reliably run nested DJGPP programs. That patch was posted to the DJGPP mailing list and can be found by using the search capabilities of the DJGPP mail archives.
If you prefer to download that patch via ftp, you can find it on the DJGPP ftp server.
In general, upgrading to DOSEmu version 0.97.10 or later is recommended, at least with versions of Linux kernel earlier than 2.1; in particular, some users report that DJGPP programs sometimes crash on version 0.66.7 under Linux 2.0.35.
You might also need to edit the RAM section of the
/etc/dosemu.conf
file to make it comfortable for DJGPP. I
suggest setting dpmi
and xms
to 16MB and ems
to
4MB. For example, I'm told that building the Allegro library with the
-O3
optimization switch fails in DOSEmu unless you allocate at
least 16MB of DPMI memory to DOSEmu sessions, and building GCC needs
18MB.
If DJGPP programs crash with an error message like this4:
DPMI: Unhandled Execption 0d - Terminating Client It is likely that dosemu is unstable now and should be rebooted
then you should add a line saying secure off
to your
/etc/dosemu.conf
file.
Some users reported that Make
, and possibly other programs which
use floating point computations, crash in DOSEmu environment on systems
without an FPU, even if you set the 387 and EMU387
environment variables correctly (as explained in Setting up the FP emulator, below). The
only known work-around is to not use floating point or to upgrade your
machine hardware. DJGPP v2.03 corrected a few subtle bugs in the
emulator code, so upgrading your DJGPP software might help. It is
possible that newer versions of Linux might solve this problem too, so
try upgrading your Linux software.
If your only problem is to run GNU Make, get the latest DJGPP port of Make, since ports of Make 3.75 or later can be configured to not issue FP instructions at all.
If you have problems running recursive Make's, or deeply nested DJGPP
programs, edit src/dosext/dpmi/dpmi.h
to enlarge the value of
DPMI_MAX_CLIENTS
(the default is 8) and then rebuild DOSEmu.
If DJGPP programs respond too slow to keyboard input, you might need to
tune the HogThreshold
parameter in the dosemu.conf
file.
Set it to zero and see if this helps; if so, further tune it until you
get reasonable response time, but still leave Linux with enough cycles
for the other programs that run.
Several users reported that DJGPP programs cannot get input from the
keyboard if Caldera's DR-DOS is booted under DOSEmu. I'm told that
adding rawkeyboard
to dosemu.conf
might solve this.
Some people complain that RHIDE crashes on DOSEmu whenever the
mouse is moved. I'm told that using the -M
switch when invoking
RHIDE solves this problem. Alternatively, you could try giving
DOSEmu access to the serial port to which the mouse is connected, and
then using your DOS mouse driver. To this end, add the following to
your dosemu.conf
5:
serial { mouse com 2 device /dev/mouse } mouse {mousesystems device /dev/mouse emulate3buttons }
and then load a DOS mouse driver in the DOSEmu AUTOEXEC.BAT
.
Note that the example above assumes that the mouse is connected to the
COM2 port; your mileage may vary.
If you have problems with mounting FAT32 partitions, upgrade the Linux kernel to version 2.0.34 or later.
I'm told that the problem with selectors being lost in nested DJGPP programs (see no DPMI selectors) exists in DOSEmu as well.
Q: Why can't I run DJGPP on my 286? It has protected mode
also....
A: True, but the protected mode isn't an issue here. Gcc doesn't care much about memory protection, but it does care to run on a 32-bit processor, which the 286 isn't. A 386 or better CPU really is required.
Q: Can I write MS-Windows applications with DJGPP?
A: Currently, you can only run DJGPP programs under Windows as DOS apps (i.e. inside the DOS Box). If you need to write true Windows apps, you will have to use auxiliary tools or another compiler. This section lists some of the possibilities.
RSXNTDJ is an add-on package for DJGPP which allows to develop Win32 programs using DJGPP development environment. It is essentially a cross-compiler targeted for Win32 (Windows 9X and NT) and Win32s (Windows 3.X + Win32s) platforms6; it supports DJGPP v2.x and includes debugging tools and an IDE. Beginning with version 1.60, RSXNTDJ is distributed under the terms of the GNU license (previous versions needed to be registered for a small fee if you wanted to develop commercial or shareware applications with it).
RSXNTDJ supports Win32 console, GUI, DLLs and bound programs (the latter can be run on DOS under the RSX extender, as well as on Windows). You can download RSXNTDJ via FTP. The latest version of the RSX IDE (called RSXIDE) is available from Rainer Schnitker's home page. Rainer's home page is also the place to look for the latest releases of RSXNTDJ, including beta releases.
RSXNTDJ version 1.31 was produced with GCC v2.7.2.1 and DJGPP v2.01. If you use it with later versions of GCC and DJGPP, it will need some tweaking; it is best to upgrade to RSXNTDJ v1.5 or later. Johan Venter wrote a HOWTO document that explains how to install and set up RSXNTDJ, and how to fix the various problems present in the current RSXNTDJ distribution. Make sure you read the RSXNTDJ HOWTO, before you try to install the package on your own; it will save you hours of banging your head against all kinds of weird problems, like missing files (due to truncated file names), linker error messages, inability to link C++ programs, etc.
In general, RSXNTDJ's support leaves a lot to be desired. Some problems take too long to fix, and response to user bug reports is slow. Even with the latest efforts by Johan Venter and others, you should expect to spend some time and effort installing and using the package. Another problem is that you depend on Microsoft SDK for the header files, and each new release of the SDK tends to break the patches to the header files required by RSXNTDJ.
These are disadvantages of RSXNTDJ with respect to other alternatives (see below); the most significant advantage is that you can use the entire DJGPP development environment. By contrast, other alternatives for free Win32 development usually provide less tools; in particular, Mingw32 provides only a few basic tools (like Make and GDB) beyond the compiler and Binutils. As a result, people who work with Mingw32 tend to use all kinds of different and subtly incompatible versions of shells, Make, etc. Cygwin has much more ports of GNU tools, but even Cygwin tool-chain doesn't have such a rich set of development tools all working together smoothly as DJGPP does. (Of course, if you don't mind developing with a minimal set of tools, this might not be a serious consideration for you.)
Here are some tips about RSXNTDJ:
patch
utility
fails.
There's an alternative to using patched MSSDK headers: Anders Norlander's WinAPI headers. These headers can be used as a drop-in replacement for the RSXNTDJ headers, and they include the functionality of Microsoft's headers. Beginning with version 1.5.1, the RSXNTDJ package includes a modified version of Anders Norlander's headers as part of the distribution.
djdevNNN.zip
package). Therefore, your best bet
would be to install RSXNTDJ in another directory, so that the headers
and libraries don't mix. Make sure that when you compile DJGPP
programs, the DJGPP include directories are searched before the
RSXNTDJ ones, and when you compile RSXNTDJ programs, the RSXNTDJ include
directories are searched first. Likewise, you should make sure the
compiler looks in the correct directories for libraries and the
crt0.o
startup module. When in doubt, add -v
to the
compiler's command line to see which directories it searches and in
which order, and what libraries does it link in.
One specific problems with conflicting headers is with the header
function.h
. Both DJGPP and RSXNTDJ have such a file, with
different contents.
stdio.h
header supplied with RSXNTDJ
defines several inline functions with the extern
qualifier, which
causes GCC to not compile them into the object file, and triggers
undefined references. The solution is to define the extern
symbol to an empty string in one of the source files which includes the
stdio.h
header.
cpp.exe
) is kept to
the PATH
environment variable, or copy the pre-processor into a
directory already on your PATH
. (Without this, the resource
compiler will not work.)
pestack
utility, allegedly due to insufficient size of the
default stack.
ld.exe
which comes with RSXNTDJ doesn't
print any message if you forget to link in libraries such as
libcomct.a
and libcomdl.a
. Instead, the produced
executables will die with SIGSEGV when run. Sometimes, forgetting to
#include
windows.h
also produces a program that crashes at
run time. You can use the stock DJGPP version of ld.exe
to see
the list of the missing functions, and then find out which libraries to
add to the link command line (use the nm
utility to find out
which libraries contain the required external symbols). The linker
supplied with RSXNTDJ is only required to link DLLs.
If RSXNTDJ doesn't suit your needs, you can use a few other free compilers which target Win32 platforms:
Cygnus GNU-Win32 tools
Mingw32 (Minimal GNU-Win32)
CRTDLL.DLL
, which is standard on Windows 9X and
Windows/NT) and doesn't require any additional DLLs like Cygnus ports
do; however, you lose the Posix layer. The basic package includes,
besides the compiler and Binutils, a few tools to compile resource files
and convert DLLs into lib*.a
wrappers. Since it doesn't use any
GPL'ed stuff except GCC and its subprograms, the programs produced by
Mingw32 are free.
A disadvantage of this package is a relative lack of development tools ported to Mingw32. The compiler, Binutils, Make, GDB, Textutils and Patch are available from the Mingw32 site, but ports of other utilities tend to be scattered around and not integrated together into a coherent package like what DJGPP or Cygwin present. This causes compatibility problems between the tools. It is possible to use the DJGPP tools where there are no equivalent Mingw32 ones, but you need to be aware of some incompatibilities, such as different methods of passing long command lines, lack of support for long file names on NT, etc.
More details, including ready binaries of ported utilities and
source-level patches to build other utilities with Mingw32, are
available on Mingw32 home page. For ports of
additional developemnt tools, visit Jan-Jaap van der Heijden's site.
Mingw32 has a mailing list.
Lcc-Win32 compiler and tools
Dev-C++ package
If you need on-line documentation of the Win32 API, you can find it as a Windows HLP file. Additional links to tutorials and other related information can be found on the bowman's home page.
The recommended book for learning Win32 programming seems to be Charles Petzold's Programming Windows: The Definitive Guide to the Win32 API, published by Microsoft Press.
Q: What is the optimal system configuration for running DJGPP?
A: Here is the description of your dream machine (at least for the next 6 months :-):
TMPDIR
environment variable points to
it (e.g., set TMPDIR=e:, if E: is the RAM drive letter);
Q: OK, I don't have this much money. What is the reasonable
configuration?
A: If you have the following machine, you should be able to stop worrying about memory and compilation performance:
This will leave you with about 19 MBytes of free extended RAM. Note
that the RAM disk must be at least 4 MBytes to hold the output of the
preprocessor for some exceedingly large source files (notably, some GCC
source files). If you don't have that much RAM to spare and still want
to compile very large source files, either reduce the disk cache
so you can give more to RAM disk, or point TMPDIR
to your hard
disk and make the disk cache larger, if you can.
Q: How do I configure my system to get optimal performance under
DJGPP?
A: That depends on the amount of RAM you have installed in your machine. Below are some guidelines to help you.
SETVER.EXE
, HIMEM.SYS
etc.) from your CONFIG.SYS
and AUTOEXEC.BAT.
TMPDIR
environment variable to a directory on your hard disk. Put a
sufficiently large BUFFERS=
statement into your CONFIG.SYS
(I recommend setting BUFFERS=40,8
) to make DOS file operations
faster8.
CWSPARAM
program
(from the csdpmi4b.zip
archive) and set the
"Minimum application memory desired before 640K paging" parameter to
512K or larger. Depending on how much memory you actually have, you
might need to further fine-tune this parameter. This parameter defines
the lowest amount of extended memory CWSDPMI will use; if your system
doesn't have that much free extended RAM, CWSDPMI will use conventional
memory instead, where usually there should be around 600K of free RAM.
With this configuration, GCC will run out of free physical RAM and start paging when compiling almost any C program and all C++ programs. If you are serious about DJGPP development, you need to buy more RAM urgently.
SETVER.EXE
, HIMEM.SYS
) from your CONFIG.SYS
and
AUTOEXEC.BAT.
TMPDIR
environment
variable to a directory on your hard disk.
With this configuration, GCC will still run out of free physical RAM and start paging when compiling large C programs and most C++ programs. Plan to buy more RAM as soon as you can.
EMM386
or QEMM386
. Try using
the FRAME=NONE
parameter of the memory manager. This will
disable Expanded Memory (EMS) services as far as most programs are
concerned; if you must use DJGPP together with any program which needs
EMS, try to configure that program to use Extended Memory (XMS) instead.
DEVICEHIGH=
command (instead of DEVICE=
in
CONFIG.SYS
, and by using the LOADHIGH
command in
AUTOEXEC.BAT
.
TMPDIR
environment
variable to a directory on your hard disk.
AUTOEXEC.BAT
file that installs an 8-MByte cache for hard disks
C:
, D:
, and F:
:
loadhigh c:\dos\smartdrv.exe c+ d+ f+ 8192
(The +
character after the drive letter enables the delayed-write
(a.k.a. write-back) feature for that drive.) Note that you do
not need, and should not install a disk cache if you intend to
use DJGPP programs from Windows 9X, because Windows includes its own
built-in disk cache (called VCACHE
) that is loaded together with
the operating system.
TMPDIR
environment variable to it.
If your RAM disk is less than 4 MBytes, GCC might run out of space there
for very large source files (e.g., cccp.c file from the GCC source
distribution), but this shouldn't happen unless the size of the source
file you are compiling approaches 1 MByte. Note that software is
available that lets you install a RAM disk even on Windows 9X.
(However, I'm told that Microsoft's own RAMDRIVE.SYS
only
supports long file name on the RAM disk if its size is less than 9MB.)
Some people disable the delayed-write feature for safety reasons, to
avoid losing files due to system crashes. If you are worried about this,
you can usually gain performance without sacrificing safety by enabling
delayed-write together with an option that causes the cache to flush the
write-behind data before the system returns to the DOS prompt. For a
SmartDrv
disk cache, this is achieved by specifying /N/F
switches instead of /X
.
Using a memory manager, such as EMM386
or QEMM
, is not
required (DJGPP will run without it), but highly recommended, since it
has several advantages:
If your memory manager is EMM386
, I recommend to put the
NOEMS NOVCPI
parameters on its command line. This will allow you
to use UMBs and up to 128MB of physical memory (if you have that much
installed). Without these parameters, many versions of EMM386
limit your physical memory to 32MB.
It is generally not recommended to install DJGPP on a networked drive,
since this makes it slower, particularly when linking programs. If you
do install DJGPP on a networked drive, you should consult your network
administrator to configure the network for maximum performance. For
Novell networks, a good place to look for advice is the Novell FAQ
(search for a file called nov-faq.htm
).
Q: How do I set my system so that DJGPP programs could use all of my
256MB of installed physical RAM?
Q: I have 128MB of memory installed, but go32-v2
only reports
32MB, how can I get more?
Q: You say that CWSDPMI supports up to 512MB of total virtual
memory, but I cannot get more than 128MB!
A: You can have as much as 256MB of physical memory in DJGPP programs, provided that you have at least that much installed, and that you observe the following guidelines:
HIMEM
: most of
the others will limit the amount of accessible memory to 64MB
(EMM386
usually limits it to 32MB unless you turn off the VCPI
support using the NOVCPI NOEMS
parameters on the EMM386
command line). Using HIMEM
from MS-DOS 7 does allow access to
more than 64MB. Note that you must install HIMEM
, since
without it, BIOS is the only way to find out how much memory is
installed, and the standard system BIOS only supports up to 64MB of
memory.
QEMM
(version 8.0 or later), you must include
the USERAM=
parameter (e.g., USERAM=128M
for 128MB) on
its command line in your CONFIG.SYS
and specify the exact amount
of memory installed on your machine, otherwise QEMM
won't support
more than 64MB.
Another possibility is to run your program from the Windows 9X DOS box,
after changing the EMM386
line in your CONFIG.SYS
like
this:
DEVICE=C:\WINDOWS\EMM386.EXE NOEMS L=131072
I'm told that this line (here for 128MB of installed memory) together with an "Auto" setting of the DPMI memory for the DOS box allows DJGPP programs to use up to 117MB of memory when running from the DOS box under Windows 9X.
If you need to use more than 256MB of physical memory, upgrade to CWSDPMI r5 or later. Another possibility is to run under OS/2 which features a built-in DPMI 1.0 support which can be configured to support as much as 512MB of DPMI memory (the user who reported this didn't know how much of this can be physical RAM).
This chapter explains where and how can you get DJGPP, and recommends which parts of the archive you should download.
Q: Where can I get DJGPP?
A: Look on any SimTel.NET mirror in the pub/simtelnet/gnu/djgpp/ subdirectory, world-wide.
Q: How do I download files from these sites?
A: FTP to the nearest site, log in as anonymous, give your
full e-mail address as password, and chdir to the djgpp
subdirectory (the exact path to it might be different on different
mirrors, check out the DJGPP archive path, for your nearest mirror). Then issue the binary
command and download the files you need (see the list of required files) with the get
or
mget
commands.
FTP
is?Q: What is that FTP
thing? I only use Netscape
and
IE4
for Internet access.
A: The SimTel.NET site is on the Web.
You can also convert any of the mirrors' addresses listed in
the list of SimTel.NET mirrors,
above to a valid URL by prepending ftp://
to it. For example,
the URL for FTP from SimTel.NET is
<ftp.simtel.net/pub/simtelnet/gnu/djgpp/
>. Typing such a URL into your
Web browser will cause it to display the directory contents, and you can
then click on individual files to download them.
For those of you who only have an e-mail connection to the Internet,
there is an ftp-mail server at
<mailto:ftpmail@pub1.bryant.vix.com
>. Send a message with a single
word "help" in the body to the above address, to get instructions.
Walnut Creek, the company which maintains the SimTel.NET collection where the DJGPP archives are held, also sells a DJGPP Development System CDROM. It includes everything from the DJGPP sites on SimTel.NET (even the old version 1.12 of DJGPP), some example source code packages to get you started, and a ready-to-run feature, which allows you to use DJGPP directly from the CDROM; you can also use a provided install program to copy some or all of the packages to your hard disk. To order the CDROM, go to the Walnut Creek Web site.
The FSF, the organization behind the GNU project, sells a CD-ROM with a full DJGPP development environment and most of the DJGPP ports of GNU software. For details, see the FSF Web site. Salvador Eduardo Tropea (SET), himself a veteran DJGPP user and developer, sells a low-cost CDROM with all the DJGPP v2 files, plus a lot of related stuff downloaded from the net. For information, send email to set-soft@usa.net.
Q: What's the minimum set of .zip
files I need to download?
A: This depends on what you are planning to use DJGPP for.
The following table lists required and recommended files by category.
An alternative method of choosing the files suitable for your needs is
to use the DJGPP zip-picker feature which will guide you through the
process.
v2/readme.1st
v2/faq230b.zip
v2/frfaq21b.zip
v2misc/csdpmi4b.zip
v2misc/pmode11b.zip
v2/djtzn203.zip
unzip
or Tar. Most people will only need a
single file from this distribution. See zoneinfo files, for a detailed explanation
of these files.
v2gnu/gcc2952b.zip
v2gnu/bnu281b.zip
as
, the GNU assembler; ld
, the
GNU linker; and their docs. GCC calls these utilities during
compilation.
v2/djdev203.zip
v2gnu/txi40b.zip
info
, you cannot
read the C library reference and the docs included with the ported GNU
software packages. This package also includes the install-info
utility, which helps to install Info docs of optional utilities that you
download. Several files required to format Texinfo docs for printing
are also included.
v2gnu/gpp2952b.zip
v2gnu/lgp2952b.zip
v2gnu/objc2952b.zip
http://www.sente.ch/cetus/oo_objective_c.html
>.
v2gnu/g772952b.zip
v2gnu/gdb418b.zip
djdev
distribution includes two simpler, assembly-level debuggers,
edebug
and fsdb.
The latter presents a user interface
similar to that of Turbo Debugger.)
v2gnu/mak3781b.zip
Makefile
.) You should install Make 3.75 or later if
you use DJGPP v2.01 (previous ports of Make have subtle
incompatibilities with v2.01 tools). The DJGPP port of Make supports
Unix-style shells (as well as DOS COMMAND.COM
and its
4DOS
/NDOS
replacements) and can be used to run Unix
Makefiles if you install a Unix-style shell (e.g., bash
) and
auxiliary utilities. It also supports long filenames on Windows 9X and
MS-DOS pathnames with drive letters.
v2apps/rhide14b.zip
gettext
library). Latest
developments and beta versions of RHIDE are available from RHIDE home page. Binaries
of an improved beta version is available from Andris Pavenis's home page; this version uses TVision
v1.0.9, SETEdit v0.4.39, and its debugging engine is based on
GDB 4.18 and DJGPP debug support from a pretest version of v2.03.
v2/djlsr203.zip
djtzs203.zip
archive.
v2gnu/bsh203b.zip
Bourne-Again SHell
), the GNU shell, and its docs. If you
mostly work in Unix environment, you will feel right at home using
bash
as your interactive shell. It is also great as a batch
shell for running Unix-born shell scripts and Makefiles when these are
too complex to convert them to MSDOS. If you install bash
, you
should also install auxiliary utilities (Fileutils, Textutils, Sh-utils,
Grep, Diffutils, Findutils, Sed and Gawk) as these are usually invoked
from many shell scripts and Makefiles.
v2gnu/bsn125b.zip
v2gnu/acnf213b.zip
v2gnu/dif272b.zip
diff
program from this package. diff
is also required by
almost all configuration-management packages, such as RCS and
CVS.
v2gnu/emacs.README
v2gnu/em2005*.zip
emacs.README
before you begin downloading the rest.
v2gnu/fil316b.zip
ls
, rm
, cp
, mv
, and
others. Highlights of the latest port: ls
supports colorization
of files (like on Linux), ln -s
knows about DJGPP-style
"symlinks" (see symlink feature of DJGPP, elsewhere in this document), install
-s
will strip executables on the fly, and all the utilities support
long filenames on Windows 9X and numbered backups (even on plain DOS).
This package is a must if you want to run Unix shell scripts, as they
use some of these utilities a lot.
v2gnu/find41b.zip
find
, xargs
, and locate
.
These programs are used to process a group of files which share some
common attributes, like the file name pattern, read/write permissions,
file time-stamps, etc. Since DOS has its own, incompatible program
called find.exe
, you will need either to make sure DJGPP's
bin
subdirectory is before the C:\DOS
directory (for
DOS and Windows 3.X) and C:\WINDOWS\COMMAND
directory
(for Windows 9X) on your PATH
, or to rename the DOS find
program to some other name. If you don't, you might see the following
message when you try to run find
:
FIND: Parameter format not correct
v2gnu/flx254b.zip
v2gnu/gwk304b.zip
v2gnu/grep24b.zip
v2gnu/idu32db.zip
Grep
, but much faster, and
their notion of a token is sensitive to the source language of the
scanned file, so they are more appropriate e.g. for searching variable
names in C source files.
v2gnu/pat254b.zip
diff
.
v2gnu/perl552b.zip
texi2html
11, use Perl. In particular, the GNU Automake package is
implemented as a Perl script.
v2gnu/sed302b.zip
v2gnu/shl112b.zip
bash
or want to run
Unix Makefiles, but some utilities (such as env
or test
)
can also be very useful on their own right.
v2gnu/txt20b.zip
sort
,
wc
, cat
, join
, paste
, od
, and
uniq
. Unix shell scripts and Makefiles call some of these
a lot, so you should install this package if you run them.
v2tk/grx23.zip
addons/print
directory in the GRX distribution).
A significant drawback of GRX is that its docs is very outdated
and incomplete.
Hartmut Schirmer is the current
maintainer of GRX. GRX is distributed under the GNU Library License
(a.k.a. LGPL). Latest versions of GRX, including fixes to known
problems and plans for future developments can be found on the GRX home page.
v2tk/bcc2grx.zip
v2tk/allegro/alleg312.zip
By popular demand, Allegro now has its mailing list. To post a message
to the list, send email to allegro@canvaslink.com. To
subscribe to the Allegro list, send a message to
listserv@canvaslink.com with the text subscribe allegro
{your full name}
. Another related resource is the Allegro home page.
v2tk/pdc22.zip
Version 2.3 of PDCurses was released. It is available via FTP.
Note that all of the packages have source distributions
(*s.zip
) which you can download in case you discover a bug, or
want to know more about how the tools work. The companion *d.zip
files hold the documentation for the package converted into HTML, DVI
and PostScript formats.
For description of additional files not mentioned here, get the file
00_index.txt
; it contains a full list of the distribution files and a
short description of every file.
Q: Wow, that's a lot of files. How much disk storage will I need?
A: The following lists the approximate disk space required for several major configurations, and additional storage required for some optional packages:
Execution-only environment | 300 KBytes
|
Developing C programs | 15 MBytes
|
Developing C++ programs | 20 MBytes
|
Developing Objective-C programs | 16 MBytes
|
Additional storage for RHIDE | 4 MBytes
|
Additional storage for DJGPP sources | 6 MBytes
|
Additional storage for GDB | 1.1 MBytes
|
Additional storage for Emacs | 30 MBytes
|
Additional storage for Flex | 280 KBytes
|
Additional storage for Bison | 310 KBytes
|
Additional storage for Diffutils | 560 KBytes
|
Additional storage for Make | 650 KBytes
|
Additional storage for Patch | 180 KBytes
|
Additional storage for Sed | 200 KBytes
|
Additional storage for Graphics libraries | 4 MBytes
|
Note that the above lists only approximate numbers. In particular, the disk cluster size can significantly change the actual disk space required by some of the distributions (those with a large number of files). The numbers above are for disks which have 8KB or smaller clusters.
In addition to the space for installing the software, you will need some
free disk space for the swap file. You should leave enough free disk
space to make the total virtual memory at least 20 MBytes; that will be
enough for most applications. Invoke the go32-v2.exe
program
without arguments to see how much DPMI memory and swap space DJGPP
applications can use. Depending on your DPMI host, you might need to
review its virtual memory settings in addition to leaving free disk
space; CWSDPMI only requires that enough free disk space be available,
but other DPMI hosts have special settings to specify how much virtual
memory they let their clients use, as explained in how to set up memory, below.
Q: The above table means that I need more about 20 MBytes for
C/C++ development environment; that's about 7 1.44MB diskettes to
hold even the compressed archive!! Seems to me DJGPP is afflicted by
the fatware disease....
Q: Pulling that many megabytes through the net from my overloaded
SimTel.NET mirror is almost impossible. Can't you prepare a ZIP archive
which only includes stuff I can't do without?
A: There are a number of shareware/freeware programs floating around which allow formatting DOS diskettes to almost twice their usual capacity, so you can use less floppies. One such program is 2M.
Also, with the proliferation of drives that can burn CD-ROMs and the availability of high-capacity removable media, like Zip drives, using floppies is no longer the only solution for a movable DJGPP.
To make downloading DJGPP easier, download and compile the
BatchFTP
program. It allows you to submit a script of FTP
commands and will repeatedly try to login into the FTP site you specify
until the script is successfully completed. It is smart enough to
understand the messages which the FTP server sends to you (like
login refused
etc.) and also is nice to the remote server by
sleeping for some time between login attempts. BatchFTP
is free
software and can be found on many FTP sites.
BatchFTP
is a Unix program; those who access the net from their
PC (not by dialing into some Unix host with a shell account), can use
one of the available programs for automated FTP access.
As for the minimal DJGPP installation, volunteers are welcome to
prepare such an archive and make it publicly available, in the same
spirit as EZ-GCC
did for DJGPP v1.x.
Q: How can I uninstall a certain package?
Q: How can I install a newer version of some package without leaving
traces of the older installation?
A: The *.mft
files in the manifest
subdirectory hold
the lists of all the files included in every package you install. For
example, when you unzip gcc2951b.zip
, it puts a file called
gcc2951b.mft
into the manifest
subdirectory. The easiest
way to remove all those files is to use the *.mft
files as
response files to a command which deletes files. For example:
rm -f @manifest/gcc2951b.mft
The rm
program is part of the GNU Fileutils package, available as
v2gnu/fil316b.zip
from the usual DJGPP FTP sites.
Some packages might not have the *.mft
files. In general, you
should complain about such cases; however, if a package installs
entirely into its own directory tree, you can uninstall it by simply
removing that tree:
rm -rf package-dir
(The -r
option tells rm
to recursively remove all
subdirectories of the named directory package-dir).
When you install a new version of a package, it is best to uninstall the previous version first, like in the above example, and then install the new one. Otherwise, you might leave behind old files that the new version doesn't overwrite, and that will cause problems due to incompatibilities with the new version.
This chapter explains where to find and how to read DJGPP documentation, and how to solve occasional problems with the docs system.
Q: I don't see any documentation files....
A: The documentation files are in the info/
subdirectory of
your main DJGPP installation directory. You will need a program to read
these docs, which are hypertext structured files. You have several
choices:
Info
reader.
Texinfo
includes INFO.EXE
and its docs. Unzip it and type info
<Enter>. It will bring up a (hopefully) self-explanatory online
help system. Confused? Press <?> to see the list of all Info
commands. Still confused? Press <h> to have Info
take you
on a guided tour through its commands and features.
Info
command of your favorite editor.
If you use Emacs, you already know about Info
. (What's that? You
don't? Type C-h <i> and you will get the top-level menu of all
the Info topics.) RHIDE also has an integrated Info reader, which
is the core of its help system.
InfView
browser. This is a replacement for the
stand-alone Info reader from the GNU Texinfo distribution, but with a
better user interface. It is written and maintained by Salvador Eduardo Tropea (SET), and is the same Info
viewer that is part of RHIDE and SETEdit.
Info?
Q: I'm too old/lazy/busy to learn yet another browser, and I despise
uGNUsable programs like Emacs. How in the world can I read the DJGPP
docs??
A: Info files are almost plain ASCII files, so you should be able to view them with your favorite text file browser or editor. You will lose the hypertext structure and you might have a hard time finding the next chapter (hint: look up the name of the Next node at the beginning of this node, then use the search commands of the browser, or the Grep program, to find that name), but other than that, you should be able to read all the text.
You can also produce pure ASCII files yourself, if you have their Texinfo
sources. These are usually called *.txi
or *.texi
and
should be included with the source distribution of every package. (You
can use the DJGPP server's downloading services, to download individual files.) To
produce an ASCII file foo.txt
from the Texinfo file
foo.txi
, invoke the Makeinfo
program like this:
makeinfo --no-split --no-headers --output=foo.txt foo.txi
Makeinfo
is one of the programs which come with the GNU Texinfo distribution.
If you prefer reading the docs through the Web, point your Web browser to the docs page of the DJGPP Web site.
The full documentation of the DJGPP C library in HTML format is available for downloading from the DJGPP server.
Q: I like my docs the old way: printed on paper, right near my
workplace. How can I print the documentation files which come with
DJGPP?
A: Most of the DJGPP packages already have their docs converted to a
printable format, look for the files named *d.zip
at the same
place where you got the binary *b.zip
distribution. For example,
the ready-to-print docs of GCC 2.95.1 should be in the
v2gnu/gcc2951d.zip
archive. These archives include a .dvi
and a .ps
file. The latter can be printed directly on a
PostScript printer. If you don't have access to such a printer, you can
use the .dvi
file in conjunction with a DVI driver for your
printer to produce a printed copy of the docs. A DVI driver is a
program that reads the .dvi
file and translates it into commands
for a particular printer device which cause it to print the document.
DJGPP ports of DVI drivers for LaserJet series of printers are available
on SimTel.NET mirrors in the v2apps/tex directory.
Drivers for DeskJet series are also available from there, in the
dvdjNNb.zip
archive. For other devices, download and install the
Ghostscript interpreter which supports a lot of popular printers.
You can also get the GNU documentation in DVI, PostScript, and two-up
PostScript formats in .tar.gz
format from the DJGPP server.
Note that some documentation files (notably, the one for GCC and Emacs) will produce voluminous print-outs. You have been warned!
If you cannot find a ready archive with printable files anywhere, you
will need to get and install a program called TeX or its work-alike,
like emTeX. A DJGPP port of TeX is available via FTP. Install
TeX, then run the texi2dvi
shell script12 on the docs' source files (called *.txi
or
*.texi
) which you get with the source distribution of every
package you download. TeX produces a .dvi
file which you can
then print using one of the available DVI drivers, as explained above.
To convert a .dvi
file into PostScript, use the DVIPS
program; you can find it as dvps584.zip on the
above-mentioned site, together with the TeX port.
If TeX won't run, check that you have the file texinfo.tex
which defines the TeX macros specific to Texinfo files. If you
don't, get the latest GNU or DJGPP Texinfo distribution which includes
that file.
If you'd like to produce printed docs of the library reference, TeX
might complain that it cannot find a file named libc2.tex
. This
file is generated from all the *.txh
files in the DJGPP source
distribution (djlsr203.zip
) and is usually
built as part of the library build procedure. In order to generate this
file without building the entire library, you need to install
djlsr203.zip
and the C++ compiler, then go
to the src/libc
directory and type this from the DOS command
prompt:
make misc.exe ../hostbin make -C mkdoc make -C libc info
DJGPP comes with a program called TEXI2PS
which can convert
*.txi
files into a crude PostScript; try it if you don't care
much about the appearance of the printed docs. Its advantage is that
you don't need to install any additional packages, just to fetch the
Texinfo sources of the docs.
Finally, if you don't mind paying for the printed documentation, the Free Software Foundation sells printed copies of manuals for GNU packages. You can contact the FSF for details.
For those who prefer reading docs with a Web browser, many GNU manuals
in HTML
(hypertext) format, suitable for reading with your Web
browser, can be viewed at the DJGPP Web site. The *d.zip
archives also include the
docs converted to HTML
, which you can browse locally on your
machine.
Q: I looked in my info/
subdirectory, but I can't find docs
for some of the utilities, like SED or GPROF.
Q: STL, the C++ Standard Template Library, seems to be
undocumented....
A: SED and GPROF are documented in the latest GNU
releases, v2gnu/sed302b.zip
and
v2gnu/bnu281b.zip
. Download the latest releases,
and you will get the missing docs.
The STL documentation is not included in the GNU GCC distribution (it appears that nobody has bothered to write a free documentation for it). But you can find the STL docs on the net; this includes the full documentation and a tutorial. Many books that describe C++ programming also include documentation of large parts of the STL. Another place to look for the reference material about C++ language and classes is the Dinkumware site. (Dinkumware is a company founded by P.J. Plauger, one of the world's leading experts on C++ and the STL, which produces the C++ library used by the MSVC compiler.)
The ANSI organization is selling the official ANSI C++ Standard (full document, about 800 pages) for $18 in PDF format.
If you have some other package without any docs, try downloading the
source archive (*s.zip
) for that package and look inside it,
usually in the directory called man/
or doc/
. Omitting
documentation from the binary (*b.zip
) distribution is generally
considered a bug, so if you find the docs in source distribution only,
please report these cases on the comp.os.msdos.djgpp news group, so that next
binary release could fix this.
foo.1
files?Q: Some docs files are called foo.1
or bar.man
or
baz.nroff
, and they seem to be written in some weird format which
is very difficult to read. How can I convert them to readable text files?
A: That weird format is the troff
format which is used for
writing Unix manual pages. The Unix command man
converts
them to formatted text files which are usually displayed with a program
like more
or less
(and here less
is considered to
be more than more
:-)). The formatted file includes bold and
underlined letters produced by over-typing using Backspace characters.
DJGPP binary *b.zip
distributions include such man pages already
formatted and ready to be browsed. To browse formatted man pages, you
will need to install a clone for the Unix man
command. One such
clone is available from the DJGPP sites.
man
knows how to find a manual page file, and will format it if
it isn't formatted already, but to browse these files you will need a
program that can page through a text file and which understands how to
show bold and underlined letters instead of backspace-over-typed
characters. I suggest to download the DJGPP port of GNU Less,
which uses colors to show bold and underlined letters.
Having installed man
and Less, you should be able to view
*.1
files like e.g. patch.1
with several alternative
tools:
man
command itself: simply typing man patch from
the DOS prompt will cause man
to look for the man page
patch.1
and pipe it to Less.
man
as its back-end and display the manual page found by
man
.
Using Info to display man pages has an advantage of displaying the Info version of documentation if it is available, and the man page version if it's not. So, by using Info, you don't need to bother to remember which version of the docs is available for every topic. Info also knows about special sections in man pages, such as "SEE ALSO", which refer to other man pages, and treats them as hypertext links (i.e., you can press <TAB> to move between the references and press <Enter> to display the man page under cursor).
patch
man page
highlighted with colors. (You will need to install SED and Gawk,
since Emacs invokes them when processing the man page.) Emacs also
supports the special sections like "SEE ALSO".
Note that all of these alternatives require man
and Less
to be installed.
The binary distribution of the DJGPP port of bash
includes a
simple SED script called man2txt.sh
which will convert
formatted man pages into plain text; you can then read them with any
text browser or editor. To convert, invoke Sed like so:
sed -f man2txt.sh < file.1 > file.txt
If you want to be able to browse unformatted man pages, get and
install the DJGPP port of Groff.
man
will automatically call Groff if it finds an unformatted
page, so all the ways mentioned above to browse man pages will work with
unformatted pages as well, once you install Groff.
Note that, for GNU packages, the man pages aren't always updated on a regular basis. If you need more up-to-date information, see the Info docs.
Q: OK, I've got the docs and have read them, but I still can't figure
out some details.
A: Some ported packages include DJGPP-specific or MSDOS-specific
README
files (named README.dj
, README.dos
or some
such), which explain DOS-specific issues; you should read them before
any serious use of the package, or in case of any problems. If this
doesn't help, download the sources and look there, or ask on the
net--either the DJGPP News group or appropriate GNU News groups.
Make
, or Info
, or ...) Crashes...This chapter explains how to deal with certain problems which may prevent DJGPP programs from running on your machine. The first 13 items on the next menu describe specific problems; if yours isn't solved with these techniques, read the description of the general debugging procedure.
Q: When I try to compile anything, GCC just hangs!
Q: Some programs, like Info and Less, hang on my Windows 9X machine
after some time, requiring to close the DOS box. Help!!
Q: Bash hangs on Windows 9X after the first DJGPP program I run from
it!
A: If you are using GCC 2.8.1, and if only GCC hangs, then chances
are that you have set the DJGPP
environment variable incorrectly,
or didn't set it at all, or messed up your DJGPP.ENV
file by
editing it. Refer to what to do if GCC hangs, later in this FAQ, for
details about possible problems with setting DJGPP
. When
DJGPP
is not set, or points to a non-existent directory, the
first release of GCC 2.8.1 would enter an endless loop (the NTDVM
process on Windows/NT will go nuts allocating memory, as a result of
this).
The latest uploads of the GCC binary (v2gnu/gccNNNb.zip
) were
modified to prevent them from hanging. They abort with an error message
instead of hanging. You should upgrade to the latest binaries of GCC,
and also set your DJGPP
variable correctly.
Some people fail to read the release notes which come with GCC, and do
not install it incorrectly. If you installed GCC recently and it began
to hang, now is the time to read those instructions again (they are
installed as gnu/gcc-X.YZ/problems.txt
).
If interactive programs like Bash, Less, Info, Emacs, and RHIDE are
those which hang, and if it only happens on Windows 9X after running
another DJGPP program from within those programs, then your Windows 9X
installation is afflicted by a subtle bug whereby programs which call
function 1680h of the Interrupt 2Fh (to release the rest of their time
slice when they are idle) hang after they spawn another DJGPP program.
A modified version of the library function __dpmi_yield
, which
works around that bug in Windows, is available in DJGPP v2.02 and later,
and latest uploads of the binaries for the affected programs should be
free from this problem. If you cannot find a pre-compiled binary that
works, get the sources and rebuild the program with the latest DJGPP
release.
Q: I'm trying to run gcc
, but all I get is a message saying
"Load error: no DPMI - Get csdpmi*.zip". What am I doing wrong?
A: You don't have a DPMI server installed, and DJGPP v2 requires it
to run. You can either use one of the commercial DPMI servers (e.g.,
run GCC in a DOS box from Windows) or download and install CWSDPMI
(v2misc/csdpmi4b.zip
from SimTel.NET
mirrors) which is a free DPMI server written for DJGPP.
If you already have CWSDPMI installed, and these messages still appear,
it might be because of a messed up PATH
setting. The DJGPP
startup code looks for cwsdpmi.exe
along the PATH
, and,
being optimized for size, it might not be robust enough to cope with
all possible cases of weirdness in the value of PATH
. Try to
copy cwsdpmi.exe
into the same directory as the program you are
invoking, and if that helps, change your PATH
as appropriate.
If you see the message "Load error: no DPMI - Get csdpmi*.zip" on
Windows/NT, it most probably means that you have disabled the DPMI
services built into NT. One way that this might happen is if you edit
the autoexec.nt
file and remove the line which loads
dosx.exe
, or change some of the parameters on that line. You
cannot use CWSDPMI on NT, so your only bet is to restore NT's built-in
DPMI services.
Q: I cannot run v2 applications: they all hang or reboot my system,
while v1.x apps run OK. Is this what v2 is all about--getting me out
of the DJGPP community?
A: No, believe it or not, we don't want to oust you. Your problems
might be caused by a buggy DPMI (see DOS Protected Mode Interface) host installed on
your machine. One DPMI host which is particularly known to be a source
of trouble is the one which comes with Novell NWDOS (and also with early
versions of Caldera's DR-DOS, a.k.a. OpenDOS, which is a derivative of
NWDOS). Please see if DJGPP programs run when you disable DPMI services
of your usual configuration (DJGPP programs will then use the CWSDPMI
host supplied with DJGPP). To turn off the DPMI host built into Novell
NWDOS and Caldera's DR-DOS, either remove the DPMI=TRUE
parameter
from the EMM386 command line, or type DPMI OFF
from the DOS
command prompt.
Version 7.03 and later of Caldera's DR-DOS reportedly don't have this bug in their DPMI server, so upgrade to a latest DR-DOS version if you can.
Another DPMI host which is known to cause problems in DJGPP is
Quarterdeck's QDPMI which comes with QEMM 7.5. It was reported to cause
Info
and all DJGPP debuggers to crash. If you use QDPMI, upgrade
to the version 7.53 or later (patches for that version are available
from the Quarterdeck's ftp site), or disable QDPMI and use CWSDPMI.
Q: When I compile my program, the compiler crashes, but the problem
seems to go away if I compile without optimization.
Q: The compiler prints "Virtual memory exhausted" and dies while
compiling some long functions with some optimization options, such as
-funroll-loops or -fforce-addr.
A: For some programs, this can be caused by an insufficient stack.
Some source files make cc1.exe
or cc1plus.exe
need
preposterously large amounts of stack space, but only when you turn on
optimizations. (One user reported that an innocent-looking C source
file required 700KB of stack before cc1.exe
was able to compile
it with optimizations!) Try stubediting the compiler to enlarge its
stack, as described elsewhere in this FAQ, how to enlarge the stack, before
you try any other remedies in this section.
If GCC reports that it has exhausted virtual memory, you should first
see if your DPMI memory plus the swap space is large enough (run
go32-v2
with no arguments to display the available memory) and
make more memory available, if the reported amount is too small. Some
programs really need large amounts of memory to compile and/or link.
For example, linking cc1.exe
is known to consume more than 12MB
of memory. On Windows 9X, be sure to set the amount of DPMI memory
available to the DOS box to the maximum value of 65535K (64MB) in the
DOS box property sheets, under Memory
, as explained in
how to enlarge memory in the DOS box.
Some users have reported that GCC seems to run out of virtual memory if TMPDIR environment variable points to a RAM disk which doesn't have enough free space. Changing TMPDIR to point to a hard disk would reportedly save the day in those cases.
Compiling with PGCC or EGCS variants of the GNU compiler, as well as GCC versions 2.95 and later (which are descendants of EGCS) can also sometimes run out of virtual memory. These versions of the compilers are memory hogs, especially when compiling C++ programs, and at high optimization levels. One particular case is when your program makes use of many STL classes. Try lowering the optimization level, or compile without optimizations.
With GCC 2.95 and later, using -pedantic
or
-Wreturn-type
can cause an explosion in the amount of memory
needed to compile template-heavy C++ code, such as code that uses
the STL. Since -Wall
includes -Wreturn-type
, it can
also cause massive memory consumption; try -Wall
-Wno-return-type
to work around the problem.
One user reported that optimization switches force GCC to use a math
co-processor, which can cause it to crash on a machine that lacks a
numeric processor. Be sure you didn't delete the emu387.dxe
file
from your bin
subdirectory, when you compile on such machines,
and that your emulation-related setup is right. See how to set up FP emulation, for details.
GCC can sometimes crash when optimizing, especially when compiling
C++ programs, in particular if your code has some syntactic or
semantic bug. (This is usually a genuine GCC bug, not something special
to DJGPP.) Upgrade to the latest version of GCC. If that doesn't help,
then narrow the offending code fragment using the #if 0 ...
#endif
paradigm. If this fragment includes an error, correct it and
try again; if it is syntactically and semantically correct, then rewrite
it as equivalent, but syntactically different one.
A GCC switch can sometimes help you zero in on the code fragment that
causes GCC to crash. If you add -Q
to the GCC command line, it
will print the name of every function it compiles. The function that
makes it crash is probably the one whose name is the last one printed,
or the one after that.
As an extreme measure, don't optimize at all, if that's the only way to make your program compile.
Another reason for crashes could be some problem with your system hardware or the BIOS (like if you set an incorrect number of wait states when accessing memory, or overclock the CPU too much). To check, try running the same compilation on another machine, or review your BIOS settings and hardware configuration.
Yet another cause for such crashes can be connected with excess memory usage that GCC needs when compiling certain programs, which makes some DPMI hosts fail. For details about this, see CWSDPMI allocation problems, below.
as
"?Q: When I try compiling a program, GCC aborts saying "Installation
problem, cannot exec `as': No such file or directory (ENOENT)". What
does that mean?
Q: When I try compiling a program, GCC aborts saying "Installation
problem, cannot exec `cpp': No such file". Huh?
A: This usually means that GCC couldn't find some program it needs
to run to compile your source. Check the COMPILER_PATH
environment variable or what the COMPILER_PATH
line in the
DJGPP.ENV
file says, and make sure they point to the directory
where DJGPP programs reside. Also check that the named directory has
all the required programs: cpp.exe
, cc1.exe
,
cc1plus.exe
, cxxfilt.exe
, gasp.exe
, as.exe
,
ld.exe
, and (for Objective-C) cc1obj.exe.
A typical case
is when people fail to install the Binutils package and
GCC cannot find as.exe
(the assembler) and ld.exe
(the
linker). You can use the -v
switch to GCC to see what programs
it invokes and which one of them causes the fatal error.
Beginning with version 2.8.0 of GCC, the place where the pre-processor,
cpp.exe
, and the C and C++ compilers, cc1.exe
and
cc1plus.exe
, are installed, has changed: they are no more in the
same directory as gcc.exe
itself. If you are using GCC version
2.8.0 or later, and the compiler cannot find cpp.exe
or
cc1plus.exe
, read the installation instructions carefully: the
file problems.txt
explains how to change the settings on
DJGPP.ENV
so that GCC will find the pre-processor. Also, be sure
to remove all traces of the previous compiler installation, since mixing
different compiler versions can be another cause for such problems.
See uninstalling a package.
Q: During compilation, GCC prints "Fatal: Error in DJGPP
installation. Environment variable DJGPP is not defined" and then
aborts....
Q: GCC aborts with "Internal compiler error" when compiling a
large C++ program.
Q: GCC behaves erratically when compiling programs, sometimes
crashes with register dump, sometimes compiles okay, sometimes reports
"Internal compiler error". Why is this happening?
Q: When I try to compile any program, GCC prints "Abort!" and
doesn't compile anything....
Q: The compiler crashes or dies with "Virtual memory exhausted"
when I compile my simple program!
A: The fatal error message about DJGPP
not being defined
means just that--that your DJGPP
environment variable is not
defined. The other two messages you could see are:
Environment variable DJGPP point to file `XXYYZZ' which doesn't exist
or
Environment variable DJGPP points to wrong or corrupt file `ABCDE'
(In both cases, you will see an actual file name instead of
XXYYZZ
and ABCDE
.) These messages mean that
DJGPP
points to a non-existing directory, or to a file whose
contents are too messed up. Beginning with version 2.8.1, GCC refuses
to work when the DJGPP
variable doesn't point to the actual path
name of a valid DJGPP.ENV
file, because GCC uses the value of the
DJGPP
variable to find out where to look for its subprograms like
cpp.exe
, cc1.exe
, etc. To solve this, set the
DJGPP
variable as the installation instructions (in the file
readme.1st
) describe. Also, make sure you didn't mess up the
beginning of the DJGPP.ENV
file, where the value of the
DJDIR
variable is computed (when in doubt, compare it with the
stock DJGPP.ENV
from the djdevNNN.zip
distribution).
A possible cause for the "Abort!" message is that the TMPDIR
environment variable points to a non-writable directory. If you don't
set TMPDIR
from your AUTOEXEC.BAT
or from the DOS prompt,
the DJGPP startup code sets it to the tmp
subdirectory of the
main DJGPP installation directory. If DJGPP is installed on a read-only
drive, like CD-ROM or an unwritable networked drive, this default will
not work. To solve this, set TMPDIR
to point to a writable
temporary directory. If TMPDIR
is not set at all, GCC tries to
use TEMP
and TMP
, in that order, so make sure these also
point to a valid directory.
Internal compiler errors (a.k.a. bugs) can also cause GCC to print "Abort!". This FAQ describes a procedure that allows you to find the spot in the sources where the compiler aborts, see use of the -Q switch, above. Once you find the offending code, you could rewrite it and/or submit a bug report to the GCC maintainers.
When GCC aborts with a message such as "Internal compiler error" or "Exiting due to signal SIGSEGV", it might mean a genuine bug in GCC (which should be reported to FSF), but it can also happen when GCC requests additional chunk of memory, and the DPMI server fails to allocate it because it exhausts available memory for its internal tables. Old releases of CWSDPMI could fail like this if an application asks for a large number of small memory chunks. Beginning with release 2, CWSDPMI defines a larger (6KB) default heap that is configurable by CWSPARAM program to be anywhere between 3K and 40K bytes, without recompiling CWSDPMI. You should upgrade to the latest CWSDPMI if you experience such problems, and if that doesn't help, bump up the size of CWSDPMI heap using CWSPARAM.
Some innocent-looking programs are known to cause GCC to gobble preposterous amounts of memory, which could cause it to crash or abort after printing "Virtual memory exhausted". One particular case of such programs is when you initialize very large arrays. For example, to compile a source which initializes a char array of 300,000 elements requires more than 60MB(!) of memory. You should avoid such constructs in your programs.
Some programs require very large amounts of stack to compile. DJGPP
programs have a fixed-size stack that is by default 256KB (512KB in
DJGPP v2.02 and later). If the compiler, cc1.exe
or
cc1plus.exe
, doesn't have enough stack to compile a program, it
will overflow its stack and crash, or hang, or die with "Internal
compiler error". You can enlarge the stack size of any DJGPP program
by running the stubedit
program, like this:
stubedit cc1.exe minstack=1024k
I recommend to enlarge the maximum stack size of cc1.exe
to at
least 1024K bytes and that of cc1plus.exe
to at least 1.5MB.
Some people report that they needed to enlarge both the heap of CWSDPMI
and the stack of the C++ compiler to make such problems go away.
For a program that you wrote, another work-around for the cases where a
program crashes due to failure of CWSDPMI to allocate more RAM is to use
an alternative algorithm for sbrk
, by putting the following
somewhere in your program:
#include <crt0.h> int _crt0_startup_flags = _CRT0_FLAG_UNIX_SBRK;
Note that the Unix algorithm for sbrk
might cause trouble in
programs that install hardware interrupts handlers.
Note that the problems with insufficient stack size have nothing to do
with the total available memory as reported by go32-v2
. A
compiler can crash because of insufficient stack size even though it has
gobs of memory available to it. When in doubt, always enlarge the
compiler stack size.
Sometimes, GCC can crash due to problems with your system hardware. In particular, bad memory chips can cause GCC to behave erratically, since the compiler is a memory-intensive program: it moves large buffers around alot, and uses lots of memory. One cause of problems with accessing memory is incorrect setting of the wait states in your BIOS setup, or too aggressive CPU cache mode that your motherboard cannot support reliably, or overclocking the CPU. Try to play with your BIOS setup and see if that helps. If you overclocked the CPU, try resetting it back to its normal speed.
One user reported that he had random crashes and seemingly-missing files due to a disk without proper cooling. So if your system sometimes cannot find files that you know are there, check whether your disk gets proper cooling and generally works okay.
Another rare case of crashes in GCC was reported on Windows 3.X. It
seems to be related to the small probability of getting non-contiguous
memory blocks from the Windows' DPMI server. In general, the DJGPP
library handles these cases, so it is possible that the problem is
actually somewhere in GCC (more accurately, in cc1
, the C
compiler). A tell-tale sign of this problem is that the CS and
DS limit value printed in the crash message is very close to the
end of the 4GB address space, like fffeffff
. The only known cure
for these cases is to patch or rebuild GCC with the Unix sbrk
algorithm, see above.
Q: I get error messages saying "Unknown filetype" from GCC.
Q: Since a few days ago, whenever I try to run most of the DJGPP
programs, they print a message "C:\DJGPP\BIN\prog.exe: not
COFF" and just terminate. Help!!!
A: It might be that your DJGPP programs and/or STUBIFY.EXE
are infected by a virus. (This is not a joke! It did happen to
a few of us and can happen even to you.) As the DOS stub prepended to
the DJGPP programs is very short, many viruses cannot attach themselves
to it without overwriting the beginning of the DJGPP COFF image which
specifies vital information such as location and length of various
program sections, therefore triggering this error from the code in the
stub that loads the COFF image.
Another possible cause of the "Unknown filetype" message is that you
mix a v2.0 gcc.exe
driver with cc1plus.exe
, cc1.exe
or other programs from an old v1.x distribution.
Q: My compiles run OK from the command line, but hang or crash when
I invoke the compiler from Make.
Q: When I try to compile something, I get a message "16-bit DPMI
not supported".
A: Be sure you aren't inadvertently invoking some 16-bit programs,
such as Borland's Make or cpp.exe
from Borland C. GCC cannot run
them, and cannot run under Borland's Make (because Borland's programs
are 16-bit DPMI clients, and the DPMI 0.9 spec doesn't allow mixing them
with 32-bit DPMI clients such as the DJGPP programs). It might be that
another program called make.exe
or cpp.exe
is found
earlier on your PATH
than the Make and the preprocessor which
came with DJGPP. Moving DJGPP's bin
directory to the beginning
of your PATH
will usually solve these problems.
Note that if you try to mix 16-bit and 32-bit DPMI clients in Windows
DOS box (like use Borland's Make to run GCC, or invoking Borland's
cpp.exe
under GCC), the DOS box will usually crash. So this
problem is not specific to CWSDPMI.
If you must use a non-DJGPP compiler or another utility together with DJGPP programs, one solution would be to find a version of that utility that doesn't use the 16-bit DPMI services. For example, many DOS compilers have a real-mode version that won't conflict with DJGPP.
If you use Make compiled under DJGPP v1.x, you will also experience all
kinds of trouble when invoking programs compiled under DJGPP v2. That's
because v1.x programs cannot spawn v2 programs directly (the v1.x
program sees that the child is a DJGPP program and tries to call
go32
to run it, but v1's go32
cannot run v2 programs).
The result usually will be that the child either crashes or silently
exits. If that's your problem, be sure to upgrade your Make
to
the port distributed with v2. (Note that v2.x programs generally know
how to spawn both v1.x and v2.x programs.) You can use go32-v2
to work around this limitation (see description of go32-v2, below), but I suggest doing
that only if you absolutely cannot upgrade to v2's Make
.
Some users report that v1.x programs might sometimes hang or reboot the
machine when invoked from v2.x Make. The reason for this is a known bug
in the versions of go32-v2.exe
program distributed with DJGPP
v2.0 and 2.01: it would not restore the keyboard handler after the COFF
image it runs exits. To work around that bug, don't touch the keyboard
throughout the entire run of Make; to solve it, upgrade.
Q: When I run the Info browser, it tells me it cannot find the node
"Top".
Q: Sometimes, when I mistype the name of the Info topic,
info.exe
seems to hang....
A: Check your installation of info files. The file DJGPP.ENV
in the root of your DJGPP installation mentions the variable
INFOPATH
which should point to the directory where Info looks for
its files. It must find there a file named DIR
, the file you are
trying to read, and other files with .iNN
or .NN
extension,
where NN
is a number. Also, make sure you didn't edit the
beginning of DJGPP.ENV
, where the value of %DJDIR%
is
computed; if you did, try the original DJGPP.ENV
.
Also, the DJGPP
environment variable should be set to point to
the full pathname of the file DJGPP.ENV
. See problems with DJGPP variable setting, for a description of some frequent problems
with setting the DJGPP
variable.
Assuming the above checks OK, and all the necessary info files are
indeed installed in those directories (did you remember to give that
-d
switch to PKUNZIP
when unzipping all DJGPP packages?),
it might be that you have an old version of info.exe
. Upgrading
to version 3.12 or later should solve several problems which cause Info
to complain about the "Top" node, or at least make its error messages
more self-explaining.
Some people unzip the txi40b.zip
file with
WinZip and fail to disable its feature whereby by default each zip file
is unzipped into a separate directory. This disrupts the DJGPP
directory structure and break almost every DJGPP package, including
Info. You need to unzip all DJGPP files under the same
directory!
Another possibility is that the file DIR
or the Info file that
you want to browse is corrupted. For example, it might be a compressed
file, but without the tell-tale .gz
or similar extension that
tells info.exe
to decompress it. You could examine the offending
file(s) with a text editor, and re-install them as needed.
If you invoke Info with a name of a topic that is non-existent or not
installed on your system, and you don't have a man
command or its
clone installed, versions of Info before 3.12 would wait for 15 seconds
before they print an error message and exit. If you think Info is
wedged, wait for 15 seconds and see what happens then. The DJGPP port
of Texinfo 3.12 removes this misfeature, so upgrade if you can.
Q: Whenever I invoke info.exe
, it immediately crashes! How
can I use DJGPP without reading all those wonderful docs??
A: One possible reason for this is a bug in EMM386
shipped
with some versions of DOS 6.x. This bug is only triggered if your
system loads the DISPLAY.SYS
driver, usually to display non-US
characters supported by your national codepage. In addition, this bug
causes Info to crash only if it is run in 35- or 40-line display mode;
any other display size avoids the problem. (The default display mode is
40 lines, as set in the [info]
section of DJGPP.ENV
.) So
either switching to another display size, or removing either
EMM386
or DISPLAY.SYS
from CONFIG.SYS
, should
prevent Info from crashing.
If you use DJGPP v2.0, yet another cause of crashes in Info
might
be trailing whitespace in the DJGPP.ENV
file. The tell-tale signs
of this failure are a stack dump that is bogus or doesn't start with
your `main' function, or a series of SIGSEGV that won't stop. Actually,
this is a bug in the DJGPP v2.0 startup code, so any v2.0 program could
crash in this way, but since the last section of stock v2.0
DJGPP.ENV
belongs to Info
, it is the one which suffers
most from this bug. Make sure your DJGPP.ENV
doesn't have a
^Z character at the end (some DOS editors put it if you edit the
file), and doesn't end with a blank line. Alternatively, upgrade to
DJGPP v2.01 or later, where that bug is fixed.
Q: Bash crashes on me when I invoke shell scripts....
Q: Why does Bash say "pipe error: Access denied" when I try to run
two programs via a pipe?
Q: When I run certain complex shell scripts, Bash sometimes prints a
message saying "Cannot make a child for command substitution:
(null)". What gives??
Q: What does it mean when Bash says "Can't make pipes for command
substitution!"?
A: If Bash crashes when you invoke shell scripts, check whether
those scripts have #!/bin/sh
on their first line. If they do,
the most probable reason for the crashes is that you don't have
sh.exe
anywhere on your PATH
(it does not have to
be in /bin
!). Either copy bash.exe
into sh.exe
, or
create a "symlink" to bash.exe
, like this:
ln -s c:/djgpp/bin/bash.exe c:/djgpp/bin/sh.exe
(replace c:/djgpp
with the actual pathname of your DJGPP
installation).
Error messages about pipes and command substitution usually mean that
your TMPDIR
environment variable points to an invalid drive; make
sure TMPDIR
is set and points to an existing directory, and that
the drive where it points is writable and not full. Old ports of Bash
had problems with `command`
substitution, so make sure you have
the latest binaries.
Q: I cannot run any DJGPP program on my ThinkPad! They all cause
the Windows DOS box to crash with a message about a General Protection
Exception at some cryptic addres like 0277:0044!
A: This is a known problem with the ThinkPad: it is supplied with a
set of PCMCIA device drivers from CardWorks that conflict with many
DOS-extended programs, not only DJGPP (the CardWorks README
file
mentions problems with PKZIP
which also uses protected-mode
instructions).
The only known solution is to uninstall the PCMCIA drivers. Obvioulsy, this is a solution only if you don't actually use any of the PCMCIA devices.
Q: Why is it that every time I link a program, the CD-ROM drive is
accessed?
Q: Whenever I link programs, GCC invokes something called `collect2'
which accesses my LAN when it runs. Why?
A: CD-ROMs or other drives being accessed during linking is due to a
bug in Binutils 2.7 and in an early release of Binutils 2.8.1: the
linker would always try to look for its script djgpp.djl
in a
certain directory on the D:
or E:
drive (the former in
Binutils 2.7, the latter in 2.8.1), no matter which disk uses that
letter (these accesses usually go unnoticed with hard disks, but are
visible with CD-ROMs, Zip drives, or other slower devices). Download
and install the latest bnuNNNb.zip
archive you can find on
SimTel.NET mirrors, and the problem should go away.
If collect2
seems to be accessing the network, it is due to a
bug in the early ports of GCC 2.95: if a root directory of some drive
appeared in your PATH
setting, collect2
would try to
access a file whose name has two slashes, like C:\/foo
. This
causes Windows 9X to treat this as a UNC (a.k.a. network share) name,
and search the network for such a server which exports this share. The
ports of GCC 2.95.1 and later don't have this bug.
You can see which directories on what drives does the linker try to
access by passing the --verbose
option to the linker. Here's an
example:
gcc -o hello.exe hello.o -Xlinker --verbose > linker.log
This redirects the linker log to a file which you can then examine. Since the list of directories accessed by the linker doesn't depend on the program being linked, you can try this with any trivial program.
Sometimes, accesses to other drives come from some over-zealous anti-virus software. If you have one of these installed, check out its options: perhaps there are some superflous drive letters there.
Q: I've installed DJGPP just like explained in the README.*
files, but when I run gcc, my machine crashes/hangs/needs cold boot.
Q: I get errors I can't figure out when I try to compile something.
A: Add the -v
switch to the GCC command line and run it again.
It will print all the subprograms (compiler passes) it is running. Then
you can see which subprogram caused the error, or where your machine
crashes. This might give you a hint on what's wrong.
One cause of such problems might be that your system is set up
inefficiently. If GCC doesn't get enough free RAM, it will run very
slowly, and you might think it crashed when in fact it didn't. (This
kind of problem usually happens on memory-starved machines.) Invoking
go32-v2
with no arguments will report the amount of memory
available to DJGPP programs; large programs might require up to 20MBytes
to compile, sometimes more. If you run from the DOS box on Windows 9X,
set its DPMI memory property to 65535KB (64MB) and try again. Check out
the system configuration advice, earlier in this FAQ list, for other suggestions about
your system configuration.
Sometimes, if the TMPDIR
environment variable points to a full
disk, GCC may hang or crash as well. Make sure you have enough free
disk space where TMPDIR
points.
A similar case is when DJGPP programs are run off a CD-ROM: in this
case, the default setting of TMPDIR
points to the CD drive, which
is unwritable. You need to point TMPDIR
to a writable directory.
Q: I want to read all the error messages that GCC throws at me, but
there are so many that I can't keep up. How can I redirect them to a
file?
Q: When I add -v
to the GCC command line, how can I put all the
voluminous output into a file, so I don't miss anything when reporting a
problem?
Q: I have this nifty graphics program which bombs from time to time,
but the registers and traceback info are hidden by the graphics display.
How can I see it?
A: Error messages are usually written to stderr
, and stock
COMMAND.COM
cannot redirect it. There are several alternatives
to do that:
COMMAND.COM
, such as 4DOS
or bash
, which knows how to redirect standard error stream to a
file. 4DOS is shareware and can be found on SimTel.NET, while bash
is available from the v2gnu
directory of DJGPP archives
on SimTel.NET.
SCRIPT
, which is similar to its Unix namesake. It
has an advantage of saving everything which goes to screen and
showing it on the screen at the same time. You can find SCRIPT on SimTel.NET.
REDIR
program which comes with DJGPP. It also
redirects standard output and/or standard error to a file, but you don't
get a chance to look at the output while the program runs. Here's how
to run GCC with REDIR
:
redir -o gcc.log -eo gcc -v ...
(put the rest of the GCC command line instead of the dots). The
messages printed by GCC will be written to the file gcc.log
.
Windows/NT has its own program named redir.exe
, so make sure the
DJGPP's bin
subdirectory is listed in the PATH
variable
before the NT directories.
Q: OK, I have all this voluminous output of gcc -v
, but I
still have no clue.
A: Your problem might be one which has already been posted and solved on the DJGPP News group. DJ Delorie has set up a searchable News group archive on his Web server. You can search the entire mailing list archives in just a few seconds. DJ's archives are always up to date, as they receive and store all posted messages automatically, but the index is updated every 24 hours, so the last day might not be searchable yet. To search the DJGPP archives, point your Web browser to the URL above and specify a list of keywords pertinent to your problem. You will get a list of messages which include those keywords; clicking on any of the messages will get the full text of that message.
Q: I've read this monstrously-large FAQ, searched the news group
archives, but didn't find anything helpful. I am totally lost.
Help!!!
Q: I don't have time to download all these messages, not to mention
looking through them. Can't you DJGPP gurus help me?
Please??
A: DJGPP News group is famous for its outstanding user support. To get a fast and effective solution to your problem, you will have to supply the relevant info about your system, and describe exactly how things went wrong for you. To gather this info, do the following:
go32-v2
program (it's in your bin/
subdirectory) and save its output.
ENVIRON.TXT
, the output of go32-v2
, the contents of your
AUTOEXEC.BAT
and CONFIG.SYS
, and what GCC printed during
compilation with the -v
switch (if your problem is that GCC won't
work).
symify
on the
stack dump, and post the output of symify
:
symify -o dumpfile yourprog
(See detailed description of symify, for more details.
Be warned that you might get several dozen messages in reply to your request; this is not meant to overflow your mailbox or sabotage your relationship with your system manager, it's just the usual friendly response of fellow DJGPP'ers to your lonely cry for help. Some of the replies might suggest what you already checked and reported in your original message, or even miss the point altogether. Be ready for this and don't flame us for trying to help you as much as we can.
This chapter deals with speed of compilation and linking under DJGPP, and how they could be improved.
If you already know whether the compiler or the linker is the slow
part, go to the appropriate section; if not, add -v
to your GCC
command line and run it again. With the -v
switch, GCC will
print all the programs it invokes, and you will be able to tell which
one is taking most of the time.
Q: Why GCC is compiling sooo slooowww?
A: That depends on what you mean by "slow". The following table gives "normal" gcc compilation speed, in source lines per second, on a 166-MHz Pentium:
Source language | Without optimizations | With -O2
|
C++ | 800 | 400
|
C | 1800 | 1000
|
Note that the numbers for compilation with -O2
are about 30%
slower for GCC 2.95 and later versions than for previous versions. This
is because GCC now does much more optimizations under -O2
than
previous versions did.
As another data point, compiling the Allegro library takes about 3 minutes on a P500 and about 50 minutes on a 486/DX2-66.
On machines faster or slower than P166, scale these numbers
accordingly. For example, 486/DX2-66 is about 4 times slower than
P166. When comparing to this table, don't forget to count header files
your program #include
s in the total line count. And
don't check compilation speed on very short programs (like the
classic Hello, world!
), because the overhead of loading the
multiple passes of the compiler will completely hide the compiler
performance. It is also useful to run the compilation twice in
succession, especially if you have a disk cache installed, to prevent
the overhead of the first load from skewing the results.
If your results are close to these (deviations of a few percent are considered "close" here), then that's as fast as you can get with GCC. If they are significantly slower, you may indeed have a problem; read on.
First, check to see if GCC pages to disk when it compiles. This is
manifested by a heavy disk traffic which won't go away even if you have
a large write-back disk cache installed. To be sure, disable the
virtual memory services for your DPMI host (for CWSDPMI
, load it
before your program with the -s-
switch, or use the
CWSPARAM
program to point the swap file to a non-existent drive),
or use CWSDPR0
or PMODE/DJ
as the DPMI host, and then
run the compilation again; if the compiler aborts with an error message
saying there isn't enough memory, then it was paging in your
original environment.
If paging does happen, you need to free more extended memory. If you have a RAM disk, make it smaller, or don't use it at all (it only makes compiles run about 20% faster), or make your disk cache smaller (but don't discard the disk cache altogether); if you have other programs which use extended RAM, make them use less of it. Failing all of the above, buy more RAM (see the description of reasonable configuration). Also see recommendations for optimal software configuration.
If GCC doesn't page, check the settings of your disk cache. If you don't
use a cache, install one--this can slash your compilation times by as much
as 40%, more so when compiling a large number of small files. If you
already have a cache, enable its delayed-write (a.k.a. write-back, a.k.a.
staggered-write) operation. Some people disable the delayed-write feature
for safety reasons, to avoid losing files due to system crashes. If you
are worried about this, you can usually gain performance without
sacrificing safety by enabling delayed-write together with an option
that causes the cache to flush the write-behind data before the system
returns to the DOS prompt. (For SmartDrv
disk cache, this is
achieved by specifying /N/F
switches instead of /X
.) GCC
usually gains a lot when you set up your cache in such a way, because
each compiler pass (pre-processor, compiler, assembler) must write
temporary files that are used by the following passes.
It is also worthwhile to check the settings of your system BIOS. In particular, the following items should be checked against your motherboard vendor recommendations:
Internal and external CPU cache | set to Enable
|
CPU cache scheme | set to Write-back, if possible
|
DRAM and SRAM wait states | vendor-recommended optimal values
|
Incorrect or suboptimal settings of the above items can explain as much as 30% performance degradation on 486 machines, and as much as 500% (!) if you have a Pentium CPU.
Q: The compiler finishes in a few seconds, but then the linker
grinds away for more than a minute, even on a very short
program....
A: Try linking the trivial Hello, world!
program; it
should take no more than 7-10 seconds on a 486, 3-5 seconds on a
Pentium. If you see much slower linking on your system, then the
following advice might help you.
If you use a disk cache, make sure you enable its write-back (a.k.a.
delayed-write) operation. Some people disable the delayed-write feature
for safety reasons, to avoid losing files due to system crashes. If you
are worried about this, you can usually gain performance without
sacrificing safety by enabling delayed-write together with an option
that causes the cache to flush the write-behind data before the system
returns to the DOS prompt. For SmartDrv
disk cache, this is
achieved by specifying /N/F
switches instead of /X
.
For very large (several MBytes) executables which are built from a large number of small source files, the link (as opposed to the compilation) stage might be the one which needs more RAM than you have free, and thus be the bottleneck of the time it takes to build your program. Check that the size of the executable isn't larger than the amount of your free RAM. If it is, then it might make sense to use a smaller (or even no) disk cache, and allow the linker as much physical RAM as it needs. Make sure that the linker wasn't stub-edited to make its transfer buffer too small.
The first release of GCC 2.95 ported to DJGPP had a bug in the
collect2
program (used during the link stage) whereby if a
root directory of any drive appeared in the PATH
environment
variable, collect2
would try to look for files with names like
C:\/foo
, which caused Windows 9X to search the network
(because two slashes in a row would look like a network share name).
This would create an illusion of a very slow link, when in fact
collect2
simply waited for the network operation to time out.
Another reason for slow linking might be that the DJGPP.ENV
file
by default sets TMPDIR
to a tmp/
subdirectory of the main
DJGPP installation directory; if DJGPP is installed on a networked
drive, this means all your temporary files go back and forth through the
network (and networked disks are usually not cached on your PC). In
such cases, setting TMPDIR
to a directory on your local drive, or
to a RAM disk, would probably make linking faster.
DJGPP can be slow if it is installed on a networked drive. Generally, this is not recommended. If you have to, you should configure your network interface for best performance. Consult your network administrator.
Being of a Unix origin, GCC has a somewhat different flavor of command-line syntax and its peculiar compilation and link algorithms. It also has a plethora of optional switches, some of them obscure or semi-documented. These are known to confuse users, especially those who had previous experience with DOS-based C compilers.
This chapter explains how to solve some of those problems which tend to appear when compiling and linking your programs.
Q: I created a simple source file hello.c
, but when I invoke
the compiler, it says: "gcc.exe: hello.c: No such file or directory",
and then exits with the message "No input files." But hello.c
is there, so why won't the compiler find it??
A: One popular reason for this problem is that you use one of those
Windows editors that think they know better how do you want them to name
the files. For example, Notepad
always attaches the
.txt
extension to the file name you provide, so when you type
hello.c
into the dialog box, Notepad
actually creates
hello.c.txt
. In addition, the files listed by My
Computer
by default have their extensions not shown, which creates an
illusion that hello.c
really is there.
Use the DIR
command in the DOS Box to see what files are in
the directory where you run GCC. (If you have the GNU Fileutils
installed, you can use ls
as well.) This will always show the
full names of the files, exactly like GCC sees them.
You are generally advised to stay away of such "helpful" editors.
Notepad
is not suited well for editing programs, anyway. If
you must use it, a work-around is to type the file name in quotes:
"hello.c"
; then Notepad
will leave it alone and not
append the .txt
extension.
Another reason for GCC to not be able to find the source file is because you use long file names on Windows/NT. Suppose you invoke GCC like this:
gcc -c file_name.c
The name file_name.c
exceeds the DOS 8+3 limits, so if you have
such a file, you probably created it with some Windows editor. However,
DJGPP programs cannot access long file names on Windows/NT, so gcc
doesn't find such a file and complains.
Type dir /x from the command line to see the short 8+3 alias name
of your file (in the example above, it should be file_n~1.c
or
some such), and use that short name when you invoke GCC. In general, if
you want to avoid such problems on Windows/NT, you should restrict
yourself to file names that are valid DOS 8+3 names.
Q: Why does GCC complain that it cannot open -lstdcx
?
Q: When I run the compiler it says it couldn't find header files
and/or libraries. But the headers and libraries are all there, so why
won't it find them?
Q: When I link my programs, ld.exe complains that it cannot open
crt0.o, although that file exists in the lib subdirectory....
Q: I tried to compile a program, but GCC complained about missing
header files netdb.h
and socket.h
. Can somebody please
mail me those headers?
Q: Why does GCC complain that it "cannot open -lgcc: File format
not recognized"?
A: An error message about missing -lstdcx
usually means that
the linker cannot find the standard C++ library, libstdcxx.a
(it is truncated to libstdcx.a
on DOS and NT systems). Look into
your lib/
subdirectory to see if it's there; if not, unzip it
from the gppNNNb.zip
file.
If libstdcxx.a
exists but the linker still complains, you most
probably have a problem related to long file names on Windows 9X
(libstdcxx.a
exceeds the DOS 8+3 limits). For a quick fix, try
to set LFN=y
in the environment and see if that helps. If that
doesn't help, make sure you unpacked gppNNNb.zip
with an unzip
program which supports long file names.
This issue is further complicated if you use RHIDE v1.4, and is
described in full in the file gnu/gcc-2.95/readme.DJGPP
which
comes with the GCC distribution (and which you should have read before
the installation). Bottom line is that you need to add a line either to
rhide.env
(the RHIDE distribution includes a file
rhide_.env
which you should rename) or to DJGPP.ENV
which
says this:
RHIDE_TYPED_LIBS_DJGPP.cc=stdcxx RHIDE_TYPED_LIBS_DJGPP.cxx=stdcxx RHIDE_TYPED_LIBS_DJGPP.cpp=stdcxx
When you add these lines, make sure neither they nor the [rhide]
line have trailing whitespace, otherwise RHIDE will not recognize
these lines.
DJGPP version 2.03 and later come with these lines in the
DJGPP.ENV
file right out of the box.
RHIDE v1.4.7 and later solves this bug, so upgrade to the latest version if you can.
See C++ headers not found, for similar problems specific to C++ header files.
In general, if the compiler complains about missing files, you need
first to find out whether they at all exist on your system. For C
header files, look in the include
directory and its
subdirectories; for C++ header files, look in the lang/cxx
directory and its subdirectories; for libraries and the crt0.o
file, look in the lib
directory. Some header files and object
files which are specific to a certain GCC version unzip into the
lib/gcc-lib/djgpp/X.YZ
directory (where X.YZ
is the GCC
version number, e.g. 2.95), so look there as well.
If a header file indeed is not there, and you cannot find it in the
djdevNNN.zip
and gppNNNb.zip
distributions, it probably
means that this header belongs to a package which isn't part of the
basic DJGPP distribution. You need to find that package and install it.
It is important to understand that if a package is missing, getting hold
of the header files like socket.h
is not enough: you need the
library of the functions whose declarations and prototypes are in the
header. For socket.h
, you need a sockets library, such as
libsock
; see DJGPP packages. For graphics.h
, you need GRX and the Borland-to-GRX
interface, BCC2GRX (rename the file libbcc.h
to
graphics.h
); see BCC2GRX interface package.
If the header or the library does in fact exist on your machine, then in order for the compiler to find them, you should have the following variable set in your environment13:
set DJGPP=c:/djgpp/djgpp.env
and it should point to the correct path of the file DJGPP.ENV
on
your system; the file itself is in djdev203.zip in
the DJGPP distribution. In the above example it is assumed to be in the
C:\DJGPP
directory, but you should set it as appropriate for
your installation.
Many of the problems with "missing" files, including the
highly-confusing message about -lgcc
("File format not
recognized"), are usually caused by having the DJGPP
variable
set incorrectly. The following describes some problems with defining
DJGPP
which pop up frequently on the DJGPP forum.
Sometimes, people make errors in their AUTOEXEC.BAT
that cause
the DJGPP variable to be defined incorrectly, or not defined at
all (some of the more common errors are listed below). To check what is
the actual setting, type from the DOS prompt:
set > env.dat
then examine the contents of the file env.dat
. You should see
there a line like this:
DJGPP=c:/djgpp/djgpp.env
If a line such as this isn't there, you should investigate the cause for this (see below for some of the possibilities).
Many problems with setting DJGPP happen when people put excess
blanks around the =
character, which has the effect of defining
"DJGPP " (with the blank) which is not the same as "DJGPP" (without
blanks). You should make sure there are no such excess blanks, or DJGPP
won't find its files.
Another possible cause of DJGPP variable not being set is that you
invoke another batch file from your AUTOEXEC.BAT
before the line
that sets DJGPP. Make sure such batch files are invoked with the
CALL
statement, because otherwise the subsidiary batch file will
never return to process the rest of AUTOEXEC.BAT
(that's a
"feature" of DOS batch file processing).
The code that processes DJGPP.ENV
assumes that this file resides
in the main DJGPP installation directory. If that assumption is wrong,
the compiler (and some other DJGPP programs) might fail to find some of
the files or auxiliary programs they need. Do NOT move DJGPP.ENV
to any other directory!
Note that if you run DJGPP on Windows/NT, you cannot use long
names of the directories in the pathname of DJGPP.ENV
when you
set the above variable in the environment; you should use their 8+3
aliases instead. That's because Windows/NT doesn't support the LFN API
for DOS programs, so the DJGPP startup code won't be able to find the
DJGPP.ENV
file using its long pathname. For example, the
following setting won't work on Windows/NT because
Development
is longer than 8 characters:
set DJGPP=c:/Programs/Development/Djgpp/djgpp.env
If the DJGPP variable is set correctly, then check the following possible causes of this misbehavior:
DJGPP.ENV
in a way that invalidated some
of the settings there; try restoring the original file from the
distribution to see if that fixes your problems. Editing
DJGPP.ENV
is not recommended, but if you must edit it,
make sure you are familiar with its syntax in advance. The DJGPP server
has a page with a description of the DJGPP.ENV
syntax.
The syntax of DJGPP.ENV
is also described in the DJGPP
Knowledge Base,
which comes with the djdev
distribution.
gcc.exe
driver to some other name. In this case,
you should edit the file DJGPP.ENV
to add a section named after
the new name of GCC, which is an exact duplicate of the section called
[gcc]
. DJGPP start-up code uses this file to find environment
variables which it should put into the environment before the main
function is called, but it searches for the relevant variables using the
actual name of the program, so when you rename the executable, it can't
find its section and doesn't put the necessary variables into the
environment.
One case where this might happen is if you install the GNAT (GNU Ada
Translator) package: its installation program alters the value of the
PATH
environment variable so that when you invoke gcc
,
you get GNAT's version of GCC, which searches header files in its own
directories. This can prevent GCC from finding header files of other
add-on packages, such as Allegro.
FILES=
setting in CONFIG.SYS
is insufficient, so GCC
runs out of available handles.
You should have at least FILE=15
in your CONFIG.SYS
, more
on Windows. See details about FILES= directive.
-B
switch to GCC. This overrides the default
location of crt0.o
and if you follow -B
with a directory
other than that where crt0.o
resides, the linker won't find it.
You should not need to use the -B
or -L
switches at all if
your installation is correct and the DJGPP
variable points to the
main installation directory, because GCC should be able to figure out
all the linker switches itself. If linking fails without explicit
-L
or -B
, check out above for the possible causes.
Q: I installed all the packages, but GCC complains it can't find
iostream.h
, _string.h
and other C++ headers. Where can I
find those header files?
Q: GCC complains about being unable to find Complex.h
,
Regex.h
and other header files which start with a capital letter,
and I indeed don't see them in my lang/cxx/
directory. Where are
they?
Q: My C++ program needs header files whose filenames exceed the 8+3
DOS filename restrictions, like stdiostream.h
and
streambuf.h
, and GCC cannot find those files. How in the world
can I write portable C++ programs??
A: All C++ include files are packaged as part of the GNU C++ compiler distribution zip file,
so if you didn't install it, GCC won't find them. Files whose names
usually start with a capital letter, on MS-DOS have an underscore
_
prepended so they can be distinguished from complex.h
,
regex.h
and the like under case-insensitive DOS. Change
Complex.h
to _Complex.h
, and String.h
to
_String.h
in your source, and GCC will find them. The same goes
for the header iostreamP.h
--you should use _iostreamP.h
instead. If you don't have the underscore _ on your keyboard, you
might find using strclass.h
instead of _String.h
easier.
Another possibility to handle header files like Complex.h
in a
portable way is to pass the -remap
switch (supported by GCC 2.8.0
and later) to the pre-processor; see the cpp
docs and the
readme.DJGPP
file in the GCC distribution, for more info about
this feature.
The most probable cause of problems with header files whose names exceed
the DOS 8+3 limits is that you are compiling on Windows 9X, but the
Long File Names (a.k.a. LFN) support is disabled. DJGPP v2.01
comes with LFN disabled by default on the DJGPP.ENV
file. To
enable it, set the environment variable LFN
to y
, like
this:
set LFN=y
If the problems with long names of header files aren't solved by this,
it is possible that you unpacked the DJGPP distribution with a program
which doesn't support long file names. The solution is to install DJGPP
again using a different unzip program. unzip32.exe
, available
from the DJGPP sites, is one possibility.
Some users copy the DJGPP directories after unzipping to another place
on their disk, or backup and restore them. If this is done by some
program that doesn't support long file names, the compiler won't be able
to find header files such as strambuf.h
. Editing the directory
with some disk-editing tool that doesn't support Windows 9X style long
file names can also cause such loss of long file names: when Windows 9X
starts up, it checks whether the long file names and their 8+3 aliases
are in sync, and if they aren't, the long file names are deleted from
the directory, leaving you only with the short file names such as
stream~1.h
. Type dir *.h
to see what are the long file
names in the directory; the long names are printed on the right side of
the file listing, and the short aliases on the left side, like this:
stream h 1,925 12-26-95 8:07p STREAM.H stream~1 h 17,020 01-24-96 2:11a streambuf.h
(The files' date, time, and size might be different in your case.) The easiest solution for these cases is to remove the entire DJGPP installation, and unzip everything again.
Another possible cause for lack of support for long file names is that you switch to the so-called "DOS Mode" when running DJGPP programs from Windows 9X. This unloads from memory most of Windows, including the VFAT Filesystem module that supports the LFN API used by DJGPP to access long file names. The solution is to make sure your DOS box's Properties don't force a switch to "DOS Mode".
If you have problems with header files with long filenames, and you run
under Windows NT, it usually means that you used an unzip program which
supports long file names on NT; unzip again using a DOS unzip program,
such as unzip32.exe
that is available from the DJGPP sites.
Alternatively, you could install an LFN driver for Windows NT, see
LFN driver for NT,
earlier in this FAQ.
Another possible cause for problems with C++ include files is that your
source file has a .c
extension. GCC then thinks that this is a C
program and doesn't instruct the pre-processor to search the include
directories specific to C++. Rename your file to .cc
or
.cpp
extension, or call GCC with the -x c++
switch, and
the header files will be found. A full list of extension rules which
GCC uses to determine the source language can be found in the list of language-specific suffixes, elsewhere in this FAQ.
Q: My C program compiles OK with Borland's C, but GCC complains
about "parse error before `/' " at a line where I have a "//"-style
comment.
A: That's because // isn't a comment neither in ANSI C nor in K&R C.
Borland and Microsoft C compilers support it as an extension. GCC also
supports this extension (beginning with version 2.7.0), but using the
-ansi
or -traditional
switches to GCC disables this
extension. In general, it's a bad practice to use this extension in a
portable program until such time as the ANSI C standard includes it. If
it's a C++ program, then rename it to have a suffix which will cause gcc
to compile it as such (see list of language-specific suffixes), or use -x
c++
switch. If it's a C program, but you want to compile it as C++
anyway, try -x c++
; it can help, but can also get you in more
trouble, because C++ has its own rules. For example, the following
program will print 10 if compiled as a C program, but 5 if compiled as
C++14:
#include <stdio.h> int main () { printf ("%d \n", 10 //* / 2 // */ 1 ); return 0; }
If you must have both -ansi
and C++-style comments, use
-lang-c-c++-comments
preprocessor switch. Gcc doesn't accept
the -lang-XXX
switches on its command line, so you will have to
use the -Wp
option, like this:
gcc -c -Wp,-lang-c-c++-comments myprog.c
Alternatively, you can add -lang-c-c++-comments
to the *cpp:
section of your lib/specs
file (but that will cause gcc
to
pass it to cpp
unconditionally).
Bottom line: until the future ANSI/ISO C standard includes this as part
of the C language, it's best to change those //
comments to
C-style ones, if you really mean to write a C program. The following
SED command will convert a C program with C++-style comments
into a valid C source, provided you don't have the string "//" in a
character string:
sed "s?//\(.*\)?/*\1 */?" file.c > newfile.c
SED can be found with the DJGPP archives on SimTel.NET, in the
v2gnu
directory.
If you want the compiler to print a warning message about usage of
//
-style comments in a C program, add the -ansi
-pedantic
options to the GCC command line. If you don't want to use
-ansi
for some reason (e.g., because it rejects some other code
that you want to keep), try using -Wp,-lang-c89
instead; this
tells the preprocessor to stick to the rules of the C89 standard.
Q: I type GCC PROG.CC and GCC complains that it can't recognize
PROG.CC
's file format. How come a C++ compiler doesn't recognize a
C++ source??
Q: I type GCC PROG.C to compile a C program which I already
remember to pass compilation without a single warning, and suddenly it
gives all kinds of strange error messages and unresolved externals.
A: That's because you typed your source file extension in UPPER case. GCC is not case-insensitive about filenames like DOS is, and it uses the file's extension to determine how to compile a file. Valid extensions are:
.cc
.C
.cxx
.cpp
.c
.i
.ii
.m
.S
.s
Any other file is passed to the linker, under the assumption that it's an object file.
In the examples above, PROG.C
is taken as a C++ program, not a C
one, and PROG.CC
is passed to the linker as if it were an object
file. You can see what GCC does by adding the -v
switch to the GCC
command line; if you see that it's invoking cc1plus.exe
(the C++
compiler) instead of cc1.exe
(the C compiler), or calling
ld.exe
(the linker) on a source file, then you'd know this is your
problem. If you have problems keeping up with the verbose GCC output
triggered by -v
, see how to capture GCC output, earlier in this FAQ.
You can override the default rules gcc uses to decide how each input
file should be treated, using the -x language
switch. For
instance, the command
gcc -x c++ prog.c
compiles prog.c
as C++ source. See The GNU C Compiler Manual, for more info on
-x
options.
Q: How do I tell gcc my .cc file is to be compiled as Objective-C
source?
Q: I compile an Objective-C program, but get unresolved symbols.
Q: I can't compile the Objective-C test program which came with DJGPP.
A: Give your sources the .m
extension, or use
-x objective-c
switch to GCC, so it will know you mean
to compile with Objective C.
Q: I must put a DJGPP-specific code fragment into my program. What
symbol should I use in the #ifdef
directive to make it only visible
under DJGPP?
A: Use __DJGPP__
, like this:
#ifdef __DJGPP__ ... DJGPP-specific code ... #else ... not seen under DJGPP ... #endif
__DJGPP__
has the value of the DJGPP major revision number, so you
can write code fragments which have different behavior under different
versions of DJGPP:
#ifdef __DJGPP__ #if __DJGPP__ > 2 .... will work only in DJGPP v3.x and later ... #else .... get here for DJGPP v2.x ... #endif #else .... get here in DJGPP v1.x or non-DJGPP environment #endif
If you need to distinguish between minor DJGPP revision numbers, use the
symbol __DJGPP_MINOR__
. For example:
#if defined(__DJGPP__) && __DJGPP__ == 2 && __DJGPP_MINOR__ == 1 .... will work only in DJGPP v2.01 .... #endif
Another DJGPP-specific pre-processor symbol which DJGPP defines is
__GO32__
; but it is only provided for compatibility with previous
versions of DJGPP (v1.x) and its use should be discouraged.
Q: Why do I get so many undefined references when linking my
programs?
Q: Why do I get "Undefined reference to yywrap" when linking
programs produced by Flex?
Q: GCC complains that it cannot find -liostream. Where can I find
this library?
A: By default, GCC instructs the linker to only look in two
libraries: libgcc.a
and libc.a.
Some functions aren't
included there, so the linker can't find them. If you need to link
against some optional library, say libxy.a
, put the library into
the DJGPP lib/
subdirectory and append a -lxy
to the link
command line. The Standard C++ Template classes are in
libstdcxx.a
(it's called libstdc++.a
on Unix); append
-lstdcxx
. To use the additional GNU C++ classes in the
libgpp.a
library (it's called libg++.a
on Unix systems),
append -lgpp
. Flex-generated lexical analyzers call functions in
the libfl.a
library; you need to append -lfl
when linking
them. Append -lgrx
if you are using GRX library, and
-lalleg
for linking with Allegro.
When linking C++ programs, you should use either the gpp
or
gxx
commands instead of gcc
; they will then instruct the
linker to also scan the C++ libraries automatically, so you don't
have to remember doing that yourself.
Another reason for undefined references when linking C++ programs is
that you mix GCC and libstdcxx.a
from different releases: they
are usually incompatible. In particular, sometimes people install an
additional compiler based on (old releases of) GCC, such as GNU Pascal
or GNAT, the GNU Ada development environment, and these additional
compilers overwrite some of the libraries, like libgcc.a
, with
older and incompatible versions. You should always make sure you don't
mix different releases of the compiler and libraries; if you must
install different releases, install them in separate directories and
prepare some batch files or shortcuts to set up the environments for
each of the compilers by pointing the DJGPP
variable to different
directories and changing the order of directories in PATH
.
If your program uses a lot of floating-point math, or needs math
functions beyond those specified in the ANSI/ISO standard, consider
appending -lm to your link command line. The basic math functions
required by ANSI/ISO standard are included in the libc.a
library,
but libm.a
includes different versions of these functions which
sometimes are more accurate or more compatible with widely-accepted
standards for numeric computations; libm.a
also includes some
functions not included in the default library, like Gamma function and
Bessel functions, support for different standards of behavior in case of
errors, a matherr
facility, etc.
Old C++ programs used to be built with the GNU iostream library,
libiostream.a
. The iostream classes are now part of the standard
C++ library, libstdcxx.a
, so if you come across a Makefile
that passes the -liostream
option to the compiler, change that
to -lstdcxx
instead.
Further problems which cause the linker to fail with C++ programs are discussed in listing libraries in the correct order, and in exceptions and inline functions.
Q: I'm lost with all those different libraries. How in the world
can I find out which functions are included in which library?
A: You can use the nm
program to check what functions are
included in a library. Run it with the -C
option and with the library
as its argument and look in the output for the name of your function (the
-C
, or --demangle
option makes the function names look
closer to what they are called in the source file). Functions which have
their code included in the library have a capital T
before their
name. For example, the following is a fragment from the listing produced by
nm
:
c:\djgpp\lib> nm --demangle libc.a . . . stdio.o: 000000e4 b .bss 000000e4 d .data 00000000 t .text 00000098 t L12 0000001e t L3 00000042 t L6 0000004d t L7 0000006a t L9 00000000 t __gnu_compiled_c U _filbuf U _flsbuf 00000000 T clearerr 000000ac T feof 000000c2 T ferror 000000d8 T fileno 0000000c T getc 00000052 T getchar 0000002a T putc 0000007c T putchar 00000000 t gcc2_compiled. . . .
Here we see that the module stdio.o
defines the functions
clearerr
, feof
, ferror
, fileno
, getc
,
getchar
, putc
and putchar
, and calls functions
_filbuf
and _flsbuf
which aren't defined on this module.
Alternatively, you can call nm
with the -s
or
--print-armap
, which will print an index of what symbols are
included in what modules. For instance, for libc.a
, we will see:
c:\djgpp\lib> nm --print-armap libc.a . . . _feof in stdio.o _ferror in stdio.o _fileno in stdio.o . . .
which tells us that the functions feof
, ferror
and
fileno
are defined in the module stdio.o.
nm
is fully described in the GNU docs. See GNU Binutils Manual.
Q: I give all the libraries to gcc, but I still get unresolved
externals when I link. What gives?
A: Ld
is a one-pass linker: it only scans each library once
looking for unresolved externals it saw until that point. This
means the relative position of object files and libraries' names on the
command line is significant. You should put all the libraries
after all the object files, and in this order:
-lstdcxx -lm
E.g., to link files main.o and sub.o into a C++ library, use the following command line:
gcc -o main.exe main.o sub.o -lstdcxx -lm
or, if you compile and link in one command:
gcc -o main.exe main.cc sub.cc -lstdcxx -lm
If you have any libraries of your own, put them before the above system libraries, like this:
gcc -o main.exe main.cc sub.cc -lmylib -lstdcxx -lm
When you use the gpp
or the gxx
drivers to compile a
C++ program, it automatically names the C++ libraries in the
correct order. (gpp
and gxx
are the alternative names for
g++
on DOS, which doesn't allow the +
character in file
names.)
You can also force the linker to repeatedly scan a group of libraries
until all externals are resolved. To this end, put the names of these
libraries between the -(
and the -)
options (if you invoke
GCC to link, use the -Wl
or -Xlinker
options to pass
switches to the linker). Check out the linker docs for more info about
-( ... -)
groups.
If your installation tree is different from the default, i.e., if you keep
the libraries not in the default lib/
subdirectory, then
you should add that directory to the line in the [gcc]
section of
your DJGPP.ENV
file which starts with LIBRARY_PATH
, or put
into your environment a variable called LIBRARY_PATH
and point it
to the directory where you keep the libraries. Note that if you invoke
the linker by itself (not through the gcc driver), then
LIBRARY_PATH
will have no effect, because this variable is only
known to the gcc driver. Invoking ld
directly is not
recommended, but if you must do it, use the -L
option to tell it
where to look for the libraries.
Q: I put all the libraries in the above order, but the linker still
can't find some C++ functions from complex.h
and
iostream.h.
Q: I can't compile a program which uses the String class: the linker
complains about undefined functions!
Q: I get many undefined references to symbols like __eh_pc
,
terminate
, and __throw
....
A: Some C++ functions are declared inline
and defined on
header files. (One example of this is the string constructor of the GNU
String
class.) However, GCC won't inline them unless you compile
with optimizations enabled, so it tries to find the compiled version of
the functions in the library. Workaround: compile with -O2
.
Another cause of missing external symbols might be that your versions of
libgcc.a
and the compiler aren't in sync. These cases usually
produce undefined references to symbols such as __throw
and
__eh_pc
. You should only use libgcc.a
from the same
distribution where you got the compiler binaries. The reason for these
problems is that the setup for supporting C++ exceptions is subtly
different in each version of the compiler.
For C++ programs, be sure to compile all of your object files and
libraries with the same version of the compiler. If you cannot
recompile some of the old C++ object files or libraries, try using
the -fno-exceptions -fno-rtti
switches to GCC, it helps
sometimes. However, note that -fno-rtti
cannot be used with
GCC version 2.95 and later: programs that use exceptions will crash if
compiled with this option.
If you call C functions from a C++ program, you need to make sure
the prototype of the C function is declared with the extern "C"
qualifier. DJGPP header files take care about this, but headers you get
with third-party libraries, or write yourself, might not. Failure to
use extern "C"
will cause the linker to look for a C++
function instead of a C function, which will fail because names of
C++ functions are mangled by the compiler (to include the types of
their arguments, since there can be many functions with the same name
but different argument types).
Yet another possible cause for the linker to complain about undefined
references is that you link files compiled using RSXNTDJ headers, or
link RSXNTDJ-compiled object files with DJGPP libraries, or vice versa.
DJGPP and RSXNTDJ are really incompatible as far as header files and
libraries are concerned, so you cannot mix them. Add -v
to the
gcc
command line and watch the order of searching the include
directories and libraries printed by GCC: if you are compiling/linking a
DJGPP program, but the RSXNTDJ directories appear first in the search
order, you should change the order of the directories in the
C_INCLUDE_PATH
, CPLUS_INCLUDE_PATH
and LIBRARY_PATH
environment variables. Similarly, if you are compiling an RSXNTDJ
program, but the DJGPP's include
and lib
subdirectories
appear first in the list printed by gcc -v
, you need to put
the RSXNTDJ directories first in the environment variables mentioned
above.
Q: I do everything like your praised FAQ says, but the linker
complains about unresolved symbols with strange names like
djgpp_first_ctor
, djgpp_last_dtor
, etc. I looked in every
library with nm
, and I cannot find these creatures. Where in the
world are they??
A: These symbols are defined by the djgpp.djl
linker script
that should be in your lib/
subdirectory. When you call
gcc
to link a program, it invokes ld.exe
with the option
-T djgpp.djl
. If you invoke ld
directly (this is generally
not recommended), be sure to include that switch. If you did invoke it
through gcc
, maybe your linker is set up incorrectly. Add
-v
to the GCC switches and check that the command line that GCC
gives to LD includes that switch, that your lib/
subdirectory
includes that script file, and that the script file is intact and
includes the definition of the above symbols.
Another reason might be that you have edited your DJGPP.ENV
file
in a way that prevents the linker from finding its djgpp.djl
script.
Mixing an old v1.x installation with a v2.x one can also cause such problems. Be sure to delete the entire v1.x tree, or rename it, before installing the v2.x distribution.
.exe
fileQ: It seems that declaring a large static
array has the effect
of bloating the program image on disk by that many bytes. Surely there is
a more compact way of telling the loader to set the next N bytes of RAM to
zero?
A: This only happens in C++ programs and is a (mis-)feature of GCC.
You can use the -fconserve-space
switch to GCC to prevent this from
happening, but that switch also turns off the diagnostics of duplicate
definitions, which, if uncaught, might cause your program to crash.
Thus, this switch isn't recommended for programs which haven't been
completely debugged (if there is such a creature). The
-fconserve-space
switch is described in the GCC docs, see GNU C Compiler Manual.
If the problems with using this switch doesn't deter you, you can even
add this switch to your lib/specs
file to make it permanent.
GCC versions 2.95.1 and later don't have this problem, even in C++ programs.
.exe
files so large?Q: I compiled a trivial "Hello world" program and got a 280KB
executable file. That's ridiculously bloated!
Q: I switched to GCC 2.95.1, and my C++ executables are
considerably larger than when compiled with GCC 2.7.2.1!
A: Did you link with -s
switch to gcc
, or run
strip
on the output of the linker? If not, the executable
includes the debugging symbols, which makes it quite a lot larger. (It
is not recommended to strip the symbols except when distributing
production programs, because this makes debugging very hard indeed; that
is why -s
is not passed to gcc by default.)
A stripped "Hello world" program written in C should be about 42KB on
disk; an analogous program written in C++ should be about 140KB on
disk (the additional overhead is due to the C++ classes that are
linked in to support cout
).
C++ programs could be further bloated because the release of
Binutils 2.8.1 was configured in a way that caused the assembler to put
into the symbol table local labels generated when compiling code that
uses exceptions. Later uploads of GNU Binutils should solve this
problem, so consider upgrading to the latest bnuNNNb.zip
.
Some other compilers with which people keep comparing the size of DJGPP programs use shared libraries or DLLs, so the size of the executable doesn't include the libraries. If you have an immediate question "Why won't DJGPP use DLLs as well", read the following section.
In general, judging code sizes by looking at the size of "Hello"
programs is meaningless, because such programs consist mostly of the
startup code. The DJGPP startup code does many things in preparation
for running a protected-mode program in a Posix-compliant environment.
This includes switching the processor to protected mode (which requires
a lot of code), wildcard expansion, long command-line support, and
loading the environment from a disk file; these usually aren't available
with other DOS compilers. Exception and signal handling (not available
at all in v1.x), FPU detection and emulator loading (which were part of
go32
in v1.x), are now also part of the startup code.
Most of the power of all these features goes wasted in "Hello"
programs. There is no point in running all that code just to print a
15-byte string and exit. However, the overhead induced by the code
needed to set up the DJGPP run-time environment is additive; the larger
the program, the smaller the overhead relative to the program size. For
non-trivial programs, the code produced by DJGPP is usually smaller than
what other compilers produce. For example, the DJGPP version of the
Povray
program is smaller by about 200KB than the same program
compiled with the Watcom compiler.
If your program doesn't need parts of the startup code, it can be made
smaller by defining certain functions with empty bodies. These
functions are __crt0_glob_function
,
__crt0_load_environment_file
, and __crt0_setup_arguments.
If you define empty substitutes for all three of these, and compile with
-O2 -s
, you can make the "Hello" program be 31KB on disk.
These functions are documented in the DJGPP libc reference, which see.
Here's an example of definitions for these functions that will make the
startup code as small as it gets15:
#include <crt0.h> char **__crt0_glob_function (char *arg) { return 0; } void __crt0_load_environment_file (char *progname) { } void __crt0_setup_arguments (void) { }
(To do this in a C++ program, prepend the extern "C"
qualifier to each one of the three lines that define the substitute
functions.)
Note that if you define an empty substitute for
__crt0_setup_arguments
, your program will not be able to access
its command-line arguments via the argv[]
array. So this is only
recommended for programs which don't accept any arguments at all.
You can make your program image still smaller by compressing it with a compressor called DJP. DJP is a DJGPP-specific executable file compressor. It is fast and has no memory overhead. It also knows about DJGPP Dynamically Loaded Modules (DLM) technology. (Note that DJP before version 1.06 was incompatible with Binutils 2.8.1 and later16, so you should always use the latest DJP version available on SimTel.NET mirrors.)
DJP is not actively developed anymore; its successor is the
UPX compressor, currently in beta testing. UPX is written by
the same people who wrote DJP, compresses better, and supports a
broader class of executable formats, including DOS .exe
,
.com
and .sys
, DJGPP's COFF, Watcom's LE, Win32 PE, and
Linux's ELF. UPX is available via the Web.
Q: Many other compilers use shared libraries and DLLs to make the
programs' size smaller; why don't you do the same?
A: DLLs are really a mixed blessing, because they introduce an additional dimension into configuration management and subtle system differences.
Consider a DJGPP developer who is maintaining a package. Let's say that this developer finds a bug in one of the library functions that adversely affects his/her package. The obvious solution is to fix the library bug, and then relink the application against the fixed library. In the current setup, the only thing that the developer needs to do is to upload a binary distribution with the new executables, which will ensure that all users can reliably use the fixed program.
Now imagine that we ditch the static linking and instead distribute the standard libraries as DLLs to be installed on every machine. Our developer will now need to put a fixed DLL into the binary distribution, otherwise the package will still exhibit the bug when it uses an old DLL on the end-user's machine. Installing the fixed package would then need to overwrite the DLLs on users' machines with the fixed version.
But now suppose that the bugfix in that single library function made it subtly incompatible with other library functions, which the program of our developer didn't use (and so these problems went unnoticed during testing). When users will install the fixed DLL, they will now have a broken system, where programs which are unrelated to the upgraded package, and which worked perfectly before, would now mysteriously fail or crash.
Moreover, since there are quite a few DJGPP packages, all maintained separately by different individuals, and since users are installing the new-and-improved versions all the time, after some time there's no way to know what versions of system DLLs are installed on any given machine. The result is that no developer can be sure that their programs would work on any particular system, because the precise mix of the system DLLs on that system cannot be predicted. What you have is an environment where major programs constantly crash.
Sounds familiar? Of course! this is precisely the reason that most Windows systems are so unstable: people are constantly installing the hottest new versions of Office, IE, etc., and are constantly overwriting their system DLLs with new and subtly incompatible versions. If you can afford it, try to install Windows 9X and run it for a year without installing any add-on packages which come with a replacement for system DLLs--you will see a rock-solid system that can be run for weeks without crashing. (Yes, I actually tried that; yes, it really didn't crash.)
In addition, DLLs in the DJGPP environment make much less sense than on Unix or Windows, because the DJGPP DLLs will only be used by DJGPP program, whereas Unix shared libraries and Windows DLLs are used by the OS itself as well. When the OS itself uses the same libraries, the libraries are most of the time in memory when the applications need them, and running several applications only loads the library once. DJGPP cannot take advantage of this, even on Windows, because each DJGPP program runs in a separate Virtual Machine with a separate address space. The only case where DJGPP program can benefit from shared libraries is when one DJGPP program invokes another.
So bottom line, I think wasting some disk space due to static linking is much cheaper than having to deal with the user frustration and outcry that would result from using the DLL approach. Perhaps a behemoth such as Microsoft can afford ignoring all the mess that DLLs bring to the end users, but around here, a good name of the product still counts.
Q: When I link my program, it fails to produce the .EXE
executable....
Q: I run STUBIFY on a networked drive under
Novell, but it doesn't produce a .EXE file. How come?
A: One possible reason for this is that your disk is full, or
there's no swap space available for the DOS box on Windows. Run
go32-v2
with no arguments and see what it reports, then follow
the advice in configuring your system, for the optimal configuration.
If you are running DJGPP on a networked drive, you might have another
copy of the file with the same name that GCC is creating in another
directory somewhere on that networked drive. If that other directory is
on your PATH
, it is searched by Novell when the linker and
STUBIFY
try to create the executable file, because that file
doesn't exist in the current directory. So what might actually happen
is that the linker and STUBIFY
are overwriting the files they
find on your PATH
instead of creating new files in the current
directory.
You can verify that this indeed is the problem by searching your networked disks for files with the same name as those you are trying to build, and looking at their time stamps. If that is indeed the problem, then you have several possible ways of solving it:
PATH
.
PATH
where the other files reside, so that you won't have write privileges to
that directory.
STUBIFY
and the linker (or for
any other program that gives you that trouble) by running commands like
these:
SMODE stubify.exe 2 SMODE ld.exe 2
Q: When I try to build the Allegro library, liballeg.a, I get some
cryptic message about register-opcode mismatch. What am I doing wrong?
Q: Why do I get these messages saying "fixed or forbidden register
0 (ax) was spilled" when I try to build Allegro?
Q: It seems I miss one of the source files from the Allegro
distribution, because Make cannot find it when I try to build Allegro.
Q: I can't build Allegro: it keeps telling me that I "need to
install gcc2721b.zip". But I already have GCC installed!
A: You should get the latest version of Allegro that is available either from SimTel.NET or from Shawn Hargreaves' site.
Versions of Allegro before 3.0 are known to have bugs which triggered register-opcode mismatch messages.
GCC 2.95 became more picky about some invalid use of clobber specifiers in Allegro's inline assembly, so what compiled with GCC 2.8.1 won't compile anymore; latest versions of Allegro (3.12 and above) correct that.
GRX versions 2.3 and older also have a few places where the newer GCC
releases won't compile the inline assembly code. Ian Miller created two patch files that solve two
different classes of problems with GRX 2.3 inline assembly, and made
them available from his Web page: patches for the clobber list problem and patch for indirect calls. You will
need to use the patch
utility to apply these patch files, and
then recompile the offending library. A DJGPP port of the GNU
patch
is available from SimTel.
For a general explanation of how to correct clobber list specifications in inline asm code so that they will compile with GCC 2.95 and later, see the GCC FAQ list.
Q: When I compile a C++ program which includes some standard C
header files, the compiler prints error messages about redefinition of
NULL
....
A: This is because GCC 2.8.1 comes with C++ header files which
redefine NULL
in a way that conflicts with the DJGPP headers.
It's a bug in the GNU C++ headers, but until it is fixed, you will
need to make sure you include the C++ headers after the C
headers. If that doesn't help in your case, you will need to hack your
headers to reconcile them.
The C header files that come with DJGPP v2.02 work around this problem, so upgrading to the latest DJGPP release should make these messages go away.
Q: I've written a program that uses C++ exceptions, but instead of
catching an exception, the program prints "Abort!" and dies....
Q: When linking C++ programs, I get messages about undefined
references to __EH_FRAME_BEGIN__
and such likes. Huh?
Q: I cannot compile C++ programs that include the header
math.h
: the compiler complains about redefinition of class
exception!
A: C++ exceptions were not fully supported in DJGPP before version
2.8.1 of GCC. Either upgrade to the latest version or compile with the
-fsjlj-exceptions
switch to GCC. GCC support of exceptions
before v2.8.0, was minimal, so even this special switch won't work with
previous versions. If you still use GCC 2.7.2.1 and cannot upgrade, you
need to compile with the -frtti
compiler switch and include the
typeinfo
header in your program.
Beginning with EGCS 1.1.2 and GCC 2.95, C++ exception support requires DJGPP v2.02 or later, and will not work with v2.01 or earlier, so you might need to upgrade your DJGPP library.
Note that exception support with -fsjlj-exceptions
is very slow,
since it has a significant runtime overhead, even if the exception
doesn't occur.
If you already use GCC 2.8.1, these problems could happen if you failed
to replace the specs
file with the version which comes with the
GCC 2.8.1 distribution. Read the file readme.DJGPP
in the GCC
distribution, for more details. GCC 2.95 and later should work with the
specs
file from djdev202.zip
(or later) or with the
specs
file that comes with GCC itself.
Exception support in GCC is generally not stable enough yet, so you need to treat with some suspicion code produced by GCC 2.8.1 for programs that use exceptions. Latest versions of GCC support exceptions better, so upgrade to GCC 2.95 or later.
Undefined references to symbols like __EH_FRAME_BEGIN__
are a
symptom of using an old linker script djgpp.djl
. You should
make sure that djgpp.djl
in your lib
subdirectory is from
djdevNNN.zip
file that belongs to DJGPP v2.02 or later. (GCC
2.8.1 distribution required to replace djgpp.djl
with a version
that came with the compiler, but the reason for that is no longer valid
with newer GCC versions, and the compiler no longer comes with
djgpp.djl
. So you must restore djgpp.djl
from
djdevNNN.zip
.) Again, readme.DJGPP
in the GCC
distribution has more on this.
If GCC complains about "Redefinition of class exception" when you
compile C++ programs which include the header math.h
, you
need to replace that header. GCC 2.8.1 comes with a header
exception
that conflicts with math.h
from DJGPP v2.01,
which defines a struct exception
. Version 2.02 of DJGPP
corrects its math.h
, but if you still use v2.01, a corrected
version is included in the gcc281b.zip
distribution. The
corrected math.h
is installed into the
lib/gcc-lib/djgpp/2.81/include
directory, so either delete or
rename the old version in the include
directory, or copy the
corrected version into include
. Another solution is to compile
with the -posix
or -ansi
compiler switch, which cause
math.h
to not define struct exception
.
Q: How can I peek at the assembly code generated by GCC?
Q: How can I create a file where I can see the C code and its
assembly translation together?
A: Use the -S
(note: capital S) switch to GCC, and it
will emit the assembly code to a file with a .s
extension. For
example, the following command:
gcc -O2 -S -c foo.c
will leave the generated assembly code on the file foo.s
.
If you want to see the C code together with the assembly it was converted to, use a command line like this:
gcc -c -Wa,-a,-ad [other GCC options] foo.c > foo.lst
which will output the combined C/assembly listing to the file
foo.lst
.
If you need to both get the assembly code and to compile/link the
program, you can either give the -save-temps
option to GCC (which
will leave all the temporary files including the .s
file in the
current directory), or use the -Wa,aln=foo.s
option which
instructs the assembler to output the assembly translation of the C code
(together with the hex machine code and some additional info) to the
file named after the =
.
sys/movedata.h
?Q: Whenever I try to compile a program that includes the
sys/movedata.h
header file, I get "parse error" messages from
the compiler. Can't you guys make your system headers right?
A: This is a bug in the sys/movedata.h
header file which
comes with DJGPP v2.01. The bug is fixed in v2.02, but if you are stuck
with v2.01, you should always include the sys/types.h
header
before sys/movedata.h
in your programs.
Q: I would like to distribute my package as a library that can be
linked into programs, but I'm unsure how to go about it....
A: First, you need to compile all your sources into object .o
files, like this:
gcc -c -Wall -O2 file1.c gcc -c -Wall -O2 file2.c gcc -c -Wall -O2 file3.c ...
The only GCC switch in this example that's required is -c
, the
rest are just recommended for better code generation and diagnostics.
Once you have the object files ready, use the ar
("Archiver")
utility to create a library, let's call it libacme.a
, like this:
ar rvs libacme.a file1.o file2.o file3.o ...
The rvs
flags tell ar
to put named files into the
library, replacing any previous versions of these files if necessary,
print the names of object files as it puts them into the library, and
add an object-file index to the library, which makes it link faster.
If you use RHIDE, you can create a library by specifying a file
with a .a
extension as the main target in the project (choose
Project | Main Target Name
and enter a file name such as
libacme.a
).
The library is now ready to use. The simplest way to force the compiler to use it while linking is to mention its name in the link command line, like this:
gcc -o myprog.exe myprog.c libacme.a
This is better than just listing in the command line all the object files in the library, since the latter will cause the linker to link in all the object files, even those which aren't used by the program.
The name of the library which begins with a lib
and ends with a
.a
extension is a convention used for convenience. When the link
command line includes an argument -lXXYYZZ
, GCC (and all Unix
compilers) will look for a file libXXYYZZ.a
in every directory
they search by default. So, if your library libacme.a
is
installed in the DJGPP lib
subdirectory, the user can instruct
GCC to look into it by appending -lacme
to the link command line.
Other systems might be configured to look for different names when a
switch such as -lfoo
is mentioned. For example, Linux might look
in /usr/lib
for files libfoo.so.*
, while Alpha/VMS will
look for SYS$GNU:[LIBRARIES]FOO.LIB;*
. Windows 98, of course,
will look for something monstrously long like
C:\Windows\Program Files\Vendors\GNU\gcc\libraries\foo.lib
.
If you don't follow this convention, you will need to type the full name
of the library file.
If you need to update a certain object file in a library, use the same command ar rvs library-name object-name as above, but only with the name(s) of the object file(s) you need to replace.
ar
is documented in the Binutils docs. To read, type this from
the DOS prompt:
info binutils ar
stubify
.Q: Whenever I try to compile something, GCC says "Installation
problem, cannot exec stubify: No such file or directory (ENOENT)". The
compiler came on a CD with one of those "Teach yourself C++"
books....
A: Blame the vendor who created the CD: their installation program
failed to copy the program stubify.exe
to your hard disk. The
compiler needs stubify.exe
when it links your programs.
To solve the problem, find stubify.exe
on the CD and manually
copy it into the same directory where gcc.exe
lives.
This chapter discusses various problems which may happen when running DJGPP programs under different environments, and gives solutions to them.
malloc
/free
.
Q: My v2 program crashes, but only under CWSDPMI; it runs OK under
other DPMI hosts like Windows, OS/2 or QDPMI. Is this a bug in CWSDPMI?
A: No, it probably is a bug in your program which just goes
unnoticed on Windows. Unlike other DPMI hosts, CWSDPMI supports some
DPMI 1.0 extensions which allow DJGPP to capture and disallow
dereference of pointers which point to addresses less than 1000h
(a.k.a. NULL pointer protection). The tell-tale sign of these
problems is a message "Page fault at ..." that is printed when a
program crashes, and an error code of 4 or 6. The NULL pointer
protection feature can be disabled by setting the
_CRT0_FLAG_NULLOK
bit in _crt0_startup_flags
and
recompiling the program; if this makes SIGSEGV
crashes go
away, your program is using such invalid pointers; the stack trace
printed when the program crashes should be a starting point to debug
this. See how to debug SIGSEGV, for more details about debugging these
problems.
To make spotting uninitialized memory simpler, you can set
_crt0_startup_flags
to _CRT0_FLAG_FILL_DEADBEEF
(don't
laugh!); this will cause the sbrk()'ed memory to be filled with the
value 0xdeadbeef
(-559038737
in signed decimal or
3735928559
in unsigned decimal) which should be easy to spot with
a debugger. Any pointer variable which has this value was used without
initializing it first.
An insufficient stack size can also be a cause of your program's demise, see setting the stack size, below.
malloc
or free
.Q: Since I upgraded to DJGPP v2.02, my program started to crash, and
the traceback points to library function free
. This program
worked flawlessly with v2.01, so I guess there's a bug in the new
version of free
, right?
A: Such problems are a tell-tale sign of programs that overwrite
buffers allocated by malloc
or calloc
, or call free
more than once with the same pointer, or pass to free
a pointer
that didn't originate from a call to malloc
or calloc
. If
the program that crashes is a C++ program, you might have several
objects that share the same data, and the object destructor crashes when
it calls free
several time with the same memory chunk.
These crashes happen inside the memory-allocation functions because
these functions maintain some crucial information about the allocated
and free memory blocks right before the beginning and beyond the end of
the allocated buffers. For speed considerations, this information is
not protected by any means like CRC or parity, so if you overwrite this
information, malloc
and free
will become confused and
eventually will blow up.
The version of malloc
in DJGPP library before v2.02 left some
slack space beyond the end of the allocated buffer (this was a
side-effect of the algorithm it used, which was optimized for speed, but
wasted some memory). Thus, a program could overrun the allocated buffer
and still get away uncaught. The new version of malloc
introduced with v2.02 doesn't waste memory, and because of this is much
less tolerant to such bugs.
Bottom line: you should debug your program to find the offending code that overwrites the end of an allocated buffer. One way of doing that is to put a data breakpoint (a.k.a. watchpoint) inside a debugger at the address which gets overwritten; then, when the program overwrites it, the debugger will kick in and you will see whodunit.
Another possibility to debug such problems is to use the YAMD package, written and maintained by Nate Eldredge. YAMD is a malloc debugger which will catch and report many problems related to allocating, freeing, and using allocated memory. YAMD is available from Nate's home page.
Q: My program dies with a cryptic message like "SIGSEGV" or
"Page Fault" or "General Protection Fault" and prints some
funny-looking numbers. Can't I get some decent human-readable traceback
information, so I could pinpoint where in the program did the problem
happen?
A: Those "funny-looking numbers" are the traceback. They
describe the sequence of function calls which led to the fatal error by
giving you the addresses where each function was called. You can have
these addresses translated to source line numbers by using the
SYMIFY
program. SYMIFY
is included in the
basic DJGPP development environment distribution, and
should be in your bin/
subdirectory. To symify the
traceback, make sure that your program was compiled with the -g
switch, linked without the -s
switch and not
stripped of its debugging symbols by running the strip
utility. Now invoke your program and do whatever it takes to make it
crash. Then, with the traceback still on the screen, type this from the
DOS command line:
symify program-name
(Note: program-name should include the .exe
suffix.)
SYMIFY
then walks through the crash traceback by reading it
from video memory, and matches the hex addresses to the source files and
line numbers of the program. It then writes back the list of source
files and line numbers right next to their hex addresses. Now you can
start debugging. More info about this is available in how to analyze crash dumps.
One problem with this translation is that it relies on info generated by GCC that maps the instruction addresses to source line numbers. This usually works okay, but one notable exception is when you use inline assembly. In this case, GCC only records the last line of the inline assembly block, which might be way off if the block is large.
You can ask SYMIFY
to put the stack trace into a file (so you can
consult it later, e.g., from your editor while fixing the bug), by giving
it an output file, like this:
symify -o problem.dmp program-name
You can also save the raw stack trace (without source info) to a disk file
and submit it to SYMIFY
later, like this:
symify -i problem.dmp program-name
This comes in handy when your program grabs the screen (e.g., for some
graphics) and the stack trace can't be seen. You can then redirect the stack trace to a file, e.g., with the REDIR
program which comes with DJGPP.
But what if you didn't compile your program with -g
, and you
aren't sure how to recreate the problem which crashed it, after you
recompile? Well, you can submit the stack dump after you recompile
your program. Just press that PrintScreen key or otherwise save the stack
trace, then submit it to SYMIFY
from a file as described above,
after you've recompiled the program. Be sure to give gcc all the
compilation switches (sans -s
) that you gave it when you originally
compiled your program (in addition to -g
), including the
optimization switches, or else the addresses shown in the stack trace
might point to wrong places.
If all you have from the crash is the program counter, the eight-digit
hex number after "eip=", you can still find out the corresponding
source line using GDB. Assuming that the EIP value is NNNNNNNN
,
type this at the GDB prompt:
list *0xNNNNNNNN
Q: I'm reading/writing data files, but the data gets corrupted.
Q: My program crashes when I read data files, but the same program
on Unix works OK.
Q: When I read a file I get only a small portion of it.
Q: I'm trying to open an existing binary file for read/write using
the fstream
class, but no mater what I do, the file is always
truncated after I write to it....
Q: I cannot read anything from a binary file using the
ifstream
class, even though I use ios::binary
!!
A: Are your data files binary? The default file type in DOS is
"text", even when you use the read
and write
library
functions. Text files get their newlines converted to CR-LF
pairs on write and vice versa on read; reading in "text" mode stops at
the first ^Z
character. Reading binary files as text will
therefore corrupt the data and fail to read all the data you need. You
must tell the system that a file is binary through the b
flag in
fopen
, or O_BINARY
in open
, or use the
setmode
library function to switch the handle to binary mode (the
latter method is handy when you didn't open the file in your code, like
what happens with standard input and output).
Note that the above distinction between binary and text files is written into the ANSI/ISO C standard, so programs that rely on the Unix behavior whereby there's no such distinction, are strictly speaking not portable.
You can also use the low-level _read
and _write
library
functions which give you the direct interface to the DOS file I/O; they
always use binary I/O.
If you have problems with read/write access to binary files via the
fstream
class in C++ programs, then make sure you call the
constructor with an explicit ios::in
and/or ios::out
parameter, like this:
ifstream object_name ("file", ios::binary | ios::in);
Likewise, if you want to write binary files, you need to mention
the ios::out
flag explicitly. (This is actually a bug in all
versions of the GNU C++ iostreams library up to and including
version 2.95.)
Versions of the GNU C++ library before 2.8.1 had a bug in the GNU iostream classes. This bug caused truncation of files, even if you never write to the file. If you still use such an old version and cannot upgrade, a workaround is to do something like this:
fstream inFile; int fd = open ("foobar", O_RDWR | O_BINARY); inFile.fstream (fd);
Q: My program prompts the user to enter data from the keyboard, then
reads its response. When compiled with a 16-bit compiler like BCC or MSC
it works as expected, but with gcc the prompt doesn't show, or is printed
much later in the program.
Q: My program prints text in a loop, but the text appears on the
screen only after the loop is finished....
Q: Help! I cannot make `gotoxy' work! The text I print appears on
the screen in incorrect locations after I use `gotoxy'!
Q: Why does the text appear in the default colors even though I call
`textcolor' and `textbackground'?
A: Do you write to screen using buffered I/O (fprintf
,
fputs
and the like) functions, or send your output to the C++
cout
stream? Then what you see is the effect of the buffering of
the standard output streams. The buffer is not written to screen until
it's full, or until a newline is output, which might produce very
unpleasant and unexpected behavior when used in interactive programs.
DJGPP library functions use more aggressive buffering than 16-bit real-mode compilers, because delivering the output to the screen requires an expensive switch from protected to real mode and back. DJGPP tries to minimize the amount of these mode switches for performance reasons.
It is usually a bad idea to use buffered I/O in interactive programs; you
should instead use screen-oriented functions like cprintf
and
cputs
. If you must use buffered I/O, you should be sure that
both stdout
and stderr
are line-buffered or unbuffered (you
can change the buffering by calling the setvbuf
library
function); another solution would be to fflush
the output stream
before calling any input function, which will ensure all pending output is
written to the operating system. While this will generally work under
DOS and DJGPP, note that in some cases the operating system might
further buffer your output, so sometimes a call like fsync
would
be needed to actually cause the output be delivered to the screen.
The functions that set text attributes only affect the screen-oriented
output (a.k.a. conio) functions (cputs
, cprintf
etc.),
the text written by fprintf
and other stdio functions
doesn't change. This is unlike some 16-bit DOS compilers where
stdio
functions can also print colored text.
Q: When I copy my DJGPP application program to another PC where no
DJGPP is installed, I can't run it. It complains that it cannot find DPMI
(??). Do I really need all of your multi-megabyte installation to run
compiled programs?
A: No, you don't. You can either (a) bring the CWSDPMI.EXE
free DPMI host to the target machine and put it in the same directory as
your compiled program or somewhere along the PATH
, or (b) make
sure there's another DPMI host (such as QDPMI, 386Max, Windows, etc.)
installed on the target machine.
If your program could be run on a machine which lacks a floating-point processor, you should also distribute an emulator, or link your program with an emulator library. See floating-point emulation issues.
PMODE/DJ
is an alternative DPMI host that can be bound with your
program, so that you have a single self-sufficient executable, but
remember that PMODE/DJ
doesn't support virtual memory, so such
programs will only run on machines with enough free physical RAM.
Q: The library reference tells me that DJGPP programs can use up to
255 file handles, but my program can only use much less, about
30....
Q: I put a FILES=60
directive in my CONFIG.SYS
, but my
programs cannot use more than 42 when they run on Windows. Why is
that?
A: It's no wonder you are confused: this is one of the most complicated issues related to the DOS filesystem. I cannot discuss all the details here17, but I will try to explain at least those aspects which directly affect a typical DJGPP user.
It is true that the DJGPP library lets you open up to 255 handles--but only if the operating system allows it. The operating system further limits this number, depending on several factors.
First, if you create new handles by calling the dup
library
function (or the underlying function 45h of the DOS Interrupt 21h), you
can always have up to 255 such handles (minus the 5 that are open by the
system before the program starts), even if the FILES=
directive
sets a much smaller count. All such handles refer to the same file or
device and moving the file pointer using one handle moves all the rest
of them.
In nested programs (that is, programs that were invoked by other
programs), this is a bit more complicated. By default, any handle that
is open in the parent program is inherited by the child, unless
the parent sets the special O_NOINHERIT
bit when it opens the
file. Thus, if the parent had 10 files open when it invoked the child,
the child program will have 10 less available handles--245--to work
with, even if it only calls dup
18.
The FILES=
directive comes into play when you call open
or
any of its brethren to create handles. Unlike the handles created by
dup
, open
(and the underlying functions 3Dh or 6Ch of
Interrupt 21h) create handles that are independent of each other,
even if you open the same file over and over again. The operating
system will not let you create more such handles than the limit set by
the FILES=
directive. This is because the FILES=
directive sets the number of entries in the SFT, the System File
Table maintained by DOS, where all the information about every open
file is kept19. So, if your
CONFIG.SYS
specifies FILES=60
, you cannot open
more
than 60 files. After that, a call to open
will fail with
ENFILE
(Too many open files in system).
In practice, you won't even be able to get 60 handles if you have
FILES=60
in your CONFIG.SYS
, since several handles are
always preconnected. On plain DOS, 5 handles are already open when a
program starts. These correspond to standard input, standard output,
and standard error streams, and the other 2 handles are connected to the
AUX and PRN devices. So, if you have FILES=60
, DOS will only let
you open up to 55 independent handles. (If your program doesn't need
some of the 5 standard handles, you can close them and gain some more
handles to play with.)
The plot thickens even more if you run DJGPP programs on Windows. Since
Windows itself uses up 10-15 handles in the System Virtual Machine (VM),
it tries to make it up for the DOS programs by adding private file
tables to each DOS box with additional handles, beyond those maintained
in the system-wide SFT. The default is to add a private table with 10
handles to each DOS box, but the PerVMFiles=
entry in the
[386Enh]
section of the SYSTEM.INI
file can override that.
So on Windows, you need to consider the PerVMFiles=
setting as
well, and the resulting limit on open handles is less predictable since
the number of handles used by Windows isn't constant (for example, it
depends on how many fonts are loaded by Windows programs at any given
moment).
If you run DJGPP on Windows 3.X, and your system loads SHARE.EXE
during bootstrap, things become even more complicated. SHARE.EXE
prevents Windows from adding private file tables (because it couldn't
spy on files open via those private handles), so you get 10-15 less
handles than what the FILES=
directive says, and sometimes even
less than that. That is how somebody who has FILES=60
on their
CONFIG.SYS
could only get 42 handles on Windows. If you are
looking for reasons not to load SHARE.EXE
, here you have another
one.
Q: I upgraded my anti-virus software, and now it finds a virus in
all DJGPP programs!!
A: Relax, this is most probably a false alarm. The DJGPP stub loader, a short 2KB DOS program prepended to each DJGPP program, is optimized for size, and employs some clever tricks to make its code smaller. A few over-zealous virus scanners take some of these tricks as tell-tale signs of a virus, and report that all DJGPP programs are infected.
The truth is that DJGPP is distributed via SimTel.NET mirrors, which are known to scan all binaries for viruses before the zip files are cleared for general use. In addition, many DJGPP packages are built on Unix systems, where a DOS/Windows virus cannot survive. So it is very unlikely that a real virus would get through, and infect all of the programs on top of that. A couple of such false alarms were seen in recent years, but all of them proved to be bugs in anti-virus programs.
This chapter discusses some problems and explains some subtle points related to graphics programming under DJGPP.
Q: Why won't GRX work with my SVGA adapter in any resolution but the
standard VGA?
Q: How do I tell GRX which driver to use with my SVGA?
A: In order for GRX to work with your SVGA, you should set the
GRX20DRV
environment variable, like this:
set GRX20DRV=et4000 gw 1024 gh 768 nc 256
To set that variable, you need to know the chip-set on your adapter; refer to your SVGA documentation. Currently, GRX supports the following chip-sets:
ati28800
cl5426
et4000
mach64
stdega
stdvga
VESA
After you set the GRX20DRV
variable, run modetest.exe
to see
what modes you have available.
If your chip-set is not one of the above, try the VESA
driver
because many adapters support the VESA BIOS extensions. If yours
doesn't, try installing a VESA BIOS emulator, like UNIVBE. The latest
version of UNIVBE and related software is always available from SciTech Web site.
Q: I try to access the video memory at 0xa0000
, but my
program crashes with SIGSEGV....
Q: How can I access the text-mode video memory of my VGA?
A: Absolute addresses of memory-mapped devices are mapped
differently under DJGPP than what you might be used to under other DOS
development environments. That's because DJGPP is a protected-mode
environment, in which you can't just poke any address: that's what
protected mode is all about! To access such absolute addresses, use the
so-called "farptr" functions like _farpeekb
and
_farpokew
; they are described in the C Library reference.
See more details on using "farptr" functions to access absolute addresses in low memory, below.
For text-mode screen updates, you can use the ScreenUpdate
and
ScreenUpdateLine
library functions to quickly update the screen
from a buffer prepared in memory.
Using the _farpeekX/_farpokeX
paradigm to access memory isn't
much slower than direct access (they compile into 2 machine instructions
when optimizations are enabled). But if you need even faster access
(and don't want to write it in assembly), see using the "nearptr" access facilities, as
described below.
Some examples of how to access video memory from DJGPP programs are available in Brennan Underwood's tutorial.
Q: When I switch away from my DJGPP program under Windows 3.X, then
switch back to it, graphics mode is down, or my screen is all messed
up. Why?
Q: I cannot run my program which uses Allegro: Windows 9X says the
program would work better in DOS Mode....
Q: When running a program that uses Allegro under Windows, I cannot
switch away from it with Alt-<TAB>: instead of switching, the
PC beeps at me.
A: Windows 3.X only saves the VGA screen in standard VGA modes (1..13h) when you task-switch away from a DOS application. In any other mode it only saves/restores the video mode number, but not the actual screen contents. Your application is most likely still in the proper video mode (if not, it's probably the fault of the Windows driver for your SVGA card), but the video memory is messed up. The beauty of all this is that your program has no way of knowing that the screen has been taken away and then returned to it.
The only reasonable thing to do is to dedicate a "hotkey" in your
application (e.g., Alt-R) whose action is to redraw the entire
screen. If you do that, it's best to start all the way from the
beginning, e.g. with a call to GrSetMode
(if you use GRX), as
there are a few bad Windows video drivers which do not restore SVGA
graphics modes properly upon the switch back.
Windows 9X does save and restore the SVGA state, but only if you
task-switch with the Alt-<TAB> key. If the switch happens
because of anything else, like a window popping up, or you pressing the
Start
button, there's nothing your application can do to ensure
it restores correctly, because it just never gets moved back into
focus. As soon as the user tries to restore it, Windows 9X comes up
with this message:
This application cannot be restored and will be terminated.
If you cannot switch from a graphics program by pressing Alt-<TAB>, it usually means that some of the Windows drivers, most likely the graphics one, is faulty. Some SVGA drivers simply don't bother to implement the save- and restore-state functions which Windows needs to switch from a DOS program that uses SVGA graphics modes. The solution is to upgrade your driver, or replace the SVGA with one that is better supported.
To prevent Windows 9X from getting in your way when running graphics programs, like popping up messages that suggest to run the program in DOS Mode, just disable one or more of the relevant properties for that program. Here's a detailed procedure to disable them all:
.exe
file in the Explorer
or
in My Computer
, then click on Properties
.
Program
tab, then press the Advanced
button,
and change the advanced properties as follows:
Suggest DOS Mode as necessary
option;
DOS Mode
option;
Warn before entering DOS Mode
option;
Prevent MS DOS programs from detecting Windows
option;
DOS Mode
option.
OK
button twice.
Programs which use latest versions of Allegro should not usually trigger warning messages from Windows, so upgrade to the latest Allegro version if you keep getting such warnings.
Q: What is OpenGL? Where can I get a DJGPP-compatible version?
Q: Where can I find a version of MESA for DJGPP?
A: First, a little background. OpenGL is an abstract interface design specification for drawing individual polygons, originally designed by SGI. It is generally regarded as well-designed, relatively high-level, and easy to program. (In contrast, DirectX is low-level and notoriously hard to program, but much faster on machines without hardware acceleration.) There are many different libraries that implement the OpenGL interface.
MESA is a free implementation of OpenGL distributed under LGPL, the GNU Library License. MGL is a 2D graphics library written by SciTech, that includes a copy of MESA and is distributed under a license that is less restrictive than LGPL. (Unfortunately, SciTech failed to mention in their docs that MESA is under LGPL, and thus people who use MGL might inadvertently violate the LGPL because they don't know it applies to their code.) Latest versions of MGL are known to work on MS-DOS, Linux, OS/2, and QNX.
The DJGPP version of MESA doesn't support hardware acceleration, but it does support some 3D chipsets on other platforms, so it should be possible to support that on DOS also, given the motivation.
You can get MESA from <http://www.mesa3d.org
>. Latest versions
might not work with DJGPP out of the box, but version 2.6 is known to
have a full DJGPP support.
Version 4.5 beta 4 of MGL is available from the SciTech FTP site; it includes version 3.0 of MESA that supports DJGPP. It also includes GLUT.
This chapter deals with issues pertaining to floating-point code and floating-point emulation under DJGPP.
Q: I don't have an 80387. How do I compile and run floating point
programs?
Q: What shall I install on a target machine which lacks hardware
floating-point support?
A: Programs which use floating point computations and could be run
on machines without an 80387 should either be linked with the
libemu.a
emulation library (add -lemu
to your link command
line) or be allowed to dynamically load the emu387.dxe
emulator at
run-time if needed. Linking with libemu makes distribution simpler at a
price of adding about 20KB to the size of the program .exe
file
(the emulator functions will be used only if no hardware floating point
support is detected at runtime). You should always do one of
the above when you distribute floating-point programs.
A few users reported that the emulation won't work for them unless they explicitly tell DJGPP there is no x87 hardware, like this:
set 387=N set emu387=c:/djgpp/bin/emu387.dxe
This is probably due to some subtle bug in the emulator setup code. It is possible that it was fixed in the latest DJGPP version, so upgrade if you can. If the problem persists, please post the details to the comp.os.msdos.djgpp news group.
There is an alternative FP emulator called WMEMU
(get the file
v2misc/wmemu21b.zip
). It mimics a real
coprocessor more closely, but is larger in size and is distributed under
the GNU General Public License (which generally means you need to
distribute its source if you distribute wmemu387.dxe
, or
distribute the source or objects to your entire program, if you link it
with libwmemu.a
). Its advantage is that with WMEMU
, you
can debug FP apps on a non-FPU machine. (But you will need to get the
latest binaries of WMEMU
, since older distributions were compiled
with a beta release of DJGPP v2.0 and will cause unresolved externals if
you try linking against libwmemu.a
without recompiling it.)
Note, however, that even WMEMU
doesn't solve all the problems of
debugging FP programs on a non-FPU machine (e.g., emulating flags
doesn't work).
Q: I am experiencing inaccurate results in some floating point
calculations, sometimes in the 2nd or 3rd significant digit (like getting
118.401 instead of 120.0). This is really unacceptable! (And no, I'm
not using a buggy Pentium CPU.)
Q: I get some very inaccurate results when my program runs on a
machine lacking an FPU....
A: Are you using the emu387.dxe
emulator? If so, it might
be that the emulator isn't as accurate as you expect. Versions of the
emulator distributed with DJGPP 2.02 and earlier had a bug that affected
addition, subtraction, and comparison of floating-point numbers with
some specific bit patterns. This bug could produce inaccuracies in math
functions such as sqrt
, sin
and tan
for some
specific argument values, and even cause a program to be trapped in an
infinite loop. The emulation of the FPATAN
instruction and
functions based on it, like atan
, asin
and acos
,
also suffered loss of accuracy for some specific arguments. DJGPP v2.03
solves these problems, so upgrade and see if your problems go away.
However, even the emulator supplied with v2.03 and later suffers some
accuracy degradation when computing trigonometric functions for
arguments that are integral multiples of
Pi/2
or Pi/4
(depending on the particular function you call), and when computing
inverse trigonometric functions which should yield results that are such
multiples. So, for example, if you use 4*atan(1.)
to get the
value of
Pi
,
that might be your problem.
The reason for this accuracy degradation is that emu387.dxe
does
not store the value of
Pi
,
with extra precision, like the real FPU does, and trig functions in
libc.a
rely on such extra accuracy to deliver accurate results.
For computing the value of
Pi
,
the solution is simple: make it a constant, as God intended. The header
file <math.h>
includes the constant M_PI
which you can
use; or get the value of Pi from the net.
In many cases that involve trigonometric functions and yield inaccurate
results, linking your program with the -lm
switch might help.
This switch causes the linker to use an alternative math library,
libm.a
, which doesn't rely on x87 instructions, and thus is more
accurate when the emulator deviates from the actual x87.
The alternate emulator WMEMU
is known to be accurate to 7
significant digits for float
variables, and 15 digits for
double
s. It also much more faithfully emulates the behavior of
the x87 processor when abnormal arguments (Inf
, NaN
, etc.)
are involved. So if emu387.dxe
which comes with DJGPP v2.03
doesn't solve your problems, you might try using WMEMU
as a
solution.
Q: My program which uses floating-point math hangs on Windows DOS
box when I try to use FP emulation....
A: This is due to a bug in the emulator in DJGPP v2.02 and earlier.
The bug affected those programs running on Windows 3.X and 9X which use
the WAIT and FWAIT instructions (most of non-trivial FP
programs do), both those which use the emulator emu387.dxe
and
those linked with the emulation library using the -lemu
switch.
The bug is solved in DJGPP versions 2.03 and later, so upgrade.
This chapter discusses the debuggers you can use with DJGPP and answers some of the questions you might have when debugging DJGPP programs.
Q: How do I debug my programs?
A: First, remember to use the -g
switch when you compile and
link. This puts debugging information into your executable. When
linking, don't use the -s
switch. Here are a few examples of
compilation and link command lines when you intend to debug a program:
gcc -Wall -c -g -O myfile.c gcc -Wall -O2 -g -o myprog.exe mymain.c mysub1.c mysub2.c -lm gcc -g -o myprog myprog.o mysub.o
Note that with gcc
, you can use optimization switches when
compiling with -g
. To use stabs debugging, compile with
-gstabs3
or -gstabs+
instead of -g
. (Stabs
debugging info is more powerful than the default COFF debugging; if the
debugger doesn't seem to support some feature, or behaves strangely, try
compiling the program with -gstabs+
and see if that helps.)
Stabs debugging is especially recommended for C++ programs, since
the default format of debugging info is not powerful enough to record
all the necessary information about C++ code.
If (or when) GCC supports the dwarf2 debugging info, compile the program
with the -gdwarf2
, since it is even better than stabs,
especially with the new generation of GCC optimizations.
Then, to debug the program, use a command line like this (here for the
GDB
debugger):
gdb myprog.exe
Beginning with v2.01, DJGPP debuggers can debug both unstubbed COFF
images and DOS-style .exe
executables (v2.0 only supported COFF
files). To debug a COFF file, name it without the .exe extension, like
so:
gdb myprog
You can use one of several available debuggers with DJGPP:
GDB
,
the GNU Debugger. This is an extremely powerful source-level debugger,
but it uses a line-oriented user interface. People who are familiar
with using GDB
on Unix should know about the following important
differences in its operation on MS-DOS:
set args
or run
commands), not from the GDB
command line. Redirection of
standard input and standard output as part of the command line is
supported only in ports of GDB
v4.18 and later.
GDB
doesn't know about PC-specific keys, so you cannot use the
arrow keys for command history editing. Use ASCII control keys instead
(^F for forward character, ^B for backward character,
^P for previous line, ^N for next line, etc.).
gdb.ini
instead
of .gdbinit
, because MS-DOS doesn't allow file names with leading
dots.
GDB
uses the GNU readline
package for its input. The
readline
init file (~/.inputrc
on Unix) is called
~/_inputrc
on MS-DOS and should be in the directory pointed to by
the HOME
environment variable.
FSDB
, the full-screen debugger, from the djdev
distribution. This presents a user interface like that of Borland's
Turbo Debugger, but unlike TD, it isn't a source-level debugger
(although it will show the source code together with the machine
instructions). It also supports data-write breakpoints: a powerful
feature for hunting down code which overwrites data it shouldn't touch.
Another advantage of FSDB
is that you can easily debug programs
that grab the screen, because it can switch between the debugger screen
and the application screen. Also, it allows to examine the FPU
registers. The main disadvantage of FSDB
is that you cannot
easily examine the contents of complex data structures. Remember to
prepend an underscore _
to the names of C identifiers when you use
them with FSDB
; for C++ programs you will have to find out the
mangled names of static class variables and methods to make FSDB
understand them.
EDEBUG32
is the most basic debugger you can use with DJGPP.
You invoke any debugger like this:
<debugger-name> <program> <args...>
(except that with GDB
, you need to pass the arguments from within
the debugger).
Q: My program crashed with SIGSEGV, but I'm unsure how to begin
debugging it....
Q: Can you help me figure out all those funny numbers printed when
my program crashes?
A: Debugging should always begin with examining the message printed when the program crashes. That message includes crucial information which usually proves invaluable during debugging. So the first thing you should do is carefully save the entire message. On plain DOS, use the <PrintScreen> key to get a hard copy of the message. On Windows, use the clipboard to copy the message to a text editor or the Notepad, and save it to a file. If you can easily reproduce the crash, try running the program after redirecting the standard error stream, where the crash dump is printed, to a file, e.g. like this:
redir -e crash.txt myprog [arguments to the program go here]
(here I used the redir
program supplied with DJGPP; the -e
switch tells it to redirect the standard error stream to the named
file).
Redirecting the standard error stream to a file has an
additional advantage of printing the entire call frame traceback, even
if it is very long, whereas when writing to the screen, the DJGPP exit
code limits the number of printed stack frames so that the crash message
won't scroll off the screen.
After you've saved the crash message, look at the name of the crashed
program, usually printed on the 4th line. Knowing which program crashed
is important when one program calls another, like if you run a program
from RHIDE. Without this step, you might erroneously try to debug
the wrong program. (If the program name is garbled, or if
<??UNKNOWN??>
is printed in its stead, it means the program
crashed inside the startup code.)
The next step in the debugging is to find out where in the code did the
program crash. The SYMIFY
program will help you translate the
call frame traceback, which is the last portion of the crash message,
into a list of function names, source files and line numbers which
describe the sequence of function calls that led to the crash. The
top-most line in the call frame traceback is the place where the program
crashed, the one below it is the place that called the function which
crashed, etc. The last line will usually be in the startup code, in a
function called __crt1_startup
, but if the screen is too small to
print the entire traceback without scrolling, the traceback will be
truncated before it gets to the startup. See how to use SYMIFY
, for more details about the
call frame traceback and SYMIFY
usage.
If you compiled your program without the -g
switch, or if you
stripped the debugging symbols (e.g., using the -s
linker
switch), running SYMIFY
will just repeat the addresses instead of
translating them to function names and source file info. You will have
to rebuild the program with -g
and without -s
, before you
continue.
Next, you need to get an idea about the cause of the crash. To this end, look at the first two lines of the crash message. There you will find a description of the type of the crash, like this:
Exiting due to signal SIGSEGV Page Fault at eip=00008e89, error=0004
(the actual text in your case will be different). The following table lists common causes for each type of crash:
Page Fault
malloc
(did your code check for that?). An uninitialized pointer holds some
random garbage value; it can come from a missing call to malloc
.
The error code (error=0004
in the example above) will usually be
either 4 or 6. The former means that the program tried to read (take
a value from) the invalid address, the latter means the program tried to
write (change the stored value) there.
Sometimes, you might see a somehwat different format of a Page
Fault
message:
Page Fault cr2=10000000 at eip e75; flags=6 eax=00000030 ebx=00000000 ecx=0000000c edx=00000000 esi=0001a44a edi=00000000 ebp=00000000 esp=00002672 cs=18 ds=38 es=af fs=0 gs=0 ss=20 error=0002
This message comes from CWSDPMI, which could happen when some crucial
data structure in the low-level library code becomes trashed. The value
in cr2
is the address which caused the Page Fault
exception. In the example above, this address is 0x10000000, and since
this is exactly the base address of the DJGPP program under CWSDPMI, it
means the program dereferenced a NULL pointer.
If the message says Page Fault in RMCB
, then it usually means
that the program installed an interrupt handler or a real-mode callback
(a.k.a. RMCB), but failed to lock all the memory accessed by the
handler or functions it calls. See installing hardware interrupt handlers, for more about this. It also might mean that a program
failed to unhook some interrupt before it exited.
General Protection Fault
In this case, the GPF will be accompanied by an error code, like this:
General Protection Fault at eip=000020bc, error=0104
The error code is actually the selector that the program tried to use, in this case 104h; its low 2 bits are 00, so this is a ring-0 selector.
int
to a
function that expects a pointer to an double
, or passing buffers
to a library function without sufficient space to hold the results;
Overwriting the stack frame can usually be detected by looking at the
values of the EBP and ESP registers, printed right below the
first two lines. Normally, ESP is slightly smaller than
EBP, smaller than the limit of the SS segment, and usually
larger than EIP21; anything else is a clear sign of a stack being overrun or
overwritten. In particular, if ESP is valid, but EBP is not,
it usually means that the stack was overwritten. In some cases,
EBP's value might look like a chunk of text, like 0x33313331 (the
string 1313
, after swapping the bytes due to the fact that x86 is
a little-endian machine).
How do you know whether the values of ESP and EBP are valid? To help you, DJGPP v2.02 and later prints the valid limits of the application stack, like this:
App stack: [000afb50..0002fb50] Exceptn stack: [0002fa2c..0002daec]
(The second range of values, for the "Exceptn stack", shows the 8KB-long stack used by the library for processing hardware exceptions, because the normal application stack might be invalid when an exception happens.)
Another tell-tale sign of an overrun stack frame is that the symified traceback points to a line where the function returns, or to its closing brace. That's because, when a program overruns the stack, the return address saved there gets overwritten by a random value, and the program crashes when the offending function tries to return to an invalid address.
Suspect a stack overflow if the EBP and ESP values are close to one another, but both very low (the stack grows downwards) and outside the valid stack limits printed below the registers' dump, or if the call frame traceback includes many levels, which is a sign of a deep recursion.
Another sign of a stack overflow is when the traceback points to some
internal library structure, like __djgpp_exception_table
, or if
the SS selector is marked as invalid
in the crash message.
Stubediting the program to enlarge its stack size might solve problems
with stack overflow (but not when the stack is being
overwritten as described above). See changing stack size, for a description of how
to enlarge the stack. If you use large automatic arrays, an alternative
to stubediting is to make the array dimensions smaller, or make the
array global, or allocate it at run time using malloc
.
Note that, unlike in the cases, described above, where the stack was
overwritten, stack overflow usually manifests itself by both
ESP and EBP being invalid (outside the valid limits printed by
the crashed program).
Stack Fault
Floating Point exception
Coprocessor overrun
Overflow
Division by Zero
SIGFPE
, mean some error in floating-point
computations, like division by zero or overflow. Sometimes such errors
happen when an int
is passed to a function that expects a
float
or a double
.
Cannot continue from exception, exiting due to signal 0123
Cannot continue from exception, exiting due to signal SIGSEGV
SIGSEGV
(0123 in hex is the numeric code of
SIGSEGV
; see the header signal.h
for the other codes), and
that handler attempted to return. This is not allowed, since returning
to the locus of the exception will just trigger the same exception again
and again, so the DJGPP signal-handling machinery aborts the program
after printing this message.
If you indeed wanted SIGSEGV
to be generated in that case, the
way to solve such problems is to modify your signal handler so that it
calls either exit
or longjmp
. If SIGSEGV
should
not have been triggered, debug this as described below.
Invalid TSS in RMCB
abort
: v2.0 had a bug in its library whereby calling abort
would bypass the cleanup code that restored the keyboard interrupt
hooked by the DJGPP startup code; v2.01 solves this bug.
Using the itimer
facility in v2.01 programs can also cause such
crashes if the program exits abnormally, or doesn't disable the timer
before it exits. The exit code in DJGPP v2.02 and later makes sure the
original timer interrupt is always restored.
Double Fault
SIGINT
generation works by invalidating the DS/SS
selector, but since CWSDPR0 doesn't switch stacks on exceptions, there's
no place to put the exception frame for the exception this triggers, so
the program double faults and bails out). Otherwise, treat this as
Page Fault
.
Control-Break Pressed
Control-C Pressed
INTR key Pressed
QUIT key Pressed
SIGINT
. The QUIT key (by default,
Ctrl-<\>) generates the SIGQUIT
signal which by
default is ignored, but some programs set it to abort the program as
well.
If you are lucky, and the crash happened inside your function (as opposed to some library function), then the above info and the symified call frame traceback should almost immediately suggest where's the bug. You need to analyze the source line corresponding to the top-most EIP in the call frame traceback, and look for the variable(s) that could provide one of the reasons listed above. If you cannot figure it out by looking at the source code, run the program under a debugger until it gets to the point of the crash, then examine the variables involved in the crashed computation, to find those which trigger the problem. Finally, use the debugger to find out how did those variables come to get those buggy values.
People which are less lucky have their programs crash inside library
functions for which SYMIFY
will only print their names, since the
libraries are usually compiled without the -g
switch. You have
several possible ways to debug these cases:
SYMIFY
succeeded to convert
to a pointer to a line number in a source file. This line should be a
call to some function in some library you used to link your program.
Re-read the docs for that function and examine all the arguments you are
passing to it under a debugger, looking for variables that could cause
the particular type of crash you have on your hands, as described above.
-g
compiler switch. After re-linking the
program, cause it to crash and run SYMIFY
to get a full
description of the place where it dies.
-g
switch. Then run
your program again, and when it crashes, SYMIFY
should be able to
find the line number info for the entire traceback.
Q: How can I debug a graphics program? The debugger runs my program
fine, but when a breakpoint is hit with the screen in a graphics mode I
can't read the text printed by the debugger.
A: You can redirect the debugger output to your printer, like this:
gdb myprog > prn
This will only work if the program itself doesn't write to stdout
(graphics programs usually don't); otherwise the debugger output will
get mixed up with your program's output. If you use GDB
4.18 or
later, you can work around this by redirecting the standard output of
the debugged program to a different file or device as part of the
run
command, like this:
run > foo.out
Beginning with version 4.18, the ported GDB
writes its output to
the screen in a way that works even in graphics modes, provided that the
system BIOS knows about the specific graphics mode your program uses.
RHIDE and RHGDB
support debugging graphics programs by
switching between debugger's and program's screen, so you can use
RHIDE's built-in debugger or the stand-alone RHGDB
subset.
This support doesn't work for all video modes, but the standard VGA
modes and VESA modes are supported. If you debug with RHIDE on
Windows, switch your RHIDE session to full-screen before starting
the debug session, otherwise Windows will cause problems when RHIDE
switches between the program's graphics screen and RHIDE's own text
screen.
The FSDB debugger can switch between the application screen and the debugger screen, so you might use it, at a price of working with a low-level debugger. Press Alt-<F5> to switch between the two screens. Stock FSDB as distributed with DJGPP can only do this with text screens, but a modified version of FSDB with graphics support is available that knows about many graphics modes. The same distribution can also be found on the Oulu repository.
As yet another possibility, consider using the MSHELL
program which
will redirect I/O from any program to the monochrome monitor at the BIOS
level, so you can use it even with GDB. MSHELL
was written by DJ Delorie
and is available from DJ's server. Be sure that you don't
have some other TSR installed that catches screen writes and bypasses
the BIOS functions, or else MSHELL
won't help you. For example,
changing the code page (with the DOS CHCP
or MODE
commands) might do this.
RHIDE also supports dual-monitor systems for debugging, it allows you to use the monochrome monitor for interface with the debugger, while leaving the color screen for your program's display, with no need to swap between them.
If you have any problems with dual-monitor support, in particular with
RHIDE, make sure your memory manager doesn't grab the B000
segment for its own purposes. This region should be available for the
mono adapter, or your system might crash when you try using it.
Another way to redirect the output of a program to a monochrome monitor is by using the MDA display driver from BinaryInfosys. It is a true DOS device driver, and so can be opened as a file--handy for sending debug info, for example. This driver is free and is available from BinaryInfosys' home page.
.cc
sourceQ: When I try to debug my C++ programs, the debugger claims it can't
find the source file:
file.cc: No such file or directory.
The source file is there, but it's called
file.cpp
, not file.cc.
Why does this happen?
A: It's a bug in GCC 2.7.2.1 and earlier. It erroneously assumes
that a C++ source always has a .cc
extension. If you are using
GCC 2.7.2.1 or earlier, you'd better call your C++ files *.cc
.
If this is unacceptable, you can work around this bug by invoking
cc1plus
and the assembler pass manually. The bug in GCC
manifests itself in that cc1plus
is called with the option
-dumpbase file.cc.
If you replace this with
-dumpbase file.cpp
(or whatever your extension is), the
debugger will happily find your sources.
GCC 2.8.0 and later corrects this bug, so upgrading is also a solution.
Q: It seems that GDB doesn't recognize C++ class members by their
original, unmangled names. Do I really need to figure out the mangled
names of all my class variables and methods to be able to debug them?
A: No, you don't. GDB does allow you to use the original
names, it's just that it usually treats the ::
in their names as
word delimiters. Include the name of the method or a class static
variable in single quotes, and GDB will recognize them as a single word.
For example, if your class CMPForward
has a method named
go
which you need to put a breakpoint in, use the following
command:
breakpoint 'CMPForward::go'
Other GDB
features that might be useful in this context are the
various demangling options, like set print demangle
, set
demangle-style
etc.; look them up in the GDB on-line docs.
However, there are some cases where you won't be able to get GDB to
demangle C++ function names no matter how hard you try. This is due
to a lack of sufficient debugging information in the COFF debug data
format. There's simply not enough info there for GDB to detect the
source language and support some C++-specific features. So, in some
case, you will need to use mangled names. If you need a
description of the GNU style of mangling C++ names (so you could
mangle them yourself), look in the GDB or Libg++ source distribution, in
the libiberty directory, for a file named cplus-demangle.c
. You
can also use the cxxfilt
utility, supplied as part of the GNU
Binutils package, to demangle the names and verify that your mangling is
correct. Note that, as the debugger built into RHIDE uses GDB
code, it will also sometimes have such problems with debugging C++
programs.
If you really need full C++ support in DJGPP, you will have to use
the stabs debugging support. GCC 2.8.0 and later are distributed with
built-in stabs support, so, if you need this, upgrade and compile your
C++ programs with -gstabs+
. Caveat emptor: FSDB
,
EDEBUG32
and SYMIFY
don't understand the stabs format,
so you will have to compile with -gcoff
option to use these
utilities (RHIDE distribution includes a utility called
gsymify
that can be used instead of SYMIFY
with stabs
debugging info).
Q: My source file #include's another source file, but I cannot set a
breakpoint in that included code, because GDB says there is no such
line, or no such source file....
Q: I cannot debug code produced by Flex, or Bison, or F2C, because
GDB somehow messes up all the source file and line number info!
Q: Why can't I step with a debugger into an inline function defined
in a header file?
Q: Why can't I trace into a function defined in an #include
d
source file?
A: This is a genuine limitation of the COFF format used by DJGPP.
It can only handle a single source file for a given object file. It
does include correct line numbers, but the name of the source file is
wrong, so setting breakpoints in such files or tracing into functions
defined in such files just doesn't work. Using stabs debugging info
(see the previous section) doesn't have this limitation, so upgrade to
GCC 2.8.0 or later and recompile your program with the -gstabs+
switch.
For source files that include other source files, you can work around
this even with COFF debugging, by just inserting the included source
with your editor while you debug the program. For code produced by
other programs, like F2C
or Bison
, Duncan Murdoch suggests a work-around: to copy the original
source file (.y
, .f
, etc.) over the generated C file. For
example, here's how you should go about debugging a Fortran program
myprog.f
using GCC
, F2C
and GDB
:
f2c
with the -g
option:
f2c -g myprog.f
gcc
with the -g
option:
gcc -g myprog.c -o myprog.exe -lf2c -lm
copy myprog.f myprog.c
gdb myprog.exe
Q: Why can't I set or display the values of some static variables in
my program?
A: This seems to be a limitation of the COFF debugging information
emitted by GCC by default: the debuggers cannot display or set the value
of an uninitialized static variables (those who are in the .bss
section of the program). A work-around is to initialize these
variables, which causes the linker to put them into the .data
section. Another solution is to use the stabs debugging support; latest
versions of GCC include this support, so upgrade and use
-gstabs+
instead of -g
.
Q: How can I watch a bool
variable with RHIDE or GDB?
When I try, the debugger always displays void
....
A: With the default COFF debugging format, you can't: it doesn't
support the bool
data type. You have to switch to stabs
debugging format; see switching to stabs, for details.
Q: I cannot display in GDB the values of my variables of type
__complex__....
A: Current versions of GDB don't support __complex__
variables. A work-around is to manually cast them to a pair of
numbers. For example, to access the real and imaginary part of a
variable foo
declared __complex__ double
, do this:
(gdb) print *(double *)&foo $1 = 4 (gdb) print *((double *)&foo + 1) $2 = 6
Q: I cannot debug Emacs (or any program that requests raw keyboard
input): when I press Ctrl-C, any debugger I tried reported SIGINT. But
I cannot operate the debugged program without Ctrl-C (in Emacs, it's
necessary to exit the editor)!
Q: I cannot debug any program which catches signals!!??
Q: I compiled my program with -pg
switch, and now I cannot
debug it....
Q: When my program hits a breakpoint in GDB, the debugger reports
SIGSEGV, but only under Windows....
A: Versions of DJGPP before v2.03 had a few grave limitations in
debugging programs which use interrupts or exceptions. Programs
compiled for profiling would crash under a debugger with SIGSEGV or a
GPF, with no addresses that symify
can identify; programs using
alarm
or setitimer
couldn't be debugged, either. You
couldn't hook the keyboard interrupt in a debugged program, and you
couldn't debug a program which uses floating point on a machine without
FP hardware (unless you use WMEMU
as your emulator, but even
WMEMU
doesn't solve all the problems). The reason for all these
problems was that any exceptions or signals that happen when your
program runs under a debugger would be caught by the debugger instead,
they won't get passed to the debuggee, and would usually terminate the
debuggee.
This is no more the case. DJGPP v2.03 and later have a much better
debug support, so all of the problems mentioned above are gone. The
DJGPP port of GDB 4.18, released in August 1999, is based on the
debugging support from DJGPP v2.03 and thus doesn't have most of these
problems anymore. Latest versions of RHIDE also use this improved
debugging support, as do the versions of edebug32
and
fsdb
from DJGPP v2.03 or later.
Some problems still remain, though, even in v2.03: if you use the stock
emu387.dxe
FP emulator while debugging floating-point programs or
debug programs that call alarm
or setitimer
library
functions, the program will sometimes crash with SIGSEGV
. This
is likely to change in the future.
Beginning with version 1.1, the debugger built into RHIDE supports debugging programs that hook keyboard and/or timer hardware interrupts, so if you need e.g. to debug programs built with the Allegro library or programs compiled for profiling, you can use RHIDE.
Another known problem is that GDB GP Faults when the program hits a
breakpoint under Windows 3.X (Windows 9X doesn't have this problem).
This is because the breakpoint instruction causes a software interrupt
(as opposed to an exception) under Windows 3.X, and the DJGPP debug
support currently only catches debug exceptions. The only work-around
is to use the hardware breakpoints (which use the special debug
registers of the i386 and higher CPUs, and which do work with DJGPP on
Windows 3), and never have more than 4 of them active at the same time.
FSDB
will automatically use the hardware breakpoints for the
first 4 breakpoints (so it works on Windows 3.X unless you set more than
4 breakpoints simultaneously), but with GDB, you will have to explicitly
use the hbreak
and thbreak
(instead of break
and
tbreak
) commands which set hardware breakpoints. This works with
DJGPP ports of GDB 4.16 and later. Note that GDB and FSDB use ordinary
breakpoints to implement single-stepping with the step
,
next
, <F7>, <F8> and similar commands, so you can't use
these on Windows 3.X; use temporary hardware breakpoints instead. The
above is also true for watchpoints (which watch for variables to change
value): you need to use hardware watchpoints with GDB (the total number
of hardware breakpoints and watchpoints cannot exceed 4). Same
considerations apply to the debugger built into RHIDE under Windows
3.X.
This chapter explains how to optimize your program for speed using the profiler, and discusses some problems you might have with it.
Q: How can I profile my program to see where it spends most of its
run time?
A: DJGPP includes a profiling facility. To use it, compile and link
with -pg
option, run your program as you usually would, then run a
program called gprof
:
gprof myprog.exe
(change myprog.exe
to whatever name your program is). This will
print an execution profile. You can now look at the profile and try to
optimize the functions which take a large proportion of the execution
time.
Gprof
is further documented in the Binutils docs as part of the
GNU Binutils distribution.
Q: I cannot profile my program: when I compile it with -pg, it
crashes or wedges my machine!
Q: When I compile my program with -pg, it runs much slower. Does
the profiling code have such a huge overhead?
Q: I profiled my program, but the profile contains an entry
_mono_putc
which I don't use, and which eats up about 70% of
execution time!
Q: When I run a profiled program on my dual (VGA+MDA) display
system, the mono screen shows loads of meaningless numbers. Is there a
way to stop this behavior?
A: DJGPP v2.01 has a bug in one of its library functions which is
linked into your program when it is compiled with the -pg
option.
The bug is that the profiled program tries to write to the secondary
mono screen, which caused the profiled programs to crash in many
environments, in particular when a memory manager remaps some of the
high memory. On systems which actually have the additional mono
display, the profiled programs won't crash, but would run significantly
slower and print debugging info on the mono display.
A patch which corrects this bug was posted to the DJGPP News group; you
can find it by searching the DJGPP mail archives. DJGPP v2.02 and later
includes a fixed version of the offending function, so upgrade to the
latest version. A work-around is to run the program compiled with
-pg
on vanilla DOS configuration (no memory managers such as
EMM386 or QEMM, and no Windows). However, when you use this
work-around, your program might run much slower, although the profile
that you get should not be affected.
Q: Whenever I compile my programs with -pg
, the profile
produced by Gprof
shows that 100% of the run time is spent in
a single function, and the rest of the code gets 0% of time. Huh??
A: This is due to a bug in the library shipped with DJGPP v2.02:
the module which handles timers works incorrectly. (The same bug is
responsible for problems with library functions setitimer
and
alarm
.) The solution is to upgrade to DJGPP v2.03 where these
bugs are solved. Relink your program with the v2.03 library, then rerun
it, and Gprof
will show reasonable results.
__dpmi_int
so heavily used?Q: I've profiled my program and found that the routine which takes
60% of the running time is some obscure library function called
__dpmi_int
. Can't you guys get your library right?
Q: What is the __dj_movedata
function for, and why does it
take such a large proportion of my program's running time?
A: Does your program use I/O or other real-mode services (like BIOS)
extensively? All those services are invoked through a DPMI function call
which is issued by __dpmi_int
. The sibling function
__dj_movedata
moves data between the transfer buffer
(see what is the transfer buffer) and your program, e.g., when it reads or writes disk files.
So what the profile really says is that the running time of your program
is governed by time-consuming operations such as disk I/O.
gprof
doesn't produce outputQ: Every time I run the profiler it says "gmon.out: no such file or
directory" and no profile is produced. What is this gmon.out
file, and why won't gprof
compute the profile?
A: gmon.out
is the file with raw execution counts and timing
info that gprof
needs to produce the profile. The file is
written by the profiled program when it exits. If the file isn't
created, it might be because of one of the following reasons:
-pg
switch.
Note that both compilation and link need to be done with
-pg
, because the functions that actually write the results to
gmon.out
are only linked in when GCC sees -pg
on the link
command line.
ld.exe
directly to link your program and forgot
to mention the files and switches necessary for the profiled program
operation. You should use gcc
to link your program instead of
calling the linker directly; a -pg
switch to gcc
is all
that it takes to make sure that the linker will get all the necessary
arguments22.
gmon.out
is registered with the atexit
library function,
and won't be called if the program was terminated in an abnormal way.
Make sure that your program exits with a call to exit
library
function or with a return
statement in your main
function.
For example, if your program dies with an exception or a signal, you
need to install a handler for that signal and make it call exit
.
This chapter deals with issues pertinent to run-time performance of DJGPP programs.
Q: How does DJGPP compare with other DOS-based C compilers in terms of
efficiency of generated code?
Q: Won't my program run much slower when compiled by
DJGPP, due to all those CPU cycles wasted in switches between protected
and real mode?
A: The quality of code generated by GCC with optimization turned on
(-O2
switch to the compiler) is generally at least as good as what
you will get from top commercial products, like Borland, Microsoft and
Watcom. Mode switches indeed impose a certain performance hit, but in
most programs it is negligibly small, because only DOS and BIOS services
require such a switch, and the majority of programs spend most of their
time doing other things.
Up until version 2.95, MSVC was the only one of the commercial compilers that used to produce code which was better than what GCC generated (by about 25% on the average), when run on a Pentium. However, with the much-improved optimization technology that is now part of GCC 2.95 and later, this gap is all but closed. More details about this are available on the compiler comparison page, maintained by Salvador Eduardo Tropea.
Q: I switched to v2 and my programs now run slower than when
compiled with v1.x....
Q: I timed a test program and it seems that GCC 2.8.1 produces
slower executables than GCC 2.7.2.1 was, which in turn was slower than
DJGPP v1.x. Why are we giving up so much speed as we get newer
versions?
Q: I installed Binutils 2.8.1, and my programs are now much slower
than when they are linked with Binutils 2.7!
A: In general, newer versions of GCC generate tighter, faster code,
than older versions. Comparison between different versions of GCC shows
that they all optimize reasonably well, but it takes a different
combination of the optimization-related options to achieve the greatest
speed in each compiler version. The default optimization options can
also change; for example, --force-mem
is switched on by
-O2
in 2.7.2.1; it wasn't before. GCC offers a plethora of
optimization options which might make your code faster or slower (see
the GCC docs for a complete list); the best way to find the correct
combination for a given program is to profile and experiment. Here are
some tips:
-O2 -mpentium
-fomit-frame-pointer -ffast-math
. (For PGCC and GCC version 2.95 and
later, use -O6
instead of -O2
.)
-S
(see getting assembly listing),
and examine the machine code.
-fforce-addr
option. This option
helps a lot if a couple of pointers are used heavily within a single
loop. If there are a lot of memory references, try adding
-fno-force-mem
, to prevent GCC from repeatedly copying variables
from memory into registers.
-fomit-frame-pointer
might make things worse, since it
uses stack-relative addresses which have longer encoding and could
therefore overflow the CPU cache. So try with and without this switch.
-mpreferred-stack-boundary=2
compiler option. This causes
the compiler to relax its stack-alignment requirements that need a lot
of sub esp,xx
instructions. The default stack alignment is 16
bytes, unless overridden by -mpreferred-stack-boundary
. The
argument to this option is the power of 2 used for alignment, so 2 means
4-byte alignment; if your code uses double
and long double
variables, an argument of 3 might be a better choice.
-malign-loops
), jumps
(-malign-jumps
), and function entry points
(-malign-functions
). Alignment changes can have especially
profound effects when programs are run on AMD's K6 CPU, since these CPUs
suffer significant slowdown for code aligned on 4-byte boundaries.
-funroll-loops
and -funroll-all-loops
and
profile the effect.
-fno-strength-reduce
. In some cases where GCC
is in dire need of registers, this could be a substantial win, since
strength reduction typically results in using additional registers to
replace multiplication with addition.
I'm told that the PGCC version of GCC has bugs in its optimizer which
show when you use level 7 or higher. Until that is solved in some
future version, you are advised to stick to -O6
. Some
programs actually run faster when compiled with -O2
or
-O3
, even when compiled with PGCC, so you might try that as
well. Several users reported that PGCC v2.95.1 tends to crash a lot
during compilation, especially with -O5
, -O6
and
-mpentium
options. (In general, PGCC version 2.95 is deemed
buggy; you are advised not to use it.)
Programs which manipulate multi-dimensional arrays inside their innermost loops can sometimes gain speed by switching from dynamically allocated arrays to static ones. This can speed up code because the size of a static array is known to GCC at compile time, which allows it to avoid dedicating a CPU register to computing offsets. This register is then available for general-purpose use.
Another problem that is related to C++ programs which manipulate
arrays happens when you fail to qualify the methods used for array
manipulation as inline
. Each method or function that wasn't
declared inline
will not be inlined by GCC, and will incur
an overhead of a function call at run time.
However, inlining only helps with small functions/methods; large inlined functions will overflow the CPU cache and typically slow down the code instead of speeding it up.
If your CPU is AMD's K6, try upgrading to GCC 2.96 or later and use the
-mcpu=k6
switch. I'm told that K6-specific optimizations are
much better in these versions of GCC.
A bug in the startup code distributed with DJGPP versions before v2.02 can also be a reason for slow-down. The problem is that the runtime stack of DJGPP programs was not guaranteed to be properly aligned. This usually only shows up on Windows (since CWSDPMI aligns the stack on its own), and even then only sometimes. But it has been reported that switching to Binutils 2.8.1 sometimes causes such slow-down, and switching to PGCC can reveal this problem as well. In some cases, restarting Windows would cause programs run at normal speed again. If you experience such problems too much, upgrade to v2.02.
Q: Does DJGPP support Pentium-specific optimizations?
Q: I run the same program on a 486 and on a Pentium, and it's slower
on a Pentium!!
A: Beginning with version 2.95, GCC includes Pentium-specific
optimizations. Be sure to use the -mcpu=pentium
switch when
you optimize for Pentium or better CPUs.
A program might sometimes run slower on a Pentium due to alignment problems in DJGPP. GCC makes assumptions about how GAS (the assembler) handles alignment, but GAS from Binutils 2.8.1 and earlier was configured to treat alignment in a way that's different from what GCC assumes. The outcome of this is that longs are word-aligned, doubles are dword-aligned, etc. Depending on the DJGPP version, link order, library differences, you might get lucky (or unlucky) with a 50/50 chance to get an improper alignment. Different CPUs have different penalties for unaligned accesses, which may explain differences in speed.
DJGPP v2.01 had a bug in the startup code, whereby the runtime stack isn't aligned; this could also be a reason for slow-down, especially in programs compiled for Pentium.
You might consider adding some slack static variables to induce changes in alignment; if any of the changes suddenly cause a significant change in the runtime performance, then alignment might be the reason.
These alignment problems were finally solved in the DJGPP ports of Binutils 2.9.1 and GCC 2.95; so if you want to get rid of these problems, upgrade.
Q: I measured the time required to read a 2 MByte file in DJGPP and in
Borland C. It took the DJGPP program 2.5 seconds to do it, while Borland
did it in just under 2. This is horribly slow: it's 25% slower
than Borland!
Q: I tried to improve DJGPP I/O throughput by defining a 64KB-large
buffer for buffered I/O with a call to setvbuf
, but that had no
effect. Why is that?
Q: It is obvious that disk-bound programs compiled with DJGPP will
run awfully slow, since FAT is such a lousy filesystem!
A: First, I would like to point out that waiting another 0.5sec for reading a 2 MByte file isn't that bad: it is indeed about 25% longer than you can do under DOS, but it's only half a second.... Besides, most programs read and write files which are only a few hundreds of kilobytes, and those will suffer only a negligible slow-down.
Doing I/O from protected-mode programs requires that low-level library functions move the data between the extended memory and low memory under the 1 MByte mark, where real-mode DOS can get at it. That area in the low memory is called the transfer buffer23. This data shuffling means that some I/O speed degradation is inevitable in any protected-mode program which runs on top of DOS (including, for example, Windows programs when Windows 3.X is set to 386-Enhanced mode).
By default, DJGPP moves data in chunks of 16 KB, so defining a buffer
larger than that won't gain anything. The size of the transfer buffer
is customizable up to a maximum of 64 KB24, so if your program really reads a lot of large files, you might
be better off enlarging it (with the STUBEDIT
program).
The DJGPP buffered I/O functions utilize a special algorithm to optimize
both sequential and random reads. These two usually contradict, since
sequential reads favor larger buffers, while random access favors small
buffers. DJGPP solves this contradiction by doubling the buffer size on
each sequential read, up to the size of the transfer buffer, and
resetting the buffer size back to the minimum of 512 bytes each time the
program calls fseek
. Experience shows that programs which use
both sequential and random access to files, like ld.exe
, the
linker, run significantly faster when linked with these optimized I/O
functions (introduced with version 2.02 of DJGPP).
Some people think that FAT is such a lousy filesystem, that programs which do a lot of disk I/O must run terribly slow when compiled with DJGPP. This is a common misconception. The speed of disk I/O is determined primarily by how efficient is the code in the operating system kernel that handles the filesystem, and the device drivers for the I/O-related devices like the hard disk, not by the disk layout. It is true that DOS and BIOS don't implement I/O too efficiently (they use too many tight loops waiting for low-level I/O to complete), but a large disk cache can help them tremendously. In addition, Windows 9X bypasses DOS and BIOS I/O code entirely, and uses much more efficient protected-mode code instead. Experience shows that DJGPP programs on plain DOS systems with a large (8MB and up) disk cache installed run about 30% slower than a Linux system on the same machine; and Windows 9X will run the same programs at roughly the same speed as Linux. If you get much slower performance on DOS/Windows, chances are that your system is not configured optimally.
Some programs which only copy data between two files might gain significantly if you write your custom low-level I/O functions that avoid moving data to extended memory (only to move them back to the transfer buffer). However, these cases are rare.
Q: How come my program, which I ported from Borland/MS C and which
doesn't use much I/O, still runs much slower under DJGPP?
A: Explore the following possible causes for this:
-O2
to produce optimized code.
If your program spends most of its time in a certain innermost loop, you
should try enabling some of the optimization options which aren't
enabled by -O2
. Some of these are described in this FAQ, see
speed-related optimization options.
You can tell how much your program switches to real mode by profiling your
program. In the profile, look at the proportion of time your program
spends in low-level library functions called __dpmi_int
(which
calls real-mode DOS/BIOS services) and __dj_movedata
(which moves
data between the transfer buffer and your program). If this proportion is
large, try rewriting your program to minimize use of those functions which
require a mode switch, even at a price of more computation (a mode switch
usually eats up hundreds of CPU cycles).
Sometimes, some device driver that uses extended memory takes up a significant portion of it, and leaves less for DJGPP programs, which then begin to page and slow down. For example, Novell Netware's VLM redirector and client software can use up to 0.5 MB of extended memory, even if you don't log into the network. A solution is not to load such resident software, or to buy more memory.
__djgpp_exception_processor
is high on the execution
profile printed by Gprof
. Due to the way FP emulation is
implemented in DJGPP25, it might
be significantly slower than the way real-mode DOS compilers handle it.
The solution is either to rewrite your code so that it doesn't use
floating-point code in its inner loops, or buy an FPU.
Gprof
profiler. If you find this to be
the problem, write your own, optimized versions of those functions.
It's best to write them as inline assembly functions, for maximum
performance. If you find library functions which are inefficient,
please inform the DJGPP news group by posting to
the comp.os.msdos.djgpp news group, so this could be fixed by people who
maintain the library.
-pg
switch), the slow-down might be due to a bug in the DJGPP library.
See slow-down in profiled programs, for more about this.
This chapter answers questions which are related to DJGPP run-time memory allocation.
Q: How much virtual memory can I use in DJGPP programs?
A: That depends on the DPMI host you are using. The latest version
r5 of CWSDPMI (the free DPMI host which comes with DJGPP) lets you use
all the available extended memory, plus the available hard disk storage
up to a total of 2GB. (Version r4 of CWSDPMI supports up to 256MB of
physical memory and up to 256MB of disk space, for a grand total of
512MB of virtual memory, but has bugs when the total memory is more than
255MB.). Try a malloc(50*1024*1024)
some day.
With other DPMI hosts, your mileage may vary. Quarterdeck's QDPMI, for
instance, has a bug in some of its versions which effectively disables
virtual memory under DJGPP (described in QDPMI VM bug, below), so you
only have whatever free physical RAM is left. On Windows 3.X, the
amount of virtual memory you get depends on various virtual memory
settings in the Control Panel and on the .pif
file settings for
the program you run (see Windows allocation subtleties, below). On
Windows 9X, the memory settings of the DOS Box Property Sheets define how
much virtual memory a DJGPP program will get (see Windows 9X allocation details, below). OS/2 reportedly can be configured to support up to 512MB
of DPMI memory.
malloc
/free
don't affect virtual memory...Q: I did malloc(50*1024*1024)
, but didn't see any paging
happen, and I only have 8 MBytes of RAM on my machine. Is this virtual
memory thing for real?
Q: I malloc
'ed a large chunk of memory, but when I check
values returned by _go32_remaining_physical_memory
or
__dpmi_get_memory_information
, I don't see any change!
Q: When I free
allocated RAM,
_go32_remaining_physical_memory
reports there was no change in the
available RAM....
Q: I'm looking for a way to tell how much memory is available,
something like coreleft
in Borland C?
A: CWSDPMI (and, possibly, other DPMI hosts) only pages in memory
when it is actually accessed. If you only malloc
it, but don't
actually access it, it won't grab those pages. Try calloc
and
see the big difference.
When you call free
, DJGPP library doesn't return memory to the
system, it just adds it to its internal pool of free pages. So, from the
point of view of the DPMI server, these pages are not "free".
In addition, several widely-used DPMI servers, such as those built into
Windows, have their own quirks related to memory allocation. For
example, some of them won't let you allocate more than half the
available memory in a single chunk. As another example, on OS/2
_go32_remaining_physical_memory
reports a constant very large
value that doesn't change in the course of the program.
Note that the distinction between the physical and virtual memory is only meaningful on DOS. More sophisticated operating systems usually conceal the difference entirely, so only the sum of these two types of memory usually (but not always) gives an approximation of the total free memory.
Because of these peculiarities, there's no convenient and easy way to
return the amount of free memory available at any given moment, even in
plain DOS. Some programs only care about available physical RAM (they
don't want to page to disk, since that causes a considerable slow-down);
for these, I recommend to call the
_go32_remaining_physical_memory
library function at program
startup, and then track memory usage with sbrk(0);
.
Alternatively, disabling virtual memory altogether (by using CWSDPR0 or
by loading CWSDPMI with -s-
parameter), and checking values
returned by malloc
against NULL
, might be all you need to
know when you are about to run out of free physical memory. Programs
that need to know when they are about to run out of virtual
memory should call _go32_remaining_virtual_memory
instead. Once
again, these methods will only work reasonably well in plain DOS.
Q: When I try to access more memory than the free physical RAM,
malloc
returns a NULL
pointer, or I get some cryptic error
message, like "Memory Paging Violation" or "Unrecoverable Exception:
000Eh".
A: This is typical of Quarterdeck's DPMI host called QDPMI which comes with QEMM386 version 7.53 and earlier. Some versions of QDPMI (those which come with QEMM v6.x) fail to resize memory blocks when the new size is more than the available physical RAM, even though virtual memory services are enabled; other versions (those which come with QEMM v7.x) just don't let you allocate more memory than is physically available. If you must use more RAM than is physically available, disable QDPMI by going to the QEMM directory and typing this:
qdpmi off
DJGPP programs will then use CWSDPMI instead.
This bug was corrected in QDPMI version 1.10 or later, distributed with
QEMM beginning with version 8.0, so upgrading to the latest version of
QEMM might also be a solution. With QEMM 6.x, make sure your programs
don't set _crt0_startup_flags
to _CRT0_FLAG_UNIX_SBRK
,
which overrides the default type of sbrk
(QEMM 8.0 and later can
allocate virtual memory with both types of sbrk
algorithm).
If you use another DPMI host, make sure that virtual memory is enabled.
E.g., for 386Max, include the swapfile=
parameter to establish a
virtual memory swap file; you can make it permanent (this will speed up
DJGPP start-up) with the /p
option.
Q: OK, I've changed my program to never allocate more memory than is
physically available, to work around that QDPMI VM bug, described in
previous section, but my program still gets a NULL
pointer
from malloc/calloc
!
Q: Why is my program dying with SIGSEGV under CWSDPMI when
allocating a chunk of memory?
A: Another peculiarity of QDPMI which came with QEMM before version 8.0: it will never let you allocate a chunk which is larger than half of what's available. Windows 3.X behaves in the same way, and several people reported the same to be true under Windows 9X.
If your program asks for memory in lots of small allocations, then it might crash when you use CWSDPMI as your DPMI host. This is because CWSDPMI runs out of its tables, allocated in the heap, where it tracks memory allocations. Beginning with release 2, CWSDPMI defines a 6KB-large default heap that is configurable by CWSPARAM program to be anywhere between 3K and 40K bytes, without recompiling CWSDPMI. The default heap size is enough for about 21MBytes in small chunks. You should upgrade to the latest CWSDPMI if you experience such problems, and bump up its heap size as needed.
Q: I'm running in Windows 3.X DOS box, but DJGPP complains about
there not being enough DPMI memory, although virtual memory is enabled.
A: You must make sure the size of your Windows swap file can be at least 2 times the largest virtual memory size you need. Check if you have enough free disk space; if you do, run a defragger (Windows needs the swap file to be contiguous). The size of the swap file is normally limited by the "virtual = 4 times free physical" rule, but you can change that by inserting the line
PageOverCommit=n
in the [386Enh]
section of your SYSTEM.INI
file. The
parameter n
is 4 by default, but can be set to be as large as 20.
Q: I seem to be unable to get more than 16 MBytes of virtual memory
under Windows 9X, even though I have 32 MBytes of RAM installed on my
machine, and a lot of disk space....
A: If your machine has 64MB or less of main memory, you must set the
maximum amount of DPMI memory to 65535K in the DOS box Property Sheets.
Click on the Properties
button of the DOS box toolbar, then click
on the Memory
tab, and type the number 65535
in the box
under DOS Protected Mode Memory
. If you leave that setting at
the default "Auto", your programs are at Windows' mercy, and in many
cases will get only 16 MBytes. You must actually type 65535 inside the
dialog box, as checking out the values from the list Windows offers will
never get you past 16384 (i.e., 16MB).
Machines that have more than 64MB of physical memory should always leave the DPMI memory setting at "Auto", since the manual setting cannot be larger than 65535K. Setting the DPMI memory property to "Auto" usually leaves the DOS box with whatever is physically installed, minus about 5MB, assuming the disk used by the Windows swap file has enough free space.
Some users report that they need to edit their EMM386
command line
in the CONFIG.SYS
file to say this:
DEVICE=C:\WINDOWS\EMM386.EXE NOEMS L=131072
The L=NNN
parameter (here for 128MB of installed memory) forces
EMM386
to use all of the installed memory. (This should work by
default with Windows 9X, but if it doesn't, try the above line.)
Note that you cannot allocate more than half the available memory in one chunk under Windows 9X, exactly as the things are under Windows 3.X, and you cannot have more than 64 MBytes of virtual memory available to DJGPP programs running on Windows, unless you have more than 64MB physical memory installed.
Q: My machine has 48 MBytes of RAM, but when I run DJGPP programs,
they start paging after 32 MBytes have been used....
Q: I have 5 MBytes of free RAM on my machine, but DJGPP programs start
paging after only 256KBytes of memory were used??
A: This might be caused by some old versions of the memory manager installed in your machine (like HIMEM or EMM386 from an old version of DOS), which were limited to 32 MBytes of extended memory. Try running without them (CWSDPMI can use raw extended memory), or upgrade to a newer version of DOS.
If your programs start paging after only 256KBytes of memory were used,
most probably you are using EMM386 and CWSDPMI, and your
CONFIG.SYS
specifies no amount of memory when it installs EMM386.
Some old versions of EMM386 default to 256K in this case; you should
tell EMM386 explicitly how much memory it should take over. You can use
the go32-v2
program to see what amount of extended memory your
DJGPP programs will get.
Q: How much memory is available when I invoke other programs from my
DJGPP program?
A: In the conventional (below 640K mark) memory, you are left with
everything which was free before your program started, except what the
DPMI host uses. The amount of conventional memory required by the DPMI
host depends heavily on the host you use. For the first DJGPP program,
CWSDPMI uses about 130KB (including 41KB to load CWSDPMI itself), QDPMI
uses about 55KB, and Windows only 18 KBytes. Each subsidiary call to
system
or spawn
(like in recursive invocation of
Make
) eats up about 18K (16K for the transfer buffer and 2K for
the PSP and environment) for most DPMI servers; a notable exception is
QDPMI which needs 104K bytes of low memory for the subsequent calls. If
you change the size of the transfer buffer (with STUBEDIT
), the
amount of free conventional RAM left when shelling out of it will change
accordingly.
In addition, most DPMI servers (with the notable exception of Windows) take up 16KB of expanded memory when they first load.
Extended memory management for the spawned programs is left to
the DPMI server; DJGPP does nothing special about XMS when system
or spawn
is called. This means that all the extended memory
used by the parent program is not freed when the child program
starts; if the child requests more memory than is physically free, the
DPMI server is expected to page some of the parent out to honor the
request. (This is unlike DJGPP v1.x, where the go32
extender
would completely page out the parent before starting the child.) The
advantage of this is that spawning a child or shelling out is much
faster in v2 than it used to be with v1.x, except on machines with low
amounts of installed RAM. A disadvantage is that if you spawn a
real-mode program that uses XMS, the extended memory used up by your
DJGPP program will be unavailable to it, unless you use a memory manager
(as opposed to when CWSDPMI uses raw XMS or HIMEM).
Note that if you use a memory manager such as EMM386 or QEMM386 with the NOEMS and NOVCPI parameters, CWSDPMI will use the XMS (as opposed to VCPI) services to allocate extended memory, and will allocate all of the available XMS memory for itself. So if, while your DJGPP program runs, some resident software such as device driver or TSR will try to allocate XMS, the allocation will fail.
Q: My program bombs when I use very large automatic arrays.
Q: How much stack space do I have in my program?
Q: My program seems to overflow the stack, but only when I run it
under a debugger....
Q: My program crashes with SIGSEGV, but the traceback makes no
sense: it points to something called ___djgpp_exception_table...
When I try to debug this, the traceback mysteriously changes to some
innocent library function, like getc(). What is going on??
A: DJGPP v2 programs get fixed-size stack which is allocated by the
startup code and then stays fixed for the entire lifetime of the
program; this is due to a bug/feature of the DPMI 0.9
specification26. By default,
you have a 512KB-long stack (DJGPP v2.01 and earlier used 256KB stack),
but some programs which use large automatic arrays, or are deeply
recursive, might need more. If the default stack size is not enough,
you can change it with the STUBEDIT
program (change the parameter
"Minimum amount of stack space"), or by setting the global variable
_stklen
in your program. Example:
unsigned _stklen = 1048576; /* need a 1MB stack */
The DJGPP startup code checks both the value in the stub (that can be
changed by STUBEDIT
) and the value of _stklen
, and uses
the larger of these two. Therefore, programs that are known to require
large stack size should set _stklen
to make sure they will always
work, even if somebody stub-edits them to a lower value. Setting
_stklen
is also safer to ensure sufficient stack size during
debugging (see below). However, you might be left with STUBEDIT
as your only option of enlarging the stack with programs for which you
don't have the sources handy, or rebuilding which is not practical.
Alternatively, you could rewrite your code to declare large arrays with
the static
qualifier, or put their declaration outside any
function (thus making them static by default). Static arrays don't use
stack space at all.
Programs that need an unusually large stack might crash with bogus
stack traces, because part of the static data gets overwritten by the
overflowing stack. To see if that is the cause of such crashes, run
STUBEDIT
on your program and crank up the stack size to a large
value (like 4 MBytes). If that makes the problem go away, tune the
stack limit to the minimum value your program can live with, then set
_stklen
to an appropriate value as explained above and recompile
the program. (Some DPMI hosts will actually allocate the entire stack,
even if not all of it is used, so leaving it at unnecessarily large
value will hurt the program on low-memory machines.)
Some users have reported that they needed to enlarge the stack size of
the C++ compiler, cc1plus.exe
, to prevent it from crashing when
compiling some exceedingly large and complex C++ programs. Another
program that was reported to need a stack larger than the default is
bccbgi.exe
from the BCC2GRX
package.
After you've used STUBEDIT
to change the stack size, run it again
to make sure it displays as default the value you thought you entered.
This is because STUBEDIT
will sometimes silently set the stack
size to 0 (and then you will get the default 512K stack) if it doesn't
like the value you type (e.g. if it has a wrong syntax).
When you run a raw COFF image under a debugger, the stack size is taken
from the debugger's stack size, which might not be appropriate for your
program . So the only way to change the default stack size in these
cases is to set _stklen
. You can also stubedit the debugger, to
achieve the same effect, albeit at a price of more memory used by the
debugger.
Under Windows 3.X, be sure you've allocated a sufficiently large swap
file (let's say, 40MBytes) from the Windows' Control Panel, and make
sure the .PIF
file for your program doesn't have too low limit on
EMS/XMS usage (better make them both -1). What's that? You don't
have a .PIF
file for this program? Then Windows uses the default
file DOSPRMPT.PIF
, which almost surely defines very low limits on
these two, and your program might have problems getting the memory it
needs for its stack.
DJGPP v2.0 has a subtle bug in its startup code that is seen very rarely, and that manifests itself by a program crashing with Page Fault or SIGSEGV. If you are using v2.0 and enlarging the stack and the CWSDPMI heap size didn't help, try adding some (e.g., 4KB) static data to your program and see if that helps. But the best way to overcome this is to upgrade to DJGPP v2.01 or later.
Q: Whenever I run a batch file that launches a DJGPP program,
Windows 98 pops up a message saying that "This program has accessed
memory in use" and threatens me with all kinds of trouble unless I
terminate the program. What's wrong with DJGPP programs??
A: Nothing's wrong with DJGPP programs, it's some weird Windows
bug, and it is not limited to DJGPP programs, either. I'm told that to
get rid of these messages, you need to change the settings in the DOS
box Properties, under Memory
. Specifically:
Auto
, type in a fixed number.
The maximum possible amount depends on your system configuration; run
mem /c
to see how much conventional memory is free, and use that
amount to set the Conventional Memory property. Do not type
640K, since that causes Windows to reset the setting back to
Auto
.
Protected
option: uncheck it.
After doing this, click OK
, close the DOS box and reopen it
again. The problem should now go away.
Btw, the message popped up by Windows is a mere nuisance, since if you
answer YES
to Windows' question whether you want to continue,
everything works just fine, and the message is never popped up again
until you reboot the machine. Seems like just one more of those Windows
teasers....
DJGPP handles command-line arguments differently from most DOS-based compilers, to make it closer to Unix platforms (so that porting of Unix programs will be easier). This chapter answers some questions about this aspect of DJGPP.
Q: Can I do filename globbing with DJGPP?
Q: I call my program with an argument x*y
and it complains
about something called xyzzy
??....
Q: I cannot find a way to use the /?
switch with my
programs!
A: The filename globbing in DJGPP is done by the start-up code,
before your main
function is called. Unlike other DOS-based
compilers, where you must link your program with a special object module
if you want the program to get expanded filenames, in DJGPP this is
considered normal behavior and is performed by default on behalf of
every DJGPP program. The x*y
above was expanded to a file called
xyzzy
which was probably present in the current working
directory; and /?
is by default expanded to the list of
one-letter files/directories you happen to have in the root directory of
the current drive. (If you don't want the default expansion, refer to
how to disable globbing.)
In DJGPP, filename globbing works like in Unix, which is more general than the usual wildcard expansion, both in DOS and even in Windows. The DJGPP wildcard expansion understands the following constructs with special meta-characters:
?
*
[aA_]
[a-d]
[!a-z]
...
.../*
Unlike DOS, the *
and ?
meta-characters can appear
anywhere in the filename pattern, like in [a-z]*[0-9].*pp.
You can also use *
instead of directories, like in
*/*/*.c
, but not on drive letters (e.g., [a-c]:/
won't work).
Note that *.*
only picks up files that actually have an
extension. This is contrary to the usual DOS practice where it means
all the files, with or without extension. Use *
to get
files with and without extensions.
An argument which cannot be expanded (no filenames matching that
particular pattern) will be passed to the program verbatim. This is
different from what you might see under Unix, where some shells (like
csh
) would say something like "No match" and won't call your
program at all. DJGPP's behavior in this case is like shells of the
Bourne legacy (sh
, ksh
, and bash
).
If the wildcards include upper-case or mixed upper- and lower-case
letters, the letter-case of the files is not ignored on Windows 9X when
expanding the wildcards. For example, [A-D]*
will not
match a file called aFileName
. Upper-case letters in wildcards
also disable automatic down-casing of short 8+3 file names returned by
the code that expand wildcards (even on plain DOS). By contrast, if the
wildcards include only lower-case letters, the letter-case is ignored
during expansion, and short 8+3 file names are automatically down-cased,
unless the environment variable FNCASE
is set to y
. The
effect of setting FNCASE
is fully described in the DJGPP C
Library reference, under the _preserve_fncase
function; type
info libc alphabetical _preserve_fncase from the DOS prompt.
Q: I don't want my program to glob its arguments (they aren't files
at all, but they include characters like *
and ?
). What
should I do?
A: You have these alternatives:
__crt0_glob_function
and make
it always return a NULL
pointer. See the documentation of this
function in the library reference, for more details. Here's an example:
#include <crt0.h> char **__crt0_glob_function (char *arg) { return 0; }
@
Q: I have a file with a single quote in its name, but the quote
seems to be stripped away when I pass it to my program ....
Q: How do I pass a command-line argument which contains double
quotes?
Q: How do I pass an argument which begins with the @
character?
A: These special characters on the command-line arguments are
handled by the filename expansion ("globbing") code before they are
passed to the main
function (see description of filename expansion), and the quote characters serve to protect the arguments
from expansion. You should escape-protect the quote characters with a
backslash in order for them to be treated as literal characters. For
example, if you have a file called myfile.c'v
, type it as
myfile.c\'v
when you call your program. If you have single
quotes in your program arguments and you don't want those
arguments to be expanded, then surround them by double quotes, like
this: "*.c'v".
The program will get the string *.c'v
with
the double quotes stripped away.
Note that backslashes are only special if they are in front of a quote or a backslash (they also serve as DOS directory separators, remember?). This is also different from what you get under a Unix shell, where a backslash quotes any character.
The @
character serves to signal a response file
(see the description of response file method), so it's also special.
To pass an argument whose first character is @
, surround that
argument with single or double quotes, otherwise it will be taken as a
name of a response file which holds the actual command line.
You can quote only some parts of the wildcard to protect only those
parts from expansion; the unquoted parts will still be expanded. This
allows to use wildcards with embedded whitespace and expand file names
with special characters which need to be quoted, like in
c:/Prog*' 'F*
(which should expand into c:/Program Files
) and *.c"'"v
(which should expand into all files with the
*.c'v
extension).
Q: Can I invoke my program with a command line longer than the
infamous DOS 126-character limit?
Q: I have a Makefile of Unix origin which contains some
very long command lines. Will it work with DJGPP?
A: Yes and yes. DJGPP supports several methods of passing command-line arguments which allow it to work around the DOS 126-character limit. These are:
!proxy
method. If you invoke the program from within another
DJGPP program (like Make or Gcc compilation driver), it gets the address
of the memory block where the actual command line is stored. The start-up
code will detect this and use that info to retrieve the command-line
arguments.
This method is suitable only for invoking DJGPP programs from other DJGPP
programs. You don't have to do anything special to use this method, it is
all done automagically for you by the library functions from the
spawnXX
and execXX
family on the parent program side, and
by the start-up code on the child side27.
!proxy
method
above, but the information about the long command line is stored in an
environment variable called " !proxy" (with the leading blank
and in lower-case). The reason for two similar, but different methods
is that command-line arguments passed by system
library functions
should be globbed by the child, while the arguments passed by
spawnXX
and execXX
family of functions should not; thus
the former uses the environment method while the latter use the
!proxy
method.
This method is used only by the system
library function, and only
when the command line is longer than the DOS 126-character limit.
@
character (like in myprog @file
) will cause the named
file to be read and its contents to be used as command-line
arguments, like in many DOS-based compilers and linkers. If you invoke
your DJGPP program from the DOS command line, this would be the only
method available for you to pass long command lines (like when calling
Gawk
or SED without the -f
option).
This method is not used by the DJGPP library functions, but you can use it explicitly in your application code. It is designed for invoking non-DJGPP programs that support response files.
Note that this method makes @
special when it is the first (or the
only) character of a command-line argument, which should be
protected with quotes if you want to use it verbatim (see how to pass the @ character).
Of course, if the DJGPP start-up code doesn't see any of the above methods, it will use the DOS command line by default.
Since the long command lines are a very important feature, DJGPP's
version of the system
library function avoids calling the DOS
command processor, COMMAND.COM
, unless it needs to run a batch
file or an internal command of COMMAND.COM
. Other features of
the command processor, like redirection and pipes, are emulated
internally by system
. See the library reference for
system
, for more details about its extended functionality.
Q: What is the longest command line I can pass to gcc when it is
invoked by Make
?
A: The arguments are passed to DOS Exec call (Int 21h function 4Bh)
via the transfer buffer which is 16KB-long. Apart of the command line, it
is also used to pass other info, such as the !proxy
parameters and
the copy of the environment for the child program (let's say, less than
2000 bytes in most cases, but your mileage may vary). This leaves at
least 13K bytes for arguments (including a separating blank between any
two arguments). So unless your arguments span more than 160 screen lines,
you shouldn't worry. However, if your environment is very large
(some people need as much as 6KB to accommodate for all the variables
used by the various packages installed on their machine), be sure to
stub-edit the programs that spawn other programs to a larger transfer
buffer, or else they could fail.
The above limit depends on the size of the transfer buffer, so check the
size of the value recorded in the stub header of the parent program
before you pass extremely long command lines to its children. GCC is
the first program you should worry about, because the linker
(ld.exe
) usually gets long command lines from GCC (they include
the list of all the object files and libraries to be linked).
Q: I use Make to compile with GCC, but GCC gets only the first 126
characters of its command line. Didn't you just explain in so many words
that invoking a DJGPP program (GCC) from another DJGPP program (Make) can
safely pass up to 13K characters of command-line arguments using the
!proxy
method?
Q: I use RHIDE, but it only passes the first 126 characters of
my long command lines to the compiler!
A: If you use Make 3.73, check your Makefile for SHELL =
command.com
statements, or for commands which include pipe or
redirection characters like >
, |
, etc. If Make sees any
such statements, it will invoke COMMAND.COM
to run GCC, and
COMMAND.COM
can't pass more than 126 characters to GCC. To work
around, comment-out the SHELL=
line, and change your commands to
work without redirection/pipe characters. One easy way to get rid of
redirection characters without losing their effect is to use the
redir
program which comes with DJGPP. For example, the following
command:
frobnicate foo.bar > myfile.tmp
can be re-written instead like this:
redir -o myfile.tmp frobnicate foo.bar
The ports of Make 3.75 and later don't call COMMAND.COM
in the
above cases, but rather emulate pipes and redirection internally, so
upgrading to the latest version of Make will solve such problems. If
you think about using Make 3.75 with DJGPP v2.0, don't: invoking v2.0
programs from v2.01 programs will cause subtle and hard-to-debug
problems due to incompatibilities between these two versions regarding
the methods of invoking child programs (in particular, v2.0 doesn't
support the environment method of passing long command lines described
above).
If you have problems with long command lines when using Make 3.75 and
later, it might be caused by the environment variable SHELL
that
points to a Unix-style shell which is not a DJGPP program. This could
happen, for example, if you use the shell from the MKS toolkit. When
SHELL
points to a program whose name looks like it's a Unix-style
shell (sh.exe
, bash.exe
, etc.), library functions like
system
will invoke the shell to do everything, instead of using
the internal shell emulator. If that shell isn't a DJGPP program, the
long command lines will end up truncated. To solve these problems,
either unset SHELL
or override it by setting MAKESHELL
to
point to COMMAND.COM
or to the DJGPP port of Bash. This is
further explained in the GNU Make manual
(see SHELL
and MAKESHELL
).
Problems with passing long commands from RHIDE are usually caused by invoking old programs compiled with DJGPP v2.0. Upgrade to the latest binaries.
If you have a program or a library developed under some other DOS-based compiler, which you want to port to DJGPP, read this chapter.
Q: I have some code written in assembly which compiles under
MASM and TASM, but GCC gives me a long list of error
messages.
A: The GNU Assembler (as.exe
), or Gas
, called by GCC
accepts the AT&T syntax, which is different from the Intel
syntax. Notable differences between the two syntaxes are:
$
; Intel immediate operands
are undelimited (Intel push 4
is AT&T pushl $4
).
%
; Intel register operands
are undelimited. AT&T absolute (as opposed to PC-relative)
jump
/call
operands are prefixed by *
; they are
undelimited in Intel syntax.
add eax, 4
is addl $4, %eax
in
AT&T syntax.
The source, dest
convention is maintained for compatibility with
previous Unix assemblers, so that GCC won't care about the assembler with
which it is configured, as some of GCC installations (on systems other
than MS-DOS) don't use GNU Binutils.
b
,
w
, and l
specify byte (8-bit), word (16-bit), and long
(32-bit) memory references. Intel syntax accomplishes this by prefixing
memory operands (not the opcodes themselves) with `byte
ptr'
, `word ptr'
, and `dword ptr'.
Thus, Intel
mov al, byte ptr FOO
is movb FOO, %al
in AT&T
syntax.
lcall/ljmp $SECTION, $OFFSET
in AT&T syntax; the Intel syntax
is call/jmp far SECTION:OFFSET.
Also, the far return
instruction is lret $STACK-ADJUST
in AT&T syntax; Intel syntax
is ret far STACK-ADJUST.
SECTION:[BASE + INDEX*SCALE + DISP]
is translated into the AT&T syntax
SECTION:DISP(BASE, INDEX, SCALE)
Examples:
Intel: [ebp - 4] AT&T: -4(%ebp) Intel: [foo + eax*4] AT&T: foo(,%eax,4) Intel: [foo] AT&T: foo(,1) Intel: gs:foo AT&T: %gs:foo
For a complete description of the differences,
see GNU assembler documentation. If you don't
read this FAQ with an Info browser, download GNU Binutils,
unzip the files named as.iN
(where N
is a digit) from
it, then type at the DOS prompt:
info as machine i386
You will see a menu of Gas
features specific to x86 architecture.
A user guide for inline assembly was written by Brennan Underwood; it describes how to use inline assembly programming with DJGPP and includes a tutorial on the AT&T assembly syntax. Check out the DJGPP inline assembly tutorial.
Another useful tutorial about writing separate assembly-language modules for DJGPP was written by George Foot and is available from George's home page.
The DJGPP User's Guide also has a tutorial on writing assembly-language code. One of the sections there describes the CPU architecture, which is geared towards assembly-language programming.
Yet another tutorial on the subject of inline assembly is available at
<http://www.castle.net/~avly/djasm.html
>.
Many people who used Intel syntax and then converted to the AT&T style say that they like the AT&T variant more. However, if you prefer to stick with the Intel syntax, download and install NASM, which is a free portable assembler. It is compatible with DJGPP and accepts a syntax which is much more similar to the Intel style. A guide for using NASM with DJGPP was written by Matthew Mastracci and is available from Matthew's Web page.
Note that Binutils maintainers are working on adding an option to Gas which will cause it accept the Intel syntax as well, so it is most probable that beginning with Binutils 2.10, Gas will have this feature.
Q: Where can I find an automated conversion tool to convert my
Intel
-style assembly code into a code acceptable by Gas
?
Q: Where can I find a converter from AT&T
assembly to
Intel
style?
A: A SED script which should do most of the conversion was posted to the DJGPP news group.
A program called TA2AS
which can convert TASM
assembly source to AT&T style can be found on the DJGPP server and on Oulu.
TA2AS
was written by Frank van Dijk of the Spirit group; if you
have questions about this tool, you may contact Jan Oonk. The authors say that the program is far from finished,
but the sources are available with the package so you can fix whatever
is broken for your needs.
Another similar converter is Intel2Gas
, available from its
Web page.
Beginning with Binutils 2.10, Gas
has an option that causes it to
accept the Intel syntax, so you can use Gas
to assembly
Intel-style code.
Alternatively, here is what you can do to make your code linkable with DJGPP programs:
Be warned that NASM is not 100% identical to MASM or TASM. Even experienced assembly programmers might need some time to adapt to the slightly different flavor of NASM. If you want something much more similar to TASM, get JAS. JAS is available from OULU.
Also note that NASM, or at least some of its versions, doesn't produce debug info in the format understood by GDB, which makes debugging NASM-assemblied code tedious (you might not be able to display source lines and refer to local symbols by name). Latest versions of NASM might correct this deficiency.
-coff
option
to generate object code in COFF format which can be submitted to GCC, so
you can compile your original source. You can also use the LIB32
librarian from Microsoft C8 to convert object files to COFF by putting
them into a .lib
library, then extracting them as COFF files.
28 Note that, unless you link the
MASM-generated object files with DJGPP's ld
(as opposed
to Microsoft's LINK /CO
command), you won't be able to debug the
resulting program, because the debug info is not in correct format. I'm
also told that masm
doesn't produce sections named ".text" and
".data", so you might need to hex-edit the section names in the object
file manually.
TA2AS
. One place to look
for such a disassembler is on SimTel.NET mirrors.
Keep in mind that syntax is only one of the aspects of converting code written for DOS to DJGPP. You should also make sure your code doesn't violate any of the rules for protected-mode programming (see GPF in asm code).
If you need to perform the opposite conversion, from the AT&T
style to the Intel
style, try the Att2Intl
converter
written by Gregory Velichansky. Its output
is intended for NASM or TASM. Att2Intl
is available
from Greg's home page.
Q: OK, I've succeeded in converting and compiling my assembly-language
program, but when I run it, I get "Segmentation Violation" and "General
Protection Fault". This program works when compiled with MASM, so
how can this be?
A: In DJGPP, your program runs in protected mode. There are certain things you can't do in protected-mode programs (that's why it is called protected mode). This issue is too complex to describe here, so only a few of the more important aspects will be briefly mentioned. If you are serious about writing assembly language protected-mode code, or have a large body of existing code to convert to protected mode, you should read any of the available books about protected-mode programming with 80x86 processors.
Here is a short list of some of the techniques found in many real-mode programs, which will trigger protection violation or erratic behavior in protected mode:
INT NN
instruction.
If your code uses one or more of these techniques, refer to low-level programming chapter, which describes the DJGPP facilities that will allow you to rewrite your code.
Q: Which register can I safely change in my assembly code that is
called from a C program?
Q: How come my program that calls assembly-language functions
crashes with a GPF, but only if I compile it with -O2?
Q: When I try to link my assembly modules with a C++ program,
the linker complains about the functions I wrote in assembly!
A: You can safely clobber EAX, ECX, EDX, FS and GS, as well as EFLAGS and the floating-point registers (including the FPU control and status words), but must save and restore all other registers at the end of your assembly function. Failure to preserve, e.g., ESI, EDI, EBX, ESP or DS in functions written in assembly can cause a C program linked with such functions to crash, since GCC expects those registers to be preserved across function calls. Special-purpose registers such as GDTR, LDTR, CR*, and DR*, although GCC and the DJGPP library don't use them, should probably not be touched at all, but if you do, it's a good idea to save and restore them.
Functions written in assembly or in C that are meant to be linked with
C++ programs should be declared extern "C"
in their
prototype, like this:
#ifdef __cplusplus extern "C" { #endif int my_assembly_func (int); #ifdef __cplusplus } #endif
This example shows how to produce a prototype that would work with both
C and C++ programs; it is usually placed in a .h
header file
that is meant to be #include
d in the C or C++ programs.
This extern "C"
declaration prevents the C++ compiler from
mangling the names of external functions according to the usual C++
rules. (The name-mangling is needed because C++ allows several
different functions to have the same name but different types of
arguments.)
.obj
or .lib
code with DJGPPQ: I have a set of useful functions in a .obj
format, but no
source code. Can I use them with my DJGPP program?
Q: I have this ACMELUXE.LIB
library of functions which I
want to use. I've extracted all the .obj
files, but when I try to
link them with my program, GCC complains: "File format not recognized".
Can't I use these object files?
Q: I've got a bunch of .obj
files I want to use. I've ran
AR to make a GCC-style .a
object library, but got an error message
from GCC saying "couldn't read symbols: No symbols". How can I link
them with my code?
A: Sorry, you probably can't. The GNU linker called by GCC doesn't
understand the format of .obj
files which other DOS-based
compilers/assemblers emit. Unless you can get the source of those
functions, convert it to protected-mode, flat-address model code and
compile them with GCC, you most probably won't be able to use
them29.
However, if you are really desperate, one conversion tool you might try
is OBJ2BFD
. It was written by Robert Hoehne based on the EMXAOUT
utility from the
emx/gcc
package. OBJ2BFD
requires the .obj
files
to be written for the flat-address memory model and will reportedly
complain if you feed it with code written for segmented memory models.
OBJ2BFD
is available from the DJGPP sites.
Another automated conversion tool called OBJ2COFF
was written
by the SPiRiT team, and it can be used to convert .obj
object
files and .lib
libraries to COFF
format, provided that the
original .obj
files have been written for flat-address memory
model.
OBJ2COFF
is available via anonymous FTP transfer from the Oulu MSDOS repository. If you have
any problems with it or questions about it, send them to its author Rico or to George van Venrooij. Note that the authors of OBJ2COFF
have
explicitly prohibited commercial use, so you shouldn't use
OBJ2COFF
for converting commercial object files.
You can also try using LIB32 librarian from Microsoft C8 to convert object files to COFF.
The main problem with these conversion methods is, of course, that most object files you'd want to converted were written for real-mode programs in memory models other than flat, and without extensive modifications would crash your program anyway.... (See previous question.)
Q: If I cannot use 16-bit .obj
files, then I would have to
give up using DJGPP. I simply cannot live without these .obj
files. Are you really sure there is nothing I can do??
A: If you need your old code that badly, then there might be a
way, albeit a cumbersome one. You can write a 16-bit, real-mode program
and link it with your precious functions you can't live without. Have
this program spawn a DJGPP-compiled program and make the two communicate
with each other via a buffer allocated in low memory, or via command-line
parameters passed to the 32-bit program by the spawnXX
function
call. On the DJGPP side, you can directly call 16-bit functions from the
real-mode program using the library function called
__dpmi_simulate_real_mode_procedure_retf
,
provided the 16-bit program passes the CS:IP values of these functions to
the 32-bit program. You can even put your 16-bit code as binary
instructions into a buffer allocated in low memory and call it with
__dpmi_simulate_real_mode_procedure_retf
(but if you can do that,
you can probably also disassemble the code into a source form and submit
it to Gas
).
Now will you consider sticking with DJGPP? Please??...
Q: I have this program that I need to port to DJGPP, but it is full
of pointers and functions declared with the "near" and "far"
keywords which GCC doesn't grok. What shall I do?
Q: A program written for a 16-bit compiler uses the MK_FP or _MK_FP
macro, but DJGPP doesn't seem to have it. How should I port it?
Q: How do I compute a segment and an offset of a protected-mode
address?
A: In DJGPP you use a flat address space with no segmentation (it is a kind of tiny model, since CS = DS = SS, but with a very large segment), so you don't need far pointers in the sense they are used in 16-bit code. Just define away those keywords and you will be fine:
#define far #define near #define huge #define _far #define _near #define _huge
Alternatively, you could add suitable -D
switches to the GCC
command line, like this:
gcc -Dfar= -Dnear= -Dhuge= -c myprog.c
Macros that create far pointers from the segment and offset (usually
called MK_FP
or _MK_FP
) are mostly used in 16-bit code to
access certain absolute addresses on memory-mapped peripheral devices,
like the video RAM. These chores are done differently in DJGPP. Here's
one possible way to express MK_FP
in DJGPP (courtesy of Charles Sandmann):
#include <sys/nearptr.h> #include <crt0.h> void * MK_FP (unsigned short seg, unsigned short ofs) { if ( !(_crt0_startup_flags & _CRT0_FLAG_NEARPTR) ) if (!__djgpp_nearptr_enable ()) return (void *)0; return (void *) (seg*16 + ofs + __djgpp_conventional_base); }
The above uses the DJGPP nearptr
facility, which effectively
disables memory protection and doesn't work on some systems (e.g. NT);
if you prefer to use farptr
functions (which are safer and work
with all known DPMI hosts), you will need to rewrite the code that uses
these macros, so don't bother writing a replacement for the MK_FP
macro itself. The details are described in Accessing absolute addresses, below.
Macros that extract the segment and the offset from a far pointer
(called FP_SEG
and FP_OFF
) are required in 16-bit code to
pass addresses in registers when calling real-mode DOS or BIOS services,
like functions of interrupt 21h. See How to call real-mode interrupt functions, which describes how that should be done in DJGPP; here, too,
you won't need to port the macros but instead rewrite the code that
calls the DOS or BIOS service. In particular, you cannot
compute a real-mode segment and offset of a protected-mode address,
because real-mode addresses can only access the first 1MB of memory,
whereas the variables of DJGPP programs all live above the 1MB mark.
Q: Since DJGPP doesn't recognize Borland-style pseudo-register
variables like _AX
, how should I port code which uses them to
DJGPP?
A: These pseudo-variables are typically used in two different contexts:
To port such code to DJGPP, use the fields of the __dpmi_regs
structure (declared on the dpmi.h
header file) to set the
register values, and library function __dpmi_int
to invoke the
interrupt service. For example, consider the following code snippet:
#include <dos.h> void _highcolor (void) { _AH = 0x10; _AL = 0x03; _BL = 0; geninterrupt (0x10); }
Here's one way to express this in DJGPP30:
#include <dpmi.h> void _highcolor (void) { __dpmi_regs r; r.h.ah = 0x10; r.h.al = 0x03; r.h.bl = 0; __dpmi_int (0x10, &r); }
Please read how to call real-mode interrupt functions, elsewhere in this document, for further details on how call real-mode services from DJGPP programs.
GCC features extensive inline assembly facilities which allow you to assign values to, or read values from registers from the inline assembly code. Since you will have to rewrite your inline assembly code anyway, make it refer directly to your C variables, instead of referencing pseudo-register variables from C. See description of inline assembly, for further info.
This chapter sheds some light on a few aspects of writing DJGPP programs which interact with hardware or use interrupts.
int86
Q: Why does my program crash with "Unsupported DOS request 0xNN"
or "Unsupported INT 0xNN" when I call int86
or intdos
functions to invoke a software interrupt?
A: Calling real-mode DOS or BIOS services from protected-mode
programs requires a switch to real mode, so the int86
family of
functions in the DJGPP library should reissue the INT instruction after
the mode switch. However, some services require pointers to memory
buffers. Real-mode DOS/BIOS functions can only access buffers in
conventional memory, so int86
has to move data between your
program and low memory to transparently support these services. But
this means int86
should know about all these services to perform
these chores correctly, because each service has its own layout and size
of the buffer(s). While int86
supports many of these services,
it doesn't support all of them. The supported functions are listed in
the library reference, see
int86.
For those services it doesn't support, you will have to call the
__dpmi_int
library function instead; it is also documented in the
library reference. __dpmi_int
requires that you set up all the
data as required by the service you are calling, including moving the
data to and from low memory (see how to use buffers with DOS/BIOS services).
Note that calling int86
and intdos
can sometimes cause
trouble due to size (16 bits as opposed to 32 bits) of the members in
the union REGS
structure. Do not assume that
e.g. regs.x.ax
is always 16 bit! This problem and the facilities
available to specify the width of the registers are all described in the
library reference; see
int86.
In particular, programs which interface with the mouse via calls to the
int86
library function, should mask off the high 16 bits of the
registers which report mouse position and other values, since the high
16 bits aren't necessarily zeroed (which will wreak havoc in any program
that interfaces to the mouse).
For these reasons, it is generally recommended to use __dpmi_int
instead of int86
and intdos
.
Q: I want to call a DOS/BIOS function which requires a pointer to a
buffer in, e.g. ES:DI (or any other) register pair. How do I get
the segment to put into the ES register?
Q: I have some real-mode code that calls the segread
function. How can I make it work with DJGPP?
A: If you call __dpmi_int
, then you must put into that
register pair an address of some buffer in conventional memory
(in the first 1 MByte). If the size of that buffer doesn't have to be
larger than the size of transfer buffer used by DJGPP (at least 2KB,
16KB by default), then the easiest way is to use the transfer buffer.
(Library functions don't assume the contents of the transfer buffer to
be preserved across function calls, so you can use it freely.) That
buffer is used for all DOS/BIOS services supported by DJGPP, it resides
in conventional memory, and is allocated by the startup code. DJGPP
makes the address and the size of the transfer buffer available for you
in the _go32_info_block
external variable, which is documented in
the library reference. Check the size of the buffer (usually, 16K
bytes, but it can be made as small as 2KB), and if it suits you, use its
linear address this way:
dpmi_regs.x.di = _go32_info_block.linear_address_of_transfer_buffer & 0x0f; dpmi_regs.x.es = _go32_info_block.linear_address_of_transfer_buffer >> 4;
For your convenience, the header file go32.h
defines a macro
__tb
which is an alias for
_go32_info_block.linear_address_of_transfer_buffer.
Here's a simple example of calling a real-mode service. This function
queries DOS about the country-specific information, by calling function
38h of the DOS Interrupt 21h, then returns the local currency symbol as
a C-style null-terminated string in malloc
ed storage. Note how
the transfer buffer is used to retrieve the info: the address of the
transfer buffer is passed to DOS, so it stores the data there, and the
function then retrieves part of that data using dosmemget
.
#include <sys/types.h> #include <sys/movedata.h> #include <dpmi.h> #include <go32.h> char * local_currency (void) { __dpmi_regs regs; regs.x.ax = 0x3800; /* AH = 38h, AL = 00h */ regs.x.ds = __tb >> 4; /* transfer buffer address in DS:DX */ regs.x.dx = __tb & 0x0f; __dpmi_int (0x21, ®s); /* call DOS */ if (regs.x.flags & 1) /* is carry flag set? */ /* The call failed; use the default symbol. */ return strdup ("$"); else { /* The call succeeded. The local currency symbol is stored as an ASCIIZ string at offset 2 in the transfer buffer. */ char *p = (char *)malloc (2); if (p != 0) dosmemget (__tb + 2, 2, p); return p; } }
If the size of the transfer buffer isn't enough, you will have to allocate
your own buffer in conventional memory with a call to the
__dpmi_allocate_dos_memory
library function. It returns to you the
segment of the allocated block (the offset is zero). If you only need a
small number of such buffers which can be allocated once, then you don't
have to worry about freeing them: they will be freed by DOS when your
program calls exit
. The only adverse effect of not freeing DOS
memory until the program exits is that if you need to run subsidiary
programs (via spawnXX
or system
library functions), those
programs will have less conventional memory. Usually, this aspect
should only be considered if a program allocates very large (like 100KB)
buffers in conventional memory.
DOS memory can also be allocated by calling function 48h of Interrupt
21h via __dpmi_int
and freed by calling function 49h. The only
disadvantage of this method is that it doesn't create a protected-mode
selector for the allocated block, so you must use the _dos_ds
selector to reference the allocated memory, which is less safe: the
_dos_ds
selector spans the entire first megabyte of memory,
whereas the selector created by __dpmi_allocate_dos_memory
spans
only the allocated block, and will therefore catch bugs that reference
memory outside that block.
For bullet-proof code, you should test the size of the transfer buffer at
runtime and act accordingly. This is because its size can be changed by
the STUBEDIT
program without your knowledge (however, it can
never be less than 2KB, the size of the stub, because memory used by the
stub is reused for the transfer buffer).
The function segread
used by some real-mode compilers does not
exist in DJGPP. It is used in real-mode code to store the values of the
CS, DS, SS, and ES registers into a struct
SREGS
variable, when some service that needs one of these registers is
called from code written for small and tiny memory models. DJGPP has
the functions _my_cs
, _my_ds
, and _my_ss
for that
purpose (ES and DS always hold the same selector in code
produced by GCC from a C or C++ source, so you don't need a fourth
function). However, these will not be useful if the original real-mode
code used the segment registers to invoke DOS/BIOS services. For these
cases, you will need to rewrite the code so that it copies the data
to/from the transfer buffer and passes the transfer buffer address via
__dpmi_int
, as described above.
If you use int86x
or intdosx
to call a DOS or BIOS
function supported by them, then just put the address of your buffer
into the register which expects the offset (regs.x.di
), forget
about the segment, and call int86
or intdos
instead of
int86x
and intdosx
. The DOS/BIOS functions supported by
int86
and intdos
are processed specially by the library,
which will take care of the rest. Note that calling int86x
and
intdosx
will usually crash your program, since they expect that
you pass them a real-mode segment:offset
address to a buffer in
conventional memory; this is done more easily with __dpmi_int
,
as described above, so I don't recommend using int86x
and
intdosx
.
Q: My program crashes/doesn't do what it should when I call
__dpmi_simulate_real_mode_procedure_retf
.
A: You should zero out some of the members of the __dpmi_regs
structure before you call the DPMI function that invoke real-mode
procedures. Random values in these members can cause your program to
behave erratically. The members in point are .x.ss
,
.x.sp
, and .x.flags
. When .x.ss
and .x.sp
are zeroed, the DPMI host will provide a stack for the call. This stack
is usually large enough, but sometimes you'll need to use your own,
larger stack, e.g., if you expect interrupts to nest deeply, or if your
handler needs a lot of stack space31. In these cases you should point
.x.ss
and .x.sp
to a larger buffer which is in
conventional memory (possibly part of the transfer buffer).
If SS:SP isn't zero, it will be used as the address of the stack for the interrupt handler, so if it points to a random location, your program will most certainly crash. A non-zero FLAGS member can also make the processor do all kinds of weird things (e.g., imagine that the single-step or the debug bit is set!).
Q: How can I move data between my program and the transfer buffer?
Q: How do I access my peripheral card which is memory-mapped to an
address between 640K and 1M?
Q: How can I read or change a value of one of the variables in the
BIOS data area?
Q: How can I peek at an address whose far pointer I get from an
INT 21h call?
A: Usually, memory-mapped devices or absolute addresses below 1MB mark are outside your program's address space, so you cannot access them directly. "Direct access", when you just dereference a pointer, means in DJGPP that you use your program's DS selector, and all the addresses are offsets relative to the base of that selector. So first, you will need a special selector that will allow you to access your device or absolute address. There are several methods you can get such a selector:
_dos_ds
macro (defined on the go32.h
header file). This
selector has base address of 0 and a limit of 1MB+64KB, so you can use
it to access any address in the conventional memory, including the UMBs,
but the relatively large limit allows a buggy program to overwrite
portions of DOS memory32. The advantage of _dos_ds
is obviously
that you don't have to create it, and that it is good for accessing
every region in the first MByte range.
_dos_ds
. For
example, here's a code snippet to set up a selector which provides
access to 32KB of text-mode video memory at 0xB800:0000
, courtesy
of Bill Currie33:
int TxtVRAMSetupSelector (void) { static char selectorData[8] = { 0xff, 0x7f, 0x00, 0x80, 0x0b, 0xf3, 0x40, 0x00 }; int screenSelector = __dpmi_allocate_ldt_descriptors (1); if (__dpmi_set_descriptor (screenSelector, selectorData) < 0) abort (); return screenSelector; }
Calling __dpmi_allocate_dos_memory
creates a protected-mode
selector that spans the allocated block. You can use that selector to
access the allocated memory.
The advantages of using a special selector are that (a) you can set up the selector limit such that it only covers the memory region that you need, thus protection of the rest of memory is retained; and (b) you may set the base address to point to the beginning of the specific memory region you need to access, so that you don't have to add the base address for every access, making the access faster.
__dpmi_segment_to_descriptor
which is a wrapper around that DPMI
service. It is easier to use than the __dpmi_set_descriptor
function above, since you don't have to mess with the 8-byte descriptor
buffer, but it always defines a 64KB limit by default. Here is an
example of code which gets a selector to access 64KB of video RAM
beginning at 0xA000:0000
:
short video = __dpmi_segment_to_descriptor(0xa000);
Note that descriptors created by this function should never be modified
or freed. For this reason, you should use this function sparingly. For
instance, if your program needs to examine various real mode addresses
using the same selector, you should allocate a descriptor and change the
base using the __dpmi_set_segment_base_address
library function
instead of using __dpmi_segment_to_descriptor
to allocate
separate descriptor for each address.
Once you have a selector, you can use one of three methods to access your absolute addresses using that selector:
<sys/farptr.h>
header file. You should convert any real-mode far pointer
segment:offset pair into a linear address (i.e., segment*16 +
offset), and use _dos_ds
or any other selector which allows
access to conventional memory, like this:
unsigned char value = _farpeekb(_dos_ds, segment * 16 + offset);
To access DOS memory allocated by __dpmi_allocate_dos_memory
, use
the selector returned by that function; a zero offset designates the
beginning of the allocated block.
For access to memory-mapped devices for which you have allocated a
dedicated descriptor, use the selector of that descriptor instead of
_dos_ds
in the above example, and use the offset into the on-board
device memory as the offset. For example, the following snippet
writes a value of 3 to the 10th dword of the device:
long lword = 3; _farpokel (selector, 9, lword);
Use _farpeekw
to peek at 16-bit shorts and _farpeekl
to
peek at 32-bit longs. If you need to access several (non-contiguous)
values in a loop, use the corresponding _farnspeekX
functions which
allow you to set the selector only once, as opposed to passing it with
every call (but be sure the loop doesn't call any function that itself
sets the selector; see the library reference for more details).
There is a corresponding set of _farpokeX
and _farnspokeX
functions to poke (change the values of) such memory locations.
These functions have an advantage of emitting inline assembly code when you compile with optimizations, so they are very fast. See the library reference Info file for further details about these functions.
dosmemget
and dosmemput
library functions. They also require you to
convert the segment:offset pair into a linear address, but they don't need
the conventional memory selector, as they can only be used to access the
conventional memory (they use _dos_ds
internally).
Note that some memory-mapped peripheral devices might require 16-bit word
accesses to work properly, so if dosmemXXX
yields garbled
results, try dosmemXXXw
or set up a loop which calls "farptr"
functions.
_dos_ds
(e.g.,
selectors created by one of the methods explained above), use the
movedata
library function. It requires that you pass a selector
and an offset for both the memory-mapped address and for the buffer in
your program's address space. Use the _my_ds()
function (note
that it's a function, not a variable!) to get the selector of
any variable in your program, and use the address of the variable (cast
to an int
) as its "offset" or linear address. movedata
is fast because it moves by 32-bit longs, but be careful with its use
when moving data to and from peripheral cards: some of them only support
8- or 16-bit wide data path, so moving data 4 bytes at a time won't gain
you much, and might even get you in trouble with some buggy BIOSes. The
functions movedatab
and movedataw
are provided for moving
by bytes and by 16-bit words, respectively.
For example, here is a code snippet that combines one of the methods for
allocating a descriptor for video RAM access with a call to
movedata
to move a buffer to the graphics screen:
short video = __dpmi_segment_to_descriptor(0xa000); movedata(_my_ds(), buffer, video, 0, 320*200);
sys/nearptr.h
header file; see the library
reference for more details. Also see description of how to get the fastest direct access to peripheral devices, below.
Q: My program uses the contents of a structure returned by a VBE
function, but some of the struct members are garbled!
A: Most probably, this happens because of incorrect declaration of the structure in your program. Many people copy a declaration from some real-mode program, and that is exactly what gets them into trouble.
Here are some gotchas in this context:
__attribute__((packed))
, to
prevent GCC from inserting gaps between some members to make them
properly aligned for faster access (see how gcc aligns structs). C programs can
declare the entire struct with the packed attribute, but C++
programs will need to declare each member with it, see __attribute__((packed)).
dosmemget
or one of the
_farpeekX
family of functions in conjunction with the
_dos_ds
selector, and don't forget to compute the linear address
as segment * 16 + offset
, where segment
and offset
are taken from the struct members converted from the far pointers in the
original real-mode code.
__dpmi_simulate_real_mode_procedure_retf
to call it
(don't forget to zero out the .x.ss
and .x.sp
members of
the __dpmi_regs
structure!). See real-mode functions.
int
s, whereas in DJGPP, an
int
is 32-bit wide. You need to change the declaration of all
struct members from int
to short
, and from unsigned
to unsigned short
.
For example, the following real-mode structure declaration:
struct ncb { unsigned ncb_command; int ncb_status; char far *ncb_buffer; /* a far pointer to a buffer */ char ncb_name[32]; int far (*ncb_dispatch)(); /* a pointer to a far function */ };
should be converted to this in a DJGPP program:
struct ncb { unsigned short ncb_command __attribute__((packed)); short ncb_status __attribute__((packed)); unsigned short ncb_buf_offset __attribute__((packed)); unsigned short ncb_buf_segment __attribute__((packed)); char ncb_name[32] __attribute__((packed)); unsigned short ncb_dispatch_offset __attribute__((packed)); unsigned short ncb_dispatch_segment __attribute__((packed)); };
With the above declaration of struct ncb
, the following real-mode
code:
int status = ncb.ncb_status; int value = *(int far *)ncb.buf[3];
should read in DJGPP:
short status, value; struct ncb ncb_struct /* Fetch the structure from the transfer buffer. */ dosmemget (__tb, sizeof (struct ncb), &ncb_struct); status = ncb_struct.ncb_status; value = _farpeekw (_dos_ds, ncb_struct.ncb_buf_segment*16 + ncb_buf_offset + 3);
In other words, you need to add code that moves the structure to and
from the transfer buffer, and replace each pointer dereference with a
call to an appropriate _farpeekX
or _farpokeX
function.
Q: The "farptr" functions are too slow for my application which
MUST have direct access to a memory-mapped device under DPMI.
How can I have this in DJGPP? My entire optimized graphics library is at
stake if I can't! :-(
A: The following so-called Fat DS, or "nearptr" method was suggested by Junaid A. Walker (he also posted a program which uses this technique to access the video RAM; you can look it up by searching the mailing list archives). But first, a word of warning: the method I'm about to describe effectively disables memory protection, and so might do all kinds of damage if used by a program with a wild pointer. It is depressingly easy, e.g., to overwrite parts of DOS code or data with "Fat DS" on. Or, as Stephen Turnbull has put it when he read the description of this trick:
Surgeon General's WARNING: The description below uses the "Fat DS hack", a steroid derivative which gives your program great strength, a thick neck, baldness, and is known to be closely linked with the Alzheimer's disease.
In addition to the above warning, experience shows that many programs which use the safer "farptr" functions do not sacrifice performance at all. So, with the exception of a small number of programs, "nearptr" is really a convenience trick: it allows you to treat memory-mapped devices with usual C pointers, rather than with function calls. Therefore, I would generally advise against using "nearptr" due to speed considerations, unless your program absolutely needs the last percent of speed.
Having said that, here is the trick: you change the limit of the segment
descriptor stored in DS to 0xffffffff
(i.e., -1), using
library function __djgpp_nearptr_enable
. After that, you have
access to all the memory which is currently mapped in. This works due to
32-bit wrap-around in the linear address space to access memory at, say,
linear address 0xa0000 (which belongs to the VGA), or any other address
on your memory-mapped device, by adding the value of the global variable
__djgpp_conventional_base
to the target address.
__djgpp_conventional_base
is the negated base address of the
DS selector that you program is using to access its data. By
adding the value of __djgpp_conventional_base
, you effectively
subtract the DS base address, which makes the result
zero-based, exactly what you need to access absolute addresses.
You should know up front that this trick won't work with every DPMI
host. Linux's DOSEmu and Windows/NT won't allow you to set such a huge
limit on the memory segment, because these operating systems take memory
protection seriously; in these cases __djgpp_nearptr_enable
will
return zero--a sign of a failure. CWSDPMI, QDPMI, Windows 3.X and
Windows 9X all allow this technique (OS/2 Warp seems to allow it too, at
least as of version 8.200), but some events break this scheme even for
those DPMI hosts which will allow it. A call to malloc
or any
other library function which calls sbrk
might sometimes change
the base address of the DS selector and break this method unless
the base address is recomputed after sbrk
call. (The "nearptr"
functions support this recomputation by providing you with the
__djgpp_conventional_base
variable, but it is your
responsibility to recompute the pointers using it.) The same change can
happen when you call system
, and as a result of some other events
external to the executing code thread, like multitasking or debugger
execution.
You should also know that the __djgpp_nearptr_enable
function
in DJGPP v2.0 didn't verify that the limit was properly set. So if the
DPMI server would fail the call silently, the function won't
detect it and will not return a failure indication. DJGPP v2.01
corrects this omission by always verifying that the DPMI host has
honored the request, and returns a failure indication if it hasn't.
If you are aware of these limitations, and don't need your code to run under all DPMI hosts, it might be the fix to your problems.
Confused about how exactly should you go about using this technique in
your program? Look at the docs of the "nearptr" functions in
the Info file libc.info
(node __djgpp_nearptr_enable
).
Another possibility is to use the DPMI function 0x508
(a wrapper
function __dpmi_map_device_in_memory_block
is available in the
DJGPP library) that can map any range of physical memory addresses into
a block that you allocate. Note that this is a DPMI 1.0 functionality
which is not supported by most DPMI 0.9 hosts (CWSDPMI does
support it). There is a convenience helper function
__djgpp_map_physical_memory
in the DJGPP C library that you can
use to call these services.
If you need a nearptr-style access to a certain region of memory which
is above the base address of the DS selector, you can enlarge the
limit of the DS selector just enough to cover the highest address
you need to access. To this end, use the library function
__dpmi_set_segment_limit
like this (thanks to Eric Rudd for posting this code):
unsigned long new_limit; if (__dpmi_set_segment_limit (_my_ds (), new_limit) == 0) { if (__dpmi_get_segment_limit (_my_ds ()) != new_limit) /* The DPMI host ignored the call. Fail. */ else { __dpmi_set_segment_limit (__djgpp_ds_alias, new_limit); __dpmi_set_segment_limit (_my_cs (), new_limit); _crt0_startup_flags |= _CRT0_FLAG_NEARPTR; } } else /* The call failed. */
Remember that new_limit
should have all its lower 12 bits set,
otherwise the above snippet will not work!
Q: How can I access memory-mapped peripheral devices (or any other
absolute address) above 1 MByte mark?
A: You should use DPMI functions to allocate an LDT descriptor, and map it to an absolute physical address. This maps the physical address of the memory on the device to a linear address, and returns that linear address to you. You then create a selector to access the span of linear addresses on the device.
Here are the DPMI calls that you will have to use:
All of these DPMI calls have __dpmi_XXX
wrappers in the DJGPP
library. Here's a somewhat schematic example:
#include <dpmi.h> . . __dpmi_meminfo mi; int selector; . . /* Map the physical device address to linear memory. */ mi.address = physical_address; mi.size = physical_address_size; __dpmi_physical_address_mapping (&mi); /* Now mi.address holds the linear address. */ . . /* Allocate an LDT descriptor and set it up to span the entire device on-board memory. */ selector = __dpmi_allocate_ldt_descriptor (1); __dpmi_set_segment_base_address (selector, mi.address); __dpmi_set_segment_limit (selector, mi.size - 1);
Note that the segment limit should be one less than the size. Also, segments over 1MB in length must be a multiple of 4KB, otherwise the DPMI server might fail the call, or silently change the limit.
You can then use the functions from the sys/farptr.h
header file
to access that device. See accessing memory-mapped devices, for more
details about accessing memory-mapped devices given their linear
address.
The DPMI function that is issued by
__dpmi_physical_address_mapping
only works reliably for addresses
above 1MB mark. If you call it with a physical address in the first
Megabyte, it might fail, depending on the DPMI server (e.g., CWSDPMI
fails such calls). (The DPMI spec explicitly says that programs should
not call this function to access memory below the 1MB boundary.)
This failure usually means that the offending address is already mapped
into the page tables, so you shouldn't worry about it; and most DPMI
servers map the first Megabyte 1:1 anyway.
Q: How can I make any real-mode service call my function? E.g., the
mouse driver has a provision (function 0Ch) to call a user-defined handler
when certain events occur, which expects a far pointer to my function in
the ES:DX register pair.
A: Those services expect a real-mode function, so you should wrap
your protected-mode function with a real-mode wrapper. To this end, call
either the _go32_dpmi_allocate_real_mode_callback_retf
or the
_go32_dpmi_allocate_real_mode_callback_iret
library function, as
required by the real-mode service you want to hook, and pass the
segment
and offset
members it returns to the service you
want (in the above example, Int 33h function 0Ch) by calling
__dpmi_int
.
Here's a code fragment that shows how to do this34:
#include <dpmi.h> #include <go32.h> static __dpmi_regs callback_regs; static _go32_dpmi_seginfo callback_info; int install_mouse_handler (unsigned mask, void (*func)(__dpmi_regs *)) { __dpmi_regs r; callback_info.pm_offset = (long)func; if (_go32_dpmi_allocate_real_mode_callback_retf(&callback_info, &callback_regs)) return -1; /* failure */ r.x.ax = 0xc; r.x.cx = mask; r.x.es = callback_info.rm_segment; r.x.dx = callback_info.rm_offset; __dpmi_int (0x33, &r); return (r.x.flags & 1) ? -1 : 0; }
The handler (func
in the above example) will be called with a
pointer to a __dpmi_regs
structure which is filled by values
found in the CPU registers when the mouse driver calls the handler. See
the docs in the library reference Info file for further details about
allocating wrapper functions.
Additional considerations apply if your callback is a C++ class member function. First, you need to remember that member functions expect a hidden extra first parameter. Second, if the function is virtual, you will need to lock the class's virtual table. Third, you need to lock the object itself, not only the method you call on it.
Q: How do I register my DJGPP function as a hardware interrupt handler?
A: The optimal setup depends on the interrupt frequency and on the amount of processing it requires. Therefore, only some basic considerations and techniques are listed below. What combination of these is best for your application is up to you to decide.
First, some background. Hardware interrupts can occur when the processor is either in real mode (like when your program calls some DOS service) or in protected mode. When your program runs under a DPMI host, hardware interrupts are caught by the DPMI host and passed to protected mode first; only if unhandled, they are then reflected to real mode. Therefore, in DPMI mode you can get away with installing only a protected-mode handler. However, if the interrupts happen at a high frequency (say, more than 10 KHz), and if your program spends lots of time calling real-mode DOS/BIOS functions, then the overhead of the interrupt reflection from real to protected mode might be too painful, and you should consider installing a real-mode interrupt handler in addition to the protected-mode one. Such a real-mode handler will be called before the interrupt gets to the DPMI host, and handle the interrupt entirely in real mode, so it must be written in assembly and located in conventional memory (below the 1MB mark). If you need to hook an interrupt with both PM and RM handlers, you must hook the PM interrupt first, then the RM one (because hooking the PM interrupt modifies the RM one). Also, you should know that some DPMI hosts don't allow you to hook the RM interrupt (CWSDPMI does), and some call both handlers, no matter in what mode the interrupt arrived (CWSDPMI will only call one of them); the only way to be sure is to try.
To install a protected-mode interrupt handler, you do this:
__dpmi_get_protected_mode_interrupt_vector
and save the
structure it returns (to restore the previous handler address before your
program exits).
__dpmi_lock_linear_region
. Failure to lock memory accessed
during the interrupt handling will cause your program to crash.
Alternatively, you could set the _CRT0_FLAG_LOCK_MEMORY
bit in
the _crt0_startup_flags
variable, like this:
#include <crt0.h> int _crt0_startup_flags = _CRT0_FLAG_LOCK_MEMORY;
Another possibility is to disable virtual memory by using CWSDPR0 as your DPMI server.
__dpmi_set_protected_mode_interrupt_vector
and pass
it a pointer to a __dpmi_paddr
structure filled with the value
returned by _my_cs()
in the selector
field and the address
of your function in the offset32
field.
_go32_dpmi_XXX
functions instead of the bare-bones API wrappers
whose names start with __dpmi_.
Specifically:
_go32_dpmi_get_protected_mode_interrupt_vector.
This
function puts the selector and offset of the specified interrupt vector
into the pm_selector
and pm_offset
fields of the structure
pointed to by its second argument. This data should be saved and later
passed to _go32_dpmi_set_protected_mode_interrupt_vector
to
restore the vector on exit.
_go32_dpmi_allocate_iret_wrapper,
passing it the address of
your function in the pm_offset
field and the value returned by
_my_cs()
in the pm_selector
field. The pm_offset
field will get replaced with the address of the wrapper function which
is a small assembler function that handles everything an interrupt
handler should do on entry and before exit (and what the code GCC
generates for an ordinary C function doesn't include); the effect is
similar to using the interrupt
or _interrupt
keyword in
other DOS-based compilers.
_go32_dpmi_set_protected_mode_interrupt_vector
with
the address of the _go32_dpmi_seginfo
structure you got from
_go32_dpmi_allocate_iret_wrapper
.
_go32_dpmi_chain_protected_mode_interrupt_vector.
This will set
up a wrapper function which, when called, will call your handler, then
jump to the previous handler after your handler returns. Put the
address of your handler into the pm_offset
field and the value of
_my_cs
into the pm_selector
field of the
_go32_dpmi_seginfo
structure and pass a pointer to it to this
function. _go32_dpmi_chain_protected_mode_interrupt_vector
allocates the wrapper internally, and also arranges for the interrupt to
call your handler, so you need not call
_go32_dpmi_allocate_iret_wrapper
and
_go32_dpmi_set_protected_mode_interrupt_vector
functions
yourself. Also note that currently,
_go32_dpmi_chain_protected_mode_interrupt_vector
doesn't return
to you the address of the wrapper it allocates, so that wrapper cannot
be freed by your program. It will be freed by the DJGPP exit code,
though, so this issue is only of concern to programs that allocate and
free lots of wrappers.
The problem with writing handlers in C as above is that in practice you
can't lock all of memory the handler itself uses, because there's no
standard way of finding the size of the code of a C function, or the
addresses on the stack used by C code. Thus, this approach is generally
unsuitable for production-quality software and should be used only when
the program is known not to page (i.e., if only the physical memory is
used). You might consider disabling virtual memory to make sure your
program doesn't page. To accomplish this, either set the
_CRT0_FLAG_LOCK_MEMORY
bit in the _crt0_startup_flags
variable, or use CWSDPR0 or PMODE/DJ as your DPMI host. In fact, using
one of these methods is the recommended way of debugging the first
versions of a program that hooks hardware interrupts; only after you are
sure that your basic machinery works should you move to testing it in a
setup when paging might happen.
Additional considerations apply if your interrupt handler is a C++ class member function. First, you need to remember that member functions expect a hidden extra first parameter--this is important if you use member functions as callbacks. Second, if the function is virtual, you will need to lock the class's virtual table. Third, you need to lock the object itself, not only the method you call on it.
Note that _CRT0_FLAG_LOCK_MEMORY
is only recommended for small
programs that run on a machine where enough physical memory is always
available, because the startup code currently doesn't test if memory is
indeed locked, and if there's not enough physical memory installed to
page in all of the memory your program needs, you can end up with
unlocked or partially unlocked memory, which will crash your program.
If you want to make sure all memory is locked, use a DPMI server which
disables paging.
Buffers in conventional memory (allocated via the
__dpmi_allocate_dos_memory
function and its equivalents)
generally need not be locked, since most DPMI servers lock DOS memory by
default. For safer code, you could try to lock them, and if the call to
__dpmi_lock_linear_region
returns a failure indication, it means
that the buffer is already locked.
It is possible to lock only the code and data segments of your program, but leave everything else unlocked. The following code snippet shows how:
#include <crt0.h> int _crt0_startup_flags = _CRT0_FLAG_LOCK_MEMORY | _CRT_FLAG_NONMOVE_SBRK; int (main (void) { _crt0_startup_flags &= ~_CRT0_FLAG_LOCK_MEMORY; ... }
This locks the .data
, .bss
, and .text
segments of
the program, and its stack, but doesn't lock the heap allocated after
main
is called.
To install a real-mode interrupt handler, you do this:
__dpmi_get_real_mode_interrupt_vector
and save the structure
it returns (to restore the previous handler address before your program
exits).
__dpmi_allocate_dos_memory
and put the code of your handler there with the dosmemput
function.
(You could also call one of the functions which allocate a real-mode
call-back, but these will cause a mode switch on every interrupt, which you
want to avoid; otherwise there is no point in installing a real-mode
handler, right?)
__dpmi_allocate_dos_memory
returned into a
__dpmi_raddr
structure (the lower 4 bits into offset16
field, the rest into segment
field), then call
__dpmi_set_real_mode_interrupt_vector.
Note that Windows 9X is reported to call both the RM and PM handlers if both are installed, at least for some interrupts (CWSDPMI only invokes one of them). So, if you want to play safe, you will need some kind of a semaphore variable that the two handlers could use so that only one of them actually handles the interrupt in any given case.
The DPMI spec says that 3 software interrupts are special, in that they also get reflected to a protected-mode handler. These interrupts are: 1Ch (the timer tick interrupt), 23h (Keyboard Break interrupt), and 24h (Critical Error interrupt). This means that, to catch these interrupts, you need to install a protected-mode handler only. Unlike hardware interrupts, it doesn't make sense to install dual RM and PM handlers for these software interrupts. In particular, Windows will call both RM and PM handlers if you install both, so you effectively wind up handling the same interrupt twice.
For examples of installing and using hardware interrupt handlers, see
the sources of the Allegro library, the sample code written by
Bill Currie, the Sound Blaster
interrupt-driven functions, the mkkbd
package, and the
libhw
library, described under sample DJGPP packages.
Alaric B. Williams has written a
tutorial on interrupt handling.
The DJGPP User's Guide includes a chapter on hardware interrupts, written by Peter Marinov, which includes sample code for hooking hardware interrupts.
The file src/libc/go32/dpmiexcp.c
in the DJGPP library sources,
djlsrNNN.zip
, is one example of the subtleties involved with
installing a real-mode interrupt handler. The handlers themselves are
in the file src/libc/go32/exceptn.S
.
Q: In v1.x I was used to the _go32_...
functions, but
now comes v2 which also has __dpmi_...
functions. Are there
any differences between these two varieties?
Q: Do I need to convert my old v1.x code to use the new
__dpmi_...
functions?
A: These two groups of functions have different functionality, so
don't just substitute the new ones for the older ones, because it
usually won't work! The new __dpmi_...
functions are just
bare-bones wrappers of the DPMI API calls36 (see DPMI Specification), generally unsuitable for use with
handlers written in C, whereas the old _go32_...
functions
are intelligent helper routines which only make sense if your interrupt
handlers are C functions. They save all the registers on the stack (to
be restored before return to caller), and set up DS, SS, and
ES registers as GCC assumes in the code it produces for a C
program. If these assumptions are wrong, the C functions called by an
interrupt handler will crash miserably.
The problem with the _go32_...
functions is that they don't
lock all the code and data that your handlers use, so they can crash on
memory-tight machines and thus aren't suitable for production-quality
code. But they are certainly useful in the initial stages of writing
and debugging code that hooks hardware interrupts, and for migrating
existing v1.x code to v2. Some of the old names were just
#define
d to the new names where the functionality is identical.
The bottom line is that it shouldn't be necessary to convert your code
for it to work at least as well as it did in v1.x; but if you want it to
be more stable, you should rewrite your handlers in assembly and use the
new __dpmi_...
functions. See How to install a hardware interrupt handler.
Q: I did everything you tell me to install the interrupt handler
correctly, but my program occasionally still hangs....
Q: From time to time my program crashes with a message "Page Fault
in RMCB". What's that?
A: Hooking interrupts in DJGPP (and in protected mode in general) has a few subtle aspects. In general, hardware interrupt handling in DJGPP v2.x is rock solid if you play by the rules. Unfortunately, the rules are a bit tricky.
One cause of your problems might be that your interrupt handler or some
memory location it uses get paged out because of the virtual memory
mechanism, or because your program spawned a child program. In that
case, the interrupt might cause a call to a non-existent service
routine, with the obvious results. You should lock all the memory pages
that your handler accesses by calling the
__dpmi_lock_linear_region
library function. This also means in
practice that you should write your handler in assembly, as described in
how to set an interrupt handler, above. You can disable virtual memory, or put
_CRT0_FLAG_LOCK_MEMORY
into _crt0_startup_flags
to make
sure nothing is paged out (but then your program might not have enough
memory to run, unless you run on memory-abundant systems).
When CWSDPMI detects that your handler accesses memory that is not locked, it aborts your program with a message saying "Page Fault in RMCB". This can happen if your program installs a callback for some real-mode service, like the mouse callback, as well as if you install a hardware interrupt handler; in both of these cases you need to lock all the memory touched by your handler or by functions it calls. CWSDPMI aborts your program if your program attempts to page while an interrupt handler or a real-mode callback are active, because paging uses DOS file I/O. Since DOS is non-reentrant, if the hardware interrupt handler was called in a middle of another DOS call, paging could badly damage your hard disk37. By refusing to page in these cases, CWSDPMI ensures the stability of your system and integrity of your files. You pay for that stability by having to lock all code and data touched by the handler.
Another problem might be that the hardware peripheral you use generates a lot of interrupts. Due to specifics of hardware interrupts handling in protected mode, there is a substantial overhead involved with reflection of interrupts between real and protected modes. For instance, on a 486DX/33 this reflection might consume up to 3000 clocks; on a 386SX/16, even a 1KHz clock might eat up 1/2 of available cycles. One user reported that a 120 MHz Pentium will be able to service up to 45-50K interrupts per second before exhausting its CPU resources, and a 486DX/50 is capable of about half that number. If your hardware fires too many interrupts, your CPU might not be able to keep up. A good rule of thumb is to consider 20KHz as the breaking point, if your program needs to do something non-trivial besides servicing interrupts. If you are beyond that interrupt rate, consider reducing the interrupt frequency, or move some of the processing done inside the interrupt handler to some other place. Use a ring-0 DPMI server such as CWSDPR0 or PMODE/DJ (of these two, the latter is the faster one) which don't swap interrupt stacks--this will reduce the overhead of the interrupt reflection to some degree. If your handler is written in C, write it in assembly and make sure it doesn't chain. And most important--make sure your program keeps the processor completely in protected mode while handling high-frequency interrupts: avoid unnecessary library calls, disk I/O, BIOS calls, and anything else that could generate a mode switch. For example, using BIOS services to wait a certain period of time while interrupts come in is clearly a bad idea when the interrupts come at high frequency.
Installing a good memory manager will usually also remove most of the mode switch overhead, since a memory manager runs the CPU in V86 mode, where hardware interrupts are delivered in protected mode by the processor, without any need for a mode switch.
Preventing the program from paging (by installing enough physical RAM and using memory efficiently) will also help keeping the CPU in protected mode, since paging is done by calling DOS in real mode. By keeping your processor in protected mode as much as you can, you avoid the expensive mode switches when the interrupts are reflected to your PM handler.
If all that still doesn't help, install a real-mode handler.
Some losing memory managers, notably EMM386, were reported to induce a high interrupt handling overhead. In one case, a user reported an increase in the maximum interrupt rate his program could support from 2 KHz to 6 KHz after uninstalling EMM386.
Still another possibility is that you use a non-default sbrk
algorithm in your program. Check if the header file crt0.h
is
included anywhere in the program, and if so, if the
_CRT0_FLAG_UNIX_SBRK
bit in the _crt0_startup_flags
variable is set by the program. If it is, then a hardware interrupt
which happens at the wrong time could crash your machine, especially if
you run under Windows 3.X.
You should also keep in mind that the DPMI server can decide to handle
some of the interrupts itself and not pass them to your program,
although this is rare. For example, Windows 9X won't pass the
Ctrl-Alt-Del combination to your keyboard interrupt handler, but
will rather act on it itself; QDPMI sometimes processes Ctrl-C
keypresses so that your program never sees them, etc. Sometimes, but
not always, you can change some configuration option to make some keys
get to your handler (e.g., the Alt-TAB setting on the Windows3.X
.PIF
file).
If the above still doesn't explain your problem, then post your code on the DJGPP mailing list or the comp.os.msdos.djgpp news group, tell there how it fails and somebody will usually have a solution or a work-around for you.
Q: I am used to writing inline assembly with Borland C, but can't
figure out the way to do it with GCC....
Q: How can I reference C variables from my inline assembly code?
A: GCC has extensive inline assembly facilities. They allow you to specify everything other compilers let you (like the registers where GCC will put specific results), but in a way that doesn't interfere with the compiler's optimizations of the C code that includes inline assembly. Because of this flexibility, the syntax of the inline assembly code is very different from the other DOS-based compilers. The GCC on-line docs describe these facilities in detail; to read the relevant sections, type this from the DOS prompt:
info gcc "C Extensions" "Extended Asm"
(Note the quotes: they are important.) You will, of course, need the
stand-alone Info reader to be installed on your system for the above
command to work. If it is not already installed, get the file
v2gnu/txi40b.zip
from the DJGPP distribution and
install it.
If you read this FAQ via WWW, you can also read about the GCC inline assembly extensions with your Web browser. Brennan Underwood has written a tutorial on using inline assembly, which is another valuable resource on this issue.
Q: How do I use DMA with DJGPP programs?
Q: I want to use DMA, but I don't know how to get the physical
address of the buffer I allocate for that purpose.
A: The main problem in using DMA with DJGPP is how to get the
physical address of a buffer, which is required to program the DMA
controller. In protected-mode environment, memory addresses that your
program manipulates are actually offsets from the base address of the
data segment. You can obtain the base address of the data segment by
calling the __dpmi_get_segment_base_address
library function and
add it to the address of your buffer, but the resulting address is a
logical address, translated into a physical address by the
memory-mapping unit which is part of the CPU.
You have several alternatives to get the physical address of your buffer:
__dpmi_allocate_dos_memory
.
This method has a disadvantage of using conventional memory which is at a premium, and is therefore generally ill-suited for large DMA buffers.
EMM386
or QEMM
, to run, since only memory
managers and Windows support the VDS API (except that Windows/NT
doesn't, or so I'm told). In other words, if you use VDS, your program
won't work on a system where CWSDPMI38 is used as the DPMI server, allocating memory by
raw XMS calls or via HIMEM.
The following snippet tests bit 5 of the BIOS data area at 0040:007B, to see whether the VDS API is supported:
#include <go32.h> #include <sys/farptr.h> int vds_available = (_farpeekb (_dos_ds, 0x0047b) & 0x20) != 0;
(However, an explicit call to the VDS API, after the test above shows that the bit is set, is a more reliable way to detect the VDS support.)
To use VDS, you allocate a buffer, then use the Lock DMA Buffer Region function (AX=8103h) to get its physical address. Then you call the Disable DMA Translation function (AX=810Bh), program the DMA controller with the physical address returned by the Lock DMA Region function, and start the transfer. After the transfer, you need to call the Enable DMA Translation (AX=810Ch) and Unlock DMA Buffer Region (AX=8104h) functions.
Alternatively, you could use the Request DMA Buffer and Release DMA
Buffer services, but then you will need to copy the data to and from the
DMA buffer (e.g. using the movedata
function).
The VDS method is convenient when your program needs to work in several different environments, such as both DOS and Windows, and if you don't want to waste the conventional memory. Experience shows, however, that VDS is inconvenient for buffers larger than 128KB, because many implementations of VDS fail for large buffers, in particular in plain DOS. If you need large DMA buffers, use the XMS method, described next.
__dpmi_simulate_real_mode_procedure_retf
(don't forget to zero
out the SS and SP registers!) passing it the address you got
from the 2Fh/4310h call. You need to allocate a buffer with XMS
function 09h, then lock that buffer with function 0Ch; the last call
returns the 32-bit physical base address of the allocated block, with
which you program the DMA controller. You can then map the physical
address to a linear address and build a descriptor that spans the entire
buffer, in the same manner as you would map a memory-mapped device, see
mapping physical address to linear address. This gives you a selector which you can
use to copy data between your program and the DMA buffer with
movedata
and "farptr" functions.
The XMS method is especially suited to very large DMA buffers, like 2MB (these obviously cannot be allocated in conventional memory). It is also supported by more system configurations used to run DJGPP, since even HIMEM supports XMS.
This chapter answers some questions about various legal aspects of writing programs with DJGPP.
Q: Can you explain in plain English the legal restrictions of
distributing programs compiled with DJGPP?
Q: Can I write commercial programs with DJGPP?
A: In most cases, you don't have to worry about any legal restrictions when you compile your programs with DJGPP. You only need to include information on how to get DJGPP, and a few other bits of information, as explained below, in the documentation of your software39.
Using the GNU C/C++ compiler doesn't make your programs subject to
any restrictions. The C library which comes with DJGPP is
free (unless you change the library sources, see below), which
means you are free to use the stock libc.a
in any way you like
(but please try to comply with basic rules of courtesy.)
Some functions from the DJGPP C library are under the BSD copyright
(their sources were taken from the Berkeley Software Distribution of
Unix). These are time-related functions time
, ctime
,
gmtime
, localtime
, mktime
, and asctime
, and
also tzset
and tzsetwall
. random
and related
functions srandom
, setstate
and initstate
are also
from the BSD distribution.
The BSD copyright used to require that your binary distribution displays an acknowledgment of the BSD origin of these functions somewhere in the docs and in all the ads. However, as of July 1999, the University of California at Berkeley withdrew that requirement, and does not require to include that blurb anymore.
So, if you write C programs and link them with the stock version of the DJGPP library, you only need to tell your recipients how to get the latest versions of DJGPP, and have absolutely nothing else to worry about.
The basic C++ classes and the Standard Template Library
(libstdcxx.a
) which come with DJGPP allow you to use them
binary-wise (i.e., without changing library sources) in your C++
programs without restrictions, unless you compile your programs
with a compiler other than Gcc (which won't happen if you work with
DJGPP). So C++ programs linked with the -lstdcxx
switch are
also free from any restrictions.
Only the library of additional GNU C++ classes (libgpp.a
)
requires that you provide your customers with source or object code of
the application, so they could relink the application with future or
modified versions of the C++ library. However, this library is
deprecated and chances are most C++ programs won't use it. (If you
intend to distribute commercial programs linked with the libgpp.a
library, you are strongly advised to read the GNU Library General Public
License which comes with the library, for rigorous definition of its
terms.)
Two GNU packages, Flex
and Bison
, are also special in that
using them to produce your programs doesn't place your programs under GPL
or LGPL. In other words, lexers produced by Flex
and parsers
produced by Bison
do not imply GPL/LGPL.
If you do use in your program any of the FSF sources that fall
under GPL/LGPL (like some of the GCC's sources, or the GNU getopt
or regex
packages which come with many GNU programs), then you
must comply with the terms of GNU licenses when distributing your
programs; in this case your entire application becomes GPL. If that is
unacceptable to you, consider using the versions of regex
and
getopt
from the DJGPP C library, which are not as powerful, but
are free from any restrictions.
You may ship any of the utilities developed specifically for DJGPP
(e.g., the floating-point emulator emu387.dxe
or the DPMI host
cwsdpmi.exe
) and the C library, as distributed by DJ
Delorie, with your program with no other requirement besides telling
your customers how to get DJGPP for themselves.
If you do change the sources of either the C library or the utilities
distributed with the djdev
package, they, and the programs
developed with them, immediately fall under the GPL, the GNU License.
In practice this means that you cannot distribute any binaries made with
such a patched version of libc.a
without offering the recipient
full sources, including your own sources. However, if you find bugs in
the library or the utilities and submit your patches to DJ Delorie, DJ
allows to freely use and redistribute patched utilities and binaries
made with the patched version of libc.a
(even if no official
DJGPP version was released with your patches yet).
For the precise legal terms of DJGPP distribution, see the file
copying.dj
via the Web. Latest versions of
the djdevNNN.zip
package also include that file, so look for it
in you DJGPP installation directory.
Note that the above says nothing about the legal aspects of contributed
packages, like GRX
and others; you will need to read their docs
to find out.
Q: Can I redistribute djgpp, and if so, how?
Q: I run a business that sells shareware for distribution costs. Can
I include djgpp on my CD-ROM?
Q: I want to include djgpp in a product that happens to need a
compiler provided with it. Can I do this?
Q: Is DJGPP public domain software?
Q: Is DJGPP shareware?
A: DJGPP is not public domain, neither is it shareware (you don't have to pay a license fee to use DJGPP). Parts of DJGPP (the compiler and some of the development tools) are GNU software, so you must comply with GNU GPL if you distribute those parts (usually, you won't need to distribute them, because they are freely available to everyone). A small part of the C library is taken from the Berkeley BSD sources, and is therefore in public domain.
Other parts of DJGPP, which include most of the C library, the free DPMI host CWSDPMI, and some of the utilities, are copyrighted, but in a way that allows you to use them freely and without restrictions. The copyright that covers these parts of DJGPP is GPL, the GNU License, but with a special exception: if you distribute the utilities unmodified, or build programs with the unmodified library, the GPL does not apply.
Compliance with GPL is therefore all you are legally required to consider when you redistribute DJGPP itself (as opposed to your programs compiled with DJGPP). However, based on many years of experience of DJGPP distribution, DJ Delorie requests vendors which distribute DJGPP to follow some additional rules. These rules are generally meant to provide a better service to the DJGPP user community:
In addition, it would be a courtesy to inform DJ that you are including DJGPP in your product, in case this information is obsolete. A token sample of your distribution would be nice also.
Note that the above are not legal restrictions (the latter are
described in the file copying.dj
mentioned in the previous
section), they are recommended guidelines for redistributing
DJGPP. These guidelines are based on many years of experience and are
generally meant to make it easier for your clients to use DJGPP and get
support from its developers. Vendors who do not follow these guidelines
could risk public humiliation, verbal abuse, and boycott by the DJGPP
community, but not legal action.
Note also that if you make source-level changes to DJGPP library or utilities, the changed software falls under the GNU License, GPL, unless these changes are made to fix bugs, and provided that you also submit all such bug-fixes to DJ Delorie for inclusion in a future DJGPP release.
This chapter tells you how to get answers to questions you didn't find in this FAQ, by asking them on DJGPP-related forums.
Q: I post my problem to the gnu.gcc.help news group, but don't get any
answers....
A: Is your problem likely to be special to the DJGPP port or to the DOS environment? If so, don't post to GNU Usenet groups, but to the comp.os.msdos.djgpp news group or to the DJGPP mailing list. People who read GNU News groups usually neither know nor care about DOS-specific problems. Post there only if the problem seems to be generic to one of the GNU utilities on any platform. For most problems, this can be deduced only after either tracing a problem in the source code or testing it on some non-DOS platform. As a general rule, always post to the DJGPP forums first.
Q: How do I post to the DJGPP mailing list?
A: Send mail to the list address as if
it were a person. Please use the mailing list only if you cannot access
the DJGPP news group, because reflecting the mail to and from the mailing
lists incurs additional load on the DJGPP server. The DJGPP news group,
<news:comp.os.msdos.djgpp
>, is two-way gated to the mailing list.
This means messages posted to either the mailing list or the news group
will appear on both (once, let's hope ;-); you can read either one
and post to either one, and everybody eventually sees everything. So
please don't post to both the news group and the mailing list.
The gateway works on DJ's server, and has a very strict anti-spam filter which prevents spam from getting into the news group; it also has an additional keyword-based anti-crap filter that doesn't pass spam to the mailing list. The entire traffic ends up in the mail archives on the DJ's Web server within 24 hours, and is available for searching.
If you have a Usenet feed, now is the time to consider unsubscribing from the mailing list and switch to reading the news group instead, so that the load on the list server will get lower.
Q: How do I subscribe to the DJGPP mailing list?
A: Send mail to the list server (NOT to djgpp@!!), leave the subject line empty and in the body write:
subscribe <your e-mail address> djgpp
If you only want to receive announcements of new versions and ported
software, but don't want to see any other DJGPP mail traffic, subscribe
to the djgpp-announce
by sending message to the list server which says so:
subscribe djgpp-announce
(Note: no email address when subscribing to the djgpp-announce
list!)
The announcements which go to djgpp-announce
get reflected to
djgpp
, so you don't need to subscribe to both these lists.
The DJGPP mailing list is available in the daily and weekly digest forms. To subscribe to one of these, send this one-line message to the above list server:
subscribe <your e-mail address> djgpp-digest-daily
or
subscribe <your e-mail address> djgpp-digest-weekly
Some mailers reject messages with too large size, so you might have trouble with the weekly digest. If you subscribe to it and don't get the digest, try the daily one instead, or switch to another mail software.
You can also subscribe to DJGPP-related mailing lists through DJ Delorie's WWW server.
Note that you don't have to subscribe to the djgpp mailing list if you don't want to get all the traffic in your mailbox (typically, about 30 messages per day). You can ask questions on the list even if you are not a subscriber, because people usually answer both to your e-mail address and to the list (well, actually, the mailer program does it automatically and most people don't bother to change that). If you want to be sure the mail gets to you directly, say in your message that you don't subscribe to the list, and ask people to answer directly. Be sure to provide a valid return address (remove any anti-spam, if you use one) when you ask for direct replies.
Q: Whew! There's too much traffic on the djgpp mailing list (at least
the SysAdmin glaring over my shoulder thinks so... ;-). How do I
unsubscribe myself?
Q: I've been trying for days to unsubscribe from the djgpp mailing
list. What am I doing wrong?
A: You should send your unsubscribe messages to the list server (not djgpp@delorie.com!), with the contents being just this:
unsubscribe <your e-mail address> djgpp
When you unsubscribe, that stops new messages from being sent to you. Messages that are already in the mail queues of various mail programs between the DJGPP list server and the machine where you receive your mail--cannot be stopped. Therefore, allow some time before you decide that your unsubscribe message didn't work. In extreme cases, when one of the machines that are forwarding mail to you is down, you can get the messages up to 5 days after you've unsubscribed.
If you think you have waited enough and the messages still keep coming, write to listserv administrator and ask him to help you.
You can also unsubscribe yourself from any DJGPP-related mailing list through DJ Delorie's WWW server.
Recently, DJ has added a mail archive browser to his Web site. With this tool, you can list and read the messages by year, month and day, as well as search the last few days for something you might have missed. This service is available via World-Wide Web.
Q: Would you please switch to English in your messages instead of
using some language nobody understands??
A: It is rude to require that people speak your language when you don't speak theirs. Therefore, any language is allowed on the DJGPP forum.
It is true that, since most people who read the DJGPP news group do speak English, posting in English will bring more answers and thus more efficient help. So, for the most efficient help, it is best to post in English. But this is not a requirement. If a DJGPP user has a question, but cannot express it in English, it is better to post it in some other language than not to be able to post it at all.
Some people can read English (and so will understand replies posted in English), but have difficulty writing in English. Therefore, if you see a message in a language other than English that you happen to know, please consider posting its translation, so that others could reply to it.
If you see a message in a language you don't understand well, and want to reply to it, try the Babelfish on-line translator. I'm told that it is not very good and sometimes returns an utter nonsense, but if its translations are treated creatively, it might help you understand the question (or the answer, as the case may be).
This chapter is for those who want to know where are the latest updates for DJGPP software, and what's new and improved in them. It also explains the differences between DJGPP v1.x and v2, for those who are still using DJGPP v1.x and want to know more about v2 while they consider switching.
Q: What exciting new features will I find in v2 as opposed to v1.x?
A: DJGPP v2.x is a DPMI-only environment, and it includes a free DPMI host for those who don't have another DPMI provider installed. In addition, v2 features the following major improvements upon v1.1x:
If you want to help in further v2 development, check out the list of features which have yet to be done and volunteer to implement some of them.
Q: There's been this talk about v2 and about go32
going away
in that version, but I'm confused on what the new setup will be. Could
you clarify the details of this change?
A: In v1.x of DJGPP, the go32
extender was responsible for the
following:
In v2.x, a minority of these functions are done by a DPMI host, which is
a memory-resident software required to run protected-mode programs under
MS-DOS. There are a few commercial DPMI hosts (like Quarterdeck's
QDPMI
, Qualitas 386Max
, MS-Windows 3.X and Windows 9X, OS/2,
even Linux), but DJGPP v2 comes with a free DPMI host called
CWSDPMI
for those who don't have one already. Loading the
application into protected-mode memory (a function done in v1.x by
go32
) is handled by a 2KB-long real-mode stub which runs at
start-up, before the application's main
functions is called
(the stub will also load CWSDPMI
if no other DPMI host is
detected). All the other custom code required to process BIOS- and
DOS-related calls from protected-mode is now built into the library
functions which your program calls, so there is no need for a special
extender, because the application just issues DPMI calls serviced by the
DPMI host.
CWSDPMI
can be loaded as a TSR, even loaded HIGH
into the
HMA/UMB, which will make applications load much faster.
Q: It's more than a year since the last DJGPP version was released.
Why won't you guys upload a new version in all this time?
Q: I've been suffering from this bug for months on end! You know
there's a bug, 'cause I told you so ages ago. So why in the world
didn't you fix that in a new version??
A: DJGPP is developed by volunteers on their free time. This sets both the schedule of new DJGPP releases and the DJGPP development agenda: they are determined by what those volunteers think is important and doable, and by the amount of free time they can devote to DJGPP-related work. Since the work of the development team is given away for free, you cannot demand that they do something they've decided not to do just yet. The only way to influence DJGPP development is to make your own contribution, by fixing a bug or adding a feature, and then submit your patches to DJ Delorie.
You don't need to look for a large project to make your contribution.
The best way to start being involved with DJGPP development is to fix
any small and minor problems you see, right when and where you see them.
Even bugs and inaccuracies in the DJGPP documentation, like the
libc.info
Info file, are a good place to begin with. DJ Delorie
says that if everybody corrects every small bug they see, we would run
out of bugs very fast.
When you submit a bug report or code that implements a new feature that you'd like to add to DJGPP, be prepared to withstand some scrutiny and peer review from the other participants of the DJGPP development team. You might hear various comments, from critique of your code and design decisions to questions why your changes are at all needed, and even requests to submit the changes in certain unified format (see instructions for submitting changes). Please be ready for that review process and don't take it as a rebuttal.
Q: There's been quite some time since the last DJGPP release. Where
can I get all the latest stuff where the bugs are fixed?
Q: I have heard rumors that there's a better C library for DJGPP
available from the net. Where is it?
A: New versions of most parts of DJGPP are released quite regularly. For example, DJGPP ports of most GNU packages are generally released short time after a new version of every package becomes available from the GNU FTP sites. So for many DJGPP packages, a new release should usually be available real soon, just stick around a bit.
The single most important component of DJGPP tool-chain that might
suffer from long release schedule is the djdev
package. This
includes the C library, libc.a
, which was written specifically
for DJGPP, and some DJGPP-specific development tools, like redir
and symify
. However, the latter are usually stable and don't
need too much fixing.
A full release of djdev
is a lot of work, so DJ Delorie decided
not to make interim releases (experience from v1.x development shows
that such interim releases also generate confusion and are hard to
maintain).
In general, you are advised to constantly improve your C library by fixing any bugs in the library sources and replacing old modules with fixed ones. All this takes is to edit the relevant source file, compile it, and put it into the library. For example, assuming you have made a source-level change in a file called foo.c, here's how you update your library:
gcc -c -O2 foo.c ar rvs c:/djgpp/lib/libc.a foo.o
(This example assumes that DJGPP is installed in the C:\DJGPP
directory; if not, you will need to change the pathname of libc.a
accordingly.)
Patching the library like that requires that you download the DJGPP
library sources, djlsrNNN.zip
(where NNN
is the version
number). That file includes sources to all the DJGPP functions and
utilities, and you can extract them as the need to edit them arises.
Bug reports regarding the library and patches to fix them are posted
from time to time to the comp.os.msdos.djgpp news group. In addition, the DJGPP
bug-tracking system, stores many
known bugs and the patches required to solve them. You can use these
resources to find solutions to known bugs. Patches are applied using
the patch
utility which is available from the DJGPP sites.
Users who need the cutting edge of the DJGPP development sources can access the latest development versions of sources of the DJGPP library and utilities via the net. All the bugfixes and new features that are accepted for inclusion in DJGPP are checked into the development source tree using CVS, a free network-based software configuration management package. DJ Delorie has set up anonymous read-only access to the DJGPP CVS tree, whereby anybody who has a CVS client installed can check out the sources via the Internet. You can check out individual source files, specific subdirectories, or the entire DJGPP tree; read the instructions for using CVS for the details. Binaries of CVS clients for Windows platforms are available from the CVS download page. If you want to install the CVS client on Unix or Linux, download the CVS sources and build it on your machine.
Note that development sources are not always as stable as the official release; some of the changes might not be tested by anyone except the person who submitted those changes. I recommend to check out only those sources which fix problems that you cannot work around.
This chapter is a hodgepodge of questions which don't belong to any of the other chapters.
.dxe
files?
Q: Why does RHIDE put all my source files in the
C:\Windows\Desktop
directory?
Q: RHIDE doesn't remember the path names of the files I edited
in my previous session....
Q: Why does RHIDE show the "Compile" option as disabled,
although there's a source file loaded?
Q: Sometimes, especially during debugging, RHIDE seems to screw
up the display, or crash, or blank the screen and hang. How can I avoid
this?
A: To prevent problems with source files not being found by RHIDE or being put into strange directories, use a simple two-step recipe:
cd
command, then type rhide file from the
command line, where file is either the name of the source file or
the name of the project file, usually with a .gpr
extension.
Project | Add
dialog from the main menu. If you use a project
file, you can have your source files in several different directories;
in that case, start RHIDE from the directory where you keep the
project file.
Alternatively, you could double-click on the project file for your
project; Windows will then invoke RHIDE in the directory of the
project file. You might need to associate the .gpr
extensions
with RHIDE, before you can use this feature.
By default, your program's .exe
executable file goes to the same
directory where you keep the project file, but the Project
dialog
lets you specify a different directory, in case you need that.
Display-related problems with RHIDE are usually caused by a faulty video driver (on Windows 9X) or bugs in the SVGA firmware. RHIDE uses advanced VESA functions to save and restore the screen contents and mode-specific settings, and some SVGAs and video drivers don't implement these functions very well. One particularly problematic SVGA card (which will remain unnamed) has bugs even in the standard VGA modes. Downloading the latest video drivers from the vendor's site and upgrading the video BIOS usually helps; if not, the only solution is to replace the video adapter.
Q: When I unzip the C++ compiler distribution in gppNNb.zip, the
unzip program complains about something called g++.exe
. What
should I do?
Q: I installed DJGPP, and found two different versions of
gxx.exe
and two versions of cxxfilt.exe
. Which one shall
I keep?
A: If you install DJGPP on anything but Windows 9X, just ignore that
error message about g++.exe
and use gpp.exe
or
gxx.exe
to compile C++ programs. g++.exe
is an
invalid file name on DOS, but is allowed on Windows 9X. It is included
for compatibility with Unix, where the C++ compiler is called
g++
. All g++.exe
does is just to run gxx
or
gpp
. So you don't lose much by not having it.
If you are installing DJGPP on Windows 9X, find an unzip program
which supports long file names and unzip the files again. Make sure
that the DJGPP long file names support (a.k.a. LFN) is enabled,
otherwise DJGPP programs such as Make won't be able to invoke
g++
. To enable LFN support, set LFN=y
in the environment.
Duplicate versions of some programs come from different distributions
that include the same programs. For example, the cxxfilt
is
part of two GNU distributions: GCC and Binutils. You should generally
kep the latest version, judging by the time stamp of the executable
file.
Q: I want to change cc1. How do I do this?
Q: How do I fix a bug/add a feature to one of the DJGPP programs?
Q: How should I produce patches for DJGPP programs I want to submit,
and to whom should I submit them?
A: First, get the sources. These are called *s.zip
in the
DJGPP distribution. The C Library sources are in
djlsr203.zip
. Some sources are too big, and
might be split into multiple zips, all of which must be unzipped to get
a complete source distribution, like this:
em1934s1.zip em1934s2.zip em1934s3.zip
All sources are shipped in ready-to-build form. Any diffs that come
with the source distribution, like the files called DIFFS
, have
already been applied, and any configuration scripts and/or batch files
have been run already; you don't need to run them again.
Next, try to build the program without changing it. Look for a file
called README.dos
or README.djgpp
: it should explain the
build procedure and list any optional packages you need to install for
that.
If such a README
file is unavailable, you will have to poke
around and figure things out for yourself; here are some hints to help
you out:
Makefile
or makefile
probably means you
could just type make.
CONFIGUR.BAT
file, possibly in a
subdirectory called djgpp/
or pc/
; if so, run it first.
MAKE.BAT
file, run it; if not, look for a file
named MAKEFILE.DJ
or MAKEFILE.DJG
; sometimes these will be
in a subdirectory called dos/
, or msdos/
, or pc/
.
If there is such a file, then type, e.g., make -f makefile.djg, if
not, just say make and see what happens.
The reason for an apparent lack of a standard here is that different packages were ported to DJGPP by different people, as best as they saw fit.
After you've successfully built the program, make your fixes and build the program the same way you did before.
Note that generally to build these programs, you must have the GNU Make program,
installed, and some makefiles require that you install additional
development utilities, like the SED editor.
Sometimes the makefiles won't even run under COMMAND.COM
(they
require a smarter shell). In that case, either get a better shell, or
convert the makefile to be runnable by COMMAND.COM
, or do the
required steps manually. If the Makefile is too complex for you and you
can't figure out what are the necessary commands, invoke make with
-n
switch and see what it would have done.
If your machine lacks floating-point hardware (like a 386 without a 387, or a 486SX), then you should know that current versions of GNU Sed and GNU Make issue floating point instructions, so you will have to make provisions for loading an emulator, see above, FP Emulation. The port of Make 3.75 and later can be built so that it doesn't issue FP instructions, but you will have to get the sources and recompile Make first, as the stock version wasn't configured in that way.
If you think that you found a bug in one of the programs or libraries written for DJGPP (e.g. the C library, CWSDPMI, symify, etc.) be sure to check the list of known bugs. If your bug is not there, you can later submit it to the bug-tracking system.
Before you submit a bug report, please make every effort to verify that your bug is not caused by incorrect usage, or by problems in your DJGPP installation. Reports such as "All DJGPP programs crash" or "I cannot compile any program" are clearly not bugs, because these things work for many hundreds of DJGPP users every day; so either your system setup is messed up or you invoke programs incorrectly.
If you can investigate the cause of the bug and find a solution that makes it go away, submit a bug report with all the details. If you cannot find the cause(s), I suggest posting your problem description to the news group and asking people to verify that it is indeed a bug, before you submit a bug report. The bug-tracking system includes a list of all known bugs, many of them with solutions or work-arounds; please check them before creating a new bug report.
Patches to DJGPP programs and ports should be sent to the person who
maintains the relevant package. Patches for the C library, utilities
and other software which comes with the djdevNNN.zip
distribution
should be sent to DJ Delorie. If you don't know who maintains a particular
package or port, post the patches to the comp.os.msdos.djgpp news group, since
the maintainer is most probably reading that group.
To generate a patch, run the diff
program (from GNU Diffutils,
v2gnu/dif272b.zip
) on the old and the new version
of a source file. For example:
diff -c src/libc/dos/dos/int86.old src/libc/dos/dos/int86.c >int86.dif
The file int86.dif
created this way should be sent to the
maintainer, with a short description of the problem it solves. It is a
good idea to run the patch file through DTOU
(a utility which
comes with DJGPP and converts DOS-style CR-LF pairs into Unix-style
newlines), since this makes the patch work on Unix as well, in case the
maintainer of the package in question does that on Unix. (The DJGPP
port of GNU patch
accept both Unix-style and DOS-style patch
files.)
Observing the following guidelines when creating the patch will make your patches easy to apply:
-c
switch to diff
, and never use
-c
with an argument that is less than 3 (for example, do
not use -c2
).
diff
from the root of the DJGPP installation, i.e. from
the directory where you keep the DJGPP.ENV
file, and specify the
files being compared with their pathnames relative to that directory.
This allows to concatenate related patches to several files, and apply
the combined patch in a single run of the patch
utility.
patch
doesn't understand backslashes. In particular, DJ Delorie
maintains DJGPP on a Unix box.
foo.old
or
foo.bak
; see the example above.
djlsrNNN.zip
distribution follow his distinct style (that style is quite obvious from
the sources, but if you are unsure, ask DJ).
diff
ignore
whitespace, like -b
or -w
. In most cases, patches
generated with these switches will fail to apply.
In addition, it would be mighty nice if every change in functionality
were accompanied by a suitable change in the relevant docs (e.g., for a
patch to a library function, edit the corresponding .txh
file
with its docs), although you are under no obligation to do that.
Q: Where can I find an example of XXXX / a package doing YYYY ?
A: Salvador Eduardo Tropea (SET) maintains a DJGPP Web Ring page. DJ Delorie offers another very large collection of DJGPP-related links.
Here is a list of places you might look into for examples of frequently needed code fragments, or for packages people keep asking about:
pmcom
, a serial
communications library for DJGPP; it is available from DJGPP sites.
v2tk/allegro
directory on the usual DJGPP sites. You can also download DZComm from
GeoCities via the Web.
libsocket
is available, originally
developed by Indrek Mandre, currently
maintained by R. Dawe. The latest version
(0.7.3, as of this writing) is available from the libsocket home page, and
via FTP from
the DJGPP archives. The latest versions support both Winsock and the
newer Winsock2 VxD which is part of Windows 98. Many useful links to
related packages and info is available from Rich Dawe's home page.
Xt
and Xmu
toolkits, a 3D version of the AW
toolkit,
a few demo applications (e.g. xmine
), and can be used to compile
Tcl/Tk
and GNU Emacs with X support. Xlibemu is based on
X11R5 and was originally developed by Antonio Costa for DJGPP v1.x. It is also available on an alternative site and on the DJGPP server.
p2c
version 1.02. the port is available
from the DJGPP sites.
v2tk
directory on DJGPP sites.
pdmlwpNN.zip
(NN
is a version number) from the v2tk
directory on the DJGPP sites.
gcc -Wall
can
identify many such cases as well, due to the superior diagnostics of
GCC, but for those who still want Lint, you can find LCLint sources on the Web.
SeeR
is a scripting engine to use in extensible programs. It
features basic C and C++ operators and data types, access to C and
C++ functions and classes, support for multitasking, ability to run
several scripts at the same time, etc. SeeR
was written by
Przemyslaw Arkadiusz Podsiadly, and is
available from his Web page.
byacc
, and bzip2
compressor/decompressor,
both ported by Juan Manuel Guerrero, are available from the
v2apps
directory on the DJGPP sites.
libwin
is a library of functions that allow DJGPP programs to
interface with some Windows services. This includes clipboard and
registry access, control of the DOS box and virtual machine titles, and
interface with Windows Virtual Devices, VxDs in short. The
library can be downloaded via the Web.
libmslot
is a library of functions for using the Windows LAN
Manager Mailslot API, which is a unidirectional form of Interprocess
Communication (IPC) facility. It was written by Richard Dawe, and is available from his home page.
LibINI
is a library for manipulating Windows style .INI
configuration files. You can find it in the programming section of
<http://stealthtech.tsx.org
>.
Q: How do I create symbolic links?
Q: I have this program that behaves differently depending on the
name it's called. Under Unix, I just create symbolic links to achieve
that, but DOS doesn't support links. Do I have to put several identical
programs under different names on my disk??
A: DJGPP allows you to simulate symbolic links to programs. Generate
a stub (which is a small DOS program attached to every DJGPP program by
the stubify.exe
program), call it by the name of the link you want,
then edit its header to run another program. For example, let's say the
real program is dj1.exe
and we want to make a link called
dj2.exe
that really calls dj1.exe.
First, generate a stub
under the name dj2.exe.
Next, run STUBEDIT
to modify the
new program's stub info block and change the name of the executable it
runs. In this case, we'd change it to dj1
:
C:\USR\BIN> stubify -g dj2.exe C:\USR\BIN> stubedit dj2.exe runfile=dj1
Voila! Now, when you run dj2
, it tells the stub to load the image
of dj1
, but pass "dj2" in argv[0].
If you use the DJGPP port of GNU Fileutils 3.13 or later, the ln
program there can do the above steps for you if you say this (like on
Unix):
ln -s dj1.exe dj2.exe
Q: What is this DPMI thing you are all talking about?
Q: Where can I find the specifications for the DPMI functions?
A: DPMI, the DOS Protected-Mode Interface, is an API that allows protected-mode programs to run on top of DOS, which is a real-mode operating system, and still be able call real-mode DOS and BIOS services. A special API is required because DOS code cannot be run in protected mode: if you try, your system will immediately crash, since the real-mode DOS and BIOS code violates many restrictions of protected-mode programming. See restrictions of protected mode, for more details. Another place to look for explanations why DPMI is necessary is in the Overview of DJGPP.
The DPMI API is implemented as a set of functions of Interrupt 31h which allow such chores as switch from real to protected mode and back (generally done upon startup and at exit), memory allocation, calling real-mode services, etc. DPMI is by far the most portable way of running protected-mode programs on MS-DOS, MS-Windows and compatible systems.
You can find the DPMI 0.9 spec by anonymous ftp to one of the following sites:
dpmip1.zip
at the same location is the PostScript
version of this spec), and also at the Oulu site.
DPMI
.
Q: Where is the DJGPP Web site?
A: Yes, DJGPP has its own home on the Internet, set up and maintained by (who else?) DJ Delorie. It has an HTML version of this FAQ list with search capabilities, the entire set of DJGPP distribution files, a searchable archive of the DJGPP mailing list and news group traffic, plus other useful and interesting information about DJGPP. For instance, did you ever wonder how DJGPP got started and what DJ's original goals were? Rejoice: the Web site includes the story of DJGPP genesis.
To visit, point your browser to the DJGPP Web site.
Q: I wrote a program using DJGPP. How can I make it available to
others?
Q: I found and corrected a bug in one of the programs distributed with
DJGPP. Where should I put it?
Q: What should I do to upload my DJGPP package to SImTel.NET?
A: If your program/patches are small enough, consider posting it to the mailing list or the the comp.os.msdos.djgpp news group. Please do not post binaries to the news group; only post source code as plain text, and only if it is not too large. Many people who read the news group cannot save the MIME attachments, and don't like to be forced to download a large message; you are requested to respect that.
If the program is larger than, say, 50K bytes, it's best to upload
it to a public site where everybody can get it. You can upload your
contribution to a special directory on the DJ Delorie's FTP server. This directory is write-only, and it gets
purged every couple of days, so be sure to write to DJ Delorie about your upload;
he will then move it to the /pub/djgpp/contrib
directory.
If you decide to upload, please send mail to the djgpp-announce
list with a brief description of your program/patch. (The message will
get reflected to both the news group and the DJGPP mailing list, so you
don't have to cross-post there, but it also goes to people who only
subscribe to djgpp-announce
list because they want to get such
announcements and nothing else.)
If your program is more than a patch or a beta version, you might consider uploading it to the DJGPP archives on SimTel.NET. Material uploaded there gets automatically distributed to all of the SimTel.NET mirrors throughout the world, which makes it easier to get.
DJ Delorie requests that all contributed packages uploaded to his server
be source-only distributions, if at all possible. This is so there will
be no danger of distributing programs infected by a virus. Please avoid
uploading self-extracting archives because DJ extracts them on a Unix
machine which can't run DOS executables. Detailed instructions for
packaging and uploading DJGPP-related files to SimTel.NET are available
in the DJGPP HOWTO repository. When the package
appears on SimTel.NET mirrors, send an announcement to the
djgpp-announce
mailing list.
Q: I want to use DJGPP as a cross-compiler for Motorola 68K targets.
How should I proceed about this?
Q: I want to build GCC as a Unix-to-DOS cross-compiler. What should I
do?
A: If you want a cross-compiler for m68k on a DOS machine, you need
DJGPP configured as host=i386-go32
, and target=m68k-coff.
Such a package is already available. The binaries there are
based on GCC 2.7.2. This package is reportedly no longer supported, but
if you have questions about it, you can send them to Jim Karpinski. You can also try to contact Kai Ruottu, who is the provider of DOS-hosted
gcc-m68k. Note that this package has only basic support for C++:
the compiler is included, but the libraries, including libstc++
and libg++
, and the C++ headers are missing, so even
cin
and cout
don't work.
A Win32-hosted gcc-m68k is another possibility. It was created by David Fiddes. The Cygwin port of GCC can also be configured as a cross-compiler with m68k as the target. See the description of the Cygwin project, for more details about the Cygwin port. Object Software Inc. is a company that supports cross-builds based on GCC and DJGPP. Pre-built binaries of the compiler and some minimal development tools are available from this site for m68k, PowerPC, and Hitachi's SH-3 targets, all based on DJGPP v2. The CrossGCC FAQ, available from the same site, includes detailed instructions for building a cross-compiler for any target.
DJGPP can be built and installed as a cross-compiler running on a Unix machine and targeting DOS/Windows platforms. Detailed instructions for doing this on Linux can be found in the DJGPP HOWTO Repository. Here is the summary of the necessary steps to do that:
v2/djcrx203.zip
from the usual DJGPP sites.
djcrx203.zip
on the Unix machine using
unzip -a
.
You must use the -a
switch, to force unzip
to
convert any DOS-style text file with CR-LF pairs at the end of each line
to Unix-style text files. If you don't, things will break for you.
unzip
is available in source form from many FTP sites. Linux
systems generally come with unzip
, but if your system doesn't,
download the sources and build it.
cross
directory to the top-level
directory, where you unzipped djcrxNNN.zip
.
The main GNU site is at ftp.gnu.org.
djcrxNNN.zip
.
cross/install
that was unpacked
from djcrx203.zip
to build and install the
cross compiler.
The cross-compiler you build is installed as dos-gcc
, so to
compile programs with it, use dos-gcc
rather than cc
or
gcc
, or set CC=dos-gcc
when invoking Make.
The file cross/readme
has some usage info for dos-gcc
. It
is generally correct, except that the version numbers for the various
packages might not be up to date. You should always use the latest
releases of every package.
Another alternative is the build.cross
script included in the
source distribution of the DJGPP port of GCC, gccNNNs.zip
. This
script was tested on GNU/Linux systems, and it allows to build GCC as a
cross compiler hosted on a GNU/Linux system whose target is
DJGPP-supported platforms. To use this script "out of the box", you
will need to take care of some preliminaries:
stubify
program is somewhere on your PATH
;
djcrxNNNb.zip
package) are in the
prefix/i586-pc-msdosdjgpp/include
directory;
gppNNNb.zip
package) are in the
prefix/i586-pc-msdosdjgpp/lang/cxx
directory;
prefix/i586-pc-msdosdjgpp/lib
directory;
i586-pc-msdosdjgpp
are on your
PATH
;
prefix/i586-pc-msdosdjgpp/version
,
where version is the version of GCC, exists.
Here, prefix is the root of your cross-development installation,
usually /usr
or /usr/local
.
Q: There is a severe bug in GCC: it says "garbage at end of
number" for this line:
i = 0xfe+0x20;
Ain't it silly that such a great compiler would fail so miserably?
A: That's not a bug, that's a feature of the ANSI C language
definition. By ANSI rules, the above expression is a single
preprocessing token, unless you place whitespace in front of the
plus sign. The reason for this seemingly counterintuitive feature is
the syntax of floating-point constants in which letters `e' and `E'
followed immediately by a sign signal a decimal exponent. You can use
the -traditional
compiler switch to turn this feature off
(however, it will also turn off a plethora of other ANSI features; see
the GCC docs for details).
Judging by the published draft, this is unchanged even in the forthcoming C9X standard.
Q: When I call sizeof
on a struct, I sometimes get values
which are larger than the sum of the sizes of the struct members, whereas
in Borland C++ I always get the correct result. Is it a bug in GCC?
Q: I have a program that reads struct contents from a binary file.
It works OK when compiled with BC, but reads garbage when compiled with
DJGPP. This must be a bug in DJGPP, right?
A: No, it's not a compiler bug. GCC generates 32-bit code, and in that mode, there is a significant penalty (in terms of run-time performance) for unaligned accesses, like accessing a 16-bit short which isn't aligned on a 16-bit word boundary, or accessing a 32-bit int which isn't aligned on a 32-bit dword boundary. To produce faster code, GCC pads struct members so that each one can be accessed without delays; this sometimes produces struct size which is larger than the sum of the sizes of its members. If you need to minimize this padding (e.g., if your program uses large arrays of such structs, where padding will waste a lot of memory), lay out your structures so that the longer members are before the shorter ones. For example, let's say that you have a struct defined thus:
struct my_struct { char name[7]; unsigned long offset; double quality; };
To make such a struct use the least number of bytes, rearrange the members, like this40:
struct my_struct { double quality; unsigned long offset; char name[7]; };
If the layout of the structure cannot be changed (e.g., when it must
match some external specification, like a block of data returned by a
system call), you can use the __attribute__((packed))
extension
of GCC (see GNU C/C++ Manual.) to prevent GCC from padding
the structure members; this will make accesses to some of the members
significantly slower.
Beginning with version 2.7.0, GCC has a command-line option
-fpack-struct
which causes GCC to pack all members of all structs
together without any holes, just as if you used
__attribute__((packed))
on every struct declaration in the
source file you compile with that switch. If you use this switch, be
sure that source files which you compile with it don't use any
of the structures defined by library functions, or you will get some
members garbled (because the library functions weren't compiled with
that switch). Also, GCC 2.95.1 and 2.95.2 had bugs in their support of
-fpack-struct
(the bug is corrected in v2.96 and later).
Alternatively, you could declare a particular structure to be packed, like so:
struct my_struct { char name[7]; unsigned long offset; double quality; } __attribute__ ((packed));
However, note that the latter will only work when you compile it as a C source; C++ doesn't allow such syntax, and you will have to fall back to declaring each struct member with the packed attribute. Therefore, it's best to only use declarations such as above if you are certain it won't be ever compiled as a C++ source.
The padding of struct members should be considered when you read or
write struct contents from or to a disk file. In general, this should
only be done if the file is read and written by the same program,
because the exact layout of the struct members depends on some subtle
aspects of code generation and the compiler switches used, and these may
differ between programs, even if they were compiled by the same compiler
on the same system. If you do need this method, be aware of the struct
member padding and don't assume that the number of the file bytes that
the structure uses is equal to the sum of the members' sizes, even if
you instructed the compiler to pack structs: GCC still can add some
padding after the last member. So always use sizeof struct foo
to read and write a structure.
Another problem with porting programs that read structs from binary
files is that the size of some data types might be different under
different compilers. Specifically, an int
is 16-bit wide in most
DOS-based compilers, but in DJGPP it's 32-bit wide.
You should never read whole structures if they were written by
other programs. Instead, read the struct members one by one, and make
sure the member declarations are consistent with their definitions in
the program that wrote the struct. For example, if a struct member was
declared int
in a 16-bit program, you need to declare it
short
in a DJGPP program.
The best, most robust and portable way to read and write structs is
through a char
buffer, which your code then uses to move the
contents into or out of the struct members, one by one. This way, you
always know what you are doing and your program will not break down if
the padding rules change one day, or if you port it to another
OS/compiler. The ANSI-standard offsetof
macro comes in handy in
many such cases. If you need to change the byte order in struct members
that occupy more than a single byte, use special library functions such
as ntohl
and htons
.
Q: When I use struct ffblk
from the header dir.h
in a
C++ program, I get garbage in some members of the structure!
A: There is a known bug in GCC 2.7.2: the C++ compiler
effectively ignores the __attribute__((packed))
directives, so
the structures end up being not packed. GCC versions 2.7.2.1 and later
corrected that bug, so upgrade. As a work-around, surround the
declaration of the structure that needs to be packed with #pragma
pack
, like this:
#ifdef __cplusplus #pragma pack(1) #endif . . . #ifdef __cplusplus #pragma pack() #endif
Q: How do I write a program that accesses floppy and CD-ROM drives,
but avoids popping that "Abort, Retry, Fail?" message from DOS?
Q: Other DOS compilers supply a function named harderr
or
_harderr
to hook the critical-error interrupt 24h, but DJGPP
doesn't seem to have these...
A: Under DPMI, Int 24h is always hooked by the DPMI server, since
Int 24h is issued by the real-mode DOS code, and it is not possible to
terminate a DPMI client (like DJGPP programs) from real mode, if you
press A in response to that prompt. The default handler under
most DPMI servers will just set AL register to 3 and do an
IRET
, thus silently failing the DOS call that triggered Int 24h.
The DJGPP startup code also hooks the protected-mode Int 24h with a
handler that fails the DOS call as described above. So in most
circumstances you won't see that DOS prompt at all; your program will
just see a failed DOS call.
However, some DPMI hosts (notably, QDPMI), will sometimes crash your program if it generates Int 24h, for instance when you access an empty floppy drive. In such cases, or when the default action of failing the DOS call is not good enough, you will have to hook Int 24h with your handler. This should be done in exactly the same manner as hooking hardware interrupts (see how to set an interrupt handler), because Int 24h is one of the few software interrupts that, like all hardware interrupts, are always reflected to protected-mode. Note that CWSDPMI currently doesn't support hooking Int 24h; if you set an interrupt handler, it won't be called.
There are ways to avoid program crashes due to Int 24h (under those DPMI
hosts that exhibit this buggy behavior) other than to install a handler
for it. For instance, you can test if the floppy drive is empty with a
BIOS call before accessing it with DOS functions; there are also similar
ways to check if a CD-ROM drive is empty. The library function
getmntent
(see getmntent.)
can be used to detect all the drives that can be safely
accessed by DOS; or you can borrow some of the internal functions used
by getmntent
from the library source distribution, or
from the zip picker.
go32-v2.exe
program?Q: What is go32-v2 for?
A: The go32-v2
program does the following:
go32
did in v1.x.
go32-v2 myprog
go32.exe
and put on your PATH
before
the v1.x go32.exe
, it can also run a v1 COFF images, by loading
the v1.x go32
and letting it do the job. With this setup, you
can run v2 programs from v1.x programs, because the v1.x program will
load go32-v2
(since it found it first on the PATH) which knows
how to run v2 images, instead the original go32
which cannot.
Q: What is a DXE?
Q: Can I make a DLL using the DXE support?
Q: Where can I find information or examples about writing/loading
the DXE files?
Q: Why do I get undefined references when I run dxegen
?
A: DXE is a limited facility to dynamically load code which is rarely needed in DJGPP. An example is the floating-point emulator code (see the details of DJGPP FP emulator) which is only used on those few machines that lack an FPU. The DXE design is intentionally limited to keep it as simple as possible, so that the code that loads a DXE could be small (it's a few hundreds bytes). Because of this, there are a number of limitations in the DXE mechanism that prevent using it for full-fledged dynamic linking (i.e., a DLL). For instance, the DXE module cannot access variables or functions in the main module.
A DXE cannot link in any library functions which reference static
variables (or which call other routines which reference static
variables); this effectively prohibits linking in I/O functions,
allocating memory, and many other useful things. If you do call any of
these, you'll get unresolved externals from dxegen
. To work
around this limitation, introduce an array of function addresses, or a
structure with pointers to functions as its members, which will be used
from the DXE at run time to call the "special" routines you cannot
link in. Then arrange for the address of this array to be returned by
_dxe_load
when it loads the DXE, and make the init routine fill
the array with the actual addresses of those "special" functions.
Unloading a DXE is also not supported (but I'm told you can add this by making a few simple changes in the C library).
The only place you can find some docs and examples of writing and using a DXE is in the "tests" archive. The example there is exceedingly simplistic, but then so is the entire DXE mechanism....
Q: I cannot make Info find some of its files under Windows 9X...
Q: Why does Make behave as if some of the files were not there?
A: Are you running DJGPP v2.0 on Windows 9X with long filename support
enabled (LFN=y in the environment)? If so, set LFN=n from the DOS
prompt and try again. If the problems go away, they are probably due to
known bugs in some v2.0 programs wrt the LFN support. Make and Info
which came with DJGPP v2.0 are two programs which are known to reveal
these bugs. Before you decide that you are a victim of these bugs,
though, make sure that all the files that your programs need to access
have been renamed to their long names. For example, if Make needs to
find a file called ALongFileName.cc
(because the Makefile tells
it to build ALongFileName.o
), make sure there indeed is such a
file in the directory. Sometimes people use archive tools (like
PKZIP
) that truncate long names, even on Windows 9X, when they
unpack an archive, which leaves you with names like alongfil.cc
,
which is not the same as the original name when LFN is supported. Be
sure to use archivers that support long filenames, e.g. use
DJTAR
, or rename all the files to their original long names after
you unpack the archive.
If the problems persist even though the filenames are correct, upgrade
to DJGPP v2.01 or later, where all programs should support long
filenames properly. If you cannot upgrade, you will have to disable LFN
support (set LFN=n from the DOS prompt, setting it in DJGPP.ENV
does not always work in DJGPP v2.0).
Q: When I invoke Make, it refuses to do anything and prints a
cryptic message: "makefile:10: *** missing separator. Stop." Now
what kind of excuse is that?
A: Unlike most other DOS Make programs which accept any whitespace character at the beginning of a command in a rule, GNU Make insists that every such line begins with a TAB. (Most other Unix Make programs also require TABs, and the Posix standard requires it as well.) Make sure that the line whose number is printed in the error message (in this case, line 10) begins with a TAB.
Beginning with version 3.78, GNU Make prints a message that hints at a possible SPACEs-vs-TAB problem, like this:
*** missing separator (did you mean TAB instead of 8 spaces?). Stop.
If you need to repair a Makefile that uses spaces, one way of converting
leading spaces into TABs is to use the unexpand
program from
the GNU Textutils package (v2gnu/txt20b.zip
from
the DJGPP sites). Another possibility is to open the Makefile in
RHIDE and choose the Edit->Compact Text
option from the menu
bar.
Note that there are editors that automatically replace TABs with spaces, so even a Makefile that used to work can become unworkable if you edit them with such an editor. Don't use such editors.
Another, more rare, cause of the above error message is if you use static
pattern rules (with the %
character) incorrectly. Read the
documentation that comes with Make carefully and try to find the error.
foo
has modification time in the future"Q: I keep getting messages about file modification time in the
future when I compile my programs using Make....
A: This happens on fast machines running Windows/NT and Windows 9X. (However, somebody even reported such a problem on a FAT32 drive under plain DOS.) It is evidently due to a misfeature in the way Windows reports the time a file was last modified. Current versions of Windows 9X store this time with 2-second granularity, but the file creation time is stored with a 100-nanosecond granularity. It seems that Windows blindly adds 2 seconds to the system clock reading when it calculates the file modification time, apparently to prevent it from being older than the file creation time, which could happen because of the greater accuracy used to store the creation time. On a fast machine, this 2-second add-on can very easily make the file modification time be ahead of the system clock when Make checks the time stamp of a file it has just created/updated.
GNU Make reports such cases because inconsistencies in file times could easily defeat Make's decisions about which files need to be rebuilt. In particular, if some of the files reside on a networked drive, and there's a clock skew between the machine where Make runs and the one which exports the drive, Make could really fail to rebuild some files.
DJGPP ports of GNU Make v3.77 and later allow for up to 3 seconds of positive difference between the file timestamp and the system clock (that is, the file is allowed to be up to 3 seconds into the future), before the above warning is printed. So upgrading to the latest version of Make should eliminate such bogus warnings and leave you only with messages due to real clock skews.
Q: I want to be able to run DJGPP both under Windows 9X with long
file names, and when I boot into plain DOS, where long file names aren't
supported. How can I set that up?
A: Such a setup is possible, but it involves a few special actions, and some vigilance on your part during routine operations.
First, you must set the option in the Windows registry which prevents it
from using numeric tails when it invents short 8+3 aliases for long file
names. When numeric tails are enabled, and a file with a long name is
created, Windows generates a short 8+3 alias for that long name by
attaching to the first 6 characters of the basename a numeric tail
~n
, where n is a digit. For example, a file called
ALongFileName.LongExtension
will get a short alias
alongf~1.lon
. When you then reboot into plain DOS, your programs
will see this short version only, which will almost certainly break
them, since, when a program running under DOS asks for a file with the
above long name, DOS transparently truncates it to alongfil.lon
,
and such a file does not exist. Disabling the numeric tails forces
Windows not to use numeric tails unless there is another file in the
same directory whose short alias clashes with that of the new file. If
no such clash happens, Windows will simply truncate the long name as DOS
would, which is exactly what you want.
Here is how you disable the numeric tails on Windows 9X:
regedit
, to start
the Registry Editor.
HKEY_LOCAL_MACHINE
branch of the registry until you
see in the left pane an item called
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\FileSystem
,
then click on it.
FileSystem
key. If you don't see an item there called
NameNumericTail
, select "New", "Binary Value" from the
"Edit" menu, then type NameNumericTail
and it will appear. Now
double-click on NameNumericTail
and enter a value of 0.
regedit
and restart Windows 9X.
As an alternative to running regedit
, you can create a file
named, say notail.reg
(the name is arbitrary, but it
must have a .reg
extension), with this content:
REGEDIT4 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem] "NameNumericTail"=hex:00
then double-click on the name of this file in Explorer or My Computer.
Windows will then run regedit
for you. You still need to
reboot the machine afterwards.
If setting NameNumericTail
to 0 breaks some programs, you can
restore its original setting temporarily, while you run those programs.
NameNumericTail
only affects the short names of new files being
created, it has no effect on the files that already exist.41
Besides the numeric tails, you need to make sure any files and
directories you create have unique 8+3 aliases which are true
truncations of the long names to 8+3 limits. This means that you should
avoid file names with leading dots, such as .emacs
and
.bashrc
, file names with more than a single dot, like
make-3.77.tar.gz
, or file names which include characters not
allowed by DOS, like libg++.a
.
One other problem is to avoid using programs which create numeric tails
even if they are disabled in Windows. One such program is
pkunzip
version 2.50. Don't use it, if you want to keep your
dual DOS/Windows installation in working order.
The most simple method of deciding at boot time which configuration (DOS
or Windows) to start is to edit the (hidden) file MSDOS.SYS
,
which is a text file in Windows 9X, and force the Windows boot process
to present a menu where one menu item, called "Command Prompt Only",
allows you to start DOS 7 without the Windows GUI. To this end, change
the line of MSDOS.SYS
that reads "BootMenu=0" to say
"BootMenu=1" instead, and reboot. Since MSDOS.SYS
is a hidden
file, you will need to remove the hidden attribute from it before you
can edit it; use the ATTRIB
command for that.
zoneinfo
directory?Q: When I installed DJGPP v2, it created a directory named
zoneinfo
with a lot of small files that take up 3.5MB of my disk
space. What are they for? Can I delete them?
A: These files exist so that time-related library functions can correctly calculate the offset between the local time and the UTC (Universal Coordinated Time). This offset is required when you get files from another time-zone, like from the Internet, or when you download an archive that was compressed in another time-zone.
One case where the time stamps might be very important is when you need
to rebuild some package with Make. Make uses file time stamps to decide
which files need to be rebuilt. Another case is if you distribute some
files compressed with Zip
and want your recipients to be able to
restore the correct time stamps of your files when they unzip them.
If you don't care about file time stamps being incorrect in such cases, you can delete all those files and never look back.
You might wonder why we need all these zoneinfo files when the UTC
offset is required. Well, the simplest way to tell programs
what the UTC offset is, is to have the user specify a single number
which is the offset; but then this number needs to be changed twice a
year, to accommodate for the daylight saving time. Another,
not-quite-so-simple way is to have the user specify the current UTC
offset and the DST rules; but this is a tedious and error-prone process,
and many users get it wrong. Both of these methods have the drawback
that if the rules change, programs misinterpret old time-stamps, since
they treat them according to new rules. Using a table that is read from
a file and includes the offset calculation rules for every year avoids
all these problems and requires the user to point the TZ
environment variable to the file that is pertinent to his/her time zone,
which is easy:
set TZ=c:/djgpp/zoneinfo/israel
or
set TZ=c:/djgpp/zoneinfo/us/alaska
To find the rule suitable for your location, look into the src
subdirectory of zoneinfo
and browse the file whose name is your
continent/part of the world. If no binary file exists with the name of
your zone, you can create one with using the time-zone compiler
zic
which comes with the v2/djtzn203.zip
file (it unzips into the etc
subdirectory of the main DJGPP
installation directory).
A public domain time-zone database exists, and is updated from time to
time with the latest world-wide changes to the offset calculation rules.
(The rules change because politicians in different countries make laws
that change the local clock settings.) The contents of the
zoneinfo
directory which comes with DJGPP is based on this
database, but if you want the latest rules, you can download them from the net as tzdata*.tar.gz
;
tzcode*.tar.gz
in the same directory includes the programs that
can be used to generate the offset tables from their source in
tzdata*.tar.gz
, the latest implementations of POSIX library
functions that use time-zone information, and the manual pages that
document the rules and the software. The last update as of this writing
was in September 1999.
On any single machine, you don't need more than a single file from that directory, which is the file for your time zone; once you find that file, you can safely delete the rest. But if you distribute a program that uses the TZ setting, you will have to include all of the files, or tell your users how to get and install them.
Q: All DJGPP programs cannot find files in the d:\dev
directory, but work okay in other directories. What is going on here??
Q: I installed DJGPP in the e:/dev/djgpp
, and it doesn't
work!
A: This is an unfortunate side-effect of the special treatment given
to the \dev
directory on each drive. DJGPP transparently
supports Unix-style devices such as /dev/null
and
/dev/tty
, so that programs ported from Unix that refer to these
devices will work. Unfortunately, due to a half-hearted way DOS and
Windows support devices, the DJGPP library must treat the
\dev
directory specially. The net effect is that if you have
a real directory by that name, you will get erratic behavior.
A work-around is either to rename the \dev
directory to some
other name, like \devel
, or move it down the directory
hierarchy, for example make it d:\software\dev
. (The
special treatment is only reserved to the \dev
directories
immediately under the root of every drive.)
Q: I hear all that stuff about the limitations of the COFF format,
and I don't understand why won't DJGPP switch to a modern standard such
as ELF?
A: DJGPP uses COFF for historical reasons: at the time it was developed ELF was not available yet. There are several grave reasons why DJGPP didn't switch to ELF yet:
stub.asm
in the library sources) needs to
be partially rewritten to be able to load an ELF executable and set it
up for execution. Since the stub loader is written in assembly and
optimized for size, this is a formidable task.
go32-v2
, stubify
and
stubedit
, need to be changed as well.
src/debug/
directory
in the djlsr
distribution) need to be changed to support ELF.
None of the above is a show-stopper, so such a switch is possible. But it is a large project, and without several devoted volunteers, chances are it will never happen.
Q: How do I produce random numbers with DJGPP?
Q: I keep getting the same random numbers each time I run my
program. How do I get a different series on every run?
Q: How do I get random numbers between 20 and 200?
A: DJGPP has in its library several functions to produce series of
pseudo-random43 numbers. One of them,
rand
, is part of the ANSI C Standard, and is therefore very
portable to other environments. Other random-number functions,
random
and the rand48
family of functions, are available
on almost every Unix platform, but are usually unsupported by
DOS/Windows compilers. On the other hand, series produced by
random
and rand48
have better qualities than those
produced by rand
. In particular, the least-significant bits in
the numbers produced by random
are much more random than those
you get from rand
, so if you need, say, a random number between 0
and 4, and portability is not an issue, you will get better results with
random () % 5
. However, the DJGPP implementation of
rand
is quite good, so when portability is important, you
should use rand
.
Both rand
and random
return a pseudo-random integer in the
range [0..RAND_MAX)
, where RAND_MAX
is defined in the
stdlib.h
header. Within the rand48
family, some functions
return integers, either in the range [0..RAND_MAX)
or in
[-RAND_MAX..RAND_MAX)
, while others return a double
value
between 0.0 and 1.0.
By default, every time you restart a program, you get the same series of
pseudo-random numbers. This is important in some applications, because
it allows to reproduce exactly the results of running a program which
used random series, and thus makes debugging easier. But sometimes,
e.g. in a game, you will want a different series every time. To achieve
that, you need to initialize the random series with a different
seed. Every random-generating function has its own seed function
provided for this purpose: rand
has srand
, random
has srandom
, the rand48
family can be seeded with either
srand48
or seed48
. You seed the series with a single call
to the appropriate seed function, and then proceed by calling
rand
, random
, etc. as usual.
A popular way of getting a different seed every run is to use the current system clock as the seed, like this:
srand (time (NULL));
If the 1-second granularity of the values returned by time
is not
enough for you (e.g., if you need to generate more than one series every
second), use gettimeofday
or uclock
, or use the values
returned by rand
as an argument to srandom
(or vice
versa).
To produce random integers from the inclusive interval
[low..high]
(where low and high are two
integer numbers), use code like this:
#include <stdlib.h> int random_number = low + (double)rand () * (high - low + 1) / RAND_MAX;
This produces a more random sequence than if you use the %
operator, but for the price of producing slower code (since it involves
floating-point math).
If you want to know more about random number generation, I suggest reading the article Random Number Generators: Good Ones Are Hard To Find, by Stephen K. Park and Keith W. Miller, in CACM, v31(10), 1988, pp. 1192-1201.
Q: All your FAQs and tutorials seem to take for granted that I know
what words like "compile", "link", "makefile" etc. mean. Where
can I find all these buzzwords explained??
A: The DJGPP docs use some basic computer lexicon, without which it would be impossible to communicate. If you find yourself completely lost in terminology you don't understand, and this FAQ doesn't explain it, try looking up the words in the DJGPP Lexicon page. Another excellent resource for computer-related terminology is the Jargon File, also available from the GNU FTP site as an Info file and in several other formats.
main
function return in a C/C++ program?Q: Why does everybody tell me that void main
is bad?
Q: If void main
is incorrect, how come the compiler lets it
compile?
A: The ANSI/ISO C Standard specifies that the main
function be declared in one of the following two ways:
int main (void);
or
int main (int argc, char **argv);
In both cases the return type is an int
, and your main
function should therefore either return an int
or call the
library function exit
, when the program ends. The C++
standard includes a similar requirements for C++ programs.
Since the runtime environment assumes that main
returns an
int
, declaring main
with any other return type, including
void
, invites trouble. The compiler might compile such a
program, since the ANSI Standard doesn't require it to fail, but
the behavior of such a program is, in the Standard's parlance,
"undefined" (read: anything can happen). That is why GCC will print a
warning in these cases if you use the -Wall
switch.
To summarize, using void main
is unsafe and can
potentially do evil things to your program. It is best to avoid it.
Note that the C++ standard, in contrast to the C standard,
explicitly prohibits void main()
, and explicitly says that if the
controls reaches the end of main
without encountering a
return
statement, the effect is that of executing return 0;
. When compiling a C++ program, GCC automatically generates the
code to return zero from the main
function, in case the
programmer leaves that out.
Q: How can I reboot the PC from a DJGPP program?
A: There are several possible ways to achieve this:
__dpmi_int
instruction. (Emitting the literal INT 19h
instruction won't work with CWSDPMI, because CWSDPMI deliberately blocks
it; version r5 of CWSDPMI might remove this restriction.)
INT 19h
instruction is a better
alternative, since Windows misbehaves when INT 19h is issued via
__dpmi_int
.
FEh
command to the port 64h:
outportb (0x64, 0xfe);
(this should be preceded by a test whether the keyboard buffer is full; because then the keyboard will ignore the command; however, keyboard-full is a very rare condition).
Q: The function usleep
doesn't work for me! When its
argument is less than 10000, there's no delay at all, and when the
argument is larger than 10000, the delay is always the same....
Q: How can I delay the execution of my program for 2msec?
Q: I need to pause my program for 100 microseconds. Can I do it?
A: Most time-related facilities in DJGPP have the same 55msec
granularity of the time intervals they measure. This is because the
timer tick interrupt that updates the time has the frequency of
18.2Hz. This is why calling usleep
with arguments less
than 55000 produces strange effects: the resolution of the argument is
1usec (for compatibility with other compilers), but the
granularity is still 55msec.
If you need to pause your program for periods of time shorter than 55msec, you have several alternatives:
delay
. It is based on the CMOS clock chip whose frequency is
1024Hz.
uclock
to see when the pause time
expires. uclock
measures time with 840-nanosecond granularity.
Here's a possible implementation of such a wait loop:
#include <time.h> uclock_t start; /* Wait for 200 microseconds. */ start = uclock (); while (uclock () < start + UCLOCKS_PER_SEC / 5000) ;
Q: I wrote a CGI program and compiled it with DJGPP, but it doesn't
seem to work....
A: If you are using a Windows Web server, it probably won't work. The reason is that Windows programs cannot easily redirect standard input and output of DOS programs (because DOS programs are run by Windows in a different Virtual Machine), and many Windows Web servers don't consider the case of a DOS CGI program, and don't bother to include the machinery necessary to do the redirection in a way that would work for DOS programs. So the output of your program never gets to the server.
Consult the docs of the server: it might include some feature that enables redirection from DOS programs. If that doesn't help, rebuild your CGI program with a Windows compiler, such as Mingw32 or Lcc-Win32 (see free Win32 compilers), and it will work.
stdin
?Q: Whenever I run my program, which reads stdin
, from
RHIDE or Bash, it gets EOF indication immediately, without any
input being typed! This program runs okay from the DOS prompt, so
something must be wrong with Bash and RHIDE, yes?
A: This is a known (mis-)behavior of DOS: to clear the EOF
condition of the console device, you need to write something to it. If
during some previous invocation of a program you typed
Ctrl-<Z>, the resulting EOF condition will stick until
something is written to the console device. When the program is run
from the DOS prompt, that "something" is the prompt string printed by
COMMAND.COM
. However, RHIDE doesn't print any prompt, and
Bash prints its prompt via the BIOS. So, as soon as you type
Ctrl-<Z>, the console device is stuck in the EOF condition,
and all subsequent invocations of programs that read standard input
immediately get receive EOF and exit. (This problem is not specific to
DJGPP: Norton Commander also exhibits it.)
A work-around is to output something to stdout
or stderr
(assuming they both are connected to the console device).
Q: How can I generate the FAQ list in a format I'm used to?
A: First, I suggest to check whether the FAQ is already available in
your format. The FAQ distribution
includes the Info, plain-ASCII (text), and HTML (one large .html
file) versions of the FAQ list. (Personally, I recommend to use the
Info version, because Info readers generally have superior search
facilities.) More formats will be available as the tools for their
generation are developed/tested.
If you prefer to read the FAQ list as hard-copy, get
faq230p.zip
from the same place. It includes the
FAQ in PostScript and PCL formats; the former is for printing on a
PostScript printer, the latter is for the LaserJet series. Be warned:
the FAQ is a large document, more than 200 printed pages.
faqNNNp.zip
also includes a .dvi
file which you can print
or view using one of the available DVI drivers and previewers, such as
Ghostscript or dvivga
.
If none of these formats is good enough for you, you will need to get
the FAQ sources and convert them into the format of your liking. The
sources of the latest version of this FAQ list are on SimTel.
This includes the FAQ sources themselves (written in Texinfo), and all
the auxiliary tools required to produce all the formats in
faqNNNb.zip
and faqNNNp.zip
.
Once you download the sources, you will need one or more of the tools listed below to generate the FAQ list in other formats.
A program called Makertf
can reportedly be used to convert a
Texinfo sources of this FAQ to the Rich File Format which can then
either be browsed by an RTF browser (such as Adobe Acrobat) or converted
into a Windows Help file with a Windows Help compiler. The Windows Help
Compiler is available via anonymous ftp from the Microsoft ftp site. I'm told that the
Windows Help compiler, hcp
, issues a lot of warnings when it
runs on Makertf
s output, but these warnings can be safely
ignored.
A derivative of TeX called PDFTeX can be used to generate a PDF
file from TeX sources. This means that a Texinfo source can also be
submitted to PDFTeX, but I didn't try that. PDFTeX was ported to
DJGPP and can be downloaded from its home site via FTP. Another option
for generating a PDF version of the FAQ is to use the DviPDFm
program which can convert the FAQ in the DVI format (included in the
faqNNNp.zip
distribution) into a PDF file. DviPDFm
is
available with several TeX distributions and can be compiled with
DJGPP.
There's also a program called INFNG
that can be used to convert
the Info (not Texinfo) version of the FAQ to the Norton Guide
format. INFNG
is available from the DJGPP archives.
If you know about any format not mentioned above that can be generated using widely available tools, please drop me a note so I could update this list and consider that format or those tools for inclusion in a future release of the FAQ. If you develop any such tools, consider uploading them to a site where they will be publicly available, and tell me about that site.
Note that the FAQ sources are heavy users of the Texinfo macro facility, so any conversion program that doesn't support Texinfo macros will probably have hard time coping with the FAQ. When confronted with this problem, try feeding the converter with the macro-expanded version of the FAQ (the Makefile in the source distribution has a special target for such cases).
Maintainer: Eli Zaretskii.
Copyright © 1994, 1995, 1996, 1997, 1998, 2000 by Eli Zaretskii. This FAQ may be freely redistributed with the DJGPP package or any part thereof, provided that you don't prevent anybody else from redistributing it on the same terms, and that this copyright notice is left intact.
Comments about, suggestions for, or corrections to this FAQ list are welcome. Please make sure to include in your mail the version number of the document to which your comments apply (you can find the version at the beginning of this FAQ list).
Much of the info in this FAQ list was taken from the DJGPP mailing list/news group traffic, so many of you have (unbeknownst to you) contributed to this list. The following people deserve special credit for reading this list in its previous versions and providing useful feedback, comments, information and/or suggestions:
John M. Aldrich Anthony Appleyard Gurunandan R Bhat John Bodfish Francois Charton Alain CULOS Bill Currie Bill Davidson DJ Delorie Tom Demmer Nate Eldredge Juergen Erhard Andy Eskilsson Jeremy Filliben Peter Gerwinski Till Harbaum James W. Haefner Kris Heidenstrom Koen Van Herck Vik Heyndrickx Robert Hoehne Gordon Hogenson Daniel Horchner Harry Johnston Jules Martynas Kunigelis Pieter Kunst Y. Lazarovitch Alexander Lehmann Marty Leisner Dave Love Randy Maas Cameron Mallory Colin S. Miller Duncan Murdoch Rob Nader Eric Nicolas Adrian Oboroc Jan Oonk Elliott Oti Bob Paddock Esa A E Peuha Prashant TR Walter Prins Steve Salter Charles Sandmann Terrel Shumway Martin Strömberg Andrew Szymkowiak Launey Thomas Chris Tilbury Ned Ulbricht Stephen Turnbull Ronan Waide Morten Welinder Anthony Edward Wesley K.B. Williams Mark H. Wood
This is an alphabetical list of all the topics covered in this FAQ. Use it to search for a description of your problem and follow the link to find the answer(s).
387
, an environment variable: Emulation
_interrupt
keyword, DJGPP replacement: Hardware interrupts
#include
d files: Included source
build.cross
script: Cross-DJGPP
#include
d files: Included source
bool
variables: Bool vars
.bss
: Static vars
complex
data type, how to debug: Complex vars
malloc
or free
: malloc crash
delay
function on OS/2: OS2
delay
function, on Windows/NT: WindowsNT
emu387
, an environment variable: Emulation
extern "C"
, use with assembly and C code: ASM and C
function.h
header, in RSXNTDJ: Windows apps
interrupt
keyword, DJGPP replacement: Hardware interrupts
rand
and random
functions, comparison: Random numbers
main
in C++ programs: void main
system
function doesn't call COMMAND.COM: Long commands
#include
d files: Included source
This index lists the problems and solutions by the program/package to which they pertain. If you know what program or package gives you the trouble, look it up here.
386Max, how to ensure virtual memory
: QDPMI VM
386Max, speeding up DJGPP start-up
: QDPMI VM
4DOS, redirecting GCC messages to a file
: Redirect
Aladdin Expander, unzipping DJGPP
: Urgent
Allegro, a graphics and gaming package
: What to download
Allegro, compilation speed
: Slow compiler
Allegro, compiling on DOSEmu
: DOSEmu
Allegro, linker switch
: Unresolved externals
Allegro, problems on Windows/NT
: WindowsNT
ATT2INTL, a converter from AT&T to Intel assembly
: Converting ASM
Bash crashes
: Bash crashes
Bash hangs on Windows 9X
: Programs hang
BatchFTP, automated downloading from a Unix box
: DJGPP Fatware
BCC2GRX, porting Borland graphics
: What to download
BCCBGI (from BCC2GRX) crashes with the default stack
: Stack size
BCSERIO, async communications package for DJGPP
: Packages
Binutils configured for ELF32
: ELF vs COFF
Binutils, incompatibilities with DJP
: Large executable
Bison doesn't imply GPL/LGPL
: Application distribution
Bison, debugging generated code
: Included source
Boot loader for an operating system
: Packages
byacc, a DJGPP port
: Packages
bzip2, a DJGPP port
: Packages
Caldera's DR-DOS, and redirection
: Minimum
Caldera's DR-DOS, and RHIDE
: Minimum
Cawf, using to read man pages
: Man pages
CC1PLUS crashes with SIGSEGV
: Stack size
Cdecl, ported to DJGPP
: Packages
CHCP DOS command might prevent MSHELL from working
: Debug graphics
collect2, accesses the network
: Linker accesses other drives
collect2
, slow operation on Windows 9X
: Slow linker
CPP, compiling, memory requirements
: Reasonable hardware
CPP, compiling, RAM disk
: Config
CWSDPMI allows ``Fat DS''
: Fat DS
CWSDPMI crashes programs allocating memory is small chunks
: QDPMI alloc
CWSDPMI crashes programs which dereference NULL pointers
: v2 crash
CWSDPMI doesn't support hooking Int 24h
: Int 24h
CWSDPMI r4, bugs with lots of virtual memory
: More than 64MB
CWSDPMI runs out of virtual memory
: Internal error
CWSDPMI, alternative DPMI hosts
: Environment
CWSDPMI, disabling virtual memory
: Slow compiler
CWSDPMI, legal restrictions
: DJGPP redistribution
CWSDPMI, maximum available virtual memory
: How much memory
CWSDPMI, memory usage for nested programs
: Swap out
CWSDPMI, minimum required system RAM
: Minimum
CWSDPMI, pages too early under EMM386
: EMM386 alloc
CWSDPMI, setting parameters for optimal performance
: Config
CWSDPMI, should be distributed with DJGPP programs
: Distributing
CWSDPR0 reduces interrupt reflection overhead
: HW Int pitfalls
CWSDPR0, use for testing HW interrupt handlers
: Hardware interrupts
CWSPARAM, a program to tune CWSDPMI performance
: Config
cxxfilt.exe, duplicate versions
: g++.exe
DISPLAY.SYS and EMM386, conflict with Info
: Info crashes
DJGPP.ENV, linker environment variables
: Libraries order
DJP compressor supports DLM
: Large executable
DJP, an executable compressor for DJGPP
: Large executable
DJP, incompatibilities with Binutils
: Large executable
DLM compression, with DJP
: Large executable
DLM, a facility to load code at run time
: Packages
DOSEMU doesn't allow ``Fat DS''
: Fat DS
DOSEmu, and recursive Make invocation
: DOSEmu
DOSEmu, leaks DPMI selectors
: DOSEmu
DOSEmu, problems with FAT32 volumes
: DOSEmu
DOSEmu, RHIDE conflicts with mouse support
: DOSEmu
DR-DOS, Ctrl-BREAK crashes
: Minimum
DR-DOS, problems with linear frame buffer
: Minimum
DXEGEN, undefined references
: DXE
EDEBUG32 can debug a DXE
: How to debug
Emacs hangs on Windows 9X
: Programs hang
Emacs, reading docs
: Where is the docs
Emacs, reading Info files
: Where is the docs
Emacs, using to read man pages
: Man pages
EMM386 and DISPLAY.SYS, conflict with Info
: Info crashes
EMM386, cannot use all free memory
: EMM386 alloc
EMM386, conflicts with dual-monitor systems
: Debug graphics
EMM386, effect on max interrupt frequency
: HW Int pitfalls
EMM386, getting the most memory
: Config
EMM386, how to get more than 32MB of memory
: More than 64MB
EMM386, malloc/calloc fails
: EMM386 alloc
EMM386, settings for optimal performance
: Config
emTeX, printing the docs
: Printed docs
emu387.dxe, distribution with DJGPP programs
: Emulation
F2C, debugging generated code
: Included source
FIND.EXE, incompatible with GNU Find
: What to download
Flex doesn't imply GPL/LGPL
: Application distribution
Flex, debugging generated code
: Included source
Flex, undefined references
: Unresolved externals
FORTIFY, a malloc debugger
: Packages
FreeBE/AF, 2D accelerator
: Packages
FSDB, the full-screen debugger
: How to debug
g++ compilation driver, alternative names on DOS
: Libraries order
g++.exe, unzip complains on DOS
: g++.exe
Gas, accepts Intel assembly syntax
: Syntax
GCC 2.95, slower compilation than old GCC versions
: Slow compiler
GCC aborts or crashes during compilation
: Internal error
GCC can't find C++ headers
: Missing C++ headers
GCC can't find crt0.o
: Missing headers or libraries
GCC can't find headers
: Missing headers or libraries
GCC can't find libraries
: Missing headers or libraries
GCC cannot resolve djgpp_first_ctor symbol when linking
: djgpp_first_ctor
GCC crashes due to CPU overclocking
: Internal error
GCC crashes due to incorrect CMOS setup
: Internal error
GCC crashes during optimization
: GCC optimizations
GCC crashes with Page Fault on Windows 3.X
: Internal error
GCC crashes, which subprogram of
: General trouble
GCC doesn't pack structs in C++ programs
: Struct packing
GCC doesn't recognize .lib libraries
: OBJ and LIB
GCC doesn't recognize .obj object files
: OBJ and LIB
GCC doesn't recognize file format
: Which language
GCC exhausts virtual memory
: GCC optimizations
GCC from v2.x crashes under v1.x Make
: Make hangs
GCC hangs under Make
: Make hangs
GCC says ``garbage at end of number''
: 0xfe+0x20
GCC won't compile C++-style comments in C programs
: C++ comments
GCC won't find inline functions without -O
: Still unresolved
GCC, -fconserve-space switch
: Large image
GCC, -v
switch shows the compilation passes
: Which language
GCC, assumes C++ source is .cc
: GDB and C++ source
GCC, code efficiency
: How fast
GCC, compiling for debugging
: How to debug
GCC, compiling, memory requirements
: Reasonable hardware
GCC, compiling, RAM disk
: Config
GCC, environment variables
: Missing headers or libraries
GCC, file source language recognition
: Which language
GCC, I/O speed
: IO speed
GCC, inline assembly facilities
: Inline Asm
GCC, maximum length of command line in Makefiles
: How long
GCC, passing long command lines via Makefile
: Makefiles
GCC, recompiling
: Changing
GCC, redirecting messages to a file
: Redirect
GCC, slow compilation
: Slow compiler
GCC, uses lots of memory with -Wall
: GCC optimizations
GDB causes stack overflow in a debuggee
: Stack size
GDB doesn't pass command-line arguments to debuggee
: How to debug
GDB doesn't recognize source language
: C++ classes in GDB
GDB fails to set or display static variables
: Static vars
GDB GP Faults on breakpoint/watchpoint under Windows
: Debugging woes
GDB, conflicts with file redirection
: How to debug
GDB, debugging DJGPP programs
: How to debug
GDB, debugging graphics programs
: Debug graphics
GDB, how is it different on MS-DOS
: How to debug
GDB, how to use C++ class variables' names
: C++ classes in GDB
GDB, how to use C++ method names
: C++ classes in GDB
GDB, init file name
: How to debug
GDB, name of the READLINE init file
: How to debug
GDB, slow loading of symbols and sources
: How to debug
GLUT for DJGPP
: OpenGL
GMP library
: Packages
go32-v2 reports the amount of memory and swap space
: Disk space
go32-v2 usage
: go32-v2
go32-v2, use to find out how much memory is available to DJGPP
: EMM386 alloc
Gprof produces garbled profile
: Garbled profile
gprof produces no output
: No profile
Gprof shows 100% of time in one function
: Garbled profile
Gprof, documentation
: Cannot find docs
Gprof, the GNU profiler
: How to profile
Groff, port to DJGPP
: Man pages
Groff, using to read man pages
: Man pages
GRX, a graphics package
: What to download
GRX, failure to compile inline assembly
: Allegro and GRX
GRX, latest versions, where to find
: What to download
GRX, linker switch
: Unresolved externals
GRX, supported SVGA types
: GRX driver
gsymify, a substitute for SYMIFY for stabs debugging
: C++ classes in GDB
gxx driver, not in gcc272b distribution
: Unresolved externals
gxx driver, searches C++ libraries automatically
: Unresolved externals
gxx.exe, duplicate versions
: g++.exe
HIMEM, malloc/calloc fails
: EMM386 alloc
INFNG, produces the FAQ in Norton Guides format
: FAQ format
Info crashes due to ^Z or whitespace at end of DJGPP.ENV
: Info crashes
Info crashes immediately upon startup
: Info crashes
Info crashes under QDPMI
: Buggy DPMI
Info hangs on Windows 9X
: Programs hang
Info won't display a file
: Info cannot find Top
Info, a stand-alone docs browser
: Where is the docs
Info, conflicts with DISPLAY.SYS, EMM386
: Info crashes
Info, using to read man pages
: Man pages
InfView, an Info browser
: Where is the docs
install-info, a program to install Info docs
: What to download
Intel2gas, a converter from Intel to AT&T assembly syntax
: Converting ASM
Internet Explorer, downloading DJGPP
: DJGPP by WWW
JAS, a free assembler with Intel-like syntax
: Converting ASM
Jlib, a gaming library
: Packages
JPTUI, a text user interface library
: Packages
LaTeX, printing the docs
: Printed docs
lcc-win32, a free compiler for Windows
: Windows apps
LCLINT, a Lint clone for DJGPP
: Packages
LD linker, linker script defines djgpp_first_ctor
: djgpp_first_ctor
ld
, how to improve linking speed
: Slow linker
ld.exe, from RSXNTDJ
: Windows apps
Less hangs on Windows 9X
: Programs hang
Less, using to read man pages
: Man pages
Lex, debugging generated code
: Included source
libemu.a FP emulation library
: Emulation
LibINI, Windows .ini files handling
: Packages
libmslot, a Windows Mailslot library
: Packages
libwin, a library for interfacing with Windows
: Packages
Linker can't find library functions in non-default directories
: Libraries order
Linker, environment variables
: Libraries order
Linker, order of libraries in the command line
: Libraries order
Linux doesn't allow ``Fat DS''
: Fat DS
Linux, compatibility
: Minimum
Linux, needs a patch to run nested programs
: DOSEmu
Linux, slow response to interactive programs
: DOSEmu
LWP multitasking for DJGPP
: Packages
Make crashes on DOSEmu
: DOSEmu
Make crashes on OS/2
: OS2
Make error message ``missing separator''
: Missing separator
Make requires floating point
: Changing
Make, can be built to not issue FP instructions
: DOSEmu
Make, GCC hangs when invoked from it
: Make hangs
Make, maximum length of command line to pass to GCC
: How long
Make, passing long command lines via Makefile
: Makefiles
Make, warning ``file modification time in the future''.
: Modification time
Makeinfo, using to convert Info files to plain ASCII
: No Info
MAKERTF, produces the FAQ in RTF format
: FAQ format
Man program for DJGPP docs
: Man pages
MDA device driver for redirecting debug output
: Debug graphics
MESA for DJGPP
: OpenGL
MESA is under LGPL
: OpenGL
MGL for DJGPP
: OpenGL
MGUI, a cross-platform GUI library
: Packages
Midnight Commander port to DJGPP
: Packages
Mingw32 port of GCC
: Windows apps
More, using to read man pages
: Man pages
MSHELL fails because of TSR programs
: Debug graphics
MSHELL, redirecting screen output
: Debug graphics
MSS, a malloc debugger
: Packages
NASM, a portable assembler with Intel syntax support
: Converting ASM
NASM, a tutorial on usage with DJGPP
: Syntax
NC clone, ported to DJGPP
: Packages
Netscape, downloading DJGPP
: DJGPP by WWW
NM, printing library contents
: Which library
Notepad, appends .txt to source files
: No input files
Novell NWDOS 7, buggy DPMI services
: Minimum
Novell NWDOS 7, compatibility
: Minimum
Novell VLM causes slow-down of DJGPP programs
: Slow-down
NWDOS, buggy DPMI services crash DJGPP
: Buggy DPMI
OBJ2BFD converter from .obj to COFF format
: OBJ and LIB
OBJ2COFF converter from .obj to COFF format
: OBJ and LIB
OBJ2COFF, commercial use is prohibited
: OBJ and LIB
Objective C, compilation problems
: Objective C
OpenDOS, bug in DPMI services crash DJGPP
: Buggy DPMI
OpenGL for DJGPP
: OpenGL
OpenGL, what it is
: OpenGL
OS/2 and RHIDE
: OS2
OS/2 Warp allows ``Fat DS''
: Fat DS
OS/2, and _go32_remaining_physical_memory
: Confusing alloc
OS/2, and delay
function
: OS2
OS/2, compatibility
: Minimum
OS/2, Ctrl-C aborts programs
: OS2
OS/2, incompatibilities
: OS2
OS/2, SIGINT cannot be caught
: OS2
Palantir, a multitasking kernel for Allegro
: Packages
PDMLWP, a multithreading package
: Packages
PGCC exhausts virtual memory
: GCC optimizations
PGCC, bugs with optimization levels -O7 and higher
: Older is faster
PKUNZIP v2.50, creates numeric tails
: Dual DOS/Windows
PMODE/DJ reduces interrupt reflection overhead
: HW Int pitfalls
PMODE/DJ, can be used to produce stand-alone programs
: Distributing
Pthreads for DJGPP
: Packages
QDPMI allows ``Fat DS''
: Fat DS
QDPMI and _crt0_startup_flags settings
: QDPMI VM
QDPMI crashes DJGPP programs when they cause Int 24h
: Int 24h
QDPMI crashes Info and debuggers
: Buggy DPMI
QDPMI fails to provide virtual memory
: QDPMI VM
QDPMI, malloc/calloc failure
: QDPMI alloc
QDPMI, memory usage for nested programs
: Swap out
QEMM, how to get more than 64MB of memory
: More than 64MB
QEMM386, settings for optimal performance
: Config
RAMDRIVE.SYS, problems with long file names
: Config
RCS port to DJGPP
: Packages
REDIR, problems on Windows/NT
: WindowsNT
REDIR, redirecting GCC messages to a file
: Redirect
REDIR, redirecting stack dump to a file
: Crash traceback
REDIR, use to get redirection and long command lines
: Makefiles
REGEDIT, running from an input file
: Dual DOS/Windows
RHGDB fails to set or display static variables
: Static vars
RHIDE aborts on OS/2
: OS2
RHIDE debugger GP Faults on breakpoints under Windows
: Debugging woes
RHIDE fails to set or display static variables
: Static vars
RHIDE hangs on Windows 9X
: Programs hang
RHIDE puts files in the wrong directory
: RHIDE
RHIDE, and Caldera's DR-DOS
: Minimum
RHIDE, and monochrome display
: Debug graphics
RHIDE, debugging graphics programs on Windows
: Debug graphics
RHIDE, display problems
: RHIDE
RHIDE, includes an integrated debugger
: How to debug
RHIDE, long command lines
: Makefiles
RHIDE, mouse problems on DOSEmu
: DOSEmu
RHIDE, problems on NT
: WindowsNT
RHIDE, problems with using
: RHIDE
RHIDE, reading Info docs
: Where is the docs
RHIDE, where to find the latest version
: What to download
RSXIDE, where to download
: Windows apps
RSXNTDJ include files, mixing with DJGPP
: Still unresolved
RSXNTDJ toolkit for developing Win32 applications
: Windows apps
RSXNTDJ, how to install
: Windows apps
RSXNTDJ, MS Platform SDK
: Windows apps
RSXNTDJ, problems with header files
: Windows apps
RSXNTDJ, using with GCC 2.8.1 and later
: Windows apps
SCRIPT, redirecting GCC messages to a file
: Redirect
Sed requires floating point
: Changing
Sed script to convert ASM to AT&T syntax
: Converting ASM
Sed, documentation
: Cannot find docs
Sed, using to convert formatted man pages to plain text
: Man pages
SeeR, a scripting engine
: Packages
SETEdit, where to download
: Man pages
setitimer, bugs
: Garbled profile
SHARE, limits available file handles
: File handles
SmartDrv, installation
: Config
STRIP makes executables smaller
: Large executable
STUBEDIT, changing stack size
: Stack size
STUBEDIT, effect on memory left to spawned programs
: Swap out
STUBIFY fails to produce .EXE
: No EXE
STUBIFY, not found during compilation
: No stubify
STUBIFY.EXE, infected by a virus
: Unknown filetype
SWORD, GUI environment
: Packages
SYMIFY, a program to read crash traceback
: Crash traceback
SYMIFY, inaccurate report for inline assembly
: Crash traceback
SYSINFO package
: Packages
TA2AS, a converter from Intel to AT&T assembly syntax
: Converting ASM
TeX, printing the docs
: Printed docs
TEXI2PS, converting docs to crude PostScript
: Printed docs
TkInfo, a Win32 tool to read Info files
: Where is the docs
UNIVBE, software VESA 2.0 emulation
: GRX driver
UPX, an excutable compressor for DJGPP
: Large executable
usleep, insufficient resolution
: usleep
VDISK from Caldera's DR-DOS
: Minimum
VIM, a Vi clone for DJGPP
: Packages
Warp, compatibility
: Minimum
Warp, incompatibilities
: OS2
WATTCP
: Packages
Windows 3.X allows ``Fat DS''
: Fat DS
Windows 3.X, compatibility
: Minimum
Windows 3.X, malloc/calloc fails
: Windows alloc
Windows 98 complains about memory access
: Windows 98
Windows 9X allows ``Fat DS''
: Fat DS
Windows 9X doesn't allow more than 16MB virtual memory
: Windows9X alloc
Windows 9X DPMI server loses selectors calling spawnXX
: WindowsNT
Windows 9X long filenames and C++ headers
: Missing C++ headers
Windows 9X, calls both PM and RM interrupt handlers
: Hardware interrupts
Windows 9X, compatibility
: Minimum
Windows 9X, how to get more than 64MB of DPMI memory
: More than 64MB
Windows 9X, setting DJGPP environment variable
: Missing headers or libraries
Windows messes up graphics screen
: Graphics and Windows
Windows, FP emulator hangs
: Emulation in Windows
Windows, malloc/calloc failure
: QDPMI alloc
Windows, memory usage for nested programs
: Swap out
Windows, setting memory parameters for DJGPP
: Config
Windows, stack size control
: Stack size
Windows, wants to run graphics programs in DOS Mode
: Graphics and Windows
Windows/NT and delay
function
: WindowsNT
Windows/NT doesn't allow ``Fat DS''
: Fat DS
Windows/NT doesn't allow port I/O
: WindowsNT
Windows/NT DPMI server loses selectors calling spawnXX
: WindowsNT
Windows/NT LFN driver
: WindowsNT
Windows/NT, bug in handling signals
: WindowsNT
Windows/NT, compatibility
: Minimum
Windows/NT, Ctrl-C aborts DJGPP programs
: WindowsNT
Windows/NT, profiled programs crash
: WindowsNT
WindowsNT doesn't support VDS
: DMA
WindowsNT, setting DJGPP environment variable
: Missing headers or libraries
WinZip, how to unzip DJGPP
: Urgent
WMEMU causes undefined references when linking
: Emulation
WMEMU, an alternative floating-point emulator
: Emulation
WMEMU, use when debugging FP programs on non-FPU machine
: Debugging woes
Yacc, debugging generated code
: Included source
YAMD, a malloc debugger
: Packages
YAMD, debugging buffer overruns
: malloc crash
ZipMagic, disable to unzip DJGPP
: Urgent
Please report any issues that couldn't be found via the indices to the maintainer of this FAQ, whose name and e-mail address can be found near the end of the FAQ.
Posix is an international standard for a portable operating system. It specifies facilities of a compiler, its libraries, and the basic set of development tools. Posix was originally modeled on Unix systems, but is currently supported by most modern operating systems.
For example, the DOS version of the well-known game Quake
by id
Software was compiled with DJGPP.
The typo in the word Exception
is in the actual message popped by
Linux.
This was reported for DOSEmu version 0.66.7; the format of
dosemu.conf
might be different in version 0.9x.
But the development environment will only run on Windows 9X/NT.
This DLL can be stripped off the debugging symbols using the
strip
utility, which leaves a much smaller--about
500KB--file.
The BUFFERS=40,8
setting defines a primary cache of 40 512-byte
blocks and a secondary cache of 8 blocks. The primary cache is used by
DOS to store the data actually read by a program, in case it is re-read
shortly afterwards; while the secondary cache is used to read data ahead
of the requests, which is optimized towards sequential reads.
ftp.simtel.net is actually several ftp sites arranged in a rotating pattern of IP addresses to help balance the load and to avoid access problems due to network outages and simultaneous user limits.
The version numbers of the packages listed here might not be up to date by the time you read this. For the latest versions, check out the DJGPP Mini-FAQ posted weekly to the the comp.os.msdos.djgpp news group. The file LISTINGS.zip available on every DJGPP site includes one file for every zip that lists all the files inside that zip; download and unzip it on your machine, and you've got a set of files you can search for substrings. Another place where you can look for the latest versions of all files is on the DJGPP server, which is also a convenient way of downloading individual files if you have lost them somehow.
Like its name suggests, texi2html
converts a Texinfo source to
HTML.
You will need to install the port of Bash and some auxiliary utilities
to be able to run shell scripts; texi2dvi
itself is part of the
GNU Texinfo distribution and comes with the v2gnu/txiNNNb.zip
archive.
The example uses Unix-style forward slashes, but DOS-style backslashes can also be used.
While admittedly perverse, this little monstrosity was written with the sole purpose of demonstrating that C and C++ have quite different semantics under certain circumstances. Some people think that C is a proper subset of C++; the above example shows that this is not true.
If you define an empty
substitute for __crt0_setup_arguments
, you don't need to define a
substitute for __crt0_glob_function
.
In particular, running strip
on a program and then compressing it
with DJP would produce a program that crashes upon startup.
Those who want all the details should consult a good book about DOS internals, such as Undocumented DOS, 2nd ed. by Andrew Schullman, or Geoff Chappel's DOS Internals.
All DOS programs get the default 20-handle table when they start; DOS only copies the first 20 handles into the child, so it is not possible to inherit more than 20 handles. The expansion of the default 20-handle table to 255 handles is a special feature of the DJGPP library, and it only happens when the programs exhausts all of the 20 handles while it runs. Therefore, when all of the first 20 handles are taken up by files inherited from the parent program, the child program can fail to start because the DJGPP stub loader needs one free handle to open and read the COFF executable into memory. The stub cannot use the enlarged 255-handle table, since it cannot call the DJGPP library. Such problems indeed happen in programs compiled with DJGPP v2.01; v2.02 fixes this bug.
Each handle created by a call to open
uses up one slot in the
SFT, whereas a handle created by dup
just increments the use
count of a slot that was already in use.
That's the program being debugged, in case you didn't know.
Programs that create machine code in malloc
ed storage and then
jump into it could have their EIP above EBP. The Allegro
library utilizes this technique in some of its functions (specifically,
compiled sprites and stretched blits are normally performed in this
way).
If you absolutely need to call ld.exe
directly, invoke gcc
once with a -v
switch and you will see what are the arguments
that you should pass to the linker in your case.
Here's a more detailed explanation. DOS cannot access memory above 1MB mark, where your DJGPP program lives, since real-mode addresses are 20-bit wide, and 20-bit addresses cover only the first megabyte. So, each time a DJGPP program needs to call a DOS function (or any other real-mode service, like some BIOS interrupt) and needs to pass data to or from that service, we must use some buffer in conventional memory to communicate with DOS and BIOS. The transfer buffer is a block of conventional memory that the DJGPP startup code allocates for this purpose. When a real-mode service is called, the data that needs to be submitted to it is copied to the transfer buffer, and the address of the transfer buffer is passed to the real-mode service. If the service returns some data (e.g., if you want to read a portion of a file), data is copied from the transfer buffer when the service returns.
The transfer buffer primarily exists for library functions, but it can also be used by an application, if it needs to invoke real-mode services.
Actually, the maximum possible value is FEF0h, or 65254 in decimal,
because the transfer buffer is created by the startup code by resizing
the PSP memory block. Since the resized block needs to leave 256 bytes
for the PSP, and needs to be aligned on a 16-byte boundary, you cannot
have the entire 65535 bytes for the transfer buffer. In DJGPP v2.01, if
you invoke stubedit
with a bufsize=64k
parameter, what you
actually get is a 2KB buffer, since the combined size of the PSP and the
transfer buffer will wrap around in a 16-bit variable when the startup
code computes it. The versions of stubedit
which will come with
DJGPP v2.02 and later explicitly warn you about this case and will reset
any value that is too large to the maximum allowed size of FE00h (65024
decimal) bytes--this is less than FEF0h because the latter is not
aligned on the 512-byte DOS sector size, which could slow down disk
I/O.
Without a real x87 FPU, an exception is generated by the CPU each time a
floating-point instruction is seen in the code.
__djgpp_exception_processor
is called for each such exception and
services it by calling the emulator, emu387.dxe
, or functions
from the emulator library libemu.a
(if the program was linked
with -lemu
), to emulate the instruction. Since exception
processing incurs a lot of overhead, this emulation is slow.
The DPMI 0.9 spec does not provide any means for the application to control where in the address space will the DPMI server allocate a particular chunk of memory. The application asks the DPMI server for whatever amount of memory it needs, and gets a chunk of that size whose address can be anywhere. Since the stack must be contiguous and expands downwards, growing it during program's run would require a very complicated code, unless it is pre-allocated at startup.
In case you wonder, the name !proxy
comes from the string
which identifies the use of this method: instead of getting the actual
command line, the program gets !proxy
followed by the address of
the actual command line.
If you use MASM or LIB32, please post your experiences to the comp.os.msdos.djgpp news group, so that I can make the above instructions less vague.
Note that mixing object files from different compilers generally doesn't
work at all, even if all the object files are in .obj
format.
This function calls the video BIOS interrupt 10h to allow bright
background colors to be used instead of blinking characters. DJGPP has
a library function, called intensevideo
, to do that, but for the
purpose of this example, let's assume we have reasons not to use it.
The DPMI spec indicates that you should not use the default stack if your procedure/interrupt handler uses more that 60 bytes, or 1/8 of the total stack space available by default.
DJGPP v2.01 makes the limit of _dos_ds
be 4GB, which effectively
disables memory protection when you use that selector. However, since
no memory outside the first 1MB is properly mapped into your program's
address space without additional DPMI calls, and the DPMI host is then
free to put memory-mapped devices, such as Weitek I/O space or the
linear frame buffer of an SVGA, on any address it sees fit, that huge
limit is an unjustified security hole. DJGPP v2.02 will really be
limited by 1MB+64KB.
If you want to decipher the 8-byte structure that is passed to
__dpmi_set_descriptor
in this example, read the documentation of
the __dpmi_get_descriptor
library function in the library
reference. This structure is the descriptor maintained by the processor
for each protected-mode segment, such as those loaded into the CS
and DS registers.
If you are using this example in your program, don't forget to disable
the handler at program's exit by calling the same function 0Ch of Int
33h with a zero mask in the CX register, and then deallocate the
callback by calling the _go32_dpmi_free_real_mode_callback
library function. Also, remember that all code and data touched by the
handler must be locked, otherwise it will crash under some DPMI servers,
such as CWSDPMI.
Locking a region of memory means that this region should be always present in RAM. Usually, the virtual-memory mechanism is allowed to page regions out of RAM when it needs to load another region that is not loaded. This happens if the program uses more memory than what is physically available to it. When a program needs to access an address that isn't currently in RAM, the operating system will look for some memory region that wasn't accessed for quite some time, and replace it with the block that needs to be accessed now. Locking a region prevents that region to be paged out, for as long as the program runs.
This discussion does not pertain to the __dpmi_int
function as opposed to the _go32_dpmi_simulate_int
function. On
the contrary, you will be much better off using __dpmi_int
, since
it automatically zeroes out some of the members of the real-mode
registers structure, while with _go32_dpmi_simulate_int
you need
to do that by hand.
Actually, it is possible to avoid reentrancy problems in interrupt-driven programs: programs known as TSRs (Terminate and Stay Resident) have been doing that for years. But doing so requires hooking and monitoring many DOS and BIOS interrupts, to know when it is safe to page. If CWSDPMI would use these techniques, it would take much more DOS memory to load and run. It would also need to be updated with every new DOS release, since some of the internal DOS structures it would need to monitor change their address and/or layout with new versions of DOS.
CWSDPMI has an experimental VDS support in its sources, but the distributed binary was compiled without it. Contact Charles Sandmann if you want to try to enable VDS support in CWSDPMI.
In case somebody thinks there is a contradiction here: I don't consider a requirement to provide information to be a restriction.
Note that this still allows the struct to be padded at the end.
For some reason, Microsoft doesn't like it when users disable numeric
tails. Several Microsoft publications warn against doing that, and I'm
told that Windows 98 has made it harder to disable them. I'm not sure
why do they object so much. Presumably, some programs rely on certain
directories to have numeric tails, so that they could be found even in
plain DOS mode. Apparently, some of those programs have short aliases
such as PROGRA~1
(the short version of the Program Files
directory) hard-wired into them, and Microsoft is afraid you could
reinstall or move those directories when numeric tails are disabled, and
thus cause such programs not to find their "home".
It is obvious that such programs are badly broken (e.g., the short alias
could easily be PROGRA~2
), and you have every right to yell at
the vendor who sells them to you. But even if you have no other way
than to live with them, my experience shows that you have nothing real
to worry about. Remember: numeric tails only have effect when files are
created or renamed. So, if you want to be on the safe side, re-enable
them before installing Windows software, especially if the programs you
install need to run in DOS mode as well (a typical example would be a
disk-recovery package such as Norton Utilities). Then disable numeric
tails again, once the installation is over.
For what it's worth, I always run my system with numeric tails disabled, and I have yet to see a single real problem.
If you need Binutils configured for elf32-i386
target that can be
used with DJGPP, you can find it at
http://www.multimania.com/~placr/.
Since these series are computed using a deterministic algorithm, they are not really random. Real random numbers can only be a result of unpredictable physical processes such as radioactive decay etc. However, a good algorithm for pseudo-random numbers produces a series of numbers that pass many tests for randomality.