先日、開発しているプログラムのメモリサイズの調査依頼がありました.
RAMの消費量はプログラムのセグメントのサイズが目安となります.
プログラムの各セグメントのサイズを調べるには size コマンドを使います.
コマンドの出力のフォーマットはそっけない Berkeley フォーマットと、細かい出力がされる SysV フォーマットがあります.
両者はどういう関係なんだろ?と疑問を持ったので調べてみました.
Table of Contents
size コマンドの出力フォーマット指定.
Linux の size の man ページに出力フォーマットについてこのように書かれてます.
-A
-B
–format compatibility
これらのうちのどれかのオプションを指定すると、 GNU size の出力形式を選択することができる。 System V 形式にするには -A または –format=sysv、 Berkeley 形式にするには -B または –format=berkeley を指定する。 デフォルトは Berkeley 形式に似た一行出力である。
今までデフォルトは Berkeley フォーマットだとばかり思ってましたが、違うんですね.
例として/usr/bin/ranlib のサイズを表示してみます.
-B (berkeley format)
-A (Sysv format)
Berkeley format は一行に text, data bss セグメントのサイズが表示されるのに対し、SysV format は細かいセグメントのサイズが表示されます.
たとえば, SysV format の .rodata セグメントは .data セグメントの初期値が置かれるのですが、そのセグメントは Berkeley format のどのセグメントに配置されるだろか? という疑問がわきました.
ちょっと調べてもわからないので size のソースコードを読んでみました.
ちなみにでデフォルトのフォーマットは Berkeley format と同じでした.
size のソースコード
size コマンドは GNU Binutils に含まれるコマンドです.
こちらからダウンロード可能です.
size コマンドの出力フォーマットを制御しているのは以下の関数です.
static bfd_size_type bsssize;
static bfd_size_type datasize;
static bfd_size_type textsize;
static void
berkeley_or_gnu_sum (bfd *abfd ATTRIBUTE_UNUSED, sec_ptr sec,
void *ignore ATTRIBUTE_UNUSED)
{
flagword flags;
bfd_size_type size;
flags = bfd_section_flags (sec);
if ((flags & SEC_ALLOC) == 0)
return;
size = bfd_section_size (sec);
if ((flags & SEC_CODE) != 0
|| (selected_output_format == FORMAT_BERKLEY
&& (flags & SEC_READONLY) != 0))
textsize += size;
else if ((flags & SEC_HAS_CONTENTS) != 0)
datasize += size;
else
bsssize += size;
}
上の関数 berkeley_or_gnu_sum() には sysv フォーマットで表示される各セクションの情報 sec が渡され、セクション名、およびセクションのサイズ、フラグが格納されてます.
そのフラグによって Berkley フォーマットで表示される .text, .data. .bss のセクションのサイズとして合算されます.
berkeley_or_gnu_sum() を以下のような修正を加え、sysv フォーマットの各セクションが Berkeley format のどのセグメントに配置されるか表示させました.
static void
berkeley_or_gnu_sum (bfd *abfd ATTRIBUTE_UNUSED, sec_ptr sec,
void *ignore ATTRIBUTE_UNUSED)
{
flagword flags;
bfd_size_type size;
flags = bfd_section_flags (sec);
if ((flags & SEC_ALLOC) == 0)
return;
printf ("%s:", sec->name);
size = bfd_section_size (sec);
if ((flags & SEC_CODE) != 0
|| (selected_output_format == FORMAT_BERKLEY
&& (flags & SEC_READONLY) != 0))
{
printf (" .text\n");
textsize += size;
}
else if ((flags & SEC_HAS_CONTENTS) != 0)
{
printf (".data\n");
datasize += size;
}
else
{
printf (".bss\n");
bsssize += size;
}
}
SysVフォーマットとBerkeleyフォーマットの狭間
先の修正を加えた size コマンドで /usr/bin/ranlib のセグメントのサイズを表示してみました.
これで sysV フォーマットのセグメントが Berkeley フォーマットのどのセグメントに属するのかが分かります.
まとめみると以下のようになります.
.interp | .text |
.note.APi-tag | .text |
.note.gnu.build-id | .text |
.gnu.hash | .text |
.dynsym | .text |
.dynstr | .text |
.gnu.version | .text |
.gnu.version_r | .text |
.rela.dyn | .text |
.rela.plt | .text |
.init | .text |
.plt | .text |
.text | .text |
.finit | .text |
.rodata | .text |
.eh_frame_hdr | .text |
.eh_frame | .text |
.init_array | .data |
.fini_array | .data |
.dynamic | .data |
.got | .data |
.got.plt | .data |
.data | .data |
.bss | .bss |
ちゃんと調べたらここに答えありました…
もうちょっと調べたらここにまさにその答えがありました…
前述の関数 berkeley_or_gnu_sum() の中で見ている各セクションのフラグ, SEC_CODE, SEC_READONLY, SEC_HAS_CONTENTS など、どのセクションにどのようなフラグが設定されているかは objdump コマンドで見ることができます.
以下は /usr/bin/ranlib の objdump -h で見た各セクションの情報です.
次は各セクションの役割について調べていきたいと思います.
この本を参考にしました
それでは