使用C/C++ 手動編寫一個讀取ELF文件的程序

目標:讀取 64位/32位 elf文件,打印section,segments,sectiom to segments mapping

一,elf文件解析

        這部分內容請參考互聯網,已經有很多博客說的很清楚了。

二,代碼佈局

       代碼非常簡單,一個頭文件用於聲明操作的類,一個cpp文件,用於實現該類,下面先介紹一下頭文件的相關聲明和組成。

/************************************************************************/
/*	AUTHOR	:	FangJianYang											
/*	VERSION	:	01														
/*	TIME	:	2019-02-17												
/*	DESC	:	Some function to read a elf file						
/*	Email	:	[email protected]								
/************************************************************************/
#include <elf.h>
#include <iostream>
#include <string.h>
#include <vector>
#include <map>
#include <string>
using namespace std;


#define SUCCESS			0
#define WRONG_ARGMENTS		-1
#define FILE_OPEN_ERR		-2
#define NOT_A_ELF_FILE		-3
#define READ_FILE_FAILED	-4
#define FILE_SEEK_ERROR		-5
#define GET_SEC_NAME_ERR	-6
#define CALC_MAPPING_ERROR	-7

上面幾行代碼,主要關注這麼幾行:

1. elf.h 頭文件可能不是所有Linux發行版都提供,因此需要按照對應的系統做調整。可以直接使用whereis 命令去查找,比如在Ubuntu上是這個結果:

xxx@ubuntu:~xxx$ whereis elf
elf: /usr/include/elf.h /usr/share/man/man5/elf.5.gz

2. 我定義了幾個宏,用於表示程序的錯誤碼,其中:

WRONG_ARGMENTS       表示:用戶輸入了錯誤的參數(本例程並不支持所有參數)

GET_SEC_NAME_ERR     表示:獲取section名字失敗(關於section的名字,可以搜索elf格式來獲取更多詳情)

CALC_MAPPING_ERROR 表示:計算section到segments的映射錯誤(同上,搜索elf格式獲取更多詳情)

typedef struct
{
	string 		sh_strname;
	Elf64_Shdr 	elf64sehd;
}myElf64_Shdr;

typedef struct
{
	string 		sh_strname;
	Elf32_Shdr 	elf32sehd;
}myElf32_Shdr;

typedef struct  sec2SegMapping
{
	int index;
	string secNameVec;
public:
	sec2SegMapping(int id,string arg):index(id),secNameVec(arg){}
}sec2SegMapping;

上面的代碼我定義了幾個結構體,其中myElf64_Shdr和myElf32_Shdr是爲了兼容64/32位系統的elf文件。然後sec2SegMapping是用於保存單個的section序號和名字,後面會看到具體用法。

class CReadElf
{
private:
	int osType ;
	vector<string> secName;
	FILE * pFile;
	string filename;
	vector<sec2SegMapping> mapping;

上面是具體工作類的部分描述,其中

  1.  osType:表示獲取的elf文件是32還是64位的
  2. secName:用於保存沒一部分的section名字
  3. pFile:用於操作ELF文件的句柄
  4. filename:具體的elf文件名字
  5. mapping:保存section和segment對應的映射(現在看來,這裏直接用map類型可能更好一點,但是我不想改了)
private:	
	vector<myElf64_Shdr> sections;
	vector<Elf64_Phdr> segments;	
	Elf64_Ehdr elfHead64;

private:
	vector<myElf32_Shdr> sections32;
	vector<Elf32_Phdr> segments32;
	Elf32_Ehdr elfHead32;

上面兩段代碼是完全一模一樣的,只是爲了兼容32/64位操作系統,可以優化(你看出來了,我不怎麼想優化)。

  1. sections:用於保存讀取到的section header相關信息(關於這個結構體——最底層的Elf64_Shdr/Elf32_Shdr,請查看elf.h)
  2. segments:同上
  3. Elf32/64_Ehdr:查看elf.h 文件,我只是需要在解析出來後,保存他們,將來直接拿來用
public:
	CReadElf(char *argc);
	virtual ~CReadElf();
	int getfd();

public:
	int readFile();
	int readSection();
	int readSegment();
	int getSectionName();
	int caclSec2Segments();
	

	void showHead();
	void showSection();
	void showSegments();
	void showSec2SegMapping();
};

上面代碼聲明瞭一些函數,函數功能可以直接從函數名字上大致獲得,不做解析。

下面簡要講解一下cpp代碼實現

/***********************************************************************/
/*	AUTHOR	:	FangJianYang											
/*	VERSION	:	01														
/*	TIME	:	2019-02-17												
/*	DESC	:	Some function to read a elf file						
/*	Email	:	[email protected]								
/************************************************************************/
#include "readelf.h"
#include <stdio.h>
#include <stdlib.h>

        這段代碼是cpp文件的一些準備工作,比如用到了stdio.h庫文件,主要是需要用到他的輸入和輸出。stdlib.h頭文件,主要是用到了他的一些字符串操作函數。

CReadElf::CReadElf(char *argc):osType(0),filename(""),pFile(NULL)
{
	if (argc!=NULL && strlen(argc) != 0)
	{
		filename= argc;
	}
}

CReadElf::~CReadElf()
{
	if(pFile != NULL)
		fclose(pFile);
}

       構造函數和析構函數的實現。在構造函數內我們檢查了一下參數是否正確(是否將要解析的elf文件傳遞了過來),在析構函數內,簡單判斷了一下是否需要關閉文件句柄(說句柄可能不太準確,因爲Linux沒這個概念,但是我沒想出來那個正確的叫法)。

int CReadElf::readFile()
{
	long lSize;
	size_t result;

	// obtain file size:
	fseek (pFile , 0 , SEEK_END);
	lSize = ftell (pFile);
	rewind (pFile);

	if(lSize < sizeof(Elf32_Ehdr) || lSize < sizeof(Elf64_Ehdr) )
	{
		printf("not a elf file!\n");
		return NOT_A_ELF_FILE;
	}


	char headbuf[EI_NIDENT] = {0};
	result = fread(headbuf,1,EI_NIDENT,pFile);

	//judge if this is a elf file
	if(
		headbuf[0] != 0x7f && headbuf[1] != 0x45 &&
		headbuf[2] != 0x4c && headbuf[3] != 0x46
		)    
	{
		printf("not a elf file!\n");
		return NOT_A_ELF_FILE;
	}

	rewind(pFile);
	if(headbuf[4] == 0x02)
	{
		fread (&elfHead64,1,sizeof(Elf64_Ehdr),pFile);
		osType = 64;
	}
	else
	{
		fread (&elfHead32,1,sizeof(Elf32_Ehdr),pFile);
		osType = 32;
	}
	rewind(pFile);
	return SUCCESS;
}

    上述代碼,主要作用是讀取用戶指定的文件,然後判斷是否是一個elf文件,假如是的話,看他對用的是32位的還是64爲的elf文件。

  1. 第一個if判斷他的長度是否超過了elf section header的長度,假如不足,那麼肯定不是正確的elf
  2. 第二個判斷,看headbuf 中是否有elf文件特有的標記(7f 45 4c 46是ELF頭裏面特有的,稱之爲magic,中文叫魔數,實際上不止這幾個,詳情可搜索readelf -h 命令)
  3. 第三個判斷,用於判斷當前的elf是作用域32還是64位系統上的
int CReadElf::readSection()
{
	size_t result;
	if(osType == 64)
	{
		size_t secNum = elfHead64.e_shnum;
		int secindex = elfHead64.e_shoff;


		if(getSectionName()!= secNum)
		{
			printf("get section head failed!\n");
			return GET_SEC_NAME_ERR;
		}
		//go to the entry of section
		fseek(pFile,secindex,SEEK_SET);

		Elf64_Shdr elf64Sec;
		
		for(int i = 0;i < secNum;i++)
		{
			result = fread(&elf64Sec,1,sizeof(Elf64_Shdr),pFile); 
			if(result != sizeof(Elf64_Shdr))
			{
				printf("read file failed\n");
				return READ_FILE_FAILED;
			}
			myElf64_Shdr tmpsec;
			tmpsec.sh_strname = secName[i];
			tmpsec.elf64sehd = elf64Sec;
			sections.push_back(tmpsec);   
		}
	}
	else
	{
		size_t secNum = elfHead32.e_shnum;
		int secindex = elfHead32.e_shoff;

		if(getSectionName()!= secNum)
		{
			printf("get section head failed\n");
			return GET_SEC_NAME_ERR;
		}
		//go to the entry of section
		fseek(pFile,secindex,SEEK_SET);

		Elf32_Shdr elf32Sec;
		
		for(int i = 0;i < secNum;i++)
		{
			result = fread(&elf32Sec,1,sizeof(Elf32_Shdr),pFile); 
			if(result != sizeof(Elf32_Shdr))
			{
				printf("read file failed\n");
				return READ_FILE_FAILED;
			}
			myElf32_Shdr tmpsec;
			tmpsec.sh_strname = secName[i];
			tmpsec.elf32sehd = elf32Sec;
			sections32.push_back(tmpsec);   
		}
	}
	rewind(pFile);
	return SUCCESS;  
}

     這個函數,關注任意一個分支即可(要麼處理64位ELF要麼處理32位,邏輯都一樣)。

  1. secNum 可以直接從ELF頭結構體中獲得,getSectionName()用於獲取每一個section的名字,該函數在後面介紹,返回獲取的名字數量,假如兩者對不上,說明出問題。
  2. secindex 保存頭結構體中的e_shoff,該成員表示“Section header table file offset”,既,section相對於ELF文件的偏移,程序隨後seek了這個偏移量(fseek(pFile,secindex,SEEK_SET);)此時pFIle所讀取的位置就是實際的Setion存儲的位置了。
  3. 程序接下來用一個for循環,每次讀取一個elf32/64_shdr的大小,將讀取出來的內容保存起來(保存整個結構體對象:tmpsec.elf64sehd = elf64Sec);以及他對應的名字(tmpsec.sh_strname = secName[i];)後面會用到。
int CReadElf::getfd()
{
	pFile = fopen (filename.c_str() , "rb" );
	if (pFile==NULL) 
	{
		printf("File error\n");
		return FILE_OPEN_ERR;
	}
	return SUCCESS;
}

這個函數就沒有很多要解釋的了。可能函數名改動一下會更合適。

int CReadElf::getSectionName()
{
	if (osType == 64)
	{
		Elf64_Shdr *shdr = new Elf64_Shdr[sizeof(Elf64_Shdr) * elfHead64.e_shnum];

		int sz = fseek(pFile, elfHead64.e_shoff, SEEK_SET);
		if (sz != 0)
		{
			printf("file fseek ERROR\n");
			delete[] shdr;
			return FILE_SEEK_ERROR;
		}
		sz = fread(shdr, sizeof(Elf64_Shdr) * elfHead64.e_shnum, 1, pFile);
		if (sz == 0)
		{
			printf("file read ERROR \n");
			delete[] shdr;
			return READ_FILE_FAILED;
		}
		rewind(pFile);

		sz = fseek(pFile, shdr[elfHead64.e_shstrndx].sh_offset, SEEK_SET);
		if (sz != 0)
		{
			printf("file fseek ERROR\n");
			delete[] shdr;
			return FILE_SEEK_ERROR;
		}
		char shstrtab[shdr[elfHead64.e_shstrndx].sh_size];
		char *temp = NULL;
		sz = fread(shstrtab, shdr[elfHead64.e_shstrndx].sh_size, 1, pFile);
		if (sz == 0)
		{
			printf("file fread ERROR\n");
			delete[] shdr;
			return FILE_SEEK_ERROR;
		}
		for (int shnum = 0; shnum < elfHead64.e_shnum; shnum++)
		{
			temp = shstrtab;
			temp = temp + shdr[shnum].sh_name;
			secName.push_back(string(temp));
		}
		delete[] shdr;
	}
	else
	{
		Elf32_Shdr *shdr = new Elf32_Shdr[sizeof(Elf32_Shdr) * elfHead32.e_shnum];

		int sz = fseek(pFile, elfHead32.e_shoff, SEEK_SET);
		if (sz != 0)
		{
			printf("file fseek ERROR\n");
			delete[] shdr;
			return FILE_SEEK_ERROR;
		}
		sz = fread(shdr, sizeof(Elf32_Shdr) * elfHead32.e_shnum, 1, pFile);
		if (sz == 0)
		{
			printf("file read ERROR \n");
			delete[] shdr;
			return READ_FILE_FAILED;
		}
		rewind(pFile);
		sz = fseek(pFile, shdr[elfHead32.e_shstrndx].sh_offset, SEEK_SET);
		if (sz != 0)
		{
			printf("file fseek ERROR\n");
			delete[] shdr;
			return FILE_SEEK_ERROR;
		}
		char shstrtab[shdr[elfHead32.e_shstrndx].sh_size];
		char *temp = NULL;
		sz = fread(shstrtab, shdr[elfHead32.e_shstrndx].sh_size, 1, pFile);
		if (sz == 0)
		{
			printf("file fread ERROR\n");
			delete[] shdr;
			return FILE_SEEK_ERROR;
		}
		for (int shnum = 0; shnum < elfHead32.e_shnum; shnum++)
		{
			temp = shstrtab;
			temp = temp + shdr[shnum].sh_name;
			secName.push_back(string(temp));
		}
		delete[] shdr;
	}
	rewind(pFile);
	return secName.size();	
}

    上面代碼依然有兩個大的分支,我們關注一個即可(關注os64分支吧)。在該函數中

  1. 首先seek了一份偏移量,然後將所有的section結構體全部讀取出來了(sz = fread(shdr, sizeof(Elf64_Shdr) * elfHead64.e_shnum, 1, pFile); 這裏和readSection套路一樣,當時做的時候,參考了一些網上的代碼,可以進行優化,畢竟此處畫蛇添足了);
  2. 然後我們用shdr的每個元素對應的(shdr[elfHead32.e_shstrndx].sh_offset),其中e_shstrndx 表示Section header string tableindex ,通俗點“從0開始,包含節名稱的字符串是第tableindex個節”,sh_offset表示shdr當前元素(即Elf64/32_Shdr)對應的頭部表的文件偏移,這樣seek到一個正確的位置
  3. 最後,獲取了從上述位置,讀取正確的section的大小,用一個for循環讀取正確的section名字。

注意,這個函數,我解釋的不是非常清晰,可以參考elf.h上的解釋,當時做的時候,這個函數我參考了其他人的博客。特此說明!

int CReadElf::readSegment()
{
	if (osType == 64)
	{
		int phoffset = elfHead64.e_phoff;
		int phnum = elfHead64.e_phnum;
		int phentsize = elfHead64.e_phentsize;

		fseek(pFile, phoffset, SEEK_SET);
		for (int i = 0; i < phnum; i++)
		{
			Elf64_Phdr Pro_header;
			if (fread(&Pro_header, 1, phentsize, pFile) != phentsize)
			{
				printf("read segments err!");
				return READ_FILE_FAILED;
			}
			segments.push_back(Pro_header);
		}
	}
	else
	{
		int phoffset = elfHead32.e_phoff;
		int phnum = elfHead32.e_phnum;
		int phentsize = elfHead32.e_phentsize;

		fseek(pFile, phoffset, SEEK_SET);
		for (int i = 0; i < phnum; i++)
		{
			Elf32_Phdr Pro_header;
			if (fread(&Pro_header, 1, phentsize, pFile) != phentsize)
			{
				printf("read segments err!");
				return READ_FILE_FAILED;
			}
			segments32.push_back(Pro_header);
		}
	}
	return SUCCESS;
}

這段代碼我認爲沒啥解釋的,參考elf.h中關於Elf64_Ehdr/Elf32_Ehdr的解釋

  1.   phoffset = elfHead64.e_phoff;  /* program header table offset */即,segments相對於文件的偏移量,所有程序中有一個seek的過程。
  2.   phnum = elfHead64.e_phnum;  /* number of program header entries */ 即,segments的數量,所以程序中用for循環依次拿出所有segments。
  3.  phentsize = elfHead64.e_phentsize;  /* section header entry size */ 即,每個segments的大小,所以程序中每次read才能正確。
/**********************************section to Segment mapping  計算方法:***************************
 首先,查看program Headers中的各個字段的地址 virtAddr (或 physAddr 一般這兩個字段的值是一樣的).         
 表示這個*segment的開始地址,然後看MenSiz(注意,不是FileSize).表示這個segment可以組裝的section的大小.    
 我們對上面兩個值相加,獲得這個segment的開始地址和結束地址.比如LOADsegment:                       
 開始地址爲: 0x0000000000400000,結束地址爲 0x00000000004007dc.                                   
 然後明確: elf會將地址連續的section分配到同一個segment,前提是這些section的地址在某個segment的範圍內.    
 比如說:Section Headers 字段中,分佈在地址 0x0000000000400000 - 0x00000000004007dc                
 剛好是 "interp .note.ABI-tag .note.gnu.build*id .gnu.hash .dynsym                            
.dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt                           
.text .fini .rodata .eh_frame_hdr .eh_frame"                                                 
 其中過最後一個section的開始地址爲 4006e8 大小爲 f4 相加後剛好爲:4007DC.                            
***********************************************************************************************/
int CReadElf::caclSec2Segments()
{
	if (osType == 64)
	{
		if (sections.size() == 0 || segments.size() == 0)
		{
			printf( "can not cal sectiong to segments mapping\n") ;
			return CALC_MAPPING_ERROR;
		}

		for (vector<Elf64_Phdr>::iterator pit = segments.begin(); pit != segments.end(); pit++)
		{
			string belongSec("");

			Elf64_Addr startAddr = pit->p_paddr;
			Elf64_Addr endAddr = pit->p_paddr + pit->p_memsz;
			for (vector<myElf64_Shdr>::iterator sit = sections.begin(); sit != sections.end(); sit++)
			{
				Elf64_Addr secBeginAddr = sit->elf64sehd.sh_addr;
				Elf64_Addr secEndAddr = sit->elf64sehd.sh_addr + sit->elf64sehd.sh_size;
				if (secBeginAddr >= startAddr && secEndAddr <= endAddr)
				{
					belongSec.append(sit->sh_strname);
					belongSec.append(" ");
				}
			}
			sec2SegMapping tmpmap(mapping.size(), belongSec);
			mapping.push_back(tmpmap);
		}
	}
	else
	{
		if (sections32.size() == 0 || segments32.size() == 0)
		{
			printf("can not cal sectiong to segments mapping\n");
			return CALC_MAPPING_ERROR;
		}

		for (vector<Elf32_Phdr>::iterator pit = segments32.begin(); pit != segments32.end(); pit++)
		{
			string belongSec("");

			Elf32_Addr startAddr = pit->p_paddr;
			Elf32_Addr endAddr = pit->p_paddr + pit->p_memsz;
			for (vector<myElf32_Shdr>::iterator sit = sections32.begin(); sit != sections32.end(); sit++)
			{
				Elf32_Addr secBeginAddr = sit->elf32sehd.sh_addr;
				Elf32_Addr secEndAddr = sit->elf32sehd.sh_addr + sit->elf32sehd.sh_size;
				if (secBeginAddr >= startAddr && secEndAddr <= endAddr)
				{
					belongSec.append(sit->sh_strname);
					belongSec.append(" ");
				}
			}
			sec2SegMapping tmpmap(mapping.size(), belongSec);
			mapping.push_back(tmpmap);
		}
	}
	return SUCCESS;	
}

這段代碼是用於整理segments和section的映射的,關鍵在於代碼中的註釋說明,他說明了他們的關係是如何組織的。關於他的驗證可以手動readelf一個文件後,動手算一算。該函數不在解釋,原理懂了就特別簡單。

特別說明:註釋中舉得例子,只是我電腦運行的結構,相關的地址位置,需要結合實際去驗證。

void CReadElf::showHead()
{
	if (osType == 64)
	{
		printf("e_type		 : %x \n", elfHead64.e_type);
		printf("e_machine	 : %x \n", elfHead64.e_machine);
		printf("e_version	 : %x \n", elfHead64.e_version);
		printf("e_entry		 : %x \n", elfHead64.e_entry);
		printf("e_phoff		 : %x \n", elfHead64.e_phoff);
		printf("e_shoff		 : %x \n", elfHead64.e_shoff);
		printf("e_flags		 : %x \n", elfHead64.e_flags);
		printf("e_ehsize	 : %x \n", elfHead64.e_ehsize);
		printf("e_phentsize	 : %x \n", elfHead64.e_phentsize);
		printf("e_phnum		 : %x \n", elfHead64.e_phnum);
		printf("e_shentsize	 : %x \n", elfHead64.e_shentsize);
		printf("e_shnum		 : %x \n", elfHead64.e_shnum);
		printf("e_shstrndx	 : %x \n", elfHead64.e_shstrndx);
	}
	else
	{
		printf("e_type		 : %x \n", elfHead32.e_type);
		printf("e_machine	 : %x \n", elfHead32.e_machine);
		printf("e_version	 : %x \n", elfHead32.e_version);
		printf("e_entry		 : %x \n", elfHead32.e_entry);
		printf("e_phoff		 : %x \n", elfHead32.e_phoff);
		printf("e_shoff		 : %x \n", elfHead32.e_shoff);
		printf("e_flags		 : %x \n", elfHead32.e_flags);
		printf("e_ehsize	 : %x \n", elfHead32.e_ehsize);
		printf("e_phentsize	 : %x \n", elfHead32.e_phentsize);
		printf("e_phnum		 : %x \n", elfHead32.e_phnum);
		printf("e_shentsize	 : %x \n", elfHead32.e_shentsize);
		printf("e_shnum		 : %x \n", elfHead32.e_shnum);
		printf("e_shstrndx	 : %x \n", elfHead32.e_shstrndx);
	}
	printf("\n\n");
}

void CReadElf::showSection()
{
	if (osType == 64)
	{
		if (sections.size() == 0)
		{
			printf("empty section map!\n");
			return;
		}
		printf("%-20s %-8s %-16s %-8s %-16s %-16s %-8s %-8s %-8s %-8s\n",
			   "Name", "Type", "Address", "Offset", "Size", "EntSize", "Flags", "Link", "Info", "Align");
		for (vector<myElf64_Shdr>::iterator pit = sections.begin(); pit != sections.end(); pit++)
		{
			printf("%-20s %-8x %016x %08x %016x %016x %-8x %-8x %-8x %-8x\n",
				   pit->sh_strname.c_str(),
				   pit->elf64sehd.sh_type,
				   pit->elf64sehd.sh_addr,
				   pit->elf64sehd.sh_offset,
				   pit->elf64sehd.sh_size,
				   pit->elf64sehd.sh_entsize,
				   pit->elf64sehd.sh_flags,
				   pit->elf64sehd.sh_link,
				   pit->elf64sehd.sh_info,
				   pit->elf64sehd.sh_addralign
			);
		}
	}
	else
	{
		if (sections32.size() == 0)
		{
			printf("empty section map!\n");
			return;
		}
		printf("%-20s %-8s %-16s %-8s %-16s %-16s %-8s %-8s %-8s %-8s\n",
			   "Name", "Type", "Address", "Offset", "Size", "EntSize", "Flags", "Link", "Info", "Align");
		for (vector<myElf32_Shdr>::iterator pit = sections32.begin(); pit != sections32.end(); pit++)
		{
			printf("%-20s %-8x %016x %08x %016x %016x %-8x %-8x %-8x %-8x\n",
				   pit->sh_strname.c_str(),
				   pit->elf32sehd.sh_type,
				   pit->elf32sehd.sh_addr,
				   pit->elf32sehd.sh_offset,
				   pit->elf32sehd.sh_size,
				   pit->elf32sehd.sh_entsize,
				   pit->elf32sehd.sh_flags,
				   pit->elf32sehd.sh_link,
				   pit->elf32sehd.sh_info,
				   pit->elf32sehd.sh_addralign
			);
		}
	}

	printf("\n\n");
}

void CReadElf::showSegments()
{
	if (osType == 64)
	{
		if (segments.size() == 0)
		{
			printf("segments reads err!\n");
			return;
		}
		printf("%-12s %-18s %-18s %-18s %-18s %-18s %-8s %-8s\n",
			   "Type", "Offset", "VirtAddr", "PhysAddr", "FileSiz", "MemSiz", "Flags", "Align");
		for (vector<Elf64_Phdr>::iterator pit = segments.begin(); pit != segments.end(); pit++)
		{
			printf("%-12x 0x%016x 0x%016x 0x%016x 0x%016x 0x%016x %-8x %-8x\n",
				   pit->p_type,
				   pit->p_offset,
				   pit->p_vaddr,
				   pit->p_paddr,
				   pit->p_filesz,
				   pit->p_memsz,
				   pit->p_flags,
				   pit->p_align
			);
		}
	}
	else
	{
		if (segments32.size() == 0)
		{
			printf("segments reads err!\n");
			return;
		}
		printf("%-12s %-18s %-18s %-18s %-18s %-18s %-8s %-8s\n",
			   "Type", "Offset", "VirtAddr", "PhysAddr", "FileSiz", "MemSiz", "Flags", "Align");
		for (vector<Elf32_Phdr>::iterator pit = segments32.begin(); pit != segments32.end(); pit++)
		{
			printf("%-12x 0x%016x 0x%016x 0x%016x 0x%016x 0x%016x %-8x %-8x\n",
				   pit->p_type,
				   pit->p_offset,
				   pit->p_vaddr,
				   pit->p_paddr,
				   pit->p_filesz,
				   pit->p_memsz,
				   pit->p_flags,
				   pit->p_align
			);
		}		
	}
	printf("\n\n");
	
}


void CReadElf::showSec2SegMapping()
{
	if(mapping.size() == 0)
	{
		printf("mapping error!\n");
		return;
	}
	for(vector<sec2SegMapping>::iterator pit = mapping.begin();pit != mapping.end();pit++)
	{
		printf("%d\t%s\n",pit->index,pit->secNameVec.c_str());
	}
	printf("\n");
}

上述函數,用於輸出結果,不贅述。

下面附加一個測試程序:

/************************************************************************/
/*	AUTHOR	:	FangJianYang											
/*	VERSION	:	01														
/*	TIME	:	2019-02-17												
/*	DESC	:	Some function to read a elf file						
/*	Email	:	[email protected]								
/************************************************************************/

#include "readelf.h"

int main(int argc,char** argv)
{
	if(argc != 2)
	{
		cout<<"argments err : you should enter 2 argments!"<<endl;
		return WRONG_ARGMENTS;
	}

	CReadElf obj(argv[1]);
	int rt = obj.getfd();
	if(rt != SUCCESS)
	{
		return rt;
	}

	rt = obj.readFile();
	if(rt != SUCCESS)
	{
		return rt;
	}

	cout<<"ELF head infomation"<<endl;
	obj.showHead();


	cout<<"section infomation"<<endl;
	rt = obj.readSection();
	if(rt != SUCCESS)
	{
		return rt;
	}
	obj.showSection();

	cout<<"segment infomation"<<endl;
	rt = obj.readSegment();
	if(rt != SUCCESS)
	{
		return rt;
	}
	obj.showSegments();

	rt = obj.caclSec2Segments();
	if(rt != SUCCESS)
	{
		return rt;
	}	

	cout<<"section to segment mapping"<<endl;
	obj.showSec2SegMapping();

	return 0;
}

三 ,編譯和使用說明:

我手寫了一個makefile,各位可以直接運行他即可,也可以用vscode打開,有相應的工程文件。我將這段代碼和所有文件都上傳到了XXX鏈接處(還在審覈中,等通過了,我吧這段給補上)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章