/* ---------- ELF Analysis Class ---------------------------------------/
 * ---------------------Coded by binoopang [binoopang@is119.jnu.ac.kr]--/
 * ---------------------Project Site : http://elfclass.kldp.net --------/
 * --------------------------------------------------------------------*/


/* ---------------------------------------------------------------------/
 * 릴리즈 노트
 * ---------------------------------------------------------------------/
 * fix 2
 *   - 기능이 있는 멤버함수들은 상속을 고려하여 가상함수로 선언
 *   - private 였던 액세스를 protected로 바꿈(상속 고려)
 *   - byName 검색시 NORESULT에 대한 에러 추가
 * --------------------------------------------------------------------*/


// 필수 헤더 인클루드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <elf.h>

// BOOL 타입 선언
#define bool unsigned char
#define true 1
#define false 0

// 에러 첵크 매크로
// fp가 널일 경우 루틴의 리턴 타입에 따라 에러 첵크
#define check_fp_false() do{if(fp==NULL){errno=NULLFP;\
				set_err_func(); return false;}}while(0)
#define check_fp_null() do{if(fp==NULL){errno=NULLFP;\
				set_err_func(); return NULL;}}while(0)
#define check_fp_count() do{if(fp==NULL){errno=NULLFP;\
				set_err_func(); return -1;}}while(0)

// 인덱스로 정보를 찾을 때 인덱스가 0보다 작을 경우 에러 첵크
#define check_index() do{if(index<0){errno=NEGINDEX;\
				set_err_func(); return NULL;}}while(0)

// 에러함수이름 버퍼 초기화 및 버퍼 내용 채우기 매크로
#define set_err_func() do{memset(err_func, 0x00, 64);\
				sprintf(err_func, "%s", __func__);}while(0)

// 에러 넘버 값 정의
#define NOSUCHFILE 1  // 분석하고자 하는 파일 경로가 잘못 됨
#define NULLFP 2			// 파일 포인터가 NULL일 경우
#define NOSECTION 3		// 섹션 정보가 없을 경우
#define OUTOFRANGE 4	// 인덱스 범위가 잘못 되었을 경우
#define NULLNAME 5		// 이름으로 검색시 이름이 NULL일 경우
#define NOPLTREL 6		// pltRelocation 정보가 없을 때
#define NODYNREL 7		// 동적 재배치 정보가 없을 경우
#define NODYNSYM 8		// 동적 심볼 정보가 없을 경우
#define NOSYMBOL 9		// 심볼 정보가 없을 경우
#define NEGINDEX 10		// 인덱스가 0보다 작을 때
#define NOARGS 11			// 클래스 초기화용 아규먼트가 없을 때
#define NORESULT 12		// 이름으로 검색시 검색결과가 없을 때
#define NOELF 13			// 열린 파일이 ELF형식이 아닐 때

class Elf32View
{
public:
		Elf32View();			// 생성자
		virtual ~Elf32View();						// 소멸자

		// 멤버함수들
		// ELF파일 로드 관련 함수
		virtual bool Load(const char *);

		// ELF헤더 관련 함수
		virtual bool Listing_ELFHeader();

		// 심볼 관련 함수
		virtual bool Listing_Symbol();
		virtual bool Listing_Dynamic_Symbol();
		virtual int Get_Symbol_Count();
		virtual int Get_Dynamic_Symbol_Count();
		virtual const char *Get_Symbol_Name(Elf32_Word);
		virtual const char *Get_Dynamic_Symbol_Name(Elf32_Word);
		virtual Elf32_Sym *Get_Dynamic_Symbol_byIndex(int);
		virtual Elf32_Sym *Get_Dynamic_Symbol_byName(const char *); 
		virtual Elf32_Sym *Get_Symbol_byIndex(int);
		virtual Elf32_Sym *Get_Symbol_byName(const char *);
		virtual const char *get_sym_type(int);
		virtual const char *get_sym_binding(int);

		// 섹션 관련 함수
		virtual bool Listing_Section();
		virtual int Get_Section_Count();
		virtual const char *Get_Section_Name(Elf32_Word);
		virtual Elf32_Shdr *Get_Section_byIndex(int);
		virtual Elf32_Shdr *Get_Section_byName(const char *);

		// 프로그램 헤더 관련
		virtual bool Listing_Program();
		virtual int Get_Program_Count();
		virtual Elf32_Phdr *Get_Program_byType(const char *);
		virtual Elf32_Phdr *Get_Program_byIndex(int);
		virtual const char *get_program_type(int);
		virtual const char *get_program_flags(int);

		// 재배치 관련함수
		virtual bool Listing_dynRelocation();
		virtual bool Listing_pltRelocation(); 
		virtual int Get_dynRelocation_Count();
		virtual int Get_pltRelocation_Count();
		virtual Elf32_Rel *Get_dynRelocation_byIndex(int); 
		virtual Elf32_Rel *Get_dynRelocation_byName(const char *);
		virtual Elf32_Rel *Get_pltRelocation_byIndex(int); 
		virtual Elf32_Rel *Get_pltRelocation_byName(const char *);
		virtual const char *get_rel_type(int);

		// 에러 핸들링 관련 함수
		virtual void Last_Error();


protected:
		Elf32_Sym *Get_Dynamic_Symbol_byIndex(FILE *, Elf32_Shdr*, int);
		Elf32_Sym *Get_Symbol_byIndex(FILE *, Elf32_Shdr*, int);
		const char *Error_List(int);
		FILE *fp;
		long program_base;
		long section_base;
		long shsstroff;
		int errno;
		char err_func[64];
		char str[128];
		
		Elf32_Ehdr *ehdr;
		Elf32_Shdr *dynsym;
		Elf32_Shdr *dynstr;
		Elf32_Shdr *plt;
		Elf32_Shdr *got;
		Elf32_Shdr *symtab;
		Elf32_Shdr *strtab;
		Elf32_Shdr *rel_dyn;
		Elf32_Shdr *rel_plt;
};



// >>멤버함수의 구현 시작 <<
// 생성자 루틴
Elf32View::Elf32View()
{
	errno = 0;  // errno 에러가 없을 경우 0
	ehdr = new Elf32_Ehdr;
	dynsym = new Elf32_Shdr;
	dynstr = new Elf32_Shdr;
	plt = new Elf32_Shdr;
	got = new Elf32_Shdr;
	symtab = new Elf32_Shdr;
	strtab = new Elf32_Shdr;
	rel_dyn = new Elf32_Shdr;
	rel_plt = new Elf32_Shdr;
}

// 클래스의 파괴자.
Elf32View::~Elf32View()
{
	delete ehdr;
	delete dynsym;
	delete dynstr;
	delete plt;
	delete got;
	delete symtab;
	delete strtab;
	delete rel_dyn;
	delete rel_plt;

	if(fp!=NULL) fclose(fp);
}

// ELF 형식 파일을 여는 함수 입니다.
// 파일이 정상적으로 열렸다면 몇몇 변수들을 초기화 합니다.
// 실패시 false를 리턴합니다
bool Elf32View::Load(const char *path)
{
	int i;
	long shstable;

	// 아규먼트는 있는가?
	if(path == NULL)
	{
		errno = NOARGS;
		set_err_func();
		return false;
	}
	// 경로는 적절한가?
	else if((fp = fopen(path, "r")) == NULL)
	{
		errno = NOSUCHFILE;
		set_err_func();
		return false;
	}
	// 열린 파일은 ELF 형식 파일인가?
	else if(!((ehdr->e_ident[1] == 'E')&&(ehdr->e_ident[2] == 'L')&&(ehdr->e_ident[3] == 'F')))
	{
		errno = NOELF;
		set_err_func();
		return false;
	}
	else
	{
		Elf32_Shdr *shdr = new Elf32_Shdr;
		fread(ehdr, sizeof(Elf32_Ehdr), 1, fp);

		// 섹션헤더 테이블의 문자열 테이블 오프셋 구하기
		// 섹션헤더테이블 오프셋 + (각 섹션헤더 크기 * 섹션헤더문자열테이블인덱스)
		shsstroff = ehdr->e_shoff + (ehdr->e_shentsize * ehdr->e_shstrndx);
		// 섹션헤더 오프셋
		section_base = ehdr->e_shoff;
		// 프로그램헤더 오프셋
		program_base = ehdr->e_phoff;
		
		// 섹션 정보 구조체 초기화 
		// 심볼테이블이나 재배치 정보등을 쉽게 확인하기 위해서 생성자 루틴이
		// 해당 섹션 헤더 정보를 미리 초기화 해 놓습니다.	
		fseek(fp, shsstroff, SEEK_SET);
		fread(shdr, sizeof(Elf32_Shdr), 1, fp);
		shstable = shdr->sh_offset;

		fseek(fp, section_base, SEEK_SET);

		for(i=0 ; i < ehdr->e_shnum ; i++)
		{
			memset(str, 0x00, 128);
			fseek(fp, section_base + i*sizeof(Elf32_Shdr), SEEK_SET);
			fread(shdr, sizeof(Elf32_Shdr), 1, fp);

			fseek(fp, shstable + shdr->sh_name, SEEK_SET);
			fgets(str, 128, fp);

			// 동적 심볼 섹션
			if(!strcmp(str, ".dynsym"))
							memcpy(dynsym, shdr, sizeof(Elf32_Shdr));
			// 동적 심볼 문자열 테이블
			else if(!strcmp(str, ".dynstr"))
							memcpy(dynstr, shdr, sizeof(Elf32_Shdr));
			// 프로시져 연결 테이블
			else if(!strcmp(str, ".plt"))
							memcpy(plt, shdr, sizeof(Elf32_Shdr));
			// 전역 오프셋 테이블
			else if(!strcmp(str, ".got"))
							memcpy(got, shdr, sizeof(Elf32_Shdr));
			// 심볼 테이블
			else if(!strcmp(str, ".symtab"))
							memcpy(symtab, shdr, sizeof(Elf32_Shdr));
			// 심볼의 문자열 테이블
			else if(!strcmp(str, ".strtab"))
							memcpy(strtab, shdr, sizeof(Elf32_Shdr));
			// .dyn 재배치 정보 테이블
			else if(!strcmp(str, ".rel.dyn"))
							memcpy(rel_dyn, shdr, sizeof(Elf32_Shdr));
			// .plt 재배치 정보 테이블
			else if(!strcmp(str, ".rel.plt"))
							memcpy(rel_plt, shdr, sizeof(Elf32_Shdr));

		}
		delete shdr;
	}

	return true;

}
// ELF 헤더 정보를 리스팅 한다.
bool Elf32View::Listing_ELFHeader()
{
	check_fp_false();
	unsigned char e_ident[EI_NIDENT];
	fseek(fp, 0x00, SEEK_SET);
	fread(e_ident, sizeof(e_ident), 1, fp);
	printf("Elf Header :\n");
	printf(" Magic : ");
	for(int i = 0 ; i<EI_NIDENT ; i++)
		printf("%.2x ", e_ident[i]);
	printf("\n");
	printf(" Entry point address : 0x%x\n", ehdr->e_entry);
	printf(" start of program header : 0x%x\n", ehdr->e_phoff);
	printf(" size of program headers : 0x%x\n", ehdr->e_phentsize);
	printf(" start of section header : 0x%x\n", ehdr->e_shoff);
	printf(" size of section headers : 0x%x\n", ehdr->e_shentsize);
	printf(" Flags : 0x%x\n", ehdr->e_flags);
	printf(" Number of section headers : 0x%x\n", ehdr->e_shnum);
	printf(" Section header string table index : %d\n", ehdr->e_shstrndx);
	return true;
}

// 모든 섹션들의 정보를 리스팅 한다.
bool Elf32View::Listing_Section()
{
	check_fp_false();
	int i;
	long shstable;		// 섹션헤더의 스트링테이블 오프셋

	if(ehdr->e_shnum == 0)
	{
		errno = NOSECTION;
		set_err_func();
		return false;
	}

	Elf32_Shdr *shdr = new Elf32_Shdr;

	// 스트링 테이블 위치 계산 법
	// [1] 스트링 테이블 섹션 헤더위치를 알아낸다.
	//   -> e_shoff+(e_shentsize*e_shstrndx)
	// [2] 해당 섹션의 offset을 알아낸다.
	//   -> sh_offset;
	
	fseek(fp, shsstroff, SEEK_SET);
	fread(shdr, sizeof(Elf32_Shdr), 1, fp);
	shstable = shdr->sh_offset;

	fseek(fp, section_base, SEEK_SET);
	printf("\n\nSection Header Table Information\n\n");

	for(i=0 ; i < ehdr->e_shnum ; i++)
	{
		memset(str, 0x00, 128);
		fseek(fp, section_base + i*sizeof(Elf32_Shdr), SEEK_SET);
		fread(shdr, sizeof(Elf32_Shdr), 1, fp);

		// 실제 문자열 위치 -> 스트링 테이블 시작지점 + 각 헤더의 sh_name
		fseek(fp, shstable + shdr->sh_name, SEEK_SET);
		fgets(str, 128, fp);

		printf(" [%3d] %36.36s %8x %8x %8x \n", 
										i, str, shdr->sh_addr, shdr->sh_offset, shdr->sh_size);

	}
	delete shdr;
	return true;
}

// 특정 이름의 섹션 헤더 정보를 포인터로 넘겨준다.
Elf32_Shdr *Elf32View::Get_Section_byName(const char *name)
{
	check_fp_null();
	int i;
	long shstable;		// 섹션헤더의 스트링테이블 오프셋
	Elf32_Shdr *shdr = new Elf32_Shdr;
	
	fseek(fp, shsstroff, SEEK_SET);
	fread(shdr, sizeof(Elf32_Shdr), 1, fp);
	shstable = shdr->sh_offset;

	fseek(fp, section_base, SEEK_SET);
	for(i=0 ; i < ehdr->e_shnum ; i++)
	{
		memset(str, 0x00, 128);
		fseek(fp, section_base + i*sizeof(Elf32_Shdr), SEEK_SET);
		fread(shdr, sizeof(Elf32_Shdr), 1, fp);

		fseek(fp, shstable + shdr->sh_name, SEEK_SET);
		fgets(str, 128, fp);
		
		// 일치하는 섹션이름이 있다면 해당 포인터를 넘김
		if(!strcmp(str, name))
			return shdr;
	}
	errno = NORESULT;
	set_err_func();
	delete shdr;

	return NULL;
}

// 인덱스로 원하는 섹션을 가져온다
Elf32_Shdr *Elf32View::Get_Section_byIndex(int index)
{
	check_fp_null();
	check_index();
	int i;

	if(index > ehdr->e_shnum-1)
	{
		errno = OUTOFRANGE;
		set_err_func();
		return NULL;
	}
	else
	{
		Elf32_Shdr *shdr = new Elf32_Shdr;
		// 원하는 위치 -> 인덱스 * 섹션헤더 크기 + 섹션헤더 오프셋
		fseek(fp, section_base + index*sizeof(Elf32_Shdr), SEEK_SET);
		fread(shdr, sizeof(Elf32_Shdr), 1, fp);
		return shdr;
	}

	return NULL;
}

// 프로그램 헤더를 모두 리스팅
bool Elf32View::Listing_Program()
{
	check_fp_false();
	// 플래그 판독 방법 : 일반적인 유닉스 권한 플래그와 동일하다.
	// 1 1 1
	// R W E(Execute)
	//
	// ex) 001 : --E,  010 : -W-,  100 : R--
	//
	
	int i;
	Elf32_Phdr *phdr = new Elf32_Phdr;

	printf("\n\nProgram Header Information\n\n");

	for(i=0 ; i<ehdr->e_phnum ; i++)
	{
		fseek(fp, program_base + i*sizeof(Elf32_Phdr), SEEK_SET);
		fread(phdr, sizeof(Elf32_Phdr), 1, fp);
		printf("%10s, 0x%08x, 0x%08x, 0x%08x, 0x%06x, 0x%06x, %3s, 0x%04x\n",
										get_program_type(phdr->p_type), phdr->p_offset,
									  phdr->p_vaddr, phdr->p_paddr, phdr->p_filesz, 
										phdr->p_memsz, get_program_flags(phdr->p_flags), 
										phdr->p_align);
	}
	delete phdr;
	return true;
}

// 타입 이름으로 프로그램 헤더 구조체 얻어오기
Elf32_Phdr *Elf32View::Get_Program_byType(const char *type_name)
{
	check_fp_null();
	int i;
	Elf32_Phdr *phdr = new Elf32_Phdr;

	for(i=0 ; i<ehdr->e_phnum ; i++)
	{
		fseek(fp, program_base + i*sizeof(Elf32_Phdr), SEEK_SET);
		fread(phdr, sizeof(Elf32_Phdr), 1, fp);

		if(!strcmp(get_program_type(phdr->p_type), type_name))
			return phdr;
	}
	delete phdr;
	return NULL;
}

// 인덱스로 원하는 프로그램 헤더 정보 가져오기
Elf32_Phdr *Elf32View::Get_Program_byIndex(int index)
{
	check_fp_null();
	check_index();

	if(index > ehdr->e_phnum-1)
	{
		errno = OUTOFRANGE;
		set_err_func();
		return NULL;
	}
	else
	{
		Elf32_Phdr *phdr = new Elf32_Phdr;
		fseek(fp, program_base + index*sizeof(Elf32_Phdr), SEEK_SET);
		fread(phdr, sizeof(Elf32_Phdr), 1, fp);
		return phdr;
	}
	return NULL;
}

// 다이나믹 재배치 정보를 모두 리스팅
bool Elf32View::Listing_dynRelocation()
{
	check_fp_false();
	// 재배치 구조체의 원소 중 r_offset을 ELF32_R_SYM()매크로를 사용하여
	// 계산해서 나온 값은 심볼테이블에 대한 인덱스 번호이다.
	// 각 재배치 테이블의 엔트리 개수 구하기
	
	int rel_dyn_ndx = rel_dyn->sh_size / sizeof(Elf32_Rel);
	int i;

	Elf32_Rel *rel = new Elf32_Rel;
	Elf32_Sym *sym;

	if(rel_dyn_ndx>0)
	{
		printf("\n\n.rel.dyn section relocation information\n");
		for(i=0 ; i<rel_dyn_ndx ; i++)
		{
			memset(str, 0x00, 128);
			fseek(fp, rel_dyn->sh_offset + i*sizeof(Elf32_Rel), SEEK_SET);
			fread(rel, sizeof(Elf32_Rel), 1, fp);

			// 재배치 정보를 통해서 심볼 인덱스를 가져오고 가져온 인덱스를 사용하여
			// 심볼 구조체를 가져온다.

			sym = Get_Dynamic_Symbol_byIndex(fp, dynsym, ELF32_R_SYM(rel->r_info));
			fseek(fp, dynstr->sh_offset+sym->st_name, SEEK_SET);
			fgets(str, 128, fp);
			printf("[%2d] %08x %8x %17s %28.28s\n", 
											i,rel->r_offset, rel->r_info,
										  get_rel_type(rel->r_info), str);
		}
	}
	delete rel;
	delete sym;
	return true;
}

// 다이나믹 재배치 정보를 모두 리스팅
bool Elf32View::Listing_pltRelocation()
{
	check_fp_false();
	// 재배치 구조체의 원소 중 r_offset을 ELF32_R_SYM()매크로를 사용하여
	// 계산해서 나온 값은 심볼테이블에 대한 인덱스 번호이다.
	// 각 재배치 테이블의 엔트리 개수 구하기
	
	int rel_plt_ndx = rel_plt->sh_size / sizeof(Elf32_Rel);

	Elf32_Rel *rel = new Elf32_Rel;
	Elf32_Sym *sym;

	if(rel_plt_ndx>0)
	{
		printf("\n\n.rel.plt section relocation information\n");
		for(int i=0 ; i<rel_plt_ndx ; i++)
		{
			memset(str, 0x00, 128);
			fseek(fp, rel_plt->sh_offset + i*sizeof(Elf32_Rel), SEEK_SET);
			fread(rel, sizeof(Elf32_Rel), 1, fp);

			// 재배치 정보를 통해서 심볼 인덱스를 가져오고 가져온 인덱스를 사용하여
			// 심볼 구조체를 가져온다.

			sym = Get_Dynamic_Symbol_byIndex(fp, dynsym, ELF32_R_SYM(rel->r_info));
			fseek(fp, dynstr->sh_offset+sym->st_name, SEEK_SET);
			fgets(str, 128, fp);
			printf("[%2d] %08x %8x %17s %28.28s\n", 
											i,rel->r_offset, rel->r_info,
										  get_rel_type(rel->r_info), str);
		}
	}
	delete rel;
	delete sym;
	return true;
}

// 원하는 심볼 이름의 plt 재배치 정보를 모두 리스팅
Elf32_Rel *Elf32View::Get_pltRelocation_byName(const char *name)
{
	check_fp_null();
	int rel_plt_ndx = rel_plt->sh_size / sizeof(Elf32_Rel);

	if(name == NULL)
	{
		errno = NULLNAME;
		set_err_func();
		return NULL;
	}
	else if(rel_plt_ndx<0)
	{
		errno = NOPLTREL;
		set_err_func();
		return NULL;
	}
	else
	{
		for(int i=0 ; i<rel_plt_ndx ; i++)
		{	
			Elf32_Rel *rel = new Elf32_Rel;
			Elf32_Sym *sym;
			memset(str, 0x00, 128);
			fseek(fp, rel_plt->sh_offset + i*sizeof(Elf32_Rel), SEEK_SET);
			fread(rel, sizeof(Elf32_Rel), 1, fp);

			sym = Get_Dynamic_Symbol_byIndex(fp, dynsym, ELF32_R_SYM(rel->r_info));
			fseek(fp, dynstr->sh_offset+sym->st_name, SEEK_SET);
			fgets(str, 128, fp);
			if(!strcmp(str, name))
			{
				delete sym;
				return rel;
			}
		}
		errno = NORESULT;
		set_err_func();
	}
	return NULL;
}

// 원하는 심볼 이름의 다이나믹 재배치 정보를 모두 리스팅
Elf32_Rel *Elf32View::Get_dynRelocation_byName(const char *name)
{
	check_fp_null();
	int rel_dyn_ndx = rel_dyn->sh_size / sizeof(Elf32_Rel);

	if(name == NULL)
	{
		errno = NULLNAME;
		set_err_func();
		return NULL;
	}
	else if(rel_dyn_ndx<0)
	{
		errno = NODYNREL;
		set_err_func();
		return NULL;
	}
	else
	{
		for(int i=0 ; i<rel_dyn_ndx ; i++)
		{	
			Elf32_Rel *rel = new Elf32_Rel;
			Elf32_Sym *sym;
			memset(str, 0x00, 128);
			fseek(fp, rel_dyn->sh_offset + i*sizeof(Elf32_Rel), SEEK_SET);
			fread(rel, sizeof(Elf32_Rel), 1, fp);

			sym = Get_Dynamic_Symbol_byIndex(fp, dynsym, ELF32_R_SYM(rel->r_info));
			fseek(fp, dynstr->sh_offset+sym->st_name, SEEK_SET);
			fgets(str, 128, fp);
			if(!strcmp(str, name))
			{
				delete sym;
				return rel;
			}
		}
		errno = NORESULT;
		set_err_func();
	}
	return NULL;
}

// 원하는 인덱스의 다이나믹 재배치 정보를 포인터로 넘깁니다
Elf32_Rel *Elf32View::Get_dynRelocation_byIndex(int index)
{
	check_fp_null();
	check_index();
	int rel_dyn_ndx = rel_dyn->sh_size / sizeof(Elf32_Rel);

	if(index > rel_dyn_ndx-1)
	{
		errno = OUTOFRANGE;
		set_err_func();
		return NULL;
	}
	else
	{
		Elf32_Rel *rel = new Elf32_Rel;
		fseek(fp, rel_dyn->sh_offset + index*sizeof(Elf32_Rel), SEEK_SET);
		fread(rel, sizeof(Elf32_Rel), 1, fp);
		return rel;
	}
	return NULL;
}

// 원하는 인덱스의 다이나믹 재배치 정보를 포인터로 넘깁니다
Elf32_Rel *Elf32View::Get_pltRelocation_byIndex(int index)
{
	check_fp_null();
	check_index();
	int rel_plt_ndx = rel_plt->sh_size / sizeof(Elf32_Rel);
	if(index > rel_plt_ndx-1)
	{
		errno = OUTOFRANGE;
		set_err_func();
		return NULL;
	}
	else
	{
		Elf32_Rel *rel = new Elf32_Rel;
		fseek(fp, rel_plt->sh_offset + index*sizeof(Elf32_Rel), SEEK_SET);
		fread(rel, sizeof(Elf32_Rel), 1, fp);
		return rel;
	}

	return NULL;
}
// 모든 동적 심볼정보 리스팅
bool Elf32View::Listing_Dynamic_Symbol()
{
	check_fp_false();
	int dynsym_ndx = dynsym->sh_size/sizeof(Elf32_Sym);

	// 심볼 갯수가 1보다 작다면 정보가 없는걸로 판단
	if(dynsym_ndx < 1)
	{
		errno = NODYNSYM;
		set_err_func();
		return false;
	}
	else
	{
		Elf32_Sym *sym = new Elf32_Sym;
		printf("\n\nDynamic Symbol Table Information\n");
		for(int i=0 ; i<dynsym_ndx ; i++)
		{
			memset(str, 0x00, 128);
			fseek(fp, dynsym->sh_offset+i*sizeof(Elf32_Sym), SEEK_SET);
			fread(sym, sizeof(Elf32_Sym), 1, fp);

			fseek(fp, dynstr->sh_offset + sym->st_name, SEEK_SET);
			fgets(str, 128, fp);
			printf("[%3d] %30.30s %8x %8x %7.7s %7.7s\n", 
											i, str, sym->st_value, sym->st_size, 
											get_sym_type(sym->st_info), 
											get_sym_binding(sym->st_info));
		}
	}
	return true;
}

// 모든 심볼 정보 리스팅
bool Elf32View::Listing_Symbol()
{
	check_fp_false();
	int i, symtab_ndx = symtab->sh_size/sizeof(Elf32_Sym);

	// 심볼 갯수가 1보다 작다면 정보가 없는걸로 판단
	if(symtab_ndx < 1)
	{
		errno = NOSYMBOL;
		set_err_func();
		return false;
	}
	else
	{
		Elf32_Sym *sym = new Elf32_Sym;
		printf("\n\nSymbol Table Information\n");
		for(i=0 ; i<symtab_ndx ; i++)
		{
			memset(str, 0x00, 128);
			fseek(fp, symtab->sh_offset+i*sizeof(Elf32_Sym), SEEK_SET);
			fread(sym, sizeof(Elf32_Sym), 1, fp);
			fseek(fp, strtab->sh_offset + sym->st_name, SEEK_SET);
			fgets(str, 128, fp);
			printf("[%3d] %30.30s %8x %8x %7.7s %7.7s\n", 
											i, str, sym->st_value, sym->st_size, 
											get_sym_type(sym->st_info), 
											get_sym_binding(sym->st_info));
		}
	}
	return true;
}

// 인덱스로 원하는 동적 심볼 정보 가져오기
// 이 함수는 클래스 사용자가 호출하는 함수가 아닙니다.(private)
Elf32_Sym *Elf32View::Get_Dynamic_Symbol_byIndex(FILE *fp, Elf32_Shdr *dynsym, int index)
{
	check_fp_null();
	int dynsym_ndx = dynsym->sh_size/sizeof(Elf32_Sym);

	// 심볼갯수가 1보다 작다면 정보가 없는 것으로 판단
	if(dynsym_ndx<0)
	{
		errno = NODYNSYM;
		set_err_func();
		return NULL;
	}
	// 인덱스의 범위 초과여부 판단
	else if(index > dynsym_ndx-1)
	{
		errno = OUTOFRANGE;
		set_err_func();
		return NULL;
	}
	else
	{
		Elf32_Sym *sym = new Elf32_Sym;
		fseek(fp, dynsym->sh_offset+index*sizeof(Elf32_Sym), SEEK_SET);
		fread(sym, sizeof(Elf32_Sym), 1, fp);
		return sym;
	}

	return NULL;
}

// 인덱스로 심볼 정보 가져오기
// 이 함수는 클래스 사용자가 호출하는 함수가 아닙니다.(private)
Elf32_Sym *Elf32View::Get_Symbol_byIndex(FILE *fp, Elf32_Shdr *symtab, int index)
{
	check_fp_null();
	int symtab_ndx = symtab->sh_size/sizeof(Elf32_Sym);

	// 심볼 갯수가 1보다 작다면 정보가 없는 것으로 판단
	if(symtab_ndx<1)
	{
		errno = NOSYMBOL;
		set_err_func();
		return NULL;
	}
	// 인덱스의 범위 초과여부 판단
	else if(index > symtab_ndx-1)
	{
		errno = OUTOFRANGE;
		set_err_func();
		return NULL;
	}
	else
	{
		Elf32_Sym *sym = new Elf32_Sym;
		fseek(fp, symtab->sh_offset+index*sizeof(Elf32_Sym), SEEK_SET);
		fread(sym, sizeof(Elf32_Sym), 1, fp);
		return sym;
	}
	return NULL;
}


// 인덱스로 원하는 동적 심볼 정보를 가져옵니다.
// 이 함수는 클래스 사용자가 호출하는 함수입니다.(public)
Elf32_Sym *Elf32View::Get_Dynamic_Symbol_byIndex(int index)
{
	check_fp_null();
	check_index();
	int dynsym_ndx = dynsym->sh_size/sizeof(Elf32_Sym);

	// 심볼 갯수가 1보다 작다면 정보가 없는 것으로 판단
	if(dynsym_ndx<1)
	{
		errno = NODYNSYM;
		set_err_func();
		return NULL;
	}
	// 인덱스의 범위 초과여부 판단
	else if(index > dynsym_ndx-1)
	{
		errno = OUTOFRANGE;
		set_err_func();
		return NULL;
	}
	else
	{
		Elf32_Sym *sym = new Elf32_Sym;
		fseek(fp, dynsym->sh_offset+index*sizeof(Elf32_Sym), SEEK_SET);
		fread(sym, sizeof(Elf32_Sym), 1, fp);
		return sym;
	}

	return NULL;
}

// 인덱스로 원하는 심볼 정보를 가져옵니다.
// 이 함수는 클래스 사용자가 호출하는 함수입니다.(public)
Elf32_Sym *Elf32View::Get_Symbol_byIndex(int index)
{
	check_fp_null();
	check_index();
	int symtab_ndx = symtab->sh_size/sizeof(Elf32_Sym);

	// 심볼 갯수가 1보다 작다면 정보가 없는 것으로 판단
	if(symtab_ndx<1)
	{
		errno = NOSYMBOL;
		set_err_func();
		return NULL;
	}
	// 인덱스의 범위 초과여부 판단
	else if(index > symtab_ndx-1)
	{
		errno = OUTOFRANGE;
		set_err_func();
		return NULL;
	}
	else
	{
		Elf32_Sym *sym = new Elf32_Sym;
		fseek(fp, symtab->sh_offset+index*sizeof(Elf32_Sym), SEEK_SET);
		fread(sym, sizeof(Elf32_Sym), 1, fp);
		return sym;
	}

	return NULL;
}



// 특정 이름의 심볼 정보를 포인터로 넘깁니다.
Elf32_Sym *Elf32View::Get_Dynamic_Symbol_byName(const char *name)
{
	check_fp_null();
	int i, dynsym_ndx = dynsym->sh_size/sizeof(Elf32_Sym);

	if(name == NULL)
	{
		errno = NULLNAME;
		set_err_func();
		return NULL;
	}
	else if(dynsym_ndx < 1)
	{
		errno = NODYNSYM;
		set_err_func();
		return false;
	}
	else
	{
		for(i=0 ; i<dynsym_ndx ; i++)
		{
			Elf32_Sym *sym = new Elf32_Sym;
			memset(str, 0x00, 128);
			fseek(fp, dynsym->sh_offset+i*sizeof(Elf32_Sym), SEEK_SET);
			fread(sym, sizeof(Elf32_Sym), 1, fp);
			fseek(fp, dynstr->sh_offset + sym->st_name, SEEK_SET);
			fgets(str, 128, fp);
			if(!strcmp(str, name))
				return sym;
		}
		errno = NORESULT;
		set_err_func();
	}
	return NULL;
}

// 특정 이름의 심볼 정보를 포인터로 넘깁니다.
Elf32_Sym *Elf32View::Get_Symbol_byName(const char *name)
{
	check_fp_null();
	int i, symtab_ndx = symtab->sh_size/sizeof(Elf32_Sym);

	if(name == NULL)
	{
		errno = NULLNAME;
		set_err_func();
		return NULL;
	}
	else if(symtab_ndx < 1)
	{
		errno = NOSYMBOL;
		set_err_func();
		return false;
	}
	else
	{
		for(i=0 ; i<symtab_ndx ; i++)
		{
			Elf32_Sym *sym = new Elf32_Sym;
			memset(str, 0x00, 128);
			fseek(fp, symtab->sh_offset+i*sizeof(Elf32_Sym), SEEK_SET);
			fread(sym, sizeof(Elf32_Sym), 1, fp);
			fseek(fp, strtab->sh_offset + sym->st_name, SEEK_SET);
			fgets(str, 128, fp);
			if(!strcmp(str, name))
				return sym;
		}
		errno = NORESULT;
		set_err_func();
	}
	return NULL;
}

const char *Elf32View::Get_Symbol_Name(Elf32_Word st_name)
{
	check_fp_null();
	fseek(fp, strtab->sh_offset + st_name, SEEK_SET);
	memset(str, 0x00, 128);
	fgets(str, 128, fp);
	return str;
}

const char *Elf32View::Get_Dynamic_Symbol_Name(Elf32_Word st_name)
{
	check_fp_null();
	fseek(fp, dynstr->sh_offset + st_name, SEEK_SET);
	memset(str, 0x00, 128);
	fgets(str, 128, fp);
	return str;
}
const char *Elf32View::Get_Section_Name(Elf32_Word sh_name)
{
	check_fp_null();
	long shstable;		// 섹션헤더의 스트링테이블 오프셋
	Elf32_Shdr *shdr = new Elf32_Shdr;
	fseek(fp, shsstroff, SEEK_SET);
	fread(shdr, sizeof(Elf32_Shdr), 1, fp);
	shstable = shdr->sh_offset;

	fseek(fp, shstable + sh_name, SEEK_SET);
	memset(str, 0x00, 128);
	fgets(str, 128, fp);
	delete shdr;
	return str;
}

// 동적 심볼의 전체 갯수를 구합니다.
int Elf32View::Get_Dynamic_Symbol_Count()
{
	check_fp_count();
	return dynsym->sh_size/sizeof(Elf32_Sym);
}

// 심볼 테이블의 전체 갯수를 구합니다.
int Elf32View::Get_Symbol_Count()
{
	check_fp_count();
	return symtab->sh_size/sizeof(Elf32_Sym);
}

// 프로그램 헤더의 갯수를 구합니다.
int Elf32View::Get_Program_Count()
{
	check_fp_count();
	return ehdr->e_phnum;
}

// 섹션의 갯수를 구합니다.
int Elf32View::Get_Section_Count()
{
	check_fp_count();
	return ehdr->e_shnum;
}

// 동적 재배치 정보의 갯수를 구합니다
int Elf32View::Get_dynRelocation_Count()
{
	check_fp_count();
	return rel_dyn->sh_size / sizeof(Elf32_Rel);
}

// plt 재배치 정보의 갯수를 구합니다
int Elf32View::Get_pltRelocation_Count()
{
	check_fp_count();
	return rel_plt->sh_size / sizeof(Elf32_Rel);
}

// 프로그램 타입을 리턴하는 함수
const char *Elf32View::get_program_type(int p_type)
{
	switch(p_type){
		case 0:   return "PT_NULL";
		case 1:   return "PT_LOAD";
		case 2:   return "PT_DYNAMIC";
		case 3:   return "PT_INTERP";
		case 4:   return "PT_NOTE";
		case 5:   return "PT_SHLIB";
		case 6:   return "PT_PHDR";
		case 0x7000000:   return "PT_LOPROC";
		case 0x7ffffff:   return "PT_HIPROC";
		case 0x6474e551:  return "GNU_STACK";
		default:  return "unknown";
	}
}

// 프로그램 권한 플래그를 리턴하는 함수
const char *Elf32View::get_program_flags(int p_flag)
{
	switch(p_flag){
		case 0:   return "---";
		case 1:   return "--E";
		case 2:   return "-W-";
		case 3:   return "-WE";
		case 4:   return "R--";
		case 5:   return "R-E";
		case 6:   return "RW-";
		case 7:   return "RWE";
		default:  return "???";
	}
}

// 심볼 타입을 리턴하는 함수
const char *Elf32View::get_sym_type(int st_info)
{
	int type = ELF32_ST_TYPE(st_info);
	switch(type){
		case 0:   return "NOTYPE";
		case 1:   return "OBJECT";
		case 2:   return "FUNC";
		case 3:   return "SECTION";
		case 4:   return "FILE";
		case 13:  return "LOPROC";
		case 15:  return "HIPROC";
		default:  return "unknown";
	}
}

// 심볼 바인딩 정보를 리턴하는 함수
const char *Elf32View::get_sym_binding(int st_info)
{ 
	int bind = ELF32_ST_BIND(st_info);
	switch(bind){
		case 0:   return "LOCAL";
		case 1:   return "GLOBAL";
		case 2:   return "WEAK";
		case 13:  return "LOPROC";
		case 15:  return "HIPROC";
		default:  return "unknown";
	}
}

// 재배치 타입을 리턴하는 함수
const char *Elf32View::get_rel_type(int r_info)
{
	int type = ELF32_R_TYPE(r_info);
	switch(type){
		case 0:   return "R_386_NONE";
		case 1:   return "R_386_32";
		case 2:   return "R_386_PC32";
		case 3:   return "R_386_GOT32";
		case 4:   return "R_386_PLT32";
		case 5:   return "R_386_COPY";
		case 6:   return "R_386_GLOB_DAT";
		case 7:   return "R_386_JMP_SLOT";
		case 8:   return "R_386_RELATIVE";
		case 9:   return "R_386_GOTOFF";
		case 10:  return "R_386_GOTPC";
		default:  return "unknown";
	}
}

// 가장 최근에 발생한 에러(예외처리) 내용을 출력해줍니다.
void Elf32View::Last_Error()
{
	if(errno != 0)
	{
		fprintf(stderr, "LAST ERROR : %s\n", Error_List(errno));
		fprintf(stderr, "LAST FUNCTION : %s\n", err_func);
	}
}

// 에러넘버에 해당하는 에러 내용을 리턴합니다.
const char *Elf32View::Error_List(int errno)
{
	switch(errno){
		case 1:		return "NOSUCHFILE (Check File path)";
		case 2:		return "NULLFP (Target File is not opened)";
		case 3:		return "NOSECTION (Have no Section)";
		case 4:		return "OUTOFRANGE (Check Index Range)";
		case 5:		return "NULLNAME (Target Name is NULL)";
		case 6:		return "NOPLTREL (Have no plt Relocation)";
		case 7:		return "NODYNREL (Have no dyn Relocation)";
		case 8:		return "NODYNSYM (Have no dyn Symbol)";
		case 9:		return "NOSYMBOL (Have no Symbol)";
		case 10:	return "NEGINDEX (Index is Negative)";
		case 11:	return "NOARGS (No arguments)";
		case 12:	return "NORESULT (Name Searching failed)";
		case 13:	return "NOELF (This is not ELF File)";
		default:	return "unknown";
	}
}
