15

The ELF file format has an e_machine field in the header, which specifies which ISA the executable expects. And the e_ident structure also has an EI_OSABI field that specifies which ABI the executable is expecting. My guess is, that these fields are how my operating system knows not to go ahead and run a binary which was compiled for some other machine.

But how was this achieved on early UNIXes, early Linuxes, and others that used a.out? a.out, from what I can tell, has no equivalent field(s).

  • 3
    It's not possible, unfortunately. – wizzwizz4 Apr 24 '18 at 15:57
19

The a_midmag field contains a machine identifier, which can be used on platforms which support that field. a_midmag is a 32-bit value stored in host byte-order (fun already), and bits 16 to 23 give the machine type. The most comprehensive list of machine identifiers I’ve found so far is the list used by libbfd, which shows that the field has been used for quite a long time (values 1, 2 and 3 indicate 68010, 68020 and SPARC respectively). file also has a number of magic values, e.g. for Sun systems and the PDP-11.

Unfortunately the “platforms which support that field” caveat means that it can’t be relied upon, and the FreeBSD a.out manpage highlights that fact:

Since not all of the supported architectures use the a_midmag field, it can be difficult to determine what architecture a binary will execute on without examining its actual machine code. Even with a machine identifier, the byte order of the exec header is machine-dependent.

Historically, a.out binaries were directly executable, their first bytes were a jump instruction to skip the header (see the original documentation). So you could try to recognise old binaries’ architecture by figuring out whether the first (two?) bytes are a valid jump instruction with a sensible offset... Later binaries are not directly executable by the CPU; for example the start of a Linux a.out binary is a magic number such as 0x0064010b (see for example this old i386 bash binary), which isn’t useful x86.

So the answer to your question is really that to determine what architecture an a.out executable runs on, you need to try to run it...

14

The short answer is: early Unix systems did not bother to track which architecture an executable was for. In general, the architecture an executable was for, was the one the executable was found on.

If you're on a PDP-11, /bin will contain PDP-11 executables. If you're on a VAX, /bin will contain VAX executables. Multiple architectures all trying to store their executables in the same file system was simply not a problem people were typically having, and therefore not a problem that needed fixing.

If you tried to run something for another architecture (e.g. you restored some files from a tape written on another system) the worst that would happen is the program would crash and give you a core dump. If you were doing that kind of thing you were expected to know to either get the correct tape for your architecture, or recompile the software from source.

In those rare cases where you needed to have binaries from multiple architectures co-existing (e.g. a file server storing files) the early solutions were to do things like having separate directories for each architecture, e.g. /nfs/vax/bin, /nfs/sun3/bin, /nfs/ibm_rt/bin. Then each client would be configured to use the particular directory that was appropriate. Only a fool would mix executables for different architectures in the same directory (absent some other way to track them, e.g. architecture-specific suffixes).

  • I don't remember multi-architecture executables until Apple introduced "universal binaries" during one of their CPU changes on Macs. Did Unix do it earlier? – Barmar Apr 24 '18 at 21:19
  • 3
    @Barmar Theoretically, it was possible to construct an executable that would work on both VAX and PDP-11, see the first winner of the IOCCC - hint.html for the description. – Leo B. Apr 25 '18 at 1:31
  • 2
    @Barmar there were hacks on various systems where code could be written to load on multiple architectures, or OSs (DOS and CP/M for example). I think the first “specified” fat binary support was in NeXTSTEP. – Stephen Kitt Apr 25 '18 at 4:50
8

edit:

Aaaargh - I just I answered something that wasn't asked for. Stupid me. I should not write answers in the early morning.

I still don't delete it, as it might help to find related information about the content of the fields. So feel free to downvote and/or complain about me not reading thruout.


So this is about the ELF fields e_machine and e_osabi , not a.out


Both values are rather random and have been used incosistently when it comes to less than common CPUs/machines/OSes.

e_machine starts with

EM_NONE  0 No machine
EM_M32   1 AT&T WE 32100
EM_SPARC 2 SPARC
EM_386   3 Intel 80386
EM_68K   4 Motorola 68000
EM_88K   5 Motorola 88000
EM_860   7 Intel 80860
EM_MIPS  8 MIPS RS3000

But be aware, it's realy rather random, as some values have been reused (it's 'only' a 16 bit word) and/or independly assigned - luckyly for more uncommon CPUs.

Similar e_osabi starts with

ELFOSABI_SYSV    0 System V
ELFOSABI_HPUX    1 HP-UX
ELFOSABI_NETBSD  2 NetBSD
ELFOSABI_LINUX   3 Linux
ELFOSABI_GNUHURD 4 GNU Hurd
ELFOSABI_SOLARIS 6 Solaris
ELFOSABI_AIX     7 AIX
ELFOSABI_IRIX    8 IRIX

When analyzing old ELF binaries it's important to know, that e_osabi is a later addition. Originally this area was padding (toward the full 16 Bytes). 'clean' old files should read 0 here, no matter what OS was used. Therefore ELFOSABI_SYSV is rather an asumption than a definitive marking.

The Wiki-article about ELF gives already away some common values found today. Linux' Portable Formats Specification lists some values (*1). A much longer and up to date list list can be found at the Binary Analysis Platform Project (*2,3)


*1 - It ofc, includes only values relevant for Linux at that time (1993!)

*2 - Here now many older symbols/values are missing.

*3 - A great source for poking into reverse engineering under Linux.

  • 5
    +1 let alone for the funny edit :) – Tommylee2k Apr 24 '18 at 11:57
  • 1
    Aaaargh - I just I answered something that wasn't asked for. Stupid me. Happened to me a few times, too. – Bregalad Apr 25 '18 at 9:02
3

There is no 100% sure way. For an earnest albeit somewhat futile attempt to recognize a.out files, see /usr/share/magic (on Linux; elsewhere your paths may vary) - the pattern file of the UNIX file command.

There you can see that both "386 executable" and "VAX executable" have the same magic header (0407) and the same offset for the symbol table length, and there is no other way to tell them apart except by execution. The first matching pattern ("386 executable", as in the file) will win, just because it is deemed more probable to encounter a 386 a.out executable than a VAX one.

2

The a.out format is, in general, simply plain binary code. It contains no information whatsoever what platform it is intended for.

Obviously, any a.out binary will do something on any platform (the CPU, after all, simply interprets the binary code in the file according to its make and type), but the intended execution can only be achieved on the platform the a.out was built for.

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.