+++
date = "2018-09-01T16:17:00+08:00"
draft = false
tags = ['lfs']
title = "LFS/BLFS Multilib/Multiarch Note"
summary = """
[Linux From Scratch](http://www.linuxfromscratch.org/lfs) is a great
project that provides you with step-by-step instructions for building
your own custom Linux system, entirely from source code.  To Keep It
Simple and Stupid as an educational project, LFS does not contain
instruction to build multilibs.  I would discuss the changes of LFS/BLFS
procedure to make a GNU/Linux system with multilib support in this article.
"""
authors = ["xry111"]
+++

# Introduction

[Linux From Scratch][1] is a great project that provides you with
step-by-step instructions for building your own custom Linux system,
entirely from source code.  To Keep It Simple and Stupid as an educational
project, LFS does not contain instruction to build multilibs.

However, for those who use LFS as their main system, multilib support may
be essential.  For example, some non-free softwares are only provided in
32-bit binaries, and others may want to use [x32 ABI][2] to improve the
system performance.

There are [various][3] [modifications][4] [which][4a] add multilib support
to LFS procedure.  Basically they just add the instruction to build multilib
in LFS book.  But as LFS an educational project, we should focus on *why*
we should use these instruction. And, for a complete multilib environment
we also have to build multilib for BLFS book.  A *Multilib BLFS* book would be a massive work and I don't think someone can maintain it. 

So, I would explain how multilib system works, and show the basic idea of
how to add multilib to LFS system.

[1]:http://www.linuxfromscratch.org/lfs/
[2]:https://sites.google.com/site/x32abi/
[3]:http://www.linuxfromscratch.org/~dj/history/lfs-systemd-multilib/
[4]:http://lists.linuxfromscratch.org/pipermail/lfs-dev/2018-August/072416.html
[4a]:https://www.williamfeely.info/lfs-multilib/

# Multilib/Multiarch Basic

## How Multilib works?

The basic idea of multilib is simple.  The kernel will parse the header
of the executables and find out whether the code is LP64 (x86-64),
LP32 (x86) or L64P32 (x32) in it.  Then the kernel can arrange the address
space and provide syscall interface specified for this ABI then the code
with all the three ABIs will work.  For example, if we have a static linked
x32 program compiled in a multilib system with

```
gcc -mx32 foo.c -o foo -static
```

To run the program `foo` in a 64-bit LFS system, just enable `X86_X32`
in the kernel config of the LFS system, and recompile the kernel.  Then you
can copy `foo` into the LFS system and run it.  It will work.  For running
traditional x86 code in 64-bit LFS system, just enable another kernel
config `IA32_EMULATION`.

Unfortunately many programs are dynamically linked.  When we try to run
a dynamically linked 32-bit program `bar` on a 64-bit LFS system, the
kernel will parse the header of `bar`.  The path to dynamic linker is
**hard coded** in the ELF file `bar`:

```
$ readelf -l bar | grep INTERP -A1
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
```

But we don't have a `/lib/ld-linux.so.2` in the 64-bit LFS system.  So
the `execve` syscall to execute `bar` just return with `ENOENT`.

Now forget LFS for a minute.  On a travial binary distribution, we solve
the problem by installing `ld-linux.so.2` to `/lib` with a package manager,
or manually.  Then we'll hit another problem.  The shared object name
`libc.so.6` is hard coded in the ELF file `bar`:

```
readelf -d bar | grep NEEDED
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
```

So `ld-linux.so.2` immediately knows `bar` needs a shared library named
`libc.so.6`.  But the path to it is not hard coded so `ld-linux.so.2` has
to find it.  If `bar` has no rpath and `LD_LIBRARY_PATH` is not set,
`ld-linux.so.2` would just search the library paths hard coded in itself,
and those specified by `/etc/ld.so.conf`.  If we just copied a
`ld-linux.so.2` into a brand new 64-bit LFS system and try to execute `bar`,
`ld-linux.so.2` won't find a 32-bit `libc.so.6`.  Then it would fail and
the `execve` call will return `ENOENT`.  We have to copy a 32-bit
`libc.so.6` and put it into a place `ld-linux.so.2` would search, and
do it for all shared libraries `bar` needs.  Finally we can execute `bar`
successfully :).

So, a *multilib* enough to run a foreign 32-bit binary should contain
a working 32-bit dynamic linker at `/lib/ld-linux.so.2` and essential
32-bit shared libraries for this binary.  And a multilib enough to run
a foreign x32 binary should contain a working x32 dynamic linker at
`/libx32/ld-linux-x32.so.2` and essential x32 shared libraries.  Now our
task is to build them *from source* to get a *Multilib* Linux From
**Scratch**.

## Where to put the multilib?

The dynamic linker path is specified by the ABI and hard coded in ELF
files.  The three ABIs supported by a 64-bit Linux kernel on x86-64
processors and their dynamic linker paths are:

|  ABI      | Dynamic linker path            |
|-----------|--------------------------------|
| 32-bit    | /lib/ld-linux.so.2             |
| 64-bit    | /lib64/ld-linux-x86-64.so.2    |
| x32       | /libx32/ld-linux-x32.so.2      |

We can choose to install the dynamic linkers to other location but then
we must create a symlink for it at the specified location.  For example,
the original LFS book installs `ld-linux-x86-64.so.2` to `/lib`, then
symlink it to `/lib64`.

However, the location of other libraries are arbitary.  You just can't put
32-bit and 64-bit libraries in a same directory.  After all, the main
Glibc library is named `libc.so.6` so 32-bit and 64-bit Glibc will certainly
collide in the same directory.  Just choose three different directories to
contain the libraries in three different ABIs.  For example, you can make
the library location to seem like the dynamic linker location:

|  ABI      | Library path in /         | Library path in /usr           |
|-----------|---------------------------|--------------------------------|
| 32-bit    | /lib                      | /usr/lib                       |
| 64-bit    | /lib64                    | /usr/lib64                     |
| x32       | /libx32                   | /usr/libx32                    |

For another possibility, you may have a *mostly* 64-bit system with a few
32-bit applications and want the "main" library path to be 64-bit:

|  ABI      | Library path in /         | Library path in /usr           |
|-----------|---------------------------|--------------------------------|
| 32-bit    | /lib32                    | /usr/lib32                     |
| 64-bit    | /lib                      | /usr/lib                       |
| x32       | /libx32                   | /usr/libx32                    |

But then there will be a 32-bit `ld-linux.so.2` in `/lib`, along with many
64-bit libraries.  Seems strange :(.

Other users (for example me) prefer Debian-like [multiarch][5] directories:

|  ABI      | Library path in /         | Library path in /usr           |
|-----------|---------------------------|--------------------------------|
| 32-bit    | /lib/i386-linux-gnu       | /usr/lib/i386-linux-gnu        |
| 64-bit    | /lib/x86\_64-linux-gnu    | /usr/lib/x86\_64-linux-gnu     |
| x32       | /lib/x86\_64-linux-gnux32 | /usr/lib/x86\_64-linux-gnux32  |

[5]:https://wiki.debian.org/MultiarchSpec

And even the following "strange" totally non-FHS layout is possible:

|  ABI      | Library path              |
|-----------|---------------------------|
| 32-bit    | /system32/lib             |
| 64-bit    | /system64/lib             |
| x32       | /systemx32/lib            |

Then how to let the dynamic linkers to find them?  The intuitive way is
creating an `/etc/ld.so.conf` contains all the directories:

```
# Begin /etc/ld.so.conf.d/01-multilib.conf

/usr/lib64
/usr/lib
/usr/libx32

# End
```

Then all three dynamic linkers will search all three directories.  But
that's not a problem since they'll ignore ABI incompatible libraries.

A better way is hard coding the correct location into the dynamic linkers.
LFS builds them from source (in Glibc package) so it is possible.  Then
each dynamic linker will only search the directory containing the
compatible libraries specified at build time.  We'll show how to do that
later.

# Changes in LFS

## Do We Need Temporary Multilib?

At first you may wonder, *maybe I can skip multilib in Chapter 5 and only
build multilib in Chapter 6.*  It's possible in theory but somehow tricky.
To build 32-bit multilib of [Glibc in Chapter 6][6], we need a 32-bit
libgcc which is part of [GCC Pass 2 in Chapter 5][7].  So we have to use
`--enable-multilib` for GCC Pass 2.  But then the multilib of `libstdc++`
and other GCC runtime libraries would be also enabled.  They rely on
multilib of [Glibc in Chapter 5][8] so we have to build 32-bit and x32
Glibc in Chapter 5 to provide it.  And, [some libraries in Chapter 5 is
temporarily linked to in Chapter 6][9].  So we should build all multilib
in Chapter 5 unless we know the multilib from one package is absolutely
unnecessary (for example `libmagic` in package [File][10]).

[DJ's multilib LFS book][3] build multilib in an additional [Chapter 10][11]
so Chapter 5 need not to be modified.  But in Chapter 10 a temporary
toolchain for multilib still has to be built.

If you are tough enough, you can try to hack GCC building procedure to
build 32-bit and x32 libgcc after GCC Pass 2 manually.  I have not done
this and I don't suggest to do this.  I just choose to build all multilib
of temporary packages in Chapter 05.

[6]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter06/glibc.html
[7]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter05/gcc-pass2.html
[8]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter05/glibc.html
[9]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter06/createfiles.html
[10]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter05/file.html
[11]:http://www.linuxfromscratch.org/~dj/history/lfs-systemd-multilib/chapter10/chapter10.html

## Toolchain packages

### Binutils

The symlink from `lib64` to `lib` should be skipped.

If the library directories is same as dynamic linker directories
(32-bit in `lib`, 64-bit in `lib64`, and x32 in `lib32`), no other changes
are needed.  Otherwise, we should edit `ld/genscripts.sh`
in binutils source code to ensure correct library directories in linker
scripts.  Manually fixing the generated linker scripts is also possible.

### GCC

The option `--disable-multilib` should be changed to
`--enable-multilib-list=64,32,x32`.  Then GCC will built runtime libraries
(libgcc, libstdc++, etc.) automatically for them.  This change should also
be applied for [Libstdc++ in Chapter 5][12].

If the library directories is same as dynamic linker directories, no
other changes are needed.  Otherwise, we should edit
`gcc/config/i386/t-linux64` in the source code to let GCC know them.

Even if the multilib directory layout is not *multiarch*, we should use
`--enable-multiarch`.  Then [Python 3][13] package can use
`gcc -print-multiarch` to discriminate shared objects and other platform
specific files with different suffixes in their names.

[12]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter05/gcc-libstdc++.html
[13]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter06/Python.html

### Glibc

Glibc need to be built three times, each for an ABI.  The complete
configure line in Chapter 5 should be changed like:

```
CC="$LFS_TGT-gcc $BUILD_MULTI" \
CXX="$LFS_TGT-g++ $BUILD_MULTI" \
../configure \
		--prefix=/tools \
		--host=$LFS_TGT_MULTI \
		--libdir=/tools/$LIBDIR_MULTI \
		--build=$(../scripts/config.guess) \
		--enable-kernel=3.2 \
		--with-headers=/tools/include \
		libc_cv_forced_unwind=yes \
		libc_cv_c_cleanup=yes
```

The variables with suffix `_MULTI` varies with ABI:

| ABI    | `BUILD_MULTI` | `LFS_TGT_MULTI`      | `LIBDIR_MULTI`           |
|--------|---------------|----------------------|--------------------------|
| 32-bit | `-m32`        | `i686-lfs-linux-gnu` | 32-bit library directory |
| 64-bit | `-m64`        |`x86_64-lfs-linux-gnu`| 64-bit library directory |
| x32    | `-mx32`       |`x86_64-lfs-linux-gnu`| x32 library directory    |

Explaination of variables and options:

* `--host=$LFS_TGT_MULTI`: LFS book has already explained that this is
necessary for cross compiling.  For 32-bit multilib we have to use the
value `i686-lfs-linux-gnu` to tell the building system to use i686 (32-bit)
assembly code instead of 64-bit assembly incompatable with 32-bit ABI.
* `CC=$LFS_TGT-gcc $BUILD_MULTI` and `CXX=$LFS_TGT-g++ $BUILD_MULTI`:
By default `--host=$LFS_TGT_MULTI` makes the building system to use
`$LFS_TGT_MULTI-gcc` as C compiler.  That's enough for the original LFS
book without multilib, but we don't have a `i686-lfs-linux-gnu-gcc` now
and `x86_64-lfs-linux-gnu-gcc` would normally generate code in 64-bit ABI.
So we have to override the C/C++ compiler for 32-bit and x32.  The `-m64`
for 64-bit could be omitted.

And, for the final GCC in Chapter 6:

```
CC="gcc $BUILD_MULTI -isystem $GCC_INCDIR -isystem /usr/include" \
CXX="g++ $BUILD_MULTI" \
../configure --prefix=/usr \
	--host=$HOST_MULTI
	--libdir=/usr/$LIBDIR_MULTI
	--disable-werror \
	--enable-kernel=3.2 \
	--enable-stack-protector=string \
	libc_cv_slibdir=/$LIBDIR_MULTI \
	libc_cv_complocaledir=/usr/lib/locale
```

Explaination of new options and variables:

* `--host=$HOST_MULTI`: Should be `i686-pc-linux-gnu` for 32-bit, and
`x86_64-pc-linux-gnu` for x32 and 64-bit.  It tells Glibc to use i686
assembly for 32-bit version.  It's necessary for 32-bit and can be omitted
for 64-bit and x32 (since `config.guess` returns the `x86_64-pc-linux-gnu`
or `x86_64-unknown-linux-gnu` when kernel is 64-bit).
* `libc_cv_slibdir=/$LIBDIR_MULTI`: Tell Glibc to install shared libraries
to correct (customized) location.
* `libc_cv_complocaledir=/usr/lib/locale`: Tell Glibc to use standard
`/usr/lib/locale` for locale archives, instead of
`/usr/$LIBDIR_MULTI/locale`.  This would save disk space and make the locale
archive consistent for 32-bit, 64-bit and x32 applications.

We should install 64-bit Glibc **after** 32-bit and x32 version.  Then the
64-bit executable binarys will overwrite the 32-bit and x32 ones.  And then
we need to edit the `ldd` script.  For security reason it only handles
the executables with correct dynamic linker path which is specified by the
`RTLDLIST` variable.  Since we have finally installed the 64-bit version,
the `RTLDLIST` variable only contains one path to 64-bit dynamic linker.
Open `/usr/bin/ldd` with an editor and modify `RTLDLIST` to be three
dynamic linker paths sperated by space.

If we are not using the *standard* library directories, the dynamic linkers
would be in wrong place (`/$LIBDIR_MULTI/ld-*.so`).  We should symlink the
dynamic linkers to correct location.

## Other packages

Most packages could be configured for multilib with:

```
CC="gcc $BUILD_MULTI" CXX="g++ $BUILD_MULTI" \
${original_configure_line_in_book} \
	--libdir=$LIBDIR_MULTI
	--host=$HOST_MULTI
```

One may think `CC="gcc -m32"` is enough for 32-bit, but several packages
has platform specific code so it's better to use `--host=i686-pc-linux-gnu`.
Again, `--host` can be omitted for 64-bit and x32.

Here `${original_configure_line_in_book}` is the original confiugre line
from the LFS book.  Actually we can simplify the line by creating some
*pesudo-cross compiler wrappers* like:

```
#!/bin/sh

exec gcc -m32 "$@"
```

If you put this shell script as `/usr/bin/i686-pc-linux-gnu-gcc`, and
do similar thing for `i686-pc-linux-gnu-g++`, you can skip the setting of
`CC` and `CXX` since `configure` will automatically find the wrapper
scripts for `i686-pc-linux-gnu` host.  For x32 ABI, though the canonical
host triplet is also `x86_64-pc-linux-gnu`, but we can use a customized
triplet like `x86_64-x32-linux-gnu` or `x86_64-pc-linux-gnux32`.

Multilib of most LFS packages can be built and installed this way.   But
some packages need special handling.

### [GMP][14]

GMP configure script recongnize "customized" host triplets for optimization
depending on the hardware.  For example when I run `config.guess` in GMP
package on my laptop I get the triplet `ivybridge-pc-linux-gnu`.  So if
we use `--host=x86_64-pc-linux-gnu` or `--host=i686-pc-linux-gnu`, the
script will configure for *generic* x86-64 or x86 CPU.  This will make
GMP slower.  So, instead of using `--host`, we should use the environment
`$ABI` for GMP.  GMP configure script recongizes `ABI=32`, `ABI=64`, and
`ABI=x32`.  For example, `ABI=32` would tell GMP to use 32-bit x86
assembly code, and add `-m32` to compiler flags.

And `gmp.h` is platform specific.  We must rename them to `gmp-64.h`,
`gmp-32.h` and `gmp-x32.h` for three ABIs and create a `gmp.h` wrapping 
them:

```C
#if defined(__x86_64__) && defined(__LP64__)
#include "gmp-64.h"
#elif defined(__x86_64__)
#include "gmp-x32.h"
#else
#include "gmp-32.h"
#endif
```

### [Bzip2][15a]

Bzip2 has no configure script.  Most annoying, the library path (relative
to install prefix) is hard coded to be `lib` in `Makefile`.  We have to
use `sed` to edit it, or install it manually.

### [Pkg-config][15]

By default `pkg-config` only know one `.pc` file path in library paths,
which is `/usr/lib/pkgconfig`. So the `.pc` files in `/usr/lib64/pkgconfig`
will be useless. Unfortunately most modern systems need to be mainly
64-bit.  They have many libraries with only 64-bit version so they only
have pkgconfig files in `/usr/lib64/pkgconfig`. We can override this using
configure option `--with-pc-path=...`.  We can add multiple directories
and seperate them by `:`.

And, if `pkg-config` detects an pkgconfig file for 64-bit, it will output
`-L/usr/lib64 -lfoo` for libfoo.  `-L/usr/lib64` is unnecessary and may
cause problem.  We can use `--with-system-library-path=...` to tell
pkg-config which `-L` ldflags should be skipped.

I recommend to compile `i686-pc-linux-gnu-pkg-config` and
`x86_64-x32-linux-gnu-pkgconfig` for 32-bit and x32.  Some BLFS packages
have additional ABI-specific information in the pkgconfig files
(for example `glib` and `gobject-introspection`).
`i686-pc-linux-gnu-pkg-config` should be configured to search 32-bit
and shared (`/usr/share/pkgconfig`) pkgconfig files only, and
`x86_64-x32-linux-gnu-pkg-config` should be configured to search
x32 and shared pkgconfig files only.  `--host=i686-pc-linux-gnu` will
tell configure script of other packages to use
`i686-pc-linux-gnu-pkg-config` instead of `pkgconfig`. if it exists.

How to add the prefix `i686-pc-linux-gnu-`?  Use the configure option
`--program-prefix` building pkg-config.

### [Ncurses][16]

Ncurses installs `ncursesw6-config`.  Since we install the 64-bit version
last, `/usr/bin/ncursesw6-config` would be the version from 64-bit.  Its
output would contain `-L/usr/lib64`.  It's annoying for 32-bit and x32.
We have to use `sed` to edit it and remove the `-L` output.

Some other packages also has `*-config` scripts.  They need to be modified
too.

### [Libffi][16a]

Do not modify headers install path since the headers are ABI specific.
If you don't have `i686-pc-linux-gnu-pkg-config`, you may need
`PKG_CONFIG_PATH=/usr/lib/pkgconfig` for the packages need libffi (for
example, Python 3).

### [OpenSSL][17]

OpenSSL has customized configure system.  Fortunately it's easy to use.
It's `Configure` script (not `config`) accepts `linux-x86_64` for 64-bit,
`linux-x86` for 32-bit, and `linux-x32` for x32.  Still we have to remember
to change `--libdir`.

### [Python 3][13]

Pythons tends to have problem when cross compiling.  So do not use `--host`
for it.

Python 3 only use `/usr/lib/python3.x` as package directory.  If we use
`--libdir=/usr/lib64` for Python, we would get a broken installation.
To discriminate shared objects in one package with different ABIs,
Python 3 hard code the *multiarch name* from `gcc -print-multiarch` at
build time and suffix the shared objects with it.  For example, there are
`_ssl.cpython-36m-x86_64-linux-gnu.so` for 64-bit, and
`_ssl.cpython-36m-i386-linux-gnu.so` for 32-bit.

The header `pyconfig.h` is ABI specific and need to be renamed and wrapped.
And, since we can't use `--libdir`, we have to manually move
libpython3.6m.so to the correct library path.

### [Meson][18]

Meson itself is in pure Python and has no binary libraries.  But to use
Meson building system to build multilib, we need to tell Meson some
information of the ABI.  After installation of Meson, create
`/usr/share/meson/native/x86` for 32-bit:

```ini
[binaries]
c = '/usr/bin/i686-pc-linux-gnu-gcc'
cpp = '/usr/bin/i686-pc-linux-gnu-g++'
pkgconfig = '/usr/bin/i686-pc-linux-gnu-pkg-config'
ar = '/usr/bin/ar'
strip = '/usr/bin/strip'
exe_wrapper = ''

[properties]
sizeof_void* = 4
sizeof_long = 4

[host_machine]
system = 'linux'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'
```

Then we can use `meson --native-file x86` for 32-bit.  `exec_wrapper = ""`
tells Meson we can run the generated executables natively.  Without it
some BLFS packages refuse to build.

{{% callout note %}}
I'd used `/usr/share/meson/cross` and `--cross-file`, just like a
*pseudo-cross* building with autoconf `configure` script.  But it turned out
some packages refuse to build certain parts (for example `gir` files) when
they are cross compiled.  And, `meson` now uses *host* pkg-config to locate
`g-ir-scanner` and `g-ir-compiler`.  So we have to stop pretending cross
building for 32-bit.
{{% /callout %}}

[14]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter06/gmp.html
[15]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter06/pkg-config.html
[15a]:http://www.linuxfromscratch.org/lfs/view/development/chapter06/bzip2.html
[16]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter06/ncurses.html
[16a]:https://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter06/libffi.html
[17]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter06/openssl.html
[18]:http://www.linuxfromscratch.org/lfs/view/8.3-rc2/chapter06/meson.html

# Changes in BLFS

Most BLFS packages can be built for multilib like normal LFS packages.
Still some packages need special case.

## [Python 2][19]

I have a [multiarch patch][20] for Python 2.  With it we can build Python 2
just like building Python 3 in LFS.

[19]:http://www.linuxfromscratch.org/blfs/view/8.2/general/python2.html
[20]:/assets/patches/Python-2.7.15-multiarch-2.patch

## [Cmake][20]

I suggest to edit the default library pathes in
`/usr/share/cmake-${version}/Modules/GNUInstallDir.cmake`
so we don't need to set them manually each time.  But it only supports
32-bit and 64-bit (no x32 support) now.  So we still nned a
`-DCMAKE_INSTALL_LIBDIR=/usr/lib32` for x32.  I don't know how to hack
cmake to support installing x32 libraries to `/usr/lib32`.

Some packages using cmake doesn not use `GNUInstallDirs.cmake` but use
a config variable `LIB_SUFFIX`.  Which can be used to specify library
path like `-DLIB_SUFFIX=64` (result in `/usr/lib64`).  But there are
still packages hard coding `lib` in `CMakeLists.txt`.  They are quite
annoying and need some `sed`.

## [Gstreamer][21]

We have to set `libexecdir` of Gstreamer same as `libdir` because
it has some ABI specific helper programs.

[21]:http://www.linuxfromscratch.org/blfs/view/systemd/multimedia/gstreamer10.html

## [Gobject-introspection][22]

It's very tricky.  My approach is install `i686-pc-linux-gnu-g-ir-scanner`
etc. alongside with the normal `g-ir-scanner`.  The Python code of gobject-
introspection is installed in the library path so we can hold all three
versions.  Then hack the code of 32-bit and x32 version so they'll find
correct compiler and pkg-config.  And, edit `gobject-introspection-1.0.pc`
so other packages can find correct gobject-introspection with (prefixed)
pkg-config.

However Meson building system always searches `g-ir-scanner` etc. from
`$PATH` instead of calling `pkg-config`.  I [hacked][23] Meson code to
force it search gobject-introspection tools using pkg-config.  It seems
working well.

[22]:http://www.linuxfromscratch.org/blfs/view/systemd/general/gobject-introspection.html
[23]:https://github.com/mesonbuild/meson/commit/766e62ef358b169770bf823ec958a5c1f5e5d009

## [Rustc][24] and [librsvg][25]

Rustc is a compiler so it doesn't need multilib itself.  But we have to
build multilib for its runtime libraries (just like libstdc++ from GCC).
Simply modify `config.toml` in BLFS book will do the job:

```toml
# see config.toml.example for more possible options
[llvm]
targets = "X86"

# When using system llvm prefer shared libraries
link-shared = true

[build]
# install cargo as well as rust
extended = true
target = ["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]

[install]
prefix = "/usr"
docdir = "share/doc/rustc-1.25.0"

[rust]
channel = "stable"
rpath = false

# get reasonably clean output from the test harness
quiet-tests = true

# BLFS does not install the FileCheck executable from llvm,
# so disable codegen tests
codegen-tests = false

[target.x86_64-unknown-linux-gnu]
# delete this *section* if you are not using system llvm.
# NB the output of llvm-config (i.e. help options) may be
# dumped to the screen when config.toml is parsed.
llvm-config = "/usr/bin/llvm-config"

[target.i686-unknown-linux-gnu]
llvm-config = "/usr/bin/llvm-config"
linker = "i386-linux-gnu-gcc"
```

But rustc will install many 64-bit libraries in `/usr/lib`.  I don't like
this behavior so I remove all of them and add
`/usr/lib/rustlib/${arch}/lib` to `/etc/ld.so.conf`.  Rustc seems also
[supporting x32][26] now but I've not tested.

After a rustc with multilib has been installed, cross compiling multilib
for librsvg [is simple][27].

[24]:http://www.linuxfromscratch.org/blfs/view/systemd/general/rust.html
[25]:http://www.linuxfromscratch.org/blfs/view/systemd/general/librsvg.html
[26]:https://github.com/rust-lang/rust/pull/45224
[27]:https://gitlab.gnome.org/GNOME/librsvg/blob/master/COMPILING.md#cross-compilation

# Other Packages

## Google Go Compiler

It needs no multilib - the compiler will compile 32-bit runtime as needed.
Set `GOARCH=386` then it would produce 32-bit code.  But it doesn't support
x32 now.
