高度な検索
Developer Connection
Member Login ログイン | ご入会 ADC連絡先


Technote 1083

Weak-Linking to a Code Fragment Manager-based Shared Library


目次

古い方法: Gestalt セレクタをインストールしてトラップテーブルを呼び出す

新しい方法: 共有ライブラリ

ライブラリインポートのチェック

Gestalt だけでは十分ではない

要約
QickTime、QuickDraw 3D、Apple Game Sprockets、および Color Picker など、Mac OS の多くの部分は、現在、共有ライブラリとして配布されています。このため、たいていの場合、特定のテクノロジーが存在するかどうかを考慮することなく、デベロッパは目的のテクノロジーを使用することができます。これまで、Apple ではさまざまなテクノロジーを共有ライブラリ化してきましたが、現在、CFM (Code Fragment Manager) の標準化を進めています。この TECHNOTE では、CFM ベースの共有ライブラリとの間に弱いリンク (Weak-link) を設定する方法、および実行時にライブラリが使用できるかどうかをチェックする方法について説明します。

この TECHNOTE は、共有ライブラリを使用しているすべての Macintosh デベロッパに役立ちます。


古い方法: Gestalt セレクタをインストールしてトラップテーブルを呼び出す
クラシックな 68K モデルでは、すべてのシステム関数はトラップテーブル経由で実行されます。新しいシステムソフトウェアの機能は、それまで未使用だったテーブルのセクションに単純に自分自身をパッチします。これらの機能をアプリケーションで使用するため、システムソフトウェアのそれぞれの部分は Gestalt セレクタをインストールします。特定の機能が使用可能であるかどうかをチェックするアプリケーションでは、Gestalt 呼び出しを実行して、対応するセレクタがインストールされているかどうかを確認します。たとえば、QuickTime をチェックするコードは次のようになります。

theErr = Gestalt (gestaltQuickTime, &result);
if (theErr != noErr)
	return false;
else
	return true;


最終的に、2 つのテストを実行することができます。一つはマネージャが使用可能であるかどうかのテストであり、もう一つは、32 ビット QuickDraw が使用可能であるかどうかといった、そのマネージャの内部で特定の機能を使用できるかどうかのテストです。2 番目のテストの場合、Gestalt ビットを使って、対象となる機能の "チャンク" を識別するというアプローチ方法をとります。


古いモデルの問題点
こうした古いモデルではさまざまな問題が発生していました。まず一つは、トラップテーブルのロケーションの数に制限があり、最終的に使用可能なロケーションがなくなってしまうという問題です。この問題を回避するために、適切なルーチンを選択するために個別のセレクタパラメータを使って、複数のシステムコールがしばしば単一のトラップの中に置かれていました。ただし、この対処方法の結果、システムソフトウェアのバグをフィックスする機能に制限が加えられ、セレクタベースのトラップにうまくパッチを当てることが困難になっていました。

また別の問題として、大部分のパッチが機能拡張のロード時、つまり、機能拡張がシステムヒープに読み込まれるときにロードされる必要がありました。その結果、これらのアイテムはアプリケーションによって使用されない場合でもロードされることになります。

仮想メモリが“入”になっている場合、システムヒープに関連するメモリのブロックをハードディスクにスワップすることは許可されていません。これは、ドライバやその他、コードの重要部分をスワップできないようにするためです。その結果、システムヒープのサイズが巨大化して、仮想メモリで使用可能なメモリ容量に重大な制限が加えられることになります。これは、メモリとハードディスクとの間でコードやデータのスワップに要する時間が長くなり、パフォーマンスが低下することを意味します。

さらに、アプリケーション以外のコードの一部に含まれるグローバル変数のインプリメントやアクセスが困難になるという問題もありました。

古いモデルに伴うこのような制限のため、新しいモデルが必要であるということが明らかになりました。


新しい方法: 共有ライブラリ
新しいモデルでは、共有ライブラリの一形態であるコードフラグメント (Code Fragment) を使用します。コードのすべての形態は、コードフラグメントとして一貫してパッケージされ、グローバル変数へのフルアクセスが可能になります。仮想メモリが Power Mac で使用されているとき、フラグメントはメモリの中にファイルマップされ、読み込み専用としてマークされます。その結果、メモリの使用がより効率的になり、プログラミングエラーに対するある程度の保護が可能になります。

フラグメントがシステムソフトウェアの機能にアクセスする必要がある場合、それらの機能を別のライブラリからインポートします。システムソフトウェアのたいていの部分は、InterfaceLib という名前のライブラリからインポートされます。一方、その他の部分 (QuickTime、QuickDraw 3D など) では、それらの機能のすべてが独自のライブラリに置かれています。


強いリンク (Strong-link)
デフォルトの設定で、すべてのインポートライブラリには強いリンクが設定されています。つまり、アプリケーションを起動するためには、そのアプリケーションでそれらのライブラリが使用可能でなければなりません。CFM ベースのアプリケーションを起動するとき、そのアプリケーションではすべてのライブラリをインポートしようとします。アプリケーションが強いリンクのライブラリを準備できない場合、Finder では次のようなエラーメッセージを表示します。

The Application "MoofWars" could not be opened because "DrawSprocketLib"
could not be found.

注意: このメッセージはアプリケーションではなく、CFM 共有ライブラリに適用されます。


弱いリンク (Weak-link)
アプリケーションでは、ライブラリに弱いリンクが設定されていることを指定できます。ライブラリに弱いリンクが設定されていると、そのライブラリが使用可能でなかったり、メモリの制限のために準備できない場合でも、アプリケーションを起動できます。弱いライブラリが見つからないと、アプリケーションが再起動されるまで、そのライブラリは見つからないままとなります。弱いリンクはインポートの呼び出し時バインディングの代用ではありません。CFM だけがインポートの準備時バインディングを行います。弱いリンクが設定されたライブラリを使用するためには、そのライブラリが準備されているかどうかを判定する必要があります。

インポートされるライブラリ全体を弱いとマークすることができます。これは、そのライブラリがまったく見つからないこともありうるという意味です。個別のインポートシンボルを弱いとマークすることもできます。これは、それらのシンボルが個別に見つからないことがありうるという意味です。ライブラリを弱いとマークすることは、そのライブラリのシンボルすべてが弱いということを暗黙的にマークしていることになります。

注意: CFM のインプリメンテーションからして、この逆は文字通りには真ではありません。強いライブラリからインポートされたすべてのシンボルが弱い場合でも、強いライブラリは使用可能でなければなりません。いわゆる "スマート" リンカにより、インポートされるすべてのシンボルが弱く、かつライブラリ全体も弱いとマークすることが可能になります。

CFM の "インプリメントの詳細" の中には、その動作が一貫していないように見えるものがあります。現在、インポートライブラリを識別するときに発生する大部分のエラーは、弱いライブラリを通過させてしまいます。こうした状況は将来変更され、その結果、弱いライブラリは文字通り見つからないことだけを許可されるようになります。ライブラリが正常に検出されると、その他すべてのエラーは致命的なものとなります。これは変更されません。たとえば、ライブラリのデータセクションを割り当てるために十分なアプリケーションヒープスペースがない場合、準備は失敗します。インプリメンテーションの詳細は今後変更される可能性があるため、この TECHNOTE の参考文献「Macintosh Runtime Architectures」の最新のドラフトを参照してください。

開発環境には、弱いリンクとのインタフェースを提供する責任があります。たとえば、CodeWarrior では、ライブラリ全体が弱くリンクされることだけを許可します。このコマンドは、それぞれのソースライブラリに対するプロジェクトウィンドウにポップアップメニューとして表示されます。これに対して MPW では、ライブラリ全体または個別のシンボルをマークすることができます。ライブラリまたは個別のシンボルをマークする方法については、それぞれの開発環境のマニュアルを参照してください。というのも、それぞれのツールが提供する機能は頻繁に変更されるためです。たとえば、MPW は現在、個別のシンボルをより容易に弱くリンクできるように改訂されています。


ライブラリインポートのチェック
コードフラグメントには、インポートする各シンボルのアドレスが含まれています。フラグメントが準備されているとき、CFM ではそのアドレスを特定し、ポインタを埋めます。C 言語の場合、これらのポインタは、インポートされたルーチンまたは変数のアドレスを見つけるときに取得できるものとまったく同じものです。インポートされたシンボルを解決できないとき、そのアドレスは、kUnresolvedCFragSymbolAddress に設定されます (これはゼロであるということを冗長に表示しているにすぎません)。

共有ライブラリが正常に準備されているかどうかをテストする最も一般的な方法は、ライブラリに含まれるシンボルを 1 つ取り出して、それを kUnresolvedCFragSymbolAddress と比較することです。たとえば、QuickDraw 3D との間に弱いリンクを設定している場合は、次のコードを使って、それが正常に準備されているかどうかを確認します。

#include <QD3D.h>
#include <CodeFragments.h>
Boolean HasQuickDraw3D (void)
{
	return ( (Ptr) Q3Initialize == (Ptr) kUnresolvedCFragSymbolAddress);
}


このシンボルの存在は、その他すべてのシンボルも使用可能であることを必ずしも意味しません。たとえば、ColorSync 1.0 を使用していて、ColorSync 2.0 の個別のシンボルとの間に弱いリンクを設定している場合、2.0 のシンボルを個別にテストすることになります。

ライブラリをテストするもう一つの方法は、kFindCFrag オプションを使って GetSharedLibrary を呼び出すことです。これを行うには、ライブラリの 'cfrg' リソースからインポートライブラリの名前を取得する必要があります。


「3D Graphics Programming With QuickDraw 3D」に示されているコードの修正
上のコードは、「3D Graphics Programming With QuickDraw 3D」に示されているコードに取って代わります。そのコードは、関数ポインタを論理値にキャストして、アドレスをテストする前に上位の 24 ビットをクリアします。次のコードは、このことを示しています。

Boolean BuggyHasQuickDraw3D (void)
{
	return((Boolean) Q3Initialize != kUnresolvedCFragSymbolAddress);
}
00000084: 80C20000 lwz r6,Q3Initialize(RTOC)
00000088: 54C6063E clrlwi r6,r6,24
0000008C: 28060000 cmplwi r6,$0000



Gestalt だけでは十分ではない
システムソフトウェアのいくつかの部分には、Gestalt 呼び出しをインストールする機能拡張が含まれています。たとえば、QuickTime は、CFM およびクラシック 68K モデルの両方で実行できる必要があります。このため、QuickTime ではトラップテーブルにパッチを当て、Gestalt 関数をインストールします。QuickDraw 3D にも実際には、まさに Gestalt セレクタをインストールする機能拡張のコードが含まれています。

システムソフトウェアのこれらの部分のいずれかをインポートする PowerPC ネイティブまたは CFM-68K アプリケーションを書いている場合、Gestalt セレクタをインストールすることはできますが、アプリケーションを実行するときに、ライブラリはそのアプリケーションで使用できるようにはなりません。このため、ライブラリと弱いリンクを行っている場合は、Gestalt セレクタとライブラリインポートの両方をチェックする必要があります。そうしないと、そのライブラリに含まれるルーチンを最初に呼び出すとき、アプリケーションはクラッシュしてしまいます。

Boolean HasQuickTime (void)
{
	OSErr theErr = noErr;
	long result;

	theErr = Gestalt (gestaltQuickTime, &result);
	if (theErr != noErr)
		return false;

	theErr = Gestalt (gestaltQuickTimeFeatures, &result);
	if (theErr != noErr)
		return false;

	if (!(result & (1 << gestaltPPCQuickTimeLibPresent)))
		return false;

	// QuickTime PowerPlug のエントリーポインタは解決されていなかったので
	// この部分は、弱いリンクを設定する場合だけ必要です。

#if GENERATINGCFM
	return ((Ptr) EnterMovies != (Ptr)kUnresolvedCFragSymbolAddress);
#else
	return true;
#endif
}


現在、Code Fragment Manager は CFM68K ランタイム内に存在します。このため、単に PowerPC に対するテストだけでは不十分です。ヘッダファイル ConditionalMacros.h では、GENERATINGCFM など、いくつかの条件をセットアップし、これらは PowerPC または CFM68K のもとでコンパイルするとき、True にセットされます。#if GENERATINGCFM に含まれる弱いリンクを設定されたシンボルを対象としたテストをラップすることは、テストを条件化する上で最適な方法といえます。


要約
Apple では、CFM (Code Fragment Manager) を標準化しました。CFM により、Mac OS のすべてのコードに一貫したインタフェースが提供されます。CFM は 68K にも適用されます。弱いリンク (Weak-link) を使用すると、特定のフラグメントが別のフラグメントに依存しなくなります。弱いリンクを設定されたフラグメントを使用する前に、それらが使用可能であるかどうかをチェックする必要があります。

参考文献