上一篇文章 中,我们提到了在 CCL 中编写汇编代码的方法。那么,怎么对这些汇编代码设断点并调试呢?这就需要使用 gdb 了。

通过 gdb 加载 CCL

首先,确保拥有全套 CCL 的源代码,假设ccl的binaries和源代码都在 ~/ccl中。

编译 CCL

在第一次加载前,先完整的重新编译一次 CCL 的源代码,确保 binaries 和 源代码 的内容保持同步。进入 CCL 后,执行如下代码。

1
(rebuild-ccl :full t)

编译完成后,退出 CCL。

加载 CCL

首先确保当前目录在 CCL 所在的目录。使用以下代码启动CCL(假设当前使用的是 x86_64 Linux 版本的CCL)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ gdb ./lx86cl64
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
Copyright (C) 2018 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-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
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 ./lx86cl64...done.

# 加载 CCL 配套的调试脚本。
(gdb) source lisp-kernel/linuxx8664/.gdbinit
Breakpoint 1 at 0x2a2e0: file ../lisp-debug.c, line 1579.

# 启动 CCL 。
(gdb) r
Starting program: /home/xps13/ccl/lx86cl64
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffd6fd1700 (LWP 32756)]
Clozure Common Lisp Version 1.12 (v1.12-2-g5d13fc7d) LinuxX8664

For more information about CCL, please see http://ccl.clozure.com.

CCL is free software. It is distributed under the terms of the Apache
Licence, Version 2.0.
?

CCL 启动后,可以随时通过 Ctrl + C 退回 gdb ,也可以随时通过 gdb 的 continue 指令返回 CCL 。

获取函数地址

假设我们已经通过LOAD加载了上一篇文章的代码,那么就有了函数MY-ADD-1。此时可以在 CCL 中获得函数的地址,用下面的代码。

1
2
? (ccl:%address-of #'my-add-1)
52914002145343

现在切换到 gdb ,执行如下操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 获取 MY-ADD-1 的地址。
(gdb) call find_symbol("MY-ADD-1")
$1 = 52914002159853

# 减去 x8664::fulltag-misc 。
(gdb) x/gx $1 - 13
0x3020004d68e0: 0x0000000000000715 # header
(gdb)
0x3020004d68e8: 0x00003020004d692d # pname
(gdb)
0x3020004d68f0: 0x0000000000000012 # value
(gdb)
0x3020004d68f8: 0x00003020004d303f # function

最终获得的地址 0x00003020004d303f 就是函数本体的地址,和 CCL:%ADDRESS-OF 获得的地址是相同的。

让我们反汇编确认下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(gdb) x/30i 0x00003020004d303f
0x3020004d303f: lea -0x7(%rip),%r13 # 0x3020004d303f
0x3020004d3046: cmp $0x10,%ecx
0x3020004d3049: jne 0x3020004d307c
0x3020004d304b: test $0x7,%dil
0x3020004d304f: je,pt 0x3020004d3054
0x3020004d3052: int $0xf7
0x3020004d3054: test $0x7,%sil
0x3020004d3058: je,pt 0x3020004d305d
0x3020004d305b: int $0xf6
0x3020004d305d: mov %rdi,%rax
0x3020004d3060: sar $0x3,%rax
0x3020004d3064: mov %rsi,%rdx
0x3020004d3067: sar $0x3,%rdx
0x3020004d306b: mov %rax,%rcx
0x3020004d306e: add %rdx,%rcx
0x3020004d3071: imul $0x8,%rcx,%rsi
0x3020004d3075: retq
0x3020004d3076: xchg %ax,%ax
0x3020004d3078: (bad)
0x3020004d3079: add %al,(%rax)
0x3020004d307b: add %cl,%ch
0x3020004d307d: retq $0x9000
0x3020004d3080: repnz add %al,(%rax)
0x3020004d3083: add %al,(%rax)
0x3020004d3085: add %al,(%rax)
0x3020004d3087: add %ch,%dh
0x3020004d3089: pushq $0x3020004d
0x3020004d308e: add %al,(%rax)
0x3020004d3090: add %dl,(%rax)
0x3020004d3092: add %al,(%rax)

Bingo ! 就是这个地址!

调试

既然获得了地址,那就可以随便下断点啦!如下所示。

1
2
(gdb) br *0x00003020004d303f
Breakpoint 2 at 0x3020004d303f

然后,回到 CCL ,调用 MY-ADD-1

1
2
3
4
5
6
7
? (my-add-1 100 200)
[Switching to Thread 0x7fffd6fd1700 (LWP 32756)]

Thread 2 "listener" hit Breakpoint 2, 0x00003020004d303f in ?? ()
1: x/i $pc
=> 0x3020004d303f: lea -0x7(%rip),%r13 # 0x3020004d303f
(gdb)

停下来了!接下来可以用各种常规调试方法进行调试了!单步等常规调试方法想必大家都很熟悉了,这里就不说了。