Why there are `/lib` and `/lib64` but only `/bin`?

First, why there are separate /lib and /lib64:

The Filesystem Hierarchy Standard mentions that separate /lib and /lib64 exist because:

10.1. There may be one or more variants of the /lib directory on systems which support more than one binary format requiring separate libraries. (...) This is commonly used for 64-bit or 32-bit support on systems which support multiple binary formats, but require libraries of the same name. In this case, /lib32 and /lib64 might be the library directories, and /lib a symlink to one of them.

On my Slackware 14.2 for example there are /lib and /lib64 directories for 32-bit and 64-bit libraries respectively even though /lib is not as a symlink as the FHS snippet would suggest:

$ ls -l /lib/libc.so.6
lrwxrwxrwx 1 root root 12 Aug 11  2016 /lib/libc.so.6 -> libc-2.23.so
$ ls -l /lib64/libc.so.6
lrwxrwxrwx 1 root root 12 Aug 11  2016 /lib64/libc.so.6 -> libc-2.23.so

There are two libc.so.6 libraries in /lib and /lib64.

Each dynamically built ELF binary contains a hardcoded path to the interpreter, in this case either /lib/ld-linux.so.2 or /lib64/ld-linux-x86-64.so.2:

$ file main
main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, not stripped
$ readelf  -a main  | grep 'Requesting program interpreter'
      [Requesting program interpreter: /lib/ld-linux.so.2]

$ file ./main64
./main64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, not stripped
$ readelf  -a main64  | grep 'Requesting program interpreter'
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

The job of the interpreter is to load necessary shared libraries. You can ask a GNU interpreter what libraries it would load without even running a binary using LD_TRACE_LOADED_OBJECTS=1 or a ldd wrapper:

$ LD_TRACE_LOADED_OBJECTS=1 ./main
        linux-gate.so.1 (0xf77a9000)
        libc.so.6 => /lib/libc.so.6 (0xf760e000)
        /lib/ld-linux.so.2 (0xf77aa000)
$ LD_TRACE_LOADED_OBJECTS=1 ./main64
        linux-vdso.so.1 (0x00007ffd535b3000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f56830b3000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f568347c000)

As you can see a given interpreter knows exactly where to look for libraries - 32-bit version looks for libraries in /lib and 64-bit version looks for libraries in /lib64.

FHS standard says the following about /bin:

/bin contains commands that may be used by both the system administrator and by users, but which are required when no other filesystems are mounted (e.g. in single user mode). It may also contain commands which are used indirectly by scripts.

IMO the reason why there are no separate /bin and /bin64 is that if we had the file with the same name in both of these directories we couldn't call one of them indirectly because we'd have to put /bin or /bin64 first in $PATH.

However, notice that the above is just the convention - the Linux kernel does not really care if you have separate /bin and /bin64. If you want them, you can create them and setup your system accordingly.

You also mentioned Android - note that except for running a modified Linux kernel it has nothing to do with GNU systems such as Ubuntu - no glibc, no bash (by default, you can of course compile and deploy it manually), and also directory structure is completely different.


The reason is that the lib/lib64 directories can contain files which happen to have the same name because those are libraries shared with diverse programs. Putting them in separate directories solves the conflict. There's (usually...) no good reason for distributing same-named executables on the same system which are 32/64-bit, but since there can be a mixture of executables, the shared libraries have to be provided for.