为什么gdb在gcc输出上工作不正常?

时间:2017-07-15 08:40:45

标签: c gcc gdb

让我们使用gcc -g -O0 -o prog prog.c编译以下程序并在其上运行gdb。如果我们一步一步走,我们会在行后看到

4     switch (c) {

直接行

38    return 0;

这是错误的,因为它首先必须去行

32          break;

适用于使用clang -g -O0 -o prog prog.c

生成的输出

GCC版本: gcc(Debian 6.4.0-1)6.4.0 20170704

clang版本: 3.8.1-24(标签/ RELEASE_381 / final)

int main(void)
{
  char c = '\x1a';
  switch (c) {
    case '\x18': /* C-x */
        break;
    case '\x12': /* C-r */
        break;
    case '\x13': /* C-s */
        break;
    case '\x10': /* C-p */
        break;
    case '\x0e': /* C-n */
        break;
    case '\x02': /* C-b */
        break;
    case '\x06': /* C-f */
        break;
    case '\x05': /* C-e */
        break;
    case '\x01': /* C-a */
        break;
    case '\x04': /* C-d */
        break;
    case '\x08': /* C-h */
        break;
    case '\x1d': /* C-] */
        break;
    case '\x16': /* C-v */
        break;
    case '\x1a': /* C-z */
        break;
    case '\x0d': /* C-m */
        break;
    default:
        (void) c;
  }
  return 0;
}

1 个答案:

答案 0 :(得分:3)

gdb不能正常工作,它只会跨越编译器生成的代码。对于这个无效的虚拟示例,gcc会生成this code

main:
        push    rbp
        mov     rbp, rsp
        mov     BYTE PTR [rbp-1], 26
        movsx   eax, BYTE PTR [rbp-1]
        cmp     eax, 29
        ja      .L2
        mov     eax, eax
        mov     rax, QWORD PTR .L4[0+rax*8]
        jmp     rax
.L4:
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
.L2:
        mov     eax, 0
        pop     rbp
        ret

没有为break语句生成相应的代码,因此gdb无法遍历它们,因为它们不存在。

另一方面,clang为break语句生成了另一个more verbose code。这就是你可以走过它们的原因:

main:                                   # @main
        push    rbp
        mov     rbp, rsp
        mov     dword ptr [rbp - 4], 0
        mov     byte ptr [rbp - 5], 26
        movsx   eax, byte ptr [rbp - 5]
        dec     eax
        mov     ecx, eax
        sub     eax, 28
        mov     qword ptr [rbp - 16], rcx # 8-byte Spill
        mov     dword ptr [rbp - 20], eax # 4-byte Spill
        ja      .LBB0_16
        mov     rax, qword ptr [rbp - 16] # 8-byte Reload
        mov     rcx, qword ptr [8*rax + .LJTI0_0]
        jmp     rcx
.LBB0_1:
        jmp     .LBB0_17
.LBB0_2:
        jmp     .LBB0_17
.LBB0_3:
        jmp     .LBB0_17
.LBB0_4:
        jmp     .LBB0_17
.LBB0_5:
        jmp     .LBB0_17
.LBB0_6:
        jmp     .LBB0_17
.LBB0_7:
        jmp     .LBB0_17
.LBB0_8:
        jmp     .LBB0_17
.LBB0_9:
        jmp     .LBB0_17
.LBB0_10:
        jmp     .LBB0_17
.LBB0_11:
        jmp     .LBB0_17
.LBB0_12:
        jmp     .LBB0_17
.LBB0_13:
        jmp     .LBB0_17
.LBB0_14:
        jmp     .LBB0_17
.LBB0_15:
        jmp     .LBB0_17
.LBB0_16:
        jmp     .LBB0_17
.LBB0_17:
        xor     eax, eax
        pop     rbp
        ret
.LJTI0_0:
        .quad   .LBB0_9
        .quad   .LBB0_6
        .quad   .LBB0_16
        .quad   .LBB0_10
        .quad   .LBB0_8
        .quad   .LBB0_7
        .quad   .LBB0_16
        .quad   .LBB0_11
        .quad   .LBB0_16
        .quad   .LBB0_16
        .quad   .LBB0_16
        .quad   .LBB0_16
        .quad   .LBB0_15
        .quad   .LBB0_5
        .quad   .LBB0_16
        .quad   .LBB0_4
        .quad   .LBB0_16
        .quad   .LBB0_2
        .quad   .LBB0_3
        .quad   .LBB0_16
        .quad   .LBB0_16
        .quad   .LBB0_13
        .quad   .LBB0_16
        .quad   .LBB0_1
        .quad   .LBB0_16
        .quad   .LBB0_14
        .quad   .LBB0_16
        .quad   .LBB0_16
        .quad   .LBB0_12

如果你想让gcc为break语句生成代码,你应该改变你的例子来至少在switch语句中做一些事情。例如,再添加一个变量i并在switch语句中更改它的值:

int main(void)
{
  int i = 0;
  char c = '\x1a';
  switch (c) {
    case '\x18': /* C-x */
        break;
    case '\x12': /* C-r */
        break;
    case '\x13': /* C-s */
        break;
    case '\x10': /* C-p */
        break;
    case '\x0e': /* C-n */
        break;
    case '\x02': /* C-b */
        break;
    case '\x06': /* C-f */
        break;
    case '\x05': /* C-e */
        break;
    case '\x01': /* C-a */
        break;
    case '\x04': /* C-d */
        break;
    case '\x08': /* C-h */
        break;
    case '\x1d': /* C-] */
        break;
    case '\x16': /* C-v */
        break;
    case '\x1a': /* C-z */
        i = 1;
        break;
    case '\x0d': /* C-m */
        break;
    default:
        (void) c;
  }
  return 0;
}