Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Making a fat binary for Linux and Mac

29 views
Skip to first unread message

Frederick Gotham

unread,
Oct 22, 2020, 6:48:34 AM10/22/20
to
I have an idea for making a 'fat binary' that doesn't require any kernel
support on Linux or Mac.

So let's say we start off with a small shell script like this:

#!/bin/sh

arch=`uname -m`

if [ $arch = "x86_64" ] ; then
// Do something
elif [ $arch = "aarch64" ] ; then
// Do something
fi

exit 0

The most important line is the last one "exit 0" because it will stop
the script. It is after this line that I will put the x86_64 binary,
followed by the aarch64 binary, so the script will look something like
this:

#!/bin/sh

arch=`uname -m`

if [ $arch = "x86_64" ] ; then
skip=00290
count=08816
elif [ $arch = "aarch64" ] ; then
skip=09106
count=14688
else
exit 1
fi

dd if="$0" of=/tmp/binaryfile bs=1 skip=${skip} count=${count} > /dev/null 2>&1

chmod +x /tmp/binaryfile
/tmp/binaryfile

exit 0
[ - - - x86_64 binary goes here - - - ]
[ - - - aarch64 binary goes here - - - ]

So as a test program to try it out, I'll now write a small C++ program
that prints a number sequence:

#include <iostream> /* cout, endl */
#include <thread> /* this_thread::sleep_for */
#include <chrono> /* seconds */

auto main(void) -> int
{
for ( unsigned i = 0; /* ever */ ; ++i )
{
std::this_thread::sleep_for(std::chrono::seconds(1u));
std::cout << i << std::endl;
}
}

I have compiled this program for two architectures:

g++ main.cpp -DNDEBUG -Os -o prog_x86_64
aarch64-linux-gnu-g++ main.cpp -DNDEBUG -Os -o prog_aarch64

and so I have two binaries as follows:

-rwxr-xr-x 1 root root 8816 Oct 22 10:53 prog_x86_64
-rwxr-xr-x 1 root root 14688 Oct 22 10:52 prog_aarch64

The x86_64 binary is 8816 bytes.
The aarch64 binary is 14688 bytes.

So now I get the size of my small script above:

-rwxr-xr-x 1 root root 290 Oct 22 11:31 myscript.sh

It is 290 bytes, which means I know how much to skip by:

dd if="$0" of=/tmp/binaryfile bs=1 skip=00290 count=08816

Finally I concatenate the 3 files:

cat myscript.sh prog_x86_64 prog_aarch64 > fatbin

And I run my fat binary:

chmod +x fatbin
./fatbin

I've tried this 'fatbin' file on both architectures and it works. You
can download the files here and try it yourself:

http://virjacode.com/experimental/prog.cpp
http://virjacode.com/experimental/prog_aarch64
http://virjacode.com/experimental/prog_x86_64
http://virjacode.com/experimental/myscript.sh
http://virjacode.com/experimental/fatbin

I think it might even be possible to extend this script so that it
doubles as a batch file for Microsft Windows, so maybe we could have two
Linux binaries, a Mac binary, as well as a Microsoft binary all in one
file. It can made threadsafe and re-enterable by using system-supplied
temporary filenames or UUID's instead of "/tmp/binaryfile".

Juha Nieminen

unread,
Oct 22, 2020, 10:03:39 AM10/22/20
to
Frederick Gotham <cauldwel...@gmail.com> wrote:
> dd if="$0" of=/tmp/binaryfile bs=1 skip=${skip} count=${count} > /dev/null 2>&1

Since you are copying the contents somewhere else anyway, you could jsut as
well compress that content and run it through gunzip (or even xz), to make
this file smaller.

Also, if you don't want to rely in this actually being a file with an actual
name (rather than, for example, being piped through something else, or for
any other reason this script not having a valid name as $0), what you can do
instead is to use heredoc in conjunction with base64 encoding/deconding
(and gzip compression to boot). It will make the file larger, but you won't
be relaying on it being actually a file and $0 being its name.

In other words, something along the lines of:

base64 -d << 'DATA_END' | gunzip > /tmp/binaryfile
(gzip-compressed base64-encoded data here)
DATA_END

Ralf Fassel

unread,
Oct 22, 2020, 12:04:41 PM10/22/20
to
* Frederick Gotham <cauldwel...@gmail.com>
| dd if="$0" of=/tmp/binaryfile bs=1 skip=${skip} count=${count} > /dev/null 2>&1

Nitpick: use mktemp(1), or at least something along these lines to
create the temporary file and remove it after use:

cleanup () {
rm -f "$TMPFILE"
exit $1
}
TMPFILE=${TMPDIR:-/tmp}/foobar.$$
trap "cleanup 1" 1 2 15

dd ... of="$TMPFILE" ...

TNX
R'
0 new messages