Linux - upgrade 32-bit to 64-bit? (i[3456]86 to amd64/x86_64)

It's a commonly asked question:
"Can I upgrade my 32-bit installed Linux distribution to 64-bit?"
The answer is: Yes*
The more accurate and complete answer is a (much more) qualified yes:
*Yes, ... if your distribution has both 32-bit and 64-bit versions,
typically significantly to much easier to backup the 32-bit version,
saving all data (and meta-data) that might be needed, do a fresh 64-bit
installation, then carefully migrate data as wanted onto the 64-bit
version.  And of course one has to have the appropriate 64-bit capable
machine.  Similar procedure can also be used migrating to new(er)
64-bit capable hardware.

But what if you really want to do an upgrade-in-place operation, from
32-bit to 64-bit?  The "worst case" would be highly difficult ... if you
really want to go that route.  It's Linux, one can dig as necessary to
determine precisely what's different and make those changes - but one
may need the host to be down when doing that.

But if you have a Linux distribution with multiarch support, e.g.
current Debian, and at least certain derivatives:
http://www.debian.org/News/2013/20130504
http://www.debian.org/News/2011/20110726b
https://wiki.debian.org/Multiarch#Current_Status
Then doing such an upgrade (or cross-grade) is quite a bit easier, but
still quite non-trivial.
"in the future will even allow live migrations from 32-bit to 64-bit systems"
Yes, in future that may be a comparatively easy procedure, but it is
quite doable presently, ... it's just not all that easy to do.

So ... thought I'd run the experiment, as I do have some hosts I might
want to do such an upgrade (or cross-grade) on.  There is also a lot of
documentation and the like on how to go about doing it ... but doesn't yet
seem to be any simple sure-fire recipe that one can run to do it all.
Though there are lots of useful bits and "tools" and procedures
information, on how to go about doing it both general, and in a fair bit
of specificity.

Anyway, I set up a virtual machine (under qemu-kvm), installed
Debian GNU/Linux 7.4.0 "Wheezy" i386 on it and upgraded to 7.5.0
(as I'm writing this, the 7.5.0 ISO images aren't out on the mirrors
quite yet, but should be very soon - 7.5.0 was released 2014-04-26, and
the packages are available).

So, the procedure - for Debian (or derivative?) with multiarch support.
This is just an outline - I don't cover all the steps in detail:

Add 64-bit architecture support:
# dpkg --add-architecture amd64

Update apt so it also knows about the 64-bit stuff:
I set the Debian CD ISO images:
Debian GNU/Linux 7.4.0 "Wheezy" - Official amd64 CD Binary-1 20140208-13:47
Debian GNU/Linux 7.4.0 "Wheezy" - Official i386 CD Binary-1 20140208-12:25
respectively on /dev/sda and /dev/sdb
and mounted respectively on /media/cdrom9 and /media/cdrom8
(I did that to avoid all the insert/remove CD stuff, and if I needed
packages from either, to generally go much lighter on the mirrors).
I then updated my /etc/apt/sources.list so its active lines contained:
deb [arch=amd64] file:/media/cdrom9 wheezy main
deb [arch=i386] file:/media/cdrom8 wheezy main
deb http://http.debian.net/debian/ wheezy main
deb-src http://http.debian.net/debian/ wheezy main
deb http://security.debian.org/ wheezy/updates main
deb-src http://security.debian.org/ wheezy/updates main
deb http://http.debian.net/debian/ wheezy-updates main
deb-src http://http.debian.net/debian/ wheezy-updates main
and then did:
# apt-get update

One should be sure one's packages are in a clean state before proceeding, e.g. see:
http://www.debian.org/releases/wheezy/i386/release-notes/ch-upgrading.en.html#system-status
and especially:
http://www.debian.org/releases/wheezy/i386/release-notes/ch-upgrading.en.html#package-status

Good idea to capture package status before we proceed further, e.g. save
the output of:
# dpkg --get-selections
to a file.  Also, many of the "recipes" given make use of such data or
portions thereof (e.g. to pick out i386 that's installed, and configure
to install amd64).  It may also be advisable to capture information
regarding the installed packages, if they were specifically requested,
or automatically installed to satisfy dependency(/ies).  One may later
want to similarly mark amd64 packages as they were for their earlier
i386 counterparts.  One might also want that information for packages
who's architecture is "all", as some of those might get
upgraded/replaced/reinstalled along the way.

Now we're ready to install packages.  First, add a 64-bit kernel:
# apt-get install linux-image-amd64:amd64
Then reboot using the 64-bit kernel.
Presuming that went fine, one can remove the 32-bit kernel

So much for the easy portions.  I'll just outline the rest.

It's mostly a matter of getting things added/replaced/removed in the
"right" order - or sufficiently correct order that it all works.

Some of the many key bits:

dpkg & apt - until these are repladed with the 64-bit versions, the
existing versions think the native architecture is 32-bit, and 64-bit is
"foreign".  There are lots of dependencies - most notably libraries.
When dealing with dpkg and apt-get, one can append specific
architecture, e.g. appending on package architecture:
:i386 for i386 (32-bit), and
:amd64 for amd64 (64-bit)
- that can be done to be sure one installs/removes/purges the desired
package(s), particularly while dpkg and apt still are at 32-bit and
treat the native architecture as i386 and treat amd64 as foreign.
Beware that the apt package has a lot of dependencies, so it's not one
of the easier ones to get from 32-bit to 64-bit, but it is both
important and essential.

One can also use - and _ suffixes for remove and purge with apt-get, so,
e.g. one can add and remove/packages with a single apt-get command.

The -s option to apt-get and --simulate option to dpkg will come in very
handy.  Many/most of the operations you'll want to see first if the
dependencies have already been met, or not or if any conflicts or other
problems show up.  If/when you encounter any predepends, those need to
be installed first, not at the same time with dpkg.

You'll want to be able to use dpkg fairly well, it's lower-level than
apt-get, but you'll need it for some operations, e.g. for making the
transition of the apt package from 32-bit to 64-bit, and even dpkg
itself.

Be very careful with "essential" packages.  If you make an error there
it may be difficult to recover.

The binutils package may come in quite handy on
current Debian, notably as it contains ar - and Debian packages (.deb
files) are in ar format.  If needed, one can use ar(1) to extract
contents from a Debian package.  E.g. if one breaks findutils, which
dpkg -i depends upon (depends upon the find binary).

In general you shouldn't have to --force anything.  If apt-get is giving
you warnings with notification such as:
To continue type in the phrase 'Yes, do as I say!'
That is probably not the path you want to go down.

Package versions need to match.  If you're going from 32-bit to 64-bit
version of package, in many cases apt-get won't handle that for you (or
at least not without forcing it, which will typically break things) and
you'll need to use dpkg.  E.g. where the version numbers match, one can
use dpkg -i to install the 64 bit version, and where it's, e.g.
something like a general utility and not a library, it will generally
replace the 32-bit version with the 64-bit version - if all the
dependencies for the 64-bit version are in place or covered on the dpkg
command, and all reverse-dependencies for removing the 32-bit version
have been covered.

Use of apt-get download can be very handy.
One may want to use /var/cache/apt/archives for download location, as
that's where apt-get install defaults to caching files it downloads.

You'll want to be sure you notice, catch and correct, certain errors
that may be critical.  E.g. if a package update/installation causes new
initramfs to be built for kernel(s) and that throws error(s), be sure to
correct those.  E.g. one can fix a missing/broken dependency, then
reconfigure the dependent package that caused the error to be thrown,
e.g.:
...
Setting up lvm2 (2.02.95-8) ...
Setting up LVM Volume Groups...done.
update-initramfs: deferring update (trigger activated)
Processing triggers for initramfs-tools ...
update-initramfs: Generating /boot/initrd.img-3.2.0-4-amd64
mount: error while loading shared libraries: libmount.so.1: cannot open shared object file: No such file or directory
# dpkg -i /media/cdrom9/pool/main/u/util-linux/mount_2.20.1-5.3_amd64.deb
...
# dpkg-reconfigure lvm2
...
In the above case, mount was installed, but it was i386 and wasn't happy
with its library situation, whereas the libraries for amd64 mount were
already installed - so just needed the amd64 mount installed and
reconfigure lvm2 to correct the error it encountered in updating the
initramfs.

And if one does go through with getting rid of all i386 (e.g. if it's
not wanted or needed), one can remove the i386 architecture, e.g.:
# dpkg --remove-architecture i386
One would also then typically want to remove any i386 specific
architecture bits from /etc/apt/sources.list
and again update apt:
# apt-get update
One may want to reboot when all is done, to ensure all is working as
expected, and to also ensure no i386 binaries are still in use (e.g.
unlinked open files still being executed).  Alternatively, one may look
for open unlinked files that belong to any removed/purged i386 packages.
lsof may be handy for that (or use of find and the proc filesystem),
one can see some examples of that for a bit different task on:
http://linuxmafia.com/pipermail/sf-lug/2014q2/010408.html
And a much cleaner run at it.
The files linked below are bzip2(1) -9 compressed from
script(1) with -t option.
One may want to shorten up some of the longer
(idle/waiting/inactive) times in the time file, e.g.:
(where here, our leading $ is PS1, and leading > is PS2)
$ bzip2 -d < 32to64.time.bz2 |
> > 32to64.shorter_wait_times \
> sed -e 's/^[0-9]\{2,\}\.[0-9]\{1,\} /3.000000 /'
And uncompress the script file:
$ bzip2 -d 32to64.script.bz2
And one can then review it with scriptreplay(1),
e.g. to see that at triple speed:
$ scriptreplay 32to64.shorter_wait_times 32to64.script 3
That was captured connecting via serial console (virtually),
as for terminal type, one would probably want to do that in an 80x24
vt100, or xterm, screen, or ANSI terminal or emulation, for best
reproduction/viewing results.

32to64.script.bz2 32to64.time.bz2

Also, what was captured mostly just shows actual changes done.
For the most part I used a separate session for doing things like
simulation runs, e.g.:
# apt-get -s ...
# dpkg --simulate ...
downloads:
# apt-get download ...
and working out various dependencies (typically from the simulations
above), and statuses (typically via use of
$ COLUMNS=200 dpkg -l
and sometimes also:
# apt-get -s install
and picking over the resultant data).
For the most part, when
# dpkg --simulate -i ...
came back looking clean, the corresponding
# dpkg -i ...
worked fine, but not always - sometime the latter would expose
additional dependencies or other (relatively minor) issues.  Most
commonly, if not always, such issues were resolved just by bringing in
another package to a few packages or so, and then a:
# dpkg --configure --pending
and all was then fine.
Seems the whole process could (almost?) be automated by some
higher-level script/program (e.g. perl) to work all that stuff out and
implement it.  The only part that might be a bit more tricky, is in some
cases, there wasn't a precise correspondence between i386 and amd64
packages.  E.g., in almost all cases, it was simply matter of same
package and version number in i386 architecture being replaced with
same from amd64 architecture.  But not always - there were some
exceptions.  But at least where I encountered such, it was easy enough
to figure out from the dependencies and/or lack thereof, or from the
names and descriptions, etc.

And one issue that was found a little bit later (2014-06-13) and corrected, from our system log file:

Noticed bug/issue with spell, e.g.:
$ echo foo | spell
/usr/bin/ispell: Illegal format hash table
Tried:
# (cd / && umask 022 && dpkg-reconfigure spell)
# (cd / && umask 022 && dpkg-reconfigure dictionaries-common)
# (cd / && umask 022 && dpkg-reconfigure ispell)
Those didn't correct it, but this did:
# (cd / && umask 022 && dpkg-reconfigure iamerican)
That updated:
/var/lib/ispell/american.hash
Guessing this may have been issue left over from i386 to amd64 conversion

See also: CrossGrading (a Debian system - on the Debian wiki)