336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

0123


뭐가 어찌되었던 처음으로 실려본 내 사진과 글들

그리고 자세잡는것부터 표정까지 모든게 어려웠던 사진 모델되기...

12월호에 실린다는데 무척 궁금하기만 할뿐

ㅋㅋㅋ









'끄적임' 카테고리의 다른 글

영광의 11월  (0) 2008.12.12
2008 공개 SW 공모대전 대상받던날  (0) 2008.12.11
Going to San Francisco  (1) 2008.11.09
100달러 지폐의 가치 -좋은생각 中...  (0) 2008.05.26
너에게 묻는다 - 안도현  (0) 2008.05.20
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

미국 도착후 첫 수업은 테드와 시작 되었다.

테드는 File system 분야의 Maintainer이고 현제 IBM을 다니고 있으며 Linux FoundationCTO(Chief Technology Officer)로 취임이 예정되어 있을정도로 실력이 뛰어난 programmer 였고 그의 세미나를 듣는다는 사실에 몹시 흥분이 되었다.

 

처음으로 그가 말한 내용은 kernel 영역에서의 효율적인 interfaceuser 영역에서의 효율적인 interface를 제작하는 방법이었다.

그중에서도 리눅스 기본 규격(LSB, linux standard base)라는것이 있는데 이는 OS의 핵심부분인 커널에 여러 툴이나 기능이 부가된 배포 패키지의 형태로 유통이 되는데 이는 누구나 자유로이 작성이 가능하기 때문에, 개인이나 그룹, 기업 등이 작성한 패키지들이 전세계에 무수히 많이 존재하고 있다. LSB는 이들의 호환성을 확보하여 이들 팩키지용으로 작성된 어플리케이션 소프트가 다른 환경에서도 작동할 수 있도록 환경을 정비하기 때문에, Linux관련 개발자나 기업 등이 모여서 API나 라이브러리의 기본셋, 상호운용을 위한 지침을 정하는 것을 말한다.

 

다음으로 설명한 내용은 APIABI에 대한 내용이었다.

API를 이용하면 시스템콜을 직접 호출하지 않기 때문에 하드웨어에 의존적인 코드가 하나도 없기때문에 결국 API는 이식성을 위한 운영체제의 표준을 제공하는 수단이라고 정의할 수 있다. , API가 소스코드 수준의 호환성을 제공한다면 ABI는 바이너리 수준에서 호환성을 제공하고 ABI는 바이트 순서, 레지스터 사용, 시스템 콜 호출 방식, 링킹, 오브젝트 코드 포맷 등에 대한 규격을 제공한다. 이를테면, 시스템콜을 호출할 때 어떤 레지스터를 사용할 것인지, 오브젝트 코드 포맷을 ELF로 할 것인지를 결정하는 것이었고 이러한 규약이 정해져야 바이너리 호환을 제공할 수 있다는 내용이었다. , 앞으로는 windows에서처럼 이런 ABI를 많이 신경써야할 것이라는 추측도 덧붙여 설명하였다.


그리고 세미나 내용중 가장 중요한 내용중의 하나인 
Symbolic versioning
을 설명하였다.
과거의 단순한 공유 라이브러리 구현 때문에 겪었던 호환성 문제를 해결하는 방법이었다.
개념은 이해하기 쉬운데 하나의 라이브러리를 갖고 있고 그 라이브러리를 공유한다고 가정하자 하지만 프로그램이 printf()를 호출하려 할 때 작동 방식은 약간 복잡하다.
동적 링크 시스템 보다 정적 링크 시스템에서 프로세스가 더 간단하다. 정적 링크 시스템에서, 생성된 코드는 함수에 대한 레퍼런스를 처리한다. 링커는 이 레퍼런스를 함수를 로딩했던 실제 주소로 교체한다. 따라서 결과 바이너리 코드는 올바른 주소를 적재적소에 갖게 되는 것이다. 그런 다음, 코드가 실행되면 관련 주소로 점프(jump)한다. 프로그램의 어떤 지점에서 실제로 레퍼런스 되는 객체들에서만 링크할 수 있기 때문에 관리가 간단하다.
하지만 대부분의 공유 라이브러리들은 동적으로 링크 된다. 이것이 의미하는 바는 크다. 함수가 호출될 때 실제로 어떤 주소에 그 함수가 있게 될지 미리 예견할 수 없다.
동적 링커(dynamic linker)는 링크 된 각 함수에 대해 상당량의 작업을 수행할 수 있기 때문에 대부분의 링커들은 게으르다. 이들은 함수가 호출될 때 그 작업을 실제로 끝낸다. C 라이브러리에는 천 개 이상의 외부에 보여지는 심볼들과, 거의 삼천 개 이상의 로컬 심볼 들이 있다. 많은 시간을 절약할 수 있다.
작동 방법은 Procedure Linkage Table (PLT)이다. 이것은 프로그램이 호출하는 모든 함수를 나열하고 있는 프로그램의 테이블이다. 프로그램이 시작되면 PLT는 각 함수용 코드를 포함하여 함수를 로딩했던 주소에 대한 런타임 링커를 쿼리한다. 그런 다음 테이블의 모든 엔트리를 채우고 그곳으로 옮겨간다. 각 함수가 호출될 때 PLT의 엔트리는 로딩된 함수로 단순히 직접 점프한다.
하지만 여분의 인다이렉션 레이어를 남겨둔다는 것을 기억해야 한다. 각 함수 호출은 점프를 통해 테이블로 바뀐다.
또, 호환성은 관계만을 위한 것은 아니다.
링크하는 것으로 완료한 라이브러리는 이것이 호출하는 코드와 호환되도록 해야 한다. 정적으로 링크 된 실행파일이 있다면 변경될 것이 없다. 동적 링크라면 보장 못한다.
이때, 버전 숫자러 해결이 가능하다. 프로그램이 라이브러리로 링크 되면 버전 숫자를 갖게 된다. 동적 링커는 매칭(matching) 버전 숫자를 검사할 수 있다. 라이브러리가 변경되면 버전 숫자는 맞지 않을 것이고 프로그램은 새로운 버전의 라이브러리로 링크 되지 않을 것이다. 하지만 동적 링크의 강력한 장점들 중 하나는 버그를 픽스하는데 있다. 라이브러리에서 버그를 픽스하고 그 픽스를 활용하기 위해 많은 프로그램들을 재컴파일 할 필요가 없다면 참 좋은 것이다. 가끔은 새 버전으로 링크해야 할 때도 있다.
불행히도 새로운 버전으로 링크해야 하는 경우도 있고, 어떤 경우는 오래된 버전을 구수해야 한다. 해결책은 있다. 두 종류의 버전 숫자이다.

    메이저 넘버(major number)는 라이브러리 버전들 간 잠재적 비호환성을 나타낸다.
    마이너 넘버(minor number)는 버그 픽스들만을 나타낸다.

대부분의 경우 같은 메이저 넘버와 더 높은 마이너 넘버를 가진 라이브러리를 로딩하는 것이 안전하다; 높은 메이저 넘버를 가진 라이브러리를 로딩하는 것은 안전하지 못하다.
사용자들이 라이브러리 넘버와 업데이트를 트래킹하지 않도록 하려면 시스템은 많은 심볼릭 링크들이 있어야 한다. 일반적일 패턴은 다음과 같다.
libexample.so
는 다음으로 링크 된다.
libexample.so.N
이 시스템에서 N은 가장 높은 메이저 버전 숫자이다.
지원되는 모든 메이저 버전 넘버들은,
libexample.so.N
다음으로 링크 된다.
libexample.so.N.M
M은 가장 큰 마이너 버전 넘버이다.
따라서 -lexample을 링커에 지정하면 최근 버전에 대한 심볼릭 링크인 libexample.so를 찾는다. 한편, 기존 프로그램이 로딩되면 libexample.so.N을 로딩할 것이다. N은 원래 링크 되었던 버전이다.
디버그 하기 위해서는 우선 컴파일 방법을 알아야 한다!
공유 라이브러리로 문제들을 디버깅하기 위해서는 이들이 어떻게 컴파일 되는지를 알아두는 것도 유용하다.
전통적인 정적 라이브러리에서, 생성된 코드는 일반적으로 .a로 끝나는 이름을 가진 라이브러리 파일로 바인딩 되고 링커로 전달된다. 동적 라이브러리에서, 라이브러리 파일의 이름은 일반적으로 .so로 끝난다. 파일 구조는 약간 다르다.
일반적인 정적 라이브러리는 ar 유틸리티에서 만들어진 포맷에 있다. 이는 기본적으로 매우 단순한 아카이브 프로그램으로서 tar 와 비슷하지만 더 단순하다. 반면 공유 라이브러리들은 일반적으로 보다 복잡한 파일 포맷으로 저장된다.
현대적인 리눅스 시스템에서, 이는 일반적으로 ELF 바이너리 포맷((Executable and Linkable Format)을 의미한다. ELF에서, 각 파일은 하나의 ELF 헤더, 0 또는 세그먼트 그리고 0 또는 몇몇 섹션 등으로 구성된다. 세그먼트는 파일의 런타임 실행에 필요한 정보를 포함하고 있고, 섹션에는 링크와 재배치를 위한 중요한 데이터가 포함된다. 전체 파일에서 각 바이트는 한번에 단 한 섹션을 차지한다. 하지만 섹션에 들어가지 않은 고아 바이트가 있을 수 있다. 일반적으로 유닉스 실행파일에서 한 개 이상의 섹션들은 하나의 세그먼트에 둘러 쌓인다.
ELF 포맷은 애플리케이션과 라이브러리를 위한 스팩을 갖고 있다. 이 라이브러리 포맷은 객체 모듈의 아카이브 보다 훨씬 더 복잡하다.
링커는 심볼에 대한 레퍼런스들을 통해 소팅하면서 그들이 어떤 라이브러리들을 발견했는지를 기록한다. 정적 라이브러리에서 온 심볼들은 최종 실행파일에 추가된다; 공유 라이브러리에서 온 심볼들은 PLT에 놓여지고 PLT에 대한 레퍼런스들이 만들어진다. 일단 이 작업들이 수행되면 결과 실행 파일들은 런타임 시 로딩 될 라이브러리에서 검색할 심볼 리스트를 갖게 된다.
런타임 시, 애플리케이션은 동적 링커를 로딩한다. 사실, 동적 링커 자체는 공유 라이브러리들과 같은 종류의 버저닝을 사용한다. 예를 들어, SUSE Linux 9.1에서 /lib/ld-linux.so.2파일은 /lib/ld-linux.so.2.3.3에 대한 심볼릭 링크이다. 반면 /lib/ld-linux.so.1을 찾는 프로그램은 새 버전을 사용하려고 하지 않는다.
동적 링커는 재미있는 작업을 수행해야 한다. 프로그램이 원래 어떤 라이브러리(그리고 어떤 버전)에 링크 되었는지를 찾아서 이들을 로딩한다. 라이브러리 로딩은 다음으로 구성된다.

    찾기(시스템 상의 여러 디렉토리들 중 하나에 있을 수 있다)
    프로그램의 어드레스 공간으로 매핑하기
    라이브러리에 필요한 제로 메모리 블록 할당하기
    라이브러리의 심볼 테이블 attach하기

이 프로세스의 디버깅은 쉬운 일이 아니다. 여러 유형의 문제들을 경험하게 된다. 예를 들어, 동적 링커가 기존 라이브러리를 찾지 못하면 프로그램 로딩에 실패하게 된다. 원하는 모든 라이브러리를 찾았지만 심볼을 찾지 못하면 이 역시 실패한다. (하지만 그 심볼로 레퍼런스를 시도할 때까지 작동하지 않을 수도 있다)—이것은 일반적으로 드문 경우이고, 심볼이 없다면 초기 링크 때 공지된다.
동적 링커 검색 경로 변경하기
프로그램을 링크할 때, 런타임 시 검색할 추가 경로를 지정할 수 있다. gcc에서 문법은 -Wl,-R/path이다. 프로그램이 이미 링크 되었다면 환경 변수 LD_LIBRARY_PATH를 설정하여 이 작동을 변경할 수 있다. 일반적으로 이것은 애플리케이션이 시스템 디폴트의 일부가 아닌 경로를 검색하려고 할 때에만 필요하다. 대부분의 리눅스 시스템에서는 드문 경우이다. 이론상으로는 Mozilla 사용자들은 이 경로 세트로 컴파일 된 바이너리를 배포했지만 실행 파일을 시작하기 전에 라이브러리 경로를 적절히 설정하는 래퍼 스크립트를 배포하는 것을 더 선호한다.
라이브러리 경로 설정은 두 애플리케이션들이 비 호환 버전의 라이브러리를 요구하는 드문 경우에 대안을 제공할 수 있다. 특별한 버전의 라이브러리를 사용하여 디렉토리에서 한 개의 애플리케이션 검색을 갖는데 래퍼 스크립트가 사용될 수 있다. 최상의 솔루션이라고는 볼 수 없지만 어떤 경우에는 이보다 더 나은 것이 없다.
많은 프로그램에 경로를 추가해야 하는 급박한 경우라면 시스템의 디폴트 검색 경로를 변경할 수 있다. 동적 링커는 /etc/ld.so.conf를 통해 제어된다. 여기에는 기본적으로 검색할 디렉토리 리스트가 포함되어 있다. LD_LIBRARY_PATH에서 지정된 모든 경로는 ld.so.conf에 나열된 경로에 앞서 검색된다. 따라서 사용자들은 이들 설정을 오버라이드 할 수 있다.
대부분의 사용자들은 시스템 디폴트 라이브러리 검색 경로를 변경할 이유가 없다; 일반적으로 환경 변수는 검색 경로를 변경하는데 더 알맞다. 툴킷의 라이브러리들과 링크하는 것 또는 새로운 버전의 라이브러리에 대해 프로그램을 테스트 하는 경우가 그 예이다.
공유 라이브러리 문제를 해결하는 한 가지 유용한 툴은 ldd이다. 이 이름은 list dynamic dependencies의 앞 글자를 딴 것이다. 이 프로그램은 주어진 실행 파일 또는 공유 라이브러리를 찾아서 로딩해야 하는 공유 라이브러리가 무엇이고 어떤 버전이 사용되는 지를 파악한다.

마지막으로 테드가
Maintainer로 제작한 ext4에 대한 설명이다.
리눅스에는 다양한 파일시스템을 지원하는데 몇몇은 다른 운영체제를 위해 개발된 네트워크 파일 시스템이나 파일 시스템에 특화되어 있다. 하지만 놀랍게도 많은 파일 시스템이 리눅스 전용 파일 시스템으로 쓰인다. 루트와 시스템 디렉터리는 전용 파일시스템에 위치한다. 현재 이런 부류에 속하는 전용 파일 시스템은 ext2, ext3, ReiserFS, XFS, JFS, btrFS 등이 있다. 하지만 파일 시스템 설계와 개발이 계속 진행중이며, 현재 개발중인 가장 중요한 리눅스 전용 파일시스템은 ext4로, 리눅스 전용으로 개발되었던 원래 파일 시스템(ext)의 네번째 개정판이다.
리눅스 커널의 창시자인 리누즈 토발즈외에도 많은 커널 개발자들이 ext4가 ext3 보다 안전등 여러면에서 우려를 하였고 반대를 하였기 때문에 ext4는 ext3를 기반으로 개선하거나 더욱더 기능을 추가시킨 라이브러리이다.
새로운 기능을 ext3에 추가하면서 다음과 같은 여러가지 문제점이 나타났다.
    몇몇 새로운 기능은 과거 하위 호환성을 깨버린다.
    ext3코드는 점점 더 복잡해지고 유지보수가 어려워진다.
    변경 내역은 아주 안정적인 ext3를 안정돼지 못한 상태로 만들어 버릴지도 모른다.
이런 이유로 인해, 개발자들은 2006년 중순에 ext4개발을 ext3로 뷴리하기로 결정하였고, 분리 시점 이후로 ext4는 계속해서 작업이 진행되었지만, 큰 호흥을 얻지 못하다가 커널 2.6.19가 발표되면서, ext4는 주류 커널에 모습을 드러냈다.
ext4 파일시스템의 장점으로는
   
ext3는 32테라바이트 파일 시스템과 2테라바이트 파일을 지원하지만 실제로는 아키텍처와 시스템 설정에 따라 제약이 좀 더 커진다. 보통 2테라바이트 파일 시스템과 16기가바이트 파일까지 낮아진다. 반면에 ext4는 1024페타바이트(1엑사바이트) 파일 시스템16테라바이트 파일을 지원한다. 이는 평균적인 데스크톱 컴퓨터나 서버에서 (아직!) 중요하지 않을지도 모르지만, 대규모 디스크 배열을 사용하는 관리자에게는 중요하다.
    extents는 디스크 파일 기술자의 효율성을 개선하는 방법으로 특히 큰 파일을 삭제할 때 완료 시간을 줄인다.
    응용 프로그램이 실제로 사용하기 전에 디스크 공간을 할당할 필요가 있다면, 대다수 파일 시스템은 아직 사용하지 않은 디스크 공간에 0을 기록하는 방법으로 선행 할당 작업을 수행한다. ext4는 이런 작업 없이도 선행 할당이 가능하므로 몇몇 데이터베이스와 멀티미디어 도구 성능을 개선한다.
    ext4는 마지막 순간까지 디스크 공간 할당을 지연하므로 성능을 개선한다.
    ext3에서 하위 디렉터리를 단지 3만 2000개만 만들 수 있었지만 ext4에서는 제한이 없다.
    ext4는 저널 자료에 체크섬을 추가해 안정성과 성능을 개선했다.
    ext3에서 과도한 단편화가 일어나지 않음에도 불구하고 파일 저장 과정에서 어느 정도 단편화는 피하기 어렵다. ext4는 온라인 조각 모음을 제공해 전반적인 성능을 개선한다.
    ext4는 fsck가 점검 과정에서 사용하지 않는 부분을 건너뛰도록 자료 구조를 추가했으므로 시스템 점검 속력을 높인다
    ext3를 포함한 대다수 파일 시스템은 초 단위로 타임스탬프 자료를 기록한다. ext4는 나노초 단위로 정밀도를 높였다.
현재 개발중이기 때문에 검색이외의 부분은 ext3와 성능이 비슷하거 조금 앞서는 수준이고 Maintainer인 테드조차 확실한 원인파확을 못 했을 정도로 쉽지 않은 일이지만 2009년 초 Fedora10에 Built-in 될 예정이라 한다.

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

24인치 모니터와 약간의 달러로 환생하신 노트북님의 도움으로 별 무리없이 가게된 미쿡 연수

환율이 너무 올라버려 환전결과가 안습이었지만 공짜로 간다는데 위안을 삼고

뭔가 많은것을 배워올수 있도록 노력해야겠다ㅋㅋ

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


분명 iPhone은 WM용 Device Driver를 제공하지 않을테고 데이터시트가 있는것고 아닐텐데...
게다가 두가지 OS를 택해쓴다는건 부트로더도 수정하거나 직접 만든걸테고
초반 OS 선택이 GUI 환경인걸 봐서는 펌웨어 딴에서 프레임버퍼를 제어했다는 얘긴데...

글구 부트 캠프 어쩌고 하는데 설마 데스크탑용 부트캠프를 포팅한다는건
절대 Never 내상식으론 말이 안되는 얘기인데..

왠지 Fake같기도 하고....

어찌되었건 사실이라면 정말 대단한 일이 아닐수 없다
나이도 어랴보이는데...

난 도데대 저나이에 뭘 하고 있었지??

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

오랫만에 떠나보는 여행 이었다
첫목적인 순천은 KTX가 정차하지 않는 관계로 익산에서 환승을 해야만 했다

익산에 날 내려주고 부랴부랴 사라지는 KTX
오전 열시반에 출발하여 순천에 도착한 시간은 오후 두시 이십분...
KTX덕에 시간이 단축되긴 했지만 예정보다 늦은 출발에 생각보다 도착시간이 늦어 버렸다

그나마 운좋게 택시를 나누어 타고 이곳 낙안 읍성에 도착하게 되었다
낙안읍성에대해 간단히 설명을 하자면
0123456789101112131415161718192021
낙안 읍성에서

60~70년대 급격한 변화 속에서도 이런곳이 남아 있다는게 한편으론 신하기도 하였고
민속촌과 같이 의도된 것이 아닌 실제 사람이 거주하는 전통 마을 이라는 면이 정말 좋아보였다

다음으로 향한곳은 불교 조계종 본사인 송광사 였다
도착한 시간이 이미 오후 여섯시가 넘어간 늦은시간인데다 산속이어서 인적도 별로 없고
땅거미가 지고 있었지만 다음에 언제올지 모르는이곳!! 발걸음을 재촉하였다
0123456

너무 늦은 시간에 도착했기에 과연 무엇을 할 수 있을까 많은 걱정을 했지만 다행인지 늦은시간 임에도
법회를 하고 있었고 그것을 지켜보는 사람들도 꽤 있었어 마음이 놓였다
하지만 돌아오는 길에는 아무도 없고 가로등 하나 제대로 없는 길을 내려오느라 제법 무서웠다

이렇게 순천여행을 마무리 짓고 다음으로 여수를 향했다


밤 아홉시가 되어서야 도착한 여수... 옛기억을 떠올려 일단 돌산대교 밑 횟집을 찾아가 그제서야 저녘을 먹었다
가격대비 좀 비싼감이 없지 않았지만 같이간 친구놈덕분에 부담없이 맛있게 먹었다 (난아직 백수이기에... -.ㅜ;)

술까지 한잔하고 나서 기분도 좋은김에 돌산대교 야경을 발로 찍어 보났다

01


야경은 포인트가 중요하다고 그나마 돌산공원에 올라가서 찍었으면 하나라도 건졌을텐데 오랫만에 돌아다니려니 피곤하기도 했도 언넝가서 짐풀도 솔도좀 먹고 싶었고... 그냥 그렇게 하루를 술과함께 마무리 했다

그리고 일어난 그 다음날
...젠장 비가 조금씩 오고 있었다. 처음에 왔을때도 이러더니 여수와 나는 인연이 없는 것일까?
덕분에 항일암 가는것은 취소하고 오동도에 들렸다

0123456789101112


비가와서 인걸까? 아니면 옛일이 떠올라서 였을까? 다시찾은 이곳은 여느 여행들과 다른 느낌이 가득했다
그리고 바다를 보러 온 곳이었는데 바다보다 음악에 맞춰 춤을추는 분수가 훨씬 더 인상적이었다

이곳을 마지막으로 6시간가까이 고속버스를 타고 다시 서울로 돌아 왔다

급하게 계획하고 다녀온 여행 이어서인지 부족함과 그로인한 아쉬움이 어느때 보다도 큰 여행이었다



항상 그렇지만 발품팔아 돌아다니기를 좋와하는 날 지탱해주는 튼튼한다리와 나 돈없는 백수 라는거 뻔히 알면서도
같이 다녀주는 친구녀석이 고마울 따름이다

마지막으로 나의 발자취...

'사진 & 여행' 카테고리의 다른 글

어린이날 부천 로봇 댄스대회  (1) 2009.06.05
크리스마스 데스크탑 배경화면 1920*1200  (1) 2008.12.11
선유도 공원 주변의 야경들...  (2) 2008.05.14
부산 여행 사진들  (1) 2008.05.14
장옥이 결혼식  (0) 2008.05.14
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
 
LN2440SBC 보드에 포팅한 ARToolKit!!

WM이 아닌 linux상에서 올렸다는게 나자신도 신기할 따름

하지만....

ARM은 FPU가 없기 때문에 플로팅 연산이 뷁스럽고 결과는 이러하다



그래서

이번 임베디드 공모대전 보드지원에서 모바일 과제로 전향하며 얻은

PXA270칩을 사용하고 3D연산 모듈도 달려 있는 녀석!!

사용자 삽입 이미지

바로 이녀석!!!

3D모듈이 openGL이 아닌 openGL ES를 지원하고

PXA칩 아키텍쳐를 잘 모르기에 과연 ARToolKit이 잘 돌아갈지는 미지수 이지만

기대를 걸어본다
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
openGL을 보드로 포팅시
TinyX, MESA, GLUT 최소한 이렇게 세가지를 컴파일 해줘야 한다

TinyX같은경우 검색해보면 쉽게 포팅관련 문서를 구할 수 있고
MESA의 경우도 어렵지 않기에 생략하고 GLUT만 설명하겠다

http://www.opengl.org/resources/libraries/glut/glut_downloads.php
여기서 GLUT-3.7을 다운받아 적당한곳에 압축을 푼다

참고로 GLUT포팅하면서 얻어야 할것은 공유 라이브러리 libglut.so.3.7이다

$GLUT_HOME/linux 디렉토리의 README파일을 열어보면
$GLUT_HOME/lib/glut/ 디렉토리에 $GLUT_HOME/linux/Makefile을
복사해 넣으라고 한다

복사후 $GLUT_HOME/lib/glut/Makefile을 열어보자

....
#-----------------------------------------------------------------------
# start of Imakefile
# $XConsortium: Library.tmpl,v 1.34 95/06/16 17:44:51 gildea Exp $
# $XFree86: xc/config/cf/Library.tmpl,v 3.3 1995/07/08 10:21:44 dawes Exp $

         CC = arm-linux-gcc


  CCOPTIONS = -ansi -fPIC


STD_DEFINES = -Dlinux -D__i386__ -D_POSIX_SOURCE -D_BSD_SOURCE -D_GNU_SOURCE -DX_LOCALE
CDEBUGFLAGS = -O2 -msoft-float #-m486

# Copyright (c) Mark J. Kilgard, 1994.
# Glut.cf - GLUT distribution Imakefile configuration info

EXTRA_INCLUDES = -I$(TOP)/include -I$(TOP)

GLUT_DEPLIBS = $(DEPGLUT) $(DEPXMULIB) $(DEPXLIB)
GLUT_LIBS = $(GLUT) $(GLU) $(OPENGL) $(XMULIB) $(XLIB) -lm
OPENGL = $(TOP)/../lib/libMesaGL.so
GLU = $(TOP)/../lib/libMesaGLU.so
INVENTOR = -lInventor
GLUT_DEPLIBS = $(DEPGLUT) $(DEPXMULIB) $(DEPXILIB) $(DEPXLIB)
GLUT_LIBS = $(GLUT) $(GLU) $(OPENGL) $(XMULIB) $(XILIB) $(XLIB) -lm
....

이부분이 GLUT라이브러리 컴파일 할때 사용되는 부분이다

빨간색으로 표시된 부분만 수정해주고 gl.h, glu.h경로만 잘 설정해 주고
$GLUT_HOME/lib/glut/ 경로에서 make를 하면 문제없이 컴파일이 될것이고

그 폴더에 libglut.so.3.7파일이 생성돼 있을 것이다

혹시라도 -msoft-float 옵션에 대해 알고싶으면 밑에글
gcc 컴파일 옵션.(에서 붉은색 부분)을 읽어 볼것을 권장한다.
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
gcc는 예전에는 GNU C Compiler의 약자였으나 지금은 GNU Compiler Collection의 약자로 다양한(?) 언어의 컴파일러들의 집합체이다. gcc는 한마디로 GNU에서 개발된 ANSI C 표준을 따르는 C 언어 컴파일러라고 말할 수 있다. gcc는 ANSI C 표준에 따르기는 하지만 ANSI C 표준에는 없는 여러 가지 확장 기능이 있다. 또한 gcc는 통합개발환경(IDE)을 가지고 있지 않은 command line 컴파일러이다. 옛날 Turbo-C를 주로 사용해 보셨던 분들은 tcc.exe와 비슷하다고 생각하면 된다.

(*) -v 옵션
현재 사용되고 있는 gcc의 버전을 나타내는 옵션이다. 특정 소프트웨어 패키지를 컴파일하기 위해 어느 버전 이상의 gcc를 쓰도록 션권장하는 경우가 있는데 시스템에 깔려있는 gcc의 버전을 파악하려고 할때 사용한다.

이제 직접 프로그램 하나를 컴파일하면서 설명하도록 하겠다. 아래는 hello.c의 소스이다.
#include〈stdio.h〉

int main()
{
  printf(“hello gccn”);
  return 0;
}

$ gcc -o hello hello.c
로 컴파일하면 실행파일 hello가 만들어진다.

(*) -o 파일이름 옵션
gcc의 수행 결과 파일의 이름을 지정하는 옵션이다. 위의 예제를 단순히
$ gcc hello.c
로 컴파일 하면 hello라고 하는 실행파일이 만들어 지는 것이 아니라 보통의 경우 a.out이라는 이름의 실행파일이 만들어진다.
-o hello 옵션을 줌으로써 결과물을 hello라는 이름의 파일로 만들어지게 하였다.

위의 컴파일 과정을 외부적으로 보기에는 단순히 hello.c파일이 실행파일 hello로 바뀌는 것만 보이지만 내부적으로는 다음과 같은 단계를 거쳐 컴파일이 수행된다.

 (1) C Preprocessing
 (2) C 언어 컴파일
 (3) Assemble
 (4) Linking

C Preprocessing은 C 언어 배울 때 배운 #include, #define, #ifdef 등 #으로 시작하는 여러 가지를 처리해 주는 과정이다. 그 다음 C 언어 컴파일은 C Preprocessing이 끝난 C 소스 코드를 assembly 소스코드로 변환하는 과정이다. Assemble은 그것을 다시 object 코드(기계어)로 변환하고 printf()함수가 포함되어 있는 라이브러리와 linking을 하여 실행파일이 되는 것이다.
위의 네 가지 과정을 모두 gcc라는 실행파일이 해 주는 것일까? 겉으로 보기에는 그렇게 보일지 모르지만 실제로는 gcc는 소위 말해 front-end라고 하여 껍데기에 지나지 않고 각각을 해 주는 다른 실행파일을 gcc가 부르면서 수행된다.
C Preprocessing을 전담하고 있는 실행파일은 cpp라고 하여 /usr/bin 디렉토리와 /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.95.12 디렉토리(당연히 gcc버전과 시스템에 따라 디렉토리 위치가 다르다. gcc -v로 확인하길 바란다.)에 존재한다. C 언어 컴파일은 cc1이라는 실행파일이 담당하는데 /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.95.12 디렉토리에 존재한다. Assemble과 linking은 각각 as와 ld라는 실행파일이 담당하고 /usr/bin 디렉토리에 존재하는 파일이다. (참고 : 시스템에 따라 /usr/bin이 아니라 /bin또는 /usr/local/bin 디렉토리에 존재할 수도 있다.)

gcc라는 실행파일이 하는 일을 정리해 보면 다음과 같다.
 (1) 사용자에게 옵션과 소스 파일명들의 입력을 받는다.
 (2) 소스 파일명의 확장자를 보고 어떤 단계를 처리해야 할지 결정한다.
 (3) 사용자의 옵션을 각각의 단계를 맡고 있는 실행파일의 옵션으로 변경한다.
 (4) 각각의 단계를 맡고 있는 실행파일을 호출(fork, exec)하여 단계를 수행하도록 한다.

=== C Preprocessing(cpp)
C preprocessing을 우리말로 하면 "C 언어 전처리"라고 할 수 있을 것이다. 모든 C 언어 문법책에서 정도의 차이는 있지만 C preprocessing에 대한 내용을 다루고 있다. C preprocessing에 대한 문법은 C 언어 문법의 한 부분으로 가장 기본이 되는 부분이다. C preprocessing에 관한 문법은 모두 '#'으로 시작된다. '#' 앞에는 어떠한 문자(공백 문자 포함)도 오면 안된다. 하지만 대부분의 compiler가 '#'앞에 공백 문자가 오는 경우에도 처리를 한다.

== C preprocessing이 하는 일
 (1) 입력 : C 언어 소스 코드
 (2) 출력 : 전처리가 완료된 C 언어 소스 코드
 (3) 하는 일
  - 파일 포함(file inclusion - 헤더파일 및 기타파일)
  - 매크로(macro) 치환
  - 선택적 컴파일(conditional compile)
  - 기타(#line, #error, #pragma)

cpp 는 C 언어 소스코드를 입력 받아서 C preprocessing에 관련된 문법 사항을 적절히 처리하고 결과로 C 언어 소스코드를 출력하는 프로그램이다. 입력은 작성된 C 언어 소스 코드이고, 출력으로 나온 C 언어 소스 코드에는 C preprocessing 문법에 관련된 어떠한 것도 남아있지 않는다. 즉, #define, #include 등을 찾을 수 없다. 남아 있는 정보가 있다면 file 이름과 줄수(line number)에 관한 정보이다. 그 이유는 추후의 컴파일 과정에서 에러가 날 때 그 정보를 이용해서 error를 리포팅할 수 있도록 하기 위해서이다. 그렇다면 C preprocessing을 직접 해보자.
$ gcc -E -o hello.i hello.c
결과로 hello.i라는 파일이 생긴다. 그 파일을 에디터로 열어보면 hello.c의 첫번째 줄에 있는 #include 를 처리한 결과가 보일것이다.

(*) -E 옵션
-E 옵션은 gcc의 컴파일 과정 중에서 C preprocessing까지만 처리하고 나머지 단계는 처리하지 말라는 것을 지시하는 것이다. 평소에는 별로 쓸모가 있는 옵션이 아니지만 다음과 같은 경우에 유용하게(?) 사용할 수 있다.
 (1) C 언어 소스 코드가 복잡한 선택적 컴파일을 하고 있을 때, 그 선택적 컴파일이 어떻게 일어나고 있는지 알고 싶은 경우.
 (2) preprocessing의 문제가 C 언어 에러로 나타날 경우. 다음과 같은 소스코드를 고려해 보자.

#define max(x, y) ((x) 〉(y) ? (x) : (y) /* 마지막에 ")"가 없다!!! */
int myMax(int a, int b)
{
  return max(a, b);
}

$ gcc -c cpperr.c
다음과 같은 에러가 난다.
cpperr.c: In function `myMax':
cpperr.c:4: parse error before `;'
cpperr.c파일의 4번째 줄에서 ';'가 나오기 전에 parse error가 났다. 하지만 실제 에러는 #define에 있었으므로 그것을 확인하려면 -E 옵션으로 preprocessing을 하여 살펴 보면 쉽게 알 수 있다.

(*) 참고 : parse error before x(어떤 문자) 에러는 소스코드를 parsing 할 때 발생한 에러를 말한다. parsing이란 syntax analysis(구문해석) 과정인데 쉽게 말하면 C 언어 소스코드를 읽어들여 문법적 구성요소들을 분석하는 과정이라고 할 수 있다. 보통 gcc에서 parse error라고 하면 괄호가 맞지 않았거나 아니면 ';'를 빼먹거나 했을 때 발생한다. 보통의 경우 before x라고하여 x라는 것이 나오기 전에 parse error가 발생하였음을 알려주기 때문에 그 x가 나오기 전에 있는 C 소스 코드를 잘 살펴보면 문제를 해결할 수 있다.

C preprocessing의 문법과 나머지 C 언어의 문법과는 거의 관계가 없다. 관계가 있는 부분이 있다면 정의된 macro가 C 언어의 문법 상의 char string literal에는 치환되지 않는다는 정도이다. (좀 더 쉽게 이야기 하면 큰 따옴표 안에서는 macro 치환이 되지 않는다.) 또한 c preprocessing은 architecture dependent하지 않다. 즉, i386용으로 컴파일된 cpp를 다른 architecture에서 사용해도 무방하다. 조금 문제가 있을 수 있는 부분이 있다면 gcc의 predefined macro(i386의 경우 i386용 자동으로 define된다.)가 다를 수 있다는 점 뿐이다. 따라서 cpp를 C 언어 소스코드가 아닌 다른 부분에서 사용하는 경우도 있다. 대표적으로 assembly 소스 코드에서도 사용한다. assembler가 사용하고 있는 macro 문법이 c preprocessing의 macro문법 보다는 배우기 쉽기 때문이다.

이제 preprocessing이 하는 일에 대해서 좀더 알아 보자.
== 파일 포함(file inclusion)
#include 〈stdio.h〉
#include "config.h"
위 와 같이 많은 C 언어 소스코드에서 헤더 파일을 포함한다.〈〉와 ""의 차이는 기본적인 헤더파일과, 사용자 정의 헤더파일을 구분하는 정도이다. include한 헤더 파일은 default로 특정 디렉토리를 찾게 된다. Linux 시스템의 경우 /usr/include가 default 디렉토리이다. (실제로도 그곳에 stdio.h라는 파일이 있다.) 그 다음은 현재 디렉토리를 찾게 된다.(물론〈〉와 ""에 따라서 다르다.) 파일이 없으면 당연히 에러가 발생한다. gcc의 경우 다음과 같은 에러가 발생한다.
>>소스코드파일명:line number: 헤더파일명: No such file or directory
또는(LANG=ko일때)
>>소스코드파일명:line number: 헤더파일명: 그런 파일이나 디렉토리가 없음

그렇다면 include하고 싶은 파일이 default 디렉토리와 현재 디렉토리에 없으면 어떻게 할까? 그런 문제를 해결하기 위해서 다음과 같은 옵션이 존재한다.

(*) -Idir 옵션
여 기서 dir은 디렉토리 이름이고 -I와 디렉토리 이름을 붙여 써야 한다. 그럼 include한 헤더 파일을 그 디렉토리에서도 찾아 주게 된다. 당연히 옵션을 여러 번 다른 디렉토리 이름으로 사용할 수도 있어서 헤더 파일을 찾을 디렉토리를 여러 개로 지정할 수 있다. 꼭 알아 두어야 할 옵션이다.

(*) -nostdinc
이 옵션은 default 디렉토리(standard include 디렉토리)를 찾지말라고 지시하는 옵션이다. 어플리케이션 프로그래머는 관심을 둘 필요가 없지만 kernel 프로그래머는 관심 있게 볼 수 있는 옵션이다.

== macro 치환
macro 치환에 대해서는 특별히 일어날 만한 에러는 없다. 가끔 문제가 되는 부분이 macro 정의가 한 줄을 넘어선 경우 역슬레쉬('')로 이어져야 하는데 그 소스 파일이 windows용 에디터로 편집 되었으면 parse error가 나는 경우가 있다. 그것은 개행문자(new line character)가 서로 달라서 그런 것인데...음 자세히 이야기하자면 끝이 없으므로 그냥 넘어가도록 해야한다. 또한 macro가 define된 상황에서 macro를 undef하지 않고 다시 define하면 다음과 같은 Warning이 난다.
'xxxx' redefined

macro 치환에서 대한 옵션 두개를 알아보도록 하자.

(*) -Dmacro 또는 -Dmacro=defn 옵션
gcc 의 command line에서 macro를 define할 수 있도록 하는 옵션이다. 예를 들어 -D__KERNEL__이라는 옵션을 주면 컴파일 과정 중에 있는 C 언어 소스코드의 맨 처음에 #define __KERNEL__이라고 해준 것과 같이 동작한다. 또한 -DMAXLEN=255라고하면 C 언어 소스코드의 맨 처음에 #define MAXLEN 255 라고 한 것과 동일한 결과를 준다. 선택적 컴파일을 하는 경우에 많이 이용하는 옵션으로 꼭 알아야 할 옵션이다.

(*) -Umacro 옵션
이 옵션은 #undef하고 하는 일이 똑같은데 C 언어 소스코드와는 하등의 관계가 없다. -Dmacro옵션처럼 C 언어 소스코드의 맨처음에 #undef macro를 해주는 것은 아무런 의미가 없기 때문이다.(어짜피 #define은 그 이후에 나올 것이므로...) 이 옵션의 목적은 위의 -Dmacro옵션으로 define된 macro를 다시 undef하고자 할 때 쓰는 옵션이다. 평상시에는 별로 쓸 일이 없는 옵션이지만 그냥 -Dmacro와 같이 짝으로 알아 두길 바란다.

== 선택적 컴파일
#if 시리즈와 #else, #elif, #endif 등으로 선택적 컴파일을 수행할 수 있다. 위에서 설명한 -Dmacro 옵션과 같이 쓰는 경우가 많다. 특별히 설명할 옵션은 없고 #if와 #else, #endif의 짝이 잘 맞아야 한다. 그렇지 않으면 당연히 에러가 발생한다. 단순히 parse error라고 나오는 경우는 드물고, #else, #if 에 어쩌고 하는 에러가 난다. 많이 경우의 수가 있으므로 직접 에러가 발생되도록 코딩을 해보고 확인해 보는 것이 좋다.

== 기타(#line, #error, #pragma)
#line, #error, #pragma라는 것이 있는지도 모르는 사람들이 꽤 있것이다. 자세한 것은 C 언어 문법 책을 찾아보길 바란다. #line의 경우 C 언어 소스코드 직접 쓰이는 경우는 거의 없으니까 무시하고 #pragma는 compiler에 dependent하고 gcc에서 어떤 #pragma를 사용하는지도 알 수 없으므로 그냥 넘어가도록 한다. #error의 경우 C preprocessing 과정에서 강제로 에러를 만드는 지시어이다. 선택적 컴파일 과정에서 도저히 선택되어서는 안 되는 부분에 간혹 쓰인다. 당연히 #error를 만나면 에러가 생긴다. linux kernel 소스코드에서 include 디렉토리를 뒤져 보시면 사용하는 예를 만날 수 있다.

== predefined macro
사 용자가 C 언어 소스코드에서 #define을 하지 않아도 이미 #define된 macro가 있다. ANSI C에서는 __LINE__, __FILE__, __TIME__, __DATE__, __STDC__ 다섯 가지는 이미 define되어 있는 macro로 강제적인 사항이다.(문법책 참조) gcc도 당연히 다섯 가지 macro를 predefine 한다. 뿐만 아니라 GCC의 버전에 대한 macro, architecture에 관한 사항 등을 -Dmacro 옵션 없이도 predefine 한다. -v 옵션을 실행하여 출력되는 specs파일을 열어보면 쉽게 알 수 있을 것이다.(specs파일이 어떻게 해석되는지는 나도 잘 모른다.)

== 꼭 알아두면 좋은 옵션 한가지
다음과 같이 shell 상에 입력해 보라.(hello.c는 계속되는 그 녀석이다.)
$ gcc -M hello.c
어떤 것이 출력되나? "hello.o: hello.c /usr/include/stdio.h 어쩌구저쩌구"가 출력될 것이다. 어디서 많이 본 듯한 형식 아닌가?

(*) -M 옵션
-M 옵션은 cpp에게 makefile로 만들 수 있는 rule을 만들어 달라고 하는 요청을 보내는 명령이다. file을 include하는 녀석은 cpp이므로 rule은 cpp가 만들 수 있다. 당연히 -Dmacro, -Umacro, -Idir 옵션 등을 같이 사용할 수 있고 그에 따라 결과가 달라질 수도 있다. makefile을 좀 쉽고 정확하게 만들 때 쓰는 옵션이므로 알아두면 좋다. 단점은 default 디렉토리에 있는 보통 사용자는 고칠 수도 없는 파일까지도 만들어 준다는 것이다.

=== C 언어 컴파일 과정
C 언어 컴파일 과정은 gcc라고 하는 frontend가 cc1이라는 다른 실행파일을 호출(fork & exec)하여 수행하게 된다. 사용자가 cc1이라는 실행파일을 직접 실행해야 할 하등의 이유도 없고 권장되지도 않는다. gcc의 입력으로 여러 개의 파일(C 소스 파일, object 파일 등)을 준다고 하더라도 컴파일 과정 중 앞 3단계, 즉 cpp, C 컴파일, assemble은 각각의 파일 단위로 수행된다. 서로 다른 파일의 영향을 받지 않고 진행된다. 특정 C소스 코드에서 #define된 macro가 다른 파일에는 당연히 반영되면 안된다. header 파일의 존재 의미를 거기서 찾을 수 있다.

이제 C 언어 컴파일 과정이 하는 일을 알아보도록 하자.

== C 언어 컴파일 과정이 하는 일
 (1) 입력 : C 언어 소스 코드(C preprocessing된)
 (2) 출력 : Assembly 소스 코드
 (3) 하는 일 : 컴파일(너무 간단한가?)

C preprocessing과 마찬가지로 너무 간단하다. 하지만 위의 “컴파일” 과정은 cc1 내부에서는 여러 단계로 나누어져 다음과 같은 순서로 일어난다. Parsing(syntax analysis)이라고 하여 C 언어 소스 코드를 파일로부터 읽어 들여 컴파일러(여기서는 cc1)가 이해하기 쉬운 구조로 바꾸게 된다. 그 다음에 그 구조를 컴파일러가 중간 형태 언어(Intermediate Language)라고 하는 다른 언어로 변환하고 그 중간 형태 언어에 여러가지 최적화를 수행하여 최종 assembly 소스 코드를 만들게 된다.

직접 수행해 보자. 다음과 같이 shell의 command line에 입력하라. 역시 지긋지긋한 hello.c를 이용하도록 한다.
$ gcc -S hello.c
결 과로 출력된 hello.s를 에디터로 열어서 살펴보라 (혹시 위의 command로 hello.s가 만들어 지지 않는다면 gcc -S -o hello.s hello.c로 하라.). “.”으로 시작하는 assembler directive와 “:”로 끝나는 label명, 그리고 몇 개의 assembly mnemonic이 보이나? Assembly 소스를 읽을 줄 몰라도 그게 assembly 소스 코드구나 생각하면 된다.

(*) -S 옵션
-S 옵션은 gcc의 컴파일 과정 중에서 C 언어 컴파일 과정까지만 처리하고 나머지 단계는 처리하지 말라는 것을 지시하는 것이다. 평소에는 거의 사용하지 않는 옵션이지만 다음과 같은 경우에 유용하게 사용할 수 있다.
 (1) 어셈블리 코드가 어떻게 생겼는지 보고 싶은 호기심이 발동한 경우
 (2) C calling convention을 알아보거나 stack frame이 어떻게 관리되고 있는 지 보고 싶은 경우

보 통의 경우는 아니지만 사용자가 직접 assembly 코딩을 하는 경우가 종종 있다. 아무래도 사람이 기계보다는 훨씬 똑똑하기 때문에 사람이 직접 assembly 코딩을 해서 최적화를 시도하여 소프트웨어의 수행 시간을 단축시키거나, 아니면 linux kernel이나 bootloader 등과 같이 꼭 assembly가 필요한 경우가 있다. 이때도 보통의 경우는 소프트웨어의 전 부분을 assembly 코딩하는 것이 아니라 특정 부분만 assembly 코딩을 하고 나머지는 C 언어나 다른 high-level 프로그래밍 언어를 써서 서로 연동을 하도록 한다. 그럼 C 언어에서 assembly 코딩된 함수를 호출할 때(반대의 경우도 마찬가지), 함수의 argument는 어떻게 전달되는 지, 함수의 return 값은 어떻게 돌려지는지 등을 알아볼 필요가 있다. 이렇게 argument와 return 값의 전달은 CPU architecture마다 다르고 그것을 일정한 약속(convention)에 따라서 처리해 주게 된다. 위의 hello.s를 i386용 gcc로 만들었다면 파일 중간에 xorl %eax,%eax라는 것이 보일 것이다. 자기 자신과 exclusive or를 수행하면 0(zero)이 되는데 이것이 바로 hello.c에서 return 0를 assembly 코드로 바꾼 것이다. 결국 i386 gcc에서는 %eax 레지스터에 return 값을 넣는다는 convention이 있는 것이다.(실제로는 gcc뿐 아니라 i386의 convention으로 convention을 따르는 모든 compiler가 %eax 레지스터를 이용하여 return값을 되돌린다.) argument의 경우도 test용 C 소스를 만들어서 살펴볼 수 있을 것이다. 물론 해당 CPU architecture의 assembly 소스코드를 어느 정도 읽을 수 있는 사람들에게만 해당하는 이야기 이다. stack frame도 비슷한 얘기 쯤으로 알아 두길 바란다.

== Parsing(Syntax Analysis)
위 에서 cc1이 컴파일을 수행하는 과정 중에 맨 첫 과정으로 나온 Parsing에 대해서는 좀더 언급을 한다. Parsing과정은 그야말로 구문(Syntax)을 분석(Analysis)하는 과정이다. Parsing의 과정은 파일의 선두에서 뒤쪽으로 한번 읽어 가며 수행된다. Parsing 중에 컴파일러는 구문의 에러를 찾는 일과 뒤에 수행될 과정을 위해서 C 언어 소스 코드를 내부적으로 다루기 쉬운 형태(보통은 tree형식을 이용)로 가공하는 일을 수행한다. 이 중에 구문의 에러를 찾는 과정은 (1) 괄호 열고 닫기, 세미콜론(;) 기타 등등의 문법 사항을 체크하는 것 뿐만 아니라, (2) identifier(쉽게 말해 변수나 함수 이름 들)의 type을 체크해야 한다.
 (1) 괄호 열고 닫기, 세미콜론(;) 기타 등등의 문법 사항에 문제가 생겼을 때 발생할 수 있는 에러가 전에 이야기한 parse error이다. 보통 다음과 같이 발생한다.
>> 파일명과 line number: parse error before x
당연히 에러를 없애려면 ‘x’ 앞 부분에서 괄호, 세미콜론(;) 등을 눈 빠지게 보면서 에러를 찾아 없애야 한다.
 (2) type checking
구문 에러를 살필 때 type 체크를 왜 해야 할까? 다음과 같은 예를 보자.
var3 = var1 + var2;
앞 뒤에서 parse error가 없다면 위의 C 언어 expression은 문법적인 문제가 없는가? 하지만 var1이 파일의 앞에서 다음과 같이 정의(definition)되었다면 어떻게 될까?
struct point { int x; int y; } var1;
당연히 ‘+’ 연산을 수행할 수 없다.(C 언어 문법상) 결국은 에러가 난다. 이렇게 identifier(여기서는 var1, var2, var3)들의 type을 체크하지 않고서는 구문의 에러를 모두 찾아낼 수 없다.
만 약 var1과 var3가 파일의 앞에서 int var1, var3;로 정의되어 있고 var2가 파일의 앞에 어떠한 선언(declaration)도 없이 파일의 뒤에서 int var2;로 정의되어 있다면 에러가 발생할까? 정답은 “발생한다”이다. 위에서 언급했듯이 Parsing은 파일의 선두에서 뒤쪽으로 한번만(!!!) 읽으면서 진행하기 때문이다.(모든 C 컴파일러가 그렇게 동작할지는 의심스럽지만 ANSI C 표준에서는 그렇게 되어 있는 것으로 알고 있다. Assembler는 다르다.)
그 렇다면 어떤 identifier를 사용하려면 반드시 파일 중에 사용하는 곳 전에 identifier의 선언(declaration) 또는 정의(definition)가 있어야 한다. 하지만 identifier가 함수 이름일 경우(즉 identifier뒤에 (…)가 올 경우)는 조금 다르다. C 컴파일러는 함수 이름 identifier의 경우는 int를 return한다고 가정하고 Error가 아닌 Warning만 출력한다.(Warning옵션에 따라 Warning조차 출력되지 않을 때도 있다.) 그럼 다음과 같은 소스 코드를 생각해 보자.
int var3, var2;
….
var3 = var1() + var2;
….
struct point var1(void) { … }
위 와 같은 경우도 문제가 생긴다. 맨 처음 var1이라는 함수 이름 identifier를 만났을 때 var1 함수는 int를 return한다고 가정했는데 실제로는 struct point를 return하므로 에러 또는 경고를 발생한다.
결국 권장하는 것은 모든 identifier는 사용하기 전(파일 위치상)에 선언이나 정의를 해 주는 것이다. 다음과 같은 에러 메시지들을 짧막하게 설명해 본다.
 파일명 line number: ‘x’ undeclared …. 에러 --> ‘x’라는 이름의 identifier가 선언되어 있지 않았다.
  파일명 line number: warning: implicit declaration of function `x' … 경고 --> ‘x’라는 이름의 함수가 선언되어 있지 않아 int를 return한다고 가정했다는 경고(Warning) 메시지이다.

변수나 함수의 선언(declaration)과 정의(definition)에 대해서 알지 못한다면 C 언어 문법책을 찾아서 숙지하길 바란다. 그런 내용이 없다면 그 문법책을 휴지통에 버리길 바란다.

Parsing 과정에는 위의 identifier 에러 및 경고를 비롯한 수많은 종류의 에러와 경고 등이 출력될 수 있다. 에러는 당연히 잡아야 하고 경고도 무시하지 않고 찾아서 없애는 것이 좋은 코딩 습관이라고 할 수 있다. 경고 메시지에 대한 gcc 옵션을 살펴보도록 하자.

(*) -W로 시작하는 거의 모든 옵션
이 옵션들은 어떤 상황 속에서 경고 메시지를 내거나 내지 말라고 하는 옵션이다. -W로 시작하는 가장 강력한 옵션은 -Wall 옵션으로 모든 경고 메시지를 출력하도록 한다. 보통은 -Wall 옵션을 주고 컴파일 하는 것이 좋은 코딩 습관이다.

== Parsing 이후 과정
특 별한 경우가 아닌 이상 Parsing을 정상적으로 error나 warning없이 통과한 C 소스 코드는 문법적으로 완벽하다고 봐야 한다. 물론 논리적인 버그는 있을 수 있지만 이후 linking이 되기 전까지의 과정에서 특별한 error나 warning이 나면 안된다. 그런 경우가 있다면 이제는 사용자의 잘못이 아니라 gcc의 문제로 추정해도 무방하다. Parsing이후에 assembly 소스 코드가 생성되는데, 당연히 이 과정에는 특별히 언급할 만한 error나 warning은 없다. 그냥 중요한 옵션 몇 가지만 집고 넘어가도록 하겠다.

(*) -O, -O2, -O3 등의 옵션
이 옵션은 컴파일러 최적화를 수행하라는 옵션이다. -O 뒤의 숫자가 올라갈수록 더욱 많은 종류의 최적화를 수행하게 된다. 최적화를 수행하면 당연히 코드 사이즈도 줄어 들고 속도도 빨라지게 된다. 대신 컴파일 수행 시간은 길어진다. 그리고 linux kernel을 위해 언급하고 싶은 것은 inline 함수들은 이 옵션을 주어야 제대로 inline 된다는 것이다.

(*) -g 옵션
이 옵션은 소스 레벨 debugger인 gdb를 사용하기 위해 debugging 정보(파일명, line number, 변수와 함수 이름들과 type 등)를 assembly code와 같이 생성하라는 옵션이다. 당연히 gdb를 이용하고 싶으면 주어야 한다. -g 옵션을 주지 않고 컴파일한 프로그램을 gdb로 디버깅하면 C 소스 레벨이 아닌 assembly 레벨 디버깅이 된다. 즉 C 소스 코드에 존재하는 변수 이름, line number 등이 없는 상황에서 디버깅을 해야 한다. 또한 -g 옵션을 -O 옵션과 같이 사용할 수도 있다. 단 그런 경우 최적화 결과, C 소스 코드에 존재하는 심볼(symbol; 쉽게 말해 함수와 변수 이름)중에 없어지는 것들이 발생한다.

(*) 여기서 잠깐
identifier 와 symbol이 모두 “쉽게 말해 함수와 변수 이름”이라고 했는데 어떻게 차이가 날까? 엄밀히 말하면 차이가 조금 있다. symbol이 바로 “쉽게 말해 함수와 변수 이름”이며 각 symbol은 특정 type과 연계되어 있다. 하지만 identifier는 그냥 “이름” 또는 “인식어”일 뿐이다. 예를 들어 struct point { int x; int y; };라는 것이 있을 때 point는 symbol은 아니지만 identifier이다. 보통 identifier라는 말은 parsing에서만 쓰인다는 정도만 알아두면 된다.

(*) -p 옵션과 -pg 옵션
profiling 을 아는가? 수행시간이 매우 중요한 프로그램(real time 프로그램이라고 해도 무방할 듯)을 작성할 때는 프로그램의 수행 시간을 함수 단위로 알아야 할 필요가 있는 경우가 많다. 프로그램의 수행 시간을 함수 단위나 더 작은 단위로 알아보는 과정을 profiling이라고 하는데, profiling은 프로그램 최적화에 있어서 중요한 기능을 담당한다. 대부분의 개발 툴이 지원하고 Visual C++에도 존재한다. 옛날 turbo C에는 있었나? 아무튼 gcc도 역시 profiling을 지원한다. -p 옵션 또는 -pg 옵션을 주면 프로그램의 수행 결과를 특정 파일에 저장하는 코드를 생성해 주게 된다. 그 특정 파일을 적당한 툴(prof또는 gprof 등)로 분석하면 profiling 결과를 알 수 있게 해 준다. 당연히 linux kernel 등에서는 사용할 수 없다.(이유는 특정 파일에 저장이 안되므로…) 초보자들은 이런 옵션도 존재하고 profiling을 할 수 있다는 정도만 알아 두면 좋을 듯 싶다. 나중에 필요하면 좀 더 공부해서 사용하면 된다.

(*) 기타 옵션(-m과 -f시리즈)
중 요한 옵션들이기는 하지만 초보자가 알아둘 필요가 없는 옵션 중에 f또는 m으로 시작하는 옵션들이 있다. f로 시작되는 옵션은 여러 가지 최적화와 assembly 코드 생성에 영향을 주는 architecture independent한 옵션이다.(assembly 코드 생성이 architecture dependent 이므로 정확히 말하면 f로 시작되는 옵션이 architecture independent라고 할 수는 없다.) m으로 시작되는 옵션은 보통 architecture dependent 하며 주로 CPU의 종류를 결정하는 옵션으로 assembly 코드 생성에 영향을 주게 된다. 하지만 대부분은 초보자는 그런 것이 있다는 정도만 알아두면 되고 특별히 신경 쓸 필요는 없다고 생각된다. m으로 시작되는 옵션 중에 -msoft-float옵션이 있다.(물론 특정 architecture에만 존재하는 옵션이다.) -msoft-float 옵션은 CPU에 FPU(floating point unit)가 없고, kernel에서 floating-point emulation을 해 주지 않을 때 C 소스 코드 상에 있는 모든 floating-point 연산을 특정 함수 호출로 대신 처리하도록 assembly 코드를 생성하라고 지시하는 옵션이다. 이 옵션을 주고 라이브러리를 linking시키면 FPU가 없는 CPU에서도 floating 연산을 할 수 있다.(대신 엄청 느리다. 어찌보면 kernel floating-point emulation보다는 빠를 것 같은데 확실하지는 않다.)

=== Assemble 과정
Assemble 과정은 앞선 과정과 동일하게 gcc라는 frontend가 as라는 실행 파일을 호출하여 수행된다. 그런데 as는 cpp와 cc1과는 달리 gcc 패키지 안에 존재하는 것이 아니라 별도의 binutils라고 하는 패키지에 존재한다. binutils 패키지 안에는 as를 비롯해 linking을 수행하는 ld, library 파일을 만드는 ar, object 파일을 보거나 복사할 수 있는 objdump, objcopy 등 여러 가지 툴이 들어 있다.

이제 Assemble 과정이 하는 일을 알아보도록 하자.

== Assemble 과정이 하는 일
 (1) 입력 : Assembly 소스 코드
 (2) 출력 : relocatable object 코드
 (3) 하는 일 : assemble(너무 간단한가?)

입 력은 당연히 C 언어 컴파일 과정을 거치면 나오는 Assembly 소스 코드이다. Assemble 과정을 거치면 소위 기계어(machine language)라는 결과가 relocatable object 형식으로 나온다. “relocatable”이라는 말이 어려우면 그냥 object 코드라고 해 두자. 이제 직접 수행해자. shell의 command line에 다음과 같이 입력하면 된다.
$ gcc -c hello.c
결 과는 hello.o라고 하는 파일이 나온다. hello.o는 binary형식의 파일이니깐 editor로 열어봐야 정보를 얻기 힘들다. 당연히 위의 예는 assemble 과정만 수행한 것이 아니라 C preprocessing 과정, C 언어 컴파일 과정, Assemble 과정을 수행했다. Assemble 과정만 수행하고 싶으면 다음과 같이 입력하면 된다.
$ gcc -c hello.s
역시 hello.o가 생긴다. hello.s는 C 언어 컴파일 과정에서 -S 옵션으로 만들었던 그 파일이다. 별로 관심이 안 생기면 as를 직접 수행할 수도 있다. 다음과 같다.
$ as -o hello.o hello.s
역시 hello.o가 생긴다.

(*) -c 옵션
많 이 쓰는 옵션이다. Assemble 과정까지의 과정만 수행하고 linking 과정을 수행하지 말라는 옵션이다. 여러 개의 C 소스 파일로 이루어진 프로그램을 컴파일 할 때 모든 소스 파일을 assemble 과정까지 수행하고 맨 마지막에 linking한다. 보통은 Makefile을 많이 이용하는데 그 때 많이 쓰이는 옵션이다.

Assemble 과정에서는 더 이상 기억해야 하는 옵션도 없고 이게 끝이다. C 언어 컴파일 과정에서 말한 바대로 C 언어 컴파일 과정이 끝난 C 소스 파일은 문법적으로 완전하다고 볼 수 있으므로 assemble 과정에서 Error나 Warning 나는 경우는 없다. 만약 Error나 Warning이 나는 경우가 있다면 gcc의 inline assemble을 이용했을 때, 그 inline assemble 소스 코드에 있는 문제 때문에 생길 수 있다. 안타깝지만 error나 warning 메시지가 나온 다면 C 소스 파일과 line number 정보는 없다. 잘 알아서 처리하는 수 밖에 다른 방법은 없는 것 같다. inline assemble 같은 것을 사용하지 않았는데도 error나 warning이 난다면 gcc의 버그라고 생각해도 무방하다.

== relocatable object 코드 파일 내용
어 떤 정보가 object 파일 안에 들어있을까? 당연히 code와 data가 들어 있다. C 컴파일 과정에서 C 언어 함수 안에 있는 내용들이 assembly mnemonic 들로 바뀌었고 그것이 assemble되어 기계어(machine language)가 되었을 것이다. 그 부분이 code를 이룬다. C 언어 소스 코드에 있는 나머지는 전역 변수(external variable)와 정적 변수(static variable)들이 data를 이룰 것이다. 또한 문자열 상수를 비롯한 상수도 data에 들어 있다. 또한 프로그램 수행에 쓰이지는 않고 단순한 정보로서 들어 있는 data들도 있다. 예를 들어 -g 옵션을 주고 컴파일 하면 프로그램의 디버깅 정보(변수, 함수 이름, C 소스 파일이름, line number 등)가 data에 속한다고 볼 수 있다. 그런데 code와 data가 무질서하게 섞여 있는 것은 아니고 section이라고 불리우는 단위로 서로 구분되어 저장되어 있다. Code는 text section에 들어 있고, data는 성격에 따라 data section, bss section, rodata section 등에 나누어져 저장되어 있다.(text, data, bss, rodata 등의 section 이름은 그냥 관습적인 것이다.) 아무튼 section 이야기는 이 정도만 우선 알아두면 될 듯 싶다.

== Symbol 이야기
relocatable object code안에 code와 data가 들어 있다고 했는데, 아주 중요한 것을 빠뜨렸다. 이 이야기는 linking 과정을 이해하기 위해 꼭 필요한 부분이므로 반드시 읽어야 할 것이다. 우선 Symbol이 무엇인지 알 것이다. C 언어 컴파일 과정에서 identifier와 함께 설명했는데 잠시 다시 말씀하자면 Symbol은 함수와 변수 이름이다. 변수 중에 특히 관심두어야 할 것 들은 자동 변수(?,auto variable)들이 아닌 전역 변수(external variable)와 정적 변수(static variable)이다. 자동 변수는 함수의 stack frame에 존재하는 변수이기 때문에 현재 stack pointer(sp, 보통의 CPU의 register중에 하나)에 대한 offset으로 표현된다. 즉 현재 함수에서 자동 변수(auto variable)를 access(read/write)하고 싶으면 sp+상수의 어드레스를 access하면 된다. 하지만 전역 변수와 정적 변수는 그냥 32bit(32bit CPU기준) 어드레스를 읽어야 한다. stack pointer랑은 전혀 관계 없다. 아무튼 여기서 관심을 두는 Symbol은 함수, 전역 변수와 정적 변수의 이름이라고 할 수 있다.
이제 생각해 볼 것은 C 언어 소스 파일을 C preprocessing, C 언어 컴파일, assemble 과정을 거치면 완전한 기계어로 바꿀 수 있느냐 하는 점이다. 완전히 기계어로 바꿀 수 있을까? C 언어 소스 파일 하나로 이루어지는 프로그램이라면 완전히 기계어로 바꾸는 것이 가능하겠지만 일반적으로는 불가능 하다. 다음과 같은 예제를 살펴보자.
int func3(void); /* func3 선언 */
extern int mydata; /* mydata 선언 */

int func2(void) /* func2 정의 */
{
….
}

int func1(void) /* func1 정의 */
{
int i;
…..
func2();
…..
func3();
….
i= mydata+3;
…..
}
-- end of test1.c
-- start of test2.c
int mydata = 3; /* mydata 정의 */
int func3(void) /* func3 정의 */
{
…..
}

위 의 예제를 컴파일 한다고 생각해보자. test1.c에서 func1()의 내용을 기계어로 바꾸고 싶은데 func2()를 호출하는 시점에서는 별로 문제가 안된다. func2()는 같은 소스 코드 내에 존재하고 func2()를 호출하는 instruction과 func2()의 실제 위치(어드레스)의 차이를 계산해 낼 수 있으므로 상대 어드레스를 이용하는 함수 호출 instruction으로 완전히 기계어로 바꿀 수 있다. 그런데 문제는 func3()를 호출할 때는 func3()의 실제 위치(address)를 계산할 수 없다는 문제점이 있다. 당연히 동일한 파일에 존재하는 함수가 아니므로 그 함수가 존재하게 될 어드레스를 계산할 수 없다. 어드레스를 모르는데 함수 호출 instruction을 완전히 만들 수 있을까? 만들 수 없다. 당연히 전역 변수 mydata를 access하는 부분도 마찬가지로 mydata의 어드레스를 모르므로 완전히 instruction으로 바꿀 수 없다. 그럼 어떻게 해야 될까?
그때 assembler는 그냥 함수 어드레스 없는 함수 호출 instruction을 기계어로 바꾸어 놓는다. 그런 다음에 그 instruction에 “func3()를 호출한다”라는 표지를 붙여 놓는다. 그럼 그 후의 과정(linking)에서 func3()의 address를 계산했을 때 그 빈 공간을 채워 넣게 된다. mydata와 같은 전역 변수도 마찬가지로 동작한다. test1.c을 컴파일할 때는 “func3()”, “mydata” 라는 표지를 사용해야 한다. 그럼 test2.c를 컴파일 할 때는 무엇이 필요할까? 상식적으로 생각하면 “func3()”, “mydata”가 여기 있다라는 정보를 가지고 있어야한다.
정리하면 object 파일 안에는 그 object 파일에 들어있는 symbol들(test1.o에서는 func1과 func2, test2.o에서는 func3와 mydata)에 대한 정보가 들어있고, 그 object 파일이 reference하고 있는 symbol들(test1.o에서 func3와 mydata 사용)에 대한 정보가 들어 있다.

== Relocatable의 의미
위 에서 object 코드라고 하지 않고 relocatable object 코드라고 지칭했는데 relocatable이 뜻하는 것을 잠시 집고 넘어 가자. Relocatable을 사전에서 찾아보면 “재배치가 가능한” 정도의 뜻이다. “재배치가 가능한” 이라는 의미는 상당히 모호하다. 좀 더 구체적으로 말하자면 위에서 설명된 symbol들의 절대 어드레스가 정해지지 않았다는 뜻이다. 즉 test1.c의 func1()이 절대 어드레스 0x80000000에 존재해야 한다라고 정해지지 않고 어떤 절대 어드레스에 존재해도 관계 없다는 뜻이다. 그런데 이 말과 헷갈리는 말이 한가지 더 있는데 그것은 position independent code이다. C 언어 컴파일 과정에서 설명한 옵션중에 -f 시리즈가 있었다. 그 중에 -fpic라는 position independent code를 만들라고 강제하는 옵션이 있다. position independent code도 역시 절대 어드레스상에 어느 위치에 있어도 무방한 code를 지칭한다. 하지만 두 가지는 분명 차이가 있는데, 그냥 넘어가기로 하자. 쉽게 relocatable은 절대 어드레스가 결정되지 않았다는 뜻, 그러나 position independent code와는 다른 말이다.

=== Linking 과정
Linking 과정은 ld라고 하는 실행파일이 담당하고 있다. Assemble을 담당하는 as와 마찬가지로 binutils 패키지의 일부분이다. 보통 어플리케이션을 컴파일하는 경우에는 gcc(실행파일)를 이용하여 ld를 호출하나, 특별한 경우에 있어서는 ld를 직접 수행하여 linking을 하는 경우가 종종 있다.

== Linking 과정이 하는 일
 (1) 입력 : 하나 이상의 relocatable object 코드 와 library
 (2) 출력 : 실행파일(executable) 또는 relocatable object 코드
 (3) 하는 일 : symbol reference resolving & location
Linking 과정은 하나 또는 그 이상의 object 파일과 그에 따른 library를 입력으로 받는다. 출력은 보통의 경우는 실행파일(executable file)이지만, 경우에 따라서 object 파일을 생성하게 할 수도 있다. 여러 개의 object 파일을 합쳐서 하나의 object 파일로 만드는 과정을 partial linking이라고 부르기도 한다. Linking 과정이 하는 일은 symbol reference resolving하고 location이라고 했는데, 저도 정확한 단어를 적은 것인지 의심스럽다. 정확한 용어를 사용한다면 좋겠지만 그렇지 못하더라도 내용을 정확히 이해하는 것이 중요하니깐 내용에 대해서 살펴보도록 하겠다.

== symbol reference resolving
어 떤 C 소스 파일에서 다른 파일에 있는 함수와 전역 변수(symbol)에 대한 참조(reference)를 하고 있다면 assemble 과정에서 완전한 기계어로 바꿀 수 없다.(실제로는 같은 소스 파일에 있는 전역 변수를 참조하는 것도 보통의 경우, 완전한 기계어로 바꿀 수 없다.) 그 이유는 당연히 assemble 까지의 과정은 단일 파일에 대해서만 진행되고, 다른 파일에 있는 해당 함수와 전역 변수의 address가 상대적이든 절대적이든 결정될 수 없기 때문이다. 따라서 완전히 기계어로 바꿀 수 없는 부분은 그대로 “공란”으로 남겨두고 표시만 해 두게 된다.
Linking 과정에서 그 “공란”을 채워 넣게 된다. 그 과정을 보통 “resolve한다”라고 말한다. 어떻게 할까? 당연히 실행 파일을 이루는 모든 object 파일을 입력으로 받기 때문에 object 파일들을 차곡 차곡 쌓아 나가면(아래 location 참조) object 파일 안에 있는 모든 symbol(함수나 전역 변수 이름)의 address를 상대적이든 절대적이든 계산할 수 있다. 이제 각 symbol의 address가 계산되었으므로 표시가 남아 있는 “공란”에 해당하는 symbol의 address를 잘 넣어주면 된다.
linking 과정에서 나올 수 있는 에러는 대부분 여기에서 발생한다. 표시가 남아 있는 “공란”을 채울 수 없는 경우가 있다. 크게 두 가지로 나누어지는데, 우선 reference하고 있는 symbol을 찾을 수 없는 경우와 reference하고 있는 symbol의 정의가 여러 군데에 있는 경우이다.

>> object파일명: In function ‘func’:
>> object파일명: undefined reference to ‘symbolname’
위 의 에러 메시지는 함수 func 안에서 사용되고 있는 symbolname이란 이름의 symbol이 어디에도 정의되지 않아서 “공란”을 채울 수 없다는 뜻이다. 당연히 symbolname을 잘못 입력하였던지 아니면 그 symbol이 속해있는 object 파일이나 library와 linking되지 않았기 때문이다.

>> object파일명1: multiple definition of ‘symbolname’
>> object파일명2: first defined here
위 의 에러 메시지는 symbolname이란 이름의 symbol이 여러 번 정의되고 있다는 뜻이다. object파일1에서 정의가 있는데 이미 object파일2에서 정의된 symbol이므로 그 symbol을 reference하고 있는 곳에서 정확하게 “공란”을 채울 수 없다는 뜻이다. 당연히 두 symbol중에 하나는 없애거나 static으로 바꾸거나 해야 해결될 것이다.

== location(용어 정확하지 않을 수 있음)
이 전 까지 object 코드를 모두 relocatable이라고 표현했다. 아직 절대 address가 결정되지 않았다는 의미로 사용된다.(position independent code와는 다른 의미) object 코드의 절대 address를 결정하는 과정이 “location”이다. Symbol reference resolving 과정에서 입력으로 받은 모든 object 파일들을 차곡 차곡 쌓아 나간다고 했다. 그런데 object 파일이 무슨 벽돌도 아닌데 차곡 차곡 쌓는 다는 것이 말이 되나? 여기서 쌓는 다는 말을 이해하기 위해서 다음과 같은 그림(?)을 살펴 보도록 하자.

많은 object code들
----------------- address(0xAAAAAAAA+0x5000)
test2.o(size 0x3000)
----------------- address(0xAAAAAAAA+0x2000)
test1.o(size 0x2000)
----------------- address(0xAAAAAAAA)

절 대 address 0xAAAAAAAA에 test1.o의 내용을 가져다 놓는다. test1.o의 크기(파일 크기와는 의미가 조금 다르지만 그냥 무시하고 파일 크기라고 생각하기 바람)가 0x2000이므로 다음에 test2.o를 쌓을 수 있는 address는 0xAAAAAAAA+0x2000가 된다. 그곳에 다시 test2.o를 쌓고 또 test2.o의 크기를 보고 새로운 address 계산하고 또 object 코드 쌓고, 계속 반복이다. 이렇게 쌓을 때 초기 절대 address 0xAAAAAAAA가 무슨 값을 가지게 되면 모든 object 파일에 있는 symbol의 절대 address도 계산해 나갈 수 있을 것이다. 그걸로 symbol reference를 resolve하게 된다. 그 초기 절대 address 0xAAAAAAAA의 값을 정하는 것을 location이라고 한다. 그럼 왜 절대 address를 결정해야 할까? 꼭 그래야 할 필요는 없지만 CPU의 instruction이 대부분의 경우 절대 address를 필요로 하는 경우가 많기 때문이라고 할 수 있다.
(주의) object 를 쌓는 것은 위의 예처럼 단순하지는 않다. 실제로는 object 전체를 쌓지 않고 object안에 있는 section별로 쌓게 된다.

그럼 이제 직접 수행해 보자.
$ gcc -o hello hello.o
object 파일이 하나라서 너무 단순하다고 생각하는가? 물론 hello.o 하나만 command line에 나타나지만 실제로는 조금 많은 object 파일이 linking되고 있다. (아래에서 좀더 자세하게 설명한다.) 지겹지만 hello를 실행해 보라. 제대로 동작하는가? 제대로 동작한다면 그 사이 어떤 일이 벌어졌을까? 그 사이에 벌어진 일을 간단히 적어보면 다음과 같다. shell이 fork() 시스템콜을 호출하고 자식 process는 exec() 시스템콜을 통해 hello라는 파일 이름을 kernel에 넘긴다. kernel에서는 hello파일을 보고 linking할 때 location된 address(여기서는 absolute virtual address 이다.)상의 메모리로 hello 파일을 복사하고 PC(program counter)값을 바꾸면 수행되기 시작한다.
(주의) 실제로 위의 hello가 수행되는 과정은 많은 생략과 누락이 있다. 실제로는 hello 파일을 완전히 메모리로 복사하는 것도 아니고, dynamic linking & loading 등의 개념이 완전히 빠져 있지만 그냥 이해하기 쉽게 하기 위해서 간단하게 적어 본 것이다.

= library
hello.o 를 linking하여 hello라고 하는 실행파일을 만드는데 command line에서는 아무것도 없지만 library가 같이 linking되고 있다. 그것은 지극히 당연하다. hello.c의 main함수에서 printf함수를 호출(linking이니깐 참조 혹은 reference라고 해야 좋겠다.)하고 있는데 printf함수 자체는 소스 중에 그 어디에도 없다.(물론 stdio.h에 printf함수의 선언은 있습니다만 정의는 어디에도 없다.) 잘 알다시피 printf함수는 C standard library 안에 있는 함수이다. C standard library가 같이 linking되었기 때문에 제대로 동작하는 hello 실행파일이 생긴 것이다.
library라는 것은 아주 간단한 것이다. relocatable object 파일들을 모아 놓은 파일이다. 소스로 제공할 수도 있으나, 그러면 매번 cpp, c 컴파일, assemble 과정을 거쳐야 하므로 컴파일 시간이 매우 증가하게 된다. 그래서 그냥 relocatable object 파일로 제공하는 것이 컴파일 시간 단축을 위해서 좋다. 그런데 필요한 relocatable object 파일이 너무 많으면 귀찮으니까 그것을 묶어서 저장해 놓은 녀석이 바로 library라고 할 수 있다.
Linux를 비롯한 unix 계열에서는 대부분의 library 파일의 이름이 lib로 시작된다. 확장자는 두 가지가 있는데, 하나는 .a이고 또 하나는 .so입니다.(뒤에 library 버전 번호가 붙는 경우가 많다.) .a로 끝나는 library를 보통 archive형식의 library라고 말하며 .so로 끝나는 library를 보통 shared object라고 부른다. /lib 디렉토리와 /usr/lib 디렉토리에 가면 많이 볼 수 있다.
archive library 안에 있는 symbol를 reference하게 되면 library중에 해당 부분(object 파일 단위)을 실행 파일 안에 포함시켜 linking을 수행한다. 즉, 해당 object 파일을 가지고 linking을 수행하는 것과 동일한 결과를 가진다. 보통 이런 linking을 static linking이라고 부른다.
그 런데 시스템 전체에 현재 수행되고 있는 실행파일(실행파일이 수행되고 있는 하나의 단위를 process라고 한다.)들에서 printf함수를 사용하고 있는 녀석들이 매우 많으므로 그것이 모두 실행 파일에 포함되어 있다면 그것은 심각한 메모리 낭비를 가져온다는 문제점을 가지고 있다. 그래서 생각해 낸 것이 dynamic linking이라는 개념이다. 예를 들어 실행파일이 printf함수를 사용한다면 실행파일이 메모리로 loading될 때 printf가 포함되어 있는 library가 메모리 상에 있는 지 검사를 해 보고 있으면 reference resolving만 수행하고, 아니라면 새로 loading과 reference resolving을 하게 된다. 그렇게 되면 printf가 포함되어 있는 library는 메모리 상에 딱 하나만 loading되면 되고 메모리 낭비를 막을 수 있다. 그런 일을 할 수 있도록 도입된 것이 shared object 이다. MS Windows쪽의 프로그래밍을 하시는 사람이라면 DLL과 동일한 개념이라고 보면 된다.
그런 shared object를 이용하여 dynamic linking을 하면 실행파일의 크기가 줄어든다. 반면에 당연히 실행파일이 메모리에 loading될 때는 reference resolving을 위해서 CPU의 연산력을 사용한다. 하지만 MS Windows의 DLL과는 달리 shared object 파일과 static linking을 할 수도 있다.(반대로 archive library를 이용하여 dynamic linking을 수행할 수는 없다.)

(*) -static 옵션
dynamic linking을 지원하고 있는 시스템에서 dynamic linking을 수행하지 않고 static linking을 수행하라는 옵션이다. dynamic linking을 지원하고 있는 시스템에서는 dynamic linking이 default 이다.

직접 수행해 보자.
$ gcc -o hello_static -static hello.o
실행파일 hello, hello_static 을 수행하면 결과는 똑같다. 파일의 크기를 비교해 보면 차이가 난다는 것을 알 수 있을 것이다.

/lib, /usr/lib에는 엄청 많은 library 파일들이 존재한다. 그럼 linker가 찾아야 하는 symbol을 모든 library 파일에 대해서 검사를 하는 것일까? CPU하고 HDD가 워낙 빠르면 그래도 무방하겠지만, 그렇게 하지 않는다.(“사용자가 쉽게 할 수 있는 일을 컴퓨터에게 시키지 말라.”라는 컴퓨터 사용 원칙이다.) 우선 gcc는 기본적인 library만 같이 linking을 하게 되어 있다. 나머지 library는 사용자의 요구가 있을 때만 같이 linking을 시도하도록 되어 있다. 그럼 기본적인 library가 무엇인지 알아야 하고 gcc에게 사용자의 요구를 전달할 옵션을 있어야 할 것이다. 기본적인 library는 당연히 C standard library 이다. C standard library의 이름은 libc.a또는 libc.so 이다. 최근의 linux 머신은 /lib/libc.so.6 이라는 파일을 찾아 볼 수 있을 것이다 (symbolic link되어 있는 파일이다). 그리고 libgcc라고 하는 것이 있는데… 생략하고. 이제 옵션을 알아보자.

(*) -nostdlib 옵션
이 름에서 의미하는 바대로 standard library를 사용하지 말고 linking을 수행하라는 뜻이다. 실제로는 standard library 뿐 아니라 startup file이란 녀석도 포함하지 않고 linking이 수행된다. startup file에 대해서는 좀 있다가 알아보도록 하겠다.

(*) -l라이브러리이름 옵션
특 정 이름의 library를 포함하여 linking을 수행하라는 뜻이다. 예를 들어 -lmyarchive라고 하면 libmyarchive.a(또는 libmyarchive.so)라는 library파일과 같이 linking을 수행하는 것이다. library 파일 이름은 기본적으로 lib로 시작하니깐 그것을 빼고 지정하도록 되어 있다.

library에 대해서 또 하나의 옵션을 알아야 할 필요가 있다. 다름 아닌 “어느 디렉토리에서 library를 찾는가”이다. 모든 library가 /lib와 /usr/lib에 있으라는 보장이 없다. 그 디렉토리를 정하는 방법은 두 가지 인데 LD_LIBRARY_PATH라고 하는 이름의 환경 변수를 셋팅하는 방법이 있고 또 한 가지는 gcc의 옵션으로 넘겨 주는 방법이 있다.

(*) -Ldir 옵션
library 파일을 찾는 디렉토리에 “dir”이란 디렉토리를 추가하라는 옵션이다.(-Idir 옵션처럼 -L과 dir을 붙여서 적습니다.) 예를 들어 -L/usr/local/mylib 라고 하면 /usr/local/mylib라는 디렉토리에서 library 파일을 찾을 수 있게 된다.

== entry 이야기
application 을 작성하고 compile, linking 과정이 지나면 실행 파일이 만들어진다. 그리고 그 실행 파일이 수행될 때는 메모리로 load되어 수행이 시작된다는 사실을 알고 있다. 여기서 한가지 의문이 생기는데, “과연 코드의 어떤 부분에서 수행이 시작되는가?”이다. 답이 너무 뻔한가? main함수부터 수행된다고 답할 것인가? 다소 충격적이겠지만 “땡”이다. main함수부터 수행되지 않고 그전에 수행되는 코드가 존재한다. 그 먼저 수행되는 코드에서 하는 일은 여러 가지가 있는데 그냥 건너 뛰도록 하겠다. 아무튼 그 코드에서 main함수를 호출해 주고 main함수가 return하면 exit 시스템호출을 불러 준다. 그래서 main이 맨 처음 수행되는 것처럼 보이고 main이 return하면 프로그램 수행이 종료되는 것이다. 그럼 그 코드는 어디 있을까? 시스템에 따라서 다르겠지만 일반적으로 /lib혹은 /usr/lib 디렉토리에 crt1.o라는 이름의 object 파일이 있는데 그 object 파일 안에 있는 _start라는 이름의 함수(?)가 맨 먼저 수행되는 녀석이다. 결국 보통 application의 entry는 _start함수가 된다.
그럼 crt1.o object 파일 역시 같이 linking 되어야 한다. gcc를 이용해 linking을 수행할 때 command line에 아무 이야기를 해주지 않아도 자동으로 crt1.o 파일이 함께 linking 된다. 실제로는 crt1.o 뿐 아니라 비슷한 crt*.o 파일들도 같이 linking 된다. 그렇게 같이 linking 되고 있는 object파일들을 startup file이라고 부르는 것 같다.(-nostdlib 옵션 설명할 때 잠시 나왔던 startup file이 바로 이 녀석들이다.) 그럼 ld는 _start파일이 entry인지 어떻게 알고, 다른 이름의 함수를 entry로 할 수는 없는것일까? 그것에 대한 해답은 아래 linking script부분에서 해결될 것이다.

== 실행 파일에 남아 있는 정보
linking 의 결과 실행파일이 생겼는데, 보통 linux에서는 실행파일 형식이 ELF라는 포멧을 가진다.(linux 시스템에 따라 다를 수 있다.) ELF는 Executable and Linkable Format의 약자이다. 보통 linux 시스템에서의 relocatable object 파일의 형식도 ELF이다. 실제로 실행파일과 relocatable object 파일과는 조금 다른 형식을 가진다. 암튼 그건 상식으로 알아두고, 그럼 실행파일에 있는 정보는 무엇일까?
이제까지의 알아낸 정보들을 모두 종합하면 알 수 있다. 우선 실행 파일이라는 녀석이 결국은 relocatable object 를 여러 개 쌓아놓은 녀석이므로 원래 relocatable object 파일이 가지고 있던 code와 data 정보는 모두 남아있을 것이다. 그리고 entry를 나타내는 address가 있어야 수행을 할 수 있을 것이다. 또, dynamic linking을 했을 경우 관련된 shared object 정보도 남아있어야 한다.
실행 파일 속에 남아있는 data는 relocatable object에 있는 data처럼 프로그램 수행에 필요한 data가 있고 그냥 실행 파일을 설명하는 정보로서의 data가 있다. 예를 들어 -g 옵션을 주고 컴파일한 실행파일에서 디버깅 정보들은 실행과는 전혀 관계 없다. 따라서 그러한 정보들은 실행 파일 수행시에 메모리에 load될 필요도 없다.(load하면 메모리 낭비니깐) 실행 파일 속에 남아있는 code와 data는 relocatable object 처럼 특별한 단위로 저장되어 있다. ELF 표준에서는 segment라고 부르는데 보통의 경우는 object 파일처럼 section이라는 말이 쓰인다. reloctable object 파일과 마찬가지로 code는 text section에 저장되고 프로그램 수행 중에 필요한 data가 성격에 따라 나누어져 data, rodata, bss section이란 이름으로 저장되어 있다. 그 section단위로 메모리로 load될 필요가 있는지에 대한 flag정보가 있고 각 section이 load될 address(location과정에서 정해진다.)가 적혀 있어야 정확하게 loading을 할 수 있다.
기타로 symbol reference resolving이 끝났는데도 ELF형식의 실행파일은 보통의 경우 많은 symbol 정보를 그냥 가지고 있는 경우가 있다. symbol 정보 역시 수행에는 하등 관계가 없으므로 없애도 되는데, strip이라고 하는 binutils안에 있는 tool로 없앨 수 있다.

== linking script
흠 이제 좀 어려운 이야기를 할 차례이다. Location과정에서 어떤 절대 address를 기준으로 각 section들을 쌓는지, 그리고 entry는 어떤 symbol인지에 대한 정보를 linker에게 알려줄 필요가 있다. 보통 application의 경우는 시스템 마다 표준(?, 예를 들어 entry는 _start 다 하는 식)이 있는지라 별로 문제될 것은 없는데, bootloader나 kernel을 만들 때는 그런 정보를 사용자가 넘겨 주어야 할 필요가 있다. 그런 것들을 ld의 command line argument로 넘길 수도 있지만 보통의 경우는 linking script라고 하는 텍스트 형식의 파일 안에 저장하여 그 script를 참조하라고 알려준다. (아무래도 command line argument로 넘겨 줄 수 있는 정보가 한계가 있기 때문이라고 생각이 든다. location과 entry에 관한 내용 중에 ld의 command line argument로 줄 수 있는 옵션이 몇가지 있으나 한계가 있다.) ld의 옵션 -T으로 linking script 파일 이름을 넘겨 주게 된다.(gcc의 옵션 아님) linux kernel source를 가지고 있는 사람은 arch/*/*.lds 파일을 한번 열어 보길 바란다. 그게 linking script고, 초기 절대 address 하고 section 별로 어떻게 쌓으라는 지시어와 entry, 실행 파일의 형식 등을 적어 놓은 내용이 보일 것이다. 물론 한 줄 한 줄 해석이 된다면 이런 글을 읽을 필요가 없다. 그 script를 한 줄 한 줄 정확히 해석해 내려면 GNU ld manual 등을 읽어야 할 것이다.

== linux의 insmod
linux kernel을 구성하고 device driver 등은 linux kernel module(이하 module) 형식으로 run-time에 올릴 수 있다는 것을 알고 있을 것이다. module을 run-time에 kernel에 넣기 위해서 사용하는 명령어가 insmod이다.(modprobe도 가능) 이 module 이라는 것이 만들어 지는 과정을 잘 살펴 보면 gcc의 옵션중에 -c옵션으로 컴파일만 한다는 것을 알 수 있다. 확장자는 .o를 사용한다. relocatable object 파일이고, ELF형식이다. 그럼 이 module이 linux kernel과 어떻게 합쳐질까? 당연히 linking 과정을 거쳐야 된다. 일종의 run-time linking 이다. 당연히 module은 kernel내의 많은 함수와 전역 변수를 참조한다. 그렇지 않다면 그 module은 linux kernel의 동작과는 전혀 관계 없는 의미 없는 module이 될것이다. 그럼 참조되고 있는 symbol을 resolving하기 위해서는 symbol의 절대 address를 알아야한다 . 그 내용은 linux kernel 내부에 table로 존재한다. /proc/ksyms라고 하는 파일을 cat 해보면 절대 address와 symbol 이름을 살펴볼 수 있을 것이다. 살펴보면 알겠지만 생각보다 적은 양이다. 적은 이유는 그 table이 linux kernel source에 있는 전역 symbol의 전부를 포함한 것이 아니라 kernel source 내부나 module 내부에서 EXPORT_SYMBOL()과 같은 특별한 방법으로 선언된(?, 이 선언은 C 언어 문법의 declaration과는 다르다.) symbol들만 포함하기 때문이다. 다른 전역 symbol 들은 module 프로그래밍에 별 필요가 없다고 생각되어 지는 녀석들이기 때문에 빠진 것이다. 따라서 EXPORT_SYMBOL()등으로 선언된 symbol들만 사용하여 module을 작성해야 한다. 당연히 linking 과정을 거치기 때문에 앞서 설명한 linking에서 발생할 수 있는 에러들이 발생할 수 있다. 제일 많이 발생할 수 있는 것은 역시 undefined reference 에러이다. gcc의 에러와는 조금 다른 메시지가 나오겠지만 결국은 같은 내용이다.

== map 파일
linking 과정을 끝내면 당연히 모든 symbol에 대한 절대 address가 정해지게 된다. 그 정보를 알면 프로그램 디버깅에 도움이 될 수도 있으니 알면 좋을 것이다. ld의 옵션중에 '-Map 파일이름'이라는 옵션이 있는데 우리가 원하는 정보를 문서 파일 형식으로 만들어 준다. 그 파일을 보통 map 파일이라고 부른다. symbol과 address 정보 말고 section에 대한 정보도 있고 많은 정보가 들어 있다.
linux kernel을 컴파일을 하고 나면 나오는 결과 중에 System.map이라는 파일이 있는데 이 녀석이 바로 ld가 만들어 준 map 파일의 내용 중에 symbol과 symbol의 절대 address가 적혀 있는 파일이다. linux kernel panic으로 특정 address에서 kernel이 죽었다는 메시지가 console에 나오면 이 System.map 파일을 열어서 어떤 함수에서 죽었는지 알아볼 수도 있다.

== 옵션 넘기기
gcc 의 이야기 맨 처음에 gcc는 단순히 frontend로 command line으로 받은 옵션을 각 단계를 담당하고 있는 tool로 적절한 처리를 하여 넘겨준다고 말했었다. 위에서 나온 ld의 옵션 -T와 -Map 과 같은 옵션은 gcc에는 대응하는 옵션이 존재하지 않는다. 이런 경우 직접 ld를 실행할 수도 있고 gcc에게 이런 옵션을 ld에게 넘겨 주라고 요청할 수 있다. 하지만 application을 컴파일할 때는 ld를 직접 실행하는 것은 조금 부담이 되므로, gcc에 옵션을 넘기라고 요청하는 방법이 조금 쉽다고 볼 수 있다. 그런 경우 사용되는 것이 -Wl 옵션인데 간단히 이용해 보도록 하자.
$ gcc -o hello -static -Wl,-Map,hello.map hello.c
그럼 hello.map이라는 매우 큰 문서 파일이 만들어진다. 한번 살펴 보도록 하자.(-static 옵션을 안 넣으면 살펴볼 내용이 별로 없을까봐 추가했다.)
실제로는 -Wl 옵션처럼 as에게도 옵션을 넘겨 줄 수 있는 -Wa와 같은 옵션이 있는데 쓰는 사람을 본 적이 없다

'Programming > Linux' 카테고리의 다른 글

Centos7 custom build (1)  (0) 2016.03.25
yum을 이용한 rpm 다운로드 방법  (0) 2016.03.25
V4L??  (0) 2008.06.26
Linux find grep 명령사용하기  (0) 2008.06.24
Fedora8사전에서 Microsoft를 검색하면...  (0) 2008.05.26
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


세계 양궁계 극심한 견제 속에서도 변함없는 챔피언

 

지금 한국은 세계 양궁계에서 양궁 발전을 저해하는 국가로 찍혀 있습니다. 그러다 보니 한국을 견제하기 위해 경기방식을 수시로 바꾸기도 합니다.

 

가장 난제는 장비입니다. 1990년대 중반까지 세계 양궁장비는 남자 선수의 경우 미제, 여자 선수의 경우 일제로 양분됐습니다. 그러다 96년 애틀랜타올림픽을 1년 앞두고 미국이 장난을 쳤습니다. ‘어떻게 저런 활을 만들 수 있을까’ 우리도 깜짝 놀랄 만큼 획기적인 신제품을 미국의 한 회사가 만들었는데, 미국이 그 회사가 한국에는 제품을 팔지 못하도록 제도적으로 막아버렸습니다. 우리 양궁협회에서 그 제품을 사기 위해 백방으로 노력했지만 결국 살 수 없었습니다. 심지어 미국 본사에까지 찾아가 올림픽에 쓸 활 4대만 달라고 통사정했지만 거절당했습니다.

 

결국 우리 선수들은 애틀랜타올림픽 단체전에서 접전 끝에 1점 차이로 미국에 금메달을 내줬습니다. 개인전도 미국 선수에게 져 동메달에 그쳤습니다. 여자개인전과 단체전에서 금메달 2개, 남자개인전에서 은메달 1개와 동메달 1개의 성적을 갖고 돌아왔습니다. 그러자 신문마다 ‘한국 양궁 빨간불!’ 이렇게 큰 글씨로 1면 톱기사가 실렸습니다. 그때 저희는 일주일간 정말 처참하게 당했습니다. 한국 양궁이 추락한 이유가 뭐냐, 지도자들이 세계적인 흐름도 읽지 못하고 우리 선수들만 구닥다리 활을 갖고 출전했더라, 훈련도 주먹구구식이다, 이미 실패가 예견됐다…. 그렇게 무능한 지도자로 매도당했습니다.

 

앞서 말씀드렸듯, 외국에서는 한국이 양궁 발전을 저해하는 나라입니다. 예선을 치르면 우리가 1, 2, 3, 4등을 싹쓸이합니다. 시드 배정에서 우리 선수들이 A, B, C, D조에 한 명씩 배정돼 총 128명이 예선을 치르면 마지막 4강전에 남는 게 또 우리 선수들입니다. 그래서 전 세계 시청자들이 채널을 돌려버린다는 겁니다. 양궁 저변 확대에 문제가 생긴다는 거죠.

 

이처럼 중요한 순간마다 다른 나라에서 장비로 장난치는 것을 극복하기 위해 생각해낸 게 바로 해발 8848m의 에베레스트 산입니다. 에베레스트를 인류 최초로 등정한 사람은 뉴질랜드의 힐러리 경(卿)입니다. 그는 1953년 정상 정복에 성공했습니다. 한국인 최초로 등정에 성공한 사람은 고(故) 고상돈 씨인데, 힐러리 경이 성공한 뒤 24년 만인 77년 세계에서 58번째로 정상 정복에 성공했습니다. 24년 동안 58번째라면 1년에 약 2명꼴로 성공했다는 얘기입니다. 그렇다면 2007년에는 에베레스트 등정에 성공한 사람이 몇 명이나 될까요?

 

답은 저도 모릅니다.(좌중 웃음) 세계산악연맹의 2004년 집계에 따르면, 1년간 무려 330명이 에베레스트 정상 등정에 성공했습니다. 2005~2006년부터는 집계 자체가 무의미하다는 이유로 아예 그 수를 세지 않고 있다고 합니다.

 

경기방식 변화 예견하고 새 훈련법 끊임없이 개발

 

그렇다면 과거 24년간 1년에 2명꼴에 불과하던 정상 정복자가 왜 이렇게 늘어났을까요? 첫 번째 답은 장비의 과학화입니다. 끊임없이 최첨단의 혁신 장비들이 개발돼 나왔기 때문입니다.

 

두 번째는 발상의 전환입니다. 힐러리 경이나 고상돈 씨가 에베레스트 등정을 시도하던 시절에는 누구나 예외 없이 해발 2000m 고지에 베이스캠프를 정했습니다. 거기서부터 정상까지 엄청난 고난을 극복해가며 등정을 시도했던 겁니다. 그러나 지금은 베이스캠프를 해발 6700m 지점에 만들고 모든 장비를 그곳에 갖다놓은 뒤 정상 정복을 시도합니다. 2000m만 올라갔다 오면 끝나는 겁니다. 그래서 그 어렵다는 에베레스트 등정을 수많은 사람들이 성공할 수 있었습니다.

 

우리 양궁도 산악인들의 이런 발상의 전환을 배워야겠다고 생각했습니다. 그래서 끊임없이 새로운 것을 개발하고자 노력하고 있습니다. 아무도 생각하지 못했던 훈련방법을 개발해 극비리에 시행하면, 외국 지도자들이 수단방법을 가리지 않고 알아내 벤치마킹합니다. 5개월쯤 지나면 우리보다 더 발전된 방법으로 훈련하고 있습니다. 그 5개월간 우리는 전보다 새로운 것을 개발해내야 합니다. 그렇지 못하면 정상에 서길 포기해야 합니다.

 

저는 양궁 지도자들에게 다음의 5가지를 강조합니다.

 

첫째는 최소한 10년 뒤의 미래를 내다보고 국내외 변화에 대비할 수 있는 통찰력입니다. 일례로 2004년 아테네올림픽 직후 양궁 지도자들에게 ‘2008년 베이징올림픽에선 경기방식이 어떻게 바뀔지’ 상상할 수 있는 모든 경우를 다 제출하라고 했습니다. 수백 가지 답이 나왔는데 그것들을 압축해보니 결국 네 가지 정도로 정리됐습니다.

 

양궁 대표선수단은 이 네 가지 경우의 수를 염두에 두고 훈련을 해왔습니다. 그런데 놀라지 마세요. 베이징올림픽 8개월을 앞둔 지난해 12월 세계양궁연맹에서 베이징올림픽 경기방식을 발표했는데, 우리가 예측했던 네 가지가 글자 하나 틀리지 않고 똑같았습니다. 외국선수들은 8개월 동안 바뀐 경기방식에 적응하기 위해 구슬땀을 흘려야 하지만, 우리는 4년 전부터 베이징올림픽의 경기방식을 준비해온 것입니다. 바로 이런 통찰력이 중요합니다.

 

둘째는 새로운 것을 창조해낼 수 있는 창의력입니다. 주변을 둘러보면 양궁 훈련과 관련된 좋은 소재들이 널려 있습니다. 아무도 생각하지 못했던 것들을 조금만 바꾸면 좋은 훈련 소재가 되는 겁니다.

 

우리가 반드시 따라 배우자고 하는 사람이 한 명 있습니다. 바로 두바이의 최고지도자 셰이크 모하메드입니다. 대한양궁협회 세미나가 열릴 때마다 우리는 늘 이 얘기로 시작합니다. 보세요, 지금 두바이가 어떻게 변했습니까? 전 세계 타워크레인의 25%가 두바이에 있다고 합니다. VVIP라는 신조어를 탄생시킨 7성급 호텔을 최초로 만들고, 버즈 두바이에 800m 초고층 건물을 세우고, 팜아일랜드에 인공섬을 만들고, 하이드로폴리스라는 수중 호텔도 짓고…. 셰이크 모하메드가 말했습니다. “기획과 전략의 원천은 상상력과 창의력”이라고. 이 상상력과 창의력은 곧 생산력이자 개인과 조직의 경쟁력입니다.

 

우리 양궁도 끊임없이 새로운 훈련방식을 개발해내려 합니다. 다른 종목에선 “양궁은 이벤트 훈련을 많이 한다”고들 말하지만 결코 이벤트가 아닙니다. 결정적 순간에 실수를 줄일 수 있는 정신훈련, 팀워크 훈련 등 훈련방법마다 다 의미가 있습니다. 이런 훈련방법을 만들 수 있었던 것도 바로 상상력, 창의력입니다.

 

통찰력·창의력·글로벌 능력·조직 적응력 그리고 열정

 

셋째는 글로벌 능력입니다. 그리고 그중에서도 가장 중요한 게 언어입니다. 영어는 기본이고 제2, 3 외국어를 할 줄 알아야 합니다. 경기방식이 왜 자꾸 한국양궁에 불리하게 바뀌는 걸까요. 세계양궁연맹 집행위원이 33명인데, 주로 유럽 출신입니다. 이들이 경기방식을 바꾸다 보니 한국 선수들에게 불리할 수밖에 없습니다.

 

그래서 대한양궁협회도 10여 년 전부터 어학연수를 1년에 두세 명씩 보내고 있습니다. 그리고 이제 우리도 글로벌 능력의 한계를 극복할 수 있다는 자신감이 생겼습니다. 우리가 2009년 세계선수권대회를 유치하게 됐거든요. 이 대회 이후 집행위원 선임투표가 있는데, 이때 한국 양궁 지도자들을 집행위원회에 넣을 계획입니다.

 

넷째는 조직생활의 기본이라고 할 수 있는 세 가지, 즉 엄격한 도덕성, 신뢰, 성실성입니다. 이 세 가지를 바탕으로 변화와 비전을 제시할 수 있는 리더십 역량을 극대화해야 합니다. 하루에도 몇 차례씩 인간 한계를 넘나드는 극한상황을 경험하는 스포츠 선수들에게 지도자의 리더십은 정말 중요합니다. 그렇다면 이 리더십은 어디서 나올까요? 제가 20년 넘게 선수들을 지도해보니 첫째는 인격, 둘째는 실력, 셋째는 상대에 대한 배려와 헌신이라는 걸 알았습니다. 이것이 바로 리더십의 3대 키워드입니다.

 

40여 년 전 양궁이 국내에 도입되던 당시의 100대 기업 가운데 지금까지 살아남은 기업은 12개뿐입니다. 나머지 88개는 흔적도 없이 사라졌습니다. 이렇게 빠르게 변화하는 무한경쟁 시대에는 자신의 능력을 극대화하는 자만이 살아남을 수 있습니다. 그렇다면 능력을 어떻게 극대화해야 할까요? 가장 중요한 점은 각자의 가슴속에 뜨거운 열정을 지녀야 한다는 것입니다. 제가 마지막으로 강조하는 것도 바로 열정입니다. 열정 없이는 어떤 위대한 비전, 거대한 꿈도 잉태될 수 없습니다.

 

매일 새벽 5시 반에 훈련이 시작되면 저녁 8시에 끝납니다. 2시간 동안 자유시간을 가진 후 밤 10시에 소등하고 잠자리에 듭니다. 여러분, 인간 한계를 넘나드는 경험을 해본 적이 있습니까? 숨이 깔딱 넘어갈 것 같은 느낌, 가슴이 찢어질 듯한 아픔…. 그런 한계상황을 하루에 두세 번씩 넘기고 저녁 8시 숙소에 돌아오면 정말 손가락 하나 까딱하기 힘듭니다.

 

그런데요, 2시간의 자유시간 동안 옆에서 TV를 보던 동료가 조용히 밖으로 나갑니다. 10분, 20분이 지나도 안 돌아오면 방에 있던 친구도 덩달아 나갑니다. 그러다 옆방 동료가 방에 와보면 아무도 없잖아요. 그럼 그 동료도 나갑니다. 이렇게 빠져나가다 보면 밤 11시에도 숙소에는 선수가 한 명도 없습니다. “이 녀석들, 또” 하면서 훈련장에 올라가보면 전원이 그 시간에 불 켜놓고 야간훈련을 하고 있습니다. 이렇게 선수들은 엄청난 경쟁 속에서 살아갑니다. 뜨거운 열정이 없다면 이런 경쟁에서 단 하루도 버틸 수 없습니다.

 

2004년 아테네올림픽에서 한국과 중국의 여자양궁 단체 결승전 때 일입니다. 중국 선수들이 다 쏘고 한국 박성현 선수가 마지막 한 발을 남겨놓은 상태였습니다. 박 선수가 10점 만점을 쏘면 우리가 우승이고, 9점을 쏘면 중국과 동점으로 재경기, 8점을 쏘면 우리가 지는 상황이었습니다.

 

선수들이 활을 쏘고 들어올 때마다 “잘했어”라고 말하고 하이파이브를 ‘탁’ 하면 물이 튑니다. 손에 땀이 흥건할 정도로 긴장하고 있다는 뜻입니다. 당시 박 선수가 쏠 준비를 할 때 중국 선수들이 방해하려고 소란을 피웠습니다. 이단옆차기를 날리고 싶을 정도로 중국 선수들이 미웠죠. 그런데 그 순간 박 선수의 눈빛을 보고 놀랐습니다. 그 초긴장의 순간에 눈빛 하나 흔들리지 않고 타깃을 바라보는 카리스마가 대단했거든요. 조금의 흔들림도 없었습니다. 그렇게 10점을 쏘고 나서 말 그대로 난리가 나지 않았습니까.

 

돌아오는 차 안에서 제가 박 선수에게 “그 마지막 순간, 마음상태가 어땠느냐”고 물었습니다. 박 선수의 첫마디가 “죽는 줄 알았어요”였습니다. 겉으로 그처럼 늠름해 보였지만 속으론 엄청난 공포를 느꼈다는 겁니다. 활을 쏴야 하는 40초 동안 고국에서 자신을 지켜보고 있을 부모님, 가족, 친구들을 생각하니 제대로 서 있기조차 어려울 정도로 다리가 후들거리고 가슴이 뛰더라는 겁니다. 그러다 문득 지난 4년간 가장 힘들고 고통스러웠던 순간들이 떠올랐다고 합니다. 그때 오히려 마음이 차분해지더라는 거예요. 그렇게 바로 쏴버린 활이 10점 과녁에 들어갔던 겁니다.

 

바로 그거였습니다. 우리는 그 순간에 박 선수가 아닌 다른 어떤 한국 선수가 거기에 서 있었더라도 10점 과녁을 꿰뚫을 수 있도록 충분히 준비했습니다. 결정적 순간에 실수를 최소화하기 위해, 우리가 상상치 못했던 일들이 일어날 수 있다는 점을 가정해 끊임없이 적응훈련을 하며 대비했습니다.

 

북파공작원 훈련장에서 선수들 기절 직전까지

 

어머니가 열 달 산고(産苦)를 거쳐 아이를 낳듯, 우리 양궁 국가대표 선수들도 열 달간 열 번의 대회를 치러 선발됩니다. 그렇다고 아무나 선발전에 참가할 수 있는 것은 아닙니다. 2008년 베이징올림픽 국가대표 선발전 참가 자격은 2007년 남녀 랭킹 100위까지에게만 주어집니다. 그런데 이 100등 안에 들어가는 것부터가 무척 치열합니다. 보통 한 달에 한두 번씩 전국대회가 열리는데, 여기서 2주일만 훈련을 소홀히 해도 바로 100등 밖으로 밀립니다. 국내에서 남녀 랭킹 80등 정도 하면 세계 랭킹 5위 안에 듭니다. 이런 선수들이 100명씩 모여 열 달간 열 번의 대회를 치르는 겁니다.

 

그 열 번의 대회가 똑같은 방식으로 치러지는 것도 아닙니다. 1차전은 체력이 좋은 선수가 기록이 잘 나오도록 대회 방식을 만들어놨습니다. 2차전은 정신력이 뛰어난 선수를 가려내기 위한 방식입니다. 11월 강원도에서 대회를 치르는데, 선수들은 닷새간 얇은 티셔츠 한 장만 입고 아침 9시부터 오후 5시까지 밖에서 경기를 합니다. 11월의 강원도는 춥습니다. 비라도 오면 손가락이 곱아 감각조차 없어집니다. 한마디로 정신력 싸움인 겁니다.

 

3차전은 담력, 4차전은 집중력, 5차전은 근성, 6차전은 환경 변화에 대한 적응력, 이런 식으로 대회마다 다른 목적을 가지고 치릅니다. 7차전은 최종 8명에서 4명이 남는 대회이기 때문에 선수들은 한 발 한 발에 엄청난 압박감을 느낍니다. 그래서 이를 잘 극복하는 선수가 좋은 점수를 받도록 경기방식을 만들었습니다.

 

이렇게 7차전이 끝나면 100명에서 남녀 각 4명이 남습니다. 이 선수들이 국내 대회 한 번, 국제대회 두 번을 더 치릅니다. 국내 대회에서는 잘하는데 국제대회에만 나가면 헤매는 선수가 있거든요. 그렇게 나머지 한 명을 걸러내면 최종적으로 남녀 각 3명이 올림픽 대표선수가 됩니다.

 

그런데 환경 변화에 적응력이 뛰어난 선수를 어떻게 뽑는지 궁금하시죠? 간단합니다. 7월에 대회를 치르는데, 먼저 기상청에 문의해 태풍이 올라오는 날짜를 뽑아달라고 요청합니다. 그 자료를 통해 태풍이 올 가능성이 가장 높은 날짜를 넣어 일정을 잡습니다. 그럼 대회가 열리는 닷새간 무조건 하루는 걸리게 돼 있거든요.(좌중 웃음)

 

지난해 대회 때 누구라고 하면 다 알 정도의 간판급 스타선수가 있었습니다. 그 선수가 시위를 당기기 위해 섰습니다. 비바람이 몰아치고, 물은 발목까지 차오르고…. 이럴 때 활을 쏠 수 있겠습니까? 조준 자체가 안 됩니다. 그런데 제한시간은 흘러갑니다. 이때의 갈등은 말도 못하죠. 그러다 선수가 순간적으로 바람이 잦아드는 듯한 느낌을 받았나 봅니다. 쐈거든요. 한데 바로 그 순간 ‘빠방’ 하면서 천둥이 쳤고, 그 선수가 깜짝 놀라 0점을 쏴버리고 말았습니다. 올림픽 2관왕에 세계선수권 2관왕, 아시안게임 2관왕. 누가 봐도 세계적인 스타인데 그 한 발 때문에 국가대표에서 탈락했습니다.

 

긴 안목으로 보면 원칙을 지키는 게 옳습니다. 그 덕에 고등학교 1학년의 어린 선수가 여자 4명이 남는 단계까지 올라왔습니다. 무명 선수도, 나이 어린 선수도 열심히 하면 국가대표가 될 수 있다는 강력한 동기부여가 된 셈입니다.

 

앞서 말씀드렸듯, 어느 선수라도 ‘그 자리에 서면 해낼 수 있는’ 능력을 기르기 위해 많은 훈련을 합니다. 양궁팀이 공수특전단에서 훈련을 한다는 사실은 잘 아실 겁니다. 한 달 전엔 북파공작원이 훈련했던 HID에 다녀왔습니다. 그런 곳에 가면 여자선수들은 기절 직전까지 갑니다. 남자선수들도 팬티에 오줌을 쌀 정도니까요.

 

올림픽 한두 달 전에는 경기에 대한 부담감 때문에 불면증에 시달리는 선수가 많습니다. 그럼 우리는 선수들을 서부 최전선 부대에 데려갑니다. 군복 입고 철모 쓰고 실탄 지급받고 GOP로 들어가 경계근무를 서게 되죠. 이걸 왜 하느냐. 밤새 자기성찰 시간을 가지면서 복잡한 머릿속을 단순화하자는 의도입니다. 그래서 이 훈련을 하고 나면 정말 머릿속이 단순해집니다. 아무 생각 없이 잠도 잘 자거든요. 그러다 또 생각이 복잡해지면 다시 집어넣습니다.(좌중 웃음)

 

그런데 지도자들은 뒷전에서 놀며 “야, 너희들 갔다와” 이러면 선수들이 제대로 하겠습니까? 지도자들도 똑같이 군복 갈아입고 들어갑니다. 제가 여러분에게 말씀드리는 양궁 훈련들은 지도자가 먼저 시범을 보이거나 함께 한다는 것을 전제로 합니다.

 

선수들에 번지점프 시범 보이느라 감독이 9번 뛰어내려

 

지난해 세계선수권대회에 나가기 전 선수들을 충주호에 있는 65m짜리 번지점프대에 데려갔습니다. 좀전에 제가 뭐라고 했죠? 가장 먼저 지도자가 시범을 보여야겠죠? 그렇게 시범을 보이고, 그 다음에 선수들을 뛰게 하는 겁니다.

 

그런데 한 여자선수가 뛰지 못했습니다. 감독이 30분간 그 선수를 설득했습니다. “다 뛰는데 너는 왜 못 뛰느냐. 너는 능력도 있고 배짱도 있다. 못하는 건 결단력이 부족해서 그렇다. 만약 올림픽에서 결정적인 순간에 맞닥뜨리면 어떻게 할 거냐. 해봐라. 넌 할 수 있다….” 온갖 감언이설로 설득했습니다.

 

그래도 안 되니까 이번엔 감독이 다시 뛰었습니다. 뛰고 내려와서 또 30분간 설득합니다. 그래도 안 되면 감독이 올라가서 다시 뛰어내리고…. 그런 식으로 충주에 간 첫날 감독은 아홉 번이나 뛰어내렸습니다.

 

그 다음엔 어떻게 해야 할까요? 그 선수의 소속팀 감독에게 연락해 올라오게 했습니다. 선수와 소속팀 감독 그리고 대표팀 감독 이렇게 셋이 또 충주호에 갔습니다. 이번엔 두 감독이 그 선수를 설득했습니다. “너는 뛸 수 있다” 이렇게 끝까지 힘을 북돋워 준 겁니다. 그래도 못 뛰잖아요? 그럼 이번엔 소속팀 감독과 대표팀 감독이 함께 뛰어내립니다.

 

나중엔 말이죠. 감독들이 “제발 나 좀 살려줘”라고 통사정합니다. “선생님이 불쌍하지도 않냐. 이게 며칠째냐?” 소속팀 감독은 또 무슨 죄가 있습니까? 국가대표 선수 길러낸 죄밖에 더 있습니까? 불려와 덩달아 뛰는 겁니다. 사흘째 되던 날, 두 발을 땅에 딱 붙이고 있던 선수가 갑자기 “으아~” 소리치며 일어나더니 “차라리 죽어버릴 거야” 하며 번지점프를 했습니다. 결국 그렇게 뛰어내려 성공한 겁니다. 거기 한 번 뛰는 데 4만원이더라고요. 첫날에만 감독이 아홉 번 뛰었는데, 다섯 번 뛴 것만 돈을 받았습니다.(좌중 웃음) 그걸 보면서 선수가 얼마나 미안했겠습니까. 그 선수는 정말 죽고 싶다는 생각으로 뛰었을 겁니다. 고소공포증이 있는 선수였거든요. 그 선수에게 “두어 번 더 뛰어내리자”고 해서 두 번 더 시켰습니다. 결국 그 선수가 독일 세계선수권대회에서 가장 좋은 성적을 냈습니다.

 

우리가 자주 하는 훈련 중에 천호대교에서 63빌딩까지 걸어가는 게 있습니다. 약 26km 거리입니다. 가장 추운 1월 중순, 밤 12시 반에 출발해 최대 속보로 가면 보통은 아침 7시경 63빌딩에 도착합니다.

 

지난해 12월20일에는 제주도에 갔습니다. 밤 9시에 앞뒤 사람 간격을 1km로 두고 출발해 1100도로를 거쳐 중문, 서귀포로 해서 표선까지 걸었습니다. 11시간 걸렸습니다. 표선에 도착해 오전 11시경 아침 겸 점심을 먹고 선수들을 버스에 태워 관광을 시키는데, 이게 실은 선수들 잠 못 자게 괴롭히는 겁니다. 밤새 걸었기 때문에 차에 태우면 얼마나 잠이 오겠습니까? 잠이 들 만하면 “하차!”, 찬바람 맞고 잠 다 깨면 5분쯤 뒤에 “승차!” 이렇게 온종일 계속하면 남자든 여자든 반은 미쳐버립니다.

 

그런데 선수들이 왜 화를 못 내는지 아십니까? 지도자든 감독이든 자기들과 똑같이 하기 때문입니다. 만약 감독이 자기는 잠자면서 선수들에겐 “야, 내렸다 타” 이랬다면 쿠데타가 나도 몇 번은 났을 겁니다. 똑같이 하니까 화도 못 내고, 자신에 대한 울분만 풀어내는 겁니다. 그렇게 돌다가 22일 새벽 4시에 다시 표선에 도착했습니다. 30분간 밤참 먹고, 새벽 4시40분부터 다시 걸었습니다. 앞뒤 사람 1km 간격으로 세워 한라산 정상까지 갔습니다.

 

 

끊임없이 위기의식 주입 … 칼날 위에서 근성으로 살아남기


당시 최연소 선수가 중학교 3학년이었습니다. 우리가 표선에서 출발할 때는 비가 왔는데 한라산을 3분의 2쯤 올라가니 눈보라로 바뀌어 앞이 안 보일 정도였습니다. 이 어린 선수가 얼마나 힘들었는지 “엄마, 엄마” 하면서 막 울었다고 합니다. 그래서 코치들이 옆에 붙어 “하나 둘, 하나 둘” 격려하며 앞으로 나아갔습니다. 그 어린 선수도 결국 한라산 정상까지 갔습니다. 한 사람의 낙오자도 없이 전원이 잠 한 숨 안 자고 무박3일 훈련을 소화해낸 겁니다.

 

우리 민족을 동이족(東夷族)이라 합니다. 동쪽의 활을 잘 쏘는 민족이라는 뜻입니다. 한국 양궁의 성공이 과연 타고난 기질 덕분일까요? 절대 그렇지 않습니다. 한국 양궁의 성공은 뼈를 깎는 노력과 치밀한 전략의 결과입니다.

 

아무리 작은 일이라도 동반자가 필요합니다. 가장 조화로운 분위기에서 최고의 결과를 기대할 수 있는 겁니다. 서로 강점을 인정하고 약점을 보완하면서 성취를 이뤄가는 것이 함께 성장하고 승리하는 윈-윈 파트너십입니다. 개인이 좀더 나은 삶의 질을 창출하려 노력할 때 자기가 몸담은 조직도 글로벌 조직으로 커갈 수 있습니다. 바로 이런 동반자 정신, 주인의식을 가져야 합니다.

 

양궁도 마찬가지입니다. 선수가 너무 힘들다 보면 “그래, 나 하나 금메달 포기하면 되지” 하는 상황까지 갈 수 있습니다. 그러나 그건 선수 개인이 금메달을 놓친 게 아니라 대한민국 양궁, 나아가 우리 한국 선수단이 금메달을 놓친 게 됩니다. 그래서 주인의식과 동반자 정신이 필요한 겁니다.

 

오늘 하루가 저물어가고 있습니다. 오늘은 우리에게 남은 인생의 첫날이었습니다. 두 번 다시 오지 않죠. 그래서 우리 선수들에게 늘 말합니다. 매순간 살아가는 의미와, 무엇이 돼야 하고 무엇을 이룰 것인지를 생각해보라고 말입니다.

 

살다 보면 어제가 오늘 같고, 오늘이 내일 같고, 내일도 모레 같고…, 그렇게 어영부영 지나는 경우가 많습니다. ‘아차’ 하면 누구나 그런 삶을 살게 됩니다. 그러나 이는 실패한 삶입니다. 끊임없이 위기의식을 갖는 게 필요합니다.

 

저는 선수와 지도자들에게 세 가지를 부탁합니다. 첫째, 매순간이 승부다. 둘째, 우리는 진정한 프로페셔널이다. 그러니 프로정신과 프로 근성으로 살아라. 셋째, 적어도 조직을 관리하는 리더라면 자기가 있어야 할 그 시간에 바로 그곳에 있어야 한다.

 

우리 선수들은 20대 초반에서 30대 초반까지 약 10년간 운동선수로서의 인생에 마지막 승부수를 펼칩니다. 단 두 번의 기회가 주어질 뿐입니다. 두 번의 올림픽입니다. 어떤 어려움이 있더라도 그 10년간 집중해서 파고들면 다들 경지에 오릅니다. 그런 꿈과 희망을 갖고 인생의 승부를 거는 겁니다.

 

오늘 제 얘기가 여러분에게 조금이나마 도움이 됐길 바랍니다. 감사합니다.

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

이제 할일은 문제의 ./util 과 ./examples 이다!!
일단 ./util/grapicsTest디렉토리로 이동해 make를 해보자

[root@localhost graphicsTest]# make
arm-linux-gcc -c -O -I/usr/src/kernels/2.6.25.6-27.fc8-i686/include/ -I../../include graphicsTest.c
graphicsTest.c:7:21: GL/gl.h: 그런 파일이나 디렉토리가 없음
graphicsTest.c:8:23: GL/glut.h: 그런 파일이나 디렉토리가 없음
graphicsTest.c: In function `draw':
graphicsTest.c:101: error: `GLfloat' undeclared (first use in this function)
graphicsTest.c:101: error: (Each undeclared identifier is reported only once
graphicsTest.c:101: error: for each function it appears in.)

                        ....생략....

graphicsTest.c:130: warning: data definition has no type or storage class
graphicsTest.c:132: warning: parameter names (without types) in function declaration
graphicsTest.c:132: warning: data definition has no type or storage class
graphicsTest.c:133: error: parse error before '}' token
make: *** [graphicsTest.o] 오류 1
[root@localhost graphicsTest]#
아마도 요런 에러가 날것임(오류가 완전 틀리다면 make clean후 make)
뭐 경우에따라 완전 똑같은 오류가 나지는 않을것이다

이경우 처음부터 살펴보면
         ▷ graphicsTest.c:7:21: GL/gl.h: 그런 파일이나 디렉토리가 없음
             graphicsTest.c:8:23: GL/glut.h: 그런 파일이나 디렉토리가 없음
요런 에러가 보이는데 간단하다!! 경로를 못찾아서 그런것이라 Makefile에서
경로를 맞춰주던지 귀찮음 디렉토리 내에 헤더파일들을 때려박고
#include "gl.h" 요런식으로 주면 된다.
한가지 주의할 점은 헤더파일을 디렉토리내에 복사할경우 의존적인 헤더들까지 같이 복사해 주어야 한다는 사실이다!!

------------------  미완된 글입니다 ------------------

+ Recent posts