Tru64 UNIX システムでは,省略時のポインタ型のサイズは 64 ビットで,すべてのシステム・インタフェースは 64 ビット・ポインタを使用します。 Compaq C コンパイラは,64 ビット・ポインタをサポートするだけでなく,32 ビット・ポインタの使用もサポートします。
ほとんどの場合,64 ビット・ポインタがプログラム内で問題を起こすことはなく,32 ビット・ポインタの問題はまったく無視しても構いません。 ただし,次の場合は 32 ビット・ポインタの問題について考慮する必要があります。
int
型へのポインタ代入を含むプログラムを移植する場合。
プログラムに 64 ビットのポインタ・サイズが必要でなく,ポインタがメモリを使いすぎる原因となっている場合。 たとえば,プログラムでポインタ・フィールドから構成される大きな構造体を使用している場合に,それらのフィールドが 32 ビットのアドレス範囲を超える必要がない場合などです。
32 ビット・システムと 64 ビット・システムが混在する環境に対するプログラムを開発しており,プログラムのメモリ内構造体が両方のシステムからアクセスされる場合。
アプリケーションで 32 ビット・ポインタを使用する場合には,特殊なオプションを使用したアプリケーションのコンパイルおよびリンクが必要であり,コード固有の性質により,ソース・コードの変更も必要となる場合があります。
この付録では,次の種類のポインタについて説明します。
long ポインタ: 64 ビット・ポインタ。 long ポインタを宣言すると 64 ビットが割り当てられます。 Tru64 UNIX システムでは,このポインタが省略時のポインタ型です。
単純ポインタ: 非ポインタ・データ・タイプへのポインタ。
たとえば,int *num_val
のようになります。
厳密に言うと,ポイントされている型にはポインタが含まれていないため,ポイントされている型のサイズは,ポインタの型のサイズに依存しません。
複合ポインタ: ポインタのサイズによってサイズが決まるデータ型へのポインタ。
たとえば,char **FontList
のようになります。
A.1 コンパイラ・システムと 32 ビット・ポインタの言語サポート
次のメカニズムにより,32 ビット・ポインタの使用が制御されます。
cc
のオプション
-xtaso
および
-xtaso_short
は,ポインタを 32 ビット・データ型として使用するプログラムをコンパイルする場合に必要です。
-xtaso
#pragma pointer_size
指示文が認識されるようにして,リンクの場合には
-taso
(切り捨てアドレス・サポート・オプション) がリンカに渡されるようにします。
-xtaso_short
-xtaso_short
では,short ポインタを使用するように初期コンパイラ状態を設定しますが,それ以外は
-xtaso
と同じです。
すべてのシステム・ルーチンは,64 ビット・ポインタを使用し続けるため,この方法でコンパイルする場合,ほとんどのアプリケーションではソースの変更が必要になります。
ただし,protect_headers_setup
(A.3.3 項を参照) を使用すると,ソース・コードを変更する必要性を大幅に減少させることができます。
通常,short ポインタの使用には対象となるアプリケーションに関する十分な知識が必要であるため,移植のための支援ツールとして
-xtaso_short
を使用することは推奨できません。
特に,コーディングのよくないプログラム (ポインタと
int
値が数多く混在するプログラム) を移植するときには使用しないでください。
ld
のオプション
-taso
は,実行可能ファイルと関連シェアード・ライブラリが下位 31 ビットでアドレス可能な仮想アドレス空間に配置されるようにします。
-taso
オプションは,アドレス値が 32 ビットの変数に格納できることを想定したプログラム (ポインタが
int
型変数と同じ長さであることを想定したプログラム) を移植する場合に役に立ちます。
-taso
オプションはポインタのデータ型のサイズには影響しませんが,プログラム内のポインタの値が,32 ビット表現でも 64 ビット表現でも同一であることを保証します。
リンカ・オプション
-taso
は,実行時環境とライブラリの使用方法を制限します。
-taso
オプションについての詳細は,A.2 節を参照してください。
#pragma pointer_size
指示文は,C 言語プログラム内のポインタ型のサイズを制御します。
これらのプラグマは,cc
コマンド行で
-xtaso
あるいは
-xtaso_short
オプションが指定されている場合にのみ,コンパイラによって認識されます。
これらのオプションがいずれも指定されていない場合,プラグマは無視されます。
#pragma pointer_size
指示文の詳細については,3.12 節を参照してください。
次は,short ポインタと long ポインタの両方を使用するための例です。
main () { int *a_ptr; printf ("A pointer is %ld bytes\n", sizeof (a_ptr)); }
省略時の設定または
-xtaso
オプションでコンパイルした場合,サンプル・プログラムは次のようなメッセージを出力します。
A pointer is 8 bytes
-xtaso_short
オプションでコンパイルした場合,サンプル・プログラムは次のようなメッセージを出力します。
A pointer is 4 bytes
-taso
オプションは,プログラム内のすべての 64 ビット・ポインタ内で 32 ビットのアドレッシングが行われるようにします。
これにより,32 ビットのアドレッシングに関する問題はほとんど解決されます。
ただし,一部のポインタの物理サイズを 32 ビットに制限することを要求することは除きます (これは
-xtaso
または
-xtaso_short
オプション,および
pointer_size
プラグマによって処理されます)。
-taso
オプションは,プログラム内の
int
型へのポインタ代入によって発生するアドレッシングの問題を処理するために頻繁に使用されます。
多くの C 言語プログラム,特に現在認められているプログラミング手法に準拠していない旧式のプログラムでは,int
型変数にポインタを代入しています。
このような代入は推奨できませんが,ポインタと
int
型変数が同一サイズのシステムの場合は正しい結果が得られます。
一方,Tru64 UNIX システムの場合,64 ビットのポインタを 32 ビットの
int
型変数に代入するときにアドレスの上位 32 ビットが失われてしまうため,誤った結果が発生する可能性があります。
次のコード例で,この問題を示します。
{ char *x; /* 64-bit pointer */ int z; /* 32-bit int variable */ . . . x = malloc(1024); /* get memory and store address in 64 bits */ z = x; /* assign low-order 32 bits of 64-bit pointer to 32-bit int variable */ }
int
型へのポインタ代入による問題を処理する際に最も移植性の高い方法は,コードを修正してこのような代入を取り除くことです。
ただし,大規模なアプリケーションの場合は,時間のかかる作業になる可能性があります。
ソース・コード内の
int
型へのポインタ代入を検索する場合は,lint -Q
コマンドを使用します。
この問題の解決に,-taso
オプションを使用する方法もあります。
-taso
オプションを使用すると,int
型へのポインタ代入を修正する必要がなくなります。
-taso
オプションは,プログラムが実行を開始するときに,プログラム内のすべての位置が 64 ビット・アドレスの下位 31 ビットで表現できるようにプログラムのアドレス空間を配置します。
これには,シェアード・ライブラリからのルーチンおよびデータのアドレスも含まれます。
-taso
オプションは,システムがサポートするどのデータ型のサイズにもまったく影響しません。
データ型への影響は,ポインタ内のアドレスを 31 ビットに制限するという点だけです。
つまり,ポインタのサイズは 64 ビットのままですが,アドレスが使用するのは下位 31 ビットだけとなります。
A.2.1 -taso オプションの使用と効果
-taso
オプションは,オブジェクト・モジュールの作成に使用する
cc
または
ld
のコマンド行に指定します。
cc
コマンド行に指定した場合,-taso
オプションは
ld
リンカに渡されます。
-taso
オプションは,リンカがオブジェクト・モジュールにフラグを設定するようにします。
このフラグが設定されていると,ローダはモジュールを 31 ビットのアドレス空間にロードします。
31 ビットのアドレス制限は,int
型へのポインタ代入を行うときに 32 ビットの
int
型変数の符号ビット (ビット 31) を設定する可能性を回避するために使用されます。
int
型へのポインタ代入によって
int
型変数の符号ビットが設定できるようにした場合,符号拡張により問題が発生する可能性があります。
次にその例を示します。
{ char *x; /* 64-bit pointer */ int z; /* 32-bit int variable */ . . . /* address of named_obj = 0x0000 0000 8000 0000 */ x = &named_obj; /* 0x0000 0000 8000 0000 = original pointer value */ z = x; /* 0x8000 0000 = value created by pointer-to-int assignment */ x = z; /* 0xffff ffff 8000 0000 = value created by pointer- to-int-to-pointer or pointer-to-int-to-long assignment (32 high-order bits set to ones by sign extension) */ }
-taso
オプションは,アプリケーションのテキスト・セグメントとデータ・セグメントが 31 ビットのアドレスでアクセスできるメモリにロードされるようにします。
したがって,int
型変数にポインタが代入される場合,64 ビットのポインタと 32 ビットの変数の値は常に等しくなります (A.2.2 項で説明する特殊な場合を除きます)。
図 A-1
は,-taso
および
-call_shared
オプションを使用するプログラムのメモリの図です。
cc
コマンドを実行してリンカを起動した場合の省略時の設定は
-call_shared
で,ld
を直接起動した場合の省略時の設定は
-non_shared
です。
図 A-1: -taso オプションを使用した場合のメモリのレイアウト
スタックとヒープのアドレスも 31 ビット内に入ることに注意してください。 スタックはテキスト・セグメントの最下部から下に向かって伸びて,ヒープはデータ・セグメントの最上部から上に向かって伸びます。
アプリケーションのテキスト・セグメントとデータ・セグメントが下位メモリにロードされるようにするには,-T
および
-D
オプション (それぞれテキスト・セグメントとデータ・セグメントのアドレスを設定するリンカ・オプション) を使用することもできます。
一方,-taso
オプションはテキスト・セグメントとデータ・セグメントの省略時のアドレス設定に加え,31 ビットのアドレス空間外にリンクされているシェアード・ライブラリもローダによって適切に再配置されるようにします。
テキスト・セグメントとデータ・セグメントに使用される省略時のアドレス設定は,cc
コマンド行で指定するオプションによって決まります。
-taso
オプションとともに
-non_shared
または
-call_shared
オプションを指定すると,省略時のアドレス設定は次のようになります。
0x0000 0000 1200 0000 (テキスト・セグメントの開始アドレス) 0x0000 0000 1400 0000 (データ・セグメントの開始アドレス)
-taso
オプションとともに
-shared
オプションを指定すると,省略時のアドレス設定は次のようになります。
0x0000 0000 7000 0000 (テキスト・セグメントの開始アドレス) 0x0000 0000 8000 0000 (データ・セグメントの終了アドレス)
ほとんどのアプリケーションの場合,これらの省略時の設定値を使用することによってテキスト・セグメントとデータ・セグメントに十分な空間が作成されます (テキスト・セグメントとデータ・セグメントの内容の詳細については,『Object File/Symbol Table Format Specification』を参照してください)。
この省略時の設定値により,アプリケーションでは大量の
mmap
空間を割り当てることができます。
-taso
オプションを指定し,同時に
-T
と
-D
を使用してテキスト・セグメントとデータ・セグメントのアドレス値を指定した場合,-taso
の省略時のアドレス設定は指定した値に置き換えられます。
31 ビットのアドレス空間内でプログラムの作成に成功したかどうかを確認する場合は,odump
ユーティリティを使用することができます。
テキスト,データ,および bss セグメントの開始アドレスを表示する場合は,次のコマンドを入力します。
% odump -ov obj_file_x.o
どのアドレスの 31 〜 63 ビットは設定されず,0 〜 30 ビットだけが設定されていなければなりません。
-taso
を使用して作成した共用オブジェクトは,-taso
を使用しないで作成した共用オブジェクトとリンクすることはできません。
taso 共用オブジェクトを nontaso 共用オブジェクトとリンクしようとすると,次のエラー・メッセージが表示されます。
Cannot mix 32 and 64 bit shared objects without -taso option
-taso
オプションは,プログラムが 31 ビットの制限を超えたアドレスをマップすることを妨げず,これが行われても警告メッセージを通知しません。
このようなアドレスは,次のいずれかのメカニズムを使用して作成することができます。
-T および -D オプション
前述したように,-T
オプションと
-D
オプションを
-taso
オプションとともに使用した場合,-taso
オプションの省略時の設定値が指定された値に置き換えられます。
したがって,-taso
オプションの目的を無効にしないために,-T
オプションと
-D
オプションに指定するアドレスを 31 ビットのアドレス範囲内から選択する必要があります。
malloc() 関数
taso アプリケーションで
malloc
を使用する場合のアドレッシングの問題を回避するには,libc
の
__taso_mode
変数 (グローバルな読み取り専用 "unsigned long" 変数) を置き換える変数をアプリケーション内で宣言して,静的に値 1 で初期化します。
この値は,malloc
ルーチンに対して taso アプリケーションで動作していることを通知し,malloc
は割り当てられたメモリが 31 ビットの範囲内にあることをチェックして保証します。
mmap システム・コール
mmap
システム・コールを使用する taso アプリケーションでは,mmap
にジャケット・ルーチンを使用し,アドレスのマップによって 31 ビットの範囲を超えることがないようにする必要があります。
この場合,次の手順に従う必要があります。
mmap
によって 31 ビットのアドレス範囲を超える空間が割り当てられないようにするには,すべてのモジュール (または最低でも
mmap
を参照するすべてのモジュール) に対して,cc
コマンド行で次のコンパイル・オプションを指定します。
-Dmmap=_mmap_32_
このオプションでは,mmap
関数の名前がジャケット・ルーチンの名前 (_mmap_32_
) に等しいことを定義しています。
その結果,ソース・プログラム内で
mmap
関数が参照されると,このジャケット・ルーチンが起動されます。
mmap
関数が 1 つのソース・モジュールだけで呼び出される場合は,ジャケット・ルーチンをそのモジュールに取り込むか,mmap_32.o
オブジェクト・モジュールを作成して
cc
コマンド行に指定します。
ジャケット・ルーチンのファイル指定は
/usr/opt/alt/usr/lib/support/mmap_32.c
であり,ファイルはオプションのソフトウェア・サブセット
CMPDEVENH
nnn
にあります。
mmap
関数が複数のソース・ファイルから呼び出される場合は,mmap_32.o
オブジェクト・モジュールを作成して
cc
コマンド行に指定するメソッドを使用する必要があります。
これは,ジャケット・ルーチンを複数のモジュールに入れた場合,リンカ・エラーが発生するためです。
call-shared の taso アプリケーション (図 A-1
を参照) に対するメモリ領域をローダがセットアップする方法に起因して,特別の手順を実行しなければ,malloc()
関数で 256 M バイトを超えるメモリを割り当てることはできません。
この問題を解決するために,割り当てのための領域を増やす必要があります。 これは,次の方法で行います。
-non_shared
でのリンク
taso アプリケーションで大きいメモリを割り当てるための,最も効果的な方法は,シェアード・ライブラリなしでアプリケーションをリンクする方法です。
-non_shared
でリンクすることができれば,malloc()
は bss の末尾から 0x7fff ffff までのメモリを割り当てることができます。
このとき,プログラムが
mmap
を用いてその範囲内のメモリを割り当てないということを前提とします。mmap
による割り当てで生じる競合の問題については,このリストの次の項で説明します。
malloc
関数の
__sbrk_override
チューニング変数の使用
malloc
ルーチン (malloc
,calloc
,realloc
,valloc
) は通常,sbrk
システム・コールによってメモリを割り当てます。
ただし,sbrk
が シェアード・ライブラリのテキスト・セクションに到達するか,または
mmap
によって割り当てられた領域に到達すると,sbrk
による割り当ては失敗します。
__sbrk_override
チューニング変数を使用すると,割り当ては,メモリの 0x0001 0000 から 0x7fff ffff までの間で行われます。
この変数を設定すると,malloc
は
sbrk
の呼び出しの失敗を検出したときに,mmap
の使用へと切り替わり,mmap
は前に示したアドレス範囲の中に十分な領域を見つけると,割り当て要求を満たします。
A.3 -xtaso または -xtaso_short オプションの使用
-xtaso
または
-xtaso_short
オプションを使用すると,プログラム内で short (32 ビット) と long (64 ビット) の両方のポインタ型を使用することができます。
ただし,-xtaso
と
-xtaso_short
のどちらにも
-taso
オプションが関係するため,64 ビットのデータ型が 31 ビットのアドレッシングに制限されることに注意してください。
-xtaso
または
-xtaso_short
オプションは,プログラム内で short ポインタが必要になる場合にだけ使用します。
32 ビットのアドレッシングを使用し,short ポインタが必要でない場合は,-taso
オプションを使用します。
Tru64 UNIX は 64 ビット・システムであり,オペレーティング・システムあるいはシステム提供のライブラリでポインタ・データを交換する場合には 64 ビット・ポインタを使用しなければならないため,short ポインタを使用するほとんどのプログラムでは,long ポインタも使用する必要があります。
通常のアプリケーションは,標準のシステム・データ型を使用するので,ポインタの変換は必要ありません。
short ポインタを使用するアプリケーションでは,pointer_size
プラグマを使用して short ポインタを long ポインタに明示的に変換しなければならない場合があります (3.12 節を参照)。
注意
新規アプリケーションで short ポインタの使用を検討している場合は,最初に long ポインタで開発しておき,後に short ポインタの方が有用かどうかを分析して判断してください。
A.3.1 ポインタ・サイズの変更に関するコーディング上の注意事項
ソース・コードでのポインタの扱いに関連するコーディング上の注意事項を次に示します。
定義の一部としてポインタを含む
typedef
で使用されるポインタのサイズは,typedef
を使用するときではなく宣言するときに決定されます。
このため,typedef
定義の一部として short ポインタが宣言されている場合,その
typedef
を使用して宣言されている変数については,long ポインタが宣言されたコンテキストでコンパイルされたとしても,すべて short ポインタが使用されます。
マクロ内のポインタのサイズは,マクロが展開されるコンテキストによって決定されます。
typedef
を使用して必要なポインタ・サイズを宣言する以外に,マクロの一部としてポインタ・サイズを指定する方法はありません。
一般的に,short および long 単純ポインタ間での変換は安全であり,代入でのキャストあるいは関数呼び出しを行う必要はなく,暗黙に行われます。 一方,複合ポインタでは通常,short および long のポインタ表現間での変換を正しく行うためにソース・コードの変換が必要です。
たとえば,引数ベクトル
argv
は複合 long ポインタであり,そのように宣言する必要があります。
多くの X11 ライブラリ関数は,複合 long ポインタを返します。
これらの関数の戻り値を正しく宣言しないと,誤動作が発生します。
関数が short ポインタだけを使用するようにコンパイルされており,そのようなベクトルにアクセスする必要がある場合には,それを関数に渡す前に,long ポインタ・ベクトルから short ポインタ・ベクトルに値をコピーするコードを追加する必要があります。
short ポインタの使用をサポートしているのは C および C++ コンパイラだけです。 C および C++ ルーチンから他の言語で作成されたルーチンへ short ポインタを渡してはなりません。
pointer_size
プラグマおよび
-xtaso_short
オプションは,main()
の 2 番目の引数 (通常
argv
と呼ばれる) のサイズには影響を与えません。
この引数は,pointer_size
プラグマが他のポインタ・サイズを 4 バイトに設定するために使用されている場合であっても,常に 8 バイトのサイズとなります。
Tru64 UNIX システム上のほとんどのアプリケーションは,32 ビットで表現できないアドレスを使用しています。
したがって,通常のアプリケーションによって呼び出される可能性のあるライブラリに,short ポインタを含めることはできません。
ソフトウェア・ライブラリを作成するベンダは,short ポインタを使用してはなりません。
A.3.3 システム・ヘッダ・ファイルに関する問題の回避
システム・ライブラリを作成する場合,コンパイラは,ポインタが 64 ビットであり,構造体メンバの自然境界合わせが行われていることを想定しています。
これは,C および C++ コンパイラの省略時の設定です。
システム・ライブラリに対するインタフェース (/usr/include
ツリーにあるヘッダ・ファイル) では,この想定を明示的にコード化していません。
ポインタ・サイズ (-xtaso_short
) および構造体メンバの境界合わせ (-Zp
n
[この場合
n!=8] または
-nomember_alignment
) に関するコンパイラの想定を変更することができます。
これらのオプションのいずれかを使用し,アプリケーションで
/usr/include
ツリーからヘッダ・ファイルをインクルードして,ライブラリ関数を呼び出したり,そのヘッダ・ファイルで宣言されている型を使用している場合には,問題が発生する可能性があります。
特に,システム・ヘッダ・ファイル宣言を処理する場合に,コンパイラで計算されたデータ・レイアウトは,コンパイルされてシステム・ライブラリに格納されたレイアウトと異なる可能性があります。
このような状態では,予測できない結果が発生する可能性があります。
この項で説明して状況下での,ポインタ・サイズおよびデータの境界合わせで問題が発生する可能性を取り除くため,システムにコンパイラをインストールした直後にスクリプト
protect_headers_setup.sh
を実行してください。
これを実行する理由と方法についての詳細は,
protect_headers_setup
(8)
コンパイル・コマンド行で
-protect_headers
オプションのバリエーションを使用することにより,protect_headers_setup
スクリプトで設定したプロテクションを有効にしたり,無効にすることができます。
-protect_headers
オプションの詳細については,
cc
(1)