at 25.11-pre 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 hash = "sha256-14pv+YKrL3NyFwbnv9MoWsZHgEZk5+pHhuZtAfkcVsU="; 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.buildPlatform.system == "i686-linux"; 82 83stdenv.mkDerivation { 84 name = "linux-headers-2.6.13.1-arm"; 85 builder = ./builder.sh; 86 src = fetchurl { 87 url = "http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.13.1.tar.bz2"; 88 hash = "sha256-qtICDjfiA1HxWBrHqtB5DCv9s9/HyznKV1C6IxCrHYs="; 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 hash = "sha256-LANmXRS7/fN2zF5JUJVd8OjNA5aCDsGLQKhSpxWA3Qk="; 156 }; 157 # !!! apply only if noSysDirs is set 158 patches = [./no-sys-dirs.patch ./gcc-inhibit.patch]; 159 inherit noSysDirs langC langCC langF77 profiledCompiler; 160 buildInputs = [binutilsArm]; 161 inherit kernelHeadersArm binutilsArm; 162 platform = "arm-linux"; 163} 164--- 165 166The builder.sh for a cross-compiler. Note that the binutils are prefixed 167with the architecture name, so arm-linux-ld instead of ld, etc. This is 168necessary because when we cross-compile a lot of programs look for these 169tools with these specific names. The standard gcc-wrapper does not take this 170into account yet. 171 172--- 173source $stdenv/setup 174 175 176export NIX_FIXINC_DUMMY=$NIX_BUILD_TOP/dummy 177mkdir $NIX_FIXINC_DUMMY 178 179 180if test "$noSysDirs" = "1"; then 181 182 if test "$noSysDirs" = "1"; then 183 # Figure out what extra flags to pass to the gcc compilers 184 # being generated to make sure that they use our glibc. 185 if test -e $NIX_CC/nix-support/orig-glibc; then 186 glibc=$(cat $NIX_CC/nix-support/orig-glibc) 187 # Ugh. Copied from gcc-wrapper/builder.sh. We can't just 188 # source in $NIX_CC/nix-support/add-flags, since that 189 # would cause *this* GCC to be linked against the 190 # *previous* GCC. Need some more modularity there. 191 extraCFlags="-B$glibc/lib -isystem $glibc/include" 192 extraLDFlags="-B$glibc/lib -L$glibc/lib -Wl,-s \ 193 -Wl,-dynamic-linker,$glibc/lib/ld-linux.so.2" 194 195 # Oh, what a hack. I should be shot for this. 196 # In stage 1, we should link against the previous GCC, but 197 # not afterwards. Otherwise we retain a dependency. 198 # However, ld-wrapper, which adds the linker flags for the 199 # previous GCC, is also used in stage 2/3. We can prevent 200 # it from adding them by NIX_GLIBC_FLAGS_SET, but then 201 # gcc-wrapper will also not add them, thereby causing 202 # stage 1 to fail. So we use a trick to only set the 203 # flags in gcc-wrapper. 204 hook=$(pwd)/ld-wrapper-hook 205 echo "NIX_GLIBC_FLAGS_SET=1" > $hook 206 export NIX_LD_WRAPPER_START_HOOK=$hook 207 fi 208 209 export NIX_EXTRA_CFLAGS=$extraCFlags 210 export NIX_EXTRA_LDFLAGS=$extraLDFlags 211 export CFLAGS=$extraCFlags 212 export CXXFLAGS=$extraCFlags 213 export LDFLAGS=$extraLDFlags 214 fi 215 216else 217 patches="" 218fi 219 220 221preConfigure=preConfigure 222preConfigure() { 223 224 # Determine the frontends to build. 225 langs="c" 226 if test -n "$langCC"; then 227 langs="$langs,c++" 228 fi 229 if test -n "$langF77"; then 230 langs="$langs,f77" 231 fi 232 233 # Cross compiler evilness 234 mkdir -p $out 235 mkdir -p $out/arm-linux 236 mkdir -p $out/arm-linux/bin 237 ln -s $binutilsArm/arm-linux/bin/as $out/arm-linux/bin/as 238 ln -s $binutilsArm/arm-linux/bin/ld $out/arm-linux/bin/ld 239 ln -s $binutilsArm/arm-linux/bin/ar $out/arm-linux/bin/ar 240 ln -s $binutilsArm/arm-linux/bin/ranlib $out/arm-linux/bin/ranlib 241 242 # Perform the build in a different directory. 243 mkdir ../build 244 cd ../build 245 246 configureScript=../$sourceRoot/configure 247 configureFlags="--enable-languages=$langs --target=$platform --disable-threads --disable-libmudflap --disable-shared --with-headers=$kernelHeadersArm/include --disable-multilib" 248} 249 250 251postInstall=postInstall 252postInstall() { 253 # Remove precompiled headers for now. They are very big and 254 # probably not very useful yet. 255 find $out/include -name "*.gch" -exec rm -rf {} \; -prune 256 257 # Remove `fixincl' to prevent a retained dependency on the 258 # previous gcc. 259 rm -rf $out/libexec/gcc/*/*/install-tools 260} 261 262 263#if test -z "$profiledCompiler"; then 264 #makeFlags="bootstrap" 265#else 266 #makeFlags="profiledbootstrap" 267#fi 268 269genericBuild 270--- 271 272Step 4: build a C library for the target platform. 273 274The previous steps are enough to compile a C library. In our case we take 275uClibc. It's intended to be a small sized replacement for glibc. It is widely 276used in embedded environments. 277 278... 279 280Step 5: Build a compiler to link with the newly built C library. 281 282... 283 284If we restrict the compiler to just C programs it is relatively easy, 285since we only need to wrap the GCC we built in the previous step with all 286the right tools and the right C library. Successfully compiled programs with 287this compiler and verified to be working on a HP Jornada 820 running Linux 288are "patch", "make" and "wget". 289 290If we want to build C++ programs it gets a lot more difficult. GCC has a 291three step compilation process. In the first step a simple compiler, called 292xgcc, that can compile only C programs is built. With that compiler it 293compiles itself two more times: one time to build a full compiler, and another 294time to build a full compiler once again with the freshly built compiler from 295step 2. In the second and third step support for C++ is compiled, if this 296is configured. 297 298One of the libraries that has to be built for C++ support step is libstdc++. 299This library uses xgcc, even when cross compiling, since libstdc++ has to be 300compiled for arm-linux. 301 302One of the compiler flags that GCC uses for this compiler is called X_CFLAGS. 303This is used by the Nix build process to set the dynamic linker, glibc 304in the case of i686-linux using the default Nix packages collection. 305 306Obviously, since we need to compile libstc++ for arm-linux with uClibc linking 307will not be done correctly: you can't link object files built for arm-linux 308with a glibc built for i686-linux. 309 310Setting X_CFLAGS to use the uClibc libraries and dynamic linker will fail 311too. Earlier on in the build process these flags are used to compile important 312files like libgcc.a by the host system gcc, which does need to be linked 313to glibc. To make this work correctly you will need to carefully juggle 314with compilation flags. This is still work in progress for Nix. 315 316 317--- 318 319After successfully completing the whole toolchain you can start building 320packages with the newly built tools. To make everything build correctly 321you will need a stdenv for your target platform. Setting up this platform 322will take some effort. Right now there is a very experimental setup for 323arm-linux, which needs to be cleaned up before it is production ready. 324 325Please note that many packages are not well suited for cross-compilation. 326Even though the package itself might be very well portable often the 327buildscripts are not. One thing that we have seen that causes frequent 328build failures is the use of the LD variable. This is often set to 'ld' 329and not $(CROSS)-ld.