main 蹂躙

http://d.hatena.ne.jp/yupo5656/20060828/p1

素敵なのでマネしてみます。

色々常識が足りないけど、一応 main は呼んであげる。

main;
__attribute__((constructor, destructor))
static x() {
    if (main) puts("world!");
    else puts("hello", main = 195);
}

実行時に急造された上に world 起き場になる main。

main[30];
__attribute__((constructor))
static x() {
    if (main[0]) {
        main[0] = 0x6c726f77;
        main[1] = 0x0a2164;
        __asm__("movl $1, %%ebx \n"
                "movl %0, %%ecx \n"
                "movl $7, %%edx \n"
                "movl $4, %%eax \n"
                "int $0x80 \n"
                :: "r"(main));
    }
    else {
        puts("hello");
        memcpy(main, x, 120);
    }
}

hello に到達しない。 main を .data セクションに置けば main の自己書き換えが容易に。

__attribute__((section (".data"))) main() {
    unsigned char *p = main;
    while (*p / 2 != 116) p++;
    *p++ = 0xb8; *p++ = 1; *p++ = 0; *p++ = 0; *p++ = 0;
    *p++ = 0xcd; *p++ = 0x80;
    puts("hello world!");
}

こっちの方がはるかにラクか…

__attribute__((section (".data"))) main() {
    unsigned char *p = &&h;
    *p++ = 0xb8; *p++ = 1; *p++ = 0; *p++ = 0; *p++ = 0;
    *p++ = 0xcd; *p++ = 0x80;
h:  puts("hello world!");
}

追記: 元リンクの追記がまた面白い!最初、え、なんでわざわざ .text に置くんだ?と。もう私ではちょっと解読しないとすぐにはわからないです。ちなみに手元では GCC3 系では hello hello になる模様。そっちの方がコンパイラの気持ちとしてはわかる気もします。 inner-function を下に出してやれば hello world になりましたが、少しさみしいですね。あと思ったんですけどこういう遊びは RISC の方が面白げですね。

せっかくなのでもうひとつ。時代は C++ ですぞ。面倒なのでwoさんの丸パクリ

struct _ {
    int a, b, c, d, e, f, g, h, i;
    _() : a(0x0a2e6f68), b(0x65486800), c(0xe1896c6c), d(0x4b8), e(0x1bb00),
          f(0x7ba0000), g(0xcd000000), h(0x8c48380), i(0x909090c3) {}
};
_ main;

GCC の __attribute__ いろいろ

NOTREACHED コメントを入れると GCC が黙るという話を読んで。

http://alohakun.blog7.fc2.com/blog-entry-429.html

えー本当ですか cpp に消されて終わりでは、 GCC のコードにもそれっぽい処理 grep できなかったし、と思って以下のようなコード。

#include <stdlib.h>
void my_exit() { exit(0); }
int main() {
    my_exit();
    /*NOTREACHED*/
}

GCC-2.95.3 から GCC-4.1 まで、全て -Wall で warning が出ました。 lint がこれで黙ったというのはそうなんだろうなぁと思うんですが。はて。まぁなんにせよ今だったら GCC の拡張、 noreturn で黙らせればいいと思いますです。

#include <stdlib.h>
__attribute__((noreturn)) void my_exit() { exit(0); }
int main() {
    my_exit();
}

__attribute__ といえば他を眺めてみると、 noinline なんてあるんですね。 C++ の volatile メンバ関数は this が volatile になるってのと inline 化抑制の二つの意味がある気がして、 inline 抑制はしたいけど this は volatile にならんでいいよ、って時があるんじゃなかろうかいや無いのかなコンパイラの気持ちがわからない…と思ってたんですが、 noinline をつければ inline だけ封印できそう。

pure も面白いと気付きました。純粋関数型の世界にようこそ。以下は -O つけるとつけないのとで最終結果は同じですが、途中の printf の回数が変化しました。

__attribute__((pure)) int twice(int i) {
    printf("%d\n", i);
    return i * i;
}

int main() {
    return twice(3) + twice(3);
}

あと Strong Using はなんか、 namespace の mixin みたいに使えるような。以下は GCC の info から。

     namespace std {
       namespace debug {
         template <class T> struct A { };
       }
       using namespace debug __attribute ((__strong__));
       template <> struct A<int> { };   // ok to specialize

       template <class T> void f (A<T>);
     }

     int main()
     {
       f (std::A<float>());             // lookup finds std::f
       f (std::A<int>());
     }

なんとなく __attribute でいいんなら __attribute__ じゃなくて今度からそっち使うか…と思い始めたんですが、そのへん使いわけとかあるんですかね…

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h