自作OS用のアプリケーションでC++を使う方法を調べた。使ったコンパイラはg++で、リンカはld。C++のうちの使う機能は、シンプルにクラスと継承のみ。RTTIや例外はテストしてない。

new, delete

デフォルトのnew, deleteで行うメモリ管理を定義する必要がある。これは単純にmalloc, freeを使えばいいだろう。new[]delete[]も定義すること。

純粋仮想関数

純粋仮想関数を定義しても、その仮想関数はサブクラスで上書きされて呼び出されないはずなので参照されないはずだけど、テーブルでは__cxa_pure_virtualという関数が参照される。 実際に呼び出された場合には例外を投げるとかしたほうがいいかもしれないけど、基本的には呼び出されないはずなので空の関数でいいかも。

グローバルコンストラクタ

グローバル変数でクラスのインスタンスを定義した場合、main関数の呼び出しよりも先にそれらのコンストラクタが呼び出される必要がある。g++はそのような変数に対する初期化コードへの関数ポインタを.preinit_array, .init_arrayというセクションに、また終了処理を.fini_arrayに?出力する。

そこで、リンカスクリプトでこれらのデータをデータセクションに配置して、OSがアプリケーションを呼び出す際にmain関数の呼び出し前に呼び出してやれば良い。

リンカスクリプト:

SECTIONS {
    ...

    .data : {
        *(.data)

        . = ALIGN(4);
        __preinit_array_start = .;
        *(.preinit_array)
        __preinit_array_end = .;

        __init_array_start = .;
        *(SORT(.init_array.*))
        *(.init_array)
        __init_array_end = .;

        __fini_array_start = .;
        *(SORT(.fini_array.*))
        *(.fini_array)
        __fini_array_end = .;
        . = ALIGN(16);
    }

crt0.c

extern void (*__preinit_array_start []) (void) __attribute__((weak));
extern void (*__preinit_array_end []) (void) __attribute__((weak));
extern void (*__init_array_start []) (void) __attribute__((weak));
extern void (*__init_array_end []) (void) __attribute__((weak));
extern void (*__fini_array_start []) (void) __attribute__((weak));
extern void (*__fini_array_end []) (void) __attribute__((weak));

...

  for (void (**pp)() = __preinit_array_start; pp < __preinit_array_end; ++pp)
    (**pp)();
  // TODO: Init.
  for (void (**pp)() = __init_array_start; pp < __init_array_end; ++pp)
    (**pp)();

ただ試したところ、初期化ルーチンは.init_arrayに入っていて、.preinit_arrayには何が入るのか、また.preinit_array.init_arrayの間には何を初期化する必要があるのか、また.fini_arrayも空で何が入るのかわからなかった。

参考: