¿Por qué dos binarios de programas con solo comentarios cambiados no coinciden exactamente en gcc?

110

Creé dos programas C

  1. Programa 1

    int main()
    {
    }
    
  2. Programa 2

    int main()
    {
    //Some Harmless comments
    }
    

AFAIK, al compilar, el compilador (gcc) debe ignorar los comentarios y los espacios en blanco redundantes y, por lo tanto, la salida debe ser similar.

Pero cuando verifiqué las sumas md5 de los binarios de salida, no coinciden. También intenté compilar con optimización -O3y -Ofasttodavía no coincidían.

¿Que está sucediendo aquí?

EDITAR: los comandos exactos y md5sums son (t1.c es el programa 1 y t2.c es el programa 2)

gcc ./t1.c -o aaa
gcc ./t2.c -o bbb
98c1a86e593fd0181383662e68bac22f  aaa
c10293cbe6031b13dc6244d01b4d2793  bbb

gcc ./t2.c -Ofast -o bbb
gcc ./t1.c -Ofast -o aaa
2f65a6d5bc9bf1351bdd6919a766fa10  aaa
c0bee139c47183ce62e10c3dbc13c614  bbb


gcc ./t1.c -O3 -o aaa
gcc ./t2.c -O3 -o bbb
564a39d982710b0070bb9349bfc0e2cd  aaa
ad89b15e73b26e32026fd0f1dc152cd2  bbb

Y sí, md5sums coincide en múltiples compilaciones con las mismas banderas.

Por cierto, mi sistema es gcc (GCC) 5.2.0yLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux

usuario registrado
fuente
17
Incluya sus banderas de línea de comando exactas. Por ejemplo, ¿se incluye información de depuración en los binarios? Si es así, el cambio de números de línea obviamente lo afectaría ...
Jon Skeet
4
¿Es la suma MD5 coherente en varias compilaciones del mismo código?
usuario poco entusiasta
3
No puedo reproducir esto. Habría adivinado que esto se debe al hecho de que GCC incrusta una gran cantidad de metadatos en binarios al compilarlos (incluidas las marcas de tiempo). Si pudiera agregar los indicadores de línea de comando precisos que utilizó, sería útil.
cyphar
2
En lugar de simplemente verificar MD5sums y quedarse atascado, hexdump y diff para ver exactamente qué bytes difieren
MM
12
Aunque la respuesta a la pregunta "¿qué es diferente entre las dos salidas del compilador?" Es interesante, observo que la pregunta tiene una suposición injustificada: que las dos salidas deben ser iguales y que necesitamos alguna explicación de por qué son diferentes. Todo lo que el compilador le promete es que cuando le da un programa C legal, la salida es un ejecutable legal que implementa ese programa. Que dos ejecuciones del compilador produzcan el mismo binario no es garantía del estándar C.
Eric Lippert

Respuestas:

159

Es porque los nombres de los archivos son diferentes (aunque la salida de las cadenas es la misma). Si intenta modificar el archivo en sí (en lugar de tener dos archivos), notará que los binarios de salida ya no son diferentes. Como tanto Jens como yo dijimos, es porque GCC descarga una gran cantidad de metadatos en los binarios que construye, incluido el nombre exacto del archivo fuente (y AFAICS también lo hace clang).

Prueba esto:

$ cp code.c code2.c subdir/code.c
$ gcc code.c -o a
$ gcc code2.c -o b
$ gcc subdir/code.c -o a2
$ diff a b
Binary files a and b differ
$ diff a2 b
Binary files a2 and b differ
$ diff -s a a2
Files a and a2 are identical

Esto explica por qué sus md5sums no cambian entre compilaciones, pero son diferentes entre diferentes archivos. Si lo desea, puede hacer lo que sugirió Jens y comparar la salida de stringscada binario; notará que los nombres de archivo están incrustados en el binario. Si desea "arreglar" esto, puede stripeliminar los binarios y los metadatos:

$ strip a a2 b
$ diff -s a b
Files a and b are identical
$ diff -s a2 b
Files a2 and b are identical
$ diff -s a a2
Files a and a2 are identical
cyphar
fuente
EDITAR: actualizado para decir que puede eliminar los binarios para "solucionar" el problema.
cyphar
30
Y es por eso que debe comparar la salida del ensamblado, no las sumas de verificación MD5.
Lightness Races in Orbit
1
He hecho una pregunta de seguimiento aquí .
Federico Poloni
4
Dependiendo del formato del archivo de objeto, el tiempo de compilación también se almacena en los archivos de objeto. Por lo tanto, el uso de archivos COFF, por ejemplo, los archivos ay a2 no sería idéntico.
Martin Rosenau
28

La razón más común son los nombres de archivo y las marcas de tiempo agregadas por el compilador (generalmente en la parte de información de depuración de las secciones ELF).

Intenta correr

 $ strings -a program > x
 ...recompile program...
 $ strings -a program > y
 $ diff x y

y es posible que veas la razón. Una vez usé esto para descubrir por qué la misma fuente causaría un código diferente cuando se compilaba en diferentes directorios. El hallazgo fue que la __FILE__macro se expandió a un nombre de archivo absoluto , diferente en ambos árboles.

Jens
fuente
1
De acuerdo con gcc.gnu.org/ml/gcc-help/2007-05/msg00138.html (desactualizado, lo sé), no guardan las marcas de tiempo y podría ser un problema del vinculador. Aunque, recuerdo haber leído recientemente una historia sobre cómo una empresa de seguridad perfilaba los hábitos de trabajo de un equipo de piratería utilizando la información de la marca de tiempo de GCC en sus archivos binarios.
cyphar
3
Y sin mencionar que OP afirma que "md5sums coincide en múltiples compilaciones con las mismas banderas", lo que indica que probablemente no sean las marcas de tiempo las que estén causando el problema. Probablemente se deba al hecho de que son nombres de archivo diferentes.
cyphar
1
@cyphar El enfoque strings / diff también debe capturar diferentes nombres de archivo.
Jens
15

Nota : recuerde que el nombre del archivo de origen va en el binario sin eliminar, por lo que dos programas que provienen de archivos de origen con nombres diferentes tendrán hash diferentes.

En situaciones similares, si lo anterior no se aplica , puede intentar:

  • corriendo stripcontra el binario para eliminar algo de grasa. Si los binarios eliminados son los mismos, entonces algunos metadatos no son esenciales para el funcionamiento del programa.
  • generar una salida intermedia de ensamblaje para verificar que la diferencia no está en las instrucciones reales de la CPU (o, sin embargo, para identificar mejor dónde está realmente la diferencia )
  • use strings, o voltee ambos programas en hexadecimal y ejecute un diff en los dos volcados hexadecimales. Una vez localizada la (s) diferencia (s), puede intentar ver si hay alguna rima o razón para ellas (PID, marcas de tiempo, marca de tiempo del archivo fuente ...). Por ejemplo, puede tener una rutina que almacene la marca de tiempo en el momento de la compilación para fines de diagnóstico.
LSerni
fuente
Mi sistema es gcc (GCC) 5.2.0yLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
Usuario registrado
2
Usted debe tratar realmente de hacer dos archivos separados. Tampoco pude reproducirlo modificando un solo archivo.
cyphar
Sí, los nombres de los archivos son los culpables. Puedo obtener los mismos md5sums si compilo los programas con el mismo nombre.
Usuario registrado