リハビリがてらなんか書こー。こういうときに一番いいのは、調べながら書くことだ。と、いうわけで、調べながら書こう。
Mudflapを理解したりしつつ、どういうときにパフォーマンスが落ちたりするのかを考察…できるかもしれない。
まず、Mudflapの使いかた。細かいオプション等は
http://d.hatena.ne.jp/toge/20050515#1116134330
を参考にしつつ。
使うだけだったら、
$ cat main.c int main() { char buf[30]; buf[30] = 30; } $ gcc -fmudflap main.c -lmudflap $ ./a.out ******* mudflap violation 1 (check/write): time=1149082987.685020 ptr=0xbfedf87e size=31 pc=0x19235d location=`main.c:5 (main)' /usr/lib/libmudflap.so.0(__mf_check+0x3d) [0x19235d] /home/w0/test/mudflap/a.out(main+0xa3) [0x8048737] /usr/lib/libmudflap.so.0(__wrap_main+0x49) [0x191df9] Nearby object 1: checked region begins 0B into and ends 1B after mudflap object 0x91e8f70: name=`main.c:4 (main) buf' bounds=[0xbfedf87e,0xbfedf89b] size=30 area=stack check=0r/3w liveness=3 alloc time=1149082987.684999 pc=0x191d9d number of nearby objects: 1
簡単。
main()でやると%espの調整とかがあって、色々面倒なので
int func() { char buf[30]; buf[30] = 30; }
こうやっとこう。gcc -S(基本だよ)で
.file "main.c" .section .rodata .LC0: .string "main.c:4 (func) buf" .LC1: .string "main.c:5 (func)" .text .globl func .type func, @function func: pushl %edi pushl %esi pushl %ebx subl $32, %esp movb __mf_lc_shift, %bl pushl $.LC0 movl __mf_lc_mask, %esi pushl $3 pushl $30 leal 14(%esp), %edi pushl %edi call __mf_register movb %bl, %cl movl %edi, %eax shrl %cl, %eax addl $16, %esp andl %esi, %eax sall $3, %eax cmpl %edi, __mf_lookup_cache(%eax) leal 32(%esp), %ecx seta %dl cmpl %ecx, __mf_lookup_cache+4(%eax) setb %al orb %al, %dl je .L2 movl %ecx, %eax pushl $.LC1 subl %edi, %eax pushl $1 incl %eax pushl %eax pushl %edi call __mf_check addl $16, %esp .L2: movb $30, 32(%esp) pushl $3 pushl $30 pushl %edi call __mf_unregister addl $44, %esp popl %ebx popl %esi popl %edi ret .size func, .-func .type _GLOBAL__I_0_func, @function _GLOBAL__I_0_func: jmp __mf_init .size _GLOBAL__I_0_func, .-_GLOBAL__I_0_func .section .ctors.65436,"aw",@progbits .align 4 .long _GLOBAL__I_0_func .ident "GCC: (GNU) 4.1.1 20060525 (Red Hat 4.1.1-1)" .section .note.GNU-stack,"",@progbits
全然わからん。結構長いようだ。
こういうときは、Tree形式を出力するんだ!
$ gcc -fdump-tree-all -Os -fomit-frame-pointer -fmudflap main.c -S
dump-tree-allね。
$ ls *mud* main.c.t100.mudflap2 main.c.t17.mudflap1
ここらへんが怪しい。(どうでもいいけど、Treeの段階でちょうど100パスだということがわかった : ちょうど100パスいったから、20060525が100パス記念日 … 無理すぎる)
## t17 ;; Function func (func) func () { char buf[30]; try { __mf_register (&buf, 30, 3, "main.c:4 (func) buf"); buf[30] = 30; } finally { __mf_unregister (&buf, 30, 3); } } ;; Function _GLOBAL__I_0_func (_GLOBAL__I_0_func) _GLOBAL__I_0_func () { __mf_init (); }
## t100 ;; Function func (func) func () { unsigned char __mf_lookup_shift_l.25; unsigned int __mf_lookup_mask_l.26; struct __mf_cache * __mf_elem.27; unsigned int __mf_base.28; unsigned int __mf_limit.29; int buf.30; unsigned int D.933; unsigned int D.934; _Bool __mf_unlikely_cond.31; unsigned int D.936; _Bool D.937; unsigned int D.938; _Bool D.939; unsigned int D.942; int D.943; char buf[30]; <bb 0>: __mf_lookup_shift_l.25 = __mf_lc_shift; __mf_lookup_mask_l.26 = __mf_lc_mask; __mf_register (&buf, 30, 3, "main.c:4 (func) buf"); buf.30 = (int) &buf; __mf_base.28 = (unsigned int) buf.30; __mf_limit.29 = (unsigned int) &buf[30]; D.933 = __mf_base.28 >> __mf_lookup_shift_l.25; D.934 = D.933 & __mf_lookup_mask_l.26; __mf_elem.27 = &__mf_lookup_cache[D.934]; D.936 = __mf_elem.27->low; D.937 = D.936 > __mf_base.28; D.938 = __mf_elem.27->high; D.939 = D.938 < __mf_limit.29; __mf_unlikely_cond.31 = D.937 || D.939; if (__mf_unlikely_cond.31) goto <L1>; else goto <L0>; <L1>:; D.942 = __mf_limit.29 - __mf_base.28; D.943 = D.942 + 1; __mf_check (__mf_base.28, D.943, 1, "main.c:5 (func)"); __mf_lookup_shift_l.25 = __mf_lc_shift; __mf_lookup_mask_l.26 = __mf_lc_mask; <L0>:; buf[30] = 30; __mf_unregister (&buf, 30, 3); return; } ;; Function _GLOBAL__I_0_func (_GLOBAL__I_0_func) _GLOBAL__I_0_func () { <bb 0>: __mf_init () [tail call]; return; }
t100のほうはわからんけど、t17のほうはわかりそう。ちなみに、例外処理の最適化が終わったあとのt20のほうが見やすい。
;; Function func (func) func () { char buf[30]; __mf_register (&buf, 30, 3, "main.c:3 (func) buf"); buf[30] = 30; __mf_unregister (&buf, 30, 3); return; }
なんとなく、わかる。
- 配列宣言で __mf_register( 配列, 大きさ, ??, エラーしたときの出力用 )
- ふつうに処理
- 最後に __mf_unregister
3つめの引数が謎。いや、そうでもないか。mf-runtime.h(/usr/lib/gcc/i686-pc-linux/4.x.x/include 等にあるはず)より、__mf_registerが、
extern void __mf_register (void *ptr, __mf_size_t sz, int type, const char *name) __attribute((nothrow));
こう、んで
#define __MF_TYPE_NOACCESS 0 #define __MF_TYPE_HEAP 1 #define __MF_TYPE_HEAP_I 2 #define __MF_TYPE_STACK 3 #define __MF_TYPE_STATIC 4 #define __MF_TYPE_GUESS 5
多分これ。__MF_TYPE_STACKが3。
ということは、最後の_GLOBAL__I_0_funcは、.ctorにあるし、アレか。__MF_TYPE_STATICとかか。
int func() { char buf[30]; buf[30] = 30; } int a[30]; // グローバルに
;; Function _GLOBAL__I_0_func (_GLOBAL__I_0_func) _GLOBAL__I_0_func () { __mf_init (); __mf_register (&a, 120, 4, "main.c:7 a"); return; }
やっぱりそうだ。ふむ、なるほど、ということは、さっきふと思ったんだけど、setjmpとかしても大丈夫そうだな…
#include <setjmp.h> char *p; jmp_buf j; int f() { longjmp( j, 1 ); } int func() { char buf[30]; p = buf; f(); } int main() { if ( setjmp( j ) == 0 ) { func(); } p[0] = 40; return 0; }
$ gcc -fmudflap main.c -lmudflap $ ./a.out $ echo $? 0
駄目じゃん!!
続くかも。