【PL/I学習|実務向け】PL/IにおけるRETURNS DESCRIPTORの仕組みとバイナリ互換の罠

1. 導入

メインフレーム環境でPL/I開発を行っていると、外部モジュールや他言語との連携で「データの境界」が正しく渡らず、異常終了やメモリ破壊を引き起こすケースがあります。特にVARYING属性を持つ文字列や動的配列を戻り値とする場合、単なるメモリ上のアドレスだけでは、呼び出し元がその長さを判別できません。これを解決するのが「RETURNS DESCRIPTOR」です。本記事では、この属性の重要性と、外部インターフェース設計における注意点を解説します。

2. 基礎知識

PL/IにおけるVARYING属性のデータは、メモリ上では「現在の長さを示すヘッダー情報」と「実際のデータ領域」から構成されます。通常、PL/I同士の呼び出しであればコンパイラが自動的にこれらを管理しますが、他言語(C言語やアセンブラなど)から呼び出す場合、あるいは呼び出し元と呼び出し先でコンパイルオプションが異なる場合、この長さ情報の受け渡しが不整合を起こします。DESCRIPTORを付与することで、戻り値の物理アドレスとは別に、メタデータ(記述子)をスタック領域などに配置し、呼び出し元がそれを参照できる「物理レベルの規約」を強制します。

3. 実装/解決策

RETURNS DESCRIPTORを記述すると、コンパイラは戻り値のポインタに加えて、記述子のポインタを呼び出し元へ渡すコードを生成します。外部インターフェースを設計する際は、呼び出し元側で、この記述子が指し示すメモリ構造を正しく解釈するロジックを実装する必要があります。特に、Pythonのctypes等からPL/Iルーチンを呼び出す際は、この規約を意識しないと、文字列が途中で切れたり、ゴミデータが混入したりするため注意が必要です。

4. サンプルプログラム

以下は、VARYING属性の文字列を返すPL/Iプロシージャの定義例です。

/ 戻り値にDESCRIPTORを付与することで、呼び出し元に長さを通知する /
GET_DATA: PROC(INPUT_ID) RETURNS(CHAR(256) VARYING DESCRIPTOR);

DCL INPUT_ID FIXED BIN(31);
DCL RETURN_VAL CHAR(256) VARYING;

/ ここで動的に文字列を構築 /
RETURN_VAL = ‘データ処理完了 ID=’ || TRIM(INPUT_ID);

/ 戻り値として返す際、コンパイラは自動的にDESCRIPTORを付加する /
RETURN(RETURN_VAL);

END GET_DATA;

5. 応用・注意点

現場で陥りやすいバグとして、「呼び出し元がDESCRIPTORの存在を無視している」ケースがあります。PL/I以外の言語からこのルーチンを呼び出す場合、単に関数のアドレスを叩くだけでは、スタック上に残された記述子ポインタを拾い上げることができません。
また、記述子のフォーマットはコンパイラや環境(z/OSのバージョン等)に依存する場合があるため、バイナリレベルの連携を行う際は、必ずIBMの「PL/I for z/OS 言語解説書」にて、特定の環境における記述子のメモリマップを確認してください。汎用的な外部連携が求められる場合は、DESCRIPTORに頼らず、長さを明示した構造体をポインタで渡す設計(出力用パラメーターとして受け取る方式)に変更することも検討すべきです。

コメント

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