C语言解析FLM(ELF)格式文件


本章博客涉及到的软件及代码,关注以下公众号,回复关键字flmparse获取下载链接!

C语言解析FLM(ELF)格式文件_ELF


1、前言

写这篇博客的目的是因为最近在做一个STM32的离线编程器,离线下载需要用到FLM文件的下载算法,所以实现了一下提取FLM文件中下载算法的C程序。

有关ELF格式的详细说明可查看这个文件:​​http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf​

推荐一个elf分析软件:​​http://www.elfparser.com/index.html​​,软件如下图:

C语言解析FLM(ELF)格式文件_#include_02


2、快速扫盲

ELF 全称 “Executable and Linkable Format”,即可执行可链接文件格式,目前常见的Linux、 Android可执行文件、共享库(.so)、目标文件( .o)以及Core 文件(吐核)均为此格式。

常见的ELF文件大致结构如下:

C语言解析FLM(ELF)格式文件_FLM_03

  • 如果是LINUX系统,使用GCC编译出来的程序就是该格式,性质等同于windows系统下的.exe格式运行程序;
  • 在keil中,编译完成之后有一个 .axf 文件,这个文件也是elf格式;
  • FLM格式文件是KEIL里的FLASH下载算法文件,他其实就是.axf文件的拷贝,换了个后缀名称而已。

3、运行效果

我提供了两种可执行程序,一是带UI界面的,一个是控制台使用的,效果如下所示:

控制台应用程序:

C语言解析FLM(ELF)格式文件_ELF_04

可执行应用程序:

C语言解析FLM(ELF)格式文件_#include_05


4、源码实现

由于elf.h这个头文件太大了,就不在博客里贴代码了,只贴一下flm文件的解析:

/*
* flmparse.c
*
* Created on: 2021年4月10日
* Author: hello
*/

#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include "elf.h"

#define FILENAME "STM32F4xx_2048.FLM"

static int ReadDataFromFile(const char* FName, uint32_t offset, void* buf, uint32_t size);
static int FLM_Prase(const void* FName, void* pBuffer, uint32_t* Size, uint32_t* Init, uint32_t* UnInit, uint32_t* EraseChip, uint32_t* EraseSector, uint32_t* ProgramPage);

int main(int argc, char const *argv[])
{
int i = 0;
uint32_t RAM[256] = {0};
uint32_t Addr[5] = {0};
uint32_t Size = 0;

if(argc != 2)
{
printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
printf("Usage:\n");
printf(" flmparse.exe [filename]\n");
printf("\n\n");
goto __exit;
}

/* 这8个数是中断halt程序,让函数执行完后返回到这里来执行从而让CPU自动halt住 */
RAM[0] = 0xE00ABE00;
RAM[1] = 0x062D780D;
RAM[2] = 0x24084068;
RAM[3] = 0xD3000040;
RAM[4] = 0x1E644058;
RAM[5] = 0x1C49D1FA;
RAM[6] = 0x2A001E52;
RAM[7] = 0x4770D1F2;

if(FLM_Prase(argv[1], &RAM[8], &Size, &Addr[0],&Addr[1],&Addr[2],&Addr[3],&Addr[4]) < 0)
{
printf("错误:解析FLM格式文件失败,请检查FLM文件是否存在或格式正确性!\r\n");
goto __exit;
}

fprintf(stdout, "\r\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\r\n");

Size += 32;

fprintf(stdout, "\r\nstatic const uint32_t flash_code[] = \n{");
for(i = 0; i < (Size >> 2); i++)
{
if(i % 8 == 0)
{
fprintf(stdout, "\n ");
}
fprintf(stdout, "0X%08X,", RAM[i]);
}
fprintf(stdout, "\n};\n");

fprintf(stdout, "\r\nconst program_target_t flash_algo =\n{\n");
fprintf(stdout, " 0X%08X, // Init\n", Addr[0] + 0X20000020);
fprintf(stdout, " 0X%08X, // UnInit\n", Addr[1] + 0X20000020);
fprintf(stdout, " 0X%08X, // EraseChip\n", Addr[2] + 0X20000020);
fprintf(stdout, " 0X%08X, // EraseSector\n", Addr[3] + 0X20000020);
fprintf(stdout, " 0X%08X, // ProgramPage\n", Addr[4] + 0X20000020);
fprintf(stdout, "\n");
fprintf(stdout, " // BKPT : start of blob + 1\n");
fprintf(stdout, " // RSB : address to access global/static data\n");
fprintf(stdout, " // RSP : stack pointer\n");
fprintf(stdout, " {\n");
fprintf(stdout, " 0X20000001,\n");
fprintf(stdout, " 0X20000C00,\n");
fprintf(stdout, " 0X20001000,\n");
fprintf(stdout, " },\n");
fprintf(stdout, "\n");
fprintf(stdout, " 0x20000400, // mem buffer location\n");
fprintf(stdout, " 0x20000000, // location to write prog_blob in target RAM\n");
fprintf(stdout, " sizeof(flash_code), // prog_blob size\n");
fprintf(stdout, " flash_code, // address of prog_blob\n");
fprintf(stdout, " 0x00000400, // ram_to_flash_bytes_to_be_written\n");
fprintf(stdout, "};\n");
fprintf(stdout, "\n");
fprintf(stdout, "\n");
fprintf(stdout, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");

__exit:
return 0;
}


static int ReadDataFromFile(const char* FName, uint32_t offset, void* buf, uint32_t size)
{
int ret = 0;
int fd = 0;

if ((fd = open(FName, O_RDONLY | O_BINARY)) < 0)
{
ret = -1;
goto __exit;
}

if (lseek(fd, offset, SEEK_SET) < 0)
{
ret = -2;
goto __exit;
}

if (read(fd, buf, size) != size)
{
ret = -3;
goto __exit;
}

__exit:
close(fd);
return ret;
}

int FLM_Prase(const void* FName, void* pBuffer, uint32_t* Size, uint32_t* Init, uint32_t* UnInit, uint32_t* EraseChip, uint32_t* EraseSector, uint32_t* ProgramPage)
{
#define LOAD_FUN_NUM 5

uint8_t buffer[1024] = {0};
int i = 0, k = 0;
int found = 0;
const Elf32_Phdr* pPhdr = (const Elf32_Phdr *) buffer;
const Elf32_Shdr* pShdr = (const Elf32_Shdr *) buffer;
const Elf32_Sym* pSymbol = (const Elf32_Sym *) buffer;
Elf32_Ehdr ehdr = {0}; // ELF文件信息头
Elf32_Shdr ShdrSym = {0}; // 符号表头
Elf32_Shdr ShdrStr = {0}; // 字符串表头
const char* StrFunNameTable[LOAD_FUN_NUM] = { "Init", "UnInit", "EraseChip", "EraseSector", "ProgramPage"};
int StrFunIndexTable[LOAD_FUN_NUM] = {-1, -1, -1, -1, -1};

//
// 读取ELF文件头信息(ELF Header)
//
ReadDataFromFile(FName, 0, &ehdr, sizeof(Elf32_Ehdr));

// 不是ELF格式文件
if (strstr((const char *)ehdr.e_ident, "ELF") == NULL)
{
return -1;
}

//
// 读取程序头信息(Program Header)
//
ReadDataFromFile(FName, ehdr.e_phoff, buffer, sizeof(Elf32_Phdr) * ehdr.e_phnum);
for (i = 0; i < ehdr.e_phnum; i++)
{
if (pPhdr[i].p_type == PT_LOAD && (pPhdr[i].p_flags & (PF_X | PF_W | PF_R)) == (PF_X | PF_W | PF_R))
{
if (pPhdr[i].p_filesz > sizeof(buffer)) // RAM代码过大
{
return -2;
}
if(ReadDataFromFile(FName, pPhdr[i].p_offset, pBuffer, pPhdr[i].p_filesz) < 0) // 提取需要下载到RAM的程序代码
{
return -3;
}
printf("====:%d\r\n", pPhdr[i].p_filesz);
*Size = pPhdr[i].p_filesz;
}
}

//
// 读取节区头部(Sections Header)
//
ReadDataFromFile(FName, ehdr.e_shoff, buffer, sizeof(Elf32_Shdr) * ehdr.e_shnum);

// 查找符号表头并拷贝出来备用
for (i = 0; i < ehdr.e_shnum; i++)
{
if (pShdr[i].sh_type == SHT_SYMTAB)
{
memcpy(&ShdrSym, &pShdr[i], sizeof(Elf32_Shdr));

// 查找字符串表头并拷贝出来备用
if (pShdr[ShdrSym.sh_link].sh_type == SHT_STRTAB)
{
memcpy(&ShdrStr, &pShdr[ShdrSym.sh_link], sizeof(Elf32_Shdr));
found = 1;
break;
}
}
}

if(!found)
{
return -4;
}

//
// 根据字符串表头读取所有字符串表
//
ReadDataFromFile(FName, ShdrStr.sh_offset, buffer, ShdrStr.sh_size);
for (i = 0; i < ShdrStr.sh_size; i++) if (buffer[i] == '\0') buffer[i] = '\n';
buffer[ShdrStr.sh_size] = 0;
for (i = 0; i < LOAD_FUN_NUM; i++)
{
char* p = NULL;
if (StrFunNameTable[i] != NULL && (p = strstr((const char *) buffer, StrFunNameTable[i])) != NULL)
{
StrFunIndexTable[i] = (uint32_t) p - (uint32_t) buffer;
}
}

//
// 读取符号表
//
ReadDataFromFile(FName, ShdrSym.sh_offset, buffer, ShdrSym.sh_size);

// 遍历查询我们用到的函数符号
for (i = 0; i < ShdrSym.sh_size / sizeof(Elf32_Sym); i++, pSymbol++)
{
for (k = 0; k < LOAD_FUN_NUM; k++)
{
if (StrFunIndexTable[k] >= 0 && StrFunIndexTable[k] == pSymbol->st_name) // symbol.st_name的值就是偏移地址
{
switch (k)
{
case 0:
*Init = pSymbol->st_value;
break;
case 1:
*UnInit = pSymbol->st_value;
break;
case 2:
*EraseChip = pSymbol->st_value;
break;
case 3:
*EraseSector = pSymbol->st_value;
break;
case 4:
*ProgramPage = pSymbol->st_value;
break;
default:
break;
}
}
}
}

return 0;
}

ends…