[OS]/Embedded&Linux

ELF format

하늘을닮은호수M 2007. 1. 24. 10:27
반응형

1.1. ELF 파일의 형식(format)

ELF(Executable and Linking Format)은 binary file[1]로서, Unix System Laboratory에서 개발되고 발전되어왔다. SVR4와 Solaris 2.X version의 운영체제에서는 기본적인 실행 file의 format으로 사용되고 있다. 실행 file의 format으로는 a.out과 COFF format이 있지만, ELF format이 보다 강력하며, 유연성을 가지고 있다. 적절한 tool과 같이 사용될 때 실행되는 과정을 제어 할 수 있다. 현재 리눅스는 kernel차원에서 binary file format에 대한 지원을 가지고 있으며, binary file 자체가 가진 특정한 magic number로 실행할 method를 찾게 된다. 이번 장에서는 먼저 ELF file의 format에 대해서 알아보도록 하자[2].
ELF는 UNIX System Laboratory에서 Application Binary Interface(ABI)의 일부로서 개발되고 발표되었다. Tool Interface Standards committee(TIS)에서 32 bit Intel Architecture 환경에서 동작하는 portable object 파일 포맷으로 ELF 표준을 선택했다. 프로그램머에게 ELF표준은 여러 운영체제 환경으로 확장될 수 있는 binary 인터페이스 정의들의 집합을 제공한다. 따라서, 프로그램머들은 binary 파일의 이러한 인터페이스만을 중심으로 프로그램을 할 수 있는 방법을 제공받을 수 있으며, 더불어 새로이 코드를 재 컴파일해서 기록할 필요가 없게된다.
Object 파일에는 크게 3가지가 존재한다. 각각은 아래와 같이 정의될 수 있다.
l 재배치 가능 파일(relocatable file) 이 파일은 링크를 쉽게 할 수 있는 코드 및 데이터와 함께, 실행 파일을 만들거나 공유 object파일을 생성하기위한 다른 object파일을 가지고 있는 형태의 파일이다.
l 실행 가능 파일(executable file) 이 파일은 실행을 위해서 적합한 프로그램을 가지는 파일로서, exec시스템 콜이 어떻게 프로세스의 이미지를 프로그램을 이용해서 만들지를 기술하는 파일이다.
l 공유 object 파일(shared object file)은 두개의 환경(context)상에서 링크에 적합한 형태의 코드와 데이터를 가지는 파일로서, 먼저 LD와 같은 링크 에디터가 이 파일과 함께, 다른 재배치 가능 파일과 공유 object파일을 처리해서 또하나의 object 파일을 생성해주게 되고, 이를 다시 동적 링커(dynamic linker)가 생성된 object 파일을 실행 가능 파일과 다른 공유 object파일을 묶어서 프로세스의 이미지를 생성해 주는 두 단계를 거치는 파일이다.
어셈블러나 링크 에디터에 의해서 생성된 object 파일은 프로세서에서 실행될 프로그램의 이진(binary) 형식을 가지게 될 것이며, 이곳에서의 논의는 쉘 스크립터와 같은 것은 제외할 것이다.
먼저 ELF 파일의 형식(format)을 보면 [그림]와 같다. 즉, object 파일은 크게 두가지의 관점에서 볼 수 있는데, 각각이 프로그램의 링킹(linking)과 프로그램의 실행이라는 측면이 될 것이다.
[그림] ELF 파일의 포맷 관점에 따른 형식
[그림]에서 중요한 부분만을 훑어보면, ELF 헤더(header)는 파일의 구성을 나타내는 로드맵(road map)과 같은 역할을 하며, 첫 부분을 차지한다. 섹션(section)은 링킹을 위한 object 파일의 정보를 다량으로 가지고 있으며, 이에 해당하는 것으로는 명령(instruction), 데이터(data), 심벌 테이블(symbol table), 재배치 정보(relocation information)등등이 들어간다. 프로그램 헤더 테이블(program header table)은 옵션(option)이며, 시스템에 어떻게 프로세스 이미지를 만들지를 지시한다. 프로세스의 이미지를 만들기 위해서 사용되는 ㅍ일은 반드시 프로그램 헤더 테이블을 가져야하며, 재배치 가능 파일의 경우에는 가지지 않아도 된다. 섹션 헤더(section header) 테이블는 파일의 섹션들에 대해서 알려주는 정보를 가지는데, 모든 섹션은 이 테이블에 하나의 엔트리(entry)를 가져야 한다. 각각의 엔트리는 섹션 이름이나, 섹션의 크기와 같은 정보를 제공해 준다. 만약 파일이 링킹하는 동안 사용된다면, 반드시 섹션 헤더 테이블을 가져야하며, 다른 object파일은 섹션 헤더 테이블을 가지고 있지 않을 수도 있다. 이에 해당하는 정보들은 리눅스의 경우 ~/include/linux/elf.h에서 찾을 수 있다.
코드를 직접 보기 전에 이하의 내용에서 사용하게 될 데이터 타입에 대한 정의부터 하기로 한다. 데이터 타입이 필요한 것은 ELF format이 다양한 프로세서에서 사용할 수 있으며, 이들 각각이 사용하는 기본 데이터의 길이가 달라질 가능성이 있기 때문이다. 따라서, object 파일은 기계 의존적인 특성들에 대비를 해야하는데, 기계에 의존적이지 않은 형식을 지원하기 위한 제어 데이터(control data)가 이 역할을 해주고 있다. 즉, object 파일을 인식하고, 파일의 내용을 일반적인 방법으로 해석하도록 돕는다.
리눅스에서는 아래와 같은 것을 정의해서 사용하고 있다.
/* 32-bit ELF base types. */
typedef __u32 Elf32_Addr;
typedef __u16 Elf32_Half;
typedef __u32 Elf32_Off;
typedef __s32 Elf32_Sword;
typedef __u32 Elf32_Word;
/* 64-bit ELF base types. */
typedef __u64 Elf64_Addr;
typedef __u16 Elf64_Half;
typedef __s16 Elf64_SHalf;
typedef __u64 Elf64_Off;
typedef __s64 Elf64_Sword;
typedef __u64 Elf64_Word;
코드 1. ELF 파일 형식에 사용할 데이터 타입 정의
두가지로 나누어 볼 수 있는데, 먼저 32bit 기계에 대한 것과 64bit 기계에 대한 부분이다. 각각이 기본 연산의 단위로 32bit과 64bit을 사용함을 알 수 있다. 나중에 여기서 정의된 것을 이용해서 다른 데이터 타입에 대한 정의를 하게 될 것이다. 만약 이러한 데이터 타입을 제공하게 될 때, 기본 연산의 단위로 정렬(alignment)하고 싶다면, 패딩(padding)을 삽입해서 4bytes나 혹은 8bytes단위로 정렬할 수 있을 것이다.

1.1.1. ELF 헤더의 정의

이젠 ELF 헤더부터 보기로 하자. 아래와 같이 정의된다. 32bit과 64bit 기계의 ELF 헤더 정의이다.
#define EI_NIDENT 16
typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry; /* Entry point */
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;
typedef struct elf64_hdr {
unsigned char e_ident[16]; /* ELF "magic number" */
Elf64_SHalf e_type;
Elf64_Half e_machine;
__s32 e_version;
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
__s32 e_flags;
Elf64_SHalf e_ehsize;
Elf64_SHalf e_phentsize;
Elf64_SHalf e_phnum;
Elf64_SHalf e_shentsize;
Elf64_SHalf e_shnum;
Elf64_SHalf e_shstrndx;
} Elf64_Ehdr;
코드 2. 32bit 64bit ELF header
각각의 필드가 하는 역할은 아래와 같다. 주석으로 처리하기에는 조금 무리가 있을 싶어서, 다시 정리했다.
Field
Description
e_ident[]
파일의 내용을 해석하고 디코딩하기 위해서 사용되는 기계 독립적인 데이터를 제공하며, 파일이 object파일임을 나타낸다.
e_type
Object 파일의 타입을 표시한다.
e_machine
파일을 사용하기 위해서 필요한 architecture정보를 나타낸다.
e_version
Object 파일의 버전 정보를 나타낸다.
e_entry
시스템이 실행하기 위해서 제어를 옮길때 어디로 옮겨야 하는지를 가르쳐주는 가상주소를 가진다. 만약 파일이 진입점(entry point) 가지지 않는다면 0 가질 것이다.
e_phoff
프로그램 헤더 테이블의 파일 옵셋을 byte단위로 나타낸다. 프로그램 헤더 테이블이 없다면 당연히 0 가질 것이다.
e_shoff
섹션 헤더 테이블의 파일 옵셋을 byte단위로 나타낸다. 역시 섹션 헤더 테이블을 가지지 않는다면 0 가질 것이다.
e_flags
파일과 관련되서 프로세서에 특수한(specific) 플랙을 가진다. 플랙을 나타내는 형태는 EF_[machine_flag] 것이다. 이것을 알기위해서 기계 의존적인 부분을 보아야 것이다.
e_ehsize
ELF 헤더의 크기를 가진다.
e_phentsize
파일의 프로그램헤더 테이블에 있는 엔트리의 크기를 byte단위로 표시한다. 모든 엔트리는 동일한 크기를 가진다.
e_phnum
프로그램
반응형