アセンブラの勉強のために、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オプションが付くようです。
関連記事
コメントを残す