[c] 주석 만 변경된 두 개의 프로그램 바이너리가 gcc에서 정확히 일치하지 않는 이유는 무엇입니까?

두 개의 C 프로그램을 만들었습니다.

  1. 프로그램 1

    int main()
    {
    }
    
  2. 프로그램 2

    int main()
    {
    //Some Harmless comments
    }
    

AFAIK, 컴파일 할 때 컴파일러 (gcc)는 주석과 중복 된 공백을 무시해야하므로 출력이 유사해야합니다.

그러나 출력 바이너리의 md5sum을 확인했을 때 일치하지 않습니다. 또한 최적화 컴파일 시도 -O3하고 -Ofast있지만 아직 일치하지 않습니다.

여기서 무슨 일이 일어나고 있습니까?

편집 : 정확한 명령과 md5sum이 있습니다 (t1.c는 프로그램 1이고 t2.c는 프로그램 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

그리고 예, md5sums는 동일한 플래그를 사용하는 여러 컴파일에서 일치합니다.

BTW 내 시스템이 gcc (GCC) 5.2.0Linux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux



답변

파일 이름이 다르기 때문입니다 (문자열 출력은 동일하지만). 두 개의 파일이 아닌 파일 자체를 수정하려고하면 출력 바이너리가 더 이상 다르지 않음을 알 수 있습니다. Jens와 내가 말했듯이 GCC 는 정확한 소스 파일 이름을 포함하여 빌드하는 바이너리에 전체 메타 데이터로드를 덤프하기 때문입니다 (AFAICS도 clang도 마찬가지 임).

이 시도:

$ 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

이것은 md5sum이 빌드간에 변경되지 않는 이유를 설명하지만 다른 파일 간에는 다릅니다. 원하는 경우 Jens가 제안한 것을 수행하고 strings각 바이너리 의 출력을 비교할 수 있습니다 . 파일 이름이 바이너리에 포함되어 있음을 알 수 있습니다. 이 문제를 “수정” strip하려면 바이너리와 메타 데이터를 제거 할 수 있습니다.

$ 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


답변

가장 일반적인 이유는 컴파일러에서 추가 한 파일 이름과 타임 스탬프입니다 (일반적으로 ELF 섹션의 디버그 정보 부분에 있음).

실행 해보세요

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

그 이유를 알 수 있습니다. 나는 한 번 이것을 사용하여 다른 디렉토리에서 컴파일 할 때 동일한 소스가 다른 코드를 일으키는 이유를 찾았습니다. 그 결과 __FILE__매크로 는 두 트리에서 다른 절대 파일 이름 으로 확장되었습니다 .


답변

참고 : 소스 파일 이름 은 스트리핑되지 않은 바이너리로 이동하므로 이름이 다른 소스 파일에서 오는 두 프로그램은 다른 해시를 갖게됩니다.

유사한 상황에서 위의 내용이 적용되지 않는 경우 다음을 시도 할 수 있습니다.

  • strip일부 지방을 제거하기 위해 바이너리에 대해 실행 합니다. 제거 된 바이너리가 같으면 프로그램 작업에 필수적이지 않은 일부 메타 데이터입니다.
  • (차이가 실제로 어디에서보다 정확히 파악할 그러나, 또는 어셈블리 중간 출력을 생성하는 차분 실제 CPU의 지시에 없는지를 확인하는 것이다 )
  • 을 사용 strings하거나 두 프로그램을 모두 16 진으로 덤프하고 두 개의 16 진 덤프에서 diff를 실행하십시오. 차이점을 찾으면 운율이나 이유 (PID, 타임 스탬프, 소스 파일 타임 스탬프 …)가 있는지 확인할 수 있습니다. 예를 들어 , 진단 목적으로 컴파일 타임에 타임 스탬프를 저장 하는 루틴이있을 수 있습니다 .

답변