Closed26

CSAPP 第7章 リンク

bayamasabayamasa

コンパイルの流れ

  1. ソースコードをプリプロセッサにより改変されたソースコードにする
  2. コンパイラによりアセンブリに変換する
  3. アセンブラにより、リロケータブルオブジェクト・プログラムに変換する
  4. リンカにより、実行可能オブジェクトファイルに変更する
bayamasabayamasa

リンクとは
コードやデータを集めて、一つのファイルにまとめあげ、メモリ上にロード(コピー)して実行できるようにする過程である

bayamasabayamasa

プリプロセッサ
コンパイルするソースコードに含まれている#の部分を解析して、コードを出力する

  1. # define
    指定されているマクロを値として持ってくる
  2. # ifndef
    ifndefがtrueならば定義をコードに反映
  3. # include
    ローカルに保存されているコードを持ってくる
bayamasabayamasa

プリプロセッサのdefineの例

#include "main.h"

int main()
{
	int r = 2;
	int result = PI * r * r;
	return (0);
}
#ifndef MAIN_H
# define MAIN_H

# define PI 3.14

#endif
# 1 "main.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 368 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "main.c" 2
# 1 "./main.h" 1
# 2 "main.c" 2

int main()
{
 int r = 2;
 int result = 3.14 * r * r;
 return (0);
}

bayamasabayamasa

.iファイルにおけるbuilt-in, command-lineにおける解説

# 1 "main.c"から# 1 "main.c"にかけて出力された行はlinemarkerと呼びincludeするファイルの呼び出しを行っている。

# linenum filename flags
このように表され、linenumが行数。flagsが各操作を表すことになる。
flagsは1 ~ 4まであり、それぞれ

  1. 新しいファイルの最初を示す
  2. ファイルを返すことを示す
  3. システムのヘッダーファイルからlinenumを読み取って返すことを示す
  4. 暗黙的なextern "C" ブロックがあることを示す

詳しいことはよくわからないが、1でopen。3でもともとあるライブラリをlinenum分だけinclude。
2で閉じる。的なことをやっているような気がする。

https://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html

bayamasabayamasa

アセンブラ
.sファイルを再配置可能オブジェクトファイル(リロケータブル)に変換する

bayamasabayamasa

リンカ
リロケータブルオブジェクトファイルを統合して、実行可能オブジェクトファイルに変換する

この辺は後々詳しくやるため今は省略

bayamasabayamasa

ローダ
シェルがOSの関数を呼び出し、実行可能ファイルのコードとデータをメモリにコピーして、プログラムの先頭に制御を移す

bayamasabayamasa

静的リンク
リロケータブルオブジェクトファイルなどを実行可能ファイルに固めること
静的にすることで、実行可能ファイルは変更不可となる
https://wa3.i-3-i.info/word14693.html

bayamasabayamasa

リンカの作業

  1. シンボル解決
    関数、グローバル変数、静的変数を一意に定めるということ
  2. 再配置
    オブジェクトファイル全体として適切な配置関係になるように、メモリ位置を再配置する
bayamasabayamasa

オブジェクトファイル

  1. 再配置可能オブジェクトファイル
    コンパイル時に他の再配置可能なオブジェクトファイルと統合して実行可能オブジェクトファイルを生成できる
  2. 実行可能オブジェクトファイル
    実行可能なバイナリコード、データを含む
  3. 共有オブジェクトファイル
    特殊な再配置可能なオブジェクトファイル
bayamasabayamasa

ELF
実行可能かつリンク可能な形式
.exeファイルのようなもの

こちらの記事が良さげ
https://drumato.hatenablog.com/entry/2019/04/22/105545

bayamasabayamasa

ELFヘッダ
様々なメタデータを含んでいる

.data
静的変数
.symtab
関数に関する情報を集めたシンボルテーブル
.bss
未初期化データ プログラム内では扱えないデータ
実行ファイル内で無駄なリソースを使わないためにわざわざ別にして扱う
bayamasabayamasa

ソースファイルはASCIIコードで書かれている。
これを分析して、実行可能オブジェクトファイルへと変換する

bayamasabayamasa

strong symbol , weak symbol
同名のシンボルが存在する時、そのシンボルをstrongとweakに分類する。
そして以下の規則に従って、リンクを行う。

  1. 同名のstrongシンボルがあったらエラー
  2. 同名のstrongシンボルとweakシンボルがあった場合、strongが優先される
  3. 同名のweakシンボルがあった場合、どちらかを選ぶ
bayamasabayamasa

weakの競合は意図しないバグを引き起こすので、-Werrorで回避するとよい。

bayamasabayamasa

アーカイブファイルとリンクさせるときは、a→bの依存があったら
gcc a bと記載しないとリンカーエラーになる。これはリンカのアルゴリズムによるものである

bayamasabayamasa

再配置
シンボル解決が終わるとコード内のシンボルが一意に定まる。
これにより、実際に実行にロードするアドレスを割り当てる作業が始まる。
これを再配置という。

  1. セクション及びシンボル定義を再配置する
    入力モジュールの全ての.dataセクションを統合して、実行可能オブジェクトにおける唯一の.dataセクションとする。各シンボルにもメモリアドレスを割り当てる
  2. セクション内のシンボル参照を再配置する。
    コード及びデータセクションで、シンボルを参照するアドレスが正しいアドレスを指すようにする。
bayamasabayamasa

再配置エントリ
リンカがオブジェクトファイルを統合して、実行可能ファイルを作るときに参照の修正を行うが、
それをどのように修正するかは再配置エントリによって行われる。
コードの再配置エントリは.rel.text
データの再配置エントリは.rel.data
上記セクションによって保存を行う

bayamasabayamasa

実行可能オブジェクトファイルのロード
linuxにおいてはexecve関数を使ってオブジェクトファイルをロードする

まずプログラムヘッダテーブルに記載されている、コード/データセグメントにコピーする。
次にプログラムのエントリポイントにジャンプする。

bayamasabayamasa

動的リンク
プログラム実行時にライブラリをロードすることで、動的ライブラリはアップデートをすることができる。
これにより、静的ライブラリよりもメンテナンス効率が上がる

bayamasabayamasa

また動的にリンクする方法として、実行ファイルを実行したときにリンクをする動的リンクとプログラムを使用したときにリンクをする動的ロードが存在する

このスクラップは2021/09/28にクローズされました