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.