프로그램을 단일 바이너리로 컴파일하려면 체크섬을 만든 다음 동일한 컴파일러 및 컴파일러 설정으로 동일한 컴퓨터에서 다시 컴파일하고 다시 컴파일 된 프로그램을 체크섬하면 체크섬이 실패합니까?
그렇다면 왜 그렇습니까? 그렇지 않은 경우 다른 CPU를 사용하면 동일하지 않은 이진이 생성됩니까?
프로그램을 단일 바이너리로 컴파일하려면 체크섬을 만든 다음 동일한 컴파일러 및 컴파일러 설정으로 동일한 컴퓨터에서 다시 컴파일하고 다시 컴파일 된 프로그램을 체크섬하면 체크섬이 실패합니까?
그렇다면 왜 그렇습니까? 그렇지 않은 경우 다른 CPU를 사용하면 동일하지 않은 이진이 생성됩니까?
답변:
동일한 머신에서 동일한 설정으로 동일한 프로그램을 컴파일하십시오.
결정적인 대답은 "의존적"이지만, 대부분의 컴파일러는 대부분 결정 론적이며 생성 된 바이너리는 동일해야한다고 기대하는 것이 합리적입니다. 실제로 일부 버전 제어 시스템은 이에 의존합니다. 여전히 예외는 있습니다. 어딘가에 어떤 컴파일러가 타임 스탬프를 삽입하기로 결정할 가능성이 있습니다 (iirc, 델파이는 예를 들어 델파이). 또는 빌드 프로세스 자체가 그렇게 할 수도 있습니다. 전 처리기 매크로를 현재 타임 스탬프로 설정하는 C 프로그램의 makefile을 보았습니다. (하지만 다른 컴파일러 설정으로 간주됩니다.)
또한 바이너리를 정적으로 연결하면 시스템에있는 모든 관련 라이브러리의 상태를 효과적으로 통합 할 수 있으며이 중 하나의 변경 사항도 바이너리에 영향을 미칩니다. 따라서 관련된 컴파일러 설정 만이 아닙니다.
CPU가 다른 다른 머신에서 동일한 프로그램을 컴파일하십시오.
여기, 모든 베팅이 종료되었습니다. 대부분의 최신 컴파일러는 대상별 최적화를 수행 할 수 있습니다. 이 옵션을 사용하면 CPU가 유사하지 않으면 바이너리가 다를 수 있습니다 (그렇더라도 가능할 수도 있음). 또한 정적 링크에 대한 위의 참고 사항을 참조하십시오. 구성 환경은 컴파일러 설정을 훨씬 뛰어 넘습니다. 매우 엄격한 구성 제어가 없으면 두 시스템간에 차이가있을 가능성이 큽니다.
gcc -c
동일 할 수 있지만 링크 된 버전은 다릅니다. 또한, 그것은 단지 아닙니다 -march
; 이 또한 -mtune/-mcpu
및 -mfpmatch
(및 기타). 이들 중 일부는 설치마다 기본값이 다를 수 있으므로 컴퓨터에 최악의 경우를 명시 적으로 지정해야합니다. 이렇게하면 특히 sse없이 i386으로 되돌릴 경우 성능이 크게 저하 될 수 있습니다. 그리고 물론, 만약 당신의 CPU 중 하나가 ARM이고 다른 하나는 i686
당신이 요구하는 것은 "출력 결정 론적 "입니다. 프로그램을 한 번 컴파일 한 경우 즉시 다시 컴파일하면 아마도 동일한 출력 파일로 끝날 것입니다. 그러나 컴파일 된 프로그램이 사용하는 구성 요소에서 변경 사항이 있더라도 (작은 변경이라도) 컴파일러의 출력도 변경 될 수 있습니다.
프로그램을 다시 컴파일하면 비트 단위의 동일한 바이너리가 생성됩니까?
모든 컴파일러에 대해? 아니요. 적어도 C # 컴파일러는 허용되지 않습니다.
Eric Lippert는 왜 컴파일러의 출력이 결정적이지 않은지에 대해 매우 철저한 분석을했습니다 .
C # 컴파일러는 의도적으로 같은 바이너리를 두 번 생성하지 않습니다. C # 컴파일러는 실행할 때마다 모든 어셈블리에 새로 생성 된 GUID를 포함하므로 두 어셈블리가 비트 단위로 동일하지 않습니다. CLI 사양에서 인용하려면 :
Mvid 열은 모듈의이 인스턴스를 식별하는 고유 한 GUID [...]를 색인화해야합니다. [...] 모든 모듈에 대해 Mvid를 새로 생성해야합니다. [...] [런타임] 자체는 Mvid를 사용하지 않지만 다른 도구 (예 : 디버거 [...])는 Mvid는 거의 항상 모듈마다 다릅니다.
C # 컴파일러 버전에 따라 다르지만 기사의 많은 부분을 모든 컴파일러에 .
우선, 매번 같은 순서로 항상 같은 파일 목록을 얻는다고 가정합니다. 그러나 일부 경우 운영 체제에 따라 다릅니다. "csc * .cs"라고 말하면 운영 체제가 일치하는 파일 목록을 생성하는 순서는 운영 체제의 구현 세부 사항입니다. 컴파일러는 해당 목록을 정식 순서로 정렬하지 않습니다.
-frandom-seed=123
일부 GCC 내부 무작위성을 제어합니다. man gcc
말한다 :
이 옵션은 컴파일 된 모든 파일에서 달라야하는 특정 심볼 이름을 생성 할 때 GCC가 난수 대신 사용하는 시드를 제공합니다. 또한 적용 범위 데이터 파일과이를 생성하는 오브젝트 파일에 고유 한 스탬프를 배치하는 데 사용됩니다. -frandom-seed 옵션을 사용하여 재현 가능한 동일한 오브젝트 파일을 생성 할 수 있습니다.
__FILE__
: 소스를 고정 폴더에 넣습니다 (예 : /tmp/build
)
__DATE__
, __TIME__
,__TIMESTAMP__
:
-D
-Wdate-time
나 -Werror=date-time
: 경고 또는 두 경우 실패 __TIME__
, __DATE__
또는 __TIMESTAMP__
사용된다. Linux 커널 4.4는 기본적으로이를 사용합니다.D
플래그를 ar
사용하거나 https://github.com/nh2/ar-timestamp-wiper/tree/master를 사용 하십시오. 우표를 닦아-fno-guess-branch-probability
: 오래된 매뉴얼 버전 은 그것이 비결정론의 근원이지만 더 이상 은 아니라고 말합니다 . 이것이 적용되는지 확실 -frandom-seed
하지 않습니다.데비안 Reproducible은 데비안 패키지를 바이트 단위로 표준화하려는 프로젝트 시도를 빌드 하고 최근에 Linux Foundation 보조금을 받았습니다. . 여기에는 컴파일 이상의 것이 포함되지만 관심이 있어야합니다.
Buildroot 에는 BR2_REPRODUCIBLE
패키지 수준에 대한 몇 가지 아이디어를 제공 할 수 있는 옵션이 있지만이 시점에서는 아직 완료되지 않았습니다.
관련 스레드 :
https://reproducible-builds.org/ 프로젝트 는 이것에 관한 것이며 가능한 한 많은 장소에서 귀하의 질문에 "아니오, 다르지 않을 것입니다"라는 답을 얻으려고 열심히 노력하고 있습니다. NixOS와 데비안은 현재 패키지의 재현성이 90 % 이상입니다.
바이너리를 컴파일하고 바이너리를 컴파일하고 비트가 동일하면 소스 코드와 도구가 출력을 결정하는 것으로 안심할 수 있습니다. 길을 따라 트로이 코드.
http://bootstrappable.org/ 에서 인간이 읽을 수있는 소스의 부트 스트랩 기능과 재현성을 결합하면 인간이 읽을 수있는 소스를 기반으로 시스템을 처음부터 결정할 수 있습니다. 우리는 시스템이 무엇을하고 있는지 알고 있다고 믿을 수 있습니다.
아니오라고 말하면 100 % 결정적이 아닙니다. 이전에는 Hitachi H8 프로세서의 대상 바이너리를 생성하는 GCC 버전으로 작업했습니다.
타임 스탬프에는 문제가 없습니다. 타임 스탬프 문제가 무시 되더라도 특정 프로세서 아키텍처에서는 동일한 비트가 1 또는 0 일 수있는 2 가지 방식으로 인코딩 될 수 있습니다. 그러나 때때로 gcc는 동일한 크기의 바이너리를 생성하지만 1 비트 만 다른 바이트의 일부는 0XE0이 0XE1이됩니다.
일반적으로 아닙니다. 가장 합리적으로 복잡한 컴파일러에는 컴파일 시간이 객체 모듈에 포함됩니다. 시계를 재설정하더라도 컴파일을 시작했을 때와 관련하여 매우 정확해야합니다 (그리고 디스크 액세스 등이 이전과 같은 속도가 되길 바랍니다).