ハロー“Hello, World” OSと標準ライブラリのシゴトとしくみ

概 要

C言語の入門書では、"Hello, World"と出力するプログラムを最初に作るのが定番です。"Hello, World"は、たった7行の単純なプログラムですが、printf()の先では何が行われているのか、main()の前にはいったい何があるのか、考えてみると謎だらけです。本書は、基礎中の基礎である"Hello, World"プログラムを元に、OSと標準ライブラリの仕組みをあらゆる角度からとことん解析します。資料に頼らず、自分の手で調べる方法がわかります。

著者 坂井弘亮
価格 本体3200円(税別)
ISBN 978-4-7980-4478-1
発売日 2015/9/11
判型 B5変
ページ数 456
CD/DVD
ダウンロード
表紙イメージ
購入 アマゾンで購入する
楽天で購入する

※リンク先によっては、販売ページが用意されていないことがあります。あらかじめご了承ください。

新しいウィンドウで開く 書籍購入のご案内

サポート

サポート情報は以下からご参照下さい。

サポート情報へのリンク

目次

第1章 ハロー・ワールドに触れてみる

【1.1】ハロー・ワールドのサンプル・プログラム

1.1.1 サンプル・プログラムのダウンロードについて

1.1.2 各種ソースコードの入手について

1.1.3 VMによるCentOS環境の利用について

【1.2】実行ファイルの生成の手順

1.2.1 実行ファイルを生成する

1.2.2 コンパイル・オプション

1.2.3 プログラムの動作を確認する

1.2.4 逆アセンブル結果を見る

1.2.5 実行ファイルの解析結果を見る

【1.3】VM環境の利用

1.3.1 VMイメージのダウンロード

1.3.2 VMイメージをインポートする

1.3.3 VMを起動する

1.3.4 ログインする

1.3.5 サンプル・プログラムを展開しておく

1.3.6 各種ソースコードを展開しておく

1.3.7 VMの終了

【1.4】VM環境を使いやすくする

1.4.1 GUIを利用する

1.4.2 SSHでのログインを可能にする

1.4.3 文字化けを防ぐ

1.4.4 カーネルの起動オプションを調整する

【1.5】アセンブラを読んでみる

1.5.1 main()のアセンブラを読む

1.5.2 レジスタの扱い

1.5.3 スタックの扱い

1.5.4 関数呼び出しの手順

【1.6】この章のまとめ

第2章 printf()の内部動作を追う

【2.1】デバッガを使ってみよう

2.1.1 GDBを起動してみよう

2.1.2 gdbserverで画面崩れを防ぐ

2.1.3 ブレークポイントを張ってみる

2.1.4 ステップ実行してみる

【2.2】デバッガで動作を追ってみる

2.2.1 printf()の中に入っていく

2.2.2 アセンブラベースで処理を見る

2.2.3 逆アセンブル結果と比較する

2.2.4 関数呼び出しの流れを見る

2.2.5 メッセージの出力箇所を探る

2.2.6 vfprintf()の中に入る

2.2.7 ポインタ経由での関数呼び出しを探る

2.2.8 ブレークポイントを整理する

2.2.9 さらに深く追っていく

2.2.10 write()が呼ばれている

2.2.11 呼ばれる命令が異なる場合

2.2.12 メッセージが出力される瞬間

【2.3】システムコールの呼び出し

2.3.1 straceによるトレース

2.3.2 ptrace()によるトレース

2.3.3 ptrace()の細かい動作をカーネルソースから知る

2.3.4 ptrace()で独自トレーサを作る

2.3.5 コアダンプを解析する

【2.4】バイナリエディタを使ってみる

2.4.1 バイナリエディタで実行ファイルを開く

2.4.2 int $0x80の呼び出し部分を探す

2.4.3 実行ファイルを書き換えて確認する

【2.5】この章のまとめ

第3章 Linuxカーネルの処理を探る

【3.1】Linuxカーネルのソースコードを読んでみよう

3.1.1 Linuxカーネルのダウンロード

3.1.2 ディレクトリ構成を見る

3.1.3 目的の処理を探す

3.1.4 見るべきファイルを限定していく

3.1.5 割込みハンドラを見る

3.1.6 割込みハンドラの登録

【3.2】パラメータの渡しかたを見る

3.2.1 レジスタの値を確認する

3.2.2 スタックの状態も確認しておく

3.2.3 システムコール呼び出し後のレジスタの状態

3.2.4 システムコール番号

3.2.5 システムコールの引数

3.2.6 システムコール・ラッパー

【3.3】戻り値の返しかたを見る

3.3.1 システムコールの戻り値

3.3.2 errnoを設定するのは誰か?

3.3.3 errnoの設定処理

3.3.4 Linuxカーネルのエラーの返しかた

【3.4】Linuxカーネルの問題点

3.4.1 引数の個数の制限の問題

3.4.2 戻り値の範囲の問題

【3.5】この章のまとめ

第4章 ライブラリからのシステムコール呼び出し

【4.1】GNU C Library(glibc)

4.1.1 システムコール・ラッパーの重要性

4.1.2 glibcのソースコード

4.1.3 int $0x80の呼び出しを探す

4.1.4 システムコール・ラッパーの定義

4.1.5 システムコール・ラッパーの実体を探す

4.1.6 lessでキーワードを探す

4.1.7 システムコール・ラッパーのテンプレート

【4.2】システムコールについて考える

4.2.1 システムコールのABI

4.2.2 簡単なシステムコール・ラッパーの例

4.2.3 アセンブラで書いた関数をC言語から呼び出す

4.2.4 関数呼び出しのABI

4.2.5 ABIとAPI

4.2.6 writeとwrite()の違い

【4.3】glibcをビルドする

4.3.1 ./configureスクリプトを実行する

4.3.2 ビルド用のディレクトリを作成する

4.3.3 makeを実行する

4.3.4 ./configureからやりなおす

4.3.5 ライブラリをシステムにインストールする

4.3.6 ビルドしたglibcで実行ファイルを作成する

4.3.7 デバッガで追ってみる

【4.4】この章のまとめ

第5章 main()関数の前と後

【5.1】デバッガでスタートアップの処理を追う

5.1.1 とりあえずmain()でブレークしてみる

5.1.2 main()の呼び出し元を探る

5.1.3 エントリ・ポイントを見てみる

5.1.4 main()が呼ばれるまでの処理を追う

5.1.5 main()の呼び出しの前後を見る

【5.2】スタートアップのソースコードを読む

5.2.1 スタートアップの役割

5.2.2 glibcのソースコードを読む

5.2.3 _startを読む

5.2.4 __libc_start_main()を読む

【5.3】exit()の処理

5.3.1 exit()の処理をデバッガで追う

5.3.2 _exit()の呼び出し

5.3.3 exit_groupとexitの2つのシステムコール

5.3.4 _exit()のソースコードを読む

5.3.5 exit()と_exit()とexit_groupとexit

5.3.6 manのカテゴリを見てみる

5.3.7 FreeBSDの場合

5.3.8 exit()の処理を読む

5.3.9 atexit()の処理を読む

【5.4】Linuxカーネルの処理を見てみよう

5.4.1 プログラムの実行はどのようにして行われるのか

5.4.2 execve()の処理

5.4.3 ELFフォーマットのロード

5.4.4 レジスタの設定処理

5.4.5 argv[]の準備

【5.5】この章のまとめ

第6章 標準入出力関数の実装を見る

【6.1】printf()のソースコードを読む

6.1.1 printf()の本体を探す

6.1.2 フォーマット文字列の処理を見る

6.1.3 文字の出力を見る

6.1.4 ファイルポインタの構造

6.1.5 ファイル構造体のバッファリング処理

6.1.6 write()の呼び出し

【6.2】FreeBSDでの実装を見る

6.2.1 FreeBSDのソースコードを見る

6.2.2 FreeBSDの標準Cライブラリ

6.2.3 printf()の先を見る

6.2.4 GDBで関数呼び出しを確認する

6.2.5 フォーマット文字列の処理を見る

【6.3】Newlibでの実装を見る

6.3.1 Newlibのソースコードを見る

6.3.2 printf()の実装を見る

6.3.3 FreeBSDでの実装に似ている

6.3.4 ミニマムな実装が別にある

【6.4】この章のまとめ

第7章 コンパイルの手順と仕組み

【7.1】コンパイルの流れ

7.1.1 コンパイルの広義と狭義の意味

7.1.2 実行ファイルが生成されるまで

7.1.3 gccが行っている処理

【7.2】OSとは何なのか

7.2.1 OSの定義について考える

7.2.2 汎用システムと組込みシステム

7.2.3 カーネルとしての「OS」

7.2.4 システムとしての「OS」

7.2.5 資源の管理と抽象化のためのOS

7.2.6 ソフトウェア動作のベース環境としてのOS

7.2.7 汎用OSと組込みOS

7.2.8 組込みOSの条件

7.2.9 UNIXというOS

7.2.10 「UNIXライク」とはどういう意味か

【7.3】GNU/Linuxディストリビューションとは何なのか

7.3.1 Linuxとは何なのか

7.3.2 CentOSとは何なのか

7.3.3 GNU/Linuxディストリビューション

7.3.4 「LinuxはUNIX互換」の2つの意味

7.3.5 標準Cライブラリ「glibc」

7.3.6 FreeBSDではどうなのか

7.3.7 FreeBSDのソースコードを見る

7.3.8 viのソースコードを見る

【7.4】リンクの処理

7.4.1 ライブラリの場所

7.4.2 glibcの実体

7.4.3 ライブラリを逆アセンブルする

【7.5】プリプロセッサの処理

7.5.1 プリプロセッサの役割

7.5.2 cppを使ってみる

7.5.3 ヘッダファイルの場所

7.5.4 2種類のインクルード方法

7.5.5 標準ヘッダファイル

7.5.6 標準ヘッダファイルの開発元

7.5.7 標準ヘッダファイルの置き場所

7.5.8 GNU/Linuxディストリビューションではどうなのか

【7.6】この章のまとめ

第8章 実行ファイル解析

【8.1】実行ファイルを見てみる

8.1.1 バイナリエディタで実行ファイルを見る

8.1.2 実行ファイルを書き換える

【8.2】ELFフォーマット

8.2.1 readelfとobjdump

8.2.2 ELFヘッダを見てみる

8.2.3 ELFヘッダの構造を知る

8.2.4 いくつものヘッダファイル

8.2.5 ELFヘッダのバイナリを読む

【8.3】セクションの情報を見る

8.3.1 セクション・ヘッダの情報を見てみる

8.3.2 機械語コードを見てみる

8.3.3 メッセージの配置先アドレスを知る

8.3.4 メッセージの配置先アドレスを書き換える

【8.4】セグメント情報を見てみる

8.4.1 領域管理の単位が2つある

8.4.2 Linuxカーネルでの扱いを見る

8.4.3 セクションとセグメントの存在意義

【8.5】共有ライブラリの仕組み

8.5.1 printf()は共有ライブラリ上にある

8.5.2 動的リンクと共有ライブラリ

8.5.3 共有ライブラリを調べる

8.5.4 共有ライブラリの実装を見る

8.5.5 GOTとPLT

8.5.6 GOTの初期値

8.5.7 PLTの全体像

【8.6】この章のまとめ

第9章 最適化

【9.1】最適化オプション

9.1.1 「-O0」の説明を見てみる

9.1.2 最適化の副作用

9.1.3 「-O1」の説明を見てみる

9.1.4 「-O2」の説明を見てみる

9.1.5 「-Os」の説明を見てみる

【9.2】最適化の効果を見てみる

9.2.1 実行ファイルのサイズを見る

9.2.2 デバッグ情報が増加している

9.2.3 実行時間を見る

9.2.4 実行命令数をカウントする

【9.3】アセンブラの変化を見る

9.3.1 -O0のアセンブラを見る

9.3.2 -O1のアセンブラを見る

9.3.3 -O2のアセンブラを見る

9.3.4 -Osのアセンブラを見る

【9.4】シンプルなハロー・ワールドの場合

9.4.1 printf()のputs()への変換

9.4.2 改行コードの除去を確認する

【9.5】この章のまとめ

第10章 様々な環境と様々なアーキテクチャ

【10.1】FreeBSDでのハロー・ワールド

10.1.1 FreeBSDのソースコードの場所

10.1.2 Linux向けの実行ファイルをFreeBSD上で実行できるのか?

10.1.3 FreeBSDでのハロー・ワールド

10.1.4 GDBで動作を追う

10.1.5 FreeBSDのwrite()の処理

【10.2】FreeBSDカーネルの処理を見る

10.2.1 FreeBSDのカーネル・ソースコード

10.2.2 C言語によるシステムコール処理

10.2.3 copyin()による引数の準備

10.2.4 システムコール番号

10.2.5 FreeBSDのシステムコール・ラッパー

10.2.6 エラー処理を見る

10.2.7 他のアーキテクチャではどうなのか

【10.3】FreeBSDのLinuxエミュレーション機能

10.3.1 システムコール・テーブルの置き換え

10.3.2 システムコールごとの対応

10.3.3 引数の渡しかたの対応

10.3.4 エラー番号の変換

10.3.5 LinuxとFreeBSDのABIの比較と考察

【10.4】Linux/x86以外について考える

10.4.1 ARMのクロスコンパイル環境

10.4.2 ARMの実行ファイルを作成する

10.4.3 シミュレータで実行してみる

10.4.4 GDBで動作を追う

10.4.5 ARMのシステムコール・ラッパー

10.4.6 シミュレータ内のシステムコール処理

10.4.7 システムコール番号を見る

10.4.8 モニタのシステムコール

10.4.9 POSIX以外のシステムコール

【10.5】この章のまとめ

第11章 可変長引数の扱い

【11.1】可変長引数の関数を作る

11.1.1 printf()をもう一度見てみる

11.1.2 可変長引数のサンプル・プログラム

【11.2】可変長引数は,どのようにして渡されているのか

11.2.1 可変長引数の関数の呼び出し

11.2.2 va_start()による初期化処理

11.2.3 va_arg()による引数の取得

【11.3】x86以外のアーキテクチャの場合

11.3.1 ARMでの関数呼び出しを見てみる

11.3.2 ARM用の実行ファイルを生成する

11.3.3 ARMでの可変長引数の関数呼び出し

11.3.4 ARMでのva_start()による初期化処理

11.3.5 ARMでのva_arg()による引数の取得

【11.4】この章のまとめ

第12章 解析の集大成-システムコールの切替えを見る

【12.1】_dl_sysinfoの設定を探る

12.1.1 システムコール呼び出しをもう一度見る

12.1.2 共有ライブラリについて調べる

12.1.3 システムコールの呼び出し箇所を見る

12.1.4 _dl_sysinfoには何が設定されているのか?

12.1.5 ウォッチポイントを利用して調べる

12.1.6 スタートアップの実装を見る

【12.2】AT_SYSINFOによるパラメータ渡し

12.2.1 Linuxカーネルからのパラメータ渡し

12.2.2 AT_SYSINFOをキーワードにして探す

12.2.3 AT_SYSINFOに渡されるもの

12.2.4 __kernel_vsyscallの定義

12.2.5 パラメータはスタック上に格納されている

12.2.6 渡されたパラメータを確認する

12.2.7 スタートアップでのパラメータ取得

12.2.8 /procでパラメータを確認する

【12.3】VDSOとシステムコール

12.3.1 VDSOとは何か

12.3.2 vsyscallの設定

12.3.3 vsyscallの選択

12.3.4 CPUIDのSEPフラグ

12.3.5 CPUIDからsysenterの利用可否を判断する

12.3.6 sysenterを無効化する方法

12.3.7 VDSOを無効化する方法

12.3.8 VDSOが無効になった場合の動作

12.3.9 gettimeofday()の実装

12.3.10 GDB側の対応

【12.4】この章のまとめ

PR

秀和システム