通过阅读 gcc 输出来学习汇编
Posted
技术标签:
【中文标题】通过阅读 gcc 输出来学习汇编【英文标题】:learning assembly by reading gcc output 【发布时间】:2015-08-13 05:01:47 【问题描述】:我试图通过阅读 GCC 生成的输出来理解程序集。这是我在 C++ 中的代码:
int main()
int x = 8;
我使用以下方法编译它:
g++ -g -Wa,-alh=source.s -masm=intel -fverbose-asm -O2 -g -Wall -c -fmessage-length=0 -o source.o "..\\source.cpp"
但是,即使使用 cmets,我也完全不了解生成的所有内容:
GAS LISTING C:\Users\Silkworm\AppData\Local\Temp\ccmDrbTs.s page 1
1 .file "source.cpp"
2 .intel_syntax noprefix
3 # GNU C++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) version 5.1.0 (x86_64-w64-mingw32)
4 # compiled by GNU C version 5.1.0, GMP version 6.0.0, MPFR version 3.1.2-p11, MPC version 1.0.3
5 # GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
6 # options passed:
7 # -iprefix C:/mingw-w64/x86_64-5.1.0-posix-seh-rt_v4-rev0/mingw64/bin/../lib/gcc/x86_64-w64-mingw3
8 # -D_REENTRANT ..\source.cpp -masm=intel -mtune=core2 -march=nocona
9 # -auxbase-strip source.o -g -g -O2 -Wall -fverbose-asm
10 # -fmessage-length=0
11 # options enabled: -faggressive-loop-optimizations -falign-labels
12 # -fasynchronous-unwind-tables -fauto-inc-dec -fbranch-count-reg
13 # -fcaller-saves -fchkp-check-incomplete-type -fchkp-check-read
14 # -fchkp-check-write -fchkp-instrument-calls -fchkp-narrow-bounds
15 # -fchkp-optimize -fchkp-store-bounds -fchkp-use-static-bounds
16 # -fchkp-use-static-const-bounds -fchkp-use-wrappers
17 # -fcombine-stack-adjustments -fcommon -fcompare-elim -fcprop-registers
18 # -fcrossjumping -fcse-follow-jumps -fdefer-pop
19 # -fdelete-null-pointer-checks -fdevirtualize -fdevirtualize-speculatively
20 # -fdwarf2-cfi-asm -fearly-inlining -feliminate-unused-debug-types
21 # -fexceptions -fexpensive-optimizations -fforward-propagate
22 # -ffunction-cse -fgcse -fgcse-lm -fgnu-runtime -fgnu-unique
23 # -fguess-branch-probability -fhoist-adjacent-loads -fident
24 # -fif-conversion -fif-conversion2 -findirect-inlining -finline
25 # -finline-atomics -finline-functions-called-once -finline-small-functions
26 # -fipa-cp -fipa-cp-alignment -fipa-icf -fipa-icf-functions
27 # -fipa-icf-variables -fipa-profile -fipa-pure-const -fipa-ra
28 # -fipa-reference -fipa-sra -fira-hoist-pressure -fira-share-save-slots
29 # -fira-share-spill-slots -fisolate-erroneous-paths-dereference -fivopts
30 # -fkeep-inline-dllexport -fkeep-static-consts -fleading-underscore
31 # -flifetime-dse -flra-remat -flto-odr-type-merging -fmath-errno
32 # -fmerge-constants -fmerge-debug-strings -fmove-loop-invariants
33 # -fomit-frame-pointer -foptimize-sibling-calls -foptimize-strlen
34 # -fpartial-inlining -fpeephole -fpeephole2 -fpic -fprefetch-loop-arrays
35 # -free -freg-struct-return -freorder-blocks -freorder-functions
36 # -frerun-cse-after-loop -fsched-critical-path-heuristic
37 # -fsched-dep-count-heuristic -fsched-group-heuristic -fsched-interblock
38 # -fsched-last-insn-heuristic -fsched-rank-heuristic -fsched-spec
39 # -fsched-spec-insn-heuristic -fsched-stalled-insns-dep -fschedule-fusion
40 # -fschedule-insns2 -fsemantic-interposition -fset-stack-executable
41 # -fshow-column -fshrink-wrap -fsigned-zeros -fsplit-ivs-in-unroller
42 # -fsplit-wide-types -fssa-phiopt -fstdarg-opt -fstrict-aliasing
43 # -fstrict-overflow -fstrict-volatile-bitfields -fsync-libcalls
44 # -fthread-jumps -ftoplevel-reorder -ftrapping-math -ftree-bit-ccp
45 # -ftree-builtin-call-dce -ftree-ccp -ftree-ch -ftree-coalesce-vars
46 # -ftree-copy-prop -ftree-copyrename -ftree-cselim -ftree-dce
47 # -ftree-dominator-opts -ftree-dse -ftree-forwprop -ftree-fre
48 # -ftree-loop-if-convert -ftree-loop-im -ftree-loop-ivcanon
49 # -ftree-loop-optimize -ftree-parallelize-loops= -ftree-phiprop -ftree-pre
50 # -ftree-pta -ftree-reassoc -ftree-scev-cprop -ftree-sink -ftree-slsr
51 # -ftree-sra -ftree-switch-conversion -ftree-tail-merge -ftree-ter
52 # -ftree-vrp -funit-at-a-time -funwind-tables -fvar-tracking
53 # -fvar-tracking-assignments -fverbose-asm -fzero-initialized-in-bss
54 # -m128bit-long-double -m64 -m80387 -maccumulate-outgoing-args
55 # -malign-double -malign-stringops -mcx16 -mfancy-math-387 -mfentry
56 # -mfp-ret-in-387 -mfxsr -mieee-fp -mlong-double-80 -mmmx -mms-bitfields
57 # -mno-sse4 -mpush-args -mred-zone -msse -msse2 -msse3 -mstack-arg-probe
GAS LISTING C:\Users\Silkworm\AppData\Local\Temp\ccmDrbTs.s page 2
58 # -mvzeroupper
59
60 .text
61 .Ltext0:
62 .cfi_sections .debug_frame
63 .def __main; .scl 2; .type 32; .endef
64 .section .text.startup,"x"
65 .p2align 4,,15
66 .globl main
67 .def main; .scl 2; .type 32; .endef
68 .seh_proc main
69 main:
70 .LFB0:
71 .file 1 "../source.cpp"
1:../source.cpp ****
2:../source.cpp **** int main()
72 .loc 1 2 0
73 .cfi_startproc
74 0000 4883EC28 sub rsp, 40 #,
75 .seh_stackalloc 40
76 .cfi_def_cfa_offset 48
77 .seh_endprologue
78 .loc 1 2 0
79 0004 E8000000 call __main #
79 00
80 .LVL0:
3:../source.cpp **** int x = 8;
4:../source.cpp ****
81 .loc 1 4 0
82 0009 31C0 xor eax, eax #
83 000b 4883C428 add rsp, 40 #,
84 .cfi_def_cfa_offset 8
85 000f C3 ret
86 .cfi_endproc
87 .LFE0:
88 .seh_endproc
89 .text
90 .Letext0:
91 .section .debug_info,"dr"
92 .Ldebug_info0:
93 0000 CF000000 .long 0xcf
94 0004 0400 .word 0x4
95 0006 00000000 .secrel32 .Ldebug_abbrev0
96 000a 08 .byte 0x8
97 000b 01 .uleb128 0x1
98 000c 474E5520 .ascii "GNU C++ 5.1.0 -masm=intel -mtune=core2 -march=nocona -g -g -O2 -fmessage-length=0\0"
98 432B2B20
98 352E312E
98 30202D6D
98 61736D3D
99 005e 04 .byte 0x4
100 005f 2E2E5C68 .ascii "..\\source.cpp\0"
100 656C6C6F
100 776F726C
100 642E6370
100 7000
101 0071 433A5C63 .ascii "C:\\cpp_workspace\\disam\\Debug\0"
GAS LISTING C:\Users\Silkworm\AppData\Local\Temp\ccmDrbTs.s page 3
101 70705F77
101 6F726B73
101 70616365
101 5C646973
102 008e 00000000 .secrel32 .Ldebug_ranges0+0
103 0092 00000000 .quad 0
103 00000000
104 009a 00000000 .secrel32 .Ldebug_line0
105 009e 02 .uleb128 0x2
106 009f 6D61696E .ascii "main\0"
106 00
107 00a4 01 .byte 0x1
108 00a5 02 .byte 0x2
109 00a6 CB000000 .long 0xcb
110 00aa 00000000 .quad .LFB0
110 00000000
111 00b2 10000000 .quad .LFE0-.LFB0
111 00000000
112 00ba 01 .uleb128 0x1
113 00bb 9C .byte 0x9c
114 00bc CB000000 .long 0xcb
115 00c0 03 .uleb128 0x3
116 00c1 7800 .ascii "x\0"
117 00c3 01 .byte 0x1
118 00c4 03 .byte 0x3
119 00c5 CB000000 .long 0xcb
120 00c9 08 .byte 0x8
121 00ca 00 .byte 0
122 00cb 04 .uleb128 0x4
123 00cc 04 .byte 0x4
124 00cd 05 .byte 0x5
125 00ce 696E7400 .ascii "int\0"
126 00d2 00 .byte 0
127 .section .debug_abbrev,"dr"
128 .Ldebug_abbrev0:
129 0000 01 .uleb128 0x1
130 0001 11 .uleb128 0x11
131 0002 01 .byte 0x1
132 0003 25 .uleb128 0x25
133 0004 08 .uleb128 0x8
134 0005 13 .uleb128 0x13
135 0006 0B .uleb128 0xb
136 0007 03 .uleb128 0x3
137 0008 08 .uleb128 0x8
138 0009 1B .uleb128 0x1b
139 000a 08 .uleb128 0x8
140 000b 55 .uleb128 0x55
141 000c 17 .uleb128 0x17
142 000d 11 .uleb128 0x11
143 000e 01 .uleb128 0x1
144 000f 10 .uleb128 0x10
145 0010 17 .uleb128 0x17
146 0011 00 .byte 0
147 0012 00 .byte 0
148 0013 02 .uleb128 0x2
149 0014 2E .uleb128 0x2e
150 0015 01 .byte 0x1
GAS LISTING C:\Users\Silkworm\AppData\Local\Temp\ccmDrbTs.s page 4
151 0016 3F .uleb128 0x3f
152 0017 19 .uleb128 0x19
153 0018 03 .uleb128 0x3
154 0019 08 .uleb128 0x8
155 001a 3A .uleb128 0x3a
156 001b 0B .uleb128 0xb
157 001c 3B .uleb128 0x3b
158 001d 0B .uleb128 0xb
159 001e 49 .uleb128 0x49
160 001f 13 .uleb128 0x13
161 0020 11 .uleb128 0x11
162 0021 01 .uleb128 0x1
163 0022 12 .uleb128 0x12
164 0023 07 .uleb128 0x7
165 0024 40 .uleb128 0x40
166 0025 18 .uleb128 0x18
167 0026 9642 .uleb128 0x2116
168 0028 19 .uleb128 0x19
169 0029 01 .uleb128 0x1
170 002a 13 .uleb128 0x13
171 002b 00 .byte 0
172 002c 00 .byte 0
173 002d 03 .uleb128 0x3
174 002e 34 .uleb128 0x34
175 002f 00 .byte 0
176 0030 03 .uleb128 0x3
177 0031 08 .uleb128 0x8
178 0032 3A .uleb128 0x3a
179 0033 0B .uleb128 0xb
180 0034 3B .uleb128 0x3b
181 0035 0B .uleb128 0xb
182 0036 49 .uleb128 0x49
183 0037 13 .uleb128 0x13
184 0038 1C .uleb128 0x1c
185 0039 0B .uleb128 0xb
186 003a 00 .byte 0
187 003b 00 .byte 0
188 003c 04 .uleb128 0x4
189 003d 24 .uleb128 0x24
190 003e 00 .byte 0
191 003f 0B .uleb128 0xb
192 0040 0B .uleb128 0xb
193 0041 3E .uleb128 0x3e
194 0042 0B .uleb128 0xb
195 0043 03 .uleb128 0x3
196 0044 08 .uleb128 0x8
197 0045 00 .byte 0
198 0046 00 .byte 0
199 0047 00 .byte 0
200 .section .debug_aranges,"dr"
201 0000 2C000000 .long 0x2c
202 0004 0200 .word 0x2
203 0006 00000000 .secrel32 .Ldebug_info0
204 000a 08 .byte 0x8
205 000b 00 .byte 0
206 000c 0000 .word 0
207 000e 0000 .word 0
GAS LISTING C:\Users\Silkworm\AppData\Local\Temp\ccmDrbTs.s page 5
208 0010 00000000 .quad .LFB0
208 00000000
209 0018 10000000 .quad .LFE0-.LFB0
209 00000000
210 0020 00000000 .quad 0
210 00000000
211 0028 00000000 .quad 0
211 00000000
212 .section .debug_ranges,"dr"
213 .Ldebug_ranges0:
214 0000 00000000 .quad .LFB0
214 00000000
215 0008 10000000 .quad .LFE0
215 00000000
216 0010 00000000 .quad 0
216 00000000
217 0018 00000000 .quad 0
217 00000000
218 .section .debug_line,"dr"
219 .Ldebug_line0:
220 0000 41000000 .section .debug_str,"dr"
220 02002800
220 00000101
220 FB0E0D00
220 01010101
221 .ident "GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 5.1.0"
这里没有我学到的一条指令,比如 movl、pop,也没有像 %eax 之类的寄存器或类似的东西。这与我在网上遇到的例子完全不同。我怎么理解这个。
【问题讨论】:
总结:这可能不是学习汇编的方法: 您的变量未使用。尝试以某种方式输出它。 一般来说,编译器生成的汇编代码与手动编写的汇编代码相比可能没有那么简单——有几个因素(如代码优化)起作用。要使推送、弹出指令可见,请尝试添加一个简单的函数并从 main 调用它。 一个什么都不做的程序不能指望有任何清楚地实现什么都不做的汇编指令。真的。查看编译器输出的想法还不错,但是您需要以更明智的方式进行处理。例如。从main
中的一个简单函数调用开始。请记住,编译器通常会添加大量样板代码,这对于最小的小程序来说没有意义。
我个人会使用编译C代码而不是C++的结果。
【参考方案1】:
正如@user2864740 所提到的,这不是学习汇编的最佳方式,但是您可以学习和了解不同的编译器如何将 C/C++ 转换为汇编以及它们如何优化您的代码。
在您的示例中,您不会看到任何有趣的学习内容,因为您的代码不包含任何功能,而且您还通过优化 (-O2) 对其进行编译。
-
如果您想查看以易于理解的方式反映您编写的内容的汇编代码,请不要使用优化 (-O0)。
请注意,您选择了“intel”汇编代码语法,还有 AT&T 代码语法 (check here)。如果您想查看 AT&T 风格,请从编译行中删除
-masm=intel
。
例如,尝试构建以下代码:
int main()
int x = 8;
int y = 18;
int z = x + y;
return z;
没有优化你会得到类似的东西:
35 main:
36 .LFB2:
37 .file 1 "assm.cpp"
1:assm.cpp **** int main()
38 .loc 1 1 0
39 0000 55 push %rbp #
40 .LCFI0:
41 0001 4889E5 mov %rbp, %rsp #,
42 .LCFI1:
43 .LBB2:
2:assm.cpp ****
3:assm.cpp **** int x = 8;
44 .loc 1 3 0
45 0004 C745F408 mov DWORD PTR [%rbp-12], 8 # x,
45 000000
4:assm.cpp **** int y = 18;
46 .loc 1 4 0
47 000b C745F812 mov DWORD PTR [%rbp-8], 18 # y,
47 000000
GAS LISTING /tmp/ccHUnQdo.s page 2
5:assm.cpp **** int z = x + y;
48 .loc 1 5 0
49 0012 8B45F8 mov %eax, DWORD PTR [%rbp-8] # y, y
50 0015 0345F4 add %eax, DWORD PTR [%rbp-12] # tmp60, x
51 0018 8945FC mov DWORD PTR [%rbp-4], %eax # z, tmp60
6:assm.cpp ****
7:assm.cpp **** return z;
52 .loc 1 7 0
53 001b 8B45FC mov %eax, DWORD PTR [%rbp-4] # D.2339, z
54 .LBE2:
8:assm.cpp ****
55 .loc 1 8 0
56 001e C9 leave
57 001f C3 ret
您可以看到局部变量在堆栈上(RBP 用于访问它们)并且编译器为它们分配了 12 个字节 (x,y,z) => 在我的机器上 sizeof(int)
是 4 个字节。 RBP 指向堆栈的末尾,x 首先在堆栈上分配,所以为了访问它,编译器使用“RBP-12”(y 是堆栈上的第二个,所以它由“RBP-8”访问,所以在)。方括号表示内存访问,括号中的值表示地址。“DWORD PTR”表示内存访问大小为“双字”,即4字节(字=16位=2字节)。
使用 -O2 优化,编译器优化所有代码并立即返回 26(如果 32 位足以保存该值而不会丢失数据,则返回值通常放在 EAX 中):
48 main:
49 .LFB2:
50 .file 1 "assm.cpp"
1:assm.cpp **** int main()
51 .loc 1 1 0
2:assm.cpp ****
GAS LISTING /tmp/ccZvidrN.s page 2
3:assm.cpp **** int x = 8;
4:assm.cpp **** int y = 18;
5:assm.cpp **** int z = x + y;
6:assm.cpp ****
7:assm.cpp **** return z;
8:assm.cpp ****
52 .loc 1 8 0
53 0000 B81A0000 mov %eax, 26 # <result>,
53 00
54 0005 C3 ret
【讨论】:
【参考方案2】:您的程序实际上并没有做任何事情;局部变量 x 未使用,已被编译器删除。大部分汇编代码是调试信息,其余的与调用主函数必须做什么有关。
不过你应该能认出这条线:
xor eax, eax
您的主函数隐式返回零。该值在 eax 寄存器中返回,因此该行将零放入 eax。
【讨论】:
以上是关于通过阅读 gcc 输出来学习汇编的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 gcc 生成可以用 nasm 编译的汇编代码 [重复]