Why do I need a Cross Compiler?: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
Line 19: Line 19:
Your distribution comes equipped with a compiler that targets yourMachineArch-distributionNativeExecFormat-distribution, and that runs on the same yourMachineArch-distributionNativeExecFormat-distribution machine. It was built on some machine (could have been built on m68k-macho-osx or some other colourful machine like that), with a cross compiler that targeted yourMachineArch-distributionNativeExecFormat-distribution, and while it was being built, its own "target" was set to yourMachineArch-distributionNativeExecFormat-distribution, so that it now both runs on, and spits out executables for yourMachineArch-distributionNativeExecFormat-distribution.
Your distribution comes equipped with a compiler that targets yourMachineArch-distributionNativeExecFormat-distribution, and that runs on the same yourMachineArch-distributionNativeExecFormat-distribution machine. It was built on some machine (could have been built on m68k-macho-osx or some other colourful machine like that), with a cross compiler that targeted yourMachineArch-distributionNativeExecFormat-distribution, and while it was being built, its own "target" was set to yourMachineArch-distributionNativeExecFormat-distribution, so that it now both runs on, and spits out executables for yourMachineArch-distributionNativeExecFormat-distribution.


A cross compiler is useful for completely unambiguously targeting a specific machine. When you begin developing for a machine separate from your native distribution's stock compiler, you'll need a cross compiler. Actually, you already *do* target a machine that's different from your native machine frmo the very first time you begin trying to produce completely standalone executables.
A cross compiler is useful for completely unambiguously targeting a specific machine. When you begin developing for a machine separate from your native distribution's stock compiler, you'll need a cross compiler. Actually, you already *do* target a machine that's different from your native machine from the very first time you begin trying to produce completely standalone executables (that is, when you're doing OSDev).


Your native compiler targets yourMachineArch-distributionNativeExecFormat-distribution. But you want to target yourOsTargetArch-yourChosenFormat-none; where "none" as the OS-name represents a machine with no system libraries. When you compile your kernel, you want to build with a compiler that does not know of any system libraries for your target machine. The kernel must be standalone completely. Everything the kernel needs must be in its source tree so that no implicit system libraries are needed (or linked in without your knowledge).
Your native compiler targets yourMachineArch-distributionNativeExecFormat-distribution. But you want to target yourOsTargetArch-yourChosenFormat-none; where "none" as the OS-name represents a machine with no system libraries. When you compile your kernel, you want to build with a compiler that does not know of any system libraries for your target machine. The kernel must be standalone completely. Everything the kernel needs must be in its source tree so that no implicit system libraries are needed (or linked in without your knowledge).


Later on, when you have a userspace and a set of system libraries for your programs to link to, you'll then build a compiler that targets: yourOsTargetArch-yourChosenFormat-yourOs while keeping the compiler that targets yourOsTargetArch-yourChosenFormat-none. You would continue to use the "bare bones" targeting compiler to build the kernel, and whenever you build programs on your development machine which are expected to run on your kernel's target machine, you would use the yourOsTargetArch-yourChosenFormat-yourOs compiler, which has a /include and /lib directories full of your own OS's native system includes and libraries.
Later on, when you have a userspace and a set of system libraries for your programs to link to, you'll then build a compiler that targets: yourOsTargetArch-yourChosenFormat-yourOs while keeping the compiler that targets yourOsTargetArch-yourChosenFormat-none. The difference between the two is that the "none" OS targeting compiler does not have any libraries in its /lib directory, and has no system libraries to link in. It is therefore impossible for any software you compile using this "bare metal" targeting compiler to have unknown dependencies. The "yourOS" targeting cross compiler is where you would place your own system libraries, (in its /lib directory), such that when it builds software, it will link them against those system libraries, thus linking them against your kernel API. You would continue to use the "bare bones" targeting compiler to build the kernel, and whenever you build programs on your development machine which are expected to run on your kernel's target machine, you would use the yourOsTargetArch-yourChosenFormat-yourOs compiler, which has a /include and /lib directories full of your own OS's native system includes and libraries.


Later still, when you're confident that you can both develop and use your kernel, from within your kernel, you'll want to stop building all your programs on a separate "development" machine. At this point, it's time to take the final step, and, from your separate "development" machine, build a compiler that will run on your OS (host = yourOsTargetArch-yourChosenFormat-yourOs), and target your OS (target = yourOsTargetArch-yourChosenFormat-yourOs). This cross compiler, the canadian cross, will allow you to natively compile other programs while running on your own OS's userspace. It is essentially a "distribution native compiler", like the one that comes with your distro. From that point on, having a compiler that runs on and targets your OS, you can essentially more freely port programs, and allow people to do the same while running your OS, assuming you package that native compiler with your OS.
Later still, when you're confident that you can both develop and use your kernel, from within your kernel, you'll want to stop building all your programs on a separate "development" machine. More specifically, at this point, you want to have your "build" machine be the same as your test machine such that you build programs that run on your OS, from within your OS. At this point, it's time to take the final step, and, from your separate "development" machine, build a compiler that will run on your OS (host = yourOsTargetArch-yourChosenFormat-yourOs), and target your OS (target = yourOsTargetArch-yourChosenFormat-yourOs). This cross compiler, the canadian cross, will allow you to natively compile other programs while running on your own OS's userspace. It is essentially a "distribution native compiler", like the one that comes with your distro. From that point on, having a compiler that runs on and targets your OS, you can essentially more freely port programs, and allow people to do the same while running your OS, assuming you package that native compiler with your OS.


So yes, it is advised, for sanity's sake, to use a cross compiler even when your distro's native compiler targets almost the same machine as your standalone environment.
So yes, it is advised, for sanity's sake, to use a cross compiler even when your distro's native compiler targets almost the same machine as your standalone environment.

Revision as of 11:01, 30 September 2010

Where did the idea of cross compiling come from?

With GNU software, since most of it has a long history of being ported from one UNIX implementation to another, they came up with a simple set of rules: You have a build machine, a host machine, and a target machine.

What are the basics of cross compiling?

The "build" machine is the machine you're compiling the software on. This software being compiled may be compiled to run on some other type of machine. See, you may be building on an x86-based machine, and wishing for the software to run on a SPARC based machine. The build machine is implicit and will usually be auto-detected by the configure script for the software. Its only real purpose is so that, if the software being compiled chooses to keep the configure arguments used to configure it somewhere in the built package, the people to whom the package is distributed will know what machine the package was built on. The name of the build machine may be used to configure to package to use workarounds as well if the build machine is universally known to have certain problems building that software.

The "host" machine is the machine on which the software must run. So in the previous example, the "build" machine is an i586-elf-yourBuildOs machine, and the host is a sparc32-elf-unix4 machine.

In the example, you must then, have a sparc32-elf-unix4 cross compiler, which can run on the i686-elf-yourBuildOs machine, and spit out (target) your host machine. A cross compiler is usually named after the host it targets, and not after the host it runs on, so by looking at the name of a compiler, you can usually tell what machine it targets.

The "target" only matters when compiling software that is used to build other software. That is, when compiling compilers, linkers, assemblers and the like. They need to be told what machine they will themselves target. When compiling something like a movie player, or other non-building software, the "target" does not matter. In other words, "target" is used to tell a compiler or other development software being compiled what host it, the compiler being compiled, should target.

For most software, like a text editor, if you're compiling it, you only need to specify host (and usually not even that). Specifying a host causes the software to be built using a compiler on the build machine which is a cross compiler that targets that host. This way, since the software was compiled using that host's targeting cross compiler, the software will be able to run on that host, even though it was built on a (potentially) different built machine.

An example to concrete things

Your distribution comes equipped with a compiler that targets yourMachineArch-distributionNativeExecFormat-distribution, and that runs on the same yourMachineArch-distributionNativeExecFormat-distribution machine. It was built on some machine (could have been built on m68k-macho-osx or some other colourful machine like that), with a cross compiler that targeted yourMachineArch-distributionNativeExecFormat-distribution, and while it was being built, its own "target" was set to yourMachineArch-distributionNativeExecFormat-distribution, so that it now both runs on, and spits out executables for yourMachineArch-distributionNativeExecFormat-distribution.

A cross compiler is useful for completely unambiguously targeting a specific machine. When you begin developing for a machine separate from your native distribution's stock compiler, you'll need a cross compiler. Actually, you already *do* target a machine that's different from your native machine from the very first time you begin trying to produce completely standalone executables (that is, when you're doing OSDev).

Your native compiler targets yourMachineArch-distributionNativeExecFormat-distribution. But you want to target yourOsTargetArch-yourChosenFormat-none; where "none" as the OS-name represents a machine with no system libraries. When you compile your kernel, you want to build with a compiler that does not know of any system libraries for your target machine. The kernel must be standalone completely. Everything the kernel needs must be in its source tree so that no implicit system libraries are needed (or linked in without your knowledge).

Later on, when you have a userspace and a set of system libraries for your programs to link to, you'll then build a compiler that targets: yourOsTargetArch-yourChosenFormat-yourOs while keeping the compiler that targets yourOsTargetArch-yourChosenFormat-none. The difference between the two is that the "none" OS targeting compiler does not have any libraries in its /lib directory, and has no system libraries to link in. It is therefore impossible for any software you compile using this "bare metal" targeting compiler to have unknown dependencies. The "yourOS" targeting cross compiler is where you would place your own system libraries, (in its /lib directory), such that when it builds software, it will link them against those system libraries, thus linking them against your kernel API. You would continue to use the "bare bones" targeting compiler to build the kernel, and whenever you build programs on your development machine which are expected to run on your kernel's target machine, you would use the yourOsTargetArch-yourChosenFormat-yourOs compiler, which has a /include and /lib directories full of your own OS's native system includes and libraries.

Later still, when you're confident that you can both develop and use your kernel, from within your kernel, you'll want to stop building all your programs on a separate "development" machine. More specifically, at this point, you want to have your "build" machine be the same as your test machine such that you build programs that run on your OS, from within your OS. At this point, it's time to take the final step, and, from your separate "development" machine, build a compiler that will run on your OS (host = yourOsTargetArch-yourChosenFormat-yourOs), and target your OS (target = yourOsTargetArch-yourChosenFormat-yourOs). This cross compiler, the canadian cross, will allow you to natively compile other programs while running on your own OS's userspace. It is essentially a "distribution native compiler", like the one that comes with your distro. From that point on, having a compiler that runs on and targets your OS, you can essentially more freely port programs, and allow people to do the same while running your OS, assuming you package that native compiler with your OS.

So yes, it is advised, for sanity's sake, to use a cross compiler even when your distro's native compiler targets almost the same machine as your standalone environment.

A further concretion example

A paste of 'gcc -v' from my Ubuntu machine gives:

gravaera@gravaera-laptop:/void/cygwin/osdev/zbz$ gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.4.3-4ubuntu5' \
--with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ \
--prefix=/usr --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib \
--libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 \
--program-suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-plugin --enable-objc-gc \
--enable-targets=all --disable-werror --with-arch-32=i486 --with-tune=generic --enable-checking=release \
--build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5)

The person who built the compiler that runs on my machine built it on a machine just like mine, an 'i486-linux-gnu' (build), and intended for it to run on a machine like his/mine: the same i486-linux-gnu (host), and he meant for this compiler he was building for me, to emit executables that targeted my very machine, so that when I compile programs, they will be able to run on a host that is i486-linux-gnu. Therefore he made the compiler target i486-linux-gnu.