Search code examples
headerexedos

DOS .exe file header if image size is divisible by 512


I have found conflicting information about the DOS .exe header fields specifying the image size. http://www.delorie.com/djgpp/doc/exe/ indicates that the first field (f2) should be image_size modulo 512, and the second field (f4) should be image_size divided by 512, rounded up. However other documentation specifies that f2 should be 512 if the image_size is divisible by 512. Some software use the formula f2 + f4 * 512.

Thus if image_size is 2046 bytes, then this is correct: f2=510, f4=4.

So if the image_size is 2048 bytes, which are the correct values?

  • f2=0, f4=4
  • f2=512, f4=3
  • f2=512, f4=4

Is there some definitive documentation about this?

(Maybe I should look at what OpenWatcom produces and what FreeDOS and DOSBox use.)


Solution

  • This answer is based on observing the .exe output of OpenWatcom (owcc -bdos -mcmodel=s) for the example image_size values in the question, and the .exe files in FreeDOS 1.2.

    • image_size is typically the size of the .exe file (e.g. for .exe files in FreeDOS 1.2), but it can be smaller (e.g. there is some extra data at the end of the file which won't be loaded to memory by DOS; or if the image is the DOS extender stub, and the 32-bit executable part is right at offset image_size).
    • image_size includes the .exe header, relocation entries, program code and program (initialized) data. It excludes uninitialized data (.bss).
    • min_extra_paragraphs (uint16 at offset 10) is the size of uninitialized data (.bss, and usually includes the stack) divided by 16, rounded up.
    • bytes_in_last_block (uint16 at offset 2) is (as generated by OpenWatcom) image_size modulo 512. Instead of the value 0, the value 512 can also be used.
    • blocks_in_file (uint16 at offset 4) is image_size divided by 512, rounded up.
    • Correct permissive formula for image_size used by stubit.c in WDOSX: image_size = -(-bytes_in_last_block & 511) + blocks_in_file * 512.
    • The exeflat tool in the FreeDOS kernel (source line) uses the following formula: image_size = ((bytes_in_last_block ? bytes_in_last_block : 512) - 512) + blocks_in_file * 512. This is equivalent to to the formula above for 0 <= bytes_in_last_block <= 512. Larger values of bytes_in_last_block are invalid, and should be reported as an error.
    • image_size <= blocks_in_file * 512.
    • The FreeDOS kernel (source line) ignores bytes_in_last_block, and loads (blocks_in_file * 32) - header_paragraphs bytes, which is equivalent to using image_size <= blocks_in_file * 512.

    Examples:

    • If image_size is 2046 bytes, this is correct: f2=bytes_in_last_block=510, f4=blocks_in_file=4.
    • If image_size is 2048 bytes, this is correct (generated by OpenWatcom): f2=bytes_in_last_block=0, f4=blocks_in_file=4, and this is also correct: f2=bytes_in_last_block=512, f4=blocks_in_file=4.