Open imshota opened 3 years ago
演習2 自分のOSSプロダクトに対し,GDBでブレークポイントを設定して変数値を確認せよ
作業記録
gdbコマンドがないと言われたので、brewでインストールする。
brew install gdb
macOSではgdbを使うのに、コード署名が必要らしい。 https://qiita.com/takahashim/items/204ffa698afe09bd4e28
% gdb gdbtest
GNU gdb (GDB) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin19.5.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
--Type <RET> for more, q to quit, c to continue without paging--c
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from gdbtest...
Reading symbols from /Users/shota/Documents/tsclass/プログラム応用開発/GDB/gdbtest.dSYM/Contents/Resources/DWARF/gdbtest...
(gdb) run
Starting program: /Users/shota/Documents/tsclass/プログラム応用開発/GDB/gdbtest
Unable to find Mach task port for process-id 55456: (os/kern) failure (0x5).
よって、macのデバッグツールである、lldbを使うことにした。 lldbのコマンドは、こちらのサイトを参考にした。 https://yokaze.github.io/2018/01/06/ コマンド一覧はhelpでも見ることは可能。
使えそうなコマンドをまとめる。 | コマンド | 意味 |
---|---|---|
r/run | デバッガ上で実行可能ファイルを起動 | |
s | 現在の位置から 1 行分だけ処理を進める | |
b | ブレークポイントを設定する | |
c | 現在のプロセスのすべてのスレッドの実行を再開 | |
p | 現在のスレッド上で式を評価する | |
log | LLDB の内部ログを制御するためのコマンド |
% lldb gdbtest
(lldb) target create "gdbtest"
Current executable set to '/Users/shota/Documents/tsclass/プログラム応用開発/GDB/gdbtest' (x86_64).
(lldb) b sanitize.cpp:3
Breakpoint 1: where = gdbtest`main + 29 at sanitize.cpp:4:11, address = 0x0000000100003f5d
(lldb) run
Process 55886 launched: '/Users/shota/Documents/tsclass/プログラム応用開発/GDB/gdbtest' (x86_64)
Process 55886 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100003f5d gdbtest`main at sanitize.cpp:4:11
1 #include <iostream>
2 int main() {
3 int a[3];
-> 4 a[1000] = 2;
5 return 0;
6 }
Target 0: (gdbtest) stopped.
lldb) p a
(int [3]) $0 = ([0] = 0, [1] = -272632288, [2] = 32766)
(lldb) c
Process 55886 resuming
Process 55886 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x7ffeefc0059c)
frame #0: 0x0000000100003f5d gdbtest`main at sanitize.cpp:4:11
1 #include <iostream>
2 int main() {
3 int a[3];
-> 4 a[1000] = 2;
5 return 0;
6 }
Target 0: (gdbtest) stopped.
演習3 コアダンプを利用したデバッグを試せ
作業記録
自分の環境だと、/coresに権限がかかっているので、chmodで/coresの権限を変える。 また、ulimitもunlimitedに変える。
% sudo chmod 777 /cores
% ulimit -c unlimited
% ./coretest
zsh: segmentation fault (core dumped) ./coretest
% ls /cores
core.2110
lldbでコアコアダンプを解析
% lldb ./coretest /cores/core.2110
(lldb) target create "./coretest"
Current executable set to '/Users/shota/Documents/tsclass/プログラム応用開発/GDB/coretest' (x86_64).
(lldb) settings set -- target.run-args "/cores/core.2110"
(lldb) run
Process 2174 launched: '/Users/shota/Documents/tsclass/プログラム応用開発/GDB/coretest' (x86_64)
Process 2174 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x7ffeefc0079c)
frame #0: 0x0000000100003f5d coretest`main at sanitize.cpp:4:11
1 #include <iostream>
2 int main() {
3 int a[3];
-> 4 a[1000] = 2;
5 return 0;
6 }
Target 0: (coretest) stopped.
演習4 自分のOSSプロダクトにint3命令を埋め込んでデバッグを実験せよ
作業記録 ターゲットプログラム int3test.cpp
#include <iostream>
int main() {
int a[3];
a[0] = 0;
asm("int3");
a[1] = 1;
asm("int3");
a[2] = 2;
asm("int3");
a[3] = 3;
asm("int3");
return 0;
}
lldbでデバッグをかけてみるとa[3]=3でstop reason = signal SIGABRTとなっており、 ここが悪いことがわかる。
% lldb ./int3test
(lldb) target create "./int3test"
Current executable set to '/Users/shota/Documents/tsclass/プログラム応用開発/GDB/int3test' (x86_64).
(lldb) run
Process 2380 launched: '/Users/shota/Documents/tsclass/プログラム応用開発/GDB/int3test' (x86_64)
Process 2380 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
frame #0: 0x0000000100003f55 int3test`main at int3test.cpp:6:8
3 int a[3];
4 a[0] = 0;
5 asm("int3");
-> 6 a[1] = 1;
7 asm("int3");
8 a[2] = 2;
9 asm("int3");
Target 0: (int3test) stopped.
(lldb) c
Process 2380 resuming
Process 2380 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
frame #0: 0x0000000100003f5d int3test`main at int3test.cpp:8:8
5 asm("int3");
6 a[1] = 1;
7 asm("int3");
-> 8 a[2] = 2;
9 asm("int3");
10 a[3] = 3;
11 asm("int3");
Target 0: (int3test) stopped.
(lldb) c
Process 2380 resuming
Process 2380 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
frame #0: 0x0000000100003f65 int3test`main at int3test.cpp:10:8
7 asm("int3");
8 a[2] = 2;
9 asm("int3");
-> 10 a[3] = 3;
11 asm("int3");
12 return 0;
13 }
Target 0: (int3test) stopped.
(lldb) c
Process 2380 resuming
Process 2380 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
frame #0: 0x0000000100003f6d int3test`main at int3test.cpp:11:3
8 a[2] = 2;
9 asm("int3");
10 a[3] = 3;
-> 11 asm("int3");
12 return 0;
13 }
Target 0: (int3test) stopped.
(lldb) c
Process 2380 resuming
Process 2380 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
frame #0: 0x00007fff6954333a libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
-> 0x7fff6954333a <+10>: jae 0x7fff69543344 ; <+20>
0x7fff6954333c <+12>: movq %rax, %rdi
0x7fff6954333f <+15>: jmp 0x7fff6953d629 ; cerror_nocancel
0x7fff69543344 <+20>: retq
Target 0: (int3test) stopped.
演習5 自分のOSSプロダクトの変数変化をウォッチポイントを用いて観察せよ
作業記録
g++ -g watchtest.cpp -o watchtest
一回main関数にブレイクポイントをおき、 そのあとで、watchlistに付け加える。 sumがold value からnew valueに移り変わっているところで止まっているのがわかる。
% lldb ./watchtest
(lldb) target create "./watchtest"
Current executable set to '/Users/shota/Documents/tsclass/プログラム応用開発/GDB/watchtest' (x86_64).
(lldb) breakpoint set -n main
Breakpoint 1: where = watchtest`main + 8 at watchtest.cpp:3:7, address = 0x0000000100003e37
(lldb) run
Process 2679 launched: '/Users/shota/Documents/tsclass/プログラム応用開発/GDB/watchtest' (x86_64)
Process 2679 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100003e37 watchtest`main at watchtest.cpp:3:7
1 #include <iostream>
2 int main() {
-> 3 int a[3] = {2, 4, 6};
4 int sum = 0;
5 for (int i = 0; i < 2; i++) {
6 sum += a[i];
7 }
Target 0: (watchtest) stopped.
(lldb) watchpoint set variable sum
Watchpoint created: Watchpoint 1: addr = 0x7ffeefbff57c size = 4 state = enabled type = w
declare @ '/Users/shota/Documents/tsclass/プログラム応用開発/GDB/watchtest.cpp:4'
watchpoint spec = 'sum'
new value: 1
(lldb) c
Process 2679 resuming
Watchpoint 1 hit:
old value: 1
new value: 0
Process 2679 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
frame #0: 0x0000000100003e53 watchtest`main at watchtest.cpp:5:12
2 int main() {
3 int a[3] = {2, 4, 6};
4 int sum = 0;
-> 5 for (int i = 0; i < 2; i++) {
6 sum += a[i];
7 }
8 std::cout << sum << std::endl;
Target 0: (watchtest) stopped.
(lldb) c
Process 2679 resuming
Watchpoint 1 hit:
old value: 0
new value: 2
Process 2679 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
frame #0: 0x0000000100003e6c watchtest`main at watchtest.cpp:5:3
2 int main() {
3 int a[3] = {2, 4, 6};
4 int sum = 0;
-> 5 for (int i = 0; i < 2; i++) {
6 sum += a[i];
7 }
8 std::cout << sum << std::endl;
Target 0: (watchtest) stopped.
(lldb) c
Process 2679 resuming
Watchpoint 1 hit:
old value: 2
new value: 6
Process 2679 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
frame #0: 0x0000000100003e6c watchtest`main at watchtest.cpp:5:3
2 int main() {
3 int a[3] = {2, 4, 6};
4 int sum = 0;
-> 5 for (int i = 0; i < 2; i++) {
6 sum += a[i];
7 }
8 std::cout << sum << std::endl;
Target 0: (watchtest) stopped.
作業6 自分のOSSプロダクトのデバッグを作業記録を取りながら行おう
作業記録 線形リストのやってしまう典型的パターン test.c
#include <stdio.h>
typedef struct pstruct {
int value;
struct pstruct* next;
} plist;
plist* Print(plist *p)
{
printf("%d\n",p->value);
if(p->next != NULL){
Print(p->next);
}
}
int main ()
{
plist a, b;
a.value = 1;
a.next = &b;
b.value = 2;
Print(&a);
Print(NULL);
}
これで実際に実行すると
% clang test.c -o test
% ./test
1
2
zsh: segmentation fault ./test
Printは線形リストの内容を表示するものだが、NULLが渡されるとNULL参照になってしまう。 では、これをデバッグで見つけてみる。
% clang -g -fsanitize=undefined test.c -o test
% ./test
1
2
test.c:10:20: runtime error: member access within null pointer of type 'plist' (aka 'struct pstruct')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior test.c:10:20 in
test.c:10:20: runtime error: load of null pointer of type 'int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior test.c:10:20 in
UndefinedBehaviorSanitizer:DEADLYSIGNAL
==5205==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0001080abd56 bp 0x7ffee7b57650 sp 0x7ffee7b57610 T156378)
==5205==The signal is caused by a READ memory access.
==5205==Hint: address points to the zero page.
#0 0x1080abd56 in Print test.c:10
#1 0x1080abf12 in main test.c:24
#2 0x7fff693fbcc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)
==5205==Register values:
rax = 0x0000000000000000 rbx = 0x0000000000000000 rcx = 0x0000000000000000 rdx = 0x000000000000003f
rdi = 0x0000000000000000 rsi = 0x0000000108bdbf80 rbp = 0x00007ffee7b57650 rsp = 0x00007ffee7b57610
r8 = 0x00000001081eb100 r9 = 0x00007ffee7b568e0 r10 = 0x0000000000000000 r11 = 0x0000000000000206
r12 = 0x0000000000000000 r13 = 0x0000000000000000 r14 = 0x0000000000000000 r15 = 0x0000000000000000
UndefinedBehaviorSanitizer can not provide additional info.
SUMMARY: UndefinedBehaviorSanitizer: SEGV test.c:10 in Print
==5205==ABORTING
zsh: abort ./test
10行目でNULL参照しているんだなということがわかる。 他にも試してみる。
% lldb ./test
(lldb) target create "./test"
Current executable set to '/Users/shota/Documents/tsclass/プログラム応用開発/GDB/test' (x86_64).
(lldb) run
Process 5218 launched: '/Users/shota/Documents/tsclass/プログラム応用開発/GDB/test' (x86_64)
1
2
Process 5218 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = Null pointer use
frame #0: 0x000000010011eb60 libclang_rt.ubsan_osx_dynamic.dylib`__ubsan_on_report
libclang_rt.ubsan_osx_dynamic.dylib`__ubsan_on_report:
-> 0x10011eb60 <+0>: pushq %rbp
0x10011eb61 <+1>: movq %rsp, %rbp
0x10011eb64 <+4>: popq %rbp
0x10011eb65 <+5>: retq
Target 0: (test) stopped.
lldbでのデバッグは、原因が分かりやすいような気がする。
演習1 自分のOSSプロダクトをUBサニタイザで検査してみよ
作業記録
clang -fsanitize=hoge foo.c サニタイザができる。(hoge = undefinedだとUBサニタイザ)
以下のサンプルコードsanitize.cppを使用
やってみる。
4行目がおかしいことを言ってくれる。
-fanitize=addressでもやってみる。
スタックオーバーフローのことを通知してくれた。