📜 ⬆️ ⬇️

ARC CrossCompilation

For a long time I wanted to master the subject, but all were other more priority matters. And now it is the turn of the crosscompilation.

In this post will be described:

  1. Instruments
  2. Elementary cross-compilation technology
  3. And, actually, HOW2

Who cares, I ask under the cat.

Introductory


One of the emerging trends in modern IT is IoT. This direction is developing quite quickly, all the time there are all sorts of cool things (such as sneakers with a built-in tracker or sneakers that can indicate the direction where to go (especially for blind people)). The bulk of these devices are something like “bluetooth bulbs”, but the rest is complex processing systems that collect data and manage this huge variety of clever things. These complex systems are usually single-board computers, such as Raspberry Pi, Odroid, Orange Pi, etc. They run Linux and write application software. Basically, they use scripting languages ​​and Java. But there are applications when high performance is needed, and here, of course, C and C ++ are required. For example, you may need to add something specific to the core or, as soon as possible, calculate the FFT. This is where cross compilation is needed.
')
If the project is not very large, then it can be assembled and debugged directly on the target platform. And if the project is large enough, then compiling on the target platform will be difficult due to time costs. For example, try building a Boost on a Raspberry Pi. I think the wait for the assembly will be long, and if there are any errors that come up, it can take oh so much time.

Therefore, it is better to collect on the host. In my case, this is i5 with 4GB of RAM, Fedora 24.

Instruments


Cross-compiling for ARM requires a toolchain and a platform emulator or a real target platform.

Since I'm interested in compiling for ARM, then the corresponding toolchain will also be used.

Toolchains are divided into several types or triplets. A triplet usually consists of three parts: the target processor, vendor and OS, vendor is often omitted.


The above is true for gcc and the toolchains made from it.

First, I tried to use the toolchains that are in the Fedora 24 reps. But I was unpleasantly surprised by this:

[gazpar@localhost ~]$ dnf info gcc-c++-arm-linux-gnu Last metadata expiration check: 3 days, 22:18:36 ago on Tue Jan 10 21:18:07 2017. Installed Packages Name : gcc-c++-arm-linux-gnu Arch : x86_64 Epoch : 0 Version : 6.1.1 Release : 2.fc24 Size : 18 M Repo : @System From repo : updates Summary : Cross-build binary utilities for arm-linux-gnu URL : http://gcc.gnu.org License : GPLv3+ and GPLv3+ with exceptions and GPLv2+ with exceptions and LGPLv2+ and BSD Description : Cross-build GNU C++ compiler. : : Only the compiler is provided; not libstdc++. Support for cross-building : user space programs is not currently provided as that would massively multiply : the number of packages. 

Searching, stumbled upon a toolchain from the company Linaro. And he completely arranged for me.

The second tool is QEMU . I will use it because My Odroid-C1 + died the death of the brave (the SD card controller bent down). But I still managed to work with him a bit, which is good news.

Elementary cross-compilation technology


Actually, nothing unusual in this. Just use toolchain as a compiler. And standard libraries come with a toolchain.

It looks like this:

 CC := g++ TOOLCHAIN := arm-linux-gnueabihf- PT := CFL := -Wextra -std=c++11 TPATH := /home/gazpar/toolchain/gcc-linaro-5.3.1-2016.05-x86_64_arm-linux-gnueabihf/bin/ LPATH := /home/gazpar/toolchain/sysroot-glibc-linaro-2.21-2016.05-arm-linux-gnueabihf/ ARCH := -march=armv7-a -mcpu=cortex-a5 --sysroot=$(LPATH) all: slc.cpp $(CC) $(CFL) -o eval slc.cpp cross: slc.cpp $(TPATH)$(TOOLCHAIN)$(CC) $(CFL) $(ARCH) slc.cpp -o acalc -static clear: rm -f *.o rm -f eval 

What toolchain keys can be viewed on the gnu website, in the appropriate section.

HOW2


First you need to run the emulation with the platform of interest. I decided to emulate the Cortex-A9.

After several unsuccessful attempts, I stumbled upon this how2, which turned out to be quite sane, in my opinion.

Well, first, of course, you need to get hold of QEMU. I installed it from the standard Fedora reps.

Next, create an image of the hard disk on which Debian will be installed.

 qemu-img create -f raw armdisk.img 8G 

This link has downloaded vmlinuz and initrd and launched them in emulation.

 qemu-system-arm -m 1024M -sd armdisk.img \ -M vexpress-a9 -cpu cortex-a9 \ -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.gz \ -append "root=/dev/ram" -no-reboot \ -net user,hostfwd=tcp::10022-:22 -net nic 

Then simply install Debian on our hard disk image (it took me ~ 1.5 hours).

After installation, you need to remove vmlinuz and initrd from the hard disk image. I did it according to the description from here .

First we find the offset where the section with the files we need is located:

 [gazpar@localhost work]$ fdisk -l armdisk.img Disk armdisk.img: 8 GiB, 8589934592 bytes, 16777216 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x000e5fe1 Device Boot Start End Sectors Size Id Type armdisk.img1 * 2048 499711 497664 243M 83 Linux armdisk.img2 499712 15958015 15458304 7.4G 83 Linux armdisk.img3 15960062 16775167 815106 398M 5 Extended armdisk.img5 15960064 16775167 815104 398M 82 Linux swap / Solaris 

Calculate the offset:

 [gazpar@localhost work]$ bc bc 1.06.95 Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc. This is free software with ABSOLUTELY NO WARRANTY. For details type `warranty'. 512*2048 1048576 

Now on this offset we will mount the section we need.

 [gazpar@localhost work]$ sudo mount -o loop,offset=1048576 armdisk.img qemu-mnt/ 

 [gazpar@localhost work]$ ls -la qemu-mnt/ total 5174 drwxr-xr-x. 3 root root 1024 Jan 14 09:30 . drwxrwxr-x. 19 gazpar gazpar 4096 Jan 14 10:35 .. -rw-r--r--. 1 root root 79252 Jan 1 01:13 config-3.2.0-4-vexpress lrwxrwxrwx. 1 root root 27 Jan 14 08:47 initrd.img -> initrd.img-3.2.0-4-vexpress -rw-r--r--. 1 root root 1991475 Jan 14 09:30 initrd.img-3.2.0-4-vexpress drwxr-xr-x. 2 root root 12288 Jan 14 08:30 lost+found -rw-r--r--. 1 root root 1130676 Jan 1 01:13 System.map-3.2.0-4-vexpress lrwxrwxrwx. 1 root root 24 Jan 14 08:47 vmlinuz -> vmlinuz-3.2.0-4-vexpress -rw-r--r--. 1 root root 2051760 Jan 1 01:13 vmlinuz-3.2.0-4-vexpress 

Copy the vmlinuz and initrd files and unmount the hard disk.

 [gazpar@localhost work]$ sudo umount qemu-mnt/ 

Now you can run the emulation.

 qemu-system-arm -m 1024M -M vexpress-a9 \ -kernel vmlinuz -initrd initrd.img \ -append "root=/dev/mmcblk0p2" \ -sd armdisk.img \ -net user,hostfwd=tcp::10022-:22 -net nic 

And here is the cherished invitation:



Now you can pick up the simulation from the host via SSH.

 [gazpar@localhost work]$ ssh -p10022 arm@localhost arm@debian:~$ arm@debian:~$ uname -a Linux debian 3.2.0-4-vexpress #1 SMP Debian 3.2.84-1 armv7l GNU/Linux 

Now you can collect the program. According to the Makefile, it is clear that there will be a calculator. Simple.

 #include <iostream> #include <string> #include <vector> // Function to check input expression bool checkExpression(std::string exp){ for(uint i=0; i<exp.length(); i++){ char c = exp[i]; if(c < '(' || c > '9' || c == '\''){ if(c != ' ') return false; } } return true; } // Template function to evaluate atomic expression template<class T> T eval(int a, int b, const char op){ switch(op){ case '+':{ return a+b; } case '-':{ return ab; } case '*':{ return a*b; } case '/':{ return a/b; } default: throw("atomEval: Undefined math operation"); } }; // Function to evaluate expression without brackets template<class T> std::string evalExpWithoutBrackets(std::string exp){ std::vector<T> operands; std::vector<char> operations; const uint explen = exp.length(); // Allocating arguments and operations without ordering for(uint shift=0, position = 0; shift<explen; shift++){ // This check need for situation when we didn't allocate last argument if(shift == explen-1){ std::string expWithoutBrackets; expWithoutBrackets.assign(exp, position, explen - position + 1); operands.push_back((T) std::stod(expWithoutBrackets)); } if( exp[shift] == '+' || exp[shift] == '-' || exp[shift] == '*' || exp[shift] == '/'){ std::string expTemp; expTemp.assign(exp, position, shift-position); operands.push_back((T) std::stod(expTemp)); operations.push_back(exp[shift]); std::string tempExp; position = shift+1; for(shift++; shift<explen; shift++){ if( exp[shift] == '+' || exp[shift] == '-' || exp[shift] == '*' || exp[shift] == '/' ){ tempExp.assign(exp, position, shift-position); operations.push_back(exp[shift]); break; } if(shift == explen-1){ tempExp.assign(exp, position, explen - position); } } operands.push_back((T)std::stod(tempExp)); position = shift+1; } } // Calculator std::vector<uint> evalOrder; // Order of operations uint highPriority = 0, lowPriority = 0; // Ordering operations // First of all we need operations with high priority for(uint i=0; i < operations.size(); i++){ if(operations[i] == '*' || operations[i] == '/'){ evalOrder.push_back(i); highPriority++; } } // Now we need to order low priority operations for(uint i=0; i < operations.size(); i++){ if(operations[i] == '-' || operations[i] == '+'){ evalOrder.push_back(i); lowPriority++; } } // Evaluating epression by order for(uint i=0; i < evalOrder.size(); i++){ T rexp = (T)NULL; try{ rexp = eval<T>(operands[evalOrder[i]], operands[evalOrder[i]+1], operations[evalOrder[i]]); } catch(char const *er){ std::cout << er << std::endl; } // Erasing operations and operands, because operands[evalOrder[i]] and operands[evalOrder[i]+1] // became single argument after completing operations[evalOrder[i]] operation if(evalOrder[i] < operands.size()-1){ operands.erase(operands.begin()+evalOrder[i]+1); operations.erase(operations.begin()+evalOrder[i]); } // Recallculating order for(uint j = i; j < evalOrder.size(); j++){ if(evalOrder[j] > evalOrder[i]) evalOrder[j]--; } // Storing result of eval<T> operands[evalOrder[i]] = rexp; } return std::to_string(operands[0]); } template<class T> std::string evalExpression(std::string exp){ uint open = 0, close = 0; for(uint i=0; i<exp.length(); i++){ if(exp[i] == '(') open++; else if(exp[i] == ')') close++; } if(open != close) return (std::string)"error: Expression have uncoupled brackets"; // Divide expression to the blocks if there are any brackets for(uint closeBracketPosition=0; closeBracketPosition<exp.length(); closeBracketPosition++){ if(exp[closeBracketPosition] == ')'){ uint openBracketPosition = closeBracketPosition; while(openBracketPosition--){ if(exp[openBracketPosition] == '('){ std::string expWithoutBrackets; expWithoutBrackets.assign(exp, openBracketPosition + 1, closeBracketPosition - openBracketPosition - 1); std::string atomExpResult = evalExpression<T>(expWithoutBrackets); std::string leftPartExp, rightPartExp; leftPartExp.assign(exp, 0, openBracketPosition); rightPartExp.assign(exp, closeBracketPosition + 1, exp.length() - closeBracketPosition); return evalExpression<T>( leftPartExp + atomExpResult + rightPartExp); } } } } return evalExpWithoutBrackets<T>(exp);; } int main(int argc, char **argv){ std::string evalexp(argv[1]); // Check input expression for unhandling symbols if(!checkExpression(evalexp)) return -1; // Clear expression from spaces for(uint i=0 ; i < evalexp.length(); i++){ if(evalexp[i] == ' '){ evalexp.erase(evalexp.begin() + i); if(i > 0) i--; } } std::cout << "Evaluating expression is: \"" << evalexp << "\"" << std::endl; std::cout << "Result is: " << evalExpression<int>(evalexp) << std::endl; return 0; } 

We compiled an executable file on the host.

 [gazpar@localhost slcalc]$ make cross /home/gazpar/toolchain/gcc-linaro-5.3.1-2016.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ -Wextra -std=c++11 -march=armv7-a -mcpu=cortex-a5 --sysroot=/home/gazpar/toolchain/sysroot-glibc-linaro-2.21-2016.05-arm-linux-gnueabihf/ slc.cpp -o acalc -static [gazpar@localhost slcalc]$ ls -la drwxrwxr-x. 2 gazpar gazpar 4096 Jan 15 16:35 . drwxrwxr-x. 7 gazpar gazpar 4096 Aug 15 07:56 .. -rwxrwxr-x. 1 gazpar gazpar 9704352 Jan 15 16:35 acalc -rwxrwxrwx. 1 gazpar gazpar 59 Jan 10 22:04 .directory -rwxrwxrwx. 1 gazpar gazpar 469 Jan 14 11:14 Makefile -rwxrwxrwx. 1 gazpar gazpar 4951 Jan 13 21:15 slc.cpp 

I note that it is easier to assemble with the -static key, if there is no particular desire to indulge in carnal pleasures with libraries on the target platform.

Copy the executable file on the target and check.

 [gazpar@localhost slcalc]$ scp -P 10022 acalc arm@localhost:/home/arm/acalc 

 arm@debian:~$ ./acalc 12*13-11*(21-3) Evaluating expression is: "12*13-11*(21-3)" Result is: -42 



Actually, here it is, this crosscut.

UPD: Corrected information on toolchains on the grossws comment.

Source: https://habr.com/ru/post/319736/


All Articles