ELF 링킹과 로딩 실험

세벌쉭   
   조회 24616   추천 2    

세벌쉭_ELF_링킹과_로딩_실험.doc (412.5K), Down : 3, 2017-07
세벌쉭_ELF_링킹과_로딩_실험.pdf (281.3K), Down : 1, 2017-07

ELF 링킹 과 로딩 실험 

 

+--------------------------------------------------------+

원문서 제목 : Linking and Loading Experiment

원문서 저자 : Department of Computing, Imperial College

원문서 작성일 : ?

문 서 번역일 : 2003. 08. 00 (약간의 내용을 추가했다.)

문 서 번역자 : 광주광역쉬에서 세벌쉭

코 멘 트 : 본 번역본은 순전히 개인적인 호기심에서 번역한 것이다. 

 조금이라도 의심스러운 해석은 원문서를 참조하기 바란다.

+--------------------------------------------------------------------+

 


Objectives

이 연습은 “the Language Processors, Architecture and Operating Systems” 과정을 지원한다. 컴파일러들, 어셈블러들, 링커들, 로더들이 유닉스 운영 시스템상에서 무엇을 하는지에 대한 약간의 경험을 주는 것을 목표로한다. 이 연습을 수행해 보고, 친구들과 토론을 해보기 바란다. 

 


What To Do

다음 파일들을 exercise linkload 명령을 사용해서 당신의 작업 디렉토리로 복사하라. 또는 아래에 나열되어있는 파일들을 다운로드 하라 그리고 이 문서에 나와 있는 내용을 연습해보라. 문서에 제안된 명령들을 시도해 보기 바란다.

assembler.s 매우 간단한 인텔 에셈블리 언어 프로그램

cversion.c 동일한 프로그램의 C 버전 프로그램

writeext.s C 버전을 위해서 몇가지 지원을 제공하기위한 또다른 어셈블리 언어 파일.

example.c 최적화 실험을 위해서 도움이될 약간더 복잡한 C 프로그램.


Looking at assembler.s

리눅스 어셈블러 프로그램들의 개념들은 여러분이 지금까지 보아온 다른 어셈블러 프로그램들과 비슷하다. 리눅스 어셈블러는 msdos와는 다른 문법을 가지고 있다. 부록 “리눅스 피씨 어셈블러 문법”은 여러분이 알아야할 필요한 차이점에 대한 간략한 요약을 포함하고 있다.

assembler.s

.text

.globl _start

_start:

movl $0x4, %eax # eax = write 시스템콜을 위한 값

movl $1, %ebx # ebx = 표준출력을 위한 파일 디스크립터

movl $message,%ecx # ecx = 메시지를 위한 포인터

movl $13, %edx # edx = 메시지의 길이

int $0x80 # 시스템 콜을 호출한다. (1)

movl $0x1, %eax # eax = exit 시스템 콜을 위한 값

int $0x80 # 시스템 콜을 호출한다. (1)

.data

.globl message

message:

.string "Hello world\n" # 메시지를 위한 데이타

 

  1. int 명령은 유닉스 오퍼레이팅 시스템에 요청할 때 사용하는 소프트웨어 인터럽트이다. 레지스터 eax 값은 시스템 콜을 위해서 사용하는 값이다. 이 값은 시스템 콜에 따라 달라진다. 다른 정보들 역시 다른 레지스터를 통해서 전달 되어진다.

     


Assembling assembler.s

유닉스 어셈블러 프로그램을 –o 옵션을 줌으로서 출력 파일의 이름을 지정할 수 있다. 여기서는 assembler.o. 이 출력 파일 이름이다.

$as assembler.s -o assembler.o

결과 파일은 값들과 다른 데이터들이 초기화 되어진 이진 기계 명령들을 포함하고 있다. 현재 출력결과 파일은 실행가능한 파일이 아닌 재배치 가능한 파일이다. Gcc 컴파일러에 –c 옵션을 써서 컴파일만을 수행하여 오브젝트파일을 만드는 것과 같은(실행가능 파일을 만드는 것이 아닌) 결과 파일을 만들어낸다.

file 프로그램은 파일의 내용을 분석하여 파일의 형태(포멧)을 식별한다. 다음과 같이 입력하라.

$file assembler.o

assembler.o: ELF 32-bit LSB relocatable, Intel 80386, version 1, not stripped

리눅스에서 실행가능 코드는 ELF(Excutable and Linking Format) 포멧으로 파일에 저장되어진다. 리눅스는 또한 a.out 포멧(출력 이름 없이 as 프로그램을 사용할 때 나타나는 포멧) 또한 인식한다. 프로그램 strip 프로그램은 실행가능 파일로부터 정보들을 제거한다. 그래서 더 작은 파일로 만든다. 각 실행가능한 파일은 파일의 내용에 관한 정보를 포함하고 있는 고정된 크기의 ELF 헤더로 시작한다. 헤더의 첫번째 부분은 전체 파일을 설명한다. 두번째 부분은 더 많은 정보들을 가지고 있는 두가지 배열을 가르킨다. 헤더는 /usr/include/elf.h 파일내에 C 구조체로 선언되어있다.

typedef struct {

unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */

Elf32_Half e_type; /* Object file type */

Elf32_Half e_machine; /* Architecture */ (1)

Elf32_Word e_version; /* Object file version */

Elf32_Addr e_entry; /* Entry point virtual address */ (2)

Elf32_Off e_phoff; /* Program header table file offset */(3)

Elf32_Off e_shoff; /* Section header table file offset */(4)

Elf32_Word e_flags; /* Processor-specific flags */

Elf32_Half e_ehsize; /* ELF header size in bytes */

Elf32_Half e_phentsize; /* Program header table entry size */ (5)

Elf32_Half e_phnum; /* Program header table entry count */(6)

Elf32_Half e_shentsize; /* Section header table entry size */

Elf32_Half e_shnum; /* Section header table entry count */

Elf32_Half e_shstrndx; /* Section header string table index */

} Elf32_Ehdr;

(1) 프로그램이 어떤 기계에서 실행되어질수 있는지를 언급한다.

(2) 프로그램 집입점이다. 실행을 어디서부터 시작할 것인지에 대한 언급이다.

(3) 프로그램에 관한 정보가 파일 내부 어디에 있는지에 관한 언급이다.

(4) 다른 섹션들에 관한 정보가 파일 내부에 어디에 있는지에 관한 언급이다.

(5) 프로그램에 관한 정보 조각의 크기이다. 재배치가능 파일은 실행 할수 없기 때문에 “프로그램 헤더”를 가지고 있지 않다.

(6) 프로그램에 관한 정보 조각의 수이다.

유닉스의 od(octal dumping) 프로그램을 사용하여 assembler.o 와 같은 비 텍스트 파일 내용을 조사해보자. 다음과 같이 입력하라. Od 프로그램은 아무 옵션없이 실행하게되면 모든 출력은 8진수의 출력 결과를 보여준다. 그래서 8진수 출력 프로그램이라 호칭하게 되었는가보다.

$od (1)-xc (2)-Ax assembler.o

(1) 이 옵션은 16진수출력과 아스키 출력을 요구한다. (역주: -tx2c, 2바이트 단위로 끓어서 출력한다. 1바이트 단위로 출력하기 위해서는 –tx1c로 옵션을 준다)

(2) 이것은 출력의 첫번째 컬럼에 보여주는 주소를 16 진수로 출력해 주기를 요구한다. 각 라인은 16바이트 값을 보여 줄것이다. 출력 결과는 다음과 비슷할 것이다.

1. ELF 파일을 식별하기 위한 값.

2. 파일은 32비트 워드의 값을 포함하고 있다. 워의 가장 하위 바이트가 워드 상에서 가장 하위 주소에 위치해 있다: 소위 “리틀 에디언”이라고 불린다. 다른 구조를 가진 머신이 프로그램을 실행 할 수 없다면, 다른 구조를 가지는 머신들은 파일을 식별하고 읽을 수 있다.

3. 인텔386 cpu(또는 호환가능한 호환기종)를 위한 재배치가능한 파일이다. 재배치가능 파일은 프로그램 섹션들을 가지고 있지 않다.

4. 실행하기위한 첫번째 명령의 주소는 0 이다. 값은 나중에 채워진다.

5. 실행가능한 코드 섹션을 설명(묘사)하는 섹션 헤더. 헤더를 식별하기위해서 섹션 5의 시작 위치에 이름 필드의 값을 추가한다. 그리고 문자열(유닉스에서 text는 프로그램 코드를 의미한다.)을 탐색한다.

6. 프로그램을위한 데이터를 묘사하는 섹션 헤더. 섹션 헤더 정의에 주어지는 첫번째 섹션 헤더는 사용되어지지 않는다. 또는 여기에 프린트되어 있지 않다. 3번째 헤더는 재배치 정보를 가르킨다(프로그램이 그것을 다른 주소들로 로드하기위해서 어떻게 수정되어져야 하는지에 대한 상세)

섹션 헤더에 대한 정의

/* Section header. */

typedef struct {

Elf32_Word sh_name; /* Section name (string tbl index) */

Elf32_Word sh_type; /* Section type */

Elf32_Word sh_flags; /* Section flags */

Elf32_Addr sh_addr; /* Section virtual addr at execution */

Elf32_Off sh_offset; /* Section file offset */

Elf32_Word sh_size; /* Section size in bytes */

Elf32_Word sh_link; /* Link to another section */

Elf32_Word sh_info; /* Additional section information */

Elf32_Word sh_addralign; /* Section alignment */

Elf32_Word sh_entsize; /* Entry size if section holds table */

} Elf32_Shdr;

 

 

/* Legal values for sh_type (section type). */

#define SHT_NULL 0 /* Section header table entry unused */

#define SHT_PROGBITS 1 /* Program data */

#define SHT_SYMTAB 2 /* Symbol table */

#define SHT_STRTAB 3 /* String table */

#define SHT_RELA 4 /* Relocation entries with addends */

#define SHT_HASH 5 /* Symbol hash table */

#define SHT_DYNAMIC 6 /* Dynamic linking information */

#define SHT_NOTE 7 /* Notes */

#define SHT_NOBITS 8 /* Program space with no data (bss) */

#define SHT_REL 9 /* Relocation entries, no addends */

#define SHT_SHLIB 10 /* Reserved */

#define SHT_DYNSYM 11 /* Dynamic linker symbol table */

#define SHT_NUM 12 /* Number of defined types. */

 

위의 5번, 6번에대한 설명이 뜻하는바에 대해서 더 알아보고 ELF 헤더의 내용들과 섹션 헤더의 내용들에 대해서 좀더 자세히 알아 보도록 하자. 우선 각 구조체의 옵셋 값을 알아보자. 왜 옵셋값이 다음과 같은 값을 가지는지에 대해서는 관련 문서들을 확인해보기 바란다. 또한 사용된 od 프로그램의 사용법에 대해서도 관련 문서나 해당 프로그램의 도움말을 참조하기 바란다.

// ELF 헤더의 정의

// EI_NIDENT 는 16으로 정의되어져 있다.

typedef struct {

unsigned char e_ident[EI_NIDENT]; 0 // 구조체 내부의 옵셋 값이다.

Elf32_Half e_type; 16

Elf32_Half e_machine; 18

Elf32_Word e_version; 20

Elf32_Addr e_entry; 24

Elf32_Off e_phoff; 28

Elf32_Off e_shoff; 32

Elf32_Word e_flags; 36

Elf32_Half e_ehsize; 40

Elf32_Half e_phentsize; 42

Elf32_Half e_phnum; 44

Elf32_Half e_shentsize; 46

Elf32_Half e_shnum; 48

Elf32_Half e_shstrndx; 50

} Elf32_Ehdr; //ELF 헤더는 총 52 바이트 길이를 갖는다.

 

// 섹션 헤더의 정의

typedef struct {

Elf32_Word sh_name; 0 //섹션 헤더에서의 옵셋값

Elf32_Word sh_type; 4

Elf32_Word sh_flags; 8

Elf32_Addr sh_addr; 12

Elf32_Off sh_offset; 16

Elf32_Word sh_size; 20

Elf32_Word sh_link; 24

Elf32_Word sh_info; 28

Elf32_Word sh_addralign; 32

Elf32_Word sh_entsize; 36

} Elf32_Shdr; // 총 40바이트의 길이를 가지고 있다.

 

 

  1. 우선 ELF 헤더의 첫번째 멤버인 unsigned char e_ident[16]; 에 대해 알아보자. 보시다 싶이 16바이트 크기의 문자 배열이다.

    bash-2.05a$ od -Ad -tx1c a.o -N16

    0000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00

    177 E L F 001 001 001 \0 \0 \0 \0 \0 \0 \0 \0 \0

    여기서 177은 8진수이다. 파일의 첫번째 4바이트값이 위와 같은 값을 가진다면 ELF 파일임을 표시한다. 나머지 값들은 관련문서를 참조하자.

  2. 두번째 멤버에 대해서 알아보자.

    Elf32_Half e_type; 16

    보시다 싶이 이녀석은 옵셋 16에서 시작하고 2바이트 값을 갖는다.

    bash-2.05a$ od -Ad -tx2 a.o -N16 -j16

    0000016 0001 0003 0001 0000 0000 0000 0000 0000

    출력 결과는 1값을 갖는다. 현재 a.o 파일은 재배치가능 파일(실행가능 파일이 아니라)이라는 것을 보여준다. 더 진행하기 전에 여기서 사용된 od 프로그램의 옵션에 대해서 잠시 알아보자.

    -Ad : 맨처음에 출력하는 주소를 10진수로 출력하라.

    -tx2 : 출력형태는 16진수로 하되 2바이트씩 끓어서 출력하라(e_type 멤버는 2바이트 크기)

    a.o : 우리가 조사할 파일 이름

    -N16 : 파일 전체를 출력하지 말고 단지 16바이트만을 출력하라.

    -j16 : 파일의 옵셋 16바이트 위치부터 출력하라.(e_type은 16바이트 옵셋 위치에 있다)

    세번째 멤버인 e_machine 은 3의 값을 가지고 있다.(인텔 386 cpu를 표시하는가 보다)

  3. 한꺼번에 조사해보자. 모두 4바이트 크기를 가지고 있는 멤버이다.

    Elf32_Word e_version; 20

    Elf32_Addr e_entry; 24

    Elf32_Off e_phoff; 28

    Elf32_Off e_shoff; 32

    Elf32_Word e_flags; 36

    bash-2.05a$ od -Ad -tx4 a.o -N32 -j20

    0000020 00000001 00000000 00000000 00000094

    0000036 00000000 00000034 00280000 00050008

    각각 다음 값을 가지고 있음을 알 수 있다.

    버전은 : 1

    진입 포인트는 : 0

    프로그램 헤더의 위치는 : 0

    섹션 헤더의 위치는 : 94(파일의 시작으로부터 옵셋 94 바이트 째부터)

    이 파일의 플래그는 : 0

  4. 역시 한꺼번에.. ^.^, 모두 2바이트 크기를 가지고 있다. 옵셋 40 바이트째 부터이다.

    Elf32_Half e_ehsize; 40

    Elf32_Half e_phentsize; 42

    Elf32_Half e_phnum; 44

    Elf32_Half e_shentsize; 46

    Elf32_Half e_shnum; 48

    Elf32_Half e_shstrndx; 50

    bash-2.05a$ od -Ad -tx2 a.o -N32 -j40

    0000040 0034 0000 0000 0028 0008 0005 04b8 0000

    0000056 bb00 0001 0000 00b9 0000 ba00 000d 0000

    ELF 헤더 사이즈 34이다. 앗 ! 52 바이트 크기인데. 그렇다. 출력 결과는 16진수이다. 다시 조사해 보자.

    bash-2.05a$ od -Ad -td2 a.o -N32 -j40

    0000040 52 0 0 40 8 5 1208 0

    0000056 -17664 1 0 185 0 -17920 13 0

    ELF 헤더의 크기 : 52 바이트

    프로그램 헤더의 엔트리 사이즈 : 0 (현재 재배치가능 파일이므로 프로그램 헤더는 없다)

    프로그램 헤더의 개수 : 0(역시 위와 같다)

    섹션 헤드의 엔트리 사이즈 : 40(위에 알아본바와 같이 40바이트의 크기를 갖는다)

    섹션 헤더의 개수 : 8 (섹션 헤더의 개수는 8개이다. 그리고 위에서 알아본바와 같이 그 위치는 파일의 처음에서부터 0x94바이트(148) 옵셋에서 시작한다.)

    각 섹션 헤더가 어떤 섹션 헤더인지를 보관하고 있는 섹션의 인덱스 : 5 (총 8개의 섹션 중에 5번째 섹션에 문자열을 보관하고 있다. 0번째부터 시작한다.)

  5. 앞으로의 조사는 섹션 헤더에 집중 될것이므로 지금까지 조사결과를 정리해보자.

    섹션 헤더의 시작 위치 : 파일의 시작으로부터 옵셋 148

    각 섹션 헤더의 크기 : 40 바이트

    섹션 헤더의 개수 : 8개

    각 섹션 헤더가 어떤 섹션인지를 표시하는 문자열을 가지고 있는 섹션의 인덱스 : 5

  6. 0번째 섹션 헤더를 조사해보자. 옵셋148 바이트에서 시작하고 40바이트의 크기를 갖는다. 총 10개의 멤버를 가지고 각각의 멤버는 모두 4바이트 크기를 가지고 있다.

    bash-2.05a$ od -Ad -td4 a.o -N40 -j148 -v

    0000148 0 0 0 0

    0000164 0 0 0 0

    0000180 0 0

    0000188

    모두 0값을 가진다. 그렇다. 첫번째 섹션 헤더의 값은 항상 모든 멤버가 0의 값을 가진다.

    1번째 섹션 헤더의 값을 조사해 보자.

    bash-2.05a$ od -Ad -td4 a.o -N40 -j`expr 148 + 40 '*' 1` v

    0000188 31 1 6 0

    0000204 52 32 0 0

    0000220 4 0

    sh_name; 이섹션 헤더가 어떤 이름을 가지는지에 대한 문자열 위치에 대한 옵셋이다. 31이다.

    sh_type; 이 섹션 헤더가 어떤 스타일에 섹션 헤더인지를 표시한다. 1이다. 이 멤버가 어떤 값을 가질수 있는지에 대해서는 미리 정의되어져 있다. 1값을 가진다. 의미하는 바는 “섹션은 프로그램에 의해서 정의되어진 정보를 가진다. 포멧과 의미는 단지 그 프로그램에 의해서만 결정되어진다”라는 의미를 가진다고 한다.

    sh_flags; 6의 값을 가진다.(2+4) 의미하는 바는 “섹션은 프로세스 실행 동안에 메모리를 차지한다.2 섹션은 실행가능한 머신 명령들을 포함하고 있다.4”이다.

    sh_addr; 0 값을 가진다. 이 멤버의 의미는 “만일 섹션이 프로세스의 메모리 이미지 안에 나타날 것이라면, 이 멤버는 섹션의 첫번째 바이트가 위치해야할 주소를 부여한다”이라고 한다.

    sh_offset; 52값이다. 이멤버가 의미하는 바는 “이 멤버의 값은 파일의 시작으로부터 섹션에 대한 첫번째 바이트 까지의 바이트 옵셋이다”라고 한다. 여기서 잠시 정리해 보자. 파일의 첫부분에는 52바이트 크기의 ELF 헤더가 위치해 있다. 이 섹션의 실제 위치는 옵셋 52부터 시작한다는 것이다. 그리고 한가지더 각 섹션 헤더들은 실제 섹션값들이 어디에 있는지를 나타내주있다는 것이다. 즉, 섹션 헤더 테이블은 각각에 실제의 데이타를 가지고 있는 것이 아니라 실제 데이터가 있는 곳에대한 정보와 관련된 각종 정보들을 표시하는 테이블 인것이다.

    sh_size; 이섹션의 크기는 32바이트이다.

    sh_link; sh_info; 이 두멤버는 sh_type에 따라서 다른 의미를 갖는다. 일단 넘어 가자.

    sh_addralign; 4바이트의 얼라인먼트를 갖는다. 4바이트 단위로 경계맞추기를 한다는 뜻이다. sh_entsize; 섹션은 여러 개의 엔트리를 가질수 있는데. 여기에 그 크기가 지정되면 각 엔트리는 해당 고정크기를 갖는다.

  7. 1번째 섹션은 어떤 값을 가지고 있으며, 그 섹션의 이름은 무엇인지에 대해서 알아보자. 위에서 5번에 관한 설명이 이녀석에 관한 것이었군요. 그럼 실제 섹션의 내용은 어떤 것을 가지고 있는지와 이 섹션의 이름은 무엇인지에 대해서만 알아보자. 우선 섹션 헤더의 이름을 가지고 있는 섹션의 번호는 5번이라고 했고(ELF 헤더를 조사할 때 마지막 멤버값이 이 값을 가지고 있었다), 이섹션의 이름은 5번 섹션에서 31번째부터의 문자열이라고 했다.

    우선 5번 섹션이 실제 어디에 위치해 있는지 알아보자.

    bash-2.05a$ od -Ad -td4 a.o -N40 -j`expr 148 + 40 '*' 5` v

    0000348 17 3 0 0

    0000364 100 48 0 0

    0000380 1 0

    100 바이트 째부터 시작한다. 크기는 48바이트이다. 시작위치부터 대충 60바이트를 조사해보자. 이 섹션은 문자열을 가지고 있으므로 문자열 타입으로 조사를 해보자.

    bash-2.05a$ od -Ad -tc a.o -N60 -j100 -v

    0000100 \0 . s y m t a b \0 . s t r t a b

    0000116 \0 . s h s t r t a b \0 . r e l .

    0000132 t e x t \0 . d a t a \0 . b s s \0

    0000148 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0

    예상한대로 문자열을 가지고 있다. 첫번째 옵셋값은 항상 0을 가지고 있다. 그럼 1번째 섹션의 이름을 가르키는 위치는 옵셋 31번째 부터라고 했다. 31번째 부터는 “.text”문자열을 가지고 있다. 즉, 1번째 섹션의 이름은 “.text”이다. 일반적으로 이런 이름을 갖는 세그먼트는 실행코드가 있는 영역이다. 이와 같은 몇몇 세그먼트의 이름들은 미리 정의되어져 있다. 역시 관련 문서를 참조하기 바란다.

    이번에는 1번째 섹션이 어떤 내용을 가지고 있는지 알아보자. 1번째 섹션은 옵셋 52부터 시작한다고 했고 아마도 프로그램의 실제 코드를 가지고 있지 않나 유추되어진다. 또한 그 크기는 시작부터 32바이트 크기를 가지고 있다. 다음은 조사 결과다.

    bash-2.05a$ od -Ad -tx1 a.o -N32 -j52 -v

    0000052 b8 04 00 00 00 bb 01 00 00 00 b9 00 00 00 00 ba

    0000068 0d 00 00 00 cd 80 b8 01 00 00 00 cd 80 8d 76 00

    0000084

    이게 멀까 아마도 프로그램의 바이너리 코드일것이다. 어떤 뜻일까. 우리가 추정하는 바이너리 코드를 가지고 있는 것이 맞을까. ? 좀더 조사해 보자.

    bash-2.05a$ objdump --disassemble a.o

    a.o: file format elf32-i386

    Disassembly of section .text:

    00000000 <_start>:

    0: b8 04 00 00 00 mov $0x4,%eax

    5: bb 01 00 00 00 mov $0x1,%ebx

    a: b9 00 00 00 00 mov $0x0,%ecx

    f: ba 0d 00 00 00 mov $0xd,%edx

    14: cd 80 int $0x80

    16: b8 01 00 00 00 mov $0x1,%eax

    1b: cd 80 int $0x80

    1d: 8d 76 00 lea 0x0(%esi),%esi

    조사 결과와 같은 뜻을 가지고 있고. 이 조사 결과는 위의 조사 결과와 일치하는 바이너리 값을 가지고 있다.

  8. 3번째와 4번째 섹션을 조사해보자. 5번째 섹션은 각 섹션들의 이름이 무엇인지에 대한 문자열을 가지고있다고 했다.

    bash-2.05a$ for i in `seq 3 1 4`; do od -Ad -td4 a.o -N40 -j`expr 148 + 40 '*' $i` v; done

    0000268 37 1 3 0

    0000284 84 16 0 0

    0000300 4 0

    0000308

    0000308 43 8 3 0

    0000324 100 0 0 0

    0000340 4 0

    0000348

    3번째 섹션의 이름은 섹션 헤더 스트링 섹션에서 37번째부터의 문자열이고, 이 섹션은 1번 스타일이고 실제 섹션이 있는곳은 옵셋 84바이트 위치이고 그 크기는 16바이트라고 말하고 있다. 또한 4바이트의 경계맞추기를 실행한다고 되어있다.

    우선 섹션 이름을 보자.

    bash-2.05a$ od -Ad -tc a.o -N60 -j100 -v

    0000100 \0 . s y m t a b \0 . s t r t a b

    0000116 \0 . s h s t r t a b \0 . r e l .

    0000132 t e x t \0 . d a t a \0 . b s s \0

    0000148 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0

    37번째이면 “.data”이다. 그럼 이섹션의 실제 내용은 어떤값을 가지고 있을까 ? 우리의 어셈블리 코드를 살펴보면

    .data

    .globl message

    message:

    .string "Hello world\n" # 메시지를 위한 데이터

    라는 내용을 포함하고 있다. 이 “.data” 세그먼트가 포함하고 있는 내용, 즉, “Hello world\n”값을 가지고 있지않을까 추측해 본다. 실제로 그런지 한번 조사해 보자.

    bash-2.05a$ od -Ad -tc a.o -N16 -j84 -v

    0000084 H e l l o w o r l d \n \0 \0 \0 \0

    0000100

    오마이 갓 ! 우리에 추측이 맞았다. 기쁘지 아니할 수 없다. 여러분은 ? 실제 문자열은 첫번째 0이 나타나는 곳까지이고 나머지 3개의 0값은 우리의 섹션이 4바이트 얼라인먼트를 행한다고 했으므로 덧붙여진값이 아닌가 추측해 본다.

    이제 4번째 섹션이다. 이섹션은 옵셋 100부터 시작하고 그 크기는 0 값을 가진다고 했다. 실제 파일에서 내용이 없는것이다. 그리고 이 섹션의 타입은 8을 가지고 있다. 8값은 SHT_NOBITS 와 동일한 값이다. 그 관련 의미에 대해서는 관련문서를 찾아보시기를 바라마지 않는다. 이 섹션의 이름은 “.bss”이다. 비 초기화 영역이다.

    다음 5번째 섹션. 이섹션은 이미 여러 번 언급되어졌다. 현재, 각 섹션이 어떤 이름을 갖고 있는지에대한 문자열 데이터를 가지고 있는것으로 이미 판결이 났다. ^.^

    현재 각 섹션을 조사하고 있는 본 내용은 번역자가 오리지날 문서에 추가하고 있는 내용이다. 본인도 나머지 섹션이 어떤 내용을 가지고 있는지 무척이나 궁금하고, 또한 8개 섹션을 가지고 있다고 했으므로 나머지 3개에 대해서도 한번 조사를 진행해보자. 여러분은 궁금하지 않는가 ? 아 ! 그리고 보니 8개의 섹션을 가지고 있다고 했고. 5번째 까지는 이미 조사했으므로 2개의 섹션이 남았다.(섹션 번호는 0번부터 시작 했다는 것을 기억 하겠죠)

  9. 자 ! 이번에는 6번째, 그리고 마지막 섹션에 대해서 진행을 해보도록 하자.

    bash-2.05a$ for i in `seq 6 1 7`; do od -Ad -td4 a.o -N40 -j`expr 148 + 40 '*' $i` v; done

    0000388 1 2 0 0

    0000404 468 96 7 4

    0000420 4 16

    0000428

    0000428 9 3 0 0

    0000444 564 16 0 0

    0000460 1 0

    0000468

    bash-2.05a$ od -Ad -tc a.o -N60 -j100 -v

    0000100 \0 . s y m t a b \0 . s t r t a b

    0000116 \0 . s h s t r t a b \0 . r e l .

    0000132 t e x t \0 . d a t a \0 . b s s \0

    0000148 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0

    6번째 섹션 : 섹션 이름은 옵셋 1부터 시작한다. 즉, “.symtab” 이다. 이 섹션의 타입은 2번이라고 한다. 관련문서를 찾아보면 2는 SHT_STRTAB와 같은 값이고, 그 의미는 “섹션은 스트링 테이블을 가진다”라고 되어있다. 어찌 되었든 이 섹션의 내용을 살펴보자. 문자열 값을 가진다고 했으니까. 문자열 형식으로 조사해 보자. 시작 위치는 468이고 그 크기는 96바이트이다.

    bash-2.05a$ od -Ad -tc a.o -N96 -j468 -v

    0000468 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0

    0000484 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 003 \0 001 \0

    0000500 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 003 \0 003 \0

    0000516 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 003 \0 004 \0

    0000532 001 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 020 \0 001 \0

    0000548 \b \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 020 \0 003 \0

    0000564

    음… 특별한 값을 가지고 있지 않다. 관련 문서를 보면 문자열 테이블은 어떤 변수가 abc 라는 이름을 가질 때(예, char abc; 라는 정의 또는 선언) 그 문자열을 저장하고있는 문자열 테이블 섹션이라고 말하고 있는 것 같다. 이와 같은 선언이 있다면 이 섹션은 “abc”라는 문자열을 가지고 있을 것이다. 우리의 예제 프로그램은 이러한 선언을 가지고 있지 않아서 아무 내용이 없는 것 같다. 아닌데…. 음. _start, 와 message 라는 선언을 가지고 있는 것 같은데…. 음… 좀더 조사해 보자.

    bash-2.05a$ od -Ad -tc a.o -N200 -j468 -v

    0000468 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0

    0000484 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 003 \0 001 \0

    0000500 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 003 \0 003 \0

    0000516 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 003 \0 004 \0

    0000532 001 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 020 \0 001 \0

    0000548 \b \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 020 \0 003 \0

    0000564 \0 _ s t a r t \0 m e s s a g e \0

    0000580 \v \0 \0 \0 001 005 \0 \0

    먼가 내용이 나타났다. 하지만 이섹션의 크기는 96 바이트라고 했다. 96바이트 까지만이 실제 이섹션의 내용인 것이다. 그럼 섹션 헤더의 내용을 좀더 살펴 보도록 하자.

    0000388 1 2 0 0

    0000404 468 96 7 4

    0000420 4 16

    섹션의 이름 위치 : 옵셋 1

    섹션의 타입 : 2. 즉 스트링 테이블이라는 것을 표시하고 있다.

    섹션의 플래그 : 0

    섹션의 주소 : 0

    섹션의 옵셋 : 옵셋 468

    섹션의 크기 : 96 바이트

    섹션의 링크 : 7

    섹션의 인포 : 4

    섹션의 얼라인먼트 : 4

    섹션의 각 엔트리 사이즈 : 16바이트

    이런 실수 ! 섹션 타입 : 2는 스트링 테이블이 아니라 심볼테이블이다. 섹션 6에 관한 위쪽 설명을 모두 취소하고 다시 한번 알아보자. SHT_SYMTAB 과SHT_DYNSYM 각각 2, 6의 값을 가지고 관련 문서에서 그 의미는 “심볼 테이블을 가진다”라고 되어있다. 심볼 테이블이라. 심볼 테이블의 각 엔트리에 대한 정의는 다음과 같다고 한다.

    typedef struct {

    Elf32_Word st_name; 0 // 옵셋값

    Elf32_Addr st_value; 4

    Elf32_Word st_size; 8

    unsigned char st_info; 12

    unsigned char st_other; 13

    Elf32_Half st_shndx; 14

    } Elf32_Sym; // 이구조체의 전체 크기는 16바이트이다.

    각 멤버는 다음과 같은 의미를 갖는다.

st_name 이 멤버는 심볼 네임들에 대한 문자 표현을 가지고 있는 오브젝트 파일의 심볼 문자열 테이블 내부에 대한 인덱스를 가지고 있다.

st_value 이 멤버는 관련된 심볼에 값을 부여 한다. 내용에 의존 적이다. 이것은 절대값, 주소 등등을 가질수 있다. 자세한 사항은 아래에 설명한다.

st_size 많은 심볼들은 관련된 사이즈를 가지고 있다. 예를 들면 데이타 오브젝트의 사이즈는 오브젝트 내부에 포한되어진 바이트의 수이다. 만일 심볼이 크기가 없거나 알려져 있지 않는 크기라면 이 멤버는 0을 가지고 있다.

st_info 이 멤버는 심볼의 타입과 특성을 결합하는 것을 지정한다. 값의 리스트와 의미는 아래에 설명한다. 다음 코드는 값들을 어떻게 처리할지를 보여 준다.

st_other 이 멤버는 현재 0을 가지고 있다. 정의된 의미를 가지고 있지 않다.

st_shndx 모든 심볼 테이블 엔트리는 어떤 섹쎤과 연관되어 정의되어진 것이다. 이 멤버는 관련 섹션 헤더 테이블 인덱스를 가지고 있다. 그림 1-7 그리고 관련된 문장이 설명하는것과 같이, 어떤 섹션 인덱스들은 특별한 의미들을 가지고 있다.

섹션의 전체 크기는 96바이트이고 이 섹션의 각 엔트리는 16바이트를 갖게됨으로 6개의 엔트리를 가질 것이다. 위쪽 설명에서 섹션 헤더의 링크 와 인포는 섹션헤더의 타입에 따라 다른 의미를 가진다고 했다. 관련 문서를 보니 타입이 SHT_SYMTAB 와 SHT_DYNSYM 값을 가질때는 링크와 인포는 오퍼레이팅 시스템에 따라서 또다른 의미를 갖는단다. 더 이상 설명이 없다. 헉 !-------- 아니다 좀더 조사해 보니 다음과 같은 내용이 있다.

sh_type
sh_link
sh_info
SHT_SYMTAB 
관련된 스트링 테이블의 
마지막 지역 심볼 
SHT_DYNSYM 
섹션 헤더 인덱스
(STB_LOCAL 바인딩을 갖는)의
 
 
심볼 테이블 인덱스 보다 
 
 
큰 값을 갖는다. 

또한 이때 sh_info의 의미는 “첫번째 비지역 심볼을 위한 심볼 테이블 인덱스를 가지고 있다.” 와 동일한 의미라고 한다.

이들 조사 결과를 가지고 다시 정리해 보자

섹션의 이름 위치 : 옵셋 1. 즉, 섹션 헤더 문자열 테이블에서 옵셋 1위치에 이름이 있다.

섹션의 타입 : 2. 즉, 이섹션은 심볼 테이블에 관한 섹션이다.

섹션의 플래그 : 0

섹션의 주소 : 0

섹션의 옵셋 : 옵셋 468. 즉 섹션의 실제 위치는 파일의 시작에서부터 옵셋 468바이트 이다.

섹션의 크기 : 96 바이트크기. 각각 16바이트 크기이므로 6개의 엔트리를 갖는다.

섹션의 링크 : 7. 이 섹션과 관련된 문자열 테이블의 위치는 7번 섹션헤더서 알아 보거라.

섹션의 인포 : 4. 첫번째 비지역 심볼을 위한 심볼 테이블 인덱스. 여기서는 “.bss”에대한 인덱스이다.

섹션의 얼라인먼트 : 4. 4바이트 단위로 얼라인먼트를 행한다.

섹션의 각 엔트리 사이즈 : 16바이트

아무튼 현재까지 파악된 정보만 가지고 여행을 계속해보자.

우선 이 섹션에 관련된 문자열을 가지고 있는 섹션 7번의 섹션 헤더의 내용과 실제 그 데이터는 어떤 값을 가지고 있는지 알아보자.

bash-2.05a$ od -Ad -td4 a.o -N40 -j`expr 148 + 40 '*' 7` -v

0000428 9 3 0 0

0000444 564 16 0 0

0000460 1 0

bash-2.05a$ od -Ad -tc a.o -N16 -j564 -v

0000564 \0 _ s t a r t \0 m e s s a g e \0

0000580

bash-2.05a$ od -Ad -tc a.o -N60 -j100 -v

0000100 \0 . s y m t a b \0 . s t r t a b

0000116 \0 . s h s t r t a b \0 . r e l .

0000132 t e x t \0 . d a t a \0 . b s s \0

0000148 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0

즉 이 섹션(7번째 섹션)의 섹션 이름은 “.strtab”이다. 실제 가지고 있는 내용은 위의 출력 결과와 같다.

그럼 6번째 섹션의 실제 내용을 담고 있는 섹션을 조사해보자.

첫번째 3개 컬럼만 참조하자. 첫번째 3개의 맴버는 4바이트 크기를 가지고 있다.

bash-2.05a$ od -Ad -td4 a.o -N96 -j468 -v;

0000468 0 0 0 0

0000484 0 0 0 65539

0000500 0 0 0 196611

0000516 0 0 0 262147

0000532 1 0 0 65552

0000548 8 0 0 196624

0000564

여기서는 마지막 4개 칼럼만 참조하자. 마지막 3개의 멤버는 1바이트, 1바이트, 2바이트의 크기를 가지고있다.

bash-2.05a$ od -Ad -td1 a.o -N96 -j468 -v;

0000468 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

0000484 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 0

0000500 0 0 0 0 0 0 0 0 0 0 0 0 3 0 3 0

0000516 0 0 0 0 0 0 0 0 0 0 0 0 3 0 4 0

0000532 1 0 0 0 0 0 0 0 0 0 0 0 16 0 1 0

0000548 8 0 0 0 0 0 0 0 0 0 0 0 16 0 3 0

0000564

0번째 엔트리 : 첫번째 엔트리는 모든 값이 0을 가진다.(미리 예약되어 있다.)

1번째 엔트리 :

st_name : 0 즉 이름을 가지고 있지 않다.

st_value : 0

st_size : 0

st_info : 3. 상위 4비트가 바인드 값, 하위 4비트가 타입.

바인드 0(로컬바인딩), 타입 3(섹션 타입)

st_other : 0. 이값은 항상 0이다.(아직 특정 의미가 부여되어 있지 않다)

st_shndx : 1. 이 엔트리와 관련된 섹션 헤더테이블은 1번이다. “.text”

2번째 엔트리 :

st_name : 0 즉 이름을 가지고 있지 않다.

st_value : 0

st_size : 0

st_info : 3. 바인드 0(로컬바인딩), 타입 3(섹션 타입)

st_other : 0. 이값은 항상 0이다.(아직 특정 의미가 부여되어 있지 않다)

st_shndx : 3. 이 엔트리와 관련된 섹션 헤더테이블은 3번이다. “.data”

3번째 엔트리 :

st_name : 0 즉 이름을 가지고 있지 않다.

st_value : 0

st_size : 0

st_info : 3 바인드 0(로컬바인딩), 타입 3(섹션 타입)

st_other : 0. 이값은 항상 0이다.(아직 특정 의미가 부여되어 있지 않다)

st_shndx : 4. 이 엔트리와 관련된 섹션 헤더테이블은 4번이다. “.bss”

4번째 엔트리 :

st_name : 1 즉 _start 이름을 갖는다.

st_value : 0

st_size : 0

st_info : 16. 바인드 0(로컬바인딩), 타입 1(오브젝트 타입)

st_other : 0. 이값은 항상 0이다.(아직 특정 의미가 부여되어 있지 않다)

st_shndx : 1. 이 엔트리와 관련된 섹션 헤더테이블은 1번이다. “.text”

5번째 엔트리 :

st_name : 8 즉 message 이름을 갖는다.

st_value : 0

st_size : 0

st_info : 16. 바인드 0(로컬바인딩), 타입 1(오브젝트 타입)

st_other : 0. 이값은 항상 0이다.(아직 특정 의미가 부여되어 있지 않다)

st_shndx : 3. 이 엔트리와 관련된 섹션 헤더테이블은 3번이다. “.data”

의미 복잡한거 ! 이정도까지만 조사해보자.

7번째 섹션에 대한 조사는 자연 스럽게 이미 진행 되었다.

  1. 2번째 섹션에 대한 조사가 빠졌다. 계속 진행해 보자.

    bash-2.05a$ od -Ad -td4 a.o -N40 -j`expr 148 + 40 '*' 2` -v

    0000228 27 9 0 0

    0000244 580 8 6 1

    0000260 4 8

    0000268

    bash-2.05a$ od -Ad -tc a.o -N60 -j100 -v

    0000100 \0 . s y m t a b \0 . s t r t a b

    0000116 \0 . s h s t r t a b \0 . r e l .

    0000132 t e x t \0 . d a t a \0 . b s s \0

    0000148 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0

    2번째 섹션의 이름은 27번째부터 시작하는 문자열인 “.rel.text”이다. 내용은 ? 음. 이 섹션도 복잡할 것 같은데 다시 자세히 알아보자.

    섹션의 이름 위치 : 옵셋 27. 즉 “.rel.text”

    섹션의 타입 : 9. SHT_REL(9) 섹션은 오브젝트 파일들의 32비트 종류를 위한 Elf_Rel 과 같은 명확한 가수(addend) 없이 재배치 엔트리들을 가진다

    섹션의 플래그 : 0

    섹션의 주소 : 0

    섹션의 옵셋 : 옵셋 580

    섹션의 크기 : 8 바이트. 엔트리 사이즈가 8이므로 엔트리의 개수는 1개.

    섹션의 링크 : 6. 관련 심볼 테이블에 대한 섹션 헤더 인덱스.

    섹션의 인포 : 1. 재배치가 적용될 섹션에대한 섹션 헤더 인덱스

    섹션의 얼라인먼트 : 4

    섹션의 각 엔트리 사이즈 : 8바이트

sh_type
sh_link
sh_info
SHT_DYNAMIC 
그 섹션안의 엔트리들에 의해서

 
사용되어진 스트링 테이블에 대한
0
 
섹션 헤더 인덱스이다.

SHT_HASH 
해쉬 테이블이 적용하기위한

 
심볼 테이블에 대한
섹션 헤더 인덱스이다.
0
SHT_REL 
관련된 심볼 테이블에 대한
재배치가 적용될 섹션에대한
SHT_RELA 
섹션 헤더 인덱스이다.
섹션 헤더 인덱스이다.
SHT_SYMTAB 
이 정보는 오퍼레이팅 시스템
이 정보는 오퍼레이팅
SHT_DYNSYM 
특성이다.
시스템 특성이다.
other 
SHN_UNDEF
0

어째든 내용은 어뗗게 되는지 조사해보자. 재배치 섹션에 관한 엔트리에 대한 정의는 다음과 같다.

typedef struct {

Elf32_Addr r_offset;

Elf32_Word r_info;

} Elf32_Rel;

r_offset 이 멤버는 재배치 행위를 적용하기 위한 위치를 부여 한다. 재배치가능 파일에서 값은 섹션의 시작으로 부터 재배치에의해 영향을 받는 저장 단위까지 바이트 옵셋이다. 실행가능 파일이나 공유 오브젝트 에서, 값은 재배치에의해서 영향을 받는 저장 단위의 가상 주소이다.

r_info 이 멤버는 재배치가 이루어져야만하는 관련된 심볼 테이블 인덱스 그리고 적용하기위한 재배치 타입을 부여한다. 예를 들면, 콜 명령의 재배치 엔트리는 호출되어지는 함수의 심볼 테이블 인덱스를 가지고 있을 수 있다. 만일 인덱스가 미정의된 심볼 인덱스인 STN_UNDEF 이라면, 재배치는 "심볼 값"에서 처럼 0을 사용한다. 재배치 타입들은 프로세서 지정 이다. 그것들의 행동 양상에대한 설명을 프로세서 부록에 나타나 있다. 프로세서 부록안에 있는 설명이 재배치 엔트리의 재배치 타입이나 심볼 테이블 인덱스에 관해 언급할때, 엔트리의 r_info 멤버에 대한 각각 ELF32_R_TYPE 나 ELF32_R_SYM 을 적용한 결과를 의미한다.

#define ELF32_R_SYM(i) ((i)>>8)

#define ELF32_R_TYPE(i) ((unsigned char)(i))

#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t))

우선 엔트리의 내용을 알아보자.

bash-2.05a$ od -Ad -td4 a.o -N20 -j580 -v;

0000580 11 1281

어떤 의미인지 알아보자. 우선 이 섹션의 섹션 헤더에서 “섹션의 인포 : 1. 재배치가 적용될 섹션에대한 섹션 헤더 인덱스”라는 정보를 얻을 수 있다. 위에서 11의 값은 1번 섹션의 시작으로부터 옵셋 11바이트 위치째 값이 영향을 받는다는 것이다. 아래의 소스코드와 어셈블 결과 파일의 내용을 보면 그 위치는 message 전역변수의 위치이다.

r_info : 1281 값을 갖는다. 상위 3바이트 값 : 관련 심볼 테이블 인덱스, 하위 1바이트값: 재배치 타입. 1281 == 0000 0101 0000 0001, 즉 관련 심볼 테이블 인덱스는 5, 재배치 타입은 1.(어! 재배치 타입 1번이 무엇인지 관련문서에 내용이 없다.) 앗 ! 찾았다. R_386_32

재배치가 어떻게 이루어질지 관련 문서에 내용을 참조해 보자.

아래의 계산은 실행가능 파일이나 공유 오브젝트 파일 안에서 재배치 가능한 파일을 전송하고있는 행위를 가정한다.(문장이 애매하다 아마 지금까지 공부한 내용을 참조하면. 아마 “재배치 가능파일을 실행가능 파일이나 공유 오브젝트 파일로 재생산할때를 가정한다” 이쯤 되는 것 같다) 개념적으로 링크 에디터는 하나나 그이상의 재배치 가능한 파일들을 출력을 형성하기 위해서 합병한다. 그것은 첫번째로 입력 파일들을 어떻게 결합하고 위치시킬지 결정을 한다. 심볼값들을 업데이트 하고 마지막으로 재배치를 수행 한다. 실행가능 파일이나 공유 파일에 대해 적용되어진 재배치는 비슷하다. 같은 결과를 수행한다. 아래의 설명은 다음의 표기법를 사용한다.

A 이것은 재배치 가능한 필드의 값을 개산하기 위해서 사용되어진 가수(addend)를 의미한다.

P 이것은 (r_offset 를 사용해서 계산되어진) 재배치되어지고 있는 저장 단위의 위치(섹션 옵셋이나 주소)를 의미한다.

S 이것은 재배치 엔트리 내에 위치해 있는 심볼의 인덱스 값을 의미한다.

재배치 엔트리의 r_offset 값은 영향을 받게되는 저당단위의 첫번째 바이트에 대한 옵셋 값이나 가상 주소를 명시한다. 재배치 타입은 어떤 비트들을 변경할지 그들의 값들을 어떻게 계산할지를 지정한다. 인텔 구조는 오직 Elf32_Rel 재배치 엔트리들만을 사용한다. 재배치되어지는 필드는 가수를 가지고 있다. 모든경우에 있어서, 가수 와 계산된 결과는 같은 바이트 순서를 사용한다.

그림 1-3. Relocation Types

Name
Value 
Field 
Calculation
R_386_NONE 
0
none
none 
R_386_32 
1
word32
S+A 
R_386_PC32 
2
word32
S+A-P 

관련 심볼 테이블 인덱스 5는 위에 조사한 값을 보면 다음과 같다.

5번째 엔트리 :

st_name : 8 즉 message 이름을 갖는다.

st_value : 0

st_size : 0

st_info : 16. 바인드 0(로컬바인딩), 타입 1(오브젝트 타입)

st_other : 0. 이값은 항상 0이다.(아직 특정 의미가 부여되어 있지 않다)

st_shndx : 3. 이 엔트리와 관련된 섹션 헤더테이블은 3번이다. “.data”

우리의 예제 소스코드

assembler.s

.text

.globl _start

_start:

movl $0x4, %eax # eax = write 시스템콜을 위한 값

movl $1, %ebx # ebx = 표준출력을 위한 파일 디스크립터

movl $message,%ecx # ecx = 메시지를 위한 포인터

movl $13, %edx # edx = 메시지의 길이

int $0x80 # 시스템 콜을 호출한다. (1)

movl $0x1, %eax # eax = exit 시스템 콜을 위한 값

int $0x80 # 시스템 콜을 호출한다. (1)

.data

.globl message

message:

.string "Hello world\n" # 메시지를 위한 데이타

이 소스를 어셈블한 결과 오브젝트 코드

bash-2.05a$ objdump --disassemble a.o

a.o: file format elf32-i386

Disassembly of section .text:

00000000 <_start>:

0: b8 04 00 00 00 mov $0x4,%eax

5: bb 01 00 00 00 mov $0x1,%ebx

a: b9 00 00 00 00 mov $0x0,%ecx

f: ba 0d 00 00 00 mov $0xd,%edx

14: cd 80 int $0x80

16: b8 01 00 00 00 mov $0x1,%eax

1b: cd 80 int $0x80

1d: 8d 76 00 lea 0x0(%esi),%esi

좋다. 아니 별로다. 일단 이정도까지 해 두자. 어이구 문서가 난잡해 졌다.

  1. 지금까지 진행해 온 내용을 한번 정리해보자

    전체 파일 내용은 ?

    bash-2.05a$ od -Ad a.o -v -tx1

    ELF 헤더(52바이트 크기)

    0000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00

    0000016 01 00 03 00 01 00 00 00 00 00 00 00 00 00 00 00

    0000032 94 00 00 00 00 00 00 00 34 00 00 00 00 00 28 00

    0000048 08 00 05 00

    섹션 헤더 1의 실제 값(32바이트 크기)

    b8 04 00 00 00 bb 01 00 00 00 b9 00

    0000064 00 00 00 ba 0d 00 00 00 cd 80 b8 01 00 00 00 cd

    0000080 80 8d 76 00

    섹션 헤더 3의 실제 값(16바이트 크기)

    48 65 6c 6c 6f 20 77 6f 72 6c 64 0a

    0000096 00 00 00 00

    섹션 헤더 5의 실제 값(48바이트 크기)

    00 2e 73 79 6d 74 61 62 00 2e 73 74

    0000112 72 74 61 62 00 2e 73 68 73 74 72 74 61 62 00 2e

    0000128 72 65 6c 2e 74 65 78 74 00 2e 64 61 74 61 00 2e

    0000144 62 73 73 00

    섹션 헤더 0(40바이트 크기)

    00 00 00 00 00 00 00 00 00 00 00 00

    0000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

    0000176 00 00 00 00 00 00 00 00 00 00 00 00

    섹션 헤더 1(40바이트 크기)

    1f 00 00 00

    0000192 01 00 00 00 06 00 00 00 00 00 00 00 34 00 00 00

    0000208 20 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00

    0000224 00 00 00 00

    섹션 헤더 2(40바이트 크기)

    1b 00 00 00 09 00 00 00 00 00 00 00

    0000240 00 00 00 00 44 02 00 00 08 00 00 00 06 00 00 00

    0000256 01 00 00 00 04 00 00 00 08 00 00 00

    섹션 헤더 3(40바이트 크기)

    25 00 00 00

    0000272 01 00 00 00 03 00 00 00 00 00 00 00 54 00 00 00

    0000288 10 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00

    0000304 00 00 00 00

    섹션 헤더 4(40바이트 크기)

    2b 00 00 00 08 00 00 00 03 00 00 00

    0000320 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00

    0000336 00 00 00 00 04 00 00 00 00 00 00 00

    섹션 헤더 5(40바이트 크기)

    11 00 00 00

    0000352 03 00 00 00 00 00 00 00 00 00 00 00 64 00 00 00

    0000368 30 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00

    0000384 00 00 00 00

    섹션 헤더 6(40바이트 크기)

    01 00 00 00 02 00 00 00 00 00 00 00

    0000400 00 00 00 00 d4 01 00 00 60 00 00 00 07 00 00 00

    0000416 04 00 00 00 04 00 00 00 10 00 00 00

    섹션 헤더 7(40바이트 크기)

    09 00 00 00

    0000432 03 00 00 00 00 00 00 00 00 00 00 00 34 02 00 00

    0000448 10 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00

    0000464 00 00 00 00

    섹션 헤더 6의 실제값(96바이트 크기)

    00 00 00 00 00 00 00 00 00 00 00 00

    0000480 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

    0000496 03 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00

    0000512 03 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00

    0000528 03 00 04 00 01 00 00 00 00 00 00 00 00 00 00 00

    0000544 10 00 01 00 08 00 00 00 00 00 00 00 00 00 00 00

    0000560 10 00 03 00

    섹션 헤더 7의 실제값(16바이트 크기)

    00 5f 73 74 61 72 74 00 6d 65 73 73

    0000576 61 67 65 00

    섹션 헤더 2의 셀제값(8바이트 크기)

    0b 00 00 00 01 05 00 00

    0000588

  2. 지금까지 우리가 진행해온 내용은 재배치가능파일 즉, 라이브러리나 소스파일에 컴파일이나 어셈블만 실행된 오브젝트 파일에 대한 조사였다. 또한 우리의 조사는 ELF 파일에 대한 내용이었다. 우리가 진행한 내용을 토대로 ELF 파일의 전체적인 내용에 대해서 알아보자.

    - 파일의 초반부에 52바이트 크기의 ELF 헤더 구조체 자료형식의 내용을 가진다.

    - 여기서 파일의 전체의 대략적인 구조를 보여준다. 프로그램 헤더, 섹션 헤더가 파일의 어디에 있는지를 알려주고, 각각의 크기와 개수를 알려준다. ELF 헤더는 오직 한 개만 있다.

    - ELF 헤더에서 알려준 섹션 헤더가 있는 위치로 간다. 섹션 헤더는 여러 개이다. 우리는 이것을 섹션 헤더테이블이다. 즉, 이 섹션 헤더 테이블은 섹션 헤더 구조체의 배열인것이다.

    - 이제 각 섹션 헤더는 자신이 실제 어떤 내용을 포함고 있고. 자신이 포함하고있는 내용의 전체 크기는 얼마이고, 자신이 자식을 가지고 있다면 각 자식의 크기는 얼마인지, 자신의 실제 내용은 파일의 어느 부분에 있는지등에 관한 정보를 제공한다.

    - 이런 섹션들의 종류에는 실제 프로그램의 바이너리 코드를 담고있는 녀석, 문자열을 담고 있는녀석, 프로그램내에 사용되는 심볼에 관한 내용만을 담고 있는 녀석, 재배치를 어떻게 할것인가에 대한 내용을 포함고 있는녀석, 디버깅 정보만을 가지고 있는 녀석등 다양 종류의 녀석들이 있는 것이다.

    - 다음은 프로그램 헤더 이녀석은 아직 우리가 경험해 보지 못했으므로…..

     

     

     

     


Examining the symbol table

아직 보여주지 않는 중요한 섹션은 파일의 끝부분에서 발견할수 있는 심볼 테이블이다. 그것은 프로그램안에 있는 표시(프로시져들의 이름들, 변수의 이름들)에 대한 나열이다. 유닉스상에 있는 심볼 이름들 유틸리티인 nm 은 ELF 파일 내에서 정의된 또는 사용되어진 모든 심볼들을 나열한다. 다음과 같이 입력하라.

$nm assembler.o

이 프로그램 내에 정의되어진 유일한 레이블들은 _start 와 message 이다.

 


Examining the instructions

objdump 프로그램은 실행가능 파일들을 탐색하는데 편리한 도구이다. 이 도구는 파일의 포멧과 섹션들에 관한 정보를 출력한다. 다음과 같이 입력하라.

$objdump --all-headers assembler.o

objdump 프로그램이 파일의 실행가능한 부분을 어셈블러 코드처럼 출력하게하기 위해

-–disassemble 옵션을 사용하라.

 


Using the linker

링커 프로그램 ld의 목적은 분리된 모듈들로 컴파일되어 구성된 프로그램내부에 있는 표시들(레이블들)을 처리한다. 위에 주어진 간단한 예에서, 실행되어지기위해서 필요한 모든 것은 text 와 data를 재배치 하는 것이다. 그것은 바로 _start 주소들에 위치해 있다. 다음과 같이 입력하라.

$ld -N assembler.o -o assembler

이 명령은 assembler.o 로부터 코드를 취한다. 그리고 그것을 유닉스 로더가 그것이 있기를 기대하는 주소 0x08048074에 재배치 한다. 그리고 결과를 실행가능 파일 assembler 에 기록한다. 이것은 assembler.o 와 동일한 포멧이다. 하지만 od 프로그램을 사용해서 이것을 조사하면, 이제 0x08048074 진입 포인트를 보여 준다. 몇 개의 다른 비트들과 조각들은 역시 변경되어 있다. 여러분이 다시 그것을 조사하기를 원하다면, od 또는 odjdump 를 사용하라.

 


Running the program

프로그램을 실행하기 위해서 assembler를 입력해라. 이 시점에서부터, 파일 assember내부에 포함되어진 데이터 구조는 오퍼레이팅 시스템 로더에의해서 해석되어 집니다. 그것은 머신 타입을 조사한다. 그리고 프로세스가 필요할 환경의 종류를 결정하기 위해서 헤더를 조사한다. 적당한 종류의 새로운 프로세스를 셋업한다. 명령들과 초기화된 데이터는 프리 메모리 내부로 로드되어진다. 그리고 오퍼레이팅 시스템은 진입점으로 분기를 한다. 이 행위는 execve 시스템 콜을 위한 매뉴얼 페이지에 의해서 자세하게 설명되어져있다.


Looking at cversion.c

간단한 프로그램의 동일한 C 버전은 cversion.c이다:

cversion.c

extern void write(int, char [], int );

extern void exit(int);

 

char message [] = "Hello World!\n"; // 포인터가 아닌, 배열이다.

 

#define MESSAGELENGTH 13

 

int test_function_01( ) {

 char *msg = “test function\n” ; //배열이 아닌, 포인터이다.

 printf(“%s”, msg) ;

}

 

int test_function_02( ) {

 static int static_i = 1 ;

 static int static_j = 2 ;

 static char * static_a = “static pointer” ;

 static char static_b[2] ;

 

 int local_i ;

 int local_j = 2 ;

 char test_a ;

}

 

int main( ) {

test_function_01() ;

write(1,message,MESSAGELENGTH);

exit(1);

}

이 프로그램은 writeexit.s 파일 에서 정의되어진 외부 정의 함수들인 write 와 exit 을 사용한다. writeexit.s:

.text

# an implementation of write and exit

.globl write

write:

pushl %ebx

movl 0x10(%esp,1),%edx

movl 0xc(%esp,1),%ecx

movl 0x8(%esp,1),%ebx

movl $0x4,%eax

int $0x80

popl %ebx

ret

.globl exit

exit:

movl %ebx,%edx

movl 0x4(%esp,1),%ebx

movl $0x1,%eax

int $0x80

연습의 나머지는 이들 두 부분으로부터 실행 프로그램을 만들기 위해서 집중한다. 매 단계에서 중간 파일들을 조사한다. 실행가능한 프로그램을 만드는데 관련된 단계는 다음과 같다:

1. C 프로그램을 어셈블러 프로그램으로 전환하기 위해서 C 컴파일러 gcc 를 사용한다. 다음과 같이 입력한다.

$gcc -S cversion.c

-S 옵션은 어셈블러 버전을 생산한 다음에 정지하게 한다. cversion.s로 불리는 어셈블러 파일을 조사하라.

결과를 조사해 보자

[guest@ftz .test]$ gcc -S cversion.c

[guest@ftz .test]$ cat cversion.s

/* 파일정보와 버전 정보가 추가 되었다.*/

.file "cversion.c"

.version "01.01"

gcc2_compiled.:

/* 배열에대한 값을 저장한다. 전역 심볼이고. 데이터 세그먼트에 값을 저장한다.

크기는 널까지 포함해서 14 바이트이다. */

.globl message

.data

.type message,@object

.size message,14

message:

.string "Hello World!\n"

/* 리드온리 데이타이다. 테스트 함수 01에서 사용될 데이타를 저장한다

대문자로 표시된 각 레이블은 실제 데이타가 있은 곳을 표시한다. */

.section .rodata

.LC0:

.string "test function\n"

.LC1:

.string "%s"

/* 텍스트 세그먼트를 정의한다. 얼라인먼트는 4로 한다. 즉, 이곳의 시작 위치는 4로 나누어서 0 이 되는 위치에서 시작한다. 코드에 사용되어지는 대문자 레이블은 데이터 세그먼트의 실제 위치를 나타낸다. 아래에 있는 사이즈는 레이블의 빼기를 통해서 구한다.*/

.text

.align 4

.globl test_function_0

반갑습니다.
세벌쉭 2017-07
코딩의 결과물인
프로그램이....
내부적으로 어떻게 실행되어 지는지....

ELF 1.2
을 선행 필독 하셔야....




제목Page 3/15
2017-07   31023   세벌쉭
2017-07   29993   세벌쉭
2017-07   25373   세벌쉭
2017-07   24617   세벌쉭
2017-07   23214   세벌쉭
2017-07   22705   세벌쉭
2017-07   18258   세벌쉭
2017-07   17649   PPC허인구
2017-07   17932   stone92김경민
2017-07   19347   stone92김경민
2017-06   16652   무아
2017-06   18271   민욱님
2017-06   13635   화령
2017-05   14299   turtl
2017-05   11136   turtl
2017-04   11144   Ansyncic
2017-04   9478   행복집
2017-04   12827   채선일
2017-04   7883   안형곤
2017-04   6769   스킬서포트