質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

3454閲覧

C言語の位置独立コード、GOT

strike1217

総合スコア651

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2016/11/04 11:36

.#include<stdio.h>

int i = 9;

int main(){

....

と宣言した場合、int i はグローバル変数になると思います。
共有ライブラリのなどの外部関数を参照するのにGOTの存在が必要だと思っていたのですが、グローバル変数へのアクセスの際にもGOTを活用する場面が出てくるようです。

位置独立グローバル変数と非位置独立グローバル変数とは何が違うのですか?
上記のint i は非位置独立グローバル変数であっていますか?
逆に、位置独立グローバル変数はどのようにして宣言するのでしょうか?
なぜ、グローバル変数へのアクセスにGOTが必要になってくるのかわかりません。

そもそもGOTから外部の関数にアクセスする際にも「共有ライブラリの仮想メモリ空間」と「各プロセスの仮想メモリ空間」とはそれぞれ独立していて、別の仮想メモリ空間にアクセスできませんよね?
GOT内に存在する外部関数のポインタは、どうやって「別空間のアドレス」を参照しているのでしょうか?

本の中に、
位置独立ではないコードから「グローバル変数」や「非static関数」を使うと再配置が発生し、メモリが共有されなくなってしまう・・・
と書いてあったのですが、意味がよくわかりません。

非位置独立コードにおいて、
マップされているメモリの内容を変更してしまうと、なぜメモリの共有ができなくなってしまうのですか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otaks

2016/11/05 01:06

参照している書籍名/サイト名(URL)は何ですか?
strike1217

2016/11/05 04:48

「コンパイラを作ろう」という本でございます。
guest

回答3

0

ベストアンサー

位置独立グローバル変数と非位置独立グローバル変数とは何が違うのですか?

この言葉は聞いたことありませんが、
ダイナミックリンカが、リンク後の機械語に対する再配置処理(具体的には、オペランドにグローバル変数のアドレスを埋め込む)をしなくてすむなら、位置独立といえるでしょう。つまり、グローバル変数が位置独立になるのではなく、グローバル変数にアクセスするコードが位置独立になるわけです。

上記のint i は非位置独立グローバル変数であっていますか?

いえ、ソースコードだけでは、位置独立かどうかはきまりません。その変数にアクセスするコードがすべて位置独立であれば、位置独立なグローバル変数だと言えると思います。

逆に、位置独立グローバル変数はどのようにして宣言するのでしょうか?

ダイナミックリンクすれば、位置独立になります。このとき、共有ライブラリのコードからグローバル変数を参照するのであれば、コンパイル時に -fPIC をつけて位置独立なコードを生成しておく必要があります。そうでなければ、リンクエラーになります。 -fPIC を指定すると、グローバル変数にGOT経由でアクセスするコードが生成されます。

なぜ、グローバル変数へのアクセスにGOTが必要になってくるのかわかりません。

メモリ共有や実行時性能などの観点から、ダイナミックリンカが実行時に機械語の再配置処理を行うことはできません。したがって、ダイナミックリンクされるコードはすべて位置独立である必要があります。グローバル変数へのアクセスを位置独立にするためにグローバル変数のアドレスを保持したテーブル GOT を用意し、ダイナミックリンカが実行時にそのテーブルを埋めます。
i というグローバル変数があれば、それを参照する共有ライブラリのGOTにそのアドレスを埋めます。この処理はロード時に一括して行われます。

そもそもGOTから外部の関数にアクセスする際にも「共有ライブラリの仮想メモリ空間」と「各プロセスの仮想メモリ空間」とはそれぞれ独立していて、別の仮想メモリ空間にアクセスできませんよね?

いえ、共有ライブラリは各プロセスの仮想メモリ空間にそれぞれロードされます。ですので、アクセスできます。

非位置独立コードにおいて、

マップされているメモリの内容を変更してしまうと、なぜメモリの共有ができなくなってしまうのですか?

ここが一番重要ですが、プロセスAとプロセスBに共有ライブラリXがロードされたとします。プロセスAとプロセスBの仮想メモリ空間は別ですが、Xのコードを保持するメモリの内容が全く同じ場合は、一つの物理メモリを共有します。ここで、共有の単位はページです。Linux であれば、4Kbytes です。4Kbytes ごとに区切ったときにプロセスAとプロセスBが同じ内容を持っているとそのページの内容(ここでは、Xのコードセグメント)を保持するための物理メモリをプロセス間で共有します。このとき、そのメモリのアドレス(Xのコードが配置されるアドレス)はプロセスAから見たときととプロセスBから見たときで異なっていてもかまいません。
(余談ですが、スタティックリンクでも同じプログラムを2つ動かすとその text セグメントは共有されます。これは、プロセス間で同じ内容を参照するからです)

つまり、ページの内容が全く同じでないと共有できないわけです。もし、コードがグローバル変数を参照するときに非位置独立なコードで参照しているとどうなるでしょう。ダイナミックリンカがコードを書き換えてグローバル変数のアドレスを埋め込むことになります。グローバル変数の割当アドレスはプロセスによって異なる場合があります。そうなると、プロセス間で内容が異なるのでもはや共有できなくなってしまうわけです。

ですから、位置独立なコードで共有ライブラリを生成することでどのアドレスに配置されても同じ内容のコードで実行できるようにするわけです。

投稿2016/11/04 12:59

mit0223

総合スコア3401

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2016/11/05 05:40 編集

返事が遅れました。 すいません。 とてもわかりやすいです。 ASLRなどのランダム化を行うと、グローバル変数はGOTに配置されるということですね。 共有ライブラリは、個々のプロセスにそれぞれロードされるんですね! その場合、「物理メモリ空間の方では、複数のページ(共有ライブラリが配置されている)は複数のプロセスで共有されている」という理解で大丈夫ですか?
mit0223

2016/11/06 00:12

> 「物理メモリ空間の方では、複数のページ(共有ライブラリが配置されている)は複数のプロセスで共有されている」という理解で大丈夫ですか? はい、そうです。 Linux では、メモリマッピングと言って、ファイル中の特定の位置の内容(オフセットと長さで指定、ページ単位)を各プロセスの仮想メモリ空間上に結びつけてアクセスできるようにすることができます。たとえば、 libx.so の オフセット 8kb から 24kb までの領域をプロセスの仮想メモリ空間0x10000 から 0x1ffff までにマッピングして、プロセスの0x10004番地を参照すると libx.so の8198バイト目の内容が見えます(この場合、4ページの連続領域をマッピングしています)。 参考:https://linuxjm.osdn.jp/html/LDP_man-pages/man2/mmap.2.html ダイナミックリンカはこの mmap システムコールを使って共有ライブラリ内のテキストセグメントや初期値ありデータセグメントをメモリ上にマッピングします。このとき、他のプロセスが同じファイル領域に対するマッピングを行っている場合、そのメモリマッピングに利用される物理メモリが共有されます。 ダイナミックリンカの動きを理解する場合、このメモリマッピングを理解するのが近道です。strace というコマンド(普通はインストールされていないので、インストールする必要があります)を使えば、ダイナミックリンカが mmap を呼ぶ様子が観測できます。 # strace /bin/echo とかやると、 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 mmap(NULL, 3936832, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f2a3bb72000 のように libc.so.cache という共有ライブラリのテキストセグメントをメモリマッピングする様子を観測できます。カーネルは mmap が呼ばれると、同じファイル領域に対するマッピングが他のプロセスですでに行われていないかを検索し、すでに行われている場合はその物理メモリを共有します。
mit0223

2016/11/06 01:02

あと、厳密には物理メモリを共有するのではなく、マッピングを共有します。たとえば、1GBytes のファイルをマッピングした場合、カーネルはマッピングした内容をすぐにファイルから物理メモリに読み込むわけではありません。アクセスしたときにオンデマンドにページ単位で読み込まれます(仮想記憶のページイン動作)。 mmapでマッピングを共有すると、物理メモリにまだ読み込まれていない部分も含めて共有されているという概念モデルです。
strike1217

2016/11/06 10:22

ありがとうございます。
guest

0

正直、「GOT」ってなんですか?と思いましたが...

例えば、ソースファイル a.c の中で、

int i; main() { .... }

の様にiをグローバル変数として使っていて、ソースファイル b.c の中でも、

int i; func() { .... }

の様にグローバル変数 i を使っていて、
両方のファイルをリンクすると、変数 i が衝突しているので
リンクエラーとなってしまうかと思います。
しかし、 b.c の中で

extern int i; func() { .... }

と宣言し、最初に書いた a.c とリンクすると、b.c の中でも a.c の変数 i が参照できます。
つまり、同じ領域が割り当てられます。

またそれとは別に、 a.c の中で、

static int i; main() { .... }

として、 b.c の中で、

static int i; func() { .... }

とした場合は、a.c の i と b.c の i は別の変数となります。
つまり、別々の領域が確保されます。

ここでは、a.c と b.c の2個のファイルで書きましたが、
リンク対象がライブラリの場合でも同じです。

この様な事を知りたかったのでしょうか?

投稿2016/11/04 13:00

編集2016/11/04 13:01
ShinyaAnan

総合スコア241

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2016/11/05 04:47

ありがとうございます。 自分が聞きたかったことは、もう少し難しい話になってしまいます。
guest

0

初めに”GOT"って何?と思ってググってみた。
ld -z relro で GOT overwrite attack から身を守る

ここを読んで行ってようやく意味が分かった。
昔からリンカーが2パスでリンクをしてきたが、恐らく”OBJ”の時に中間的なアドレスを”仮”にセットして”EXE"や”DLL"などを作成するまでの間に攻撃を受ける危険についての解説であった。

きっと質問の意図は、最終的な実行形式が作成される場合、そのスタティックな位置(メモリー配置)が決まるまでGOTが要るが、何故?という質問と考えたのですが。

投稿2016/11/04 12:03

MasahikoHirata

総合スコア3747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2016/11/04 12:08

超簡単に言いますと・・・ 「GOTがなぜ必要で、これを使ってどのようにして、位置独立コードを実現しているか」 という質問内容です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問