2020年12月05日

STM32をClang/LLVMでコンパイルした時のメモ 2020/12

STM32をClang/LLVMでコンパイルした時のメモ 2020/12
はじまり
    Clang/LLVMを使ったARMマイコン(主にSTM32)のコンパイルは、かなり前からできることは知っていたが、
    今回初めてトライしたところ意外と迷走してしまったのでメモ。
経緯
    Clang/LLVMがARMマイコンで使えると知ってから何年も経つので
    Windows上でインストールからコンパイルまでアッという間にできるかと思ったら、
    ネット上にほとんど情報(注1)がなくて悶絶しました。orz
armclang
    商用で有料のMDK ARMは、v6.0からそれまでのコンパイラ(armcc)を捨ててClang/LLVMに移行(注2)した様です。
    それほどメリットがあるというこでしょうか。でかすね。:D (注5)
    最近使ってないmbed系のコンパイラもarmclang(Clang/LLVM)になっているみたい。
実は簡単にコンパイルできた。orz orz
    迷走から脱出するきっかけとなったページ
        https://github.com/RIOT-OS/RIOT/issues/8356
        「簡単な対策はリンク時もgccを使うんだよ」の記述。この一文がキモでした。
        RTOSのRIOTもそのうち試してみよう。

    結構迷走しましたが、
    分かってしまえば、かなり簡単にコンパイルできてしまいしました。

    「これを 最初に教えてくれよ!」って感じです。 xD

(注0)
Clang/LLVM コンパイルとリンクの概要
    これを先に分かっておくと楽。
    現状(2020/12)、Clang/LLVMは組み込みARM(eabi)用のコンパイル済みC(++)言語用 標準ライブラリ群+α(注3)を持っていない。
    従ってそれらは、
    (1) arm-none-eabi-*ツール群に付属のライブラリをリンクして使う。
    (2) C(++)言語ソースのコンパイル時に限り「clang(++)」を使い、
           あと(アセンブル(*.s)、リンク、bin/hex/lstファイルの生成)は全部arm-none-eabi-*ツール群に任せる。(注6)
    (3) 上記 arm-none-eabi-*ツール群のライブラリを使うために、clangに「ライブラリのヘッダファイル」の場所を教える必要がある。
    (4) clangを使う時は必ずオプションに「--target=arm-none-eabi」を加える。
        ただし、このオプションをarm-none-eabi-*ツール群に加えるとエラーで落ちるので注意(以下の-Ozもエラーになる)。

    その他:
        (a) 最適化オプションは -Ozを指定。 gccの-Osよりもサイズが小さくなるとか、ならないとか。:D らしい。
        (b) enumの取扱いがgccと異なるので「-fshort-enums」で合わせておく。(注4)
まとめ
    STM32CubeMXが吐き出したC言語ソースと「Makefile」を使ってコンパイルします。
    Makefileの改変部分は以下の様になる。
    ざっくり言うと、コンパイラをclangに替えた以外はほぼ無変更+α 程度でいける。と覚える。xD
Make変数Clang/LLVMを使う時arm-none-eabi-*を使う時
コンパイラCC = clangarm-none-eabi-gcc
リンカLD = 同右,
arm-none-eabi-gccを使う
(CCをLDに変更)
リンカはCCで代用(本来LDは未定義)
アセンブラAS = 同右 or (clang -x assembler-with-cpp)arm-none-eabi-gcc -x assembler-with-cpp
binutilsCP = 同右 or (llvm-objcopy)arm-none-eabi-objcopy
binutilsSZ = 同右 or (llvm-size)arm-none-eabi-size
追加オプションCFLAGS += --target=arm-none-eabi
-Oz
-fshort-enums
-
追加オプションC_INCLUDES += ライブラリヘッダの場所を指定-
上の表はごちゃごちゃしてるけど、よく見ると一番上の「clang」以外は全部「arm-none-eabi-*ツールそののまま」という意味です。 C言語ファイル(*.c)をコンパイルする時: コンパイラをclangに替えて「--target=arm-none-eabi」オプションを追加。 -Oz : 任意 -fshort-enums : 追加した方が無難。 インクルードファイルの場所を抽出する: arm-none-eabi-*がインストールされたフォルダ内の「ヘッダファイルフォルダ」を抽出します。 絶対パスでベタ書きでもOKだけど、以下は自動抽出する書き方。arm-none-eabi-*ツール群に実行パス(又は絶対パス)が通っていることが前提。
PREFIX = arm-none-eabi-
ARM_GNU_CC                = $(PREFIX)gcc
ARM_CORTEXM_SYSROOT       =  $(shell $(ARM_GNU_CC) $(MCU) -print-sysroot 2>&1)
C_INCLUDES += -I$(subst \,/,$(ARM_CORTEXM_SYSROOT))/include
C_INCLUDESがgccライブラリのヘッダファイル・パスなのでこれをコンパイル時に加える。 絶対パス指定の時は、 C_INCLUDES += -Ic:/[arm-gcc]/arm-none-eabi/include [arm-gcc]はarm-none-ebai-*ツールのトップフォルダ。 Linux/Debian(buster)32bit では絶対パス指定: Debian上のarm-none-eabi-gcc(gcc-arm-none-eabi)だと「-print-sysroot」が使えないので ヘッダファイル指定は絶対パスとしました。以下の3つを指定した。 というか、指定しないとエラーになった。orz C_INCLUDES += -I/usr/lib/gcc/arm-none-eabi-gcc/7.3.1/include C_INCLUDES += -I/usr/include C_INCLUDES += -I/usr/include/i386-linux-gnu Clang/LLVMのインストール(v11.0.0) !?: Clang/LLVMはWindowsパソコン用のものを使います。 以下の話はARMマイコンのコンパイルだけに使う(PC用のコンパイルはしない)なら気にしなくて良いものの PC上でもそれなり使える様にしたメモです。 Clang/LLVMのダウンロードは本家のこれがそうですが、これは無視します。(オイ というのも結局単独では完結しなくて * Visual Studio C++ツールが必要とか、 * MinGWのgccがデフォルトの場所にインストールされていることが必要とか、 デフォルトの場所ってどこよ ? c:/MinGW/らしいんだけど、なにそれ? 状態。 この場所にMinGWのgccがあることが前提条件。え? そこにはないし、 そこにインストールする単独のMinGW/gccってどこ? 終了。。。 orz 自分の場合: 利用中のMsys2/MinGWのgccがあるからいいやと思って 本家のClang/LLVMをインストールしてみたら、 迷走しました。orz 結局使えたものの、デフォルトの場所にMinGW/gccがないと面倒くさいことになるのであきらめました。 Msys2/MinGWでインストール OK 楽ちん: 自分の場合、前提条件はMsys2/MinGWです。 pacmanを使ってMinGW/gccはインストール済みなのでClang/LLVMは以下でインストール終了しました。 $ pacman -S mingw-w64-i686-clang mingw-w64-i686-lld mingw-w64-i686-lldb 64bit版は、 $ pacman -S mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-lldb Clang/LLVMは最新版のv11.0.0がインストールされました。 さ、最初にこれに気づけば。。。 orz orz ちみにgccはこれ mingw-w64-i686-gcc 32bit版 mingw-w64-x86_64-gcc 64bit版 Clang/LLVMのインストールサイズの違い: 本家版: 約1.4GB MinGW版: 約0.85GB 詳細不明なものの恐らくMinGW版はVisual Studioツール系が手薄なのかも。 Clang/LLVM: NUCLEO-F411RE コンパイル サンプルプロジェクト for STM32CubeMX (Makefile) Clang/arm-none-eabi-*ツール群に実行パスが通っていることが前提です。 トップフォルダにあるMakefileで $ make とすればデフォルトでClang/LLVMでコンパイルします。 $ make TOOLCHAIN= なら(TOOLCHAINには何も指定しない) arm-none-eabi-gccでコンパイルします。 ダウンロード: stm32f411cube_v1.1-2020012.7z old: stm32f411cube_v1.0-2020012.7z 標準ライブラリ(newlib-nano)のprintfを使うにはSrc/main.cの「xprintf(...);」を printfに変えればOK。
c:\stm32f411cube_v1.1-2020012> make
mkdir build
Compiling : clang : Src/main.c
Compiling : clang : Src/gpio.c
Compiling : clang : Src/stm32f4xx_it.c
Compiling : clang : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_gpio.c
Compiling : clang : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_rcc.c
Compiling : clang : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_utils.c
Compiling : clang : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_exti.c
Compiling : clang : Src/system_stm32f4xx.c
Compiling : clang : Src/usart.c
Compiling : clang : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usart.c
Compiling : clang : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_dma.c
Compiling : clang : lib/xprintf/xprintf.c
Compiling : clang : Src/syscalls.c
Assembling: arm-none-eabi-gcc -x assembler-with-cpp : startup_stm32f411xe.s
Linking : arm-none-eabi-gcc : build/stm32f411cube.elf
   text    data     bss     dec     hex filename
   3900      12      36    3948     f6c build/stm32f411cube.elf
c:\stm32f411cube_v1.1-2020012> make TOOLCHAIN=
mkdir build
Compiling : arm-none-eabi-gcc : Src/main.c
Compiling : arm-none-eabi-gcc : Src/gpio.c
Compiling : arm-none-eabi-gcc : Src/stm32f4xx_it.c
Compiling : arm-none-eabi-gcc : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_gpio.c
Compiling : arm-none-eabi-gcc : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_rcc.c
Compiling : arm-none-eabi-gcc : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_utils.c
Compiling : arm-none-eabi-gcc : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_exti.c
Compiling : arm-none-eabi-gcc : Src/system_stm32f4xx.c
Compiling : arm-none-eabi-gcc : Src/usart.c
Compiling : arm-none-eabi-gcc : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usart.c
Compiling : arm-none-eabi-gcc : Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_dma.c
Compiling : arm-none-eabi-gcc : lib/xprintf/xprintf.c
Compiling : arm-none-eabi-gcc : Src/syscalls.c
Assembling: arm-none-eabi-gcc -x assembler-with-cpp : startup_stm32f411xe.s
Linking :   arm-none-eabi-gcc : build/stm32f411cube.elf
   text    data     bss     dec     hex filename
   3896      12      36    3944     f68 build/stm32f411cube.elf
C言語の標準ライブラリを使わない場合のメモ 上はlibc.a系(標準ライブラリ)を使う前提の話で、もしそれらを使わない場合、 (1) 標準ライブラリ未使用なのでヘッダファイル指定もいらない? (2) 従って、arm-none-eabi-*ツールも不要? (3) リンクもclang(ld.lld)で可能? リンク自体はもともと「--target=arm-none-eabi」を指定すれば 上の方に書いたやり方でも可能かも。ダメでした。orz という話になるんだけど、ググって迷走中だった時の情報によると、 なぜかgccに固有のライブラリ「libgcc.a」(マクロ関数等々)が必要らしい。 clangがgccと互換を保つために「libgcc.a」内の関数と同じ名前の関数呼び出しを吐き出す様だ。 それと同等のものをClangで作れば完全に縁が切れる風な話が恐らくこれ。 https://interrupt.memfault.com/blog/arm-cortexm-with-llvm-clang#cross-compiling-with-clang libclang_rt.builtins.*.a という名前のものを作るらしい。 結局、作るならマルチlibにしないといけないので、やっぱり面倒くさそう感はある。 $ arm-none-eabi-gcc $(MCU) $(CFLAGS) -print-libgcc-file-name でlibgccのフルネームが分かるらしい。 これも参考 Armv6-m, Armv7-m and Armv7E-M targets https://www.derhaeg.be/doc/llvm8/HowToCrossCompileBuiltinsOnArm.html#armv6-m-armv7-m-and-armv7e-m-targets Compiler_rt: libgcc相当のソースコードはこれの様です。 https://github.com/llvm-mirror/compiler-rt/tree/master/lib/builtins 各種IDEで使う 試してないものの、上述の様にIDE上の設定にclangを指定すれば使えると思う。 ベアメタルARMをClangで使うページ: これらのページに最初にたどりつけばよかったのに。orz * 6年前(2014年)の記事だけど、ここにエッセンスが凝縮されていた。orz ARM Bare Metal Hello World: Comparing LLVM & ARM-GCC * Interface 2015年3月号 知らないとカッコ悪い!ARM用GCC&LLVM初体験 高性能でフリー!新時代コンパイラ入門 このPDFの図1にもgccツールを使えとちゃんと書いてあった。orz その他 AVRで試してみたメモ: clang + avr-gccも同様に簡単にコンパイルできるハズと思ってやってみたが、 以下の3つの問題に遭遇して現状では諦めました。 (1) PROGMEM,PSTR問題 AVR固有の仕様。 PROGMEMは以下の様な感じで置き換え可能だが、PSTRは難しそう。 #undef PROGMEM #define PROGMEM addrspace(1) 既存プログラムの書き換えは必至。 (2) libgcc.a問題 Armのlibgcc.aとは内容や関数名が違って、ここで未定義エラーが出る。 リンク時に最大で以下の4つの関数未定義エラーが出る。 https://github.com/avr-rust/rust-legacy-fork/issues/149 __ashlsi3 __lshrsi3 __divmodti4 __ashrsi3 avr-rustプロジェクトが上記関数をrust言語で書いたのを参考にC言語で 書き直せばひとまずOK. (3) 割り込み関数 定義問題 以下のアトリビュートに対応済みなはずだけどエラーになる。orz 無理にやるとclangがクラッシュした。orz orz __attribute__((interrupt)) avr-rustプロジェクトを参考にするしかないのかも。 (注0) 以下arm-none-eabi-gccをgccと書いたり、いろいろざっくりgccと書いたりします。:-) (注1) 簡単に分かる解説。日本語、英語含めて。 (注2) 5年くらい前? (注3) gccコンパイラ固有のlibgcc.a 。 (注4) arm-none-eabi-* のライブラリ群がこのオプションでコンパイルされている前提なんだけど、未確認。(^^; (注5) 巨大なメリットがあるならもっと情報が溢れていてもいいはず。。。 (注6) リンカ以外はClang/LLVMツールを使う手も可能。ここでは省略。
posted by Copyright (C) avrin All Rights Reserved. at 22:18| Comment(0) | ARM系 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。