+++ 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.