본문 바로가기

Hacked Brain/embeddedland.net

부트로더의 기본 구현-2

저 자 : 유영창
출판일 : 2003년 12월호

== 이지부트의 디렉토리 구조
이지부트 소스에서 start와 main의 두 프로그램은 상호 유기적인 관계가 있기 때문에 디렉토리를 하나로 작성하고 하부 디렉토리에 따로 관리한다. 하나의 디렉토리 안에 모든 소스를 관리하면 나중에 무척 힘들게 되고 Makefile이나 링크 스크립트 작성이 어렵다. 그래서 Makefile이나 링크 스크립트의 관리적인 측면에서 따로 나누는 것이 유리하다.
우선 이지부트의 하부 `디렉토리 구조를 살펴보자. 이지부트가 존재하는 디렉토리가 ezboot라는 이름을 갖는다고 가정하면 하부 디렉토리의 모습은 다음과 같다.

◆ ezboot/image/ : 최종적으로 생성되는 부트로더 이미지가 들어가는 디렉토리
◆ ezboot/include/ : 공통적인 헤더 파일 디렉토리
◆ ezboot/main/ : 부트로더의 주 구현 소스가 있는 디렉토리
◆ ezboot/start/ : 초기 부팅용이 들어가는 디렉토리

이렇게 관리하면 작성되는 Makefile은 3개가 된다. 각각 전체 컴파일을 위한 Makefile, start를 위한 Makefile, main을 위한 Makefile이다. 가장 먼저 전체 컴파일을 위한 Makefile을 보자(<리스트 1>).

사용자 삽입 이미지

<리스트 1> ezboot/Makefile

이 Makefile의 각 문장을 살펴보면 <리스트 1>의 (1) 문장은 하부 디렉토리의 Makefile을 처리할 때 설정된 변수 값들이 전달되게 하는 문장이다. (2) 문장은 DIRS 변수에 디렉토리 목록이 지정되도록 해주는 문장이다. 이렇게 지정된 DIRS 변수는 (6) 문장에 의해서 디렉토리를 탐색하는 데 사용되고 지정된 디렉토리에 존재하는 Makefile들을 처리한다. (3) 문장에 의해서 start 프로그램의 링크 옵션과 링크 스크립트 파일을 결정한다. 여기서는 start/start-ld-script가 start 프로그램의 링크 옵션이 된다.
(4) 문장은 C 언어를 처리하기 위한 링크 옵션이다. start 옵션은 지난 호에서 설명한 것과 동일하지만 C 언어에서는 옵션이 조금 다르다. 먼저 기억해야 할 것은 C로 작성하는 부트로더의 main 프로그램은 리눅스 운영체제에서 동작하는 C 프로그램과는 다르다는 것이다. 리눅스에서 동작하는 C 프로그램은 암묵적으로 공유 라이브러리와 표준 헤더 파일을 참조하는 것을 전제 조건으로 작성되며, 이 라이브러리들은 운영체제에 영향을 받는다. 그러나 부트로더는 운영체제 위에서 동작하는 것이 아니고 스스로가 아주 작은 운영체제이기 때문에 이런 표준 라이브러리와 헤더 파일을 사용하면 안 된다. 더욱이 C 프로그램은 스타트업 코드를 가지고 있는데, 이 코드 역시 운영체제의 로더 프로그램에 영향을 받는다.
부트로더는 로더 프로그램에 의해 구동되는 것이 아니라 바이너리 파일을 직접 플래시 롬에 기록하는 방식이기 때문에 일반적인 C 프로그램처럼 스타트업 코드에 의해서 호출되는 main 함수를 가지는 구조로 구성되면 안 되는 것이다. 이런 이유로 부트로더를 위한 옵션은 달라져야 하는데 이지부트의 main을 처리하기 위한 옵션은 다음과 같다.

◆ -static : 공유 라이브러리를 포함하지 않고 모든 라이브러리는 실행 코드에 포함한다.
◆ -nostdlib : 표준 라이브러리를 사용하지 않는다.
◆ -nostartfiles : 스타트업 코드를 포함하지 않는다.
◆ -nodefaultlibs : 디폴트 라이브러리를 사용하지 않는다.

이렇게 C에 관련된 옵션을 설정하고 나서 <리스트 1>의 (5) 문장에 의해서 최종적으로 만들어질 부트 이미지의 이름을 결정한다. 앞에서 이지부트 이미지는 두 개의 프로그램이 합쳐져서 만들어진다고 설명했는데, 이 과정을 좀더 자세하게 알아보자.
두 개의 프로그램 소스가 포함된 각각의 디렉토리, 즉 start/와 main/에 있는 Makefile에 의해 두 개의 프로그램 소스가 컴파일되고 링크되면 최종적으로 start/start_org와 main/main_org가 만들어진다. 두 프로그램 중에서 첫 번째 이미지는 2KB 이하의 크기를 갖는다. 그래서 강제로 start/start_org를 2KB 크기로 맞추기 위해 (7) 문장을 수행한다(지면 관계상 dd에 대해서는 설명하지 않겠다. 여러 가지로 이용 가능한 매우 유용한 프로그램이니 펌웨어를 작성하거나 임베디드 리눅스를 하려면 필히 익히기를 당부한다).
start 프로그램을 2KB로 맞추는 이유는 나중에 부트로더 이미지가 롬에서 램으로 복사되었을 때 쉽게 main의 시작 위치로 갈 수 있기 때문이다. 이렇게 2KB로 맞추어진 start 프로그램 뒤에 main 프로그램을 붙이는 것이 (8) 문장이다. 이 문장이 수행되면 image/ ezboot.x5라는 파일이 생긴다.
(9) 문장은 관리를 편리하게 하기 위한 문장이다. 보통 부트로더가 컴파일된 후 이미지가 생기면 tftp 프로토콜을 이용해 그 이미지를 타겟 보드에 다운로드하게 된다. 이것을 매번 tftp 서비스 디렉토리에 복사하면 귀찮기 때문에 이 문장은 컴파일이 정상적으로 수행되면 생성된 부트로더 이미지를 tftp 서비스 디렉토리에 자동으로 복사해 주는 것이다.
(10) 문장은 모든 내용을 다시 컴파일할 때나 소스만 다른 쪽에 옮길 경우를 위해서 컴파일 과정에서 생성된 모든 파일을 지우는 것이다. make clean이라는 명령으로 동작한다. (11) 문장은 디렉토리에 있는 소스간 의존성에 관련된 내용을 Makefile에 기록한다. 이것은 하나의 파일을 수정했을 때, 특히 헤더 파일을 수정했을 때 연관된 소스들이 자동으로 재컴파일 대상이 되도록 하기 위한 것이다. make dep이라는 명령으로 동작한다. 이 Makefile을 사용해서 컴파일하는 경우는 두 가지로 나누는데, 가장 처음이라면 다음과 같이 수행한다.

make dep; make clean; make all

이런 과정을 거친 뒤에 특정 소스만 수정해서 컴파일할 때는 그냥 make만 치면 된다. 이제 전체를 컴파일하는 Makefile을 보았으므로 실제 소스를 컴파일하는 start/Makefile과 main/Makefile을 살펴보자(<리스트 2>, <리스트 3>).

사용자 삽입 이미지

<리스트 2> start/Makefile


























사용자 삽입 이미지

<리스트 3> main/Makefile

start 프로그램을 위한 Makefile은 이전 강좌에 사용한 것과 유사하다. main 프로그램을 위한 Makefile도 마찬가지로 거의 유사하다. 단지 main 프로그램을 위한 Makefile은 C 언어를 컴파일하기 위한 구조로 구성된다는 차이만 있다(Makefile에 대해서는 더이상 언급하지 않겠다. 이전 연재의 설명으로도 이 Makefile에 대한 이해가 가능할 것으로 생각되기 때문이다).
한 가지 주의할 점은 각각의 디렉토리에서 make 명령은 수행하지 말아야 한다는 것이다. 기본 설정 변수들이 상위 디렉토리의 Makefile에서 설정한 값을 사용하기 때문에 하부 디렉토리에서 직접 make 명령을 사용하면 오류가 발생한다.