1) 지식 창고는 본인이 작성한 콘텐츠(팁/노하우/리소스/강좌 등)을 무료 혹은 가상화폐인 납포인트를 통해 공유하는 공간입니다.
2) 본인이 작성한 콘텐츠에 대해서만 지식 창고에 등록할 수 있으며, 저작권에 위배되는 콘텐츠는 사전경고 없이 삭제될 수 있습니다.
3) 콘텐츠 구매 및 첨부파일 다운로드는 회원그룹 '연구원' 이상 가능하오니, 경험치를 쌓아 진급한 후에 이용 부탁드립니다.
4) 무료 콘텐츠의 본문은 구매절차 없이 즉시 이용할 수 있으며, 판매 납포인트가 있는 콘텐츠는 구매 후 이용할 수 있습니다.
5) 콘텐츠 판매에 따른 납포인트 수익은 지정한 비율(50%)에 따라 판매자에게 지급하며, 납포인트 수익을 통해 진급을 빨리할 수 있습니다.
6) 구매 후 평가를 하면 구매 납포인트의 20%를 돌려 드립니다.
판매자 | 나임 | 판매 납포인트 | 무료 | 평점 | 0점 / 총 0명 참여 |
---|
1. 리눅스 어셈블리 프로그래밍의 기초
이번 강좌를 통해 리눅스 어셈블리 프로그래밍에 대해서 알아보고자 한다. 리눅스에서 어셈블리 프로그래밍을 한다는 것은 황량한 사막을 건너는 것과 같은 느낌이 들 것이다. 어쩌다가 좋은 문서를 발견하면 사막에서 오아시스를 만나는 것처럼 갈증을 어느 정도 해소해 주지만 조금 더 쉽고 자상한 초보자들을 위한 문서들이 부족함을 느낄 것이다.
이번 강좌는 이러한 초보자들을 위해 기초부터 공부할 수 있는 예제 위주의 직접 실행해볼 수 있는 좋은 길잡이가 될 것이다. 또 중급 사용자들을 위해 리눅스 어셈블리를 사용하여 조그마한 운영체제를 만들어 테스트할 수 있도록 할 것이다.
자, 이제 나와 같이 리눅스에서 어셈블리 프로그래밍을 하는 사막을 향해 낙타를 타고 여행을 떠나보자.
[그림 1-1] 리눅스 어셈블리 로고
1.1 리눅스 어셈블리 프로그래밍에 대하여
리눅스 어셈블리 프로그래밍을 하기 위해서는 다음과 같은 여러 가지 항목에 대해서 알아야 할 것이다.
우선 리눅스 어셈블리 프로그래밍을 하기 위해 필요한 툴을 알아보자.
1. as : 일명 GAS라고 하며, GNU 어셈블러이다.
2. ld : GNU 링커이다.
3. gdb : GNU 디버거이다.
이들 툴을 사용함에 있어, 우선 리눅스 시스템에 대한 전반적인 이해가 필요할 것이다. 즉, 만약에 자신의 리눅스 시스템에 GCC가 설치가 안되었다면 문제가 될 것이며, 필요한 라이브러리와 헤더 파일들이 시스템의 어느 곳에 위치하는지도 알아야 할 것이다.
우선 앞에 말한 3가지 툴들중 첫 번째와 두 번째를 이진파일 유틸리티(Binary Utility)라고 부르며, 어떤 시스템에 컴파일러를 만들 때 가장 먼저 포팅되는 것들이다. 만약 여러분이 전혀 새로운 CPU에서 어셈블리 프로그래밍을 하고 싶다면 이러한 이진파일 유틸리티부터 포팅해야 한다.
운영체제 만드는 일도 이와 비슷해서, 일단 이진파일 유틸리티와 C 컴파일러가 제공되어야 비로소 운영체제 만드는 일이 시작된다. 리눅스라는 운영체제도 어떻게 보면 Richard Stallman이 만든 GNU 어셈블러와 C 컴파일러로 만들어진 하나의 프로그램에 지나지 않을지 모른다.
일단 어셈블리어 프로그래밍은 운영체제에 많이 좌우되는데, 우리가 프로그래밍하는 환경은 현재 거의 모든 리눅스 프로그램들이 쓰는 ELF라는 실행파일 형식을 따른다고 가정한다.
리눅스에는 실행파일 형식으로 예전부터 3가지 이진형식을 지원했는데, 첫 번째는 a.out이라는 형식으로 기존의 BSD 유닉스에서 쓰던 형식이고, 두 번째는 이것을 개량한 coff 형식이고, 세 번째는 ELF 형식으로 MS 윈도우의 DLL과 같이 동적으로 이진 파일을 다룰 수 있게 만든 것이다.
우선 리눅스에서 어떤 프로그램이 실행되는 과정을 알아보는 것이 중요하다. 즉, 사용자가 쉘에서 프로그램을 실행하면, sys_execve()라는 함수가 실행파일을 실행하게 되는데, 커널은 이 함수에서 받은 실행파일을 do_execve()라는 함수를 사용하여 열어 파일 형식을 search_binary_handler()라는 함수로 찾는다. 만약 파일 형식이 ELF이면 load_elf_binary()를 사용하여, 시스템의 메모리에 올리고, 마침내 start_thread()를 사용하여, 커널에서 사용자가 넘긴 실행파일이 실행할 수 있도록 하고 있다.
어셈블리어로 프로그래밍하는 사람들은 load_elf_binary()에 의해 프로그램이 시스템의 메모리에 올라갈 때, 메모리의 어떤 번지에 올라가는지 궁금할텐데, i386의 경우 0x08048000번지가 이러한 ELF 파일의 첫 번째 번지가 되며, 계속해서, text 섹션과 data 섹션 그리고, stack 섹션이 존재하게 된다.
하지만 이러한 번지도 ELF 형식으로 결과물을 내는 GCC와 GAS가 있다면 굳이 알 필요가 없으며, text 섹션과 data 섹션, stack 섹션에다가 필요한 어셈블리어를 쓰고, GAS와 LD를 사용하여, ELF 형식의 실행파일을 만들어 주게 되면, 시스템 커널이 알아서, ELF로 된 실행파일을 시스템의 메모리 0x08048000번지에 로딩하게 되는 것이다.
1.2 GAS의 코딩 스타일
다음으로 알아봐야 할 것이 실제 GAS를 프로그래밍하기 전에 GAS에서 쓰는 코딩 스타일이다. GAS는 UNIX에서 쓰는 어셈블러이기 때문에, 모토롤라 m68k등에서 쓰는 이른바, "AT&T" 스타일로 된 어셈블리어를 어셈블리한다. 이에 반해 인텔 CPU와 Microsoft의 어셈블리어에서 쓰는 스타일을 "Intel" 스타일이라고 한다.
다음은 "AT&T"로 된 어셈블리어의 코딩 스타일의 특징을 나열한 것이다.
레지스터 이름은 %로 시작한다.
즉, 기존의 "Intel" 스타일로 된 eax나 dl 은 $eax나 $dl로 써야한다.
오퍼렌드의 순서는 소스 레지스터가 먼저 오고, 목적 레지스터가 나중에 온다.
만약 ax의 레지스터의 내용을 dx에 넣고자 하면, 기존의 "Intel" 스타일에서는 mov ax, dx 와 같이 해줘야 한다면, "AT& T" 스타일에서는 mov %dx, %ax가 된다.
오퍼렌드의 길이는 인스트럭션 이름의 첨가자로 부여된다.