x86のret命令

コンテキストスイッチを理解するにあたって、アセンブラのret命令の動きを試してみます。実験環境はx86qemu)のFC13です。
ret命令は、スタックからEIPを読んでそこに制御を移します。ということは、スタックに適当なアドレスを積んでおけばそこに制御が行くはずですので、簡単な実験で確かめます。
コンテキストスイッチは、Linuxswitch_to()を見ているので、関数の呼び出し方とかは、swith_to()っぽくjmp命令で呼び出すようにやってます。call命令だとEIPをスタックに積んでくれるので、ここでやりたいこととは違うってのもありますが。。。

最初に呼ぶhello()はfastcallの属性付けて引数をレジスタ渡しにしてますが、特に意味はありません。普通にfastcall外して引数をスタックに積んでも同じ結果になります。

#include <iostream>
#include <cstdlib>

using namespace std;

void hello2(void)
{
        cout << __FUNCTION__ << endl;
        exit(0);
}

extern "C" __attribute__((fastcall)) int hello(int a, int b)
{
        cout << a << ":" << b << endl;
        return 0;
}

int main(int argc, char **argv)
{
        int a = 10, b = 20;
        unsigned long addr = (unsigned long) &hello2;

        __asm__ __volatile__("push %[ret_ip]\n\t"
                             "jmp hello;\n\t"
                             ::
                              [ret_ip] "g" (addr),
                              [arg_a] "c"(a),
                             [arg_b] "d"(b));

        return 0;
}

これを実行するとこんな感じになります。

[masami@ftest ~]$ g++ switch.cc -g
[masami@ftest ~]$ ./a.out
10:20
hello2