gccのアセンブル出力を解析する時は-gオプションが便利

アセンブラの勉強のために、gccで-Sオプションを使用してC言語のコードからアセンブラのコードを出力していたのですが、ソースコード中の何行目がどの出力に対応するのか分かり辛く、苦労していました。

もちろん注意深く読めば対応は分かるのですが、本質的でないところに時間が取られてしまい、本来の解析が出来なくなってしまうので非効率です。

と思ってたのですが、コンパイル時に-gオプションをつけ、デバッグ情報を付与させると、ソースとアセンブリの対応が出力される事に最近気づきました。

以下、試してみた結果です。


こちらがテストに使ったコード

#include<stdio.h>
 
int main()
{
    int a;
    int b;
    int c;
 
    a = 2;
    b = 5;
    c = a + b;
    printf( "%d\n", c );
 
 
}



普通に “gcc -S test10.c”でコンパイル(アセンブリの出力)を行った場合

    .file   "test10.c"
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    movl    $2, 20(%esp)
    movl    $5, 24(%esp)
    movl    24(%esp), %eax
    movl    20(%esp), %edx
    addl    %edx, %eax
    movl    %eax, 28(%esp)
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"



“gcc -S -g test10.c”でコンパイルした場合
(非常に長いので、ざっと読み流してください)

    .file   "test10.c"
    .text
.Ltext0:
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .file 1 "test10.c"
    .loc 1 4 0
    .cfi_startproc
    pushl   %ebp
.LCFI0:
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
.LCFI1:
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    .loc 1 9 0
    movl    $2, 20(%esp)
    .loc 1 10 0
    movl    $5, 24(%esp)
    .loc 1 11 0
    movl    24(%esp), %eax
    movl    20(%esp), %edx
    addl    %edx, %eax
    movl    %eax, 28(%esp)
    .loc 1 12 0
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    .loc 1 15 0
    leave
    .cfi_restore 5
.LCFI2:
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
.Letext0:
    .section    .debug_info,"",@progbits
.Ldebug_info0:
    .long   0xac
    .value  0x2
    .long   .Ldebug_abbrev0
    .byte   0x4
    .uleb128 0x1
    .long   .LASF10
    .byte   0x1
    .long   .LASF11
    .long   .LASF12
    .long   .Ltext0
    .long   .Letext0
    .long   .Ldebug_line0
    .uleb128 0x2
    .byte   0x4
    .byte   0x7
    .long   .LASF0
    .uleb128 0x2
    .byte   0x1
    .byte   0x8
    .long   .LASF1
    .uleb128 0x2
    .byte   0x2
    .byte   0x7
    .long   .LASF2
    .uleb128 0x2
    .byte   0x4
    .byte   0x7
    .long   .LASF3
    .uleb128 0x2
    .byte   0x1
    .byte   0x6
    .long   .LASF4
    .uleb128 0x2
    .byte   0x2
    .byte   0x5
    .long   .LASF5
    .uleb128 0x3
    .byte   0x4
    .byte   0x5
    .string "int"
    .uleb128 0x2
    .byte   0x8
    .byte   0x5
    .long   .LASF6
    .uleb128 0x2
    .byte   0x8
    .byte   0x7
    .long   .LASF7
    .uleb128 0x2
    .byte   0x4
    .byte   0x5
    .long   .LASF8
    .uleb128 0x2
    .byte   0x1
    .byte   0x6
    .long   .LASF9
    .uleb128 0x4
    .byte   0x1
    .long   .LASF13
    .byte   0x1
    .byte   0x3
    .long   0x4f
    .long   .LFB0
    .long   .LFE0
    .long   .LLST0
    .uleb128 0x5
    .string "a"
    .byte   0x1
    .byte   0x5
    .long   0x4f
    .byte   0x2
    .byte   0x74
    .sleb128 20
    .uleb128 0x5
    .string "b"
    .byte   0x1
    .byte   0x6
    .long   0x4f
    .byte   0x2
    .byte   0x74
    .sleb128 24
    .uleb128 0x5
    .string "c"
    .byte   0x1
    .byte   0x7
    .long   0x4f
    .byte   0x2
    .byte   0x74
    .sleb128 28
    .byte   0
    .byte   0
    .section    .debug_abbrev,"",@progbits
.Ldebug_abbrev0:
    .uleb128 0x1
    .uleb128 0x11
    .byte   0x1
    .uleb128 0x25
    .uleb128 0xe
    .uleb128 0x13
    .uleb128 0xb
    .uleb128 0x3
    .uleb128 0xe
    .uleb128 0x1b
    .uleb128 0xe
    .uleb128 0x11
    .uleb128 0x1
    .uleb128 0x12
    .uleb128 0x1
    .uleb128 0x10
    .uleb128 0x6
    .byte   0
    .byte   0
    .uleb128 0x2
    .uleb128 0x24
    .byte   0
    .uleb128 0xb
    .uleb128 0xb
    .uleb128 0x3e
    .uleb128 0xb
    .uleb128 0x3
    .uleb128 0xe
    .byte   0
    .byte   0
    .uleb128 0x3
    .uleb128 0x24
    .byte   0
    .uleb128 0xb
    .uleb128 0xb
    .uleb128 0x3e
    .uleb128 0xb
    .uleb128 0x3
    .uleb128 0x8
    .byte   0
    .byte   0
    .uleb128 0x4
    .uleb128 0x2e
    .byte   0x1
    .uleb128 0x3f
    .uleb128 0xc
    .uleb128 0x3
    .uleb128 0xe
    .uleb128 0x3a
    .uleb128 0xb
    .uleb128 0x3b
    .uleb128 0xb
    .uleb128 0x49
    .uleb128 0x13
    .uleb128 0x11
    .uleb128 0x1
    .uleb128 0x12
    .uleb128 0x1
    .uleb128 0x40
    .uleb128 0x6
    .byte   0
    .byte   0
    .uleb128 0x5
    .uleb128 0x34
    .byte   0
    .uleb128 0x3
    .uleb128 0x8
    .uleb128 0x3a
    .uleb128 0xb
    .uleb128 0x3b
    .uleb128 0xb
    .uleb128 0x49
    .uleb128 0x13
    .uleb128 0x2
    .uleb128 0xa
    .byte   0
    .byte   0
    .byte   0
    .section    .debug_loc,"",@progbits
.Ldebug_loc0:
.LLST0:
    .long   .LFB0-.Ltext0
    .long   .LCFI0-.Ltext0
    .value  0x2
    .byte   0x74
    .sleb128 4
    .long   .LCFI0-.Ltext0
    .long   .LCFI1-.Ltext0
    .value  0x2
    .byte   0x74
    .sleb128 8
    .long   .LCFI1-.Ltext0
    .long   .LCFI2-.Ltext0
    .value  0x2
    .byte   0x75
    .sleb128 8
    .long   .LCFI2-.Ltext0
    .long   .LFE0-.Ltext0
    .value  0x2
    .byte   0x74
    .sleb128 4
    .long   0
    .long   0
    .section    .debug_aranges,"",@progbits
    .long   0x1c
    .value  0x2
    .long   .Ldebug_info0
    .byte   0x4
    .byte   0
    .value  0
    .value  0
    .long   .Ltext0
    .long   .Letext0-.Ltext0
    .long   0
    .long   0
    .section    .debug_line,"",@progbits
.Ldebug_line0:
    .section    .debug_str,"MS",@progbits,1
.LASF6:
    .string "long long int"
.LASF2:
    .string "short unsigned int"
.LASF0:
    .string "unsigned int"
.LASF11:
    .string "test10.c"
.LASF3:
    .string "long unsigned int"
.LASF7:
    .string "long long unsigned int"
.LASF1:
    .string "unsigned char"
.LASF13:
    .string "main"
.LASF8:
    .string "long int"
.LASF10:
    .string "GNU C 4.6.1"
.LASF4:
    .string "signed char"
.LASF5:
    .string "short int"
.LASF12:
    .string "/home/..."
.LASF9:
    .string "char"
    .ident  "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
    .section    .note.GNU-stack,"",@progbits




上記のように、-gオプションつきでコンパイルすると、出力が長くなりすぎるので敬遠してたのですが、コードが出力されている箇所を見ると、 “.loc”の行で.cファイルのソースコードが表示してくれています。

先ほどの出力結果から、該当箇所を抜粋すると…

    ...
.LCFI1:
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    .loc 1 9 0                     <- 1 9 0の"9"が行番号をあらわしている!!
    movl    $2, 20(%esp)
    .loc 1 10 0
    movl    $5, 24(%esp)



といった感じになり、.cファイルでの9行目が”movl $2, 20(%esp)”に変換された事が分かります。

ちなみに.locディレクティブ自体の書式は、以下の仕様だそうです。

.loc ファイル番号 行番号 [カラム位置] [オプション]





また、.cファイル上の1行が、アセンブラで複数個所に分かれる場合は、以下の出力になります。
※for文は通常、初期条件、終了条件、繰り返し処理の3つに分かれます。

ソース

#include<stdio.h>
 
int main()
{
    int i;
    for ( i = 0; i < 10; i++ ) {
        printf( "%d\n", i );
    }
}



gcc -S -g test10.cの出力(main関数のところだけ抜粋)

main:
.LFB0:
    .file 1 "test10.c"
    .loc 1 4 0
    .cfi_startproc
    pushl   %ebp
.LCFI0:
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
.LCFI1:
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    .loc 1 6 0
    movl    $0, 28(%esp)
    jmp .L2
.L3:
    .loc 1 7 0 discriminator 2
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    .loc 1 6 0 discriminator 2
    addl    $1, 28(%esp)
.L2:
    .loc 1 6 0 is_stmt 0 discriminator 1
    cmpl    $9, 28(%esp)
    jle .L3
    .loc 1 9 0 is_stmt 1
    leave
    .cfi_restore 5
.LCFI2:
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc




“.loc x 6″の記述が3箇所に分かれているのが確認できます。

i = 0; の部分

    .loc 1 6 0
    movl    $0, 28(%esp)
    jmp .L2



i++ の部分

    .loc 1 6 0 discriminator 2
    addl    $1, 28(%esp)



i < 10; の部分

    .loc 1 6 0 is_stmt 0 discriminator 1
    cmpl    $9, 28(%esp)
    jle .L3



複数個所に分かれる場合は、.locにdiscriminatorオプションが付くようです。

関連記事

コメントを残す

メールアドレスが公開されることはありません。