ELF文件解析和加载(附代码)

ELF文件解析和加载(附代码)目录 1 elf 文件基本概念 2 elf 文件结构 3 elf 文件装载 4 代码实现 1 elf 文件基本概念 elf 文件是一种目标文件格式 用于定义不同类型目标文件以什么样的格式 都放了些什么东西 主要用于 linux 平台 windows 下是 PE COFF 格式 可执行文件 可重定位文件 o 共享目标文件 so 核心转储文件都是以 elf 文件格式存

目录:

1.    elf文件基本概念

2.    elf文件结构


3.    elf文件装载

4.    代码实现

1.elf文件基本概念

elf文件是一种目标文件格式,用于定义不同类型目标文件以什么样的格式,都放了些什么东西。主要   用于linux平台。windows下是PE/COFF格式。       

    可执行文件、可重定位文件(.o)、共享目标文件(.so)、核心转储文件都是以elf文件格式存储的。

ELF文件组成部分:文件头、段表(section)、程序头

2.elf文件结构 —- 文件头

文件头数据结:

ELF文件解析和加载(附代码)

 

ccs中解析出来的文件头信息
 

ELF文件解析和加载(附代码)

从上图中可以看到,elf文件头定义了文件的整体属性信息,比较重要的几个属性是:魔术字,入口地址,程序头位置、长度和数量,文件头大小(52字节),段表位置、长度和 个数。

2.elf文件结构—段表

段表(section)数据结构

 

 

ELF文件解析和加载(附代码)

 

 

 

解析段表内容
ELF文件解析和加载(附代码)

 

几个重要的段:.text(代码)段 、.data(数据)段、.bss段 。

.text:保存程序代码,权限为只读

.data:保存已初始化的全局变量和局部静态变量,可读可写

.bss:保存未初始化的全局变量和局部静态变量。初始化为0的变量也会保存在.bss段。可读可写。

2.elf文件结构 —- 程序头

在ELF中把权限相同、又连在一起的段(section)叫做segment,操作系统正是按照“segment”来映射可执行文件的。

描述这些“segment”的结构叫做程序头,它描述了elf文件该如何被操作系统映射到内存空间中。

程序头数据结构

ELF文件解析和加载(附代码)

解析的程序头内容

ELF文件解析和加载(附代码)

由上图的程序头可知,该elf文件有9个LOAD类型的segment,因为只有LOAD类型是需要被映射的。

我们需要做的是找到这些segment在文件中的位置,并将其加载到对应的内存空间,对于memsiz大于filesiz的部分全部填充为0,加载完之后让程序跳转到入口地址

3.elf文件装载

ELF文件解析和加载(附代码)

代码实现

#include 
  
    / * hello.c */ #include 
   
     #include 
    
      #include 
     
       #include 
      
        #include 
       
         //#include 
        
          #define ELF_HEAD_LENGTH 128 #define MAGIC_NUM 0x464c457f #define SECTION_TABLE_MAX 40 static uint32_t SecTableStrIndx = 0; uint32_t SecStrTableOff = 0; uint16_t SecTabNum = 0; /elf 文件头和section表解析和检查*/ int check_elf_head(FILE *file ) { FILE *file_elf = file; int i = 0; int MagNum = 0; unsigned long file_size = 0; int sechnum = 0; uint32_t symsize = 0; uint32_t symoff = 0; uint32_t nSyms = 0,kk=0; struct Elf32_Ehdr *Elf_header = NULL; struct Elf32_Shdr *Section_header = NULL; struct Elf32_Sym *Symbol_tab = NULL; //file_elf = fopen(path,"r"); /*文件大小*/ fseek(file_elf,0,SEEK_END); file_size = ftell(file_elf); fseek(file_elf,0,SEEK_SET); printf("file total size is:%ld bytes\n",file_size); fread(Elf_header,sizeof(struct Elf32_Ehdr),1,file_elf); printf("\nSection Name String Table index: %d\n",SecTableStrIndx); /ELF header解析/ printf("Magic:\t"); for(MagNum=0; MagNum<16; MagNum++) { printf("%02x ",Elf_header->e_ident[MagNum]); } /确认是否为elf格式/ if((Elf_header->e_ident[0] == '\x7f') && (Elf_header->e_ident[1] == ELFMAG1) \ && (Elf_header->e_ident[2] == ELFMAG2) && (Elf_header->e_ident[3] == ELFMAG3)) { printf("\nThis is ELF file!\n"); } else { printf("\n NOT ELF file!\n"); return -1; } printf("\n"); printf("Type: \t%d\n",Elf_header->e_type); printf("Machine: \t%d\n",Elf_header->e_machine); /* Architecture */ printf("Version: \t%#02x\n",Elf_header->e_version); printf("Entry point address: \t%#02x\n",Elf_header->e_entry); printf("Start of program headers: \t%d(bytes)\n",Elf_header->e_phoff); printf("Start of section headers: \t%d(bytes)\n",Elf_header->e_shoff); printf("Flags: \t%#02x\n",Elf_header->e_flags); printf("Size of this header: \t%d(bytes)\n",Elf_header->e_ehsize); printf("Size of program headers: \t%d(bytes)\n",Elf_header->e_phentsize); printf("Number of program headers: \t%d\n",Elf_header->e_phnum); printf("Size of section headers: \t%d(bytes)\n",Elf_header->e_shentsize); printf("Number of section headers: \t%d\n",Elf_header->e_shnum); printf("Section header string table index:\t%d\n",Elf_header->e_shstrndx); if(Elf_header->e_ehsize != sizeof(*Elf_header)) { printf("\nELF file header size is err\n!"); return -1; } if(Elf_header->e_type != ET_REL && Elf_header->e_type != ET_EXEC ) { printf("file type is err!__FUNCTION__ %s __LINE__ %d\n",__FUNCTION__,__LINE__); } /section header/ sechnum = Elf_header->e_shnum; fseek(file_elf,Elf_header->e_shoff,SEEK_SET); printf("\n/*section header table/\n"); fread(Section_header,sizeof(struct Elf32_Shdr),sechnum,file_elf); printf("[Nr] Name Type Addr Off Size ES Flg Al"); for(i=0; i 
         
           sh_name,Section_header->sh_type,Section_header->sh_addr,\ Section_header->sh_offset,Section_header->sh_size,Section_header->sh_entsize,\ Section_header->sh_flags,Section_header->sh_addralign); if(Section_header->sh_type == 2)/*if symtab*/ { symsize = Section_header->sh_size; symoff = Section_header->sh_offset; nSyms = symsize/(Section_header->sh_entsize); fseek(file_elf,symoff,SEEK_SET); fread(Symbol_tab,sizeof(struct Elf32_Sym),nSyms ,file_elf); } Section_header++; } printf("\n\n*symbol table"); printf("\nid size other bind\n"); /*while(kk < nSyms) { if(Symbol_tab->st_shndx == 2) //ext { printf("[%d] %x %x %x\n",kk,Symbol_tab->st_size,\ Symbol_tab->st_other,(Symbol_tab->st_info)>>4 ); } kk++; Symbol_tab++; }*/ return 0; } /* Program Header Table ,只有可执行文件和共享文件有程序头,linux命令:readelf -l xx 可查看结构*/ void ProgramHeadInfo(FILE *file,struct Elf32_Phdr *ProHead) { FILE *file_elf = file; struct Elf32_Ehdr *Elf_header = NULL; struct Elf32_Phdr *Pro_header = NULL; uint32_t phoffset = 0; //程序头表偏移 uint16_t phnum = 0;//程序头表数目 uint16_t phentsize = 0;// 程序头表大小 int num = 0; Elf_header = (struct Elf32_Ehdr *)malloc(sizeof(struct Elf32_Ehdr)); memset(Elf_header,0,sizeof(struct Elf32_Ehdr)); fseek(file_elf,0,SEEK_SET); fread(Elf_header,sizeof(struct Elf32_Ehdr),1,file_elf); phoffset = Elf_header->e_phoff; phnum = Elf_header->e_phnum; phentsize = Elf_header->e_phentsize; fseek(file_elf,phoffset,SEEK_SET); //fread((struct Elf32_Phdr*)Pro_header,phentsize,phnum,file_elf); fread((struct Elf32_Phdr *)Pro_header,phentsize,phnum,file_elf); //ProHead = Pro_header; memcpy((char *)ProHead, (char *)Pro_header, sizeof(struct Elf32_Ehdr)); printf("\n/*Program Headers:*/\n"); printf("starting at offset: %d\n",phoffset); printf("Number of program headers: %d\n",phnum); printf("Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg\n"); for(num=0; num 
          
            p_type,Pro_header->p_offset,\ Pro_header->p_vaddr,Pro_header->p_paddr\ ,Pro_header->p_filesz,Pro_header->p_memsz,Pro_header->p_flags); Pro_header++; } free(Elf_header); Elf_header = NULL; return; } /加载elf文件segment 到内存中 返回值: elf的entry point/ typedef void (*pCALLFUNC)(void); uint32_t LoadElf2Mem(FILE *file ) { struct Elf32_Phdr *ProHead = NULL; struct Elf32_Ehdr *Elf_header = NULL; FILE *file1 = file; uint16_t i = 0; int size=0; uint32_t addrPoint = 0; Elf_header = (struct Elf32_Ehdr *)malloc(sizeof(struct Elf32_Ehdr)); memset(Elf_header,0,sizeof(struct Elf32_Ehdr)); fseek(file1,0,SEEK_SET); size = fread(Elf_header, sizeof(struct Elf32_Ehdr), 1, file1); ProgramHeadInfo(file1,ProHead); for(i=0;i< Elf_header->e_phnum; i++,ProHead++) { if(ProHead->p_type != PT_LOAD ) { printf("\nnot PT_LOAD %d %d size: %d\n", Elf_header->e_phnum,ProHead->p_type,size); continue; } if(ProHead->p_filesz) { fseek( file1,ProHead->p_offset,SEEK_SET); if((size = fread((char *)ProHead->p_vaddr,1,ProHead->p_filesz,file1)) != ProHead->p_filesz) { printf("\nfunction:%s,line:%d, read p_vaddr err!\n",__FUNCTION__,__LINE__); printf("\nread 返回值%d\n",size); printf("read(file,ProHead->p_vaddr,ProHead->p_filesz) != ProHead->p_filesz %x p_filesz %d\n",\ ProHead->p_vaddr,ProHead->p_filesz); //return; } } /多余的空间写0,做BSS段/ if((ProHead->p_filesz) < (ProHead->p_memsz)) { memset((char*)ProHead->p_vaddr+ProHead->p_filesz, 0, ProHead->p_memsz - ProHead->p_filesz); } else{} } //pEntry = (uint32_t *)Elf_header->e_entry; addrPoint = Elf_header->e_entry; printf("\nEntry point address:%x",addrPoint); free(Elf_header); Elf_header = NULL; return addrPoint; } int main(void) { char *path ="D:/workspace_v7/test_one/Debug/app.out"; FILE *file_elf = NULL; file_elf = fopen(path,"rb"); pCALLFUNC pEntry = (pCALLFUNC)NULL; uint32_t addr = 0; if(file_elf == NULL) { printf("open file err!!%s\n",strerror(errno)); return -1; } check_elf_head(file_elf); addr = LoadElf2Mem(file_elf); pEntry = (pCALLFUNC)addr; fclose(file_elf); pEntry(); } 
           
          
         
        
       
      
     
    
  

 

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/218293.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 上午7:36
下一篇 2026年3月18日 上午7:36


相关推荐

  • html字体下划线取消,取消下划线与显示下划线设置

    html字体下划线取消,取消下划线与显示下划线设置a标签下划线和勾销下划线样式text-decoration配置篇以下介绍DIVCSS组织时刻,默许情况下A超链接锚文本下划线几种情况兼容各阅读器设置装备摆设。1、取消A默认下划线在CSS代码中最前面设置CSS以下:a{text-decoration:none}多么就可设置默认状况下超链接标签A字体无论是默许情况下照常鼠标悬停超链接字体均不闪现下划线。2、兼容各大涉猎器默许A超链接全显示下划线岂论…

    2022年5月26日
    45
  • iOS学习之 plist文件的读写

    iOS学习之 plist文件的读写

    2021年12月14日
    41
  • [新详细]让Keil5续签到2032年的办法,不可商用

    [新详细]让Keil5续签到2032年的办法,不可商用使用方法和以前的版本一样,MDK或者C51以及ARM等均适用,软件来自网络,如有纠纷请留言。仅供学习与参考,商业用途请购买正版。非新手,直接看文章最后流程打开你的Keil主页面,添加证书激活的位置在File→LicenseManagement复制CID将CID复制进软件,通过Target选择你用的类型,复制后,点击Generate计算出新的licenseID码复制…

    2022年6月1日
    168
  • Java优先级队列PriorityQueue「建议收藏」

    Java优先级队列PriorityQueue「建议收藏」目录普通队列对比优先级队列:逆序优先级队列自定义优先级队列的优先级相较于普通先进先出队列来说,优先级队列会根据优先级进行由高到低排序,出队时优先级高的先出队。普通队列对比优先级队列:1.普通队列:importjava.util.LinkedList;importjava.util.Queue;publicclassMainTest{publicstaticvoidmain(String[]args){ Queue<Integer>queue

    2026年2月25日
    3
  • Java刷新bean重新加载bean 上下文 刷新bean

    Java刷新bean重新加载bean 上下文 刷新bean@Autowiredprivate ApplicationContext applicationContext;// 可以为接口或者业务方法被调用public void reloadInstance(){ //获取上下文 DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory)applicationContext.getAutowireCapableBeanFactory();

    2022年8月19日
    19
  • ipvsadm 参数说明

    ipvsadm 参数说明root zh74 ipvsadm L n cIPVSconnect 22TIME WAIT192 168 1 133 168 1 133 80192 1

    2026年3月19日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号