2012-01-20 10 views
10

一時的に私のC++プログラムでウォッチポイント(ハードウェア書き込みのブレーク)を設定してメモリ破損を検出したいとします。プログラムでgdbウォッチポイントを設定することはできますか?

gdbで手作業で行う方法はすべて見てきましたが、実際にコード内のいくつかの方法でウォッチポイントを設定したいので、gdbに侵入する必要はありません。ウォッチポイントに戻り、次に続行します。以下のような

何か:GDBで

#define SET_WATCHPOINT(addr) asm ("set break on hardware write %addr") 

答えて

3

、ウォッチポイント、ハードウェアとソフトウェアの2つのタイプがあります。

  • あなたは簡単にソフトウェアウォッチポイントを実装することはできません:GDBがシングルステップにデバッグ中のプログラムを必要としての値をテストするので(参照GDB Internals

ソフトウェアウォッチポイントは、非常に遅いです各命令後の監視された表現。

EDIT:

私はまだハードウェアウォッチポイントが何であるかを理解しようとしています。ハードウェアブレークポイントのため

  • this articleは、いくつかのテクニクスを与える:

我々からの読み取りまたはアドレス100005120h(アドレス範囲100005120h-100005127h)

で1 QWORDへの書き込みを見たいです
lea rax, [100005120h] 
mov dr0, rax 
mov rax, dr7 
and eax, not ((1111b shl 16) + 11b) ; mask off all 
or eax, (1011b shl 16) + 1  ; prepare to set what we want 
mov 
dr7, rax    ; set it finally 

完了しました。コードfalまで待つことができますトラップに入ってきた!メモリ範囲で100005120h-100005127hを任意のバイトにアクセスした後、INT 1が発生するとDR6.B0ビットはあなたがまた、GDBローエンドのファイルを見てみることができます。1.

に設定されます(例えば、amd64-linux-nat.c)が、 1 /あなたは2/ptraceとの最初の1に取り付ける軽量のデバッガを見たいと思う1、とは使用しています:

ptrace (PTRACE_POKEUSER, tid, __regnum__offset__, address); 

セットとウォッチポイントを処理するためには、(確かに)2工程を必要とします。

+0

: **ハードウェア・ブレークポイントは、いくつかのチップを搭載した組み込みのデバッグ機能として時々利用できます。通常、これらは、ブレークポイントアドレスが格納される専用のレジスタを持つことによって動作します。** – Neil

+0

@Neilはい、私はそれがどのように実装されたかを意味しました。私は答えを更新しました – Kevin

+0

GDBはそれをサポートしていないかもしれませんが、x86は確かにそうです。あなたがリンクしている実際にはgdbファイルはそれを行う方法を示しています! ** i386_insert_aligned_watchpoint **機能を参照してください。効果的にDR7レジスタを設定するようですが、それは特権命令なので、カーネル以外のモードでは使用できません。 – Neil

0

プログラム自身がGDBにコマンドを供給することができます。 GDBを実行するには特殊なシェルスクリプトが必要です。unteeという名前のファイルに

コピーこのコード、およびのchmod 755 untee

#!/bin/bash 

if [ -z "$1" ]; then 
    echo "Usage: $0 PIPE | COMMAND" 
    echo "This script will read the input from both stdin and PIPE, and supply it to the COMMAND." 
    echo "If PIPE does not exist it will be created with mkfifo command." 
    exit 0 
fi 

PIPE="$1" 

if [ \! -e "$PIPE" ]; then 
    mkfifo "$PIPE" 
fi 

if [ \! -p "$PIPE" ]; then 
    echo "File $PIPE does not exist or is not a named pipe" > /dev/stderr 
    exit 1 
fi 

# Open the pipe as a FD 3 
echo "Waiting for $PIPE to be opened by another process" > /dev/stderr 
exec 3<"$PIPE" 
echo "$PIPE opened" > /dev/stderr 
OPENED=true 

while true; do 
    read -t 1 INPUT 
    RET=$? 
    if [ "$RET" = 0 ]; then 
     echo "$INPUT" 
    elif [ "$RET" -lt 128 ]; then 
     echo "stdin closed, exiting" > /dev/stderr 
     break 
    fi 

    if $OPENED; then 
     while read -t 1 -u 3 INPUT; do 
      RET=$? 
      if [ "$RET" = 0 ]; then 
       echo "$INPUT" 
      else 
       if [ "$RET" -lt 128 ]; then 
        echo "$PIPE closed, ignoring" > /dev/stderr 
        OPENED=false 
       fi 
       break 
      fi 
     done 
    fi 
done 

そして今、Cのコードを実行します。

#include <stdio.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <signal.h> 
#include <unistd.h> 

void gdbCommand(const char *c) 
{ 
    static FILE * dbgpipe = NULL; 
    static const char * dbgpath = "/tmp/dbgpipe"; 
    struct stat st; 

    if(!dbgpipe && stat(dbgpath, &st) == 0 && S_ISFIFO(st.st_mode)) 
      dbgpipe = fopen(dbgpath, "w"); 
    if(!dbgpipe) 
     return; 
    fprintf(dbgpipe, "%s\n", c); 
    fflush(dbgpipe); 
} 

void gdbSetWatchpoint(const char *var) 
{ 
    char buf[256]; 
    snprintf(buf, sizeof(buf), "watch %s", var); 

    gdbCommand("up"); /* Go up the stack from the kill() system call - this may vary by the OS, you may need to walk the stack more times */ 
    gdbCommand("up"); /* Go up the stack from the gdbSetWatchpoint() function */ 
    gdbCommand(buf); 
    gdbCommand("continue"); 
    kill(getpid(), SIGINT); /* Make GDB pause our process and execute commands */ 
} 

int subfunc(int *v) 
{ 
    *v += 5; /* GDB should pause after this line, and let you explore stack etc */ 
    return v; 
} 

int func() 
{ 
    int i = 10; 
    printf("Adding GDB watch for var 'i'\n"); 
    gdbSetWatchpoint("i"); 

    subfunc(&i); 
    return i; 
} 

int func2() 
{ 
    int j = 20; 
    return j + func(); 
} 


int main(int argc, char ** argv) 
{ 
    func(); 
    func2(); 
    return 0; 
} 

コピーしたファイルにtestということを.c、コンパイルコマンドgcc test.c -O0 -g -oテスト then ./untee/tmp/dbgpipe |

これはGDB 7.3で、私は64ビットのUbuntu上で動作します

./test GDB -ex「実行」子プロセスから

10

セットハードウェアウォッチポイント(古いGDBのバージョンは、非ターミナルからコマンドを読み取るために拒否される場合もあります) 。あなたはXcodeのを使用することが起こる場合

#include <signal.h> 
#include <syscall.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <stddef.h> 
#include <sys/ptrace.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <linux/user.h> 

enum { 
    DR7_BREAK_ON_EXEC = 0, 
    DR7_BREAK_ON_WRITE = 1, 
    DR7_BREAK_ON_RW = 3, 
}; 

enum { 
    DR7_LEN_1 = 0, 
    DR7_LEN_2 = 1, 
    DR7_LEN_4 = 3, 
}; 

typedef struct { 
    char l0:1; 
    char g0:1; 
    char l1:1; 
    char g1:1; 
    char l2:1; 
    char g2:1; 
    char l3:1; 
    char g3:1; 
    char le:1; 
    char ge:1; 
    char pad1:3; 
    char gd:1; 
    char pad2:2; 
    char rw0:2; 
    char len0:2; 
    char rw1:2; 
    char len1:2; 
    char rw2:2; 
    char len2:2; 
    char rw3:2; 
    char len3:2; 
} dr7_t; 

typedef void sighandler_t(int, siginfo_t*, void*); 

int watchpoint(void* addr, sighandler_t handler) 
{ 
    pid_t child; 
    pid_t parent = getpid(); 
    struct sigaction trap_action; 
    int child_stat = 0; 

    sigaction(SIGTRAP, NULL, &trap_action); 
    trap_action.sa_sigaction = handler; 
    trap_action.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER; 
    sigaction(SIGTRAP, &trap_action, NULL); 

    if ((child = fork()) == 0) 
    { 
     int retval = EXIT_SUCCESS; 

     dr7_t dr7 = {0}; 
     dr7.l0 = 1; 
     dr7.rw0 = DR7_BREAK_ON_WRITE; 
     dr7.len0 = DR7_LEN_4; 

     if (ptrace(PTRACE_ATTACH, parent, NULL, NULL)) 
     { 
      exit(EXIT_FAILURE); 
     } 

     sleep(1); 

     if (ptrace(PTRACE_POKEUSER, parent, offsetof(struct user, u_debugreg[0]), addr)) 
     { 
      retval = EXIT_FAILURE; 
     } 

     if (ptrace(PTRACE_POKEUSER, parent, offsetof(struct user, u_debugreg[7]), dr7)) 
     { 
      retval = EXIT_FAILURE; 
     } 

     if (ptrace(PTRACE_DETACH, parent, NULL, NULL)) 
     { 
      retval = EXIT_FAILURE; 
     } 

     exit(retval); 
    } 

    waitpid(child, &child_stat, 0); 
    if (WEXITSTATUS(child_stat)) 
    { 
     printf("child exit !0\n"); 
     return 1; 
    } 

    return 0; 
} 

int var; 

void trap(int sig, siginfo_t* info, void* context) 
{ 
    printf("new value: %d\n", var); 
} 

int main(int argc, char * argv[]) 
{ 
    int i; 

    printf("init value: %d\n", var); 

    watchpoint(&var, trap); 

    for (i = 0; i < 100; i++) { 
     var++; 
     sleep(1); 
    } 

    return 0; 
} 
+1

これは悪いメモリ書き込みをデバッグするのに非常に便利です。質問、睡眠(1)声明の目的は何ですか?これがなければ動作しませんが、ウォッチポイントを繰り返し設定しクリアするループがあるので、それほど待たずにいます。また、子プロセスなしでウォッチポイントを設定することは可能ですか?私は単純にptrace呼び出しを親プロセスに移動しようとしましたが、失敗しますか? –

1

、あなたはあなたのウォッチポイントを設定するために、別のブレークポイントにアクションを使用して、必要な効果(ウォッチポイントの自動設定)を達成することができます

  1. を設定しますブレークポイントをどこかで見たい変数がどこにあるかは、変数の監視を開始する前にヒットするスコープになります
  2. ブレークポイントを右クリックし、ブレークポイントを編集...
  3. をクリックアクションを追加し、同じようLLDBコマンドでデバッガコマンドを追加します。引き続き自動的をチェックし、
  4. watchpoint set variable <variablename>watch <variablename>またはあなたがGDB 、のようなコマンドを使用している場合)アクションを評価した後チェックボックス。

enter image description here

1:GDBは、もはやXcodeのの最近のバージョンでサポートされていますが、私は手動で設定することは可能であると考えています。 https://github.com/whh8b/hwbp_lib

にですgithubの上:

+1

もし私がWindowsでプログラミングしていたのであれば、90年代半ば以降、これを実現できました。まだLinuxが追いつくのを待っている! – Neil

0

はuser512106の偉大な答えに基づいて、私は誰かが役立つかもしれないと少し「ライブラリ」をアップコード化されました。私は彼の答えに直接コメントすることができたらいいのに、まだ十分な担当者がいない。コミュニティからのフィードバックに基づいて

は、私は/ここに関連するコードをコピー&ペーストするつもりです:

#include <stdio.h> 
#include <stddef.h> 
#include <signal.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <sys/ptrace.h> 
#include <sys/user.h> 
#include <sys/prctl.h> 
#include <stdint.h> 
#include <errno.h> 
#include <stdbool.h> 

extern int errno; 

enum { 
    BREAK_EXEC = 0x0, 
    BREAK_WRITE = 0x1, 
    BREAK_READWRITE = 0x3, 
}; 

enum { 
    BREAK_ONE = 0x0, 
    BREAK_TWO = 0x1, 
    BREAK_FOUR = 0x3, 
    BREAK_EIGHT = 0x2, 
}; 

#define ENABLE_BREAKPOINT(x) (0x1<<(x*2)) 
#define ENABLE_BREAK_EXEC(x) (BREAK_EXEC<<(16+(x*4))) 
#define ENABLE_BREAK_WRITE(x) (BREAK_WRITE<<(16+(x*4))) 
#define ENABLE_BREAK_READWRITE(x) (BREAK_READWRITE<<(16+(x*4))) 

/* 
* This function fork()s a child that will use 
* ptrace to set a hardware breakpoint for 
* memory r/w at _addr_. When the breakpoint is 
* hit, then _handler_ is invoked in a signal- 
* handling context. 
*/ 
bool install_breakpoint(void *addr, int bpno, void (*handler)(int)) { 
    pid_t child = 0; 
    uint32_t enable_breakpoint = ENABLE_BREAKPOINT(bpno); 
    uint32_t enable_breakwrite = ENABLE_BREAK_WRITE(bpno); 
    pid_t parent = getpid(); 
    int child_status = 0; 

    if (!(child = fork())) 
    { 
     int parent_status = 0; 
     if (ptrace(PTRACE_ATTACH, parent, NULL, NULL)) 
      _exit(1); 

     while (!WIFSTOPPED(parent_status)) 
      waitpid(parent, &parent_status, 0); 

     /* 
     * set the breakpoint address. 
     */ 
     if (ptrace(PTRACE_POKEUSER, 
        parent, 
        offsetof(struct user, u_debugreg[bpno]), 
        addr)) 
      _exit(1); 

     /* 
     * set parameters for when the breakpoint should be triggered. 
     */ 
     if (ptrace(PTRACE_POKEUSER, 
        parent, 
        offsetof(struct user, u_debugreg[7]), 
        enable_breakwrite | enable_breakpoint)) 
      _exit(1); 

     if (ptrace(PTRACE_DETACH, parent, NULL, NULL)) 
      _exit(1); 

     _exit(0); 
    } 

    waitpid(child, &child_status, 0); 

    signal(SIGTRAP, handler); 

    if (WIFEXITED(child_status) && !WEXITSTATUS(child_status)) 
     return true; 
    return false; 
} 

/* 
* This function will disable a breakpoint by 
* invoking install_breakpoint is a 0x0 _addr_ 
* and no handler function. See comments above 
* for implementation details. 
*/ 
bool disable_breakpoint(int bpno) 
{ 
    return install_breakpoint(0x0, bpno, NULL); 
} 

/* 
* Example of how to use this /library/. 
*/ 
int handled = 0; 

void handle(int s) { 
    handled = 1; 
    return; 
} 

int main(int argc, char **argv) { 
    int a = 0; 

    if (!install_breakpoint(&a, 0, handle)) 
     printf("failed to set the breakpoint!\n"); 

    a = 1; 
    printf("handled: %d\n", handled); 

    if (!disable_breakpoint(0)) 
     printf("failed to disable the breakpoint!\n"); 

    return 1; 
} 

が、私はこれが誰かを助けることを願っています!あなたのリンクから

ウィル

+0

このリンクは質問に答えるかもしれませんが、答えの本質的な部分をここに含めて参考にしてください。リンクされたページが変更された場合、リンクのみの回答は無効になります。 - [レビューから](/レビュー/低品質の投稿/ 17842042) –

+0

フィードバックに感謝します。私はそのようにしなければならないことを理解していませんでした。私は自分の答えを更新しました。ごめんなさい! –

関連する問題