[整理]How do you get assembler output from C/C++ source in gcc?
http://stackoverflow.com/questions/137038/how-do-you-get-assembler-output-from-c-c-source-in-gcc
http://www.delorie.com/djgpp/v2faq/faq8_20.html
http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#s3
gcc -S -o my_asm_output.s helloworld.c
gcc -c -g -Wa,-a,-ad [other GCC options] foo.c > foo.lst
file helloworld
objdump -s --disassemble helloworld > helloworld.dump
-masm=dialect
Output asm instructions using selected dialect. Supported choices
are intel or att (the default one).
Use the -S option to gcc (or g++).
eg. gcc -S helloworld.c
This will run the preprocessor (cpp) over helloworld.c, perform the initial compilation and then stop before the assembler is run.
By default this will output a file helloworld.s. The output file can be still be set by using the -o option.
eg. gcc -S -o my_asm_output.s helloworld.c
Of course this only works if you have the original source. An alternative if you only have the resultant object file is to use objdump, by setting the –disassemble option (or -d for the abbreviated form).
eg. objdump -s –disassemble helloworld > helloworld.dump
This option works best if debugging option is enabled for the object file (-g at compilation time) and the file hasn’t been stripped.
Running file helloworld will give you some indication as to the level of detail that you will get by using objdump.
As mentioned before, look at the -S flag.
It’s also worth looking at the ‘-fdump-tree’ family of flags, in particular ‘-fdump-tree-all‘, which lets you see some of gcc’s intermediate forms. These can often be more readable than assembler (at least to me), and let you see how optimisation passes perform.
This will generate the asm with the C code + line numbers interweaved to more easily see what lines generate what code.
# create assembler code:
c++ -S -fverbose-asm -g -O2 test.cc -o test.s
# create asm interlaced with source lines:
as -alhnd test.s > test.lst
Found in Algorithms for programmers, page 4.
As everyone has pointed out, use the -S option to GCC. I would also like to add that the results may vary (wildly!) depending on whether or not you add optimization options (-O0 for none, -O2 for agressive optimization).
On RISC architectures in particular, the compiler will often transform the code almost beyond recognition in doing optimization. It’s impressive and fascinating to look at the results!
If what you want to see depends on the linking of the output, then objdump on the output object file/executable may also be useful in addition to the aforementioned gcc -S. Here’s a very useful script by Loren Merritt that converts the default objdump syntax into the more readable nasm syntax:
#!/usr/bin/perl -w $ptr='(BYTE|WORD|DWORD|QWORD|XMMWORD) PTR '; $reg='(?:[er]?(?:[abcd]x|[sd]i|[sb]p)|[abcd][hl]|r1?[0-589][dwb]?|mm[0-7]|xmm1?[0-9])'; open FH, '-|', '/usr/bin/objdump', '-w', '-M', 'intel', @ARGV or die; $prev = ""; while(<FH>){ if(/$ptr/o) { s/$ptr(\[[^\[\]]+\],$reg)/$2/o or s/($reg,)$ptr(\[[^\[\]]+\])/$1$3/o or s/$ptr/lc $1/oe; } if($prev =~ /\t(repz )?ret / and $_ =~ /\tnop |\txchg *ax,ax$/) { # drop this line } else { print $prev; $prev = $_; } } print $prev; close FH;
I suspect this can also be used on the output of gcc -S.
How to get GCC to generate assembly code
Q: How can I peek at the assembly code generated by GCC?
Q: How can I create a file where I can see the C code and its assembly translation together?
A: Use the -S (note: capital S) switch to GCC, and it will emit the assembly code to a file with a .s extension. For example, the following command:
gcc -O2 -S -c foo.c
will leave the generated assembly code on the file foo.s.
If you want to see the C code together with the assembly it was converted to, use a command line like this:
gcc -c -g -Wa,-a,-ad [other GCC options] foo.c > foo.lst
which will output the combined C/assembly listing to the file foo.lst.
If you need to both get the assembly code and to compile/link the program, you can either give the -save-temps option to GCC (which will leave all the temporary files including the .s file in the current directory), or use the -Wa,aln=foo.s option which instructs the assembler to output the assembly translation of the C code (together with the hex machine code and some additional info) to the file named after the =.
GCC, the GNU C Compiler for Linux, uses AT&T/UNIX assembly syntax. Here we’ll be using AT&T syntax for assembly coding. Don’t worry if you are not familiar with AT&T syntax, I will teach you. This is quite different from Intel syntax. I shall give the major differences.
Source-Destination Ordering.
The direction of the operands in AT&T syntax is opposite to that of Intel. In Intel syntax the first operand is the destination, and the second operand is the source whereas in AT&T syntax the first operand is the source and the second operand is the destination. ie,
“Op-code dst src” in Intel syntax changes to
“Op-code src dst” in AT&T syntax.
Register Naming.
Register names are prefixed by % ie, if eax is to be used, write %eax.
Immediate Operand.
AT&T immediate operands are preceded by ’$’. For static “C” variables also prefix a ’$’. In Intel syntax, for hexadecimal constants an ’h’ is suffixed, instead of that, here we prefix ’0x’ to the constant. So, for hexadecimals, we first see a ’$’, then ’0x’ and finally the constants.
Operand Size.
In AT&T syntax the size of memory operands is determined from the last character of the op-code name. Op-code suffixes of ’b’, ’w’, and ’l’ specify byte(8-bit), word(16-bit), and long(32-bit) memory references. Intel syntax accomplishes this by prefixing memory operands (not the op-codes) with ’byte ptr’, ’word ptr’, and ’dword ptr’.
Thus, Intel “mov al, byte ptr foo” is “movb foo, %al” in AT&T syntax.
Memory Operands.
In Intel syntax the base register is enclosed in ’[’ and ’]’ where as in AT&T they change to ’(’ and ’)’. Additionally, in Intel syntax an indirect memory reference is like
section:[base + index*scale + disp], which changes to
section:disp(base, index, scale) in AT&T.
One point to bear in mind is that, when a constant is used for disp/scale, ’$’ shouldn’t be prefixed.
Now we saw some of the major differences between Intel syntax and AT&T syntax. I’ve wrote only a few of them. For a complete information, refer to GNU Assembler documentations. Now we’ll look at some examples for better understanding.
+------------------------------+------------------------------------+ | Intel Code | AT&T Code | +------------------------------+------------------------------------+ | mov eax,1 | movl $1,%eax | | mov ebx,0ffh | movl $0xff,%ebx | | int 80h | int $0x80 | | mov ebx, eax | movl %eax, %ebx | | mov eax,[ecx] | movl (%ecx),%eax | | mov eax,[ebx+3] | movl 3(%ebx),%eax | | mov eax,[ebx+20h] | movl 0x20(%ebx),%eax | | add eax,[ebx+ecx*2h] | addl (%ebx,%ecx,0x2),%eax | | lea eax,[ebx+ecx] | leal (%ebx,%ecx),%eax | | sub eax,[ebx+ecx*4h-20h] | subl -0x20(%ebx,%ecx,0x4),%eax | +------------------------------+------------------------------------+
http://blog.chinaunix.net/u2/72255/showart_1286995.html
AT&T汇编和intel汇编的区别
一、AT&T 格式Linux 汇编语法格式
- 在 AT&T 汇编格式中,寄存器名要加上 ‘%’ 作为前缀;而在 Intel 汇编格式中,寄存器名不需要加前缀。例如:
|
AT&T 格式 |
Intel 格式 |
|
pushl %eax |
push eax |
- 在 AT&T 汇编格式中,用 ‘$’ 前缀表示一个立即操作数;而在 Intel 汇编格式中,立即数的表示不用带任何前缀。例如:
|
AT&T 格式 |
Intel 格式 |
|
pushl $1 |
push 1 |
- AT&T 和 Intel 格式中的源操作数和目标操作数的位置正好相反。在 Intel 汇编格式中,目标操作数在源操作数的左边;而在 AT&T 汇编格式中,目标操作数在源操作数的右边。例如:
|
AT&T 格式 |
Intel 格式 |
|
addl $1, %eax |
add eax, 1 |
- 在 AT&T 汇编格式中,操作数的字长由操作符的最后一个字母决定,后缀’b'、’w'、’l'分别表示操作数为字节(byte,8 比特)、字(word,16 比特)和长字(long,32比特);而在 Intel 汇编格式中,操作数的字长是用 “byte ptr” 和 “word ptr” 等前缀来表示的。例如:
|
AT&T 格式 |
Intel 格式 |
|
movb val, %al |
mov al, byte ptr val |
- 在 AT&T 汇编格式中,绝对转移和调用指令(jump/call)的操作数前要加上’*'作为前缀,而在 Intel 格式中则不需要。
- 远程转移指令和远程子调用指令的操作码,在 AT&T 汇编格式中为 “ljump” 和 “lcall”,而在 Intel 汇编格式中则为 “jmp far” 和 “call far”,即:
|
AT&T 格式 |
Intel 格式 |
|
ljump $section, $offset |
jmp far section:offset |
|
lcall $section, $offset |
call far section:offset |
- 与之相应的远程返回指令则为:
|
AT&T 格式 |
Intel 格式 |
|
lret $stack_adjust |
ret far stack_adjust |
- 在 AT&T 汇编格式中,内存操作数的寻址方式是
|
section:disp(base, index, scale) |
- 而在 Intel 汇编格式中,内存操作数的寻址方式为:
|
section:[base + index*scale + disp] |
- 由于 Linux 工作在保护模式下,用的是 32 位线性地址,所以在计算地址时不用考虑段基址和偏移量,而是采用如下的地址计算方法:
|
disp + base + index * scale |
- 下面是一些内存操作数的例子:
|
AT&T 格式 |
Intel 格式 |
|
movl -4(%ebp), %eax |
mov eax, [ebp - 4] |
|
movl array(, %eax, 4), %eax |
mov eax, [eax*4 + array] |
|
movw array(%ebx, %eax, 4), %cx |
mov cx, [ebx + 4*eax + array] |
|
movb $4, %fs:(%eax) |
mov fs:eax, 4 |

MoeFan (萌番)
最近评论