2026年2月16日月曜日

Rust@naked関数

Rust@naked関数

multiboot仕様でブートローダを自作せずGRUBから自作カーネル(もどき)を起動できたので、起動情報も色々受け取りたい。multiboot準拠していれば、起動直前にブートローダがレジスタEAXにマジック値を、レジスタEBXに起動情報へのアドレスを設定してくれているそう。

やりたいこと

  • レジスタEAXに設定されたマジック値を変数に代入する
  • レジスタEBXに設定された起動情報アドレスを変数に代入する

_start関数をnaked関数化

普通に_start関数が動くとコンパイラ君が色々設定してくれる(スタックとか?)のでCPUレジスタが破壊されてしまう。chatGPT君に聞いたらnaked関数にすればいいよ!と教えてくれた。
naked関数はコンパイラ君が色々設定してくれるコードを生成しない関数のことなのだけど、実際はどう違うんだろうか。

通常関数のコード

#![no_std]
#![no_main]

#[unsafe( no_mangle )]
pub extern "C" fn _start() -> !
{
    let num = 0xDEAD_BEEF;
    test( num );
}

#[unsafe( no_mangle )]
fn test( _num: u32 ) -> !
{
    loop {}
}

_startをloopだけにしたらjmp命令しか生成してくれなかったので、一個関数かますことに・・・。

naked関数のコード

#![no_std]
#![no_main]

use core::arch::naked_asm;

#[unsafe( naked )]
#[unsafe( no_mangle )]
pub unsafe extern "C" fn _start() -> !
{
    naked_asm!( "
        cli
        hlt
    " )
}

ディスアセンブリしてみる

以下のようなコマンドで。

objdump -S -M intel kernel | less

通常関数

00100030 <_start>:
  100030:       53                      push   ebx
  100031:       83 ec 08                sub    esp,0x8
  100034:       e8 00 00 00 00          call   100039 <_start+0x9>
  100039:       5b                      pop    ebx
  10003a:       81 c3 2f 00 00 00       add    ebx,0x2f
  100040:       c7 44 24 04 ef be ad    mov    DWORD PTR [esp+0x4],0xdeadbeef
  100047:       de
  100048:       c7 04 24 ef be ad de    mov    DWORD PTR [esp],0xdeadbeef
  10004f:       e8 0c 00 00 00          call   100060 <test>
  100054:       cc                      int3
  100055:       cc                      int3
  100056:       cc                      int3
  100057:       cc                      int3
  100058:       cc                      int3
  100059:       cc                      int3
  10005a:       cc                      int3
  10005b:       cc                      int3
  10005c:       cc                      int3
  10005d:       cc                      int3
  10005e:       cc                      int3
  10005f:       cc                      int3

00100060 <test>:
  100060:       8b 44 24 04             mov    eax,DWORD PTR [esp+0x4]
  100064:       eb fe                   jmp    100064 <test+0x4>

100030はebxがcallee-savedなのでEBXの保存。
100031は_start関数のスタック確保。
100034は何がしたいんだろう…スタックに戻り先アドレス(100039)を入れたい?んだろうけど
100039で戻り先アドレス(100039)をebxに入れている。ここもまだこれだけだと謎。
10003aでebx+=0x2F。100068になるわけだけど、それはtest関数のちょうど終わりのアドレスだ。謎。お試しコードが特殊(無限ループ)すぎて本当はなにをしようとしているかわからない。忘れよう。
100040はlet numに0xDEAD_BEEFを代入しているところ。
100048はtest関数の引数用にnumをコピーしているとこかな。

naked関数

00100030 <_start>:
  100030:       fa                      cli
  100031:       f4                      hlt

うん、なんにもしないね。

EAXとEBXを引数で渡す

rustのABIは未安定(今後変わるかもしれない)なので、CのABI(もっと明示的にcdeclじゃなくていいんかな・・・)を指定した関数mainを関数_startから呼び出してやる。
EAXとEBXはスタックに右から引数として積んであげれば渡せる。

#[unsafe( naked )]
#[unsafe( no_mangle )]
pub unsafe extern "C" fn _start() -> !
{
    naked_asm!( "
        push ebx
        push eax
        call main
        cli
        hlt" );
}

#[unsafe( no_mangle )]
extern "C" fn main( eax: u32,
                    ebx: u32  ) -> !
{
    let magic = eax;
    loop {}
}

アセンブリで見る

00100030 <_start>:
  100030:       53                      push   ebx
  100031:       50                      push   eax
  100032:       e8 09 00 00 00          call   100040 <main>
  100037:       fa                      cli
  100038:       f4                      hlt
  100039:       cc                      int3
  10003a:       cc                      int3
  10003b:       cc                      int3
  10003c:       cc                      int3
  10003d:       cc                      int3
  10003e:       cc                      int3
  10003f:       cc                      int3

00100040 <main>:
        hlt
  100040:       8b 44 24 08             mov    eax,DWORD PTR [esp+0x8]
  100044:       8b 44 24 04             mov    eax,DWORD PTR [esp+0x4]
  100048:       eb fe                   jmp    100048 <main+0x8>

たぶんいい感じ。

0 件のコメント:

コメントを投稿

Rust@naked関数

Rust@naked関数 multiboot仕様でブートローダを自作せずGRUBから自作カーネル(もどき)を起動できたので、起動情報も色々受け取りたい。multiboot準拠していれば、起動直前にブートローダがレジスタEAXにマジック値を...