任务: 读取符号表中的func,并将其函数名、函数首地址、函数大小(函数地址范围)保存起来。
两个c语言相关函数:
fread:
int fread(void *buffer,int size,int count,FILE *fp);
fread()──从fp所指向文件的当前位置开始,一次读入size个字节,重复count次,并将读入的数据存放到从buffer开始的内存中; buffer是存放读入数据的起始地址(即存放何处)。
fseek:
int fseek(FILE *stream, long offset, int fromwhere);
第一个参数file指针 第二个参数移动的偏移量 第三个参数移动到哪里 分别用3个宏
SEEK_SET 即0 文件开头
SEEK_CUR 即1 文件当前位置
SEEK_END 即2 文件结尾
fseek(fp,100L,SEEK_SET);把fp指针移动到离文件开头100字节处;
fseek(fp,100L,SEEK_CUR);把fp指针移动到离文件当前位置100字节处;
fseek(fp,100L,SEEK_END);把fp指针退回到离文件结尾100字节处。
#include <elf.h>
typedef struct {
char name[64];
paddr_t addr; //the function head address
Elf32_Xword size;
} Symbol;
Symbol *symbol = NULL; //dynamic allocate memory or direct allocate memory (Symbol symbol[NUM])
void parse_elf(const char *elf_file)
{
if(elf_file == NULL) return;
FILE *fp;
fp = fopen(elf_file, "rb");
if(fp == NULL)
{
printf("failed to open the elf file!\n");
exit(0);
}
Elf32_Ehdr edhr;
//读取elf头
if(fread(&edhr, sizeof(Elf32_Ehdr), 1, fp) <= 0)
{
printf("fail to read the elf_head!\n");
exit(0);
}
if(edhr.e_ident[0] != 0x7f || edhr.e_ident[1] != 'E' ||
edhr.e_ident[2] != 'L' ||edhr.e_ident[3] != 'F')
{
printf("The opened file isn't a elf file!\n");
exit(0);
}
fseek(fp, edhr.e_shoff, SEEK_SET);
Elf32_Shdr shdr;
char *string_table = NULL;
//寻找字符串表
for(int i = 0; i < edhr.e_shnum; i++)
{
if(fread(&shdr, sizeof(Elf32_Shdr), 1, fp) <= 0)
{
printf("fail to read the shdr\n");
exit(0);
}
if(shdr.sh_type == SHT_STRTAB)
{
//获取字符串表
string_table = malloc(shdr.sh_size);
fseek(fp, shdr.sh_offset, SEEK_SET);
if(fread(string_table, shdr.sh_size, 1, fp) <= 0)
{
printf("fail to read the strtab\n");
exit(0);
}
}
}
//寻找符号表
fseek(fp, edhr.e_shoff, SEEK_SET);
for(int i = 0; i < edhr.e_shnum; i++)
{
if(fread(&shdr, sizeof(Elf32_Shdr), 1, fp) <= 0)
{
printf("fail to read the shdr\n");
exit(0);
}
if(shdr.sh_type == SHT_SYMTAB)
{
fseek(fp, shdr.sh_offset, SEEK_SET);
Elf32_Sym sym;
size_t sym_count = shdr.sh_size / shdr.sh_entsize;
symbol = malloc(sizeof(Symbol) * sym_count);
for(size_t j = 0; j < sym_count; j++)
{
if(fread(&sym, sizeof(Elf32_Sym), 1, fp) <= 0)
{
printf("fail to read the symtab\n");
exit(0);
}
if(ELF32_ST_TYPE(sym.st_info) == STT_FUNC)
{
const char *name = string_table + sym.st_name;
strncpy(symbol[func_num].name, name, sizeof(symbol[func_num].name) - 1);
symbol[func_num].addr = sym.st_value;
symbol[func_num].size = sym.st_size;
func_num++;
}
}
}
}
fclose(fp);
free(string_table);
}
需要注意符号表中的
st_name
(符号名称),它其实不是一个字符串,而是一个数值,代表的是目标文件中字符串表(.strtab
节)中的一个索引值, 那里才真正存储着该符号的名称对应的字符串,也正是这个原因,我们需要读取字符串表的首地址,并将其存入string_table
变量中。
通过上述代码,也可以看出,在了解了
elf
文件的一些结构体后,解析elf
文件就主要靠fread
函数与fseek
函数,fseek
函数用于不断调整fp
文件指针的位置,然后fread
再读取对应的节头表、字符串表、符号表等,并将其保存在所定义的变量中,最后再通过此变量,获取到我们需要的数据。😀😀😀