【问题标题】:How can I find the size of a ELF file/image with Header information?如何找到带有标题信息的 ELF 文件/图像的大小?
【发布时间】:2010-06-08 07:05:32
【问题描述】:

我需要找到一个精灵图像的大小来进行一些计算。我已经尝试使用 linux 上的 readelf 实用程序,它提供了有关标题和部分的信息。我需要有 elf 的确切文件大小(总体上)。

如何从标头信息中找到ELF的大小,或者有没有其他方法可以在不读取完整图像的情况下找到elf的大小。

【问题讨论】:

  • 由于接受的答案是错误的,这里我的 2 个字节:
  • e_ehsize 字段仅包含可执行文件头的大小,而不是实际文件的大小,但您当然可以使用节头表的偏移量,因为它位于二进制文件的最后,并且偏移量在文件内(不是虚拟的)。所以计算 e_shoff + [number_of_sectionheaders * sizeof(Elf32_Shdr)]
  • 为了澄清一下,节标题的数量应该在字段 e_shnum 中找到,在字段 e_shentsize 中找到 IIRC 的大小

标签: linux filesize elf


【解决方案1】:

具体问题的答案对于 ELF 文件来说有点棘手。

以下将使用标题计算 ELF 文件中“描述性”信息的大小:e_ehsize + (e_phnum * e_phentsize) + (e_shnum * e_shentsize)

以上内容基于 ELF 文档。

要添加到上述总和的下一部分是文件中节条目的大小。直观地说,我们希望使用 sh_size 对文件中的每个部分进行计算——其中 e_shnum。但是,由于对齐问题,这不会产生正确的答案。如果您使用 sh_offset 值的有序列表,您可以计算节条目占用的确切字节数(我发现一些奇怪的对齐方式,使用 sh_addralign 并没有您想要的那么有用);对于最后一个节条目,请使用文件头的 e_shoff,因为节头表是最后一个。这对我检查的那对夫妇有效。

libelf 中的 update.c 包含更新 elf 文件时使用的详细信息。

【讨论】:

  • 建议使用 elf_getphdrnum()elf_getshdrnum() 函数分别检索 ELF 对象中的 PHDR 和 SHDR 条目数。这些函数正确处理使用扩展编号的 ELF 对象,其中直接使用 ELF 标头的 e_phnume_shnum 字段可能不正确。
  • 但是由于节头表直接映射,并且 sh_offsets 的有序列表被扩展并映射到内存中,所以节头表最终不会位于内存映像的末尾。例如,磁盘上的 sh_offset 将小于最后一部分的内存映像偏移量。小 - 大 = 否定
  • 任何受支持类型的 ELF 文件的数据块是否可以不在该文件任何部分的边界内?
【解决方案2】:

例子:

ls -l gives 126584

Calculation using the values also reported by readelf -h:

Start of section headers    e_shoff     124728
Size of section headers     e_shentsize 64
Number of section headers   e_shnum     29

e_shoff + ( e_shentsize * e_shnum ) = 126584

这假定节头表 (SHT) 是 ELF 的最后一部分。这通常是这种情况,但也可能是最后一部分是 ELF 的最后一部分。这应该被检查,但在这个例子中没有。

这是一个 C 中的工作实现,使用 gcc elfsize.c -o elfsize 编译:

#include <elf.h>
#include <byteswap.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

typedef Elf32_Nhdr Elf_Nhdr;

static char *fname;
static Elf64_Ehdr ehdr;
static Elf64_Phdr *phdr;

#if __BYTE_ORDER == __LITTLE_ENDIAN
#define ELFDATANATIVE ELFDATA2LSB
#elif __BYTE_ORDER == __BIG_ENDIAN
#define ELFDATANATIVE ELFDATA2MSB
#else
#error "Unknown machine endian"
#endif

static uint16_t file16_to_cpu(uint16_t val)
{
    if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE)
        val = bswap_16(val);
    return val;
}

static uint32_t file32_to_cpu(uint32_t val)
{
    if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE)
        val = bswap_32(val);
    return val;
}

static uint64_t file64_to_cpu(uint64_t val)
{
    if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE)
        val = bswap_64(val);
    return val;
}

static long unsigned int read_elf32(int fd)
{
    Elf32_Ehdr ehdr32;
    ssize_t ret, i;

    ret = pread(fd, &ehdr32, sizeof(ehdr32), 0);
    if (ret < 0 || (size_t)ret != sizeof(ehdr)) {
        fprintf(stderr, "Read of ELF header from %s failed: %s\n",
            fname, strerror(errno));
        exit(10);
    }

    ehdr.e_shoff        = file32_to_cpu(ehdr32.e_shoff);
    ehdr.e_shentsize    = file16_to_cpu(ehdr32.e_shentsize);
    ehdr.e_shnum        = file16_to_cpu(ehdr32.e_shnum);

    return(ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shnum));
}

static long unsigned int read_elf64(int fd)
{
    Elf64_Ehdr ehdr64;
    ssize_t ret, i;

    ret = pread(fd, &ehdr64, sizeof(ehdr64), 0);
    if (ret < 0 || (size_t)ret != sizeof(ehdr)) {
        fprintf(stderr, "Read of ELF header from %s failed: %s\n",
            fname, strerror(errno));
        exit(10);
    }

    ehdr.e_shoff        = file64_to_cpu(ehdr64.e_shoff);
    ehdr.e_shentsize    = file16_to_cpu(ehdr64.e_shentsize);
    ehdr.e_shnum        = file16_to_cpu(ehdr64.e_shnum);

    return(ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shnum));
}

long unsigned int get_elf_size(char *fname)
/* TODO, FIXME: This assumes that the section header table (SHT) is
the last part of the ELF. This is usually the case but
it could also be that the last section is the last part
of the ELF. This should be checked for.
*/
{
    ssize_t ret;
    int fd;
    long unsigned int size = 0;

    fd = open(fname, O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "Cannot open %s: %s\n",
            fname, strerror(errno));
        return(1);
    }
    ret = pread(fd, ehdr.e_ident, EI_NIDENT, 0);
    if (ret != EI_NIDENT) {
        fprintf(stderr, "Read of e_ident from %s failed: %s\n",
            fname, strerror(errno));
        return(1);
    }
    if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) &&
        (ehdr.e_ident[EI_DATA] != ELFDATA2MSB))
    {
        fprintf(stderr, "Unkown ELF data order %u\n",
            ehdr.e_ident[EI_DATA]);
        return(1);
    }
    if(ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
        size = read_elf32(fd);
    } else if(ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
        size = read_elf64(fd);
    } else {
        fprintf(stderr, "Unknown ELF class %u\n", ehdr.e_ident[EI_CLASS]);
        return(1);
    }

    close(fd);
    return size;
}

int main(int argc, char **argv)
{
    ssize_t ret;
    int fd;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s <ELF>\n", argv[0]);
        return 1;
    }
    fname = argv[1];

    long unsigned int size = get_elf_size(fname);
    fprintf(stderr, "Estimated ELF size on disk: %lu bytes \n", size);
    return 0;
}

【讨论】:

    【解决方案3】:

    也许gelf 可能有用。

    GElf 是一个通用的、独立于 ELF 类的 API,用于操作 ELF 对象文件。 GElf 为处理 32 位和 64 位 ELF 格式的目标文件提供了一个单一的通用接口。

    特别是这些功能:

    elf32_fsize, elf64_fsize - 返回目标文件类型的大小

    【讨论】:

    • 我不能这样做,因为我需要在根文件系统中安装库的东西,这会占用更多空间..
    • @fasil:您可以使用“libelf by Example”教程开始使用 GELF(3) API。
    【解决方案4】:

    您是否尝试过使用 gnu "readelf" 实用程序?

    http://sourceware.org/binutils/docs/binutils/readelf.html

    【讨论】:

      【解决方案5】:

      您所要做的就是将最后一节的文件偏移量及其大小相加。

      fseek(fileHandle, elfHeader.e_shoff + (elfHeader.e_shnum-1) * elfHeader.e_shentsize, SEEK_SET);
      Elf64_Shdr sectionHeader; // or Elf32_Shdr
      fread(&sectionHeader, 1, elfHeader.e_shentsize, fileHandle);
      
      int fileSize = sectionHeader.sh_offset + sectionHeader.sh_size;
      

      elfHeader 使用的值:

      e_shoff = Section header table file offset
      e_shnum = Section header table entry count
      e_shentsize = Section header table entry size       
      

      sectionHeader 使用的值:

      sh_offset = Section file offset
      sh_size = Section size in bytes
      

      【讨论】:

      • ELF 文件可能在其任何部分的边界内都没有数据块吗?
      • 即使节头表不是 ELF 文件中的最后一个节,是否可以保证所有节在节头表中按其在文件中的偏移量排序?
      【解决方案6】:

      您可以使用stat 函数系列(stat()lstat()fstat())来获取任何文件的大小(使用st_size 成员的stat 成员)。 您需要更具体的内容吗?


      如果你真的想使用 ELF 结构,请使用包含该结构的 elf.h 头文件:

      typedef struct {
               unsigned char  e_ident[EI_NIDENT];
               uint16_t       e_type;
               uint16_t       e_machine;
               uint32_t       e_version;
               ElfN_Addr      e_entry;
               ElfN_Off       e_phoff;
               ElfN_Off       e_shoff;
               uint32_t       e_flags;
               uint16_t       e_ehsize;
               uint16_t       e_phentsize;
               uint16_t       e_phnum;
               uint16_t       e_shentsize;
               uint16_t       e_shnum;
               uint16_t       e_shstrndx;
       } Elf32_Ehdr;
      

      它是 ELF32 文件的标头(对于 64 位文件,将 32 替换为 64)。 e_ehsize 是文件的大小,以字节为单位。


      我将逐字复制作为编辑建议发布的评论:

      这个答案不正确。 e_ehsize 只是elf头的大小,不是elf文件的大小。

      【讨论】:

      • 我们如何使用这种结构来查找任何 elf 文件的大小。我的意思是 API 之类的东西。
      • 您的语言是什么?例如,在 C 中,使用 fread() 读取标题应该很容易,然后打印 e_ehsize 成员。
      猜你喜欢
      • 2020-08-21
      • 1970-01-01
      • 1970-01-01
      • 2015-03-05
      • 2018-09-30
      • 1970-01-01
      • 1970-01-01
      • 2021-03-22
      • 1970-01-01
      相关资源
      最近更新 更多