View Full Version : Fat Binary for DOS Windows Linux

2020-11-04, 10:33
I have devised a technique for making a 'fat binary' that will run on 16-Bit MS-DOS, several architectures of Linux, and all versions of MS-Windows.

This is a follow-up to "Fat Binary - MS-Windows and four Linux" which I
multi-posted on 24 October 2020 to three newsgroups:

comp.lang.c, comp.lang.c++, comp.unix.programmer

You can see it here on comp.lang.c++:


Two days prior I posted "Making a Fat Binary for Linux and Mac", which
you can see here on comp.lang.c:


I have now made a fat binary that contains 6 executable programs:

* 16-Bit MS-DOS (for 8088 processors and above)
* 32-Bit MS-Windows (which runs on x86_64 too)
* Linux arm32
* Linux arm64
* Linux x86_32
* Linux x86_64

For the Microsoft Windows executable file, I went with a 32-Bit
executable because it will run on both 32-Bit and 64-Bit versions of

I used the shareware "Pacific C" compiler to build the 16-Bit DOS
version, and originally I was going to embed this 16-Bit program as the
"MZ stub" inside the "PE" file for the Win32 program, however it was
simpler to keep the two programs separate and to use the method
described on the following webpage to embed a binary in a DOS batch file,
I used the "H2B" method that creates a COM program for printing hex as


So here's how my 'combined Linux / DOS / Windows' script looks now. Note
that each line begins with four spaces. This script will run as an 'sh'
shell script on Linux, and will run as a batch file on MS-DOS and Windows.

:; if [ -z 0 ]; then #
@echo off

# Linux shell script starts here

# x86 32-Bit = i386, i686
# x86 64-bit = x86_64
# arm 32-Bit = arm
# arm 64-Bit = aarch64 aarch64_be armv8b armv8l

arch=`uname -m`

case ${arch} in
exit 1

tmpfile=$(mktemp /tmp/fatbinary_${arch}.XXXXXXXXXX)
dd if="$0" of=${tmpfile} bs=1 skip=${skip} count=${count} > /dev/null 2>&1
chmod +x ${tmpfile}
${tmpfile} "$@"
rm -rf "$tmpfile"
exit ${retval}
# Microsoft batch file starts here


VER | FIND "Windows" > NUL

REM Script for Windows 95 and above starts here (32-Bit)

REM The next 6 lines are just for creating a temporary file
set /a y$$=%random%+100000
set y$$=temp%y$$:~1,2%.%y$$:~-3%
if exist "%temp%\%y$$%.exe" goto loop
set "y$$=%temp%\%y$$%"
set "tempfile=%y$$%.exe"

findstr /v /c:" " "%~f0" > %tempfile%
%tempfile% %1 %2 %3 %4 %5 %6 %7 %8 %9
set "finalerrorcode=%ERRORLEVEL%"
del %tempfile%
exit /b %finalerrorcode%


REM Script for MS-DOS v6.22 and below starts here (16-Bit)

IF EXIST %0.BAT %0.BAT %1 %2 %3 %4 %5 %6 %7 %8 %9
REM The next 6 lines are to create the DOS COM program "hex to binary"
ECHO 0DxFP,0Xx.t0P,=XtGsB4o@$?PIyU WwX0GwUY Wv;ovBX2Gv0ExGIuht6>>%TEMP%\H2B_@!7$.COM
ECHO ?@}I{uNWEF~NFAa_Lj@KLtH]~CEvEFIKbAa_wN@SuNS`{ECCttasae~BHM>>%TEMP%\H2B_@!7$.COM
ECHO AcjFnvnHAwrvx[}gIKDw??Frt\gqj~{?s?csIsqo{O_KtBve{Sx{nB{Eu@>>%TEMP%\H2B_@!7$.COM
ECHO fq`tkfk?e@oKCA_?_E@?WxAs?agBwRjnLK?s@w`G`LKLAcyA?@ xAsZpk`L>>%TEMP%\H2B_@!7$.COM
ECHO ~KxlqLct@vAc_A_yBJ@xAGZp?o?sBXq`LR@xUrFQt=A_E?B?~r B?~r0>>%TEMP%\H2B_@!7$.COM

%TEMP%\H2B_@!7$ " ::dos16bit" < %0 > %TEMP%\S256@!7$.EXE

%TEMP%\S256@!7$.EXE %1 %2 %3 %4 %5 %6 %7 %8 %9
DEL %TEMP%\S256@!7$.EXE
REM The exit command won't work so must use goto with a bad label

The Linux part of the above script has "\n" line endings, whereas the
Microsoft part of the above script has "\r\n" line endings. See the
first link at the end of this post to get the original file which also
has the hex values for the 16-Bit DOS program at the end.

As for the program for which I chose to make a fat binary, I went with a
sha256 digest calculator, using code by Alain Mosnier from repository:

I have turned Mosnier's code into a single-source-file C program that
calculates the sha256 digest of its first command line argument. For the
C code, see the second link at the end of this post.

I compiled this C program 6 times and got the 6 following binaries:

-rwxr-xr-x 1 root root 7842 Nov 2 12:15 prog_dos16bit.exe
-rwxr-xr-x 1 root root 17422 Oct 24 11:10 prog_mswin_x86_32.exe
-rwxr-xr-x 1 root root 5464 Oct 24 13:50 prog_x86_32
-rwxr-xr-x 1 root root 10216 Oct 24 13:50 prog_x86_64
-rwxr-xr-x 1 root root 5528 Oct 24 13:50 prog_arm32
-rwxr-xr-x 1 root root 10080 Oct 24 13:50 prog_arm64

(See download links 3-8 at the end of this post)

The DOS program is concatenated to script as hex values:

cat prog_dos16bit.exe | xxd -p \
| awk '{ print " ::dos16bit " $s "\\r" }' >> script.txt

and I concatenated the 5 other binaries as follows:

cat script.txt prog_mswin_x86_32.exe prog_x86_32 prog_x86_64 \
prog_arm32 prog_arm64 > ftsha256.bat

The result is my fat binary with size 75 kilobytes:

-rwxr-xr-x 1 root root 75016 Nov 2 13:52 ftsha256.bat
(See download link 9 at the end of this post)

This file can be executed at the command line on any PC like this:

ftsha256.bat This is my message

And it prints the digest:

3311b7c0bd91b6c73a38212de8ade31c51910f17480ad212ed 2b9798a35b7747

I have verified it works on:
* MS-DOS 6.22
* MS-Windows XP 64-Bit
* Linux arm32
* Linux arm64
* Linux x86_64

It should also work on Linux x86 32-Bit, let me know if someone tries
it. It might need more work on the ASCII/ANSI versions of MS-Windows
that came between Windows 3.11 and Windows 2000, I haven't tried it so
I'm not sure.

I'm gonna take a break from this for a while, but in the future I might
iron out the ASCII versions of MS-Windows, and then later add in
binaries for Apple MacOS.


1: http://virjacode.com/experimental3/script.txt
2: http://virjacode.com/experimental3/sha256.c
3: http://virjacode.com/experimental3/prog_dos16bit.exe
4: http://virjacode.com/experimental3/prog_mswin_x86_32.exe
5: http://virjacode.com/experimental3/prog_arm32
6: http://virjacode.com/experimental3/prog_arm64
7: http://virjacode.com/experimental3/prog_x86_32
8: http://virjacode.com/experimental3/prog_x86_64
9: http://virjacode.com/experimental3/ftsha256.bat