【PL/I学習|豆知識】メインフレーム間連携の落とし穴:DESCRIPTOR / NODESCRIPTORの正しい使い分け

導入:なぜ記述子の指定が重要なのか

メインフレーム開発において、PL/IやCOBOL、アセンブラ間でのサブルーチン呼び出しは日常茶飯事です。しかし、異なる言語間でデータをやり取りする際、最も多く発生するトラブルの一つが「引数の不整合」によるABENDです。特に、ポインタを引数として渡す際、コンパイラが自動的に付加する「記述子(Descriptor)」の扱いは、デバッグを困難にする隠れた要因となります。本稿では、この記述子を制御するNODESCRIPTOR指定の重要性について解説します。

基礎知識:記述子(Descriptor)とは何か

PL/Iなどの高級言語では、ポインタを渡す際、そのデータが「どのような属性(配列の長さ、境界、文字列の長さなど)」を持っているかを、ポインタと一緒に「記述子」として渡す仕組みがあります。これにより、受け取り側のルーチンは、渡されたデータの詳細情報を動的に把握できます。

一方で、C言語やアセンブラにはこのような記述子の概念がありません。これらの言語は、純粋なメモリ上のアドレス(ポインタ)のみを期待します。ここでPL/I側がデフォルトの「DESCRIPTOR」状態で呼び出すと、期待されるポインタの他に、隠し引数として記述子情報をスタックに積んでしまい、相手先ではスタックがずれてしまい、最悪の場合は致命的なABENDを引き起こします。

実装・解決策:NODESCRIPTORの指定

他言語で書かれた外部ルーチンを呼び出す場合は、ENTRY宣言時に「NODESCRIPTOR」を明示的に指定する必要があります。これにより、コンパイラは記述子の生成を抑制し、純粋なアドレスのみを引数として渡すようになります。

サンプルプログラム:外部ルーチン呼び出しの定義例

以下は、アセンブラで書かれたルーチンをPL/Iから安全に呼び出すためのENTRY宣言の例です。

/ アセンブラルーチン呼び出し用の定義 /
DCL EXT_ASM_ROUTINE ENTRY(
POINTER NODESCRIPTOR, / 記述子なしで純粋なアドレスのみを渡す /
FIXED BIN(31) / 通常の数値引数 /
) EXTERNAL;

/ 使用例 /
DCL PTR_DATA POINTER;
DCL LEN FIXED BIN(31) INIT(100);

/ アドレスをセットして呼び出し /
CALL EXT_ASM_ROUTINE(PTR_DATA, LEN);

応用・注意点:現場でのトラブルシューティング

現場で「他言語との連携後に異常終了する」「データがずれている」といった事象が発生した場合、まずはENTRY宣言を確認してください。

注意点1:属性の不一致
NODESCRIPTORを指定すると、受け取り側ルーチンはデータの長さや境界を一切知ることができません。呼び出し側で、渡すデータのサイズを固定長にするか、あるいは別途サイズを引数として渡すなどの設計が必要です。

注意点2:隠し引数の罠
移行プロジェクトなどで古いプログラムを修正する際、ENTRY宣言を適切に記述しないと、コンパイルは通るものの実行時にランタイムエラーになります。特に、複数の引数が並んでいる場合は、記述子の有無でスタックのオフセットが完全に狂うため、引数の順番に関係なく異常が発生します。

他言語連携のコードを触る際は、必ず呼び出し先(Callee)が何を期待しているのか、アセンブラソースや設計書で確認し、必要に応じてNODESCRIPTORを適用する癖をつけておきましょう。

コメント

タイトルとURLをコピーしました