本日重點: 裸機跨平台要怎麼修改 Makefile
因為 sha256 要改成用 CryptoCell 310 和 CryptoCell 312 計算,我想先在 Renode 上模擬,所以今天先說如何將不同平台的模擬整合進 GitHub CI
裸機有兩種型式,一種是完全的裸機,連基本的 memcpy 都自己刻,第二種是連結到一個最小化的 C library,這樣 memcpy 這種就不用自己重寫一個實作。本系列走的是第二種,用的是 libc_nano.a。
為了連結到 libc_nano.a,而且 不動態連結 glibc,Makefile 需要為此調整 compile-time 和 link-time 的參數,以下是幾個相關參數。
除此之外,也要增加 CC310/CC312 的路徑,讓 sha256 能夠在 link-time 直接切換不同 CryptoCell 的 implementation
-ffreestanding
-specs=nano.specs -nostartfiles -Wl,--start-group -lc -lgcc -Wl,--end-group -Wl,-u,memcpy -Wl,-u,__aeabi_memcpy
然後我們試試在 Makefile 裡用 nm 檢查 ELF file,看看 memcpy 是不是真的有 link 到一份 implementation
$(NM) $(ELF) | grep -E 'memcpy|__aeabi_memcpy'
注意看每一個 function name 前面的 T,T 代表 text section,表示有一份 implementation 存在於 text section
nm sign_nrf52840.elf | grep -E 'memcpy|__aeabi_memcpy'
000014f8 T __aeabi_memcpy
000014f8 T __aeabi_memcpy4
000014f8 T __aeabi_memcpy8
000014fc T memcpy
nm sign_nrf5340.elf | grep -E 'memcpy|__aeabi_memcpy'
0000114c T __aeabi_memcpy
0000114c T __aeabi_memcpy4
0000114c T __aeabi_memcpy8
00001150 T memcpy
如果是 U(Undefined,代表 implementation 不存在,會造成連結失敗.
雖然 x86 的 memcpy 的連結是 U(Undefined),不過其實 x86 的環境不一樣,因為我在 x86 並不打算 bare-metal,所以讓他動態連結 glibc
nm sign_x86.elf | grep -E 'memcpy|__aeabi_memcpy'
U __aeabi_memcpy
U __memcpy_chk@GLIBC_2.3.4
U memcpy@GLIBC_2.14
還有另一個方法能檢查連結的結果,就是在 LDFLAGS 加 -Wl,-Map
,然後在 map file 裡檢查
-Wl,-Map,sign_nrf52840.map
如果在 sign_nrf52840.map 看到 libc_a-aeabi_memcpy.o
,表示 memcpy 真的有連結到 implementation
/opt/hostedtoolcache/arm-gnu-toolchain-13.2.rel1/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m/nofp/libc_nano.a(libc_a-aeabi_memcpy.o)
(__aeabi_memcpy)
/opt/hostedtoolcache/arm-gnu-toolchain-13.2.rel1/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m/nofp/libc_nano.a(libc_a-memcpy-stub.o)
/tmp/cc3xD4qJ.o (memcpy)
為了能連結到 cc310/cc312,所以 ci.yml 要增加這幾行
env:
NRFXLIB_DIR: ${{ github.workspace }}/third_party/nrfxlib
NRFXLIB_TAG: v3.1-branch
steps:
- name: Cache nrfxlib
id: cache-nrfxlib
uses: actions/cache@v4
with:
path: ${{ env.NRFXLIB_DIR }}
key: nrfxlib-${{ env.NRFXLIB_TAG }}
- name: Fetch nrfxlib (cache miss only)
if: steps.cache-nrfxlib.outputs.cache-hit != 'true'
uses: actions/checkout@v4
with:
repository: nrfconnect/sdk-nrfxlib
ref: ${{ env.NRFXLIB_TAG }}
path: ${{ env.NRFXLIB_DIR }}
fetch-depth: 1
這個也要加到 LDFLAGS
$(NRFXLIB_DIR)/crypto/nrf_cc310_bl/lib/cortex-m4/soft-float/libnrf_cc310_bl_0.9.12.a -Wl,--no-whole-archive
NRFXLIB_DIR 也可以透過 ci.yml 指定, 我用的是 ?= 而不是 :=,所以,如果 ci.yml 沒有指定 NRFXLIB_DIR,那 Makefile 就會對其設一個初始值 third_party/nrfxlib。然而,如果 ci.yml 有指定,那麼 Makefile 的這一行就不會生效
NRFXLIB_DIR ?= $(abspath third_party/nrfxlib)
ci.yml 要注意的是這 3 行,這樣做的好處是 ci.yml 可以將自己尋找 CC310/CC312 的 header files,cc310_bl static libs 的路徑 (NRFXLIB_DIR) 提供給 Makefile,然後 Makefile 和 ci.yml 就能對於 NRFXLIB_DIR 的路徑有共識
NRFXLIB_DIR: ${{ github.workspace }}/third_party/nrfxlib
...
make TARGET=nrf52840 NRFXLIB_DIR="${NRFXLIB_DIR}"
...
make TARGET=nrf5340 NRFXLIB_DIR="${NRFXLIB_DIR}"
ci.yml 的修改不是本系列的主題,所以我就不在 ci.yml 上有過多的說明,有興趣的朋友可以看這裡 check nrfxlib present
, Find cc310_bl static libs
和 Find CC310/CC312 C/H files
CFLAGS 還要加上,因為是在 renode 上模擬,所以用 -mfloat-abi=soft
-mfloat-abi=soft -mfpu=fpv4-sp-d16
如果 CC310 連結成功,sign_nrf52840.map 會看到
/home/runner/work/bare-simple-sha2-128/bare-simple-sha2-128/third_party/nrfxlib/crypto/nrf_cc310_bl/lib/cortex-m4/soft-float/libnrf_cc310_bl_0.9.12.a(nrf_cc310_bl_hash.c.obj)
(--whole-archive)
/home/runner/work/bare-simple-sha2-128/bare-simple-sha2-128/third_party/nrfxlib/crypto/nrf_cc310_bl/lib/cortex-m4/soft-float/libnrf_cc310_bl_0.9.12.a(nrf_cc310_bl_hash_sha256.c.obj)
(--whole-archive)
/home/runner/work/bare-simple-sha2-128/bare-simple-sha2-128/third_party/nrfxlib/crypto/nrf_cc310_bl/lib/cortex-m4/soft-float/libnrf_cc310_bl_0.9.12.a(nrf_cc310_bl_init.c.obj)
(--whole-archive)
我想在 GitHub 的 Actions 就能看到 nrf 的 crypto 是不是真實存在,也想知道 CC310/CC312 的 header file 和 cc310_bl static libs 各自放在什麼地方,所以我在 ci.yml 加了一些 step name,執行後會是這個結果
這些資訊對於 compile-time 和 link-time 發生的錯誤很有幫助,能讓我在第一時間就確定到底是參數的問題,還是檔案自始不存在。
Makefile 對於 nrf52840 要指定 CFLAGS 和 LDFLAGS,總結如下
...
ifeq ($(TARGET),nrf52840)
CFLAGS := -mcpu=cortex-m4 -mthumb -mfloat-abi=soft -mfpu=fpv4-sp-d16 -O2 -ffreestanding -Wall -Wextra -DCRYPTO_BACKEND_CC310_BL -Wl,--gc-sections
LDFLAGS := -T $(LDS) -Wl,-Map,sign_nrf52840.map -Wl,--whole-archive $(NRFXLIB_DIR)/crypto/nrf_cc310_bl/lib/cortex-m4/soft-float/libnrf_cc310_bl_0.9.12.a -Wl,--no-whole-archive -specs=nano.specs -nostartfiles
...
endif
最後是執行 Renode,參考 Makefile
RENODE_IMG = antmicro/renode@sha256:1a4879e047b22827205f4fb1d1e5474d5fdce17eb69f22726ab1afed479f5e22
WORKDIR ?= $(shell pwd)
RESC ?= run_sign.resc
ci-run-nrf52840: $(ELF) $(RESC)
docker run --rm -v "$(WORKDIR):/w" $(RENODE_IMG) \
sh -lc 'cd /w && renode --console --disable-xwt -e "set ansi false; include @$(RESC); sleep 2; q"' | sed 's/\x1B\[[0-9;]*[A-Za-z]//g'
run_sign.resc
是 renode 的 script,這是用於 nrf52840,如果是 nrf5340 就要另外寫一個,本系列會在之後提到 nrf5340 的 repl 怎麼產生,目前先用 nrf52840.repl
set ansi false
mach create "nrf52840_sign"
machine LoadPlatformDescription @platforms/cpus/nrf52840.repl
sysbus LoadELF @sign_nrf52840.elf
showAnalyzer sysbus.uart0
start