使用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 
#include 
#include 
#include 
#include 
#include 
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 secName;
	FILE * pFile;
	string filename;
	vector mapping;

上面是具体工作类的部分描述,其中

  1.  osType:表示获取的elf文件是32还是64位的
  2. secName:用于保存没一部分的section名字
  3. pFile:用于操作ELF文件的句柄
  4. filename:具体的elf文件名字
  5. mapping:保存section和segment对应的映射(现在看来,这里直接用map类型可能更好一点,但是我不想改了)
private:	
	vector sections;
	vector segments;	
	Elf64_Ehdr elfHead64;

private:
	vector sections32;
	vector 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 
#include 

        这段代码是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::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::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::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::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::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::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::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::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::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!"<

三 ,编译和使用说明:

我手写了一个makefile,各位可以直接运行他即可,也可以用vscode打开,有相应的工程文件。我将这段代码和所有文件都上传到了https://download.csdn.net/download/baijiaheizhiganmao/11293397链接处,该链接里面也有一个readme文件。

你可能感兴趣的:(C++,ubuntu)