¿Cómo se obtiene la salida del ensamblador desde la fuente C/C++ en GCC?
¿Cómo se hace esto?
Si quiero analizar cómo se compila algo, ¿cómo obtendría el código ensamblador emitido?
Utilice la opción -Sgcc
para (o g++
), opcionalmente con -fverbose-asm , que funciona bien con el valor predeterminado -O0 para adjuntar nombres C a operandos asm como comentarios. Funciona menos bien en cualquier nivel de optimización, que normalmente desea utilizar para que valga la pena mirarlo.
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. Para conocer opciones útiles del compilador para usar en ese caso, consulte ¿Cómo eliminar el "ruido" de la salida del ensamblado GCC/clang? (o simplemente mire su código en el Compiler Explorer en línea de Matt Godbolt , que filtra directivas y demás, y tiene resaltado para hacer coincidir las líneas fuente con asm usando información de depuración).
De forma predeterminada, esto generará el archivo helloworld.s
. El archivo de salida aún se puede configurar usando la opción -o , incluso -o -
para escribir en la salida estándar para canalizar a less .
gcc -S -o my_asm_output.s helloworld.c
Por supuesto, esto sólo funciona si tienes la fuente original. Una alternativa si solo tiene el archivo objeto resultante es usar objdump , configurando la --disassemble
opción (o -d
para la forma abreviada).
objdump -S --disassemble helloworld > helloworld.dump
-S
entrelaza líneas fuente con la salida normal del desensamblado, por lo que 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 ha sido eliminado.
La ejecución file helloworld
le dará alguna indicación sobre el nivel de detalle que obtendrá al usar objdump .
Otras objdump
opciones útiles incluyen -rwC
(para mostrar reubicaciones de símbolos, deshabilitar el ajuste de líneas de código de máquina largo y exigir nombres de C++). Y si no le gusta la sintaxis de AT&T para x86, -Mintel
. Consulte la página de manual .
Así por ejemplo, objdump -drwC -Mintel -S foo.o | less
.
-r
es muy importante con un .o
que solo tiene 00 00 00 00
marcadores de posición para referencias de símbolos, a diferencia de un ejecutable vinculado.
Esto generará código ensamblador con el código C + números de línea entrelazados, para ver más fácilmente qué líneas generan qué código ( -S -fverbose-asm -g -O2 ):
# 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 encontró en Algoritmos para programadores , página 3 (que es la página 15 del PDF).
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 Windows XP, contra una rutina que contiene una conversión implícita
cd C:\gpp_code
g++ -g -O -Wa,-aslh horton_ex2_05.cpp > list.txt
Producción:
horton_ex2_05.cpp: In function `int main()':
horton_ex2_05.cpp:92: warning: assignment to `int' from `double'
La salida es código generado ensamblado, intercalado con el código C++ original (el código C++ se muestra como comentarios en el flujo de lenguaje ensamblador 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)
Utilice el modificador -S :
g++ -S main.cpp
O también con gcc
:
gcc -S main.c
Vea también esto .
-save-temps
Esto se mencionó en la respuesta de METADATA , pero permítanme ejemplificarlo con más detalle.
La gran ventaja de esta opción sobre -S es que es muy fácil agregarla a cualquier script de compilación, sin interferir mucho en la compilación misma:
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 ventaja 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
Documentos: https://gcc.gnu.org/onlinedocs/gcc/Developer-Options.html#index-save-temps
-save-temps=obj
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 base.
Por ejemplo:
gcc -save-temps -c -o out/subdir/main.o subdir/main.c
conduce a la creación de archivos:
out/subdir/main.i
out/subdir/main.o
out/subdir/main.s
Claramente un complot de Apple para apoderarse del mundo.
-save-temps -v
Otra cosa interesante de esta opción es si agregas -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 feos archivos temporales debajo /tmp
, por lo que es fácil saber exactamente qué 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
Fue probado en Ubuntu 19.04 (Disco Dingo) amd64, GCC 8.3.0.
CMake objetivos predefinidos
CMake proporciona automáticamente objetivos para el archivo preprocesado:
make help
nos muestra que podemos hacer:
make main.s
y ese objetivo se ejecuta:
Compiling C source to assembly CMakeFiles/main.dir/main.c.s
/usr/bin/cc -S /home/ciro/hello/main.c -o CMakeFiles/main.dir/main.c.s
para que el archivo se pueda ver en CMakeFiles/main.dir/main.c.s
.
Fue probado en CMake 3.16.1.