新千葉 ガーベージ・コレクション

FPGA マガジンやインターフェースで書けなかったこと等をちょぼちょぼ書いてます。@ryos36

ARM の EABI とか newlib とか

あまりまとまっていないけど最近の成果をメモ。わすれちゃうからね。

XilinxSDK 用の gccgdb だけ再コンパイルしようとしたけどだめだった。もう全部コンパイルしないといけないみたい。結構めんどくさい。とはいえ、シェルプロがあるようなのでそれを一発動かせばよさそうであるのも事実。この SDK 用の gcc では g++ が使える。特に arm-xilinx-eabi- の方はどうしているのだろうとちょっと調べてみた。普通と言えば普通なんだけど、newlib を使っている。newlib を使うと、open や write などの Unixシステムコールが用意されているものとして libc を作ってくる。あとはこれらのシステムコールをシミュレーションすれば(用意すれば)c++ はできあがる。実際に、SDK では普通に c++ を選択できる。Eclispe での設定は次のようになっている。

-Wl,--start-group,-lxil,-lgcc,-lc,-lstdc++,--end-group
  • lstdc++ が指定されているが、 -nostdlib は指定されていない。つまり crt0 なんかがくっつく。crt0 はほとんど何もしていない。だから本来は crt0 なんかくつけないように -nostdlib を使用するべきなのだ。がそうはなっていない。理由は恐らく _init の存在。なぜか _init は crtbegin の中にあったりする。この _init はシステムの初期化なので本来は OS が持っていなければならないはずだ。そこが手抜きされている。っていうかなにリンクされているかわからないからほんとはすべて自分でコントロールすべきだよ。

まぁそんなこんなで newlib が要求するシステムコールは bsp 内の ps7_cortexa9_0/libsrc/standalone_v5_3/src にある。write なんてかなり手抜き。まぁいいのか。

リンカーで特徴的なのは .ctors とか .dtors で(コンストラクタとデストラクタかな)、さらに初期化ルーチンの preinit_array とinit_array 。C だけならあまりこの辺は気にしなくてよいのだけど、c++ を使うなら、この preinit_array と init_array に登録された関数をちゃんと呼ばないといけない。newlib では misc の init.c で __libc_init_array を呼べばよい。実際、SDK の main の前でよばれている。(xil-crt0.S)。もー crt0.S を用意するなら -nostdlib を設定すればいいのに、、、この __libc_init_array から _init が呼ばれているが、これは main に入る前の _init なんで(くりかえすけど) OS が用意するもの。SDK は用意しないで gcc の crt をリンクすることで解決している。まぁ弊害はないからいいのか?

c++ でめんどくさいのはグローバル変数のオブジェクト。これは gcc のバージョンにもよるが、4.9.1 だと

__static_initialization_and_destruction_0(int, int)

という関数が出来た。init_array 各関数をコールすることで呼ばれる。そして、このとき cxa_atexit 経由でデストラクタが登録される。いま検索したら

-fno-use-cxa-atexit

というオプションをつけると登録されないらしい。なんだよ~~~。意味がなくなりつつあるけど、続けて書くよ。

つまり終了処理の時にデストラクタを呼ぶ。終了処理は exit の時だけではなく、動的ライブラリのアンロードの時も呼ばれる。動的ライブラリを使っていないのに動的ライブラリの事を気にしなくてはいけなくなるのよ(あーでも -fno-use-cxa-atexit を使うと解決するみたいよ)。

動的ライブラリは各ライブラリに handle が割り当てられて、本体も仮想的に動的ライブラリ扱いされるので、main に dso_handle が必要になる。そして、デストラクタ登録のために cxa_atexit が呼ばれる。これらは newlib に用意されているから本来あまり気にしなくてよい。cxa_atexit などの必要な関数というのは EABI で決められているらしい。

あと、もう一つ、gcc は void *_impure_ptr; も用意しなければならない。これは再入可能な環境を保持していくもののようで、 newlib は reentrant な環境も用意していているので、newlib を使うとばっちりそろっている。

ということで、SDK ちょっとへんだけど普通に g++ が使えるという話。めでたしめでたし。

tkernel みたいな

tkernel みたいなのはちょっとめんどくさくなってくる。どういうわけか malloc を持っている。まぁそもそも libc とはなじまないから -nostdlibc だよね。最初は -nostdlibc を削除して SDK のように -lc をつかった。はい。できました。でも、これはよくなくて、crtX.o とか atexit とか、あまり tkernel の OS の事を考えてないものがくっつく。malloc なんてリンク順でかろうじて tkernel のが呼ばれる。危ない。

だから、この手の RTOS はやっぱり -nostdlibc で自前で crtX.o や libc をつくらないといけない。

_impure_ptr は newlib にその構造体が定義されていて、それを指すポインタになっている。 ./libsupc++/vterminate.cc から参照されている。システムでどのように reent の構造を用意するかということみたいなので、とりあえず、変数だけを用意しておく。(おい!! > 自分)

なんとなくだが、libstdc++ のコンパイルに失敗している気がするぞ。たしかに configure に --with-newlib がある。