Mudflapを追え!!
リハビリがてらなんか書こー。こういうときに一番いいのは、調べながら書くことだ。と、いうわけで、調べながら書こう。
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
駄目じゃん!!
続くかも。