Load 云々確認メモ

ロードというよりは共有ライブラリというか ld.so がヤッてる仕事を理解できれば、と思いつつ Linkers & Loaders 確認してみましたメモです。
以下、順不動で諸々メモ。
ええと、確認したのは

  • 3 章 オブジェクトファイル
  • 8 章 ロードとオーバレイ
  • 9 章 共有ライブラリ
  • 10 章 動的なリンクとロード

のあたりですが、基本的に ELF 限定で確認してたりしてます。

a.out のオブジェクトについて

何故か a.out について結構な量の補足説明があります。やっぱこれも ELF を知るには a.out を知っておけ的な何かがあるのでしょうか。a.out を load する手法についても

  • NMAGIC
  • ZMAGIC
  • QMAGIC

というものが紹介されています。
v6 UNIX な exec で a.out な実行ファイルをメモリに展開する手法は NMAGIC と呼ばれるみたい。ちなみに Linkers & Loaders にて a.out のマジックナンバーに関する記述発見。以下、Linkers&Loaders 52p の原注 1 より引用。

歴史的に、オリジナルの PDP-11 におけるマジックナンバは 8 進表現で 407 だった。この値は、ヘッダの次の 7 ワードをスキップしてテキストセグメントにジャンプする分岐命令である。

Linkers & Loaders より引用

ELF

ELF のプログラム (セグメント) ヘッダとセクションヘッダの件。

  • リンカが使用するのはセクションヘッダテーブル
  • ローダが使用するのはプログラム (セグメント) ヘッダテーブル

ええと、x86-64 な環境で /bin/ls の readelf を材料にしてるんですがプログラムヘッダテーブルにあるオブジェクトの Type な属性のみ列挙してみたのが以下 (番号は自分で付けました)。

0  PHDR
1  INTERP
2  LOAD  
3  LOAD  
4  DYNAMIC
5  NOTE   
6  GNU_EH_FRAME
7  GNU_STACK   
8  GNU_RELRO   

セクションヘッダテーブルにあるオブジェクトも同様に列挙してみます (一部のみ)。

  [ 0]                   NULL            
  [ 1] .interp           PROGBITS        
  [ 2] .note.ABI-tag     NOTE            
  [ 3] .note.gnu.build-i NOTE            
  [ 4] .gnu.hash         GNU_HASH        
  [ 5] .dynsym           DYNSYM          
  [ 6] .dynstr           STRTAB          
  [ 7] .gnu.version      VERSYM          
  [ 8] .gnu.version_r    VERNEED         
  [ 9] .rela.dyn         RELA            
  [10] .rela.plt         RELA            
  [11] .init             PROGBITS        
  [12] .plt              PROGBITS        
  [13] .text             PROGBITS        
  [14] .fini             PROGBITS        
  [15] .rodata           PROGBITS        
  [16] .eh_frame_hdr     PROGBITS        
  [17] .eh_frame         PROGBITS        
  [18] .ctors            PROGBITS        
  [19] .dtors            PROGBITS        
  [20] .jcr              PROGBITS        
  [21] .dynamic          DYNAMIC         
  [22] .got              PROGBITS        
  [23] .got.plt          PROGBITS        
  [24] .data             PROGBITS        
  [25] .bss              NOBITS          
  [26] .gnu_debuglink    PROGBITS        
  [27] .shstrtab         STRTAB          

readelf がすばらなのはプログラムヘッダの出力のあとに Section to Segment mapping なる出力があることですね (下記については整形してます)。

  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym 
          .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init 
          .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .ctors .dtors .jcr .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .ctors .dtors .jcr .dynamic .got 

0 はヘッダで 7 はスタックですね。どうやら .interp はテキストセグメントに含まれてて shell script の

#!/bin/bash

みたいな扱いになっているのかどうか。
あと、セクションタイプについても記述があるので肝心な部分一部控えを。

  • DYNSYM は動的リンク用のシンボルとのこと
  • RELA は再配置用ベース値を再配置エントリ自体に格納する、とあります
  • DYNAMIC は動的リンク情報とのこと

また、セクションの種類として .got は Global Offset Table、.plt は Procefure Linkage Table で動的にリンクに使われる、とあります。
まとめの項でいくつか情報が列挙されてますが略。

動的リンク

10.3 節では「動的リンクプログラムのロード」という題が付いているのですが、ここは実際のソースと対比させつつ確認した方が良いのかも、と思っているのですが、実際ソースの可読性てきにどうなのかな、と思っていたりしています。
で、ここでは上でも控えた GOT だの PLT だのなソレが連呼されております。

  • GOT は共有ライブラリ毎に存在
  • プログラムが参照する静的なデータを指すポインタが格納される
  • 動的リンカは GOT に格納されている全てのポインタを解決

あ、PLT というのは手続きの、なんだ。(PLT は、データのための GOT と同様の間接参照レベルを関数の呼び出しについても実現する(Linkers & Loaders より参照) とありますね。
ええと、ELF の動的リンクファイルの .dynsym というセクションではインポートおよびエクスポートなシンボルが格納されているとのこと。ええと、.dynstr とか .hash (上だと .gnu.hash?) はそれぞれシンボルの名前を示す文字列と実行時リンカ (って何でしょ) がシンボルを高速に検索するために使うハッシュテーブルが格納、とあります。これらも readelf な出力で確認できています。
また、.dynamic なセクションも動的リンカが使うとの記述あり。

遅延評価

鵜飼さん提供の例のスライド (http://labs.gree.jp/Top/Study/20061024/Report.html から download 可能) の 37 枚目のナニがようやくイメージできました。PLT の解決は実際に手続きが呼びだされるまでは保留になってて、二度目以降は cache されたオフセットと使って解決、ということになるのか。
このあたりを前提に例の読みづらいコードを確認してみます。