C/C++读取elf文件的头部

你快乐吗

Posted by bbkgl on February 14, 2020

鸳鸯被里成双夜

一树梨花压海棠

从libelfin的例子和网上各种资料我也没找到怎么读取elf文件的头部,非常懵逼呀!

其实有种很简单的方法:

20200217223025.png

不过这是从命令行获取的,肯定是不够的,实际上elf可执行文件/共享库文件都可以当成二进制文件读取。

关于共享库文件和可执行文件

在GCC/G++低版本和CLANG编译的可执行elf文件,其实都是EXEC类型的,其实就是上图中说到的类型,是通过readelf命令读取elf文件的头部来实现的。

可以看一下elf文件的头部包括哪些内容,这个其实对应的是/usr/include/elf.h中的一个结构体:

typedef struct
{
  unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
  Elf64_Half    e_type;                 /* Object file type */
  Elf64_Half    e_machine;              /* Architecture */
  Elf64_Word    e_version;              /* Object file version */
  Elf64_Addr    e_entry;                /* Entry point virtual address */
  Elf64_Off     e_phoff;                /* Program header table file offset */
  Elf64_Off     e_shoff;                /* Section header table file offset */
  Elf64_Word    e_flags;                /* Processor-specific flags */
  Elf64_Half    e_ehsize;               /* ELF header size in bytes */
  Elf64_Half    e_phentsize;            /* Program header table entry size */
  Elf64_Half    e_phnum;                /* Program header table entry count */
  Elf64_Half    e_shentsize;            /* Section header table entry size */
  Elf64_Half    e_shnum;                /* Section header table entry count */
  Elf64_Half    e_shstrndx;             /* Section header string table index */
} Elf64_Ehdr;

这里就不一一翻译了,重点关注的是e_typee_type对应的几个值:

/* Legal values for e_type (object file type).  */

#define ET_NONE		0		/* No file type */
#define ET_REL		1		/* Relocatable file */
#define ET_EXEC		2		/* Executable file */
#define ET_DYN		3		/* Shared object file */
#define ET_CORE		4		/* Core file */
#define	ET_NUM		5		/* Number of defined types */
#define ET_LOOS		0xfe00		/* OS-specific range start */
#define ET_HIOS		0xfeff		/* OS-specific range end */
#define ET_LOPROC	0xff00		/* Processor-specific range start */
#define ET_HIPROC	0xffff		/* Processor-specific range end */

也就是elf文件类型有多种,有常见的重定位文件,可执行文件,共享库文件,coredump文件等。这里主要关注的是可执行文件和共享库文件。

在GCC高版本中,编译得到的目标文件其实是共享库文件,以及我们常见的.so动态库文件也是共享库文件,这里用readelf做个验证。

比如动态库文件:

20200217230453.png

在Type对应的属性是DYN(Shared object file),确实是共享库文件。

再比如我用ubuntu 18.04中的gcc 7.4编写的hello word:

20200218230233.png

可以看到,也是共享库文件。

当然这种方式对于有些实现某些需求不太合适,比如想要在代码中判断elf文件的类型,当然可以用system()函数执行readelf -h <elffile>命令,然后获取命令行输出,但是显然效率和可靠性都是问题。

C/C++读取ELF文件头部信息

其实可以直接把elf文件当成二进制文件来读取,而64位中elf文件的前64字节就是elf的头部。

#include <elf.h>
#include <iostream>
#include <fcntl.h>
#include <unistd.h>

int main() {
    char file[] = "/home/bbkgl/vimcode/server";
    int fd = open(file, O_RDONLY);
    Elf64_Ehdr header;
    ssize_t ret = read(fd, &header, sizeof(Elf64_Ehdr));
    close(fd);
    if (fd > 0 && ret > 0) {
        switch (header.e_type) {
            case ET_DYN:
                printf("DYN\n");
                break;
            case ET_EXEC:
                printf("EXEC\n");
                break;
            default:
                printf("OTHER\n");
                break;
        }
    }
    return 0;
}

代码相当简单,看下输出:

20200218232522.png

如果把更换elf文件为之前的helloword,再看输出:

int main() {
    char file[] = "/home/bbkgl/vimcode/helloworld";
    int fd = open(file, O_RDONLY);
    ...
}

20200218232710.png

简单2333!