A    Tru64 UNIX システムにおける 32 ビット・ポインタの使用

Tru64 UNIX システムでは,省略時のポインタ型のサイズは 64 ビットで,すべてのシステム・インタフェースは 64 ビット・ポインタを使用します。 Compaq C コンパイラは,64 ビット・ポインタをサポートするだけでなく,32 ビット・ポインタの使用もサポートします。

ほとんどの場合,64 ビット・ポインタがプログラム内で問題を起こすことはなく,32 ビット・ポインタの問題はまったく無視しても構いません。 ただし,次の場合は 32 ビット・ポインタの問題について考慮する必要があります。

アプリケーションで 32 ビット・ポインタを使用する場合には,特殊なオプションを使用したアプリケーションのコンパイルおよびリンクが必要であり,コード固有の性質により,ソース・コードの変更も必要となる場合があります。

この付録では,次の種類のポインタについて説明します。

A.1    コンパイラ・システムと 32 ビット・ポインタの言語サポート

次のメカニズムにより,32 ビット・ポインタの使用が制御されます。

次は,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

A.2    -taso オプションの使用

-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 コマンド行で指定するオプションによって決まります。

ほとんどのアプリケーションの場合,これらの省略時の設定値を使用することによってテキスト・セグメントとデータ・セグメントに十分な空間が作成されます (テキスト・セグメントとデータ・セグメントの内容の詳細については,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

A.2.2    -taso オプションの効果に対する制限事項

-taso オプションは,プログラムが 31 ビットの制限を超えたアドレスをマップすることを妨げず,これが行われても警告メッセージを通知しません。 このようなアドレスは,次のいずれかのメカニズムを使用して作成することができます。

A.2.3    taso 環境での malloc の動作

call-shared の taso アプリケーション (図 A-1 を参照) に対するメモリ領域をローダがセットアップする方法に起因して,特別の手順を実行しなければ,malloc() 関数で 256 M バイトを超えるメモリを割り当てることはできません。

この問題を解決するために,割り当てのための領域を増やす必要があります。 これは,次の方法で行います。

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    ポインタ・サイズの変更に関するコーディング上の注意事項

ソース・コードでのポインタの扱いに関連するコーディング上の注意事項を次に示します。

A.3.2    32 ビット・ポインタの使用に関する制限事項

Tru64 UNIX システム上のほとんどのアプリケーションは,32 ビットで表現できないアドレスを使用しています。 したがって,通常のアプリケーションによって呼び出される可能性のあるライブラリに,short ポインタを含めることはできません。 ソフトウェア・ライブラリを作成するベンダは,short ポインタを使用してはなりません。

A.3.3    システム・ヘッダ・ファイルに関する問題の回避

システム・ライブラリを作成する場合,コンパイラは,ポインタが 64 ビットであり,構造体メンバの自然境界合わせが行われていることを想定しています。 これは,C および C++ コンパイラの省略時の設定です。 システム・ライブラリに対するインタフェース (/usr/include ツリーにあるヘッダ・ファイル) では,この想定を明示的にコード化していません。

ポインタ・サイズ (-xtaso_short) および構造体メンバの境界合わせ (-Zpn [この場合 n!=8] または -nomember_alignment) に関するコンパイラの想定を変更することができます。 これらのオプションのいずれかを使用し,アプリケーションで /usr/include ツリーからヘッダ・ファイルをインクルードして,ライブラリ関数を呼び出したり,そのヘッダ・ファイルで宣言されている型を使用している場合には,問題が発生する可能性があります。 特に,システム・ヘッダ・ファイル宣言を処理する場合に,コンパイラで計算されたデータ・レイアウトは,コンパイルされてシステム・ライブラリに格納されたレイアウトと異なる可能性があります。 このような状態では,予測できない結果が発生する可能性があります。

この項で説明して状況下での,ポインタ・サイズおよびデータの境界合わせで問題が発生する可能性を取り除くため,システムにコンパイラをインストールした直後にスクリプト protect_headers_setup.sh を実行してください。 これを実行する理由と方法についての詳細は, protect_headers_setup(8) を参照してください。

コンパイル・コマンド行で -protect_headers オプションのバリエーションを使用することにより,protect_headers_setup スクリプトで設定したプロテクションを有効にしたり,無効にすることができます。 -protect_headers オプションの詳細については, cc(1) を参照してください。