¿Cómo se hace esto?
Si quiero analizar cómo se está compilando algo, ¿cómo obtendría el código de ensamblaje emitido?
¿Cómo se hace esto?
Si quiero analizar cómo se está compilando algo, ¿cómo obtendría el código de ensamblaje emitido?
Respuestas:
Use la -S
opción para gcc (o g ++).
gcc -S helloworld.c
Esto ejecutará el preprocesador (cpp) sobre helloworld.c, realizará la compilación inicial y luego se detendrá antes de ejecutar el ensamblador.
Por defecto, esto generará un archivo helloworld.s
. El archivo de salida todavía se puede configurar mediante la -o
opción.
gcc -S -o my_asm_output.s helloworld.c
Por supuesto, esto solo funciona si tiene la fuente original. Una alternativa si solo tiene el archivo de objeto resultante es usar objdump
, configurando la --disassemble
opción (o -d
para la forma abreviada).
objdump -S --disassemble helloworld > helloworld.dump
Esta opción funciona mejor si la opción de depuración está habilitada para el archivo objeto ( -g
en el momento de la compilación) y el archivo no se ha eliminado.
La ejecución file helloworld
le dará alguna indicación sobre el nivel de detalle que obtendrá al usar objdump.
.intel_syntax
es compatible con NASM . Es más como MASM (por ejemplo, mov eax, symbol
es una carga, a diferencia de NASM donde es una mov r32, imm32
de las direcciones), pero tampoco es totalmente compatible con MASM. Lo recomiendo encarecidamente como un buen formato para leer, especialmente si te gusta escribir en sintaxis NASM. objdump -drwC -Mintel | less
o gcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | less
son útiles (Consulte también ¿Cómo eliminar el "ruido" de la salida del conjunto GCC / clang? ). -masm=intel
también funciona con clang.
gcc -O -fverbose-asm -S
Esto generará código de ensamblaje con los números de línea del código C + entrelazados, para ver más fácilmente qué líneas generan qué código:
# create assembler code:
g++ -S -fverbose-asm -g -O2 test.cc -o test.s
# create asm interlaced with source lines:
as -alhnd test.s > test.lst
Se encuentra en Algoritmos para programadores , página 3 (que es la 15ª página general del PDF).
as
en OS X no conoce estas banderas. Sin embargo, si lo hiciera, probablemente podría usar esto en una línea -Wa
para pasar opciones as
.
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lst
sería la versión abreviada de esto.
gcc -c -g -Wa,-ahl=test.s test.c
ogcc -c -g -Wa,-a,-ad test.c > test.txt
-O0
? Está lleno de cargas / tiendas que dificultan el seguimiento de un valor, y no le dice nada acerca de cuán eficiente será el código optimizado.
La siguiente línea de comando es del blog de Christian Garbin.
g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt
Ejecuté G ++ desde una ventana de DOS en Win-XP, contra una rutina que contiene un reparto implícito
c:\gpp_code>g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt
horton_ex2_05.cpp: In function `int main()':
horton_ex2_05.cpp:92: warning: assignment to `int' from `double'
La salida se ensambla con el código generado que se mezcla con el código original de C ++ (el código de C ++ se muestra como comentarios en el flujo de asm generado)
16:horton_ex2_05.cpp **** using std::setw;
17:horton_ex2_05.cpp ****
18:horton_ex2_05.cpp **** void disp_Time_Line (void);
19:horton_ex2_05.cpp ****
20:horton_ex2_05.cpp **** int main(void)
21:horton_ex2_05.cpp **** {
164 %ebp
165 subl $128,%esp
?GAS LISTING C:\DOCUME~1\CRAIGM~1\LOCALS~1\Temp\ccx52rCc.s
166 0128 55 call ___main
167 0129 89E5 .stabn 68,0,21,LM2-_main
168 012b 81EC8000 LM2:
168 0000
169 0131 E8000000 LBB2:
169 00
170 .stabn 68,0,25,LM3-_main
171 LM3:
172 movl $0,-16(%ebp)
-O2
, o cualquier opción de optimización que realmente use al construir su proyecto, si desea ver cómo gcc optimiza su código. (O si usa LTO, como debería, entonces debe desarmar la salida del enlazador para ver lo que realmente obtiene.)
Si lo que quiere ver depende de la vinculación de la salida, entonces objdump en el archivo / ejecutable del objeto de salida también puede ser útil además del gcc -S mencionado anteriormente. Aquí hay un script muy útil de Loren Merritt que convierte la sintaxis objdump predeterminada en la sintaxis nasm más legible:
#!/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;
Sospecho que esto también se puede usar en la salida de gcc -S.
mov eax,ds:0x804b794
no es muy NASMish. Además, a veces simplemente elimina información útil: movzx eax,[edx+0x1]
deja al lector adivinar si el operando de la memoria fue byte
o no word
.
objconv
. Puede hacer que se desmonte en stdout con el archivo de salida = /dev/stdout
, para que pueda canalizarlo less
para verlo. También hay ndisasm
, pero solo desmonta los binarios planos, y no conoce los archivos de objetos (ELF / PE).
Como todos han señalado, use la -S
opción para GCC. También me gustaría agregar que los resultados pueden variar (¡salvajemente!) Dependiendo de si agrega o no opciones de optimización ( -O0
para ninguno, -O2
para una optimización agresiva).
En arquitecturas RISC en particular, el compilador a menudo transformará el código casi más allá del reconocimiento al hacer la optimización. ¡Es impresionante y fascinante ver los resultados!
Como se mencionó anteriormente, mira la bandera -S.
También vale la pena mirar la familia de banderas '-fdump-tree', en particular '-fdump-tree-all', que le permite ver algunas de las formas intermedias de gcc. Estos a menudo pueden ser más legibles que el ensamblador (al menos para mí) y le permiten ver cómo funcionan los pases de optimización.
No veo esta posibilidad entre las respuestas, probablemente porque la pregunta es de 2008, pero en 2018 puede usar el sitio web en línea de Matt Goldbolt https://godbolt.org
También puede git clone localmente y ejecutar su proyecto https://github.com/mattgodbolt/compiler-explorer
-save-temps
Esto se mencionó en https://stackoverflow.com/a/17083009/895245 pero permítanme ejemplificarlo más.
La gran ventaja de esta opción -S
es que es muy fácil agregarlo a cualquier script de compilación, sin interferir mucho en la compilación misma.
Cuando tu lo hagas:
gcc -save-temps -c -o main.o main.c
C Principal
#define INC 1
int myfunc(int i) {
return i + INC;
}
y ahora, además de la salida normal main.o
, el directorio de trabajo actual también contiene los siguientes archivos:
main.i
es una bonificación y contiene el archivo preprocesado:
# 1 "main.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "main.c"
int myfunc(int i) {
return i + 1;
}
main.s
contiene el ensamblaje generado deseado:
.file "main.c"
.text
.globl myfunc
.type myfunc, @function
myfunc:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
addl $1, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size myfunc, .-myfunc
.ident "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
.section .note.GNU-stack,"",@progbits
Si desea hacerlo para una gran cantidad de archivos, considere usar en su lugar:
-save-temps=obj
que guarda los archivos intermedios en el mismo directorio que la -o
salida del objeto en lugar del directorio de trabajo actual, evitando así posibles conflictos de nombres básicos.
Otra cosa interesante de esta opción es si agrega -v
:
gcc -save-temps -c -o main.o -v main.c
En realidad, muestra los archivos explícitos que se utilizan en lugar de los temporales feos debajo /tmp
, por lo que es fácil saber exactamente lo que está sucediendo, lo que incluye los pasos de preprocesamiento / compilación / ensamblaje:
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu main.c -mtune=generic -march=x86-64 -fpch-preprocess -fstack-protector-strong -Wformat -Wformat-security -o main.i
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -fpreprocessed main.i -quiet -dumpbase main.c -mtune=generic -march=x86-64 -auxbase-strip main.o -version -fstack-protector-strong -Wformat -Wformat-security -o main.s
as -v --64 -o main.o main.s
Probado en Ubuntu 19.04 amd64, GCC 8.3.0.
Use la opción -S:
gcc -S program.c
De: http://www.delorie.com/djgpp/v2faq/faq8_20.html
gcc -c -g -Wa, -a, -ad [otras opciones de CCG] foo.c> foo.lst
en alternativa a la respuesta de PhirePhly O simplemente use -S como todos decían.
Estos son los pasos para ver / imprimir el código de ensamblaje de cualquier programa C en su Windows
consola / terminal / símbolo del sistema:
Escriba un programa C en un editor de código C como bloques de código y guárdelo con una extensión .c
Compilar y ejecutarlo.
Una vez ejecutado con éxito, vaya a la carpeta donde ha instalado su compilador gcc y proporcione el
siguiente comando para obtener un archivo '.s' del archivo '.c'
C: \ gcc> gcc -S ruta completa del archivo C ENTER
Un comando de ejemplo (como en mi caso)
C: \ gcc> gcc -SD: \ Aa_C_Certified \ alternate_letters.c
Esto genera un archivo '.s' del archivo '.c' original
4) Después de esto, escriba el siguiente comando
C; \ gcc> cpp filename.s ENTER
Comando de ejemplo (como en mi caso)
C; \ gcc> cpp alternate_letters.s
Esto imprimirá / imprimirá todo el código del lenguaje ensamblador de su programa en C.
Use "-S" como una opción. Muestra la salida del ensamblaje en la terminal.
gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less
. -S
por sí mismo crea foo.s
.
Recientemente quería saber el ensamblaje de cada función en un programa,
así es como lo hice.
$ gcc main.c // main.c source file
$ gdb a.exe // gdb a.out in linux
(gdb) disass main // note here main is a function
// similary it can be done for other functions
Aquí hay una solución para C usando gcc:
gcc -S program.c && gcc program.c -o output
Aquí la primera parte almacena la salida del ensamblaje del programa en el mismo nombre de archivo que el Programa pero con una extensión .s modificada , puede abrirlo como cualquier archivo de texto normal.
La segunda parte aquí compila su programa para el uso real y genera un ejecutable para su programa con un nombre de archivo especificado.
El program.c utilizado anteriormente es el nombre de su programa y la salida es el nombre del ejecutable que desea generar.
Por cierto, es mi primera publicación en StackOverFlow: -}