[インデックス 14207] ファイルの概要
このコミットは、Go言語のリンカ (cmd/5l
, cmd/6l
, cmd/8l
はそれぞれARM、x86-64、x86アーキテクチャ向けのリンカを指します) において、浮動小数点数を.rodata
(読み取り専用データ) セクションに配置するように変更するものです。これにより、浮動小数点定数が実行時に変更されることを防ぎ、メモリ効率とセキュリティを向上させます。
コミット
commit 3dbbb6eb4c5b063e3ab863c2a7d1d607945aa29d
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Tue Oct 23 00:59:53 2012 +0800
cmd/5l, cmd/6l, cmd/8l: put floating point numbers in .rodata section
R=golang-dev, rsc
CC=0xe2.0x9a.0x9b, golang-dev
https://golang.org/cl/6742063
---
src/cmd/5l/obj.c | 4 ++--
src/cmd/6l/obj.c | 4 ++--
src/cmd/8l/obj.c | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c
index bf2978d05e..4c2603d28a 100644
--- a/src/cmd/5l/obj.c
+++ b/src/cmd/5l/obj.c
@@ -688,7 +688,7 @@ loop:
sprintf(literal, "$%ux", ieeedtof(&p->from.ieee));
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SBSS;
+ s->type = SRODATA;
adduint32(s, ieeedtof(&p->from.ieee));
s->reachable = 0;
}
@@ -710,7 +710,7 @@ loop:
p->from.ieee.l, p->from.ieee.h);
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SBSS;
+ s->type = SRODATA;
adduint32(s, p->from.ieee.l);
adduint32(s, p->from.ieee.h);
s->reachable = 0;
diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c
index 5987310f95..dad217cc86 100644
--- a/src/cmd/6l/obj.c
+++ b/src/cmd/6l/obj.c
@@ -682,7 +682,7 @@ loop:
sprintf(literal, "$%ux", ieeedtof(&p->from.ieee));
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SDATA;
+ s->type = SRODATA;
adduint32(s, ieeedtof(&p->from.ieee));
s->reachable = 0;
}
@@ -716,7 +716,7 @@ loop:
p->from.ieee.l, p->from.ieee.h);
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SDATA;
+ s->type = SRODATA;
adduint32(s, p->from.ieee.l);
adduint32(s, p->from.ieee.h);
s->reachable = 0;
diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c
index 773a6ddfb1..19e351d998 100644
--- a/src/cmd/8l/obj.c
+++ b/src/cmd/8l/obj.c
@@ -675,7 +675,7 @@ loop:
sprintf(literal, "$%ux", ieeedtof(&p->from.ieee));
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SDATA;
+ s->type = SRODATA;
adduint32(s, ieeedtof(&p->from.ieee));
s->reachable = 0;
}
@@ -709,7 +709,7 @@ loop:
p->from.ieee.l, p->from.ieee.h);
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SDATA;
+ s->type = SRODATA;
adduint32(s, p->from.ieee.l);
adduint32(s, p->from.ieee.h);
s->reachable = 0;
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3dbbb6eb4c5b063e3ab863c2a7d1d607945aa29d
元コミット内容
cmd/5l, cmd/6l, cmd/8l: put floating point numbers in .rodata section
R=golang-dev, rsc
CC=0xe2.0x9a.0x9b, golang-dev
https://golang.org/cl/6742063
変更の背景
この変更の背景には、プログラムのメモリセクションの適切な利用と最適化があります。従来のGoリンカでは、浮動小数点定数が.bss
(未初期化データ) または.data
(初期化済みデータ) セクションに配置される可能性がありました。
.bss
セクション: 通常、初期値がゼロであるグローバル変数や静的変数が配置されます。プログラムの起動時にゼロで初期化されます。.data
セクション: 初期値を持つグローバル変数や静的変数が配置されます。プログラムの実行前に、実行ファイルに格納された初期値で初期化されます。
浮動小数点定数は、プログラムの実行中に値が変更されるべきではない「定数」です。これらを書き込み可能な.bss
や.data
セクションに配置することは、以下の問題を引き起こす可能性があります。
- 意図しない変更のリスク: プログラムのバグにより、誤って浮動小数点定数の値が変更されてしまう可能性があります。これは、特に数値計算の正確性が求められるアプリケーションにおいて深刻な問題となります。
- メモリ効率の低下: 読み取り専用であるべきデータが書き込み可能なセクションに配置されることで、メモリ保護の粒度が粗くなり、潜在的にメモリ使用効率が低下する可能性があります。
- セキュリティの脆弱性: 悪意のある攻撃者が、書き込み可能なセクションにある定数を改ざんすることで、プログラムの挙動を不正に操作する可能性が生じます。
これらの問題を解決し、プログラムの堅牢性、効率性、セキュリティを向上させるために、浮動小数点定数を読み取り専用の.rodata
セクションに移動する必要がありました。
前提知識の解説
このコミットを理解するためには、以下の概念を把握しておく必要があります。
1. リンカ (Linker)
リンカは、コンパイラによって生成された複数のオブジェクトファイル(.o
ファイルなど)とライブラリを結合し、実行可能なプログラムや共有ライブラリを生成するツールです。リンカの主な役割は以下の通りです。
- シンボル解決: オブジェクトファイル間で参照されている関数や変数のアドレスを解決します。
- 再配置: 各オブジェクトファイル内のコードやデータが、最終的な実行ファイル内のどこに配置されるかを決定し、それに応じてアドレス参照を修正します。
- メモリセクションの構築: プログラムのコード、データ、スタック、ヒープなどを格納するためのメモリセクションを定義し、オブジェクトファイルの内容をこれらのセクションに配置します。
Go言語のビルドプロセスでは、cmd/5l
(ARM), cmd/6l
(x86-64), cmd/8l
(x86) といったツールがリンカとして機能します。
2. 実行ファイルのメモリセクション
一般的な実行ファイル(ELF形式など)は、プログラムの異なる種類のデータを格納するために、いくつかの論理的なメモリセクションに分割されます。主要なセクションは以下の通りです。
.text
(または.code
): 実行可能な機械語コードが格納されます。通常、読み取り専用で実行可能です。.rodata
(Read-Only Data): 読み取り専用のデータが格納されます。これには、文字列リテラル、定数、浮動小数点定数などが含まれます。実行時に変更されることはありません。.data
(Initialized Data): 初期値を持つグローバル変数や静的変数が格納されます。プログラムの起動時に、実行ファイルに格納された初期値で初期化されます。読み書き可能です。.bss
(Block Started by Symbol): 初期値を持たないグローバル変数や静的変数が格納されます。プログラムの起動時に、通常はゼロで初期化されます。読み書き可能です。.stack
: 関数呼び出し時のローカル変数、引数、戻りアドレスなどが一時的に格納される領域です。.heap
: プログラム実行中に動的にメモリを確保する領域です。
3. 浮動小数点数 (Floating-Point Numbers)
浮動小数点数は、実数をコンピュータで表現するための形式です。IEEE 754標準が広く用いられており、単精度 (32ビット) や倍精度 (64ビット) などがあります。プログラム内で直接記述される浮動小数点リテラル(例: 3.14
, 1.0e-5
)は、コンパイル時にそのバイナリ表現に変換され、定数として扱われます。
4. Go言語の内部表現とリンカのシンボルタイプ
Go言語のリンカは、内部的に様々なシンボルタイプを扱います。このコミットで登場するSBSS
, SDATA
, SRODATA
は、Goリンカがシンボルをどのメモリセクションに配置するかを示す内部的なタイプです。
SBSS
:.bss
セクションに対応するシンボルタイプ。SDATA
:.data
セクションに対応するシンボルタイプ。SRODATA
:.rodata
セクションに対応するシンボルタイプ。
lookup
関数は、特定の文字列リテラル(この場合は浮動小数点数のバイナリ表現を文字列化したもの)に対応するシンボルを検索または作成します。s->type = ...
は、そのシンボルのタイプを設定しています。
技術的詳細
このコミットの技術的な核心は、Go言語のリンカが浮動小数点定数を処理する方法を変更した点にあります。
Goのコンパイラは、ソースコード中の浮動小数点リテラルを、リンカが処理できる形式に変換します。リンカは、これらの浮動小数点定数をメモリ上に配置する際に、どのセクションに割り当てるかを決定します。
変更前は、浮動小数点定数がSBSS
またはSDATA
として扱われていました。これは、これらの定数がそれぞれ.bss
または.data
セクションに配置されることを意味します。
src/cmd/5l/obj.c
(ARMリンカ) では、浮動小数点定数がSBSS
として扱われていました。src/cmd/6l/obj.c
(x86-64リンカ) およびsrc/cmd/8l/obj.c
(x86リンカ) では、浮動小数点定数がSDATA
として扱われていました。
このコミットでは、これらのリンカが浮動小数点定数をSRODATA
として扱うように修正されました。これにより、浮動小数点定数は最終的に実行ファイルの.rodata
セクションに配置されるようになります。
.rodata
セクションは、オペレーティングシステムによって読み取り専用としてメモリにマッピングされます。これにより、以下の利点が得られます。
- 不変性の保証: 浮動小数点定数が実行時に誤って変更されることが物理的に不可能になります。これは、プログラムの信頼性と予測可能性を高めます。
- メモリ保護の強化: 読み取り専用データと書き込み可能データを明確に分離することで、メモリ保護メカニズムをより効果的に活用できます。例えば、データセクションへの書き込みを監視するセキュリティツールは、
.rodata
セクションへの書き込み試行を不正アクセスとして検出できます。 - ページ共有の可能性: 複数のプロセスが同じ実行ファイルを共有する場合、
.rodata
セクションは読み取り専用であるため、OSはこれらのプロセス間で同じ物理メモリページを共有させることができます。これにより、システム全体のメモリ使用量を削減できる可能性があります。 - キャッシュ効率: 読み取り専用データは、CPUのキャッシュにロードされた後、変更されることがないため、キャッシュコヒーレンシの維持に必要なオーバーヘッドが少なくなります。
この変更は、Go言語のビルドツールチェーンの低レベルな部分に影響を与えるものであり、Goプログラムの実行時挙動の堅牢性と効率性を向上させるための重要な改善です。
コアとなるコードの変更箇所
変更は、src/cmd/5l/obj.c
, src/cmd/6l/obj.c
, src/cmd/8l/obj.c
の3つのファイルにわたって行われています。それぞれのファイルで、浮動小数点定数を処理する部分のシンボルタイプの設定が変更されています。
diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c
index bf2978d05e..4c2603d28a 100644
--- a/src/cmd/5l/obj.c
+++ b/src/cmd/5l/obj.c
@@ -688,7 +688,7 @@ loop:
sprintf(literal, "$%ux", ieeedtof(&p->from.ieee));
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SBSS;
+ s->type = SRODATA;
adduint32(s, ieeedtof(&p->from.ieee));
s->reachable = 0;
}
@@ -710,7 +710,7 @@ loop:
p->from.ieee.l, p->from.ieee.h);
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SBSS;
+ s->type = SRODATA;
adduint32(s, p->from.ieee.l);
adduint32(s, p->from.ieee.h);
s->reachable = 0;
diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c
index 5987310f95..dad217cc86 100644
--- a/src/cmd/6l/obj.c
+++ b/src/cmd/6l/obj.c
@@ -682,7 +682,7 @@ loop:
sprintf(literal, "$%ux", ieeedtof(&p->from.ieee));
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SDATA;
+ s->type = SRODATA;
adduint32(s, ieeedtof(&p->from.ieee));
s->reachable = 0;
}
@@ -716,7 +716,7 @@ loop:
p->from.ieee.l, p->from.ieee.h);
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SDATA;
+ s->type = SRODATA;
adduint32(s, p->from.ieee.l);
adduint32(s, p->from.ieee.h);
s->reachable = 0;
diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c
index 773a6ddfb1..19e351d998 100644
--- a/src/cmd/8l/obj.c
+++ b/src/cmd/8l/obj.c
@@ -675,7 +675,7 @@ loop:
sprintf(literal, "$%ux", ieeedtof(&p->from.ieee));
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SDATA;
+ s->type = SRODATA;
adduint32(s, ieeedtof(&p->from.ieee));
s->reachable = 0;
}
@@ -709,7 +709,7 @@ loop:
p->from.ieee.l, p->from.ieee.h);
s = lookup(literal, 0);
if(s->type == 0) {
- s->type = SDATA;
+ s->type = SRODATA;
adduint32(s, p->from.ieee.l);
adduint32(s, p->from.ieee.h);
s->reachable = 0;
コアとなるコードの解説
各ファイルの変更箇所は非常に似ています。if(s->type == 0)
ブロック内で、新しく作成されるシンボル s
のタイプを設定している行が変更されています。
-
src/cmd/5l/obj.c
:s->type = SBSS;
がs->type = SRODATA;
に変更。- これは、ARMアーキテクチャ向けのリンカが、浮動小数点定数を未初期化データセクション (
.bss
) ではなく、読み取り専用データセクション (.rodata
) に配置するように変更されたことを意味します。
-
src/cmd/6l/obj.c
:s->type = SDATA;
がs->type = SRODATA;
に変更。- これは、x86-64アーキテクチャ向けのリンカが、浮動小数点定数を初期化済みデータセクション (
.data
) ではなく、読み取り専用データセクション (.rodata
) に配置するように変更されたことを意味します。
-
src/cmd/8l/obj.c
:s->type = SDATA;
がs->type = SRODATA;
に変更。- これは、x86アーキテクチャ向けのリンカが、浮動小数点定数を初期化済みデータセクション (
.data
) ではなく、読み取り専用データセクション (.rodata
) に配置するように変更されたことを意味します。
これらの変更により、Go言語でコンパイルされたプログラム内の浮動小数点定数は、すべてのサポート対象アーキテクチャにおいて、実行時に変更されることのない安全なメモリ領域に配置されるようになります。ieeedtof
関数は、IEEE 754形式の浮動小数点数を整数表現に変換するユーティリティ関数であり、adduint32
はシンボルに32ビットのデータを追加する関数です。これらの関数は、浮動小数点定数のバイナリ表現を.rodata
セクションに書き込むために使用されます。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- このコミットのGo Code Review: https://golang.org/cl/6742063
参考にした情報源リンク
- ELFファイルフォーマットとセクションについて:
- リンカの役割について:
- IEEE 754 浮動小数点標準について:
- Go言語のビルドプロセスに関する一般的な情報 (より詳細な情報が必要な場合):
- "Go in Action" や "The Go Programming Language" などの書籍
- Go言語の公式ドキュメント: https://go.dev/doc/