ELF64 Relocation 처리(1) - ELF64 구조

ELF64 Relocation 처리(1) - ELF64 구조

원문 : http://kkamagui.springnote.com/pages/1709220

 

들어가기 전에...

 

0.시작하면서...

 이 문서는 ELF64 파일을 Relocation하는 데 필요한 정보가 기술된 문서입니다. 이 문서는 2부분으로 나누어져있으며, 첫번째 문서는 ELF64 파일 포맷에 대한 내용을 다루고 있고, 두번째 문서는 예제 코드를 통해 Relocation을 실제로 수행하는 내용을 담고 있습니다.

 

1.데이터 타입(Data Type)

 ELF64의 경우 데이터 타입은 아래와 같습니다. ELF32와 다른 점은 8Byte 크기의 데이터 타입이 추가되었다는 것입니다.

DataType.PNG

 

 데이터 타입은 ELF의 각 파트를 설명할 때 값의 표현범위를 나타내기위해 사용할 것이므로, 한번쯤 훓어보면 됩니다. ^^;;;

 

2.ELF 기본 구조(ELF Basic Structure)

 ELF64 또는 ELF32는 크게 아래와 같이 두가지 구조를 가집니다( 이 글에서 다룰 구조는 Relocation File 형태의 구조입니다).

elf구조.PNG

 

 Relocation File은 일반적으로 Relocation에 대한 정보를 포함하고 있으며, 각 Section 마다 해당 Section의 Relocation 정보를 담당하는 Section이 별도로 존재합니다. 따라서 특수한 Section만 골라서 쉽게 Relocation을 수행할 수 있습니다.

 

3.File Header

 File Header는 ELF 파일 포맷의 가장 앞쪽에 위치하는 정보로 ELF 파일 포맷에 대한 각종 정보를 포함하고 있습니다. File Header에 포함된 데이터는 아래와 같습니다.

  1. typedef struct
    {
        unsigned char e_ident[16]; /* ELF identification */
        Elf64_Half e_type; /* Object file type */
        Elf64_Half e_machine; /* Machine type */
        Elf64_Word e_version; /* Object file version */
        Elf64_Addr e_entry; /* Entry point address */
        Elf64_Off e_phoff; /* Program header offset */
        Elf64_Off e_shoff; /* Section header offset */
        Elf64_Word e_flags; /* Processor-specific flags */
        Elf64_Half e_ehsize; /* ELF header size */
        Elf64_Half e_phentsize; /* Size of program header entry */
        Elf64_Half e_phnum; /* Number of program header entries */
        Elf64_Half e_shentsize; /* Size of section header entry */
        Elf64_Half e_shnum; /* Number of section header entries */
        Elf64_Half e_shstrndx; /* Section name string table index */
    } Elf64_Ehdr;

 

 여러 필드가 있지만, 몇가지 특징적인 부분과 Relocation에 관련된 부분만 살펴보겠습니다.

 

 우리는 코드가 존재하는 Section(.text)을 돌면서 Relocation을 수행해야하므로, 위에서 언급한 것 중에 파란색으로 마킹한 부분만 유심히 보면 됩니다.

 

4.Section Header Entries

  1. typedef struct
    {
        Elf64_Word sh_name; /* Section name */
        Elf64_Word sh_type; /* Section type */
        Elf64_Xword sh_flags; /* Section attributes */
        Elf64_Addr sh_addr; /* Virtual address in memory */
        Elf64_Off sh_offset; /* Offset in file */
        Elf64_Xword sh_size; /* Size of section */
        Elf64_Word sh_link; /* Link to other section */
        Elf64_Word sh_info; /* Miscellaneous information */
        Elf64_Xword sh_addralign; /* Address alignment boundary */
        Elf64_Xword sh_entsize; /* Size of entries, if section has table */
    } Elf64_Shdr;

 역시나 많은 필드가 있지만, 특징적인 몇가지와 Relocation에 필요한 부분 위주로 살펴보겠습니다. ^^;;; (자세한 내용이 궁금하신분을은 ELF64  파일 포맷에 대한 Spec 문서를 참조하세요).

 

4.1 Section Type, sh_type

 sh_type은 Section의 타입을 단적으로 보여주는 값입니다. Section의 목적에 따라 아래와 같이 다양한 값을 가질수 있습니다.

SectionType.PNG

 

 일반적으로 코드 및 데이터, 그리고 초기화되지 않은 데이터 Section을 의미하는 .text, .data, .bss의 경우는 SHT_PROGBITS를 가집니다. 즉 Program이 정의한 데이터를 삽입하는 영역이라는 의미입니다.

 하지만 Section의 Relocation 정보를 담고있는 Section의 경우는 SHT_RELA를 가지며, Symbol 정보를 포함하는 Symbol Table Section의 경우는 SHT_SYMBTAB의 값을 가집니다.

 이 타입에 따라서 sh_link 및 sh_info의 의미가 달라지는데, SHT_PROGBITS가 설정된 Section의 경우는 특별한 의미를 지니지 않습니다. 하지만 SHT_SYMTAB이나 SHT_RELA의 경우는 다릅니다. 다름 챕터를 같이 한번 볼까요?

 

4.2 sh_link 및 sh_info의 의미

 sh_link 및 sh_info는 sh_type 값에 따라 아래와 같은 의미를 가집니다.

sh_link.PNG

sh_info.PNG

 

 그렇다면 우리가 Relocation을 수행할 때는 어떤 정보를 봐야 할까요?

 일단 SHT_RELA 타입을 가지는 Section을 검색해서 찾은 후, 해당 Section의 sh_link 필드를 이용해서 Symbol Table Section을 찾습니다. 그리고 SHT_RELA 타입을 가지는 Section의 sh_info 필드를 이용하면 Relocation을 적용해야하는 Section을 찾습니다. 그리고 SHT_RELA 타입을 가지는 Section의 Relocation 정보를 가지고 Relocation을 수행하면 됩니다.

 그런데 Relocation 정보를 가지는 Section의 Relocation 정보와 sh_info가 가리키는 Relocation을 적용해야할 Section 정보만 알면 되지, 왜 Symbol 정보를 봐야할까요? 눈치가 빠른 분들은 이미 감을 잡으셨을 겁니다. ^^ 그것은 바로 Relocation Section에 포함되는 Relocation 엔트리 정보는 Symbol Table에 있는 Symbol 엔트리를 참고해서 실질적으로 얼마를 더하고 뺄지가 정해지기 때문입니다. 자세한 내용은 뒤쪽 챕터에서 설명하도록 하고, 지금은 SHT_RELA 타입을 가지는 Section을 찾고 그 Section의 sh_link 및 sh_info 정보를 이용한다는 것만 알고 넘어가겠습니다. ^^

 

4.3 Section Attribute, sh_flags

 속성 값은 아래와 같이 쓰기가 가능한지, 메모리를 할당받아서 실제로 로딩해야하는 Section인지, 코드가 포함되어있어서 실행가능한지에 대한 내용을 포함합니다. 우리가 Relocation 해야하는 Section(.text) 같은 경우,  .text Section은 메모리에 로딩되어 실행되야하는 Section이기 때문에 SHF_ALLOC 및 SHF_EXECINSTR 값을 설정합니다.  sh_flag는 Relocation 수행 시에는 크게 중요한 필드가 아니므로, 이런 것이 있다는 것만 알고 넘어가겠습니다. ^^;;;

sh_flag.PNG

 

4.4 Special Section

 Section의 속성은 위에서 언급한 각가지 필드를 이용해서 설정할 수 있지만, 관습적으로 사용하는 Section 이름들이 있습니다. 예를 들면 실행파일 코드가 있는 Section은 .text, 데이터가 존재하는 Section은 .data, 초기화 되지 않은 데이터는 .bss와 같이 말이지요. ^^

 아래는 Elf 파일 포멧에서 정의해놓은 Special Section들의 이름과 Type, 그리고 Attribute를 나타낸 테이블입니다.

Special_Section.PNG

 주의해서 볼 점은 .relname과 .relaname 라는 이름을 가진 Section이 있다는 겁니다. Section 이름에서 알 수 있듯이 name이 의미하는 Section의 Relocation 정보를 포함하고 있습니다. Section Type 뿐만 아니라 이름에서까지 Relocation Section 임을 알 수 있게 해놓다니... 여차하면 Section Name으로도 Relocation Section을 찾을 수 있을 것 같습니다(물론 권장하지는 않아요~!!! ^^;;;).

 

5.Symbol Table

 Symbol Table은 코드에서 사용된 각종 변수명 및 함수명, 그리고 Section 이름까지 많은 정보를 포함할 수 있습니다. Symbol Table은 아래와 같은 구조체의 배열로 구성되어 있습니다.

Symbol_Table.PNG

 

 역시나 각 필드의 의미를 살펴봐야겠지요? ^^ Symbol Table은 특별히 모든 필드를 살펴보겠습니다.

 

 Symbol Table의 경우 각 필드의 의미를 정확하게 알고 있어야 Relocation을 수행할 수 있기 때문에, 모든 필드를 다 살펴봤습니다. 이제 Symbol Table 중에 남은 것은 st_info의 상위 4bit 및 하위 4bit에 대한 내용입니다만, 그리 중요한 부분은 아니니 슬쩍 보고 넘어가겠습니다. ^^;;; Relocation을 수행하는데 Symbol Binding(상위 4bit)과 Symbol Type(하위 4bit)은 그리 중요하지 않기 때문이지요. 일단 Symbol이 있으면 코드 내에 참조하는 곳이 있을테니 Relocation 준비를 (거의 무조건) 해야합니다. 다만 st_shndx의 값이 SHN_ABSSHN_COMMON 일 때는 좀 생각을 해야합니다.

 SHN_ABS의 의미는 Value의 값이 Relocation의 영향을 받지않는 절대값이라는 의미이므로 이 Symbol은 Relocation을 하면 안됩니다. SHN_COMMON의 의미는 아직 메모리 공간이 할당되지 않은 Section이라는 의미이므로, 아래에서 언급할 일반적인 방식으로는 Relocation을 수행할 수 없습니다. 만약 하려면 SHN_COMMON으로 설정된 Symbol을 다른 Section에 공간을 할당해주고 Relocation 해야하는데, 이 작업 자체가 쉽지 않고 임의로 할당했다가는 다른 Section의 데이터를 엉망으로 만들 위험이 있습니다.

 따라서 수동으로 Relocation하려면 SHN_COMMON을 Section Index로 갖는 Symbol을 줄일 필요가 있는데, 이것은 배열이나 구조체를 "0"으로 초기화 함으로써 해결할 수 있습니다. ^^;;;; 구조체나 배열을 0으로 초기화하면 .bss나 .data 쪽으로 들어가기 때문이지요. 초기화하지 않으면 대용량의 배열이나 구조체 같은 경우는 SHN_COMMON으로 들어갈 확률이 높습니다.

 

 Symbol Binding(st_info의 상위 4bit) 정보는 아래와 같은 값들이 있습니다(하지만 패스~!!).

Symbol_Bindings.PNG

 Global Symbol인지 Local Symbol인지를 나타내는 값이 정의되어있군요. STB_LOCAL이 Object 파일 내에서만 보인다는 것을 보니 static으로 사용한 변수명쯤 되는 것 같습니다.

 

 Symbol types(st_info의 하위 4bit) 정보는 아래와 같은 값들이 있습니다(역시 중요하지 않으므로 패스~!!).

Symbol_Types.PNG

 변수명 같은 경우는 STT_OBJECT로 표시되고, 함수명 같은 경우는 STT_FUNC로 표시됨을 추측할 수 있습니다. Section 이름도 Symbol Table에 표시되는데, 이런 경우는 STT_SECTION 타입으로 표시되더군요. ^^;;;

 

6.Relocations

 드디어 Relocation 과정에서 핵심이 되는 Section인 Relocation Section까지 넘어왔습니다. Relocation Section은 Relocation에 대한 정보를 가지고 있는  Elf64_Rel 및 Elf64_Rela 구조체의 배열로 이루어져 있으며, 각각은 아래 그림과 같이 구성되어 있습니다.

Relocation_Entry.PNG

 

 Relocation Entry는 아주 중요하므로 전 필드를 다 살펴보겠습니다.

 

 Relocation Entry의 필드중에 r_info를 보시면, Symbol Table의 Symbol 값을 이용하는 것을 볼 수 있습니다. 그럼 Symbol 값과 r_addend 값을 더해서 r_offset이 가리키는 영역에다가 덮어쓰면 될까요? 정답은 "아니요" 입니다. ^^;;;;

 Relocation도 단순히 Symbol 값을 대입하는 것부터, r_addend를 더하고 r_offset 값을 빼는 것같은 다양한 방식으로 계산을 수행합니다. 그럼 어떠한 연산을 수행해야 하는지 어떻게 알 수 있을까요? 그렇습니다. 바로 r_info의 하위 32bit 값인 Relocation Type을 보면 됩니다.

 

 Relocation Type은 아래와 같이 정의되어있습니다. ^^

Relocation_Types.PNG

 표의 가장 오른쪽에 있는 Calculation 항목에 있는 각 항목들은 아래와 같은 의미를 가지고 있습니다.

 

 위 항목 중에 G, GOT, B 그리고 L 항목은 공유 라이브러리(Shared Library)나 특수한 옵션(-fPic 등)을 사용하지 않으면 보기 힘든 항목이므로, 일반적으로 많이 사용되는 S, A, P를 위주로 보시면 됩니다. 위 테이블에 나와있는 계산 과정을 가만히 보면 지극히 간단한 규칙을 가지는 것을 알 수 있습니다. 대부분 계산 방법이 Symbol 값에 r_addend 값을 더하고 r_offset의 값을 빼서 Field 항목에 나와있는 크기만큼을 덮어쓰는 것으로 끝입니다. 다음 문서의 Relocation 수행 예제를 보시면 너무 간단해서 허무해 하실지도 모르겠군요. ^^;;;;

 다음 챕터로 넘어가기 전에 한마디 덧붙이자면, 정의를 보시면 비슷한 방식으로 계산하지만 Relocation이 수행되어야할 크기에 따라 다르게 분류되어 있습니다.. 이것은 x86 어셈블리어 코드가 다양한 크기의 메모리 및 레지스터를 가지고 연산할 수 있기 때문이며, 그로인해 Relocation을 수행해야하는 영역의 크기도 다양해졌기 때문입니다. 이부분은 값을 실제로 삽입할때 잘라서 넣어주면 처리할 수 있습니다.

 

7.마치면서...

 지금까지 Relocation을 수행하기위해 알아야할 ELF64 파일 포맷에 대한 몇가지 내용들을 봤습니다. Relocation을 수행하기 위해서 위 내용들을 달달 외울 필요는 없습니다. 그냥 "알고" 있으면 되는 것지요. 지금까지 표와 말로 많은 설명을 했습니다만, 아직 감이 잘 안오실 겁니다. ^^;;;; 프로그래머에게는 백마디 말보다 한줄의 코드가 더 좋은 법~!!!! 다음 문서(~!!!)에서는 예제 코드를 작성해서 Relocation을 실제로 해보겠습니다. >ㅁ<)-b