C/C++を使用して、ELFファイルを読み込むプログラムを手動で作成します.

21657 ワード

ターゲット:64ビット/32ビットelfファイルを読み込み、section,segments,sectiom to segments mappingを印刷
一、elfファイル解析
この部分はインターネットを参考にしてください.すでに多くのブログがはっきり言っています.
二、コードレイアウト
コードは非常に簡単で、1つのヘッダファイルは操作を宣言するクラスで、1つの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リリースで提供されていない可能性がありますので、対応するシステムに従って調整する必要があります.この結果は、Ubuntuなどのwhereisコマンドを使用して直接検索できます.
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は、segmentsへのsectionのマッピングエラーを計算する(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;

上のコードはいくつかの構造体を定義しています.myElf 64_ShdrとmyElf 32_Shdrは64/32ビットシステムに対応するためのelfファイルである.次にsec 2 SegMappingは、個々のsectionシーケンス番号と名前を保存するために使用され、後で具体的な使い方が表示されます.
class CReadElf
{
private:
	int osType ;
	vector secName;
	FILE * pFile;
	string filename;
	vector mapping;

具体的な作業クラスの一部を説明します.
  • osType:取得したelfファイルが32ビットであるか64ビットであるかを示す
  • secName:一部を保存するためのsection名前
  • pFile:ELFファイルを操作するためのハンドル
  • filename:具体的なelfファイル名
  • mapping:sectionとsegmentに対応するマッピングを保存(今から見れば、ここではmapタイプを直接使うほうがいいかもしれませんが、変更したくない)
  • private:	
    	vector sections;
    	vector segments;	
    	Elf64_Ehdr elfHead64;
    
    private:
    	vector sections32;
    	vector segments32;
    	Elf32_Ehdr elfHead32;

    上の2つのコードは完全にそっくりで、32/64ビットのオペレーティングシステムと互換性があるため、最適化することができます(あなたは見て、私はあまり最適化したくありません).
  • sections:読み込んだsection headerに関する情報(この構造体については最下層のElf 64_Shdr/Elf 32_Shdr、elf.hを参照)
  • を保存する.
  • segments:同
  • 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 

    このコードはstdioなどのcppファイルの準備です.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!
    "); 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!
    "); 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ビットのelfファイルを使用しているか64ビットのelfファイルを使用しているかを判断することである.
  • 最初のifは彼の長さがelf section headerの長さを超えたかどうかを判断し、不足すれば、正しいelf
  • ではないに違いない.
  • 第2の判断は、headbufにelfファイル特有のマークがあるかどうかを見てみましょう(7 f 454 c 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!
    "); 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
    "); 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
    "); 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
    "); 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ビットを扱うか,論理は同じである).
  • secNumは、ELFヘッダ構造体から直接取得することができ、getSectionName()は、各sectionの名前を取得するために使用され、この関数は後述し、取得した名前の数を返し、両者が一致しない場合、問題を説明する.
  • secindexヘッド構造体のe_を保存shoff、このメンバーは「Section header table file offset」を表し、また、ELFファイルに対するsectionのオフセットであり、プログラムはその後、このオフセット量(fseek(pFile,secindex,SEEK_SET);このときpFIleが読み取った位置が実際のSetionが格納している位置となる.
  • プログラムは次に1つのforサイクルでelf 32/64_を読み出すたびにshdrのサイズは、読み込まれた内容(構造体オブジェクト全体を保存:tmpsec.elf 64 sehd=elf 64 Sec);および対応する名前(tmpsec.sh_strname=secName[i];)の後に使用されます.
  • int CReadElf::getfd()
    {
    	pFile = fopen (filename.c_str() , "rb" );
    	if (pFile==NULL) 
    	{
    		printf("File error
    "); 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
    "); delete[] shdr; return FILE_SEEK_ERROR; } sz = fread(shdr, sizeof(Elf64_Shdr) * elfHead64.e_shnum, 1, pFile); if (sz == 0) { printf("file read ERROR
    "); 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
    "); 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
    "); 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
    "); delete[] shdr; return FILE_SEEK_ERROR; } sz = fread(shdr, sizeof(Elf32_Shdr) * elfHead32.e_shnum, 1, pFile); if (sz == 0) { printf("file read ERROR
    "); 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
    "); 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
    "); 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(); }

    上のコードには依然として2つの大きなブランチがあります.1つに注目すればいいです(os 64ブランチに注目しましょう).この関数では
  • は、まずseekのオフセット量を1部、その後、すべてのsection構造体を全て読み出す(sz=fread(shdr,sizeof(Elf 64_Shdr)*elfHead 64.e_shnum, 1, pFile); ここはreadSectionと同じように、当時やっていたとき、ネット上のコードを参考にして最適化することができました.
  • その後、shdrの各要素に対応する(shdr[elfHead 32.e_shstrndx].sh_offset)を使用し、e_shstrndxはSection header string tableindexを表し、一般的には「0から、節名を含む文字列はtableindex番目の節」、sh_offsetはshdrの現在の要素を表す(すなわち、Elf 64/32_Shdr)対応するヘッダテーブルのファイルは、seekが正しい位置
  • にシフトする.
  • 最後に、上記位置から正しいsectionのサイズを読み出し、正しいsectionの名前を1つのforループで読み出す.

  • 注意、この関数は、私が説明したのは非常にはっきりしていないので、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におけるElf 64_についてEhdr/Elf32_Ehdrの解釈
  •   phoffset = elfHead64.e_phoff;/*program header table offset*/すなわち、segmentsのファイルに対するオフセット量は、すべてのプログラムにseekのプロセスがあります.
  •   phnum = elfHead64.e_phnum;/*number of program header entries*/すなわち、segmentsの数なので、プログラムではforループですべてのsegmentsを順次取り出す.
  •  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
    ") ; 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
    "); 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 
    ", elfHead64.e_type); printf("e_machine : %x
    ", elfHead64.e_machine); printf("e_version : %x
    ", elfHead64.e_version); printf("e_entry : %x
    ", elfHead64.e_entry); printf("e_phoff : %x
    ", elfHead64.e_phoff); printf("e_shoff : %x
    ", elfHead64.e_shoff); printf("e_flags : %x
    ", elfHead64.e_flags); printf("e_ehsize : %x
    ", elfHead64.e_ehsize); printf("e_phentsize : %x
    ", elfHead64.e_phentsize); printf("e_phnum : %x
    ", elfHead64.e_phnum); printf("e_shentsize : %x
    ", elfHead64.e_shentsize); printf("e_shnum : %x
    ", elfHead64.e_shnum); printf("e_shstrndx : %x
    ", elfHead64.e_shstrndx); } else { printf("e_type : %x
    ", elfHead32.e_type); printf("e_machine : %x
    ", elfHead32.e_machine); printf("e_version : %x
    ", elfHead32.e_version); printf("e_entry : %x
    ", elfHead32.e_entry); printf("e_phoff : %x
    ", elfHead32.e_phoff); printf("e_shoff : %x
    ", elfHead32.e_shoff); printf("e_flags : %x
    ", elfHead32.e_flags); printf("e_ehsize : %x
    ", elfHead32.e_ehsize); printf("e_phentsize : %x
    ", elfHead32.e_phentsize); printf("e_phnum : %x
    ", elfHead32.e_phnum); printf("e_shentsize : %x
    ", elfHead32.e_shentsize); printf("e_shnum : %x
    ", elfHead32.e_shnum); printf("e_shstrndx : %x
    ", elfHead32.e_shstrndx); } printf("

    "); } void CReadElf::showSection() { if (osType == 64) { if (sections.size() == 0) { printf("empty section map!
    "); return; } printf("%-20s %-8s %-16s %-8s %-16s %-16s %-8s %-8s %-8s %-8s
    ", "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
    ", 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!
    "); return; } printf("%-20s %-8s %-16s %-8s %-16s %-16s %-8s %-8s %-8s %-8s
    ", "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
    ", 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("

    "); } void CReadElf::showSegments() { if (osType == 64) { if (segments.size() == 0) { printf("segments reads err!
    "); return; } printf("%-12s %-18s %-18s %-18s %-18s %-18s %-8s %-8s
    ", "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
    ", 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!
    "); return; } printf("%-12s %-18s %-18s %-18s %-18s %-18s %-8s %-8s
    ", "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
    ", 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("

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

    上記の関数は、結果を出力するために用いられ、言うまでもない.
    次のテストプログラムを追加します.
    /************************************************************************/
    /*	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<

    三、コンパイルと使用説明:
    私はmakefileを手書きで書きました.皆さんは直接彼を実行してもいいし、vscodeで開くこともできます.対応するエンジニアリングファイルがあります.このコードとすべてのファイルをアップロードしました.https://download.csdn.net/download/baijiaheizhiganmao/11293397リンク先にはreadmeファイルもあります.