Por que dois binários de programas com apenas comentários alterados não correspondem exatamente ao gcc?

110

Eu criei dois programas C

  1. Programa 1

    int main()
    {
    }
  2. Programa 2

    int main()
    {
    //Some Harmless comments
    }

AFAIK, ao compilar, o compilador (gcc) deve ignorar os comentários e espaços em branco redundantes e, portanto, a saída deve ser semelhante.

Mas quando verifiquei os md5sums dos binários de saída, eles não correspondem. Eu também tentei compilar com otimização -O3e -Ofastmas eles ainda não se encontraram.

O que esta acontecendo aqui?

EDITAR: os comandos exatos e md5sums são (t1.c é o programa 1 e t2.c é o 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

E sim, md5sums correspondem a várias compilações com os mesmos sinalizadores.

BTW, meu sistema é gcc (GCC) 5.2.0eLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux

Usuário Registrado
fonte
17
Inclua seus sinalizadores de linha de comando exatos. Por exemplo, as informações de depuração estão incluídas nos binários? Se sim, a mudança dos números das linhas obviamente afetaria ...
Jon Skeet
4
A soma MD5 é consistente em várias compilações do mesmo código?
unenthusiasticuser
3
Eu não consigo reproduzir isso. Eu teria imaginado que isso é causado pelo fato de o GCC incorporar um monte de metadados em binários ao compilá-los (incluindo carimbos de data / hora). Se você pudesse adicionar os sinalizadores de linha de comando precisos que você usou, isso será útil.
cyphar
2
Em vez de apenas verificar MD5sums e travar, hexdump e diff para ver exatamente quais bytes diferem
MM
12
Embora a resposta à pergunta "o que é diferente entre as duas saídas do compilador?" é interessante, observo que a questão tem uma suposição injustificada: que as duas saídas devem ser as mesmas e que precisamos de alguma explicação de por que são diferentes. Tudo o que o compilador promete a você é que, quando você dá a ele um programa C válido, a saída é um executável legal que implementa esse programa. Que quaisquer duas execuções do compilador produzam o mesmo binário não é uma garantia do padrão C.
Eric Lippert,

Respostas:

159

É porque os nomes dos arquivos são diferentes (embora a saída das strings seja a mesma). Se você tentar modificar o próprio arquivo (em vez de ter dois arquivos), notará que os binários de saída não são mais diferentes. Como Jens e eu dissemos, é porque o GCC despeja uma carga inteira de metadados nos binários que cria, incluindo o nome de arquivo de origem exato (e AFAICS também clang).

Experimente isto:

$ 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

Isso explica porque seus md5sums não mudam entre as compilações, mas são diferentes entre os diferentes arquivos. Se quiser, você pode fazer o que Jens sugeriu e comparar a saída de stringscada binário, você notará que os nomes dos arquivos estão embutidos no binário. Se você quiser "consertar" isso, você pode stripos binários e os metadados serão removidos:

$ 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
cifar
fonte
EDIT: Atualizado para dizer que você pode remover os binários para "consertar" o problema.
cyphar
30
E é por isso que você deve comparar a saída do assembly, não as somas de verificação MD5.
Lightness Races in Orbit
1
Eu fiz uma pergunta complementar aqui .
Federico Poloni,
4
Dependendo do formato do arquivo objeto, o tempo de compilação também é armazenado nos arquivos objeto. Portanto, usar arquivos COFF para os arquivos de exemplo ae a2 não seria idêntico.
Martin Rosenau
28

O motivo mais comum são nomes de arquivos e carimbos de data / hora adicionados pelo compilador (geralmente na parte de informações de depuração das seções ELF).

Tente correr

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

e você pode ver o motivo. Certa vez, usei isso para descobrir por que a mesma fonte causaria código diferente quando compilada em diretórios diferentes. A descoberta foi que o__FILE__ macro se expandiu para um nome de arquivo absoluto , diferente nas duas árvores.

Jens
fonte
1
De acordo com gcc.gnu.org/ml/gcc-help/2007-05/msg00138.html (desatualizado, eu sei), eles não salvam carimbos de data / hora e pode ser um problema de vinculador. Embora, eu me lembre de ter lido uma história recentemente sobre como uma empresa de segurança traçou o perfil dos hábitos de trabalho de uma equipe de hackers usando as informações de carimbo de data / hora do GCC em seus binários.
cyphar
3
E sem mencionar que o OP afirma que "md5sums correspondem a várias compilações com os mesmos sinalizadores", o que indica que provavelmente não são os carimbos de data / hora que estão causando o problema. Provavelmente é causado pelo fato de serem nomes de arquivo diferentes.
cyphar
1
@cyphar Nomes de arquivos diferentes também devem ser capturados pela abordagem strings / diff.
Jens,
15

Nota : lembre-se de que o nome do arquivo de origem vai para o binário não compactado, portanto, dois programas provenientes de arquivos de origem com nomes diferentes terão hashes diferentes.

Em situações semelhantes, caso o acima não se aplique , você pode tentar:

  • corrida strip contra o binário para remover um pouco de gordura. Se os binários removidos são os mesmos, então alguns metadados não são essenciais para a operação do programa.
  • gerar uma saída intermediária de montagem para verificar se a diferença não está nas instruções reais da CPU (ou, no entanto, para identificar melhor onde a diferença realmente está )
  • use stringsou despeje ambos os programas em hexadecimal e execute um diff nos dois despejos hexadecimais. Depois de localizar a (s) diferença (ões), você pode tentar ver se há alguma rima ou razão para elas (PID, timestamps, timestamp do arquivo de origem ...). Por exemplo, você pode ter uma rotina armazenando o carimbo de data / hora no momento da compilação para fins de diagnóstico.
LSerni
fonte
Meu sistema é gcc (GCC) 5.2.0eLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
Usuário registrado
2
Você deve tentar realmente fazer dois arquivos separados. Também não consegui reproduzi-lo modificando um único arquivo.
cyphar
Sim, os nomes dos arquivos são os culpados. Posso obter os mesmos md5sums se compilar os programas com o mesmo nome.
Usuário registrado em