読者です 読者をやめる 読者になる 読者になる

clang UBsan

clang の -ftrap-function オプションが動作しないと思ってたらちゃんと動いた。
しかし、-fsanitize-undefined-trap-on-error オプションと両立しない。
というか両方付けないと動かないみたいだ。

整数のゼロ割りについては最適化(-O1とか)を有効にしないとx87で行われるコード出力になるみたいで浮動小数点例外が発生してしまってUBsanでは拾ってくれない。

  • fsanitize=integer オプションを付けた場合、同じエラー発生箇所での診断表示は一度しか行われない。診断出力は確かに同じ個所で何度もやってほしくない場合が殆どかもしれない。
  • fsanitize-undefined-trap-on-error オプションも付けた場合、エラー診断表示が行われず、同じ個所でエラー発生時に毎回トラップが発生する。シグナルのアドレス位置は取れるので重複の見分けは出来そう。
  • ftrap-function オプションを更に付けた場合、エラー診断表示は行われず、同じ箇所でエラーが発生した場合に何度も指定した関数が呼び出される。エラー発生位置を捉える事は出来ない。

診断表示が欲しくて、なおかつコールバックも欲しかったんだけど、
tools/clang/lib/CodeGen/CGExpr.cpp の CodeGenFunction::EmitCheck の中で ユーザー定義関数を呼ぶコードを出力させる方法がさっぱりわからない。
なので、
projects/compiler-rt/lib/ubsan/ubsan_handlers.cc の HandleIntegerOverflow とかで raise(SIGILL); するコードを追加して、ユーザープログラム側でそれを受け取ってそこで好きな処理を行う方法を取る事にしよう。。

compiler-rt-3.4/lib/ubsan/ubsan_handlers.cc

21a22,25
> #include <unistd.h>
> #include <signal.h>
> #include <stdio.h>
> 
76a81,84
>   
>   printf("before SIGILL\n");
>   raise(SIGILL);
>   printf("after SIGILL\n");
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>
#include <memory.h>
#include <stdbool.h>

jmp_buf JumpBuffer;

static
void ILLaction(int sn , siginfo_t* si , void* sc)
{
/*
	const void* addr = si->si_addr;
	static const size_t len = 64;
	static const void* s_addresses[len];
	bool found = false;
	for (size_t i=0; i<len; ++i) {
		if (s_addresses[i] == addr) {
			found = true;
			break;
		}
	}
	if (!found) {
		for (size_t i=0; i<len; ++i) {
			if (s_addresses[i] == 0) {
				s_addresses[i] = addr;
				printf("%p\n", addr);
				break;
			}
		}
	}
	
	siglongjmp(JumpBuffer, 1);
*/
	printf("SIGILL\n");
}

static
void regist_sa()
{
	struct sigaction sa_sigill;
	memset(&sa_sigill, 0, sizeof(sa_sigill));
//	sa_sigill.sa_handler = ILLhandler;
	sa_sigill.sa_sigaction = ILLaction;
	sa_sigill.sa_flags = SA_SIGINFO;
	sigaction(SIGILL, &sa_sigill, (struct sigaction *)NULL);
}

/*

	-fsanitize=integer,unsigned-integer-overflow,shift \
	-fsanitize-undefined-trap-on-error \

*/

int test(int a, int b)
{
	int add = a + b;
	int sub = a - b;
	int mul = a * b;
	int div = a / b;
	return mul;
}

void onerr()
{
	printf("err!\n");
	raise(SIGILL);
}

int main(int argc, char* argv[])
{
	regist_sa();
	
	for (int i=0; i<3; ++i) {
		int a;
		int b;
//		if (sigsetjmp(JumpBuffer, 1) == 0) {
			printf("input\n");
			char buff[256];
			fgets(buff, 256, stdin);
			sscanf(buff, "%d %d", &a, &b);
			int c = test(a, b);
			printf("%d\n", c);
//		}else {
//			printf("err : %d %d\n", a, b);
//		}
	}
	
	return 0;
}

Linuxllvmのbuild自体はかなりすんなり行った。
http://llvm.org/releases/3.4.1/cfe-3.4.1.src.tar.gz
http://llvm.org/releases/3.4.1/llvm-3.4.1.src.tar.gz
http://llvm.org/releases/3.4/compiler-rt-3.4.src.tar.gz
http://llvm.org/docs/GettingStarted.html#getting-started-quickly-a-summary

ちゃんと調査して理解しないとコード生成周りは手を入れられない。巨大なクラスライブラリだ。