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;
}

なんとなく、わかる。

  1. 配列宣言で __mf_register( 配列, 大きさ, ??, エラーしたときの出力用 )
  2. ふつうに処理
  3. 最後に __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

駄目じゃん!!
続くかも。