"C 자체는 어떤 언어로 쓰여 있는가?"
다른 관점에서 보면, C 언어가 실행되기 전에 반드시 컴파일되어야 하는데, 그렇다면 C 언어 컴파일러는 어디서 오는 걸까요? 어떤 언어로 쓰여 있나요? 만약 C로 작성된 것이라면, 먼저 알이 있나요, 아니면 닭이 먼저 있나요?
1
세상에 컴파일러가 없다고 가정해 봅시다. 기계어부터 시작해 보겠습니다.
기계어는 컴파일러 없이 CPU가 직접 실행할 수 있습니다.
그리고 어셈블리 언어가 있는데, 어셈블리 언어는 기계어의 기억법일 뿐이고, 실행하려면 기계어로 컴파일되어야 하므로 이 첫 번째 컴파일러를 작성할 때 기계어를 사용할 수밖에 없습니다(앞으로는 사용되지 않습니다).
어셈블리 언어 문제는 해결되었고, 이는 큰 진전입니다. 현재는 어셈블리 언어를 사용해 C 언어 컴파일러를 작성할 수 있게 되었는데, 이는 C 컴파일러의 조상이라고 할 수 있습니다.
이 조상과 함께라면 어떤 C 언어 프로그램도 컴파일할 수 있으니, C 언어 자체로 컴파일러를 작성할 수 있나요? 조상들과 함께 모으면 됩니다.
좋아요, 그런 층을 거쳐서 드디어 C로 작성된 컴파일러를 만들었는데, 정말 골치 아픈 일입니다.
이 시점에서 이전 패키지에서 작성된 C 컴파일러는 포기할 수 있습니다.
물론, 만약 파스칼과 같은 C 이전에 다른 고급 언어들이 있었다면, 파스칼을 사용해 C 컴파일러를 작성할 수 있었을 것입니다.
첫 번째 파스칼의 편자는 포트란(Fortran)으로 작성되었다고 전해집니다. 최초의 고수준 언어로서 Fortran의 컴파일러는 어셈블리어로 작성되어야 합니다.
2
컴파일러에 관한 흥미로운 전설이 있습니다:
전설에 따르면 유닉스 발명가 중 한 명인 켄 톰슨은 벨 연구소의 어떤 유닉스 기기든 당당하게 다가가 자신의 사용자 이름과 비밀번호를 입력했고, 루트 방식으로도 로그인할 수 있었다고 합니다!
벨 연구소는 재능이 넘치고, 다른 대형 기업들이 이 취약점을 찾겠다고 다짐했으며, 유닉스 C 소스 코드를 읽어 로그인 백도어를 찾아냈고, 백도어를 정리한 후 유닉스를 컴파일해 실행했지만 톰슨은 여전히 로그인할 수 있었습니다.
일부 사람들은 컴파일러에 문제가 있을 수 있고, Unix 컴파일 중에 백도어가 심어졌다고 생각해, 컴파일러를 C로 다시 작성하고 새 컴파일러로 Unix를 다시 컴파일했습니다.
하지만 여전히 작동하지 않고, 톰슨은 루트로 로그인할 수 있어서 정말 치명적입니다!
나중에 톰슨 자신이 비밀을 밝혀냈는데, 이 컴파일러는 처음으로 문제가 생긴 C 컴파일러였고, 이 컴파일러는 Unix 소스 코드를 컴파일할 때 백도어에 심어질 것입니다. 이것만으로는 충분하지 않습니다. 더 좋은 점은, C 언어로 새 컴파일러를 작성한다면, 반드시 그것을 바이너리 코드로 컴파일해야 한다는 것입니다. 무엇을 컴파일할지 톰슨이 처음 작성한 컴파일러만 사용해 컴파일할 것입니다. 알겠습니다. 당신이 작성한 컴파일러는 오염될 것이고, 컴파일러가 다시 Unix를 컴파일할 것입니다. 백도어도 설치할 예정입니다 :-)
말이 나온 김에, 몇 년 전 XcodeGhost 사건이 떠오릅니다. 이는 단순히 비공식 채널에서 다운로드한 트로이 목마가 Xcode에 심어져 XCode가 컴파일한 iOS 앱들이 오염되었고, 해커들이 불법적인 일을 할 수 있도록 했습니다.
이 XCodeGhost는 Thompson의 것과는 거리가 멀지만, 소프트웨어를 다운로드할 때는 공식 절차를 거쳐 공식 웹사이트에서 다운로드하고, 웹사이트의 HTTPS 표준을 확인하고, 체크섬을 검증해야 한다는 점을 상기시켜 줍니다.
3
어떤 사람들은 이렇게 물을 수도 있습니다: 저는 Hello World 단락을 작성할 때 Hui를 사용하는데, 누군가는 복잡한 컴파일러를 작성할 수 있다고요? 이게 가능할까요?
물론 1세대 유닉스가 개발되었을 때는 C 언어가 없었고, 켄 톰슨과 데니스 리치가 조립 라인을 이용해 유닉스를 타이핑했습니다. WPS의 첫 버전은 후이의 추보준이 작성했고, 터보 파스칼의 컴파일러도 후이의 안데르스가 작성했으며, 신들의 능력은 일반인에게는 상상할 수 없는 것입니다.
컴파일러의 경우, '눈덩이(snowball)' 방식으로 개발할 수도 있습니다:
여전히 C 언어를 예로 들면, 첫 번째 버전은 기본 데이터 타입, 프로세스 제어 문, 함수 호출만 지원하는 C 언어의 하위 집합을 선택할 수 있습니다...... 우리는 이 부분집합을 C0라고 부른다.
그 다음 어셈블리어로 컴파일러를 작성하고, 이 언어의 일부만 C0만 사용해 작성이 훨씬 쉬워집니다.
C0 언어는 작동하며, 구조체, 포인터, ......를 추가하고 새 언어 C1을 호출하여 이 하위 집합을 확장합니다.
C1 언어의 컴파일러는 누가 작성하나요? 당연히 C0입니다.
C1이 작동하면 언어 기능을 다시 확장하고, 컴파일러를 C1로 작성한 뒤 C2를 얻으세요.
그리고 C3, C4가 있습니다...... 마지막으로 완전한 C 언어를 얻을 수 있습니다.
이 과정을 부트스트래핑이라고 하며, 중국어로는 부트스트래핑이라고 합니다.
|