🧝
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