12

Is there some simple method for determining if a DOS (or OS/2, or Windows etc.) binary (.exe or .dll) is 16-bit or 32-bit? The Linux file command just says "executable".

I want to distinguish between programs that run on any "x86" processor, and those that "require a 386", in a way that is simpler than setting up two environments and attempting to run the program.

And what I want is a tool, like the file command, not primarily more detailed technical explanations.

1
42

Plain DOS executables, in either COM or MZ format, don’t provide this information in their headers (when there is one — COM format doesn’t have a header). The only reliable way to determine whether a program requires a given CPU is to try running it on some less capable system (or emulation, e.g. with PCem which has accurate emulations of different x86 processors), and rely on either the program checking that the CPU is good enough for it, or on it crashing with an invalid opcode (on a 186 or later). Alternatively, you could try disassembling the program and looking for 386+ opcodes; but that’s only as reliable as the disassembly process, and will give inconsistent results on programs which adapt to the CPU (running 8086 code only on 8086s, 386 code on 386s, etc.).

For protected-mode executables specifically, you could look for one of the common 32-bit DOS extenders or stubs, e.g. DOS/4G, 32RTM... A program using one of these will require a 386. (There are also 286 extenders.) See Are .COM executable binaries real mode or protected mode? for more discussion of how DOS programs start (but real- v. protected-mode is only one aspect of instruction set usage; programs can use 286, 386, 486, Pentium etc. instructions even in real mode).

Many post-MZ executable formats encode this information; for example, 16-bit Windows and OS/2 executables are “new executables”, with a flag indicating which CPU is required. 32-bit OS/2 executables are “linear executables”, identified by an LX header following the MZ stub, and specify their target CPU and operating system. Likewise, 32-bit Windows programs are “portable executables”, and the PE header identifies the target CPU. file knows about these and will identify them correctly. However, it’s worth checking the details of the MZ stub too, since LX or PE executables can contain a 16-bit variant in their MZ stub, and would therefore be capable of running on a 16-bit CPU.

5
  • 2
    Yes, I understand you’re after a simple tool, but AFAIK there isn’t one. There are emulators of only 16-bit PCs; for example PCem has to be configured with a given CPU, and if set to a 286 it won’t emulate any 386 instructions. – Stephen Kitt Feb 14 at 16:39
  • 7
    WRT running under emulation - to be really sure there are no 32-bit instructions, one would need to execute every possible code path under that emulation. I think it's clear that file doesn't want to solve the Halting Problem. – Toby Speight Feb 15 at 14:57
  • 2
    @Toby not necessarily – you don’t want to run the code paths on the 32-bit side after a CPU check, if there’s a 16-bit side ;-). But yes, I agree it’s impossible to get a definitive statement, and one ends up relying on the realities of computers at the time: a program which crashed with an invalid opcode when run on a 286, because it didn’t bother checking it was running on a 386, wouldn’t be too popular. – Stephen Kitt Feb 15 at 15:00
  • 2
    I'd argue that the 32-bit side of a test isn't a possible code path in the emulated environment! But, especially in early personal computers, programmers tended to play fast and loose with self-modifying code (such as self-uncompressing binaries) and worse. I think we're in violent agreement - it's really not possible by mere inspection. – Toby Speight Feb 15 at 15:08
  • 2
    @Toby oh yes I agree we’re in violent agreement, I was being facetious. I have a program somewhere which does code-path analysis to try to determine the type of CPU required, but even then the only thing it claims is to attempt to determine the maximum CPU which may be required, not the minimum. – Stephen Kitt Feb 15 at 15:15
17

There is no easy way.

The original DOS "MZ" type executable header do not contain such information about what kind of code it contains or what CPU type it needs. It just contains a binary image that is loaded to memory and information about how to start it in real mode, so there are no separate 16-bit or 32-bit binaries.

The binary image may contain any real mode opcodes that are available for a given type of CPU, but if it does contain 32-bit protected mode code, there will be a real mode loader stub to switch to protected mode and jump to run the 32-bit image contained in the executable file.

So DOS cannot know what the executable contains, DOS simply loads the executable binary into memory, does the relocations, and jumps to run the code at the entry point given in the headers. It is up to the executable what opcodes it contains, and for example if it needs a certain type of CPU to run certain opcodes, it can perform the CPU type detection if it is suitable for running the rest of the program. The executable might for example contain a stub to put the 386 CPU into 32-bit protected mode and then execute most of the program in that mode instead of 16-bit real mode.

So in order to detect what type of CPU is needed, the program has to be for example run in a virtual machine that sees what opcodes are executed. A disassembler can also be used to see what opcodes there is in the binary. While there might be standard compiler startup codes that could be detected by a tool analyzing the generated binary, there are also infinitely many other ways to make a program that does not use standard compilers or libraries, or that can make the disassembler to not be able to follow each and every code path, for example by dynamically generating a pointer where to go execute code.

So, it is not impossible. It is just not easy.

28
  • 6
    Why not? If I just add because of what I said, there is no easy way to determine if the executable contains 32-bit code or not, without disassembling it or running it under an emulator to figure it out? – Justme Feb 14 at 15:05
  • 7
    You are asking about DOS executables. DOS cannot know what the executable contains, DOS simply loads the executable binary into memory and jumps to run the code at the given entry point. It is up to the executable what opcodes it contains, and for example detect the CPU type if it is suitable for running the rest of the program. The executable might for example contain a stub to put the 386 CPU into 32-bit protected mode and then execute most of the program in that mode instead of 16-bit real mode. – Justme Feb 14 at 15:25
  • 2
    @Justme - re there is no easy way... IMO if you added that to your answer, it would be complete. One can infer that as it stands, but explicit is better. – another-dave Feb 14 at 15:45
  • 6
    No, simply stop at the first 386-specific instruction. The ones that are not 386-specific instructions, the same opcode work differently in different CPU modes. The same data move opcode will move 16 bits in real mode, and 32 bits in protected mode. The file command does not include a heuristic x86 disassembler to determine it. That is almost same as if the file command could determine if a JPG file contains a picture of a cat or a dog. – Justme Feb 14 at 18:04
  • 6
    @Justme To add more complications: many programs were compressed and unpacked after loading (either to save disk space and/or to make tampering more difficult). A simple "file scan" wouldn't even see the op-codes of the main program, only of the unpacker. – TripeHound Feb 14 at 23:42
8

Is there some simple method for determining if a DOS binary (.exe or .dll) is 16-bit or 32-bit?

For one, DOS doesn't know about 32 bit, it's a strict 16 bit system. Second, .DLL are not DOS executables but Windows libraries.

The Linux file command just says "executable".

Because all EXE start out as 16 bit programs, marked by the magic number "MZ" in the first two bytes. DLLs as well, as they carry a stub header of a 16 Bit EXE. This is done to enable a warning (*1) in case someone tries hard enough to start it under DOS. So for all practical purpose they are 16 bit executables.

Clarification: I want to distinguish between programs that run on any "x86" processor, and those that "require a 386", in a way that is simpler than setting up the two environments and attempting to run the program.

For real DOS programs, that is not windows programs, there is no simple to detect such, as DOS does simply not support 32 Bit. Any DOS program starts out as a 16 bit program, marked an MZ header. 32 Bit usage under DOS is custom made by the program itself and invisible to DOS. Of course, one could search for signatures of common DOS extenders, but that would still only cover part of all 32 Bit programs. Nor would it be decisive, as programs may include 32 bit support for memory access, but not being 32 bit themself.

Now, if this is about a 32 bit Windows EXE programs. Here, much like with DLL or any other file for Windows containing code, the file starts out with a PE-Header - which in turn starts out with a 16 bit EXE stub able to issue a warning (*1) if started under DOS.

Any detector looking for a (16 Bit) DOS program will find that EXE files as well as DLLs are exactly that, a DOS program.

After that stub (*2), the PE header contains several sections, foremost the COFF (Common Object File Format) chunk, which, after it's magic number, contains a machine type, telling what machine the code is intended for. Common values are

  • 00000h for 16 bit
  • 0014Ch for 32 Bit (Decimal 332 for 386-32)
  • 08664h for 64 bit

This is followed by more fields defining other characteristics, including word size and so on. For all practical purpose a machine type of 0014Ch is the most significant indicator for 32 Bit Windows applications. This is followed by an optional PE+ header, denoting different formats for headers in 64 bit versions.

Last but not least Windows DLL work exactly like Windows EXE featuring the same PE header.

While the PE format is nowadays the most common one, there are some variations. The most common are

  • New Executable (NE), introduced by Windows 1.0 / MSDOS, 16 bit
  • Linear Executable (LE), for mixed 16/32 bit in Windows 9x, OS2 and some DOS extenders
  • Linear Executables for OS2 2.0 (LX)
  • MP used with Phar Lap DOS extender.

*1 - The well known "This program cannot be run in DOS mode" message.

*2 - Consisting of the DOS header, a pointer to the PE header and the stub itself. Ignoring the pointer is a common error of programs trying to decode a PE format.

22
  • 7
    @TomasBy Windows programs do not have to have a GUI, Windows sppots as well CLI programs - still, they are not DOS programs, but Windows programs. They can use all Windows resources and functions. There is no way to distinguish a Windows CLI program form a Windows GUI program - well, no except missing runtime calls to the GUI. Bottom line, a Windows program not using the Windows GUI is not a DOS program, but stays a Windows program. – Raffzahn Feb 14 at 17:37
  • 2
    @TomasBy ‘Some are DOS, some are Win or OS/2. How do you quickly tell them apart’ – that’s a different question from what you have asked in your original post. – user3840170 Feb 14 at 17:51
  • 3
    You know the Windows command prompt (cmd.exe) is nothing to do with DOS, right? It just started life by implementing a very similar set of commands. FWIW, I spent a few years writing programs that ran on 32-bit Windows NT, which programs almost never had a GUI. – another-dave Feb 14 at 21:41
  • 2
    Yeah, I don't know what that means to you. Suppose I type sc start foo at a cmd prompt. Is that "DOS-looking" even though what it's doing is sending an RPC to the service controller? – another-dave Feb 14 at 22:55
  • 5
    @TomasBy - I'm sincerely attempting to help; but you have been using imprecise language, thus the "what do you mean by <I>X</I>?" questions. You may know what you mean, but we perhaps do not. – another-dave Feb 15 at 0:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.