Skip to main content

Murphy's law when creating cross compiler for windows.

Out of curiosity I got a bit intrigued with a concept known 'cross compilers'. Essentially this is a compiler that is compiled under to run under one platform (host), that outputs executable files and linker archives that are consumable by another (target). For those familiar with console programming, this brings us a step closer to devkit es-queue workflow; where we just use the device to run content as opposed to producing content on it.

One of the huge advantages this offers is that the development platform can remain fixed. For me personally that means I can keep using Visual Studio; and use all the development tools which makes me productive. It also means that my development platform needs only to be functional for a single target which I'd argue is more friendlier on the developer as opposed to running virtual machines; familiarizing with all of it's tools. So with the recent announcement of Steam-OS; I thought this was a good time, to play around with a small scale setup.


For those not willing to spent the time to go over the installation procedure, here is a prebuilt image for mint 14 [7] with linux 3.5.1, gcc 4.8.1, eglibc 2.1.5, binutils 2.24.1 [image]



First make sure you have an unix style environment ready. For windows this can be done in two flavors, there is cygwin and mingw [1]. Within this I focus on mingw because in general it's faster than cygwin. In mingw you want the following packages: i. mingw-developer-toolkit, ii. mingw32-base, iii. mingw32-gcc-g++, iv. mysys-base [2]. Now if you're new to mingw/mysys; by default the installer does not setup the execution environment correctly, so you need to setup the environment settings from the shell.

SET PATH=C:\MinGW\bin;%PATH%
C:\MinGW\msys\1.0\msys.bat 

Next we need to gather the dependencies. The dependencies that we need are binutils; gcc; linux kernel; (e)glibc. To figure out which versions of I needed I queried the libc and kernel version from  an existing linux installation, i deducted that I required eglibc 2.15 and linux kernel 3.5.1.  Likelihood is you wont be able to find the exact kernel release, you just have to keep in mind that the kernels are backwards compatible. So 3.5.1 is be compatible with 3.5.0.17, which is a minor release that wasn't available for download. For gcc i used the latest version which were 4.8.1 and binutils 2.24.1

# Queries the linux kernel version (example output 3.5.0.17-generic) 
uname -r
# Queries the libc version (example output (Ubuntu EGLIBC 2.15-0ubuntu20) 2.15)
ldd --version

Next is to create a directory somewhere. I placed mine on the desktop, named mingw-elf. And add the sub-directories source, install, test. Inside the source directories we unpack the required source code. Roughly the directory tree should look similar to the structure below. Install is the directory
we are going to use, and it will reflect the root of the linux file system.

------ Directory
    |  install
            |   bin
            |   etc
            |   include
            |   lib
    |  test
    |  source
            |   binutils-2.24.51
            |   gcc-4.8.1
            |   linux-headers
            |   eglibc-2.15

I prepared a number of shell scripts with predefined options, going over all of the options by itself is worth a few posts, but there's plenty of resources to examine the options on your own. https://drive.google.com/file/d/0B8LpEdZ0qbtod2M2dkQwOTN1X1E/edit?usp=sharing Before the shell scripts will run, we need to setup a few environment variables, to configure versions of the software we want. We do this with the following variable. The installation directory needs to be absolute, and written in posix format.

export install_directory=/c/Users/Phr34k/Desktop/mingw-elf #no trailing slash
export target=i686-pc-linux-gnu
export version_binutils=binutils-2.24.51
export version_gcc=gcc-4.8.1
export version_libc=libc
export version_linux=2-3.5

build-binutils.sh
build-gcc.sh
build-linux.sh
build-clib.sh
build-gcc-2.sh
build-clib-install.sh
build-gcc-3.sh 

Compiling bin-utils pretty much when out of the box. The target flag lets you indicate platform initially i tried i686-mint-elf, which was already a mistake. The target needs to be conformed according gnu style which is i686-pc-linux-gnu,  you wont realize this until you're trying to configure libc which is less forgiving and at that point you can reconfigure and redo all the steps again.

build-binutils.sh

Compiling gcc is actually split into several phases. The first phase is to prepare gcc without a c or c++ runtime. At this point the compiler that can be used to built the c and c++ run-time in their target format. Before we can build though we need to append the following code to cppdefault.c which works around mysys silent redictions of /usr/ [1]. This proofed to be ineffective so I conjured a solution to just add an additional entry to just append the system root and header folders together, setting the sys-root append flag back to 0 (which appends mysys system root).
 
#if defined(__MINGW32__) && defined(TARGET_SYSTEM_ROOT)
#define NATIVE_SYSTEM_HEADER_DIR "/usr/include"
#endif

#ifdef NATIVE_SYSTEM_HEADER_DIR
    /* /usr/include comes dead last.  */
    { NATIVE_SYSTEM_HEADER_DIR, NATIVE_SYSTEM_HEADER_COMPONENT, 0, 0, 1, 2 },
    { NATIVE_SYSTEM_HEADER_DIR, NATIVE_SYSTEM_HEADER_COMPONENT, 0, 0, 1, 0 },

    #if defined(TARGET_SYSTEM_ROOT)
    { TARGET_SYSTEM_ROOT NATIVE_SYSTEM_HEADER_DIR, "HACK", 0, 0, 0, 0 },
    #endif
#endif

build-gcc.sh


Next I prepared the linux headers; Ideally we can just prepare the headers with  make install-headers and a flag to indicate our target, however mingw32 crashes while performing several operations during the procedure. So I leveraged the target-os, and ran install-headers where it worked like expected and transferred the results back.

 
# Important note: the shell script expects the headers to be present in source/$version_linux-headers. 
# Notice the -header suffix, here you can put the headers that you've obtained by running the make #instal-headers command.
build-linux.sh 

Compiling eglibc is sadly also a multiphased setup. First we run configure which by default also insists on performing self checking, relying on compiler features and headers which are not available yet.

 build-clib.sh 

Next we have to to build libgcc saddly once we start compiling, we get an error message about a conflict in __caddr_t [3]. Internally gcc defines this symbol, however some of the headers in eglibc also define this, so the fix is to go to the source of eglibc, and look for the definition of caddr_t and guard them. After you've done this libgcc can be built.

#ifdef    __USE_BSD
# ifndef __daddr_t_defined
typedef __daddr_t daddr_t;
typedef __caddr_t caddr_t;
#  define __daddr_t_defined
# endif
#endif 

build-gcc-2.sh


When building eglibc it complains about a make-file not having valid targets. This is because the targets are programatically created from a .map file that was constructed earlier in the build. The problem is that the map constructed contains paths in a windows formats. So we need the regular expression to use windows paths   /c/mingw/ it becomes c:/mingw/    forward and backwards slashed are not converted. The code below are the adjustments done to elf/Makefile

-    sed -n 's@^$(common-objpfx)\([^(]*\)(\([^)]*\.os\)) *.*$$@\1 \2@p' \
+    sed -n 's@^$(shell echo "$(common-objpfx)" | sed 's/^\///' | sed 's/^./\0:/')\([^(]*\)(\([^)]*\.os\)) *.*$$@\1 \2@p' \

After correcting this eglib is getting a lot further but again the compile is halted to an stop on missing references of internal system calls  such as __lseek. After doing some digging around the mailinglists the root cause of this is the lack of a case-sensitive operating systems [4]. You can correct this by renaming all ".oS"  to ".oZ" after that we can yet again resume.

The next error encountered was about a missing a libgcc_eh.a [5]. This is caused because we built gcc with multilib disabled. Ideally we should recompile gcc with multilib enabled at this point but you can also just copy libgcc.a to libgcc_eh.a.

build-clib-install.sh

Again the software wont compile without a fight and I stumbled over another error.  So roughly what the below code does is  delete the old .so2 file, allowing us to create a backup of the current symbol file. The problem here is that ld-linux is a relative path, whereas likely absolute paths are intended. So in Make-rules file we need to adjust $(LN_S) $(<F) $@ to $(LN_S) $< $@ after this adjustment the copy will work.
 
mv -f /lib/libc-2.15.so.new /lib/libc-2.15.so
rm -f /lib/ld-linux.so.2
cp -p ld-2.15.so /lib/ld-linux.so.2
cp: cannot stat `ld-2.15.so': No such file or directory
make[2]: *** [/lib/ld-linux.so.2] Error 1 

Now that we finally have a working installation of eglibc. And we can resume the remaining task of finalizing the gcc build, in particulair this means adding c++ features.

build-gcc-3.sh

[1] http://mingw.org/wiki/HostedCrossCompilerHOWTO
[2] http://wiki.osdev.org/GCC_Cross-Compiler
[3] https://sourceware.org/ml/crossgcc/2011-03/msg00161.html
[4] https://sourceware.org/ml/crossgcc/2010-10/msg00015.html
[5] http://www.linuxfromscratch.org/lfs/view/stable/chapter05/gcc-pass1.html
[7] http://www.traffictool.net/vmware/mint14t.html

Comments

Popular posts from this blog

The 8 Best U.S. Cities to Visit for a Quick Vacation

The best thing about visiting a new city is experiencing the thrill of adventure. From delicious food to rich history, there’s always something new to do. Whether you live close to these cities or you’re planning on making a trip to the USA, here's 8 of the best U.S. cities to visit on your next vacation (in no particular order): 1. Portland, Oregon As Oregon’s largest city, Portland has steadily been on the rise as a hotspot for food and beer connoisseurs. It’s nestled between the Columbia and Willamette Rivers with a stunning view of snowy Mount Hood which only adds to the thriving artistic culture. Portland is also home to beautiful parks, bridges and bike paths, making this city a top choice for outdoor adventurists. If you’re looking for more breathtaking escapades, Portland is nearby to a few national forests including Mount Hood National Forest and Gifford Pinchot National Forest. 2. Nashville, Tennessee Nashville rightfully owns

Roadtrip germany

On our way to a road-trip headed to Germany, Frankfurt amz Main. We had the chance to stop by for one of my favorite foods as a child: Curry-wurst with fries. This brings back so much memories where we'd often go spent the Christmas weeks in Germany, going over the Christmas markets/fairs and enjoying the hot curry-wurst from the stands with snaps or gluhwein. Of course during a road-trip one cannot stop to have a little lunch too, yummie pie and sandwhich

A visual approach to programming

It's been a while since I had opportunity to write anything, with the added misfortune of a hardware deficit but seemingly still had some backups to recover old older entries. Over the years I've taken a interest in language theory and in particular visual programming. Inspired by Unreal Kismet, CryEngine Flow and BitSquid Flow; I too set out myself of creating a similar environment. Primarily I just wanted a visual language as I believed they hold a certain productivity value. My initial designs were of a very object-orientated nature. However this approach just never felt right to me. It means you are going to increase post-deserialization time due to v-table fix-ups but it is also takes dexterity to maintain the code hierarchy required. So what I really wanted to do was design a system a) that reduces post-deserialization times to a bare minimum b) was not inheritance heavy c) small enough to be embeddable. On of the interesting methods that I considered was generating m