at 15.09-beta 11 kB view raw
1Setting up a cross compiler with Nix 2 3"Cross compilation" means compiling a program on one machine for another 4type of machine. A typical use of cross compilation is to compile programs 5for embedded devices. These devices often don't have the computing power 6and memory to compile programs natively. 7 8For a fully working cross compiler the following are needed: 9 10* cross binutils: assembler, archiver, linker, etcetera that understand 11the format of the target system 12 13* cross compiler: a compiler that can generate binary code and object files 14for the target platform 15 16* cross C library: a library to link object files with to create fully 17functional programs 18 19Cross compilers are difficult to set up. A lot of people report that they 20cannot succeed in building a cross toolchain successfully. The answers 21usually consist of "download this pre-built toolchain", which is equally 22unhelpful. 23 24A toolchain is set up in five steps: 25 261. build binutils to that can run on the host platform, but generate code 27for the target platform 28 292. build Linux kernel headers for the target platform 30 313. build a minimal C only version of GCC, that can run on the host platform 32and generate code for the target platform 33 344. build a C library for the target platform. This includes the dynamic 35linker, C library, etc. 36 375. build a full GCC 38 39**** 40NB: 41 42Keep in mind that many programs are not very well suited for cross 43compilation. Either they are not intended to run on other platforms, 44because the code is highly platform specific, or the configuration process 45is not written with cross compilation in mind. 46 47Nix will not solve these problems for you! 48*** 49 50This document describes to set up a cross compiler to generate code for 51arm-linux with uClibc and runs on i686-linux. The "stdenv" used is the 52default from the standard Nix packages collection. 53 54Step 1: build binutils for arm-linux in the stdenv for i686-linux 55 56--- 57{stdenv, fetchurl, noSysDirs}: 58 59stdenv.mkDerivation { 60 name = "binutils-2.16.1-arm"; 61 builder = ./builder.sh; 62 src = fetchurl { 63 url = http://ftp.nluug.nl/gnu/binutils/binutils-2.16.1.tar.bz2; 64 md5 = "6a9d529efb285071dad10e1f3d2b2967"; 65 }; 66 inherit noSysDirs; 67 configureFlags = "--target=arm-linux"; 68} 69--- 70 71This will compile binutils that will run on i686-linux, but knows the 72format used by arm-linux. 73 74Step 2: build kernel headers for the target architecture 75 76 default.nix for kernel-headers-arm: 77 78--- 79{stdenv, fetchurl}: 80 81assert stdenv.system == "i686-linux"; 82 83stdenv.mkDerivation { 84 name = "linux-headers-2.6.13.4-arm"; 85 builder = ./builder.sh; 86 src = fetchurl { 87 url = http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.13.4.tar.bz2; 88 md5 = "94768d7eef90a9d8174639b2a7d3f58d"; 89 }; 90} 91--- 92 93 builder.sh for kernel-headers-arm: 94 95--- 96source $stdenv/setup 97 98 99buildPhase() { 100 make include/linux/version.h 101} 102 103buildPhase=buildPhase 104 105 106installPhase() { 107 mkdir $out 108 mkdir $out/include 109 #cd $out/include 110 #ln -s asm-arm asm 111 make include/asm ARCH=arm 112 cp -prvd include/linux include/asm include/asm-arm include/asm-generic $out/include 113 echo -n > $out/include/linux/autoconf.h 114} 115 116installPhase=installPhase 117 118 119genericBuild 120--- 121 122Step 3: build a minimal GCC 123 124Extra/different parameters include the target platform and the kernel 125headers argument (this needs a major cleanup, as well as the name, it 126needs to be different!). Profiled compilers are disabled. The tarball 127used here is just gcc-core. For some reason it doesn't install nicely 128if the whole tarball is used (or is this some braino on my side? -- AH). 129 130Only C is used, because for other languages (such as C++) extra libraries 131need to be compiled, for which libraries compiled for the target system 132are needed. 133 134There is a bit of evilness going on. The cross compiled utilities need 135to be either copied to or be linked from the output tree of the compiler. 136(Is this really true? Back this up with arguments! -- AH) 137 138Symbolic links are not something we want inside the Nix store. 139 140--- 141{ stdenv, fetchurl, noSysDirs 142, langC ? true, langCC ? true, langF77 ? false 143, profiledCompiler ? false 144, binutilsArm 145, kernelHeadersArm 146}: 147 148assert langC; 149 150stdenv.mkDerivation { 151 name = "gcc-4.0.2-arm"; 152 builder = ./builder.sh; 153 src = fetchurl { 154 url = ftp://ftp.nluug.nl/pub/gnu/gcc/gcc-4.0.2/gcc-core-4.0.2.tar.bz2; 155 md5 = "f7781398ada62ba255486673e6274b26"; 156 #url = ftp://ftp.nluug.nl/pub/gnu/gcc/gcc-4.0.2/gcc-4.0.2.tar.bz2; 157 #md5 = "a659b8388cac9db2b13e056e574ceeb0"; 158 }; 159 # !!! apply only if noSysDirs is set 160 patches = [./no-sys-dirs.patch ./gcc-inhibit.patch]; 161 inherit noSysDirs langC langCC langF77 profiledCompiler; 162 buildInputs = [binutilsArm]; 163 inherit kernelHeadersArm binutilsArm; 164 platform = "arm-linux"; 165} 166--- 167 168The builder.sh for a cross-compiler. Note that the binutils are prefixed 169with the architecture name, so arm-linux-ld instead of ld, etc. This is 170necessary because when we cross-compile a lot of programs look for these 171tools with these specific names. The standard gcc-wrapper does not take this 172into account yet. 173 174--- 175source $stdenv/setup 176 177 178export NIX_FIXINC_DUMMY=$NIX_BUILD_TOP/dummy 179mkdir $NIX_FIXINC_DUMMY 180 181 182if test "$noSysDirs" = "1"; then 183 184 if test "$noSysDirs" = "1"; then 185 # Figure out what extra flags to pass to the gcc compilers 186 # being generated to make sure that they use our glibc. 187 if test -e $NIX_CC/nix-support/orig-glibc; then 188 glibc=$(cat $NIX_CC/nix-support/orig-glibc) 189 # Ugh. Copied from gcc-wrapper/builder.sh. We can't just 190 # source in $NIX_CC/nix-support/add-flags, since that 191 # would cause *this* GCC to be linked against the 192 # *previous* GCC. Need some more modularity there. 193 extraCFlags="-B$glibc/lib -isystem $glibc/include" 194 extraLDFlags="-B$glibc/lib -L$glibc/lib -Wl,-s \ 195 -Wl,-dynamic-linker,$glibc/lib/ld-linux.so.2" 196 197 # Oh, what a hack. I should be shot for this. 198 # In stage 1, we should link against the previous GCC, but 199 # not afterwards. Otherwise we retain a dependency. 200 # However, ld-wrapper, which adds the linker flags for the 201 # previous GCC, is also used in stage 2/3. We can prevent 202 # it from adding them by NIX_GLIBC_FLAGS_SET, but then 203 # gcc-wrapper will also not add them, thereby causing 204 # stage 1 to fail. So we use a trick to only set the 205 # flags in gcc-wrapper. 206 hook=$(pwd)/ld-wrapper-hook 207 echo "NIX_GLIBC_FLAGS_SET=1" > $hook 208 export NIX_LD_WRAPPER_START_HOOK=$hook 209 fi 210 211 export NIX_EXTRA_CFLAGS=$extraCFlags 212 export NIX_EXTRA_LDFLAGS=$extraLDFlags 213 export CFLAGS=$extraCFlags 214 export CXXFLAGS=$extraCFlags 215 export LDFLAGS=$extraLDFlags 216 fi 217 218else 219 patches="" 220fi 221 222 223preConfigure=preConfigure 224preConfigure() { 225 226 # Determine the frontends to build. 227 langs="c" 228 if test -n "$langCC"; then 229 langs="$langs,c++" 230 fi 231 if test -n "$langF77"; then 232 langs="$langs,f77" 233 fi 234 235 # Cross compiler evilness 236 mkdir -p $out 237 mkdir -p $out/arm-linux 238 mkdir -p $out/arm-linux/bin 239 ln -s $binutilsArm/arm-linux/bin/as $out/arm-linux/bin/as 240 ln -s $binutilsArm/arm-linux/bin/ld $out/arm-linux/bin/ld 241 ln -s $binutilsArm/arm-linux/bin/ar $out/arm-linux/bin/ar 242 ln -s $binutilsArm/arm-linux/bin/ranlib $out/arm-linux/bin/ranlib 243 244 # Perform the build in a different directory. 245 mkdir ../build 246 cd ../build 247 248 configureScript=../$sourceRoot/configure 249 configureFlags="--enable-languages=$langs --target=$platform --disable-threads --disable-libmudflap --disable-shared --with-headers=$kernelHeadersArm/include --disable-multilib" 250} 251 252 253postInstall=postInstall 254postInstall() { 255 # Remove precompiled headers for now. They are very big and 256 # probably not very useful yet. 257 find $out/include -name "*.gch" -exec rm -rf {} \; -prune 258 259 # Remove `fixincl' to prevent a retained dependency on the 260 # previous gcc. 261 rm -rf $out/libexec/gcc/*/*/install-tools 262} 263 264 265#if test -z "$profiledCompiler"; then 266 #makeFlags="bootstrap" 267#else 268 #makeFlags="profiledbootstrap" 269#fi 270 271genericBuild 272--- 273 274Step 4: build a C library for the target platform. 275 276The previous steps are enough to compile a C library. In our case we take 277uClibc. It's intended to be a small sized replacement for glibc. It is widely 278used in embedded environments. 279 280... 281 282Step 5: Build a compiler to link with the newly built C library. 283 284... 285 286If we restrict the compiler to just C programs it is relatively easy, 287since we only need to wrap the GCC we built in the previous step with all 288the right tools and the right C library. Successfully compiled programs with 289this compiler and verified to be working on a HP Jornada 820 running Linux 290are "patch", "make" and "wget". 291 292If we want to build C++ programs it gets a lot more difficult. GCC has a 293three step compilation process. In the first step a simple compiler, called 294xgcc, that can compile only C programs is built. With that compiler it 295compiles itself two more times: one time to build a full compiler, and another 296time to build a full compiler once again with the freshly built compiler from 297step 2. In the second and third step support for C++ is compiled, if this 298is configured. 299 300One of the libraries that has to be built for C++ support step is libstdc++. 301This library uses xgcc, even when cross compiling, since libstdc++ has to be 302compiled for arm-linux. 303 304One of the compiler flags that GCC uses for this compiler is called X_CFLAGS. 305This is used by the Nix build process to set the dynamic linker, glibc 306in the case of i686-linux using the default Nix packages collection. 307 308Obiously, since we need to compile libstc++ for arm-linux with uClibc linking 309will not be done correctly: you can't link object files built for arm-linux 310with a glibc built for i686-linux. 311 312Setting X_CFLAGS to use the uClibc libraries and dynamic linker will fail 313too. Earlier on in the build process these flags are used to compile important 314files like libgcc.a by the host system gcc, which does need to be linked 315to glibc. To make this work correctly you will need to carefully juggle 316with compilation flags. This is still work in progress for Nix. 317 318 319--- 320 321After successfully completing the whole toolchain you can start building 322packages with the newly built tools. To make everything build correctly 323you will need a stdenv for your target platform. Setting up this platform 324will take some effort. Right now there is a very experimental setup for 325arm-linux, which needs to be cleaned up before it is production ready. 326 327Please note that many packages are not well suited for cross-compilation. 328Even though the package itself might be very well portable often the 329buildscripts are not. One thing that we have seen that causes frequent 330build failures is the use of the LD variable. This is often set to 'ld' 331and not $(CROSS)-ld.