FreeBSD and -m32 Support

I'm curious as to why FreeBSD doesn't work with multilib toolchains. As I recall, 32bit applications need to be run in a 32bit chroot.

I'm wondering why this hasn't been fixed. On ArchBSD, we can use -m32 and even use it with C++11 support using gcc 4.8.0. Should ports have this enabled by default on amd64 systems?

I know that most of the issues caused by multilib on FreeBSD was due to the includes/machine directory.

We worked around this by having machine/64 and machine/32 and using a wrapper to gcc, that would including from each directory depending on if -m32 was passed or not.

I'm posting this because I was reading the Wine page, and it still states that Wine needs to be run in a 32 bit chroot, but this problem is pretty easily overcome, and it would be highly beneficial to be able to use -m32 on FreeBSD amd64.

So I'm just curious on your thoughts about this, or why multilib support isn't enabled by default in base or ports.

Anyways, here are the documented steps took to get -m32 working in FreeBSD.

EDUCATIONAL PURPOSE ONLY!

Code:
  # Move machine headers to a 64/ directory and create wrappers
  mv "$srcdir"/machine64 "$pkgdir"/usr/include/machine/64

  # Install 32 bit machine headers to /usr/include/machine/32
  cp -R "/usr/src/freebsd/sys/i386/include /usr/include/machine/32

Create the wrappers.

Code:
  cd /usr/include/machine/
  for i in 64/pc/*; do
    b=${i#64/pc/}
    cat > "pc/$b" <<EOF
#if __x86_64
#  include <machine/64/pc/$b>
#else
#  include <machine/32/pc/$b>
#endif
EOF
  done

  for i in 64/*; do
    b=${i#64/}
    if [ "$b" = "pc" ]; then continue; fi
    if [ -e "32/$b" ]; then
      cat > "$b" <<EOF
#if __x86_64
#  include <machine/64/$b>
#else
#  include <machine/32/$b>
#endif
EOF
    else
      cat > "$b" <<EOF
#if __x86_64
#  include <machine/64/$b>
#else
#  error "No such 32-bit include: <machine/32/$b>"
#endif
EOF
    fi
  done
  for i in 32/*; do
    if [ "$b" = "pc" ]; then continue; fi
    b=${i#32/}
    if [ ! -e "$b" ]; then
      cat > "$b" <<EOF
#if __x86_64
#  error "No such 64-bit include: <machine/64/$b>"
#else
#  include <machine/32/$b>
#endif
EOF
    fi
  done


You'll want to build lib32 also:

Code:
  cd /usr/src
  make build32 install32 DESTDIR=/usr/lib32

That's the initial setup. You'll need to compile a multlilib gcc now.

You'll need to pull in gcc 4.8 source code, extract and cd into the directory.

Set up the 32 bit headers for the gcc build:

Code:
        mkdir include32
        mkdir include32/machine
        for i in /usr/src/sys/i386/include/*; do
                ln -svf "$i" "include32/machine/$(basename "$i")"
        done

Next you need to dump and patch the specs file.

Code:
        gcc -dumpspecs > specs
        patch -p1 -i specs.diff

Here is the specs.diff

Code:
--- a/specs	2013-07-11 17:40:39.532971335 +0200
+++ b/specs	2013-07-11 17:40:22.891970908 +0200
@@ -77,22 +77,22 @@ cc1 -E %{traditional|traditional-cpp:-tr
 4.8.0
 
 *multilib:
-. ;
+. !m64 !m32;64:../lib m64 !m32;32:../lib32 !m64 m32;
 
 *multilib_defaults:
-
+m64
 
 *multilib_extra:
 
 
 *multilib_matches:
-
+m64 m64;m32 m32;
 
 *multilib_exclusions:
 
 
 *multilib_options:
-
+m64/m32
 
 *multilib_reuse:

We'll export some stuff to make sure our compiler uses the specs file:

Code:
        export CC="gcc -specs=$srcdir/specs"
        export CXX="g++ -specs=$srcdir/specs"
        export CPP=cpp
        export _CHOST_=amd64-unknown-freebsd9.2
        export _gccver=4.8.0

and configure and make the new gcc.

Code:
        ../gcc-4.8.0/libstdc++-v3/configure \
                --cache-file=./config.cache \
                --prefix=/usr/local \
                --build=${_CHOST_} --host=${_CHOST_} --target=${_CHOST_} \
                --with-bugurl='https://bugs.archbsd.net/' \
                --disable-nls \
                --with-as=/usr/local/bin/as \
                --with-gmp=/usr/local \
                --with-ld=/usr/bin/ld \
                --with-libiconv-prefix=/usr/local \
                --with-pkgversion='ArchBSD Package Collection' \
                --with-system-zlib \
                --enable-cloog-backend=isl \
                --disable-cloog-version-check \
                --enable-multilib \
                --enable-languages=c,c++,fortran,lto,objc \
                --program-transform-name=s,y,y, \
                --with-target-subdir=${_CHOST_} \
                --srcdir=../gcc-4.8.0/libstdc++-v3 \
                --with-build-libsubdir=.

Hopefully all builds well and you can install to /usr/local

Code:
        gmake DESTDIR="temp-install-dir" install

        mv "temp-install-dir/usr/lib32" "${pkgdir}/usr"
        install -dm755 "/usr/local/include/c++/${_gccver}/${_CHOST_}"
        mv "temp-install-dir/usr/local/include/c++/${_gccver}/${_CHOST_}/32" \
           "/usr/local/include/c++/${_gccver}/${_CHOST_}/32"
        rm -r "temp-install-dir"


        cd "$srcdir"
        # specs file
        install -dm755 "/usr/local/lib/gcc/x86_64-unknown-freebsd9.2/${_gccver}"
        install -m644 specs "/usr/local/lib/gcc/x86_64-unknown-freebsd9.2/${_gccver}/specs"

After all is done, you should now be able to compile applications with -m32.

Test results:

Code:
[amzo@Bahamut ~]$ gcc test.c -o test.64
[amzo@Bahamut ~]$ ./test.64 
Long int size is 8 bytes long!
[amzo@Bahamut ~]$ gcc -m32 test.c -o test.32
[amzo@Bahamut ~]$ ./test.32 
Long int size is 4 bytes long!

Code:
#include <stdio.h>
int main(){
        long z; printf("Long int size is %i bytes long!\n", sizeof(z)); return 0;
}

Code:
[amzo@Bahamut ~]$ ldd test.32
test.32:
	libc.so.7 => /usr/lib32/libc.so.7 (0x28069000)
[amzo@Bahamut ~]$ ldd test.64
test.64:
	libc.so.7 => /lib/libc.so.7 (0x80081c000)

Credits go to Blub for getting the initial steps working. Now compiling with -32 compiles and links correctly as seen above. This post was for educational purpose only, and not recommended unless you know what you're doing.
 
Just wanted to clear this up a bit.
You don't need a wrapper for gcc, just the specs file has to be changed, because it seems to not like the -m32 option by default.
clang works just as well with the changed machine/ includes.

We had wrappers as a first approach to do some testing, but with /usr/include/machine changed to include the correct 32/64 bit headers, this is not required anymore.

clang accepted -m32 even without any of our changes, but compiling sources with that causes issues like sizeof(uint64_t) being 4 instead of 8, so any kind of binary interface would break.

Another thing in case it's unclear, you don't replace gcc with the above mentioned one (as seen in the installation script below the configure code) - this part comes from our lib32-devel package which is used to generate /usr/lib32/lib{stdc,supc}++ files, as well as 32-bit bits/ includes for C++/C++11 (/usr/include/c++/4.8.0/x86_64-unknown-freebsd9.1/32/bits) for use with gcc.

Here are some links to the actual scripts in use:

FreeBSD package PKGBUILD:
https://github.com/Amzo/ArchBSD/blob/abs/core/FreeBSD/PKGBUILD
(important part: in _package_freebsd-world(), starting from 'msg "Fixing up 32/64 bit include files" (around line 211))

lib32-devel package PKGBUILD:
https://github.com/Amzo/ArchBSD/blob/abs/multilib/lib32-devel/PKGBUILD
(the steps to produce 32 bit c++ libraries and header files)

as an example: lib32-freetype package PKGBUILD:
https://github.com/Amzo/ArchBSD/blob/abs/multilib/lib32-freetype2/PKGBUILD
(important part: the 'export' lines in build())

the modified wine PKGBUILD as example:
https://github.com/Amzo/ArchBSD/blob/abs/multilib/wine/PKGBUILD
 
Back
Top