직관적으로 언어의 컴파일러 Foo
자체는 Foo로 작성할 수 없습니다. 보다 구체적으로, 언어 의 첫 번째 컴파일러 Foo
는 Foo로 작성할 수 없지만 이후의 컴파일러는에 대해 작성할 수 있습니다 Foo
.
그러나 이것이 사실입니까? 첫 번째 컴파일러가 “자체”로 작성된 언어에 대한 읽기에 대한 모호한 기억이 있습니다. 이것이 가능합니까? 그렇다면 어떻게됩니까?
답변
이것을 “부트 스트래핑”이라고합니다. 먼저 다른 언어 (보통 Java 또는 C)로 언어에 대한 컴파일러 (또는 인터프리터)를 빌드해야합니다. 완료되면 언어 Foo로 새 버전의 컴파일러를 작성할 수 있습니다. 첫 번째 부트 스트랩 컴파일러를 사용하여 컴파일러를 컴파일 한 다음이 컴파일 된 컴파일러를 사용하여 다른 모든 버전 (향후 버전 자체 포함)을 컴파일합니다.
대부분의 언어는 실제로 이런 방식으로 만들어집니다. 부분적으로 언어 디자이너는 자신이 만들고있는 언어를 사용하기를 원하며, 사소하지 않은 컴파일러는 종종 언어의 “완전한”정도에 대한 유용한 벤치 마크 역할을하기 때문입니다.
이에 대한 예는 스칼라입니다. 최초의 컴파일러는 Martin Odersky의 실험 언어 인 Pizza에서 만들어졌습니다. 버전 2.0부터 컴파일러는 Scala로 완전히 다시 작성되었습니다. 그 시점부터는 새로운 Scala 컴파일러를 사용하여 향후 반복을 위해 자체 컴파일 할 수 있기 때문에 기존 피자 컴파일러를 완전히 버릴 수 있습니다.
답변
나는 듣고 기억합니다 소프트웨어 공학 라디오 팟 캐스트 딕 가브리엘은 LISP의 베어 본 버전을 작성하여 원래의 LISP 인터프리터를 부트 스트랩에 대해 언급 항에있어서, 종이에 손이 기계 코드로 조합. 그때부터 나머지 LISP 기능은 LISP로 작성되고 해석되었습니다.
답변
이전 답변에 호기심 추가.
다음은 Linux From Scratch 매뉴얼 에서 인용 한 것입니다. 소스에서 GCC 컴파일러 빌드를 시작하는 단계입니다. (Linux From Scratch는 대상 시스템의 모든 단일 바이너리 를 컴파일해야한다는 점에서 배포판 설치와 근본적으로 다른 Linux를 설치하는 방법 입니다.)
make bootstrap
‘bootstrap’대상은 GCC를 컴파일 할뿐만 아니라 여러 번 컴파일합니다. 첫 번째 라운드에서 컴파일 된 프로그램을 사용하여 두 번째로 컴파일 한 다음 다시 세 번 컴파일합니다. 그런 다음이 두 번째와 세 번째 컴파일을 비교하여 완벽하게 재생산 할 수 있도록합니다. 이것은 또한 올바르게 컴파일되었음을 의미합니다.
‘bootstrap’타겟의 사용은 타겟 시스템의 툴체인을 빌드하기 위해 사용하는 컴파일러가 타겟 컴파일러의 버전과 동일하지 않을 수 있다는 사실에 의해 동기가 부여됩니다. 이런 식으로 진행하면 대상 시스템에서 자체 컴파일 할 수있는 컴파일러를 얻을 수 있습니다.
답변
C에 대한 첫 번째 컴파일러를 작성할 때 다른 언어로 작성합니다. 이제 C 어셈블러에 대한 컴파일러가 있습니다. 결국, 문자열을 구문 분석 해야하는 곳, 특히 이스케이프 시퀀스를 찾아야합니다. \n
10 진수 코드 10 (및 \r
13 등)으로 문자 로 변환 하는 코드를 작성합니다 .
해당 컴파일러가 준비되면 C로 다시 구현하기 시작합니다.이 프로세스를 ” 부트 스트래핑 “이라고합니다.
문자열 파싱 코드는 다음과 같습니다.
...
if (c == 92) { // backslash
c = getc();
if (c == 110) { // n
return 10;
} else if (c == 92) { // another backslash
return 92;
} else {
...
}
}
...
컴파일 할 때 ‘\ n’을 이해하는 바이너리가 있습니다. 즉, 소스 코드를 변경할 수 있습니다.
...
if (c == '\\') {
c = getc();
if (c == 'n') {
return '\n';
} else if (c == '\\') {
return '\\';
} else {
...
}
}
...
그렇다면 ‘\ n’이 13의 코드라는 정보는 어디에 있습니까? 바이너리에 있습니다! DNA와 같습니다.이 바이너리로 C 소스 코드를 컴파일하면이 정보가 상속됩니다. 컴파일러가 스스로 컴파일하면이 지식을 자손에게 전달합니다. 이 시점부터는 소스만으로 컴파일러가 수행 할 작업을 볼 수있는 방법이 없습니다.
일부 프로그램의 소스에서 바이러스를 숨기려면 다음과 같이 수행하십시오. 컴파일러의 소스를 가져 와서 함수를 컴파일하는 함수를 찾아서 다음과 같이 바꾸십시오.
void compileFunction(char * name, char * filename, char * code) {
if (strcmp("compileFunction", name) == 0 && strcmp("compile.c", filename) == 0) {
code = A;
} else if (strcmp("xxx", name) == 0 && strcmp("yyy.c", filename) == 0) {
code = B;
}
... code to compile the function body from the string in "code" ...
}
흥미로운 부분은 A와 B입니다. A는 compileFunction
바이러스 를 포함 하는 소스 코드 이며 어떤 방식으로 암호화되어 있으므로 결과 바이너리를 검색하는 것이 분명하지 않습니다. 따라서 컴파일러 자체를 컴파일하면 바이러스 주입 코드가 보존됩니다.
B는 바이러스로 대체하려는 기능과 동일합니다. 예를 들어, 소스 파일 “login.c”의 “login”함수는 Linux 커널에서 가져온 것일 수 있습니다. 일반 비밀번호 외에 루트 계정의 비밀번호 “joshua”를 허용하는 버전으로 바꿀 수 있습니다.
컴파일하고 바이너리로 확산 시키면 소스를보고 바이러스를 찾을 수있는 방법이 없습니다.
아이디어의 원천 : https://web.archive.org/web/20070714062657/http://www.acm.org/classics/sep95/
답변
시작 소스 코드를 컴파일 할 것이 없기 때문에 컴파일러 자체를 작성할 수 없습니다. 이 문제를 해결하는 데는 두 가지 방법이 있습니다.
가장 덜 선호되는 것은 다음과 같습니다. 최소한의 언어 집합을 위해 최소 컴파일러를 어셈블러 (yuck)로 작성한 다음 해당 컴파일러를 사용하여 언어의 추가 기능을 구현합니다. 모든 언어 기능을 갖춘 컴파일러가 생길 때까지 길을 닦으십시오. 고통스러운 과정은 일반적으로 다른 선택이 없을 때만 수행됩니다.
선호되는 방법은 크로스 컴파일러를 사용하는 것입니다. 다른 머신에서 기존 컴파일러의 백엔드를 변경하여 대상 머신에서 실행되는 출력을 작성합니다. 그런 다음 대상 컴퓨터에서 멋진 컴파일러를 만들고 작업합니다. 교체 가능한 플러그 백엔드가있는 기존 컴파일러가 많이 있으므로 C 언어가 가장 많이 사용됩니다.
약간의 사실은 GNU C ++ 컴파일러가 C 서브 세트 만 사용하는 구현을 가지고 있다는 것입니다. 그 이유는 일반적으로 새로운 대상 시스템의 C 컴파일러를 쉽게 찾을 수 있기 때문에 전체 GNU C ++ 컴파일러를 빌드 할 수 있습니다. 이제 대상 머신에서 C ++ 컴파일러를 사용하도록 부팅했습니다.
답변
일반적으로 컴파일러가 작동하는 (기본적 인 경우) 컷을 먼저 작동시켜야합니다. 그러면 자체 호스팅에 대해 생각할 수 있습니다. 이것은 실제로 일부 언어에서 중요한 이정표로 간주됩니다.
내가 “모노”에서 기억 한 바에 따르면, 그것들을 작동시키기 위해서는 몇 가지 사항을 반영해야 할 것입니다. 모노 팀은 어떤 것이 불가능하다고 지적합니다 Reflection.Emit
. 물론, MS 팀은 그들이 틀렸다는 것을 증명할 수 있습니다.
여기에는 몇 가지 실질적인 장점이 있습니다. 초보자에게는 상당히 좋은 단위 테스트입니다! 그리고 걱정해야 할 언어는 하나뿐입니다 (즉, C # 전문가가 C ++을 많이 알지 못할 수도 있지만 이제는 C # 컴파일러를 고칠 수 있습니다). 그러나 나는 직장에서 많은 전문적 자부심이 없는지 궁금합니다. 그들은 단순히 자체 호스팅이 되기를 원합니다 .
컴파일러는 아니지만 최근에 자체 호스팅 시스템에서 작업하고 있습니다. 코드 생성기는 코드 생성기를 생성하는 데 사용됩니다. 따라서 스키마가 변경되면 자체적으로 새 버전으로 실행합니다. 버그가 있으면 이전 버전으로 돌아가서 다시 시도하십시오. 매우 편리하고 유지 보수가 쉽습니다.
업데이트 1
방금 PDC에서 Anders의 비디오 를 보았 으며 (약 1 시간) 그는 컴파일러로서의 서비스에 관한 훨씬 더 유효한 이유를 제시합니다. 기록만을 위해서.
