2    コンパイラ・システム

この章では,次の項目について説明します。

コンパイラ・システムはソース・コードを実行可能プログラムに変換します。 これは,次のステップから構成されます。

これらのステップは,前処理,コンパイル,リンクの別々のコマンドによって実行することも,単一のオペレーションで,コンパイル中にコンパイラ・システムが適切なタイミングで各ツールを呼び出すことによって実行することもできます。

コンパイラ・システムには,この他にも,コンパイルしてリンクされたプログラムのデバッグや,作成されたオブジェクト・ファイルのチェック,ルーチンのライブラリの作成,プログラムの実行時性能の分析などを行うツールがあります。

表 2-1 に,コンパイラ・システムのツールと,本書および他のマニュアルの参照箇所を示します。

表 2-1:  コンパイラ・システムの機能

目的 使用ツール 参照箇所およびマニュアル
プログラムのコンパイル,リンク,ロードおよびシェアード・ライブラリの作成 コンパイラ・ドライバ,リンク・エディタ,動的ローダ この章,第 4 章cc(1), c89(1), as(1), ld(1), loader(5), 『Assembly Language Programmer's Guide』, 『Compaq C 言語リファレンス・マニュアル
プログラムのデバッグ シンボリック・デバッガ (dbx および ladebug) および Third Degree 第 5 章第 6 章第 7 章dbx(1)third(1)ladebug(1), 『Ladebug Debugger Manual
プログラムのプロファイル プロファイラ,コール・グラフ・プロファイラ 第 8 章hiprof(1)pixie(1)gprof(1)prof(1)atom(1)
プログラムの最適化 最適化プログラム,ポストリンク最適化プログラム この章,第 10 章cc(1)第 7 章third(1)
オブジェクト・ファイルの検査 nm, file, size, dis, odump, stdump のツール この章, nm(1), file(1), size(1), dis(1), odump(1), stdump(1), 『プログラミング・サポートツール・ガイド
必要なライブラリの作成 アーカイバ (ar),リンカ(ld) のコマンド この章,第 4 章ar(1), ld(1)

2.1    コンパイラ・システムの構成要素

図 2-1 に,コンパイラ・システムの主な構成要素とそれらの入力と出力との関係を示します。

図 2-1:  プログラムのコンパイル

コンパイラ・システムのコマンドは,ドライバ・プログラムとも呼ばれ,コンパイラ・システムの構成要素を起動します。 各言語にはそれぞれのコンパイラ・コマンドおよびオプションが用意されています。

C コンパイラは cc コマンドで起動します。 Tru64 UNIX のプログラミング環境では,1 つの cc コンパイラ・コマンドで,次のような複数のアクションを実行することができます。

表 2-2:  ファイルの接尾語と対応ファイル

接尾語 ファイル
.a アーカイブ・ライブラリ
.c C ソース・コード
.i この接尾語の付くファイルは C プリプロセッサによって処理された,ドライバ用のソース・コードであるとみなされる。 たとえば %  cc  -c  source.i のように実行する。 ファイル source.i は,C ソース・コードを含んでいるとみなされる。
.o オブジェクト・ファイル
.s アセンブリ・ソース・コード
.so 共用オブジェクト (シェアード・ライブラリ)

2.2    Tru64 UNIX 環境におけるデータ型

以降の各項で,Tru64 UNIX システムでデータ項目を表現する方法について説明します。

注意

Tru64 UNIX システムでの省略時のメモリ・アクセス・サイズは 8 バイト (クォドワード) です。 したがって,2 つ以上の実行スレッドが同時に隣接するメモリを変更する場合,そのメモリ・アドレスをクォドワードに整列させて,個々の変更で誤って上書きされないようにしなければなりません。 たとえば,コンポジット・データ構造の同じクォドワードに含まれる別々のデータ項目が同時に変更されるような場合にエラーが生じることがあります。

クォドワード以外での整列や,その他の問題が生じるさまざまな状況については,『Guide to the POSIX Threads Library』 の「Granularity Considerations」を参照してください。

2.2.1    データ型のサイズ

Tru64 UNIX システムは下位バイト処理型であり,右から左の順にバイトを使用します。 C コンパイラでは,下位バイト処理型のバイト順のみをサポートしています。 サポートするデータ型のサイズは次のとおりです。

データ型 ビットの大きさ
char 8
short 16
int 32
long 64
long long 64
float 32 (IEEE 単精度)
double 64 (IEEE 倍精度)
pointer 64 [脚注 1]
long double 128

2.2.2    浮動小数点の範囲と処理

C コンパイラは,『IEEE Standard for Binary Floating-Point Arithmetic』(ANSI/IEEE Std 754-1985) に定義されているように,IEEE 単精度 (32 ビット float),倍精度 (64 ビット double),拡張倍精度 (128 ビット long double) 浮動小数点データをサポートしています。

浮動少数点数は次の範囲になります。

Tru64 UNIX では,規格に定義されている基本的な浮動小数点数フォーマット,演算 (加算,減算,乗算,除算,平方根,剰余,比較),および変換を提供しています。 コンパイラ・オプションを指定することにより,完全に IEEE に準拠したトラップ動作 (NaN [番号でない] を含む) を使用することができます。 また,IEEE 形式のトラップが必要ない場合には,速いモードを指定することができます。 コンパイル時に丸めモードを選択して,IEEE 演算の結果に適用することもできます。 IEEE 浮動少数点処理をサポートするオプションについての詳細は, cc(1) を参照してください。

ユーザ・プログラムでは,ieee_set_fp_control() を呼び出して,浮動小数点トラップのスレッドへの引き渡しを制御したり,write_rnd() を呼び出して,IEEE 丸めモードを動的に設定することができます。 IEEE 浮動少数点の例外を処理する方法についての詳細は, ieee(3) を参照してください。

2.2.3    構造体の位置合わせ

C コンパイラは,省略時の設定では,構造体のメンバを自然境界に位置合わせします。 つまり,構造体の構成要素は,宣言された順にメモリに配置されます。 最初の構成要素は,構造体全体と同じアドレスを持ちます。 それに続く各構成要素は,前の構成要素に続いて,その構成要素の型の次の自然境界に合わせられます。

たとえば,次の構造体は図 2-2 に示すように位置合わせが行なわれます。

struct {char c1;
        short s1;
        float f;
        char c2;
       }

図 2-2:  省略時の構造体の位置合わせ

構造体の最初の構成要素 c1 は,オフセット 0 から始まって,その最初のバイトを占めています。 2 番目の構成要素の s1 は,short であるため,ワード境界から始まる必要があります。 したがって,c1s1 の間に埋め込みを行います。 fc2 を自然境界に合わせるには,埋め込みは必要ありません。 ただし,サイズが f の位置合わせの倍数に切り上げられるため,c2 の後ろに 3 バイトの埋め込みが行われます。

構造体メンバの省略時の位置合わせの変更には,次のような方法を使用することができます。

これらの指示文については,3.8 節および3.11 節を参照してください。

2.2.4    ビット・フィールドの位置合わせ

一般に,ビット・フィールドの位置合わせは,その前のフィールドのビット・サイズとビット・オフセットによって決定されます。 たとえば,次の構造体は,図 2-3に示すような位置合わせが行われます。

struct a {
    char  f0:   1;
    short f1:  12;
    char  f2:   3;
} struct_a;

図 2-3:  省略時のビット・フィールドの位置合わせ

最初のビット・フィールド f0 は,ビット・オフセット 0 から始まり 1 ビットを占めています。 2 番目の f1 は,オフセット 1 から始まり 12 ビットを占めています。 3 番目の f2 は,オフセット 13 から始まり 3 ビットを占めています。 構造体のサイズは 2 バイトです。

次のような場合には,ビット・フィールドの位置合わせの前に埋め込みが行われます。

図 2-4:  次のパック境界までの埋め込み

2.2.5    _ _align 記憶クラス修飾子

データ位置合わせはデータ型によって暗黙に定義されます。 たとえば,C コンパイラは int (32 ビット) を 4 バイト境界に位置合わせし,long (64 ビット) を 8 バイト境界に位置合わせします。 _ _align 記憶クラス修飾子は,任意の C データ型のオブジェクトを指定された記憶境界に位置合わせします。 これは,データ宣言または定義で使用できます。

_ _align 修飾子は次の形式で使用します。

_ _align (keyword) _ _align (n)

keyword には定義済み位置合わせ定数を,n には 2 の累乗の整数を指定します。 定義済み定数あるいは 2 の累乗は,コンパイラに対してデータの位置合わせのためのバイト数を指定します。

たとえは,整数を次のクォドワード境界に合わせる場合は,次のいずれかの宣言を使用します。

   int _ _align( QUADWORD ) data;
   int _ _align( quadword ) data;
   int _ _align( 3 ) data;
 

この例では,int _ _align ( 3 ) は 2x2x2 バイト (8 バイト),つまりメモリの 1 クォドワードの位置合わせを指定します。

定義済み境界合わせ定数とそれに対応する2 の累乗およびバイト数は次のとおりです。

定数 2 の累乗 バイト数
BYTE あるいは byte 0 1
WORD あるいは word 1 2
LONGWORD あるいは longword 2 4
QUADWORD あるいは quadword 3 8

2.3    C プリプロセッサ

C プリプロセッサは,ソース・ファイルをコンパイルする前に,マクロの展開,ヘッダ・ファイルの取り込み,プリプロセッサ指示文の実行を行います。 以降の各項で,C プリプロセッサが実行する Tru64 UNIX に固有の処理について説明します。 C プリプロセッサについての詳細は,リファレンス・ページの cc(1) および cpp(1),および『Compaq C 言語リファレンス・マニュアル』を参照してください。

2.3.1    定義済みマクロ

コンパイラが起動されると,入力ファイルの言語とそのコードの実行環境を識別する C プリプロセッサ・マクロが定義されます。 プリプロセッサ・マクロの一覧については, cc(1) を参照してください。 #ifdef 文でこれらのマクロを参照すると,特定の言語または環境に適用するコードを分離することができます。 Tru64 UNIX を一意に識別するには,次の文を使用します。

#if defined (_ _digital_ _) && defined (_ _unix_ _)

ソース・ファイルのタイプと適用する規格のタイプによって,定義されるマクロが決定されます。 C コンパイラはいくつかのレベルの規格をサポートしています。

cpp コマンドは,標準の C マクロ,すなわち _ _LINE_ __ _FILE_ __ _DATE_ __ _TIME_ _,(該当する場合は _ _STDC_ _) 以外は事前に定義しないので注意してください。

2.3.2    ヘッダ・ファイル

ヘッダ・ファイルは,通常,次の目的で使用されます。

C ヘッダ・ファイル (インクルード・ファイルとも呼ばれる) には,.h 接尾語が付きます。 必要なヘッダ・ファイルは,通常,ライブラリ・ルーチンまたはシステム・コールのリファレンス・ページに示されています。 ヘッダ・ファイルは,さまざまな言語で記述されたプログラムで使用することができます。

注意

dbx または ladebug を使用してプログラムをデバッグする場合は,ヘッダ・ファイルに実行可能コードを入れてはなりません。 デバッガがヘッダ・ファイルをソース・コードの 1 行として解釈し,デバッグ・セッション時にファイルのソース行が,まったく表示されなくなるためです。 dbx デバッガについての詳細は,第 5 章を参照してください。 ladebug についての詳細は,『Ladebug Debugger Manual』を参照してください。

ヘッダ・ファイルは,次の 2 つの方法のうちのいずれかでプログラム・ソース・ファイルに含めることができます。

#include "filename"

これは,C マクロ・プリプロセッサが,その指示文を含むファイルを見つけたディレクトリ,-I オプションで指定された探索パス,/usr/include の順でインクルード・ファイル filename を探索することを示します。

#include <filename>

これは,C マクロ・プリプロセッサが,その指示文を含むファイルを見つけたディレクトリではなく,-I オプションで指定された探索パスと /usr/include でインクルード・ファイル filename の探索を行うことを示します。

また,-Idir および -nocurrent_include オプションを使用して,C プリプロセッサに #include ファイルを探索させる追加のパス名 (ディレクトリ) を指定することもできます。

2.3.3    各国語対応インクルード・ファイルの設定

C,Fortran,およびアセンブリ・コードは,同じインクルード・ファイルに常駐させることができ,必要に応じてプログラムの中に条件付きで挿入することができます。 共用可能なインクルード・ファイルを設定するには,.h ファイルを作成し,次の例のように,それぞれのコードを入力しなければなりません。

   #ifdef _ _LANGUAGE_C_ _
    .
    .    (C code)
    .
   #endif
   #ifdef _ _LANGUAGE_ASSEMBLY_ _
    .
    .    (assembly code)
    .
   #endif
 
 

コンパイラがこのファイルを C ソース・ファイルに取り込むと,_ _LANGUAGE_C_ _ マクロが定義されて,C コードがコンパイルされます。 コンパイラがこのファイルをアセンブリ言語ソース・ファイルに取り込むと,_ _LANGUAGE_ASSEMBLY_ _ マクロが定義されて,アセンブリ言語コードがコンパイルされます。

2.3.4    処理系固有のプリプロセッサ指示文 (#pragma)

#pragma 指示文は,コンパイラごとに異なる機能をインプリメントする標準的な方法です。 C コンパイラは,次の処理系固有のプラグマをサポートします。

これらのプラグマについての詳細は,第 3 章で説明します。

2.4    ソース・プログラムのコンパイル

cc コマンドで設定されたコンパイル環境は,共通オブジェクト・ファイル・フォーマット (COFF) に準拠しているオブジェクト・ファイルを作成します。

cc コマンドでサポートされているオプションは,デバッグ,最適化,プロファイル機能など,さまざまなプログラム開発関数と出力ファイルへ割り当てる名前を選択します。 cc コマンド行オプションについての詳細は, cc(1) を参照してください。

以降の各項で,省略時のコンパイラの動作および各国語対応プログラムのコンパイル方法について説明します。

2.4.1    省略時のコンパイル動作

ほとんどのコンパイラ・オプションには,そのオプションがコマンド行で指定されない場合に使用される省略時の値があります。 たとえば,出力ファイルの省略時の名前は,オブジェクト・ファイルに対しては filename.o になります。 このとき,filename は,ソース・ファイルのベース名です。 実行可能プログラムのオブジェクトの省略時の名前は,a.out になります。 次の例では,prog1.c および prog2.c のソース・ファイルをコンパイルする際に,省略時の設定が使用されます。

% cc prog1.c prog2.c

このコマンドによって C コンパイラが実行され,prog1.oprog2.o というオブジェクト・ファイルと,a.out という実行可能プログラムが作成されます。

cc コンパイラ・コマンドに他のオプションを指定しないで入力した場合には,次のオプションが有効になります。

-noansi_alias

ANSI C 別名化規則をオフにします。 これは,最適化プログラムが最適化においてアグレッシブにならないようにします。

-arch generic

すべての Alpha プロセッサに適切な命令を生成します。

-assume aligned_objects

コンパイラがそのような仮定をして,位置合わせされたポインタ型のポインタ逆参照のためにより効率的なコードを生成できるようにします。

-assume math_errno

コンパイラは,errno を設定できる算術ライブラリ・ルーチンを呼び出した後,プログラムが errno を問い合わせる可能性があることを想定できるようにします。

-call_shared

実行時に共用可能オブジェクトを使用する動的な実行可能ファイルを作成します。

-nocheck_bounds

配列境界の実行時検査を無効にします。

-cpp

コンパイル前に C およびアセンブリ・ソース・ファイルで,C マクロ・プリプロセッサが呼び出されるようにします。

-error_limit 30

指定のコンパイルに対してコンパイラが出力するエラー・レベルの診断数を 30 までに制限します。

-float

型表現を float から double に昇格する必要がないことをコンパイラに通知します。

-nofp_reorder

精度に影響するような浮動小数点演算の順序変更をしないようにコンパイラに指示します。

-fprm n

浮動少数点数の通常の丸め (不偏の丸めから近似値へ) を行います。

-fptm n

浮動小数点アンダフローまたは不正確なトラッピング・モードを生成しない命令を生成します。

-g0

シンボリック・デバッグのためのシンボル情報を生成しません。

-I/usr/include

ディレクトリ /usr/include において,ファイル名がスラッシュ (/) で始まらない #include ファイルが最初に探索されることを指定します。

-inline manual

#pragma  inline 指示文によって明示的にインライン化が要求されている関数呼び出しのみをインライン化します。

-intrinsics

特定の関数を intrinsics として認識して,適切な最適化を行うようにコンパイラに指示します。

-member_alignment

コンパイラに指示して,データ構造体のメンバを (ビット・フィールドのメンバを除き) 自然境界に位置合わせするようにします。

-nomisalign

任意に位置合わせされたアドレスの位置合わせフォールトを生成します。

-nestlevel=50

インクルード・ファイルのネスト・レベル制限を 50 に設定します。

-newc

省略時のオプション設定をすべてリストしてコンパイラを起動します。 このオプションは,-migrate をオフにするためだけに用意されています。

-O1

グローバルな最適化を可能にします。

-p0

プロファイリングを無効に設定します。

-nopg

gprof プロファイリングをオフにします。

-preempt_module

モジュールごとにシンボルの優先使用を可能にします。

-SD/usr/include

パス名が /usr/include で始まるヘッダ・ファイル内の移植性のない構造に対するメッセージを抑制します。

-signed

char 型の表現を signed  char 型と同じにします。

-std

ANSI C 規格を拡張しますが,規格によって禁止されている一般的なプログラミング手法を許可します。

-tune generic

Alpha アーキテクチャのすべての処理系に適切な命令のチューニングを選択します。

-writable_strings

文字列リテラルを書き込み可能にします。

表 2-3 に, cc(1) のオプション・カテゴリごとの省略時オプションを示します。

表 2-3:  cc コマンドのオプション・カテゴリごとの省略時オプション

オプション・カテゴリ 省略時オプション
コンパイラ選択 -newc
言語モード -std
コンパイラ全体の動作 -arch generic, -error_limit 30, -nestlevel=50
コンパイラの診断制御 -SD/usr/include
C プリプロセッサ -cpp, -I/usr/include
リンカまたはローダ -call_shared
最適化 -noansi_alias, -assume math_errno, -float, -nofp_reorder, -inline manual, -intrinsics, -O1, -preempt_symbol, -tune generic
フィードバック主導の最適化 なし
ソース・コードのデバッグ -g0
プログラムのプロファイリング -p0, -nopg
データの整列 -assume aligned_objects, -member_alignment, -nomisalign
データの揮発性 -weak_volatile
C 言語 -signed, -writable_strings
スタックの処理とポインタの処理 なし
IEEE の浮動小数点サポート -fprm n, -fptm n
コンパイラの開発 なし

次に示すのは,cc コンパイラのその他の省略時の動作です。

2.4.2    多言語プログラムのコンパイル

メイン・プログラムのソース言語がサブプログラムのソース言語と異なっている場合は,適切なドライバで各プログラムを個別にコンパイルし,別のステップでリンクしてください。 -c オプションを指定すると,リンクに適したオブジェクトを作成することができます。 -c オプションは,オブジェクト・ファイルが作成された直後にドライバを停止します。 たとえば,次のように入力してください。

% cc -c main.c

このコマンドは,実行可能ファイル a.out ではなく,オブジェクト・ファイル main.o を作成します。

C 以外の言語で作成したソース・ファイルのオブジェクト・モジュールを作成したのち,cc コマンドを使用して C ソース・ファイルをコンパイルし,すべてのオブジェクト・モジュールをリンクして 1 つの実行可能ファイルにすることができます。 たとえば,次の cc コマンドは c-prog.c をコンパイルし,c-prog.o および nonc-prog.o をリンクして a.out という実行可能ファイルを作成します。

% cc nonc-prog.o c-prog.c

2.4.3    配列境界の実行時検査の有効化

cc コマンドで -check_bounds オプションを指定すると,実行時コードを生成して,配列境界検査が行われます。 -nocheck_bounds オプション (省略時の設定) は,配列境界の実行時検査を無効にします。

コンパイラに対して実行時検査を指示するコード,および指定された検査で使用される正確な境界値は,コンパイラのインプリメンテーション上の特性に影響されるため,ユーザには明白ではありません。 これらを左右する厳密な条件は,次のとおりです。 以下の説明は,配列を含めて C の言語規則を十分に理解していることを前提としています。

次の例は,これまで示した規則を例証するものです。

int a[10];
int *b;
int c;
int *d;
int one[1];
int vla[c];          // C9X variable-length array
 
a[c] = 1;            // check c is 0-9, array subscript
c[a] = 1;            // check c is 0-9, array subscript
b[c] = 1;            // no check, b is a pointer
d = a + c;           // check c is 0-10, computing address
d = b + c;           // no check, b is a pointer
b = &a[c]            // check c is 0-10, computing address
*(a + c) = 1;        // check c is 0-10, computing address 
*(a - c) = 1;        // check c is -10 to 0, computing address
 
a[1] = 1;            // no run-time check - know access is valid
vla[1] = 1;          // run-time check, vla has run-time bounds
a[10] = 1;           // run-time check  (and compiler diagnostic)
d = a + 10;          // no run-time check, computing address
                     // SUBSCRBOUNDS2 message can be enabled
 
c = one[5];          // no run-time check, array of one element
                     // SUBSCRBOUNDS1 message can be enabled
 
 

境界外のアクセスに遭遇すると,出力は次のようになります。

Trace/BPT trap (core dumped)

プログラムは,次のコードを使ってこのエラーをトラップできます。

signal(SIGTRAP, handler);
 
 

実行時検査が有効な場合は,ポインタ算術を使って配列への正当なアクセスが行われた場合に,間違った検査が行われることがあります。

コンパイラは,配列名からポインタへの変換により得られたポインタへの最初の算術操作に対する検査コードだけを出力できます。 このため,得られたポインタ値に対して再びポインタ算術による操作が行われると,検査が不正確になる可能性があります。 a = b + c - d という式があり,a がポインタ,b が配列,cd が整数の場合を考えてみます。 境界検査が有効な場合,c が配列の境界内にあるかどうかが検査されます。 このとき,c が配列の境界外にあるが,c - d は境界内にある場合,間違った実行時トラップとなります。

この場合には,ポインタ式をコーディングし直して,整数部分をカッコ内に含めることができます。 これで,式には 1 つのポインタ算術演算子だけが含まれることになり,正確な検査が行われます。 前の例では,式は次のように変更されます。

a = b + (c - d);

2.5    オブジェクト・ファイルのリンク

cc ドライバ・コマンドは,オブジェクト・ファイルをリンクして,実行プログラムを作成します。 場合によっては,ld リンカを直接使用することもあります。 アプリケーションの特性に応じて,コンパイルしてから別にリンクを行うか,またはコンパイラ・コマンドを使用して,コンパイルとリンクを一度に実行するかを決定してください。 考慮すべき点は数多くありますが,特に次のことを考慮にいれてください。

2.5.1    コンパイラ・コマンドによるリンク

リンカ・コマンドの代わりにコンパイラ・コマンドを使用して,異なるオブジェクトを 1 つの実行可能プログラムにリンクすることができます。 アセンブラを除くコンパイラは,.o 接尾語を認識するとすぐにリンカを実行します。 この接尾語は,そのファイルにリンクが可能なオブジェクト・コードを含まれていることを示します。

コンパイラのドライバ・プログラムは言語に対応するライブラリを自動的にリンカに渡すため,通常はコンパイラ・コマンドを使用してください。 たとえば,cc ドライバでは,省略時の設定として C ライブラリ (libc.so) を使用します。 各コンパイラ・コマンドが使用する省略時のライブラリについての詳細は, cc(1) などのリファレンス・ページの該当するコマンドを参照してください。

また,cc コマンドの -l オプションを使用して追加のライブラリを指定し,未解決の参照を探索することもできます。 次の例は,cc ドライバを使用して,-l オプションで 2 つのライブラリ名をリンカに渡す方法を示しています。

% cc -o all main.o more.o rest.o -lm -lexc

-lm オプションは算術ライブラリを指定し,-lexc オプションは例外ライブラリを指定します。

プログラムを最適化する場合には,1 つのコマンドを使用してモジュールのコンパイルとリンクを行ってください。 ほとんどのコンパイラには,特定のオプションを使用して,最適化のレベルを上げる機能があります。 たとえば,次のようなオプションを使用できます。

2.5.2    ld コマンドによるリンク

通常,ユーザがリンカを直接実行するのではなく,cc コマンドを使用して間接的に実行します。 アセンブラ・オブジェクトから単独で作成する必要のある実行可能プログラムは ld コマンドで作成しなければなりません。

リンカ (ld) は,コマンド行に指定された順番で 1 つまたは複数のオブジェクト・ファイルを結合して,1 つのプログラム・オブジェクト・ファイルを作成します。 リンカによって,再配置,外部シンボルの解決,および実行可能ファイルの作成に必要な,他のすべての処理が実行されます。 -o オプションで指定しない限り,実行可能プログラム・ファイルは,a.out という名前になります。 このプログラム・ファイルを実行することも,別のリンカ・オペレーションへの入力として使用することもできます。

as アセンブラはリンカを自動的に実行しないため,アセンブリ言語で記述されたプログラムをリンクするには,次のいずれかの処理を行います。

リンクのプロセスに影響を与えるオプションやライブラリについての詳細は,リファレンス・ページの ld(1) を参照してください。

2.5.3    ライブラリの指定

Tru64 UNIX システム上でコンパイルされたプログラムは,自動的に libc.so という C ライブラリとリンクされます。 libc.so にないルーチンまたはコンパイラ・コマンドに対応するアーカイブ・ライブラリのうちのいずれかを使用する場合は,プログラムをライブラリと明示的にリンクしなければなりません。 ライブラリと明示的にリンクしない場合には,プログラムは正常にリンクされません。

次のような状況では,ライブラリを明示的に指定する必要があります。

2.5.4    リンカ出力ファイルでのリンク・エラー問題の回避

さまざまな種類のリンク・エラーに対して,リンカはエラーを無視して出力ファイルを作成し,同名の同じ出力ファイルを上書きします。 ほとんどのアプリケーションでは,この動作による問題は生じません。 問題が生じる場合は,既存の出力ファイルが上書きされないようにすることができます。 この場合は,リンカに対して一時ファイルの出力を指定し,次にリンクがエラーなしで完了した時点で一時ファイルの名前を目的の出力ファイルに変更します。 call_shared 実行可能ファイルでの実行例を次に示します。

cc -o main.exe.X main.o
mv main.exe.X main.exe

次の例に示すように,シェアード・ライブラリを構築する際にも同じ手法を使用できます。 この場合は,-soname オプションを使用して,ライブラリの内部名を正しく設定する必要があります。

ld -shared -soname libfoo.so -o libfoo.so.X foo.o -lc
mv libfoo.so.X libfoo.so

2.6    プログラムの実行

現在の作業ディレクトリで実行可能プログラムを実行するには,ほとんどの場合,そのファイル名を入力します。 たとえば,現在のディレクトリにあるプログラム a.out を実行するには,次のように入力します。

% a.out

実行可能プログラムがパスのディレクトリ内に存在しない場合は,次のように,ファイル名の前にディレクトリ・パスを入力します。

% ./a.out

プログラムが起動されるとき,C プログラムの main 関数が,次のオプションのパラメータのいずれかを指定して定義されている場合には,main 関数はコマンド行から引数を受け取ることができます。

int main ( int argc, char *argv[ ], char *envp[ ] )[...]

argc パラメータは,プログラムを起動したコマンド行に指定されている引数の数です。 argv パラメータは,引数を含む文字列の配列です。 envp パラメータは,ユーザ名や制御ターミナルなどのプロセス情報を含む環境配列です。 (envp パラメータはコマンド行引数の引き渡しには何の関係もありません。 主に,exec および getenv 関数の呼び出し中に使用されます。 )

定義したパラメータにのみアクセスすることができます。 たとえば,次のプログラムでは,argc および argv パラメータを定義して,プログラムに渡されたパラメータの値をエコーします。

/*
 * Filename: echo-args.c
 * This program echoes command-line arguments.
*/
 
#include <stdio.h>
 
int main( int argc, char *argv[] )
{
int i;
 
printf( "program: %s\n", argv[0] ); /* argv[0] is program name */
 
for ( i=1; i < argc; i++ )
	printf( "argument %d: %s\n", i, argv[i] );
 
return(0);
}

このプログラムを次のコマンドでコンパイルすると,a.out というプログラム・ファイルが作成されます。

$ cc echo-args.c

ユーザが a.out を起動して,コマンド行引数を引き渡すと,プログラムによりそれらの引数がターミナル上にエコーされます。 たとえば,次のように入力します。

$ a.out Long Day\'s "Journey into Night"
	    program: a.out
	    argument 1: Long
	    argument 2: Day's
	    argument 3: Journey into Night

引数を a.out に引き渡す前に,シェルによってすべての引数が解析されます。 このため,単一引用符の前にはバックスラッシュを指定し,アルファベットの引数は空白またはタブで区切り,引数に空白やタブを含める場合は引用符で囲む必要があります。

2.7    オブジェクト・ファイルのツール

ソース・ファイルをコンパイルした後,次に示すツールを使用すると,オブジェクト・ファイルや実行可能ファイルをチェックすることができます。

次の項では,上記のツールについて詳しく説明します。 また,オブジェクト・ファイルやバイナリ・ファイル中でプリント可能な文字列を探索する string コマンドの使用についての情報は, strings(1) を参照してください。

2.7.1    ファイル内の選択した部分のダンプ (odump)

odump ツールは,ヘッダ・テーブルおよび他のオブジェクト・ファイルやアーカイブ・ファイル内の選択した部分を表示します。 たとえば,ファイル echo-args.o に対して odump を使用すると,次のような情報が表示されます。

% odump -at echo-args.o
 
 
			***ARCHIVE SYMBOL TABLE***
 
 
 
 
			***ARCHIVE HEADER***
	Member Name        Date       Uid     Gid     Mode      Size
 
 
 
			***SYMBOL TABLE INFORMATION***
[Index]	Name	Value	Sclass	Symtype	Ref
echo-args.o:
[0]	 main	0x0000000000000000	0x01	0x06	0xfffff
[1]	 printf	0x0000000000000000	0x06	0x06	0xfffff
[2]	 _fpdata	0x0000000000000000	0x06	0x01	0xfffff

詳細は, odump(1) を参照してください。

2.7.2    シンボル・テーブル情報の表示 (nm)

nm ツールは,オブジェクト・ファイルのシンボル・テーブル情報を表示します。 たとえば,nm では,実行可能ファイル a.out を作成するためのオブジェクト・ファイルについて,次のような情報が表示されます。

% nm
nm: Warning: - using a.out
 
Name                            Value        Type       Size
 
.bss                   | 0000005368709568 | B | 0000000000000000
.data                  | 0000005368709120 | D | 0000000000000000
.lit4                  | 0000005368709296 | G | 0000000000000000
.lit8                  | 0000005368709296 | G | 0000000000000000
.rconst                | 0000004831842144 | Q | 0000000000000000
.rdata                 | 0000005368709184 | R | 0000000000000000

.
.
.

Name 欄にはシンボルまたは外部名,Value 欄にはシンボルのアドレスまたはデバッグ情報,Type 欄にはシンボルのタイプを示す英文字,Size 欄にはシンボルのサイズ (ソース・ファイルがデバッグ・オプション -g などでコンパイルされている場合のみ正確) がそれぞれ表示されています。 シンボル・タイプの英文字は,次のことを示しています。

詳細は, nm(1) を参照してください。

2.7.3    ファイル・タイプの決定 (file)

file コマンドは,入力ファイルを読み取って各ファイルを検査し,タイプによって分類します。 ファイル・タイプは標準出力に書き込まれます。 file コマンドは,/etc/magic ファイルを使用して,マジック・ナンバを含むファイルを識別します。 マジック・ナンバは,ファイル・タイプを示す数字または文字列定数です。

次の例は,C ソース・ファイル,オブジェクト・ファイル,および実行可能ファイルを含むディレクトリに対して file を使用した場合の出力を示しています。

% file *.*
.:       directory
..:      directory
a.out:   COFF format alpha dynamically linked, demand paged executable
or object module not stripped - version 3.11-8
echo-args.c:    c program text
echo-args.o:    COFF format alpha executable or object module not
stripped - version 3.12-6
 
 

詳細は, file(1) を参照してください。

2.7.4    ファイルのセグメント・サイズの決定 (size)

size ツールは,指定されたオブジェクト・ファイルまたはアーカイブ・ファイルの text,data,および bss セグメントに関する情報を,8 進数,16 進数,または 10 進数で表示します。 たとえば,引数を指定しないで呼び出された場合,size コマンドは a.out に関する情報を返します。 次のように,コマンド行に,オブジェクト・ファイルまたは実行可能ファイルの名前を指定することもできます。

% size
text    data    bss    dec      hex
8192    8192    0      16384    4000
% size echo-args.o
text    data    bss    dec      hex
176     96      0      272      110

詳細は, size(1) を参照してください。

2.7.5    オブジェクト・ファイルの逆アセンブル (dis)

dis ツールは,オブジェクト・ファイルのモジュールを機械語に逆アセンブルします。 たとえば,a.out プログラムの逆アセンブルを行った場合,dis コマンドは,次のような出力を作成します。

% dis a.out

.
.
.
_ _start: 0x120001080: 23defff0 lda sp, -16(sp) 0x120001084: b7fe0008 stq zero, 8(sp) 0x120001088: c0200000 br t0, 0x12000108c 0x12000108c: a21e0010 ldl a0, 16(sp) 0x120001090: 223e0018 lda a1, 24(sp)
.
.
.

詳細については, dis(1) を参照してください。

2.8    標準 C ライブラリにおける ANSI 名前空間汚染のクリーンアップ

ANSI C 規格では,libc にリンクされるユーザのプログラムは,プログラム内における一定範囲のグローバル識別子を,libc 内のグローバル識別子と競合したり,強制排除の問題を起こさないで使用できることが保証されています。

ANSI C 規格はグローバル識別子のある範囲を予約して,libc が内部インプリメンテーションで使用できるようにしています。 これらは予約識別子と呼ばれ,ANSI の X3.159-1989 に定義されているように,次のものがあります。

ANSI 準拠のプログラムでは,ANSI ルーチン名と同じか,または前述の予約されている名前空間にあてはまるグローバル識別子は定義できません。 その他のグローバル識別子名はすべて,ユーザ・プログラムで使用できます。

歴史的な libc のインプリメンテーションには,多数の ANSI でなく,予約されていない識別子が含まれています。 これらはどちらも文書化されサポートも行われています。 これらのルーチンは,ANSI であっても ANSI でなくても,libc 内から他の libc ルーチンによって呼び出されることがよくあります。 ユーザのプログラムで,このような ANSI でなく,予約されていない項目を独自に定義している場合には,libc にある同名のルーチンを強制排除します。 このために,ユーザのプログラムが ANSI 準拠であっても,ANSI のものも ANSI でないものも,サポートされている libc ルーチンの動作が変わることがあります。 このような潜在的な競合は,ANSI 名前空間汚染と呼ばれます。

Tru64 UNIX の libc のインプリメンテーションには,文書化されサポートされている ANSI でなく,予約されていない多数のグローバル識別子が含まれています。 libc 内のこれらグローバル識別子を強制排除から保護し,ユーザの名前空間汚染を避けるため,これらの識別子の大多数の名前を,識別子名の前に 2 つの下線 (_ _) の付く予約されている名前空間に変更しています。 これらの項目への外部アクセスを維持するために,弱い識別子も,変更前の元の識別子名を使用して追加されています。 弱い識別子は,ファイル間のシンボリック・リンクのような働きをします。 弱い識別子が参照されると,代わりに強い識別子が使用されます。

libc と静的にリンクするユーザ・プログラムは,弱い識別子用の余分のシンボル・テーブル・エントリを持つことがあります。 これらの識別子は,それぞれ対応する予約識別子と同じアドレスを持ち,このアドレスもテーブルに含まれています。 たとえば,静的にリンクされたプログラムが libc から tzset() 関数を単に呼び出した場合,シンボル・テーブルにはこの呼び出しに対して,次の 2 つのエントリが含まれます。

# stdump -b a.out | grep tzset
18. (file 9) (4831850384) tzset Proc Text symref 23 (weakext)
39. (file 9) (4831850384) _ _tzset Proc Text symref 23

この例では,tzset が弱い識別子であり,_ _tzset が強い識別子です。 識別子 _ _tzset が,実際に作業を行うルーチンです。

共用としてリンクされたユーザ・プログラムでは,シンボル・テーブルへのこのような追加は見られません。 これは,弱い識別子と強い識別子のペアがシェアード・ライブラリに残っているためです。

libc から ANSI でなく,予約されていない識別子を参照している既存のユーザ・プログラムは,1 つの例外を除いて,この変更のためにリコンパイルする必要はありません。 つまり,libc におけるこれらの識別子の強制排除に依存していたプログラムは,予約されていない名前を使用することによってそれらを強制排除することはできなくなります。 この種の強制排除は ANSI 準拠ではないため,使用しないでください。 ただし,これらの識別子を強制排除する機能は,新しく予約された "_ _" 名を使用すると,まだ利用できます。

この変更は,動的および静的に libc を使用する場合にあてはまります。

デバッグ・プログラムを libc にリンクすると,弱いシンボルへの参照は,次のように強いシンボルへの参照として解決されます。

% dbx a.out
dbx version 3.11.4
 
Type 'help' for help.
 
main:   4   tzset
 
(dbx) stop in tzset
 
[2] stop in _ _tzset
 
(dbx)

弱いシンボル tzset が参照されている場合,デバッガは代わりに強いシンボル _ _tzset で応答します。 これは,強いシンボルが実際に作業を行っているためです。 dbx デバッガは,_ _tzset が直接参照された場合と同様に動作します。

2.9    インライン・アセンブリ・コード -- ASM

コンパイラは,一般に ASM と呼ばれるインライン・アセンブリ・コードをサポートしています。

組み込み関数と同じように,ASM は関数呼び出しの構文で実装されています。 ただし組み込み関数と異なり,ASM を使用するためには,3 種類の ASM のプロトタイプとプリプロセッサ指示文 #pragma intrinsic を含むヘッダ・ファイル <c_asm.h> をインクルードしなければなりません。

これらの関数のフォーマットは,次のとおりです。

_ _int64 asm(const char *, ...); /* for integer operations, like MULQ */ 
float fasm(const char *, ...); /* for single precision float instructions, like MULS */ 
double dasm(const char *, ...); /* for double precision float instructions, like MULT */ 
#pragma intrinsic (asm, fasm, dasm)

const char*

asmfasmdasm 関数への最初の引数には,インラインで生成される命令と,引数の解釈を記述するメタ言語が含まれています。

...

生成される命令に対するソースおよびデスティネーションの引数 (あれば) と,生成された命令で使用されるその他の値。

これらの値は,生成される命令からは,呼び出し基準の引数渡しについての通常の規約に従って利用可能になります (最初の整数引数はレジスタ R16 で使用できます)。

ASM を使用する際は,ヘッダ・ファイル <c_asm.h> 内の #pragma intrinsic 指示文は必須です。 これは,次のようなことをコンパイラに知らせます。

引数の参照に関するメタ言語の形式は,次のとおりです。

<metalanguage_sequence> : <register_alias> 
                         | <register_number> 
                         | <register_macro> 
                         ; 
 
 <register_number>       : "$" number 
                         ; 
 
 <register_macro>        : "%" <macro_sequence> 
                         ; 
 
 <macro_sequence>        : number 
                         | <register_name> 
                         | "f" number | "F" number 
                         | "r" number | "R" number 
                         ; 
 
 <register_name> :     /* argument registers: R16-R21 */ 
                      "a0" | "a1" | "a2" | "a3" | "a4" | "a5" 
 
                      /* return value: R0 or F0, depending on type */ 
                    | "v0"   
 
                      /* scratch registers: R1, R22-R24, R28 */ 
                    | "t0" | "t1" | "t2" | "t3" | "t4"  
 
                      /* save registers: R2-R15 */ 
                    | "s0" | "s1" | "s2" | "s3" | "s4"  | "s5"  | "s6"  | "s7" 
                    | "s8" | "s7" | "s8" | "s9" | "s10" | "s11" | "s12" | "s13" 
 
                      /* stack pointer: R30 */ 
                    | "sp" | "SP" | "$sp" | "$SP" 
 
                    | "RA" | "ra"           /* return addr:        R26  */ 
                    | "PV" | "pv"           /* procedure value:    R27  */ 
                    | "AI" | "ai"           /* arg info:           R25  */ 
                    | "FP" | "fp"           /* frame pointer:      R29  */ 
                    | "RZ" | "rz" | "zero"  /* sink/source: R31 == zero */
 

構文上,メタ言語は命令シーケンス内のどの位置にも配置できます。

命令,オペランド,メタ言語を含むリテラル文字列は,次の一般形式に従っていなければなりません。

<string_contents>       :  <instruction_seq>         
                         |  <string_contents> ";" <instruction_seq> 
                         |  error 
                         |  <string_contents> error 
                         ; 
 
 <instruction_seq>       :  instruction_operand 
                         |  directive 
                         ;

通常,instruction_operand は,アセンブリ言語命令と見なされます。 これは,コンマで区切られたオペランドのシーケンスとはホワイト・スペースで区切られます。

C 言語では,隣接した文字列リテラルを連結して単一の文字列にするため,連続した命令は,個々の命令がセミコロンで終わっていれば (例に示すように),1 行に 1 つづつ文字列として記述できます (通常のアセンブリ言語の記述方法と同じ)。

ASM には意味および構文上の規則があります。

たとえば,次のテストでは 6 個の引数が引数レジスタの a0a5 にロードされ,各部分式の結果はリターン値レジスタの v0 に格納されます。 v0 は呼び出し標準のリターン値レジスタ (整数関数では R0) なので,最後の MULQ の結果は「呼び出し」から返される値になります。

if (asm("mulq %a0, %a1,  %v0;" 
            "mulq %a2, %v0,  %v0;" 
            "mulq %a3, %v0,  %v0;" 
            "mulq %a4, %v0,  %v0;" 
            "mulq %a5, %v0,  %v0;", 1, 2, 3, 4, 5, 6) != 720){ 
      error_cnt++; 
      printf ("Test failed\n"); 
         }

次の例は正しく動作しません。 浮動小数点のリターン値レジスタには値がロードされません。 また,引数は r2 ではなく引数レジスタにロードされるため,コンパイル時に,r2 が設定される前に使用されているという警告が表示されます。

z =  fasm("mulq %r2, %a1 %r5", x=10, y=5);

正しく実行させるには,r2 の代わりに引数レジスタの番号を指定します。 上記の例を正しく記述すると,次のようになります。

z = fasm("mulq   %a0, %a1, %a1;" 
          "stq    %a1, 0(%a2);" 
          "ldt    %f0, 0(%a2);" 
          "cvtqf  %f0, %f0;",  x=10, y=5, &temp);

整数から浮動小数点レジスタへの変換に使用されるメモリ位置が,asm のコードで使用できる点に注意してください。 これは,この目的のために C コード内で割り当てられた変数のアドレスを引数として渡すことで行います。

asm("MB");