size コマンドの SysVフォーマットとBerkeleyフォーマットの狭間

先日、開発しているプログラムのメモリサイズの調査依頼がありました.

RAMの消費量はプログラムのセグメントのサイズが目安となります.

プログラムの各セグメントのサイズを調べるには size コマンドを使います.

コマンドの出力のフォーマットはそっけない Berkeley フォーマットと、細かい出力がされる SysV フォーマットがあります.

両者はどういう関係なんだろ?と疑問を持ったので調べてみました.

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 で見た各セクションの情報です.

次は各セクションの役割について調べていきたいと思います.

この本を参考にしました

それでは