SIGSEGVを捕捉するライブラリの作成
はじめに
SIGSEGVを捕捉するライブラリを作成する.作成したライブラリはLD_PRELOAD
を用いて対象となる実行ファイルに,動的リンクさせる.
SIGSEGVの捕捉方法についてはSIGSEGVを捕捉するを参照.
プログラム
以下のようなSIGSEGVを発生させるプログラムを用意した.
/* main.c */
#include<stdio.h>
#include<string.h>
int main(){
printf("main() is called\n");
char *ptr;
*ptr = "A";
return 0;
}
このプログラムに対して,LD_PRELOAD
を用いて,作成したライブラリを動的リンクさせることで,前回のように,シグナルの情報やコンテキストを表示させる.
ライブラリとなるプログラムは以下のようになる.
/* my_libsignal.c */
#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU
#include <signal.h>
#include <ucontext.h>
#include <string.h>
#include <sys/mman.h>
#define STACK_SIZE (4096*8)
static void signal_handler(int sig, siginfo_t* sig_info, void *sig_data)
{
ucontext_t *ctx = sig_data;
size_t *gregs = (size_t *)ctx->uc_mcontext.gregs;
if (sig = SIGSEGV) {
fprintf(stderr, "si_addr %p\n", sig_info->si_addr);
fprintf(stderr, "[ucontext] %p\n", sig_data);
fprintf(stderr, "[REG_RIP] %p\n", (void *)gregs[REG_RIP]);
fflush(stderr);
// exit(1);
}
}
__attribute__((constructor))
void set_signal()
{
stack_t ss;
ss.ss_sp = malloc(STACK_SIZE);
ss.ss_size = STACK_SIZE;
ss.ss_flags = 0;
if (sigaltstack(&ss, NULL)) {
fprintf(stderr, "Failed sigaltstack\n");
exit(1);
}
struct sigaction act;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGSEGV);
act.sa_sigaction = signal_handler;
act.sa_flags = SA_SIGINFO|SA_RESTART|SA_ONSTACK;
if ( sigaction( SIGSEGV, &act, NULL ) == -1 ) {
/* error */
fprintf(stderr, "Failed to set my signal handler.\n" );
exit(1);
}
printf("set_signal() is called\n");
}
main.cのプログラムの実行を開始する前に,独自のハンドラを登録する必要がある.そのため今回は,GCC拡張である__attribute__((constructor))
を用いた.他の手法としては,__libc_start_main関数
をライブラリ内でラップする手法がある.違いとしては,__libc_start_main関数
は,__attribute__((constructor))
より先に呼ばれる性質がある.
コンパイルと実行
コンパイル
gcc -o main main.c
gcc -shared -fPIC -o my_libsignal.so my_libsignal.c
実行
LD_PRELOAD=./my_libsignal.so ./main
上記のように実行することで,実行ファイルmainに,作成したライブラリmy_libsignal.so
を動的にリンクすることができる.
実行すると前回と同じような結果になる.
si_addr (nil)
[ucontext] 0x196db00
[REG_RIP] 0x400543
si_addr (nil)
[ucontext] 0x196db00
[REG_RIP] 0x400543
si_addr (nil)
[ucontext] 0x196db00
[REG_RIP] 0x400543
si_addr (nil)
[ucontext] 0x196db00
[REG_RIP] 0x400543
(snip)
まとめ
SIGSEGVを捕捉するライブラリを作成し,実行ファイルに動的にリンクさせた.似たような既存のライブラリにlibSegFault.so
などがある.