2008/08/06

WMU-6500FS - gcc 4.1.2



Overview

During the deluge build I tried to workaround the issue with uncaught exceptions (due to the GCC mis-configuration), but after the long effort (and after a discovery of a linker problem) I decided to rebuild GCC compiler.

Rationalization

This discussion contains a simple test for exception catching. So we can get the two simple programs and test whether the exceptions are handled properly:
dev# cd /usr/local/src/
dev# mkdir test_exceptions
dev# cd test_exceptions/
dev# wget http://codepoet.org/throw1.cpp
dev# wget http://codepoet.org/throw2.cpp
dev# g++ -Wall -O2 throw1.cpp -o throw1
dev# ./throw1
Aborted
With the current compiler (gcc version 3.3.6 configured without --enable-sjlj-exceptions flag) the exceptions are not caught at all. This message named "libgcc_s.so compatibility between 3.3 and 3.4 (sjlj/dwarf2 exceptions)" describes a problem with the change of default exception model and it is likely that this issue applies exactly in our case.

What version?

When we are re-building the compiler, why not to upgrade to a newer version? Based on uClibc FAQ I choose the version 4.1.2 (along with binutils version 2.18):
The last two GNU binutils releases are 2.17 and 2.18. While 2.16 should work, workarounds for bugs in it are not considered. Since the GCC-3.4 and GCC-4.1 series are the recent stable release series (since the 4.0 and 4.2 series tend to be a bit broken for everyone), they are the latest versions we will address. If you encounter a compile error using an older version (say GCC 3.2), then you're out of luck. You'll need to workaround it yourself.

binutils-2.18

Building this package was the easy part:
Build results: [binary] [files list]

Build sequence:
dev# cd /usr/local/src
dev# wget http://ftp.gnu.org/gnu/binutils/binutils-2.18.tar.bz2
dev# tar xjvf binutils-2.18.tar.bz2
dev# cd binutils-2.18
dev# ./configure --host=i386-linux-uclibc --target=i386-linux-uclibc --build=i386-pc-linux-uclibc --prefix=/mnt/C/sys
dev# ./make
dev# make install

gcc 4.1.2

Info sources: [Prerequisites] [How to configure] [How to build]
Build results: [binary] [files list]

Build sequence:
We can either download whole source package:
dev# cd /usr/local/src
dev# wget ftp://ftp.fu-berlin.de/unix/languages/gcc/releases/gcc-4.1.2/gcc-4.1.2.tar.bz2
dev# tar xjvf gcc-4.1.2.tar.bz2
dev# cd gcc-4.1.2/
... or we can pick out just packages we are interested in - for example:
dev# cd /usr/local/src
dev# wget ftp://ftp.fu-berlin.de/unix/languages/gcc/releases/gcc-4.1.2/gcc-core-4.1.2.tar.bz2
dev# tar xjvf gcc-core-4.1.2.tar.bz2
dev# wget ftp://ftp.fu-berlin.de/unix/languages/gcc/releases/gcc-4.1.2/gcc-g++-4.1.2.tar.bz2
dev# tar xjvf gcc-g++-4.1.2.tar.bz2
dev# wget ftp://ftp.fu-berlin.de/unix/languages/gcc/releases/gcc-4.1.2/gcc-objc-4.1.2.tar.bz2
dev# tar xjvf gcc-objc-4.1.2.tar.bz2
dev# wget ftp://ftp.fu-berlin.de/unix/languages/gcc/releases/gcc-4.1.2/gcc-testsuite-4.1.2.tar.bz2
dev# tar xjvf gcc-testsuite-4.1.2.tar.bz2
dev# cd gcc-4.1.2/
Now we can try to configure it:
dev# ./configure --prefix=/mnt/C/sys --host=i386-linux-uclibc --target=i386-linux-uclibc --build=i386-pc-linux-uclibc 
--enable-languages=c,c++ --enable-shared --disable-__cxa_atexit --enable-target-optspace --with-gnu-ld --disable-nls 
--enable-multilib --with-x --x-includes=/mnt/C/sys/X11/include/ --x-libraries=/mnt/C/sys/X11/lib/ 
--with-gxx-include-dir=/usr/include/c++ --enable-bootstrap 
...
checking for MPFR... no
*** This configuration is not supported in the following subdirectories:
     target-libada gnattools target-libgfortran target-libffi target-boehm-gc 
     target-zlib target-libjava zlib fastjar target-libobjc
    (Any other directories should still work fine.)
The MPFR library is a C library for multiple-precision floating-point computations with correct rounding.
For now I decided to proceed without it:
dev# make
...
/usr/local/src/gcc-4.1.2/host-i386-linux-uclibc/prev-gcc/xgcc 
-B/usr/local/src/gcc-4.1.2/host-i386-linux-uclibc/prev-gcc/ -B/mnt/C/sys/i386-linux-uclibc/bin/   
-O2 -g -fomit-frame-pointer -DIN_GCC   
-W -Wall -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -pedantic 
-Wno-long-long -Wno-variadic-macros -Wold-style-definition -Wmissing-format-attribute 
-Werror    -DHAVE_CONFIG_H -DGENERATOR_FILE  -o build/genmodes \
 build/genmodes.o build/errors.o ../../build-i386-pc-linux-uclibc/libiberty/libiberty.a
build/genmodes -h > tmp-modes.h
/bin/sh: build/genmodes: No such file or directory
make[3]: *** [s-modes] Error 127
This problem emerged after the long portion of the build process, in stage 2. When we try to launch genmodes from the stage 1 (built with the "old" gcc) it runs with no problem:
dev# ./host-i386-linux-uclibc/stage1-gcc/build/genmodes
...
DOUBLE ? &ieee_extended_intel_96_round_53_format : &ieee_extended_intel_96_format);
}
But the one from stage 2 (built with a new gcc) does not run at all:
dev# ./host-i386-linux-uclibc/stage2-gcc/build/genmodes
-sh: ./host-i386-linux-uclibc/stage2-gcc/build/genmodes: No such file or directory
In this message somebody is dealing with similar problem - In such cases it seems there is wrong path to the loader hardcoded in the generated elf file.
So let's inspect the executables with readelf tool (we are mainly interested in used dynamic loader aka interpreter):
dev# readelf -a ./host-i386-linux-uclibc/stage1-gcc/build/genmodes | grep interpreter
      [Requesting program interpreter: /lib/ld-uClibc.so.0]
dev# readelf -a ./host-i386-linux-uclibc/stage2-gcc/build/genmodes | grep interpreter
      [Requesting program interpreter: /lib/ld-linux.so.2]
So now we see the root of the problem. Now how to solve it... I found that there is the loader path hardcoded in gcc/config/linux.h (it does not depend on --target configuration) as follows:
#define DYNAMIC_LINKER "/lib/ld-linux.so.2"
... so it seems we need a patch to correct this (and and possibly other settings) specific to the build in uClibc environment.
After some Web browsing I have found such set of patches here (the patches are actually for gcc-4.1.0, to overcome it we use -p1 patch option instead of -p1):
dev# cd /usr/local/src/gcc-4.1.2
dev# wget -O 100-uclibc-conf.patch "http://dev.wireless-fr.org/browser/firmware/trunk/kamikaze/toolchain/gcc/patches/4.1.2/100-uclibc-conf.patch?rev=353&format=raw"
dev# patch -p1 < 100-uclibc-conf.patch
... now try to continue and after long time we end up with another kind of problem, missing locale support in uClibc:
dev# make
...
-fdiagnostics-show-location=once -ffunction-sections -fdata-sections -c ../../.././libstdc++-v3/src/ctype.cc  -fPIC -DPIC -o .libs/ctype.o
/usr/local/src/gcc-4.1.2/i386-linux-uclibc/libstdc++-v3/include/i386-linux-uclibc/bits/ctype_noninline.h: In static member function 'static const short unsigned int* std::ctype<char>::classic_table()':
/usr/local/src/gcc-4.1.2/i386-linux-uclibc/libstdc++-v3/include/i386-linux-uclibc/bits/ctype_noninline.h:51: error: '__ctype_b' was not declared in this scope
/usr/local/src/gcc-4.1.2/i386-linux-uclibc/libstdc++-v3/include/i386-linux-uclibc/bits/ctype_noninline.h: In constructor 'std::ctype<char>::ctype(int*, const short unsigned int*, bool, size_t)':
/usr/local/src/gcc-4.1.2/i386-linux-uclibc/libstdc++-v3/include/i386-linux-uclibc/bits/ctype_noninline.h:85: error: '__ctype_toupper' was not declared in this scope
/usr/local/src/gcc-4.1.2/i386-linux-uclibc/libstdc++-v3/include/i386-linux-uclibc/bits/ctype_noninline.h:86: error: '__ctype_tolower' was not declared in this scope
/usr/local/src/gcc-4.1.2/i386-linux-uclibc/libstdc++-v3/include/i386-linux-uclibc/bits/ctype_noninline.h:87: error: '__ctype_b' was not declared in this scope
/usr/local/src/gcc-4.1.2/i386-linux-uclibc/libstdc++-v3/include/i386-linux-uclibc/bits/ctype_noninline.h: In constructor 'std::ctype<char>::ctype(const short unsigned int*, bool, size_t)':
/usr/local/src/gcc-4.1.2/i386-linux-uclibc/libstdc++-v3/include/i386-linux-uclibc/bits/ctype_noninline.h:120: error: '__ctype_toupper' was not declared in this scope
/usr/local/src/gcc-4.1.2/i386-linux-uclibc/libstdc++-v3/include/i386-linux-uclibc/bits/ctype_noninline.h:121: error: '__ctype_tolower' was not declared in this scope
/usr/local/src/gcc-4.1.2/i386-linux-uclibc/libstdc++-v3/include/i386-linux-uclibc/bits/ctype_noninline.h:122: error: '__ctype_b' was not declared in this scope
make[4]: *** [ctype.lo] Error 1
...
For this there is another patch we can apply:
dev# cd /usr/local/src/gcc-4.1.2
dev# wget -O 200-uclibc-locale.patch "http://dev.wireless-fr.org/browser/firmware/trunk/kamikaze/toolchain/gcc/patches/4.1.2/200-uclibc-locale.patch?rev=353&format=raw"
dev# patch -p1 < 200-uclibc-locale.patch 
After that I was able to finish build and installation successfully:
dev# make
dev# make install
dev# gcc --version
gcc (GCC) 4.1.2
Copyright (C) 2006 Free Software Foundation, Inc.
...
Ok, let's test the new compiler with our simple exception test:
dev# cd /usr/local/src/test_exceptions/
dev# g++ -Wall -O2 throw1.cpp -o throw1
/mnt/C/sys/lib/gcc/i386-linux-uclibc/4.1.2/../../../../i386-linux-uclibc/lib/libstdc++.so: 
  undefined reference to `___tls_get_addr'
collect2: ld returned 1 exit status
According to ldd it seems there are no missing library dependencies:
dev# ldd /mnt/C/sys/lib/gcc/i386-linux-uclibc/4.1.2/../../../../i386-linux-uclibc/lib/libstdc++.so
  libm.so.0 => /lib/libm.so.0 (0x00000000)
  libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00000000)
  libc.so.0 => /lib/libc.so.0 (0x00000000)
  /lib/ld-uClibc.so.0 => /lib/ld-uClibc.so.0 (0x00000000)
Following message contains some info about this issue. According to this message there should be no tls problem after applying all the patches, so maybe the correct solution is to download all remaining patches from this site. There is one patch dealing with libstdc++, but I am not sure whether it solves the TLS problem.
At the time I decided to re-configure GCC regarding to advice in this message:
dev# cd /usr/local/src/gcc-4.1.2
dev# make distclean
dev# ./configure --prefix=/mnt/C/sys --host=i386-linux-uclibc --target=i386-linux-uclibc 
--build=i386-pc-linux-uclibc --enable-languages=c,c++ --enable-shared --disable-__cxa_atexit 
--enable-target-optspace --with-gnu-ld --disable-nls --enable-multilib --with-x 
--x-includes=/mnt/C/sys/X11/include/ --x-libraries=/mnt/C/sys/X11/lib/ 
--with-gxx-include-dir=/usr/include/c++ --enable-bootstrap --disable-tls
dev# make
dev# make install
Now let's repeat the test:
dev# cd /usr/local/src/test_exceptions/
dev# g++ -Wall -O2 throw1.cpp -o throw1
dev# ./throw1
Aborted
It seem that exceptions are still not being caught, let's debug it just to make sure:
dev# gdb throw1
GNU gdb 6.3
...
(gdb) run
Starting program: /usr/local/src/test_exceptions/throw1

Program received signal SIGABRT, Aborted.
0xb7e63164 in kill () from /lib/libc.so.0

(gdb) where
#0  0xb7e63164 in kill () from /lib/libc.so.0
#1  0xb7e5345a in raise () from /lib/libc.so.0
#2  0xb7e5cc67 in abort () from /lib/libc.so.0
#3  0xb7eb82f8 in uw_init_context_1 (context=0xbf83b150, outer_cfa=0xbf83b1d0, outer_ra=0xb7f5fb08)
    at ../.././gcc/unwind-dw2.c:1255
#4  0xb7eb86d4 in _Unwind_RaiseException (exc=0x804ab68) at unwind.inc:92
#5  0xb7f5fb08 in __cxa_throw (obj=0x804ab88, tinfo=0x80499e0, dest=0)
    at ../../.././libstdc++-v3/libsupc++/eh_throw.cc:72
#6  0x08048737 in main ()

(gdb) list
67        header->unwindHeader.exception_cleanup = __gxx_exception_cleanup;
68
69      #ifdef _GLIBCXX_SJLJ_EXCEPTIONS
70        _Unwind_SjLj_RaiseException (&header->unwindHeader);
71      #else
72        _Unwind_RaiseException (&header->unwindHeader);
73      #endif
74
75        // Some sort of unwinding error.  Note that terminate is a handler.
76        __cxa_begin_catch (&header->unwindHeader);
So it seems there are still two versions of exception handling and the one without SjLj does not work even with the new gcc version. I do not know exactly what is going on here, initially I thoought that the exception problem is specific to the 3.3 - 3.4 gcc versions, but regarding to this message it is possible that in some scenarios the --enable-sjlj-exceptions configuration is necessary for 4.x versions as well:
I have rebuilt gcc-3.4.4 w/ sjlj-exceptions, both the uclibc++ test and and throw2 perform ok against libstdc++ and uclibc++, so up to 3.4.4 (but probably 4.0.0 too) need --enable-sjlj-exceptions
So let's try to re-configure the build once more:
dev# cd /usr/local/src/gcc-4.1.2
dev# make distclean
dev# ./configure --prefix=/mnt/C/sys --host=i386-linux-uclibc --target=i386-linux-uclibc 
--build=i386-pc-linux-uclibc --enable-languages=c,c++ --enable-shared --disable-__cxa_atexit 
--enable-target-optspace --with-gnu-ld --disable-nls --enable-multilib --with-x 
--x-includes=/mnt/C/sys/X11/include/ --x-libraries=/mnt/C/sys/X11/lib/ 
--with-gxx-include-dir=/usr/include/c++ --enable-bootstrap --disable-tls --enable-sjlj-exceptions
dev# make
dev# make install
And now when we repeat the test:
dev# cd /usr/local/src/test_exceptions/
dev# g++ -Wall -O2 throw1.cpp -o throw1
dev# ./throw1
caught an exception
Hurray! It seems now we have some progress.
Let's try the second one:
dev# g++ -Wall -O2 throw2.cpp -o throw2
dev# ./throw2
1  Throwing out_of_range()
    Catching: out_of_range out_of_range meaningful comment
2  Throwing exception()   // Can't specify comment
    Catching: St9exception
3  Throwing underflow_error  // caught by base class (runtime_error)
    Catching: runtime_error underflow_error
4  Throwing runtime_error
    Catching: runtime_error a comment
5  Throwing length_error   // caught be super-super-class (exception)
    Catching: St9exception
6  Throwing int
    Catching: int 26
7  Throwing const char*
    Catching: const char* This is a const char*
8  Throwing string
    Catching: string I'm a string
9  Throwing float
    ERROR: Nobody caught this!
10  Throwing up
The exception #9 is not caught but regarding to this discussion it seems ok.
Now we have the gcc version 4.1.2 properly configured, built and ready for building c++ applications like deluge.

1 comment:

lukaszz said...

The gcc bin package does not contain /usr/include/c++ content, and I think it should