Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Ubuntu 20.04でC言語、アセンブラのプログラムをQEMUで実行して
- IDEのEclipse内でお手軽デバッグできたので方法を書きます
- Ubuntu Server 20.04のRaspberry Pi 4でもできます。
- ここの例ではUbuntu 20.04のパッケージのgcc RISC-Vクロスコンパイラを使用してるので
- RISC-VのRV64GCでABIがilp64dの場合です
- 1.QEMU user modeのインストール
- sudo apt-get install qemu-user
- 2.gccのRISC-V RV64GC ilp64d linuxのクロスコンパイラをインストール
- sudo apt-get install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu
- 3.GDBのインストール
- sudo apt-get install gdb-multiarch
- 4.makeのインストール
- sudo apt-get install make
- 5.Eclipse IDE for C/C++ Developers 2020-12-Rのダウンロード
- https://www.eclipse.org/downloads/packages/release/2020-12/r/eclipse-ide-cc-developers
- wget https://ftp.jaist.ac.jp/pub/eclipse/technology/epp/downloads/release/2020-12/R/eclipse-cpp-2020-12-R-linux-gtk-x86_64.tar.gz
- Ubuntu Server 20.04のRaspberry Pi 4ではaarch64版をダウンロードしてください
- https://www.eclipse.org/downloads/packages/release/2020-12/r/eclipse-ide-cc-developers
- wget https://ftp.jaist.ac.jp/pub/eclipse/technology/epp/downloads/release/2020-12/R/eclipse-cpp-2020-12-R-linux-gtk-aarch64.tar.gz
- 6.OpenJDK JREのインストール
- sudo apt install default-jre
- 7.Eclipse IDE for C/C++ Developers 2020-12-Rの解凍
- sudo tar xf eclipse-cpp-2020-12-R-linux-gtk-x86_64.tar.gz -C /opt
- Ubuntu Server 20.04のRaspberry Pi 4では下記になります
- sudo tar xf eclipse-cpp-2020-12-R-linux-gtk-aarch64.tar.gz -C /opt
- 8.Eclipse IDE for C/C++ Developers 2020-12-Rの実行ファイルのシンボリックリンクを/usr/local/bin/に作成
- sudo ln -s /opt/eclipse/eclipse /usr/local/bin/
- 9.Eclipse IDE for C/C++ Developers 2020-12-Rの実行
- eclipse &
- Eclipse IDE Launcherの画面が開いてworkspaceのディレクトリを聞いてくるので
- /home/username/eclipse-workspaceにしてLaunchをクリック(おそらくデフォルトのままでいいはずです)
- ここでusernameは自分のユーザ名
- 10.プロジェクトの作成
- File→New→C/C++ Projectを選択
- 画面のAllを選択してC Managed Buildを選択してNext
- C Project画面が開くので
- Project name:にプロジェクト名を入力(ここでは作成例としてriscv_c_test01と入力してください)
- 左側のProject TypeでExecutableの中のEmpty Projectを選択
- 右側のToolchains:でCross GCCを選択してNext
- Select Configurations画面が開くので
- Debug、Release両方にチェックが付いた状態でNext
- Cross GCC Command画面が開くので
- Cross compiler prefix:にriscv64-linux-gnu-を入力
- Cross compiler path:に/bin/riscv64-linux-gnu-gccを入力してFinish
- Welcomの画面が出たままの場合はWelcomの画面を終了してください
- 11.Cソースコードの作成
- 画面左側のProject Explorerの画面の中の
- 作成したプロジェクト名(ここではriscv_c_test01)を右クリック
- ポップアップするメニューからNew→Source Fileをクリック
- New Source File画面が開くので
- Source folderにプロジェクト名が最初から入ってます(ここではriscv_c_test01)
- Source fileにCのソース名を入力(ここでは例としてc_test01.cにしてください)
- Template:にDefault C Source templateを選択してFinish
- ここの最後の方に掲載してあるc_test01.cの内容をコピペして
- 保存(メニューのFile→Save)してください
- 12.アセンブラソースコードの作成
- 画面左側のProject Explorerの画面の中の
- 作成したプロジェクト名(ここではriscv_c_test01)を右クリック
- ポップアップするメニューからNew→Source Fileをクリック
- New Source File画面が開くので
- Source folderにプロジェクト名が最初から入ってます(ここではc_test01)
- Source fileにアセンブラのソース名を入力(ここではc_test01-asm.S)
- アセンブラソースの拡張子は.asmにするか.S(大文字のSにする)
- 小文字の.sだと認識されない
- Template:に<None>を選択してFinish
- ここの最後の方に掲載してあるc_test01-asm.Sの内容をコピペして
- 保存(メニューのFile→Save)してください
- 13.ライブラリの追加
- sin関数を使っているのでgccのmathライブラリを登録します
- 画面左側のProject Explorerの画面の中の
- 作成したプロジェクト名(ここではriscv_c_test01)を右クリック
- ポップアップするメニューの一番下のPropertiesを選択します
- Properties for riscv_c_test01画面が開くので
- 画面左側でC/C++ General→Path and Symbolsクリックします
- Path and Symbols画面が開くのでConfigurationにDebugを選択します
- 画面中央のLibrariesタブをクリック
- 右側のAddボタンを押してFile:にmを入力してOK(mathライブラリの名前は"m"1文字だけです)
- 次に上のConfiguration:をReleaseにして同様にmを追加します
- 次に上のConfiguration:をDebugに戻してからApply and Closeを押します
- 14.ビルド
- 画面左側のProject Explorerの中のプロジェクト名(ここではriscv_c_test01)をクリックして選択状態にします
- ツールバーの左側端のBuild、Launch in 'Run'modeもしくはLaunch in 'Debug'mode、Stopと
- ボタンが3つあるところの右隣のLaunch 'Mode'をDebugにします
- メニューのProject→Build Projectを選択します
- するとビルドが開始されます
- ConsoleにBuild Finished. 0 errorsと出ればOKです
- エラーがあった場合はソースコードが何か間違っているので修正してください
- 15.Debugの準備
- Ubuntu 20.04のコマンドラインで設定ファイルなどを作成します。
- ホームディレクトリに.gdbinitを作成
- vi ~/.gdbinit
- 以下~/.gdbinitの内容
- set auto-load safe-path ~
- set confirm 0
- set pagination 0
- set architecture riscv:rv64
- set sysroot /usr/riscv64-linux-gnu
- プロジェクトのディレクトリにqemuを起動するためのスクリプトを作成(debug用)
- ここで~/eclipse-workspace/はワークスペースのディレクトリ
- ~/eclipse-workspace/riscv_c_test01/はプロジェクトのディレクトリです
- vi ~/eclipse-workspace/riscv_c_test01/qemu.sh
- 以下~/eclipse-workspace/riscv_c_test01/qemu.shの内容
- #!/bin/bash
- /bin/qemu-riscv64 -L /usr/riscv64-linux-gnu/ -g 10000 ~/eclipse-workspace/riscv_c_test01/Debug/riscv_c_test01 &
- 作成したqemu.shに実行許可を与える
- chmod u+x ~/eclipse-workspace/riscv_c_test01/qemu.sh
- プロジェクトのディレクトリにqemuを起動するためのスクリプトを作成(実行用)
- ここで~/eclipse-workspace/はワークスペースのディレクトリ
- ~/eclipse-workspace/riscv_c_test01/はプロジェクトのディレクトリです
- vi ~/eclipse-workspace/riscv_c_test01/qemu-run.sh
- 以下~/eclipse-workspace/riscv_c_test01/qemu-run.shの内容
- #!/bin/bash
- /bin/qemu-riscv64 -L /usr/riscv64-linux-gnu/ ~/eclipse-workspace/riscv_c_test01/Debug/riscv_c_test01 &
- 作成したqemu.shに実行許可を与える
- chmod u+x ~/eclipse-workspace/riscv_c_test01/qemu-run.sh
- 16.ビルドしたプログラムの実行
- この段階でビルドが成功していて、プログラムが正常に動作するなら
- ビルドしたプログラムを実行可能です
- Ubuntu 20.04のコマンドラインから下記を入力します
- ~/eclipse-workspace/riscv_c_test01/qemu-run.sh
- もしくは
- bash ~/eclipse-workspace/riscv_c_test01/qemu-run.sh
- 17.Debugの設定
- 画面左側のProject Explorerの画面の中の
- 作成したプロジェクト名(ここではriscv_c_test01)をクリックして選択状態にします
- ツールバーの左側端のBuild、Launch in 'Run'modeもしくは、Launch in 'Debug'mode、Stopと
- ボタンが3つあるところの右隣のLaunch 'Mode'をDebugにします
- メニューのRun→Debug Configurationsをクリック
- Debug Configurations画面が開きます
- 画面左側のGDB Remote Applicationをダブルクリックする
- 新しくriscv_c_test01 Debugが作成されて右側の画面に自動的に設定が入力されます
- 中段あたりのC/C++ Application:に何も入力されてない場合はビルドに失敗してます
- 一度GDB Remote Applicationの中のriscv_c_test01 Debugを
- 右クリックしてDeleteをクリックして削除してビルドからやり直してください
- きちんとビルドされていればC/C++ Application:にDebug/riscv_c_test01が入っているはずです
- 画面のMainタブの一番下、Using GDB(DSF)Automatic Remote Debugging LauncherのSelect Other...をクリック
- Select Prefered Launcher画面が開きます
- Use configuration specific settingsをチェック
- GDB(DSF) Manual Remote Debugging Launcherを選択してOK
- DebuggerタブのDebugger OptionsのMainタブのGDB debuggerに/bin/gdb-multiarchを入力
- GDB command fileに~/.gdbinit
- Non-stop modeのチェックが付いていたら外す
- 今度はDebugger OptionsのConnectionタブを開き
- Type:にTCP、Host name or IP address:にlocalhost、Port numberに10000を入力
- Applyを押します
- まだDebugは押さずにそのままの状態にしてください。
- 18.QEMUを実行
- Ubuntu 20.04のコマンドラインから先ほど作成したqemu.shを実行します
- ~/eclipse-workspace/riscv_c_test01/qemu.sh
- もしくは
- bash ~/eclipse-workspace/riscv_c_test01/qemu.sh
- するとqemuが起動して10000ポートの接続待ち状態になります
- Ubuntu 20.04のコマンドラインから下記を入力することでqemu.shが実行中になってるのがわかります
- ps -ef | grep qemu-riscv64
- /bin/qemu-riscv64 -L /usr/riscv64-linux-gnu/ -g 10000 /home/usershin/eclipse-workspace/riscv_c_test01/Debug/riscv_c_test01
- これが起動してればOKです
- grep --color=auto qemu-riscv64
- これだけだった場合はQEMUが起動してません
- 19.Debugを開始
- さきほどのEclipseのDebug Configurationsの画面に戻って、画面内の右下端のDebugボタンを押します
- Confirm Perspective Switchという画面が開いた場合はSwitchを押します
- しばらくするとデバッグが始まり、int mainの中の最初の命令が記述されている行で止まります
- この状態でデバッグを行います
- プログラムの標準出力はqemu.shを実行したときのコマンドラインに出力されます
- ステップイン F5もしくはRun→Step Into
- ステップオーバー F6もしくはRun→Step Over
- 実行再開 F8もしくはRun→Resume
- カーソル行まで実行 CTRL+RもしくはRun→Run to Line
- ブレークポイント ブレークポイントを設定したい行にカーソルを置いてShift+CTRL+bまたはRun→Toggle Breakpoint
- ブレークポイントの解除 解除したいブレークポイントの行にカーソルを置いてShift+CTRL+bまたはRun→Toggle Breakpoint
- 変数の確認 Window→Show View→Variablesまたは変数名にカーソルを合わせると変数の内容がポップアップする
- アセンブラデバッグ
- Window→Show View→Registersでレジスタービューの表示
- Window→Show View→Disassemblyで逆アセンブラビューの表示
- アセンブラレベルで1命令ずつ実行したい場所の直前まで進めます
- (アセンブラで書いた関数をアセンブラレベルで1命令ずつデバッグするにはアセンブラで書いた関数の上にカーソルが来たときに
- メニューのRun→Instruction Stepping ModeのチェックをつけてInstruction Stepping Modeにしてから
- F5もしくはRun→Step Intoで実行していきます)
- そこでメニューのRun→Instruction Stepping Modeのチェックをつけると1命令ずつ実行するようになります
- Cのソースコードデバッグに戻るときはRun→Instruction Stepping Modeをもう1度選択してInstruction Stepping Modeのチェックを外します
- インラインアセンブラの場合はインラインアセンブラの直前で止めてから
- メニューのRun→Instruction Stepping Modeを選択してInstruction Stepping Modeにして
- インラインアセンブラを抜けるときに再度Run→Instruction Stepping Modeを選択してInstruction Stepping Modeから抜けます
- プログラムが終了して実行が終わるとQEMUが終了してEclipseのデバッグも終了します
- デバッグ実行時には毎回Ubuntu 20.04のコマンドラインから
- ~/eclipse-workspace/riscv_c_test01/qemu.sh
- を実行して、
- 画面左側のProject Explorerの画面の中の
- 作成したプロジェクト名(ここではriscv_c_test01)をクリックして選択状態にして
- Launch modeがDebugになってることを確認し(RunになってたらDebugにする)
- メニューのRun→Debug Configurationsを開いて
- C/C++ Remote Applicationの中のプロジェクト名+Debug
- (ここではriscv_c_test01 Debug)が選択された状態で画面右下端のDebugボタンを押します。
- ソースコード編集画面に戻るにはツールバーの画面の右端のボタンの右から2番目のC/C++のボタンを押します
- デバッグが開始しない場合はQEMUの動作がおかしくなってるかもしれません
- killコマンドでqemu.shのプロセスを強制終了して18.QEMUの実行からやり直してください
- killコマンドの使い方はネットで調べてください
- テスト用プログラム
- 以下c_test01.c
- extern int test_asm_sin01(int start, int end, int step);
- int test_asm_sin02(int start, int end, int step);
- #include <stdio.h>
- int func_asm_add(int a, int b);
- int main(int argc, char *argv[]) {
- int a, i, j;
- for (j = 1; j <= 3; j++) {
- for (i = 1; i <= 3; i++) {
- a = func_asm_add(i, j);
- printf("%d + %d = %d\n", i, j, a);
- }
- }
- test_asm_sin01(0, 360, 10);
- test_asm_sin02(0, 360, 10);
- return 0;
- }
- void print01(int i, double a) {
- printf("sin(%3d) = %.15f", i, a);
- printf("\n");
- }
- int func_asm_add(int a, int b){
- int ret;
- asm volatile (
- "sext.w a0, %1\n\t"
- "sext.w a1, %2\n\t"
- "add %0, a0, a1\n\t"
- :"=r"(ret)
- :"r"(a), "r"(b)
- :"x1", "x10", "x11"
- );
- return ret;
- }
- int test_asm_sin02(int start, int end, int step) {
- double pi = 3.141592653589793;
- double num01 = 180.0;
- double size = 20.0;
- int column = 50;
- int ret;
- asm volatile (
- // 関数のパラメーターはa0からa7に格納される
- // 後でbgeで比較するので64bitに符号拡張する
- "sext.w s1, %1\n\t"
- "mv s2, %2\n\t"
- "mv s3, %3\n\t"
- "fmv.d.x fa0, %4\n\t"
- "fmv.d.x fa1, %5\n\t"
- "fdiv.d fs0, fa0, fa1\n\t"
- "fmv.d.x fs1, %6\n\t"
- "mv s4, %7\n\t"
- "loop01:\n\t"
- // s1 > s2なのでs2に1を足した値をbgeで比較する
- // addwを実行すると64bitレジスタの上位32bitは符号拡張される
- "addw a0, s2, 1\n\t"
- // bgeは符号付き64bitで比較する
- "bge s1, a0, jump01\n\t"
- "fcvt.d.w fa0, s1\n\t"
- "fmul.d fa0, fs0, fa0\n\t"
- // sin関数の呼び出し
- "call sin@plt\n\t"
- "fmv.d fs2, fa0\n\t"
- // s5 = (int)(size * sin() + column / 2)
- "fmul.d fa0, fs2, fs1\n\t"
- "li a0, 2\n\t"
- "fcvt.d.w fa1, a0\n\t"
- "fcvt.d.w fa2, s4\n\t"
- "fdiv.d fa1, fa2, fa1\n\t"
- "fadd.d fa0, fa0, fa1\n\t"
- "fcvt.w.d s5, fa0\n\t"
- // if (s5 > column) s5 = column;
- "addiw a0, s4, 1\n\t"
- "blt s5, a0, jump04\n\t"
- "mv s5, s4\n\t"
- "jump04:\n\t"
- // if (s5 < 0) s5 = 0;
- "bge s5, zero, jump05\n\t"
- "mv s5, zero\n\t"
- "jump05:\n\t"
- // スペースをs5回分表示
- "mv s6, s5\n\t"
- "loop02:\n\t"
- "addiw s6, s6, -1\n\t"
- "blt s6, zero, jump02\n\t"
- "li a0, ' '\n\t"
- "call putchar@plt\n\t"
- "j loop02\n\t"
- "jump02:\n\t"
- // '*'を表示
- "li a0, '*'\n\t"
- "call putchar@plt\n\t"
- // スペースをcolumn-s5回分表示
- "subw s6, s4, s5\n\t"
- "loop03:\n\t"
- "addiw s6, s6, -1\n\t"
- "blt s6, zero, jump03\n\t"
- "li a0, ' '\n\t"
- "call putchar@plt\n\t"
- "j loop03\n\t"
- "jump03:\n\t"
- "fmv.d fa0, fs2\n\t"
- "mv a0, s1\n\t"
- "call print01\n\t"
- "addw s1, s1, s3\n\t"
- "j loop01\n\t"
- "jump01:\n\t"
- "mv %0, zero\n\t"
- :"=r"(ret)
- :"r"(start), "r"(end), "r"(step), "r"(pi), "r"(num01), "r"(size), "r"(column)
- :"x1", "x10", "x11", "x12", "x9", "x18", "x19", "x20", "x21", "x22", "f10", "f11", "f12", "f8", "f9", "f18", "memory"
- );
- return ret;
- }
- 以下c_test01-asm.S
- # test_asm_sin01(int start, int end, int step);
- .text
- .align 1
- .globl test_asm_sin01
- test_asm_sin01:
- # 保存領域確保
- # spの値は常に16バイト境界にする
- addi sp, sp, -80
- # 戻りアドレスを退避
- sd ra, (sp)
- # 保存レジスタの値を退避
- sd s1, 8(sp)
- sd s2, 16(sp)
- sd s3, 24(sp)
- sd s4, 32(sp)
- sd s5, 40(sp)
- sd s6, 48(sp)
- fsd fs0, 56(sp)
- fsd fs1, 64(sp)
- fsd fs2, 72(sp)
- # 関数のパラメーターはa0からa7に格納される
- # 後でbgeで比較するので64bitに符号拡張する
- sext.w s1, a0
- mv s2, a1
- mv s3, a2
- la a0, pi
- fld fa0, (a0)
- la a0, num01
- fld fa1, (a0)
- fdiv.d fs0, fa0, fa1
- la a0, size
- fld fs1, (a0)
- la a0, column
- lw s4, (a0)
- loop01:
- # s1 > s2なのでs2に1を足した値をbgeで比較する
- # addwを実行すると64bitレジスタの上位32bitは符号拡張される
- addw a0, s2, 1
- # bgeは符号付き64bitで比較する
- bge s1, a0, jump01
- fcvt.d.w fa0, s1
- fmul.d fa0, fs0, fa0
- # sin関数の呼び出し
- call sin@plt
- fmv.d fs2, fa0
- # s5 = (int)(size * sin() + column / 2)
- fmul.d fa0, fs2, fs1
- li a0, 2
- fcvt.d.w fa1, a0
- fcvt.d.w fa2, s4
- fdiv.d fa1, fa2, fa1
- fadd.d fa0, fa0, fa1
- fcvt.w.d s5, fa0
- # if (s5 > column) s5 = column;
- addiw a0, s4, 1
- blt s5, a0, jump04
- mv s5, s4
- jump04:
- # if (s5 < 0) s5 = 0;
- bge s5, zero, jump05
- mv s5, zero
- jump05:
- # スペースをs5回分表示
- mv s6, s5
- loop02:
- addiw s6, s6, -1
- blt s6, zero, jump02
- li a0, ' '
- call putchar@plt
- j loop02
- jump02:
- # '*'を表示
- li a0, '*'
- call putchar@plt
- # スペースをcolumn-s5回分表示
- subw s6, s4, s5
- loop03:
- addiw s6, s6, -1
- blt s6, zero, jump03
- li a0, ' '
- call putchar@plt
- j loop03
- jump03:
- fmv.d fa0, fs2
- mv a0, s1
- call print01
- addw s1, s1, s3
- j loop01
- jump01:
- # 戻り値を設定
- mv a0, zero
- # 保存レジスタの値を復帰
- ld s1, 8(sp)
- ld s2, 16(sp)
- ld s3, 24(sp)
- ld s4, 32(sp)
- ld s5, 40(sp)
- ld s6, 48(sp)
- fld fs0, 56(sp)
- fld fs1, 64(sp)
- fld fs2, 72(sp)
- # 戻りアドレスを復帰
- ld ra, (sp)
- # 保存領域開放
- addi sp, sp, 80
- jr (ra)
- .data
- .align 3
- pi: .double 3.141592653589793
- num01: .double 180.0
- size: .double 20.0
- column: .word 50
- .end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement