SOC Boot学习(一)——ELF文件
01 ELF Header组成
纯手搓C++实现读取ELF文件头信息、段表信息和节表信息_c代码实现解析elf文件-CSDN博客
- elf header
- programm header table
- section header table
- section
- …
02 read elf
#include <iostream>
using namespace std;
using ELF64_Addr = uint64_t;
using ELF64_Off = uint64_t;
using ELF64_Half = uint16_t;
using ELF64_Word = uint32_t;
using ELF64_Sword = int32_t;
using ELF64_Xword = uint64_t;
using ELF64_Sxword = int64_t;
/*
e_ident[16]
包含了Maigc Number用于表明这是一个ELF文件,共16字节。
0~3:前4字节为Magic Number,固定为、'\\0x7f' 'E' 'L' 'F'。
4(EI_CLASS):00为非法文件,01位32位,02为64位。
5(EI_DATA):00为非法数据编码,01为小端序,02为大端序。
6(EI_VERSION):文件版本,固定为01。
7(EI_OSABI):操作系统ABI标识(实际未使用)。
8(EI_ABIVERSION):ABI版本(实际 未使用)。
9~15:对齐填充,无实际意义。
*/
typedef struct {
unsigned char e_ident[16];
ELF64_Half e_type; // 可重定位、可执行、共享目标文件
ELF64_Half e_machine; // CPU架构
ELF64_Word e_version;
ELF64_Addr e_entry;
ELF64_Off e_phoff;
ELF64_Off e_shoff;
ELF64_Word e_flags;
ELF64_Half e_ehsize;
ELF64_Half e_phentsize;
ELF64_Half e_phnum;
ELF64_Half e_shentsize;
ELF64_Half e_shnum;
ELF64_Half e_shstrndx;
} ELF64_Ehdr;
typedef struct {
ELF64_Half p_type;
ELF64_Half p_flags;
ELF64_Off p_offset;
ELF64_Half p_offset;
ELF64_Addr p_vaddr;
ELF64_Addr p_paddr;
ELF64_Xword p_filesz;
ELF64_Xword p_memsz;
ELF64_Xword p_align;
} ELF64_Phdr;
typedef struct {
ELF64_Word sh_name;
ELF64_Word sh_type;
ELF64_Xword sh_flags;
ELF64_Addr sh_addr;
ELF64_Off sh_offset;
ELF64_Xword sh_size;
ELF64_Word sh_link;
ELF64_Word sh_info;
ELF64_Xword sh_addralign;
ELF64_Xword sh_entsize;
} ELF64_Shdr;
int main() {
char filename[10010];
cin >> filename;
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
cout << "fail to open file" << endl;
exit(0);
}
ELF64_Ehdr elf_head;
int shnum, status;
status = fread(&elf_head, sizeof(ELF64_Ehdr), 1, fp);
if (status == 0) {
cout << "fail to read elf header" << endl;
exit(0);
}
if (elf_head.e_ident[0] != 0x7f ||
elf_head.e_ident[1] != 'E' ||
elf_head.e_ident[2] != 'L' ||
elf_head.e_ident[3] != 'F') {
cout << "not a elf file" << endl;
exit(0);
}
printf("程序入口(start标签位置) %#x\\n\\n",elf_head.e_entry);
printf("Program header table文件中偏移 %#x\\n",elf_head.e_phoff);
printf("Program header table大小 %#x\\n",elf_head.e_phentsize*elf_head.e_phnum);
printf("Program header数量 %d\\n\\n",elf_head.e_phnum);
printf("Section header table文件中偏移 %#x\\n",elf_head.e_shoff);
printf("Section header table大小 %#x\\n",elf_head.e_shentsize*elf_head.e_shnum);
printf("Section header数量 %d\\n\\n",elf_head.e_shnum);
// 解析Segment Header
// 制作Segment Header数组用来存储每一个Segment Header
ELF64_Phdr *phdr = (ELF64_Phdr*)malloc(sizeof(ELF64_Phdr) * elf_head.e_phnum);
if (NULL == phdr)
{
printf("phdr malloc failed\\n");
exit(0);
}
// 设置文件偏移量,定位到e_phoff
status = fseek(fp, elf_head.e_phoff, SEEK_SET);
if (0 != status)
{
printf("\\nfaile to fseek\\n");
exit(0);
}
// 读取所有Segment Header 到 phdr, 大小为sizeof(Elf64_Phdr) * 数量
status = fread(phdr, sizeof(ELF64_Phdr) * elf_head.e_phnum, 1, fp);
if (0 == status)
{
printf("\\nfail to read segment\\n");
exit(0);
}
// 重置指针位置到文件流开头
rewind(fp);
// 遍历每一个Segment Header
for (int i = 0; i < elf_head.e_phnum; i++)
{
printf("段首的偏移: %#x\\n", phdr[i].p_offset);
printf("段在文件中的大小: %#x\\n", phdr[i].p_filesz);
printf("段的运行时虚拟内存地址: %#x\\n", phdr[i].p_vaddr);
printf("段在内存中的大小: %#x\\n", phdr[i].p_memsz);
printf("\\n");
}
// 解析Section Header
// 制作Section Header数组用来存储每一个Section Header
ELF64_Shdr *shdr = (ELF64_Shdr*)malloc(sizeof(ELF64_Shdr) * elf_head.e_shnum);
if (NULL == shdr)
{
printf("shdr malloc failed\\n");
exit(0);
}
// 设置文件偏移量,定位到e_shoff
status = fseek(fp, elf_head.e_shoff, SEEK_SET);
if (0 != status)
{
printf("\\nfaile to fseek\\n");
exit(0);
}
// 读取所有Segment Header 到 phdr, 大小为sizeof(Elf64_Phdr) * 数量
status = fread(shdr, sizeof(ELF64_Shdr) * elf_head.e_shnum, 1, fp);
if (0 == status)
{
printf("\\nfail to read section\\n");
exit(0);
}
// 重置指针位置到文件流开头
rewind(fp);
// 读取每个Section的名字
// 将fp指针移到字符串表(.shstrtab)内容的偏移位置处
fseek(fp, shdr[elf_head.e_shstrndx].sh_offset, SEEK_SET);
// e_shstrndx项是字符串表(.shstrtab)的下标
// 把这个段的内容全部存储到shstrtab数组里面
uint64_t size = shdr[elf_head.e_shstrndx].sh_size;
char *shstrtab = (char*) malloc(size);
char *temp = shstrtab;
// 读取内容
status = fread(shstrtab, shdr[elf_head.e_shstrndx].sh_size, 1, fp);
if (0 == status)
{
printf("\\nfaile to read\\n");
}
// 遍历每一个节
for (int i = 0; i < elf_head.e_shnum; i++)
{
// temp指针用于定位当前节的名字在.shstrtab节内容中的首地址
temp = shstrtab;
// shdr[i].sh_name表示这个节名字在(.shstrtab)节内容中的偏移
temp = temp + shdr[i].sh_name;
printf("节的名称: %s\\n", temp);
printf("节首的偏移: %#x\\n", shdr[i].sh_offset);
printf("节的大小: %#x\\n", shdr[i].sh_size);
printf("节的运行时虚拟内存地址: %#x\\n", shdr[i].sh_addr);
printf("\\n");
}
system("pause");
return 0;
}