ファイルローカル変数の再配置情報

static int s1 = 0x7fffffff;
static int s2 = 0x12345678;
int main(int argc, char **argv)
{
    s1 = 0x7f7f7f7f;
    s2 = 0x87654321;

    return 0;
}

.symtabは、シンボルに関する属性情報(そのシンボルがメモリ上に実体を持つセクションのセクション番号と、当該セクション先頭からのオフセット、オブジェクトサイズ、シンボルタイプ(変数か関数か))を一元的に管理するセクションである。シンボルが初期値を持つファイルローカルな変数の場合、そのバイト実体は、.dataセクションに作成される。

$ readelf -s main.o
Symbol table '.symtab' contains 17 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     5: 00000000     4 OBJECT  LOCAL  DEFAULT    3 s1
     6: 00000004     4 OBJECT  LOCAL  DEFAULT    3 s2

上記シンボルテーブル(抜粋)から、変数s1,s2について、以下のことが分かる。

  1. メモリ上の実体が作成されるセクションのセクション番号は、.symtabのNdxで示される。s1,s2とも、3番セクションに実体を持つ。
  2. メモリ上の実体が作成されるセクション先頭からのオフセットは、.symtabのValueで示される。s1は3番セクションのオフセット0番地に、s2は3番セクョンのオフセット4番地に、それぞれのバイト実体が作成される。
  3. メモリ上の実体のサイズは、.symtabのSizeで示される。s1,s2とも、メモリ上の実体のサイズは4バイト。

3番セクションとは、.dataセクションである。int型変数x2の領域を確保するため、セクションのSizeは8バイトとなっている。

$ readelf -S main.o
There are 21 section headers, starting at offset 0x348:
Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 2] .rel.text         REL             00000000 0007b4 000010 08     19   1  4
  [ 3] .data             PROGBITS        00000000 000054 000008 00  WA  0   0  4

.dataセクションをバイナリダンプすると、確かにソースコード上で指定した値で初期化されていることが分かる。

$ readelf -x 3 main.o
Hex dump of section '.data':
  0x00000000 ffffff7f 78563412                   ....xV4.

実行コードにおけるシンボルの再配置情報は、.rel.textで管理される。
.rel.textで再配置情報を確認すると、

  1. .textセクションのオフセット0x5バイト目に、.dataセクションにバイト実体を持つシンボルのアドレスが展開される。
  2. .textセクションのオフセット0xfバイト目に、.dataセクションにバイト実体を持つシンボルのアドレスが展開される。
$ readelf -r main.o
Relocation section '.rel.text' at offset 0x7b4 contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000005  00000301 R_386_32          00000000   .data
0000000f  00000301 R_386_32          00000000   .data
$ readelf -x 2 main.o
Hex dump of section '.rel.text':
  0x00000000 05000000 01030000 0f000000 01030000 ................

再配置されるシンボルはs1,s2であるが、上記の再配置テーブルではシンボルではなく、セクションと紐づけられている(ファイルローカルのシンボルの場合、シンボル(.symtab内でのゼロベースのエントリ番号)ではなく、セクション(ゼロベースのセクション番号)が設定される)。このままでは、.dataセクション上のどのバイト実体と紐づけられるべきなのかが分からない。

再配置エントリが、どのシンボル(.dataセクション内のバイト実体)に関連するかの情報は、再配置前の実行コードに直書きされている。その実行コード上の場所は、.rel.textセクションのOffset(.textセクション先頭からのオフセット)によって示されている。具体的には、以下コード内の中括弧の箇所[00 00 00 00][04 00 00 00]に該当する。それぞれ、.dataセクションの先頭からのオフセット0x00と0x04の領域に、そのバイト実体を持つことを示す。

$ objdump -d main.o
main.o:     file format elf32-i386
Disassembly of section .text:
00000000 <main>:
   0:	55                   push   %ebp
   1:	89 e5                	mov    %esp,%ebp
   3:	c7 05 [00 00 00 00] 7f movl   $0x7f7f7f7f,0x0
   a:	7f 7f 7f
   d:	c7 05 [04 00 00 00] 21 movl   $0x87654321,0x4
  14:	43 65 87
  17:	b8 00 00 00 00       mov    $0x0,%eax
  1c:	5d                   pop    %ebp
  1d:	c3                   ret

.dataセクションのオフセット(ELFファイル先頭からのオフセット)は0x54であるため、ELF先頭からのオフセットで表現すると、実行コード内の[00 00 00 00]は値0x54(0x54+0x00)に、[04 00 00 00]は値0x58(0x54+0x04)に、それぞれ読み替えられる。

以上をまとめると、

  1. 実行コードにおけるファイルローカル変数の再配置情報は、.rel.textで一元管理される。
  2. ファイルローカル変数の場合、再配置情報には、該当シンボル番号ではなく該当セクション番号が記載される。
  3. 該当セクション内のバイト実体を特定するためには、実行コード内に埋め込まれたオフセットを参照する。これは、該当セクションをベースとするオフセットである。