ARMのクロスコンパイル

長い間、私は主題を学びたいと思っていましたが、すべては他のより優先事項でした。 そして今、クロスコンパイルの番です。



この投稿では以下について説明します。



  1. ツール
  2. エレメンタリークロスコンパイルテクノロジー
  3. そして、実際には、HOW2


猫の下で誰が気にしてください。



入門



現代のITの開発分野の1つはIoTです。 この方向は非常に急速に発展しており、あらゆる種類のクールなものが常に出てきています(統合されたトラッカーを備えたスニーカーや、進むべき方向を示すことができるスニーカーなど)(特に目の不自由な人向け)。 これらのデバイスの大部分は「ブルートゥース電球」のようなものですが、残りはデータを収集し、この多種多様なスマートなものを管理する複雑なプロセッサシステムです。 これらの複雑なシステムは通常、Raspberry Pi、Odroid、Orange Piなどのシングルボードコンピューターです。 Linuxを実行し、アプリケーションソフトウェアを作成します。 ほとんどの場合、スクリプト言語とJavaを使用します。 ただし、高いパフォーマンスが必要なアプリケーションもあります。もちろん、ここではCとC ++が必要です。 たとえば、カーネルに固有の何かを追加したり、FFTをできるだけ早く計算する必要がある場合があります。 これは、クロスコンパイルが必要な場所です。



プロジェクトがそれほど大きくない場合は、ターゲットプラットフォームで直接組み立ててデバッグできます。 また、プロジェクトが十分に大きい場合、ターゲットプラットフォームでのコンパイルは時間コストのために困難になります。 たとえば、Raspberry PiでBoostをビルドしてみてください。 アセンブリの待ち時間は長くなると思います。ポップアップするエラーがある場合、どれくらい時間がかかるかわかりません。



したがって、ホストで収集することをお勧めします。 私の場合、これは4GBのRAMを搭載したi5、Fedora 24です。



ツール



ARMのクロスコンパイルには、ツールチェーンとプラットフォームエミュレーターまたは実際のターゲットプラットフォームが必要です。



なぜなら ARM向けのコンパイルに興味がある場合は、対応するツールチェーンが使用されます。



ツールチェーンは、いくつかのタイプまたはトリプレットに分かれています。 トリプレットは通常、ターゲットプロセッサ、ベンダー、OSの3つの部分で構成され、ベンダーはしばしば省略されます。





上記は、gccとそれに基づいて作成されたツールチェーンに当てはまります。



最初は、Fedora 24のカブにあるツールチェーンを使用しようとしましたが、これには不愉快な驚きがありました。



[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.
      
      





検索後、リナロのツールチェーンに出会いました。 そして彼は私に完璧に合っていました。



2番目のツールはQEMUです。 私はそれを使用します、なぜなら 私のOdroid-C1 +は勇敢な人の死をもたらしました(SDカードコントローラーが屈んだ)。 しかし、私はまだ彼と少し仕事をすることができました。



エレメンタリークロスコンパイルテクノロジー



実際、これに異常はありません。 コンパイラとしてツールチェーンを使用しました。 また、標準ライブラリにはツールチェーンが付属しています。



次のようになります。



 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
      
      





ツールチェーンのキーは、gnu Webサイトの対応するセクションで表示できます。



How2



まず、対象のプラットフォームでエミュレーションを実行する必要があります。 Cortex-A9をエミュレートすることにしました。



いくつかの試みが失敗した後、私はこの how2に出くわしました。



もちろん、まず、QEMUを取得する必要があります。 Fedorの標準担当者からインストールしました。



次に、Debianをインストールするハードディスクイメージを作成します。



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





このリンクからvmlinuzとinitrdをダウンロードし、エミュレーションで起動しました。



 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
      
      





次に、Debianをハードドライブイメージにインストールします(1.5時間ほどかかりました)。



インストール後、ハードディスクイメージからvmlinuzとinitrdを削除する必要があります。 ここからの説明通りにやっ



まず、必要なファイルのあるセクションが存在するオフセットを見つけます。



 [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
      
      





オフセットを計算します。



 [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
      
      





次に、このオフセットで、必要なセクションをマウントします。



 [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
      
      





vmlinuzおよびinitrdファイルをコピーし、ハードドライブをアンマウントします。



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





これで、エミュレーションを実行できます。



 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
      
      





そして、ここに大切な招待状があります:







これで、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
      
      





これで、プログラムを組み立てることができます。 Makefileから、電卓があることは明らかです。 気取らない。



 #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; }
      
      





ホストで実行可能ファイルを収集します。



 [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
      
      





ターゲットプラットフォーム上のライブラリを使用して肉体的な喜びを満喫したい場合は、 -staticスイッチを使用して簡単にアセンブルできることに注意してください。



実行可能ファイルをターゲットにコピーして確認します。



 [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
      
      









実際、彼女はこのクロスコンパイルです。



UPD: growswsのコメントに従ってツールチェーンの情報を修正。



All Articles