みつきんのメモ

組み込みエンジニアです。Interface誌で「My オリジナルLinuxの作り方」連載中

Pico ベアメタルをClang(LLVM)でビルド

はじめに

ここまで作ってきたベアメタルのLチカはGCCでビルドしてきたが、それをclangでビルドしてみる。

clang-10のインストール

Ubuntu 20.04環境でClangをインストールする。バージョンは10。

$ sudo apt install -y clang-10 lld-10 make

Makefile

ベースはLチカのFLASH + コンパイル済みBoot Stage2バージョン

まずはMakefileを下記のように変更する。

LLVM_OPT = --target=armv6-m-unknown-none-eabi -mcpu=cortex-m0plus 

ASMOPT = $(LLVM_OPT) -c -g
COPT = $(LLVM_OPT) -c -mthumb -ffreestanding -g -O0
LOPT = $(LLVM_OPT) -nostdlib

all: led.uf2

clean:
    rm -f *.o
    rm -f *.elf
    rm -f *.list
    rm -f *.uf2
    rm -f *~

start.o: start.S
    clang-10 $(ASMOPT) start.S -o start.o

boot2.o: boot2/bs2_default_padded_checksummed.S
    clang-10 $(ASMOPT) boot2/bs2_default_padded_checksummed.S -o boot2.o

main.o: main.c
    clang-10 $(COPT) -fpic -mthumb -c main.c -o main.o

led.elf: start.o boot2.o main.o
    clang-10 -fuse-ld=lld $(LOPT) start.o boot2.o main.o -T memmap.ld  -o led.elf 

led.uf2: led.elf
    elf2uf2 led.elf led.uf2

GCC環境とのコマンドは下記のように異なっている。

機能 GCC Clang
アセンブラ as clang
コンパイラ gcc clang
リンカ ld clang

clangは全ての機能においてclangから呼び出されるようになっている

ポイントはアセンブルコンパイルの時にリンクまでしたくない場合は-cをつけること。

clangではクロスコンパイルのためにそれぞれのバイナリがあるのではなくLLVMのバックエンドを切り替えるだけなので、すべてオプションで指定する。

基本的にはRaspberry Pi Pico向けの場合下記のようになる。

LLVM_OPT = --target=armv6-m-unknown-none-eabi -mcpu=cortex-m0plus 

それぞれの機能で渡したいオプションが違うので下記のようにする。

ASMOPT = $(LLVM_OPT) -c -g
COPT = $(LLVM_OPT) -c -mthumb -ffreestanding -g -O0
LOPT = $(LLVM_OPT) -nostdlib

リンカスクリプト

そのままでは下記のようなエラーになる。

ld.lld: error: no memory region specified for section '.ARM.exidx'
clang: error: ld.lld command failed with exit code 1 (use -v to see invocation)
make: *** [Makefile:26: led.elf] エラー 1

これは.ARM.exidxセクションが無いというエラー。

前にリンカスクリプトを眺めた時はnewlibを使用する時に必要になるセクションらしい。とのことだったが、LLVMでバイナリを吐き出す場合にも必要になるようだ。

MEMORY
{
    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
    RAM(rwx) : ORIGIN =  0x20000000, LENGTH = 256k
}

ENTRY(reset)

SECTIONS
{
    /* 
       boot2 section is for embed the precompiled boot2.
       this code from memmap_default.ld in pico-sdk
    */
    .boot2 : {
        __boot2_start__ = .;
        KEEP(*(.boot2))
        __boot2_end__ = .;
    } > FLASH

    ASSERT(__boot2_end__ - __boot2_start__ == 256,
        "ERROR: Pico second stage bootloader must be 256 bytes in size")
    .text : {
        KEEP (*(.vectors))
        KEEP(*(.text*))
    } > FLASH

    .ARM.exidx :
    {
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    } > FLASH
}

これでエラーは回避できるようになった。

$ make
clang-10 -fuse-ld=lld --target=armv6-m-unknown-none-eabi -mcpu=cortex-m0plus  -nostdlib start.o boot2.o main.o -T memmap.ld  -o led.elf 
elf2uf2 led.elf led.uf2

まとめ

アセンブラコンパイラ、リンカの呼び出しが全てclangだとは思わなかった。

.ARM.exidxセクションは必要になる。