🧝

ELFファイルのファイルサイズをヘッダーから正確に計算

に公開

ELFヘッダーからファイルサイズを求める実験

ELFファイルのサイズって、実はヘッダー情報だけからも求められるんです。
具体的には以下の式になります:

e_shoff + (e_shentsize * e_shnum)

つまり、セクションヘッダーがファイルのどこに始まって、いくつ並んでいて、1つあたりのサイズがどれくらいか――を足し合わせれば、ファイルの終端、すなわちサイズに一致するはずです。

「本当にそうなるの?」と思ったので、実際にコードを書いて検証してみました。


実行ログ

$ ls
Makefile  main.c
$ make
gcc main.c -g -o elf-experiment
$ ./elf-experiment 
> fd = open(/proc/self/exe)
> stat = fstat(fd)
> map = mmap(fd)
> eh = (Elf64_Ehdr*)map
> print(stat.st_size)
19520
> print(eh.e_shoff + (eh.e_shentsize * eh.e_shnum))
19520

ちゃんと一致しました 🎉
stat(2) で取った実際のファイルサイズと、ELFヘッダーから計算した値が同じになっています。


ソースコード

最小限の C プログラムで /proc/self/exe を mmap してヘッダーを読んでいます。

main.c
#include <stdio.h>
#include <elf.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <assert.h>

int main() {
    int ret;

    const char* self = "/proc/self/exe";

    printf("> fd = open(%s)\n", self);
    int fd = open(self, O_RDONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    struct stat stat;
    printf("> stat = fstat(fd)\n");
    ret = fstat(fd, &stat);
    if(ret < 0) {
        perror("fstat");
        close(fd);
        return 1;
    }

    printf("> map = mmap(fd)\n");
    void* map = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (map == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return 1;
    }

    close(fd);

    Elf64_Ehdr* ehdr = (Elf64_Ehdr*)map;
    printf("> eh = (Elf64_Ehdr*)map\n");

    printf("> print(stat.st_size)\n");
    printf("%lu\n", stat.st_size);
    printf("> print(eh.e_shoff + (eh.e_shentsize * eh.e_shnum))\n");
    printf("%lu\n", ehdr->e_shoff + (ehdr->e_shentsize * ehdr->e_shnum));
}

ビルド用の Makefile はこれだけ。

elf-experiment: main.c
	gcc $^ -g -o $@

まとめ

ELFファイルのサイズは、わざわざ stat(2) しなくても、
ELFヘッダーの

  • e_shoff (セクションヘッダーテーブルの先頭位置)
  • e_shentsize (セクションヘッダー1つあたりのサイズ)
  • e_shnum (セクションヘッダーの個数)

から計算できる、という確認ができました。

シンプルですが「ELFの構造をちゃんと理解しているとこういうこともできるんだな」と実感できる実験でした。


自分でコードだけ書いた後、チャッピーに清書してもらいました。

Discussion