OSなし環境とかRTOS環境下ではちょくちょくやってましたが、Linux とかではそういえばやったことないなと思い、やってみました.
Segmantation Fault を出しながら、試行錯誤して一応できたのが以下.
※ (1)
mprotect で指定するアドレスは page size 境界にある必要があるので、そのための attribute.
※ (2)
関数のバイナリを含む配列を .text 領域に配置するための attribute. 最初 .bss セクションにおいてやってみたけれど、どうも .bss だと mprotect で PROT_EXEC を付与してもSegmantation Fault が出るのようなので指定. なんで出来ないのか… linker script とか Intel のアーキテクチャマニュアルとか、詳解 Linux kernel とか、そのへん読み解いたら分かるかもしれませんが、とりあえず今回は .text に配置.
※ (3)
.text 領域はデフォルトで書き込み権限がないため、mprotect で書き込み権限を付与.
※ (4)
配列内に機械語でバイナリーを直接プログラミングするのがしんどかったので、double の乗算を行う関数 mul を buf にコピー (サイズに指定している 128 は適当で多分余分なのもコピーされている) 今回は単純な乗算なので問題ないけれど、相対アドレッシングで外に分岐したり外のデータにアクセスするような関数の機械語をコピーしても変な所にアクセスして期待した動作はしないのでその辺りは注意.
※(5)
buf の先頭を関数ポインター (double(*)(double, doble)) にキャスト.
※(6)
関数ポインタ fp 経由で buf の内容をコードとして実行.
さてこのソースを make してみると…
g++ -g -Wall -Wextra -c main.cpp /tmp/ccxKSKnq.s: Assembler messages: /tmp/ccxKSKnq.s:35: Warning: ignoring changed section attributes for .text g++ main.o -o main
のようになにやら warning がでていますが、実行した結果は以下のようになっており、buf は .text 領域に配置され、コピーされた mul の内容も関数として正しく動作しているようです.
./main 0x402000 0x403000 6.000000