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.