If you are a developer creating binary packages, like an RPM, DEB, Flatpak, or Snap, it’s a must to compile code for a wide range of completely different goal platforms. Typical targets embrace 32-bit and 64-bit x86 and ARM. You might do your builds on completely different bodily or digital machines, however meaning sustaining a number of techniques. Instead, you need to use the GNU Compiler Collection (GCC) to cross-compile, producing binaries for a number of completely different architectures from a single construct machine.
Assume you’ve a easy dice-rolling recreation that you simply wish to cross-compile. Something written in C is comparatively simple on most techniques, so so as to add complexity for the sake of realism, I wrote this instance in C++, so this system relies on one thing not current in C (iostream, particularly).
utilizing namespace std;
void lose (int c);
void win (int c);
void draw ();
void lose (int c )
void win (int c )
cout << "You win!! Computer rolled " << c << "n";
void draw ( )
Compile it in your system utilizing the g++ command:
$ g++ cube.cpp -o cube
Then run it to verify that it really works:
Pick a quantity between 1 and 20:
You can see what sort of binary you simply produced with the file command:
$ file ./cube
cube: ELF 64-bit LSB executable, x86-64, model 1 (SYSV), dynamically
linked (makes use of shared libs), for GNU/Linux 5.1.15, not stripped
And simply as necessary, what libraries it hyperlinks to with ldd:
$ ldd cube
linux-vdso.so.1 => (0x00007ffe0d1dc000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1
You have confirmed two issues from these checks: The binary you simply ran is 64-bit, and it’s linked to 64-bit libraries.
That implies that, with a purpose to cross-compile for 32-bit, you need to inform g++ to:
- Produce a 32-bit binary
- Link to 32-bit libraries as an alternative of the default 64-bit libraries
Setting up your dev atmosphere
To compile to 32-bit, you want 32-bit libraries and headers put in in your system. If you run a pure 64-bit system, then you don’t have any 32-bit libraries or headers and wish to put in a base set. At the very least, you want the C and C++ libraries (glibc and libstdc++) together with 32-bit model of GCC libraries (libgcc). The names of those packages might range from distribution to distribution. On Slackware, a pure 64-bit distribution with 32-bit compatibility is accessible from the multilib packages offered by Alien BOB. On Fedora, CentOS, and RHEL:
$ yum set up libstdc++-*.i686
$ yum set up glibc-*.i686
$ yum set up libgcc.i686
Regardless of the system you are utilizing, you additionally should set up any 32-bit libraries your undertaking makes use of. For occasion, when you embrace yaml-cpp in your undertaking, then you need to set up the 32-bit model of yaml-cpp or, on many techniques, the event bundle for yaml-cpp (as an example, yaml-cpp-devel on Fedora) earlier than compiling it.
Once that is taken care of, the compilation is pretty easy:
$ g++ -m32 cube.cpp -o cube32 -L /usr/lib -march=i686
The -m32 flag tells GCC to compile in 32-bit mode. The -march=i686 possibility additional defines what sort of optimizations to make use of (seek advice from data gcc for an inventory of choices). The -L flag units the trail to the libraries you need GCC to hyperlink to. This is often /usr/lib for 32-bit, though, relying on how your system is ready up, it might be /usr/lib32 and even /choose/usr/lib or anywhere you retain your 32-bit libraries.
After the code compiles, see proof of your construct:
$ file ./cube32
cube: ELF 32-bit LSB executable, Intel 80386, model 1 (SYSV),
dynamically linked (makes use of shared libs) [...]
And, after all, ldd ./cube32 factors to your 32-bit libraries.
Compiling 32-bit on 64-bit for a similar processor household permits GCC to make many assumptions about the best way to compile the code. If you want to compile for an solely completely different processor, you need to set up the suitable cross-build GCC utilities. Which utility you put in relies on what you might be compiling. This course of is a bit more complicated than compiling for a similar CPU household.
When you are cross-compiling for a similar household, you possibly can anticipate finding the identical set of 32-bit libraries as 64-bit libraries, as a result of your Linux distribution is sustaining each. When compiling for a completely completely different structure, you’ll have to search out libraries required by your code. The variations you want is probably not in your distribution’s repositories as a result of your distribution might not present packages on your goal system, or it might not mirror all packages in a handy location. If the code you are compiling is yours, then you definately most likely have a good suggestion of what its dependencies are and presumably the place to seek out them. If the code is one thing you’ve downloaded and must compile, then you definately most likely aren’t as accustomed to its necessities. In that case, examine what the code requires to construct appropriately (they’re often listed within the README or INSTALL recordsdata, and definitely within the supply code itself), then go collect the parts.
For instance, if you want to compile C code for ARM, you need to first set up gcc-arm-linux-gnu (32-bit) or gcc-aarch64-linux-gnu (64-bit) on Fedora or RHEL, or arm-linux-gnueabi-gcc and binutils-arm-linux-gnueabi on Ubuntu. This supplies the instructions and libraries you want to construct (at the very least) a easy C program. Additionally, you want no matter libraries your code makes use of. You can place header recordsdata within the common location (/usr/embrace on most techniques), or you possibly can place them in a listing of your selection and level GCC to it with the -I possibility.
When compiling, do not use the usual gcc or g++ command. Instead, use the GCC utility you put in. For instance:
$ arm-linux-gnu-g++ cube.cpp
Verify what you have constructed:
$ file armdice.bin
armdice.bin: ELF 32-bit LSB executable, ARM, EABI5 model 1 (SYSV) [...]
Libraries and deliverables
This was a easy instance of the best way to use cross-compiling. In actual life, your supply code might produce greater than only a single binary. While you possibly can handle this manually, there’s most likely no good purpose to do this. In my subsequent article, I am going to show GNU Autotools, which does a lot of the work required to make your code transportable.