I'm going through the Linux From Scratch 11.0 book. In III. Building the LFS Cross Toolchain and Temporary Tools, ii. Toolchain Technical Notes, there is a bit about Canadian Cross cross-compilation. I do not understand why there need to be 3 stages and 3 machines to get to the end result. The text assumes that we start with computer A and a compiler that runs on A and produces binaries for A. So why don't we just use that compiler to build a compiler that runs on C and builds binaries for C? Why there is so much hassle instead, with building a compiler that runs on A, but builds for B, then a compiler that runs on B, but builds for C, and finally the compiler that runs on C and builds for C?

I also found an article on Wikipedia about it - https://en.wikipedia.org/wiki/Cross_compiler.


3 Answers 3


Scenario described is this

  • Machine A is slow and has a compiler
  • Machine B is fast, but has no compiler
  • Machine C is the target but it is slow, and has no compiler

You could build all the binaries for C on A, but it would take a long time because machine A is slow.

The author argues that it is worth taking a small amount of time to cross-compile on A a compiler for B. Then the fast machine B could be used to cross-compile all the necessary binaries for slow machine C, resulting in an overall time saving against compiling on A or C.

The final step, where a compiler is built on C for C, is simply to remove the dependency on machine B. Although it's slow, machine C can now compile the occasional program itself, directly.

  • That was exactly my understanding when I was reading the chapter for the first time. Since we start with a compiler that runs on A, and build programs for A, all we need to do is to use that compiler (just once) to build another compiler - one that would run on B, and build programs for C. And that's it - single step. Then we can use that last compiler to build programs for C. However, the book goes through more steps, eventually ending with a compiler that runs on C and builds for C. At that point I did not understand why the whole "machine B is fast, while A and C are slow" part was for.
    – mnj
    Commented Sep 13, 2021 at 19:12
  • 2
    @Loreno: If you're starting with a compiler that runs on A, and builds programs for A, then it can't build a compiler that will run on B, because it only builds programs for A. So first, you need to build a compiler that runs on A but that builds for B. (This is a program that runs on A, so your first compiler can build it.). Once you have a compiler that runs on A but builds for B, you can start building compilers etc to run on B. If your goal is to compile things for C, then your next step may well be to build a compiler to run on B that compiles programs to run on C.
    – psmears
    Commented Sep 14, 2021 at 21:29
  • 2
    @roaima Yes - to be clear, I'm not disagreeing with you, just amplifying the part that OP seems to be having trouble with :)
    – psmears
    Commented Sep 14, 2021 at 22:13
  • @psmears You are right! Somehow I completely missed that part, thanks for "slower" explanation, that's what I needed :)
    – mnj
    Commented Sep 16, 2021 at 18:06

First, the computer C is not something you can practically compile on. It may be an embedded device, or even just a production machine you don't want to set up for compiling.

So you have such a 'nix machine. You need to be able to cross compile for it.

What you do have is a build machine. This is B. It is a machine that is fast enough and powerful enough, but it doesn't have a compiler. It doesn't even have a compiler that builds for B, let alone one that builds for C. You want to use it to build stuff for C.

How do you get it? Well, you talk to someone who has a compiler for B, ideally one that cross-compiles to C directly. Then you download it to B, and use it to cross-compile for your bespoke system C.

Where do they get the compiler for B? Odds are their system isn't identical to B. How do they get started?

All they really need is a system, A, with a compiler that compiles programs for A.

Now, this compiler already installed on A might not be able to build cross-compilers. It is just some compiler for a language (like ) that was provided by the vendor or something.

What you need is a cross compiler that can compile binaries for B. To get this, first you take your A to A compiler (your "vendor" compiler), and compile a compiler that supports cross-compiling to B.

Next, you take that compiler, and you have it compile a cross-compiler that compiles from B to C.

Then you distribute that B to C compiler to the person with computers B and C.


Now, this is (and was) a real world problem. You have a microsoft windows machine with a vendor compiler, or a solaris unix machine, or a macOS machine. In all case you can get a compiler, but not a cross compiler, easily. Those vendors have little interest in providing you with cross compilers.

You don't have the computers that everyone who you supply tools to has (B and C). So you bootstrap from your "locked in" system A to the point of being able to build and distribute full sets of pre-built tools for users to download.

The "hard" requirement is that computer A has a compiler that can compile a cross-compiler from B to C; where both the execution instruction set of the compiler and the target instruction set of the compiler are foreign to A.

  • "The final step, where a compiler is built on C for C, ", I don't understand why you wouldn't build the native C toolchain on B. If you can build stuff on B that will run on C that ought to include being able to build a native C toolchain. Commented Sep 14, 2021 at 4:13
  • @soron Who are you quoting? In any case, making the C toolchain on B is extra work. You'd have to get B to B chain, use that to build B to C, then start building stuff for C. If someone provides B to C, you skip a pile of work. The bespoke system (C) has code you want to custom compile for C. All work you do (with B and C) that isn't that custom code is a waste. Commented Sep 14, 2021 at 4:24

Roaima's answer describes the logic put forward by LFS. I'm not convinced that LFS gives a good explanation for a three way "Canadian Cross" compile.

Assuming that machine A produces a compiler for machine B which compiles for machine C:

B and C are commonly different due to limitations of machine C (yes). As an example a developer might want to compile code for machine C, but C is a very limited SBC or embedded device and can't easily run it's own compiler due to CPU and memory limits. So the developer uses their much more powerful desktop (machine B) to compile for C.

A and B are most commonly different but not because of resource constraints. The more common reason is often that the developer who compiles the compiler itself is not the same as the developer who compiles the final code for C. They're often not in the same organisation.


Take the ubuntu package gcc-arm-linux-gnueabihf. This is maintained by Ubuntu Core Developers. This package can be downloaded by anyone wishing to compile a program for ARM on another machine such as an AMD64 machine. That is, it can be installed onto B to compile for C by anyone, and Ubuntu Core Developers have no control over machine's B and C.

Now Ubuntu Core Developers compile gcc-arm-linux-gnueabihf to run on amd64 arm64 and i386. They don't want or need three build machines to build all three versions of it. That's because machine a can be a different architecture to both machines B and C.

  • The Ubuntu connection is tenuous: Ubuntu buildds don’t cross-compile, so gcc-arm-linux-gnueabihf is built on all its host architectures, not as a Canadian cross. (The same applies to Debian.) Commented Sep 14, 2021 at 3:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .