Open4

手動Arduino(Adafruit nRF52編)

okuokuokuoku

SWDが無ければ即死だった。。SWDデバッグは↓に書いた。

https://zenn.dev/okuoku/scraps/44f8a7684e47f8

"手動Arduino" とは、Arduino IDEを使わずにIDEをビルドすることでArduinoでないと使えないマイコンボードを普通の開発環境で活用しましょうという取り組み。だってESP32もRaspberry Pi PicoもビルドシステムがCMakeなのに他がビルドシステム違うとか超不便じゃん。。

とりあえずリポジトリは:

https://github.com/okuoku/nrftest

そのうち、ESP-IDFとかSpresenseのような他の奴と纏めたリポジトリを用意したい。

前回

前回はWio TerminalのSAMD51の奴をやった。これはAdafruit nRF52の元になった https://github.com/sandeepmistry/arduino-nRF5 の元になっているので、これらは親戚同士と言える。

https://zenn.dev/okuoku/scraps/22aadef55e2c75

okuokuokuoku

そもそも起動しない

普通にcrosstool-NGでビルドしたnewlibでビルドすると、全く動作しなかった。

SWDで止まったところを観察してみると、 Newlibのスタートアップで bkpt AngelSWI でfaultしていた。...これデフォルトで有効なの。。?

とりあえず、gdbを繋いで c を連射したらちゃんと実行されたのでとりあえずコレで。。 ARM_RDI_MONITOR がdefineされた経緯を調べる必要があるな。。

newlib/configure.host
# If newlib is supplying syscalls, select which debug protocol is being used.
# ARM_RDP_MONITOR selects the Demon monitor.
# ARM_RDI_MONITOR selects the Angel monitor.
# If neither are defined, then hard coded defaults will be used
# to create the program's environment.
# If --disable-newlib-supplied-syscalls is specified, then the end-user
# may specify the protocol via gcc spec files supplied by libgloss.
	if [ "x${newlib_may_supply_syscalls}" = "xyes" ] ; then
#         newlib_cflags="${newlib_cflags} -DARM_RDP_MONITOR"
	  newlib_cflags="${newlib_cflags} -DARM_RDI_MONITOR"
	fi
	;;

うーん。。やっぱりcrosstool-NGでsyscallを外し忘れたからかな。。

okuokuokuoku

USB-CDCのUARTが初期化されない

これも gdb で追っていくと

(gdb) s
Adafruit_USBD_CDC::isValid (this=0x20007620 <Serial>)
    at ../Adafruit_nRF52_Arduino/libraries/Adafruit_TinyUSB_Arduino/src/arduino/Adafruit_USBD_CDC.h:85
85        bool isValid(void) { return _instance != INVALID_INSTANCE; }

この isValid が false で、そもそも初期化されていないことがわかった。

...どうもTinyUSB用のAPIがweakシンボルになっているようで、リンカが正常に参照を拾わず、 実行自体が省略 されてしまっているようだ。

Adafruit_TinyUSB_API.h
// Called by core/sketch to initialize usb device hardware and stack
// This also initialize Serial as CDC device
void TinyUSB_Device_Init(uint8_t rhport) __attribute__((weak));

// Called by core/sketch to handle device event
void TinyUSB_Device_Task(void) __attribute__((weak));

// Called by core/sketch to flush write on CDC
void TinyUSB_Device_FlushCDC(void) __attribute__((weak));

というわけで --whole-archive を指定して全部の .o をリンク対象に含めて未使用関数の削除は --gc-sections に任せることにした。

https://github.com/okuoku/nrftest/commit/6c2677d28ab2c46b5b876ce933b505ca4da60ac3

そもそもリンカがコードを変更できるの!?

C言語としてはnull dereferenceは未定義挙動なので何が起きても文句は言えない。けどもうちょっと安全に失敗しても良い気がする。。call命令がcall以外になってるわけで。。

例えば:

void NEVERLAND(void) __attribute__((weak));

int
main(int ac,char** av){
    NEVERLAND();
    return 0;
}

のようなコードがあったとして、普通に .o を出力させると、

$ arm-none-eabihf-objdump -S check.o

check.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <main>:
   0:   b580            push    {r7, lr}
   2:   b082            sub     sp, #8
   4:   af00            add     r7, sp, #0
   6:   6078            str     r0, [r7, #4]
   8:   6039            str     r1, [r7, #0]
   a:   f7ff fffe       bl      0 <NEVERLAND> ;; ★ 未解決のシンボルに対する bl 
   e:   2300            movs    r3, #0
  10:   4618            mov     r0, r3
  12:   3708            adds    r7, #8
  14:   46bd            mov     sp, r7
  16:   bd80            pop     {r7, pc}

のように、ブランチ命令 bl が生成され、関数を呼出したい気持ちは逆アセンブリに現われている。しかし、これをリンクすると:

00008184 <main>:
    8184:       b580            push    {r7, lr}
    8186:       b082            sub     sp, #8
    8188:       af00            add     r7, sp, #0
    818a:       6078            str     r0, [r7, #4]
    818c:       6039            str     r1, [r7, #0]
    818e:       f3af 8000       nop.w ;; ★ nop になっている
    8192:       2300            movs    r3, #0
    8194:       4618            mov     r0, r3
    8196:       3708            adds    r7, #8
    8198:       46bd            mov     sp, r7
    819a:       bd80            pop     {r7, pc}

のように、 nop で上書きされてしまう。

普通のOSでは、ダイナミックリンクが行われる都合上このように未解決のweakシンボルを nop で埋めることは行われない。そもそも weak シンボルの意味合い自体が環境によって割と異なると言える。

okuokuokuoku

ツールチェーンをリビルドしたら動かなくなった

[1/1] Linking CXX executable target
/opt/yuniboard/arm-m4f/lib/gcc/arm-none-eabihf/11.2.0/../../../../arm-none-eabihf/bin/ld: warning: start of section .text changed by 12

"warning: start of section .text changed by 12" ... なぜズレるんだ。。

oku@stripe ~/repos/nrftest/build
$ /opt/yuniboard/arm-m4f/bin/arm-none-eabi-objdump.exe -h target

target:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .note.gnu.build-id 00000024  00026000  00026000  00006000  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .text         0000b664  00026030  00026030  00006030  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .ARM.exidx    00000008  00031694  00031694  00011694  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

.text の前に何か入ってるじゃねぇか。。 ldscriptで落とさないとダメだな。。

https://github.com/okuoku/nrftest/commit/8816e71178e9ed54557aa22cdec21fc42ae8eea4

とりあえず -Wl,--build-id=none でお茶を濁す。とりあえずこれで正常に起動するようになった。