summaryrefslogtreecommitdiff
path: root/doc/tutorial.txt
blob: 68a7cadbd32c477e671802e8d0031df99c4644e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
Cross Compiling Linux - 2 hour tutorial

This is a practical introduction to cross compiling, during which we'll build
a working cross-compiler, use it to cross-compile a native uClibc-based Linux
development environment, and boot this new environment under QEMU.

Attendees may choose arm, mips, x86, x86_64, sparc, or PPC as the platform
they wish to build for.  The author's Firmware Linux project (which already
does all this) will be used as an example.  Attendees should bring a reasonably
fast laptop with net access and at least 256 megs of ram.

General outline:

1) Terminology: cross compiling, native compiling, host/target, toolchain, etc.
2) Why cross compiling is hard, and why we need to do it anyway.
3) Building a cross compiler toolchain from linux, binutils, gcc, and uClibc.
4) Making a native build environment (adding make, busybox, and bash).
5) Packaging disk images, booting, and running under QEMU.
6) Optimizations and alternatives.
   (distcc, armulator, boards/bootloaders, nfs, tsrpm)
7) Where to from here?  (LFS, gentoo, etc.)


-----------------------------------------------------------------------------
Links:
  http://www.landley.net/writing/docs/cross-compiling.html
  http://www.landley.net/code/firmware/about.html
  http://www.landley.net/code/firmware/design.html
  http://cross-lfs.org/files/BOOK/1.0.0/
  http://www.gentoo.org/proj/en/base/embedded/index.xml
  http://gentoo-wiki.com/Embedded_Gentoo

http://www.quietearth.us/articles/2006/08/16/Building-deb-package-from-source

http://qemu-forum.ipi.fi/qemu-snapshots/
git://git.kernel.dk/data/git/qemu.git
-----------------------------------------------------------------------------

Today's agenda:

  - learn about cross compiling
  - build a working cross-compiler
  - use it to cross-compile a native uClibc-based Linux development environment
  - boot this new environment under QEMU.

Platforms:
  - What platforms does Linux support?
    - To get the full list: cd include; echo asm-* | sed 's/asm-//g'
      - alpha arm arm26 avr32 cris frv generic h8300 i386 ia64 m32r
        m68k m68knommu mips parisc powerpc ppc s390 sh sh64 sparc sparc64 um
        v850 x86_64 xtensa
    - Not quite architectures: generic=shared code, um=User Mode Linux
    - What dominates the big iron space?
      - Top 500 supercomputers list: http://top500.org/stats/28/procfam/
        x86-64 (44%), x86 (24%), and PPC (18%).
      - Note: s390 important but not general purpose
    - What dominates the embedded space?
      - The big four: arm, mips, i386/x86_64, ppc.
      - important but not general purpose:
        - sh (super-hitachi) used in japan, especially in auto industry.
        - coldfire (m68knommu): used in a small number of high volume devices.
        - blackfin: up-and-coming, not merged yet.  Employ interesting people.
    - in decline (used to be more important) but still in use:
        alpha, ia64, sparc, parisc.

  - price, power consumption, performance, features
    - power consumption == heat, high end and low end converge due to this.
    - features could be software or integrated peripherals.

  - close up on arm
    - best power/performance ratio, owns over 80% of cell phone market.
    - entire arm core only 43,000 transistors, 34 instructions.
    - armv3 vs arm 7, architecture vs processor.  We focus on architecture.
      - armv3 introduced 32-bit, but obsolete.  armv4 now low-end.
      - most systems now being manufactured armv5 or up.
      - newer runs older instructions.  armv4->armv5 25% speedup for recompile.
    - arm26 obsolete 26 bit addressing mode, like x86's 16-bit mode.
    - can be LE or BE.  Linux supports both, but only one at a time.
      Chip doesn't care but motherboard might.

  - close up on mips
    - customizable: sold as library, FPGA version, fab your own, used in SOC.
      - there is now a 64 bit version.  Probably for bragging rights.
    - reasonable power consumption, reasonable performance.
    - can be LE or BE.

  - close up on ppc
    - can be LE or BE, long ago software selectable now almost universally BE.
    - Apple/IBM/Motorola.  Apple switched, Motorola spun off freescale.
      - power.org attempt to stir up third party interest, some success.
    - Game consoles give it serious volume, but Apple TV is x86-64.
    - Models:
      - Everything can run 7xx code except 4xx (ibm) and 8xx (motorola).
        - 74xx is "G4", 970 is "G5" and is 64-bit.
          - High power consumption, high performance
    - Cell would have been very interesting if it had shipped in 2005, but
      it's too late to matter outside gaming consoles now.  Too much power
      for embedded space, and big iron assembly programming specialized.

  - close up on x86/x86-64
    - Intel and AMD have stopped making non-embedded 32-bit processors.
      - Once existing inventory sold, it's 64-bit only from here on out.
    - A number of smaller players like via.
    - Best price/performance, often best general purpose absolute performance
    - Historically terrible power/performance, hence the fan.
      - Recently paying attention to that, but a ways to go.  On x86 a
        fanless heat sink is a victory, whereas most arm runs cool to the touch.

Compiling software for different platforms:
  - Native compiling on different platforms:
    - endianness, word size, alignment, sign of char, optimizations
    - nommu is its own can of worms: stack, malloc, vfork, mmap
    - x86 is the common case, but this changes to x86-64 soon.
      - Neither intel nor amd making x86 outside of embedded space anymore.
        Once current inventory sold, it's all 64-bit from here.
  - cross compiling
    - host and target are two contexts, most programs used to one.
    - For most programs there is only one context: the target.  Worrying about
      host is the compiler's job, not yours.
      - The compiler tells program what target it's building for with #defines
        (__i386__, __arm__, __mips__) etc.
        - To see all predefined symbols: gcc -dM -E - < /dev/null
      - The headers specify endianness, #include <endian.h>
    - confusing the two contexts:
      - May have to build and run programs on host, ala menuconfig or unifdef.
        - Need a host compiler for that, HOSTCC.
          - Two compilers, keep track of which to use where
      - ./configure asks questions about the machine it's building on to
        determine what kind of program to build.  Assumes host==target.
        Fundamentally wrong for cross compiling, at the design level.
      - two compilers (host and target), they get each other's files mixed up.
        - #includes paths, library paths, gcc calls wrong ld...
        - prefix the names, but this isn't complete ("collect2").
        - gcc falls back to "default" search path when it can't find something.
          - gcc doesn't know where to find the files it installed.

  - Everybody cares about native compiling, nobody cares about cross compiling.
    - Most projects don't care about cross compiling, and never will.
      - gentoo over 4000 packages, gentoo embedded cross compiles maybe 300.
      - Cross compiling complicates build system while restricting options,
        this infrastructure is a source of bugs even when it's not used, and
        only a tiny minority of the userbase will ever want it.
    - Everybody cares about native compiling.
      - If they don't, they'll probably take patches.
        - simple to fix, non-intrusive, generally considered a good thing.
      - Most developers don't know there's more to it than "build on arm".

  - Building natively on real hardware can be problematic
    - 200 mhz, 32 megs of ram, only access through serial port
    - We have one piece of real hardware and five developers.

  - But there are emulators, and desktops are cheap and powerful these days.
    - Throwing hardware at the problem cheaper than developers.
    - State of the art is QEMU, which is GPL.

  - A certain amount of cross compiling can't be avoided.
    - where do you get your development environment from?
    - bootstrap new platform.
    - recent ubuntu desktop CD for little-endian mips?

  - So cross compile to bootstrap a native environment, then build natively
    under emulation.

    - Trick: have the emulator call out to the cross compiler with distcc.
      - ./configure, make, preprocessing, and linking all run native.
      - heavy lifting of compilation farmed out, but that's hard to screw up.

Firmware Linux Walkthrough.
  - Prepare (download source, build some optional tools)
  - Build cross compiler (cross-compiler.sh)
  - Cross-compile native build environment (mini-native.sh)
  - package this so qemu can run it
  - Run it under qemu
  - Build hello world natively.

Build binutils.
  Fairly straightforward, no target dependencies.

Build gcc.
  Needs a target version of binutils.
  Pain in the ass because gcc is "special".

Beating GCC into submission
  - gcc is NOT SPECIAL.  But it thinks it is.
    - special case, special olympics, very special episode, isn't that special.
  - Compiler turns input into output.  So does a docbook to pdf converter.
    - Explicit and implicit input files.
      - xmlto has cmdline files, plus fonts and stylesheets.
      - gcc has headers and libraries and such.

  - A compiler doesn't try to run the programs it's building.
    - Reads C source, assembly, and ELF files.
    - Outputs C source, assembly, ELF files.
      - It reads and generates ELF and a.out as archive formats
        - readelf, ar, ldd, nm, objdump, libbfd
    - Not special.  Just files.

  - In theory, all you have to tell GCC during ./configure is what target it
    should produce binaries for.
    - What host the resulting gcc you're building _runs_ on is determined by
      the host compiler you're building gcc with, and it's got the same
      __i386__ #defines as every other platform if it really wants details.
    - That some compilers produce binaries that can be immediately executed
      is sheer coincidence, nothing more.  So can sed when it outputs a shell
      script.

  - Targets are descirbed as tuples.
    - What's a tuple?
      - It's designed to conflate together several characteristics to reduce
        orthogonality in the configuration.  (No, this is not a good thing.)
        Luckily, most of it doesn't apply to Linux.
      - just append "-unknown-linux" to your architecture.
        - armv4l, armv5l, mipsel, mips, sparc, powerpc, i686, x86_64
    - So nothing like the kernel's ARCH= values?
      - Not really.

  - In practice, gcc wants to build itself with itself.
    - No other compiler could possibly build a usable copy of gcc!
      - gcc is _special_
    - so build a temporary version (xgcc) with the tainted other compiler,
      then rebuild a _clean_ version with xgcc.
    - That's not enough, it wants to build itself three times.
      - 1) build xgcc, 2) rebuild, 3) rebuild with #2 and compare 2 & 3.
        - Serious paranoia.  Emotional scars.  It was abused as a child.
        - I'm sure the developers can tell us horror stories of platforms
          where this was a vitally important safeguard.  Those platforms were
          not Linux.
    - Dear gcc: you can't always get what you want.

  - This redundant build is not compatible with cross compiling.
    - When gcc is making cross compiling, it can't do this crazy stuff
      - Two vastly different build systems within the same project?
        - But of course.  It's from the FSF, where bigger is better.
    - How do you tell it to produce a cross-compiler?
      - When host and target differ, it behaves almost rationally.
        --host=i686-unknown-linux --target=armv4l-unknown-linux

      - But what if I'm building for i686-uclibc target from i686-glibc host?
        If gcc does the three-step, xgcc won't run because uclibc isn't
        installed on the host.
          - Lie to gcc, like so:
            --host=i686-walrus-linux --target=i686-unknown-linux
          - Then it thinks it's cross compiling, and will WORK.

  - The wrapper script:
    - "pathological", adjective, "having to do with the path logic in gcc".
      - gcc can't find files it installed.
        - It searches in lots of different places using crazy paths with lots
          of "subdir/../../../newdir" in them.  Run it under strace sometime,
          it's frightening.
        - Paths from environment variables, paths from built-in spec files,
          paths supplied from ./configure, paths added on the command line,
          paths hardwired into the C code...
        - Every time the previous layer bit-rots to the point of collapse,
          they add yet another layer.  They never _remove_ anything, they
          just stick it at the front of the list and fall back to looking
          in all the other locations when they can't find it.
        - All this is based on an absolute path from the root, hardwiring
          into the gcc binary the path to the directory gcc was built in.
          (What if you want to install a toolchain into your home directory,
          without needing root access?)
        - At the end it falls back to default locations like /usr/include
          which contains host stuff, not target stuff.
          - If it can't find the headers or libraries it needs, it happily
            substitutes the ones out of the host compiler.  THIS IS WRONG.

      - How to we untangle this?

      - gcc has seven important sources of input (spec files don't count):
        - Explicit input (C files listed on the command line)
        - system library headers
          - #include stdio.h.  From libc, zlib, etc.
          - default is /usr/include
        - compiler headers
          - #include stdarg.h, for va_arg and such.
          - default looks like /usr/lib/gcc/i486-linux-gnu/4.1.2/include
        - system libraries
          - libc.so, libz.so, libncurses.so.5.5
          - search path, includes /lib:/usr/lib and elsewhere.
          - try ldd /bin/ls
          - note .a vs .so vs .so.6
        - compiler libraries
          - libgcc_s.so (is evil).
            - stack unwinding, divide by long on 32-bit platforms, soft float...
          - at compile time, /usr/lib/gcc/i486-linux-gnu/4.1.2
          - at run time libgcc_s.so is in /lib
        - Executable search path
          - In theory so gcc can call ld.
            - in practice, collect2 and cc1 because gcc is bloated.
          - You'd think this would use $PATH, but that would be too easy.
            - Install binaries in $PATH?  But I'm _SPECIAL_

      - Bypass gcc's path logic entirely.  It's the only way to be sure.
        - Wrapper, parse gcc command line, call gcc with --nostdinc and
          --nostdlib (so it won't fall back to leaking in host stuff),
          explicitly specify header and library search paths (since we know
          where they are), and edit $PATH so it can call ld and such.
        - While we're at it, specify the location of the shared library loader.
          - Not actually used at compile time, just written into the binary to
            be used at runtime.  But it's something else we need to get right
            to make usable binaries.

    - Hang on, if we've got a wrapper why do we need to recompile for the
      i686-glibc to i686-uClibc case?
      - Because libgcc_s.so links against glibc, thus leaking a reference to
        the wrong C library.  (Thanks gcc!)  If we rebuild libgcc_s.so against
        uClibc, it's at least leaking a reference to the right library.  (Or
        better yet, configure gcc with --disable-shared so it has libgcc.a
        instead and doesn't do this at all.)

  - Ok, that was the hard part.  It's all downhill from here.

So back to bootstrapping a toolchain.
  Binutils builds by itself with no dependencies on other target packages.
  gcc needs a target version of binutils
    gcc also needs a wrapper script (or _extensive_ patching) to have sane
    path logic, but the wrapper script has no dependencies on anything else.
  uclibc needs:
    a gcc for the target
    linux kernel headers for the target (but the kernel headers have no
    dependencies and can be installed first).

  - start with binutils, wrapper, and/or linux kernel headers.
    - gcc comes after binutils.
    - uClibc comes after gcc and kernel headers.

Now build a native build environment for the target.
  In theory, you could follow Linux From Scratch from this point on.
  In practice, a minimal development environment only needs seven packages:
    binutils, gcc, kernel (headers _and_ vmlinux this time),  uClibc,
    busybox, make, bash.
    (Why bash?  Busybox shell isn't good enough yet.)
      - might have been fixed in newer versins, I don't follow it anymore.
  From that, you can build an entire Linux From Scratch system.
    I spent ~3 years fixing up busybox to the point where it works for this.

  In theory, the smallest self-bootstrapping system would be four packages:
    Linux, uclibc, busybox, tcc.
  But that doesn't work yet. :)

How do you build a native compiler for another platform?
  Build twice:
    Use your host compiler to build a compiler targeting the platform.
      This runs on the host to produce target binaries, so it's a
      cross compiler.
    Use the cross compiler to build a compiler targeting the platform.
      This runs on the target to produce target binaries, so it's a
      native compiler for the target.
  Technically, this is called a "canadian cross".
    - They had to come up with a special name for it.  It's NOT SPECIAL.
    - Your cross compiler could target one platform and the second compiler
      could target another, so you could use your x86 machine to build a
      cross compiler that runs on sparc and outputs arm binaries.  So what?
    - This is what gcc is doing internally anyway if you tell it
      --host arm and --target arm on your x86 machine.  Except in _that_
      case it wants to know --build.  Why?  Don't go there.

General outline:

1) Terminology: cross compiling, native compiling, host/target, toolchain, etc.

2) Why cross compiling is hard, and why we need to do it anyway.

3) Building a cross compiler toolchain from linux, binutils, gcc, and uClibc.
   Bootstrapping issues, what depends on what?

4) Making a native build environment (adding make, busybox, and bash).
   Building a new system.

5) Packaging disk images, booting, and running under QEMU.

6) Optimizations and alternatives.
   (distcc, armulator, boards/bootloaders, nfs, tsrpm)

7) Where to from here?  (LFS, gentoo, etc.)


-----------------------------------------------------------------------------
Links:
  http://www.landley.net/writing/docs/cross-compiling.html
  http://www.landley.net/code/firmware/about.html
  http://www.landley.net/code/firmware/design.html
  http://cross-lfs.org/files/BOOK/1.0.0/
  http://www.gentoo.org/proj/en/base/embedded/index.xml
  http://gentoo-wiki.com/Embedded_Gentoo

http://www.quietearth.us/articles/2006/08/16/Building-deb-package-from-source

http://qemu-forum.ipi.fi/qemu-snapshots/
git://git.kernel.dk/data/git/qemu.git