(57)【要約】【課題】ソフトウェアの単体試験における作業を簡略化することのできる単体試験支援装置を得る。【解決手段】関数置換部１２１は、試験対象プログラム１１０中で書き換えられる被置換関数１１２の識別子と、被置換関数１１２を書き換えるための置換関数１０２の識別子を入力する。アドレス解決部１２２は、被置換関数１１２の識別子から試験対象プログラム１１０の書換え位置のアドレスを取得する。プログラム書換え部１２３は、アドレス解決部１２２が取得したアドレスの試験対象プログラム１１０を書き換えることで被置換関数１１２を置換関数１０２で書き換える。【選択図】図１
2014203384.tif 000002
【特許請求の範囲】
【請求項１】
試験対象プログラム中で書き換えられる被置換関数の識別子と、前記被置換関数を書き換えるための置換関数の識別子を入力する関数置換部と、
前記被置換関数の識別子から前記試験対象プログラムの書換え位置のアドレスを取得するアドレス解決部と、
前記アドレス解決部が取得したアドレスの前記試験対象プログラムを書き換えることで前記被置換関数を前記置換関数で書き換えるプログラム書換え部とを備えた単体試験支援装置。
【請求項２】
前記アドレス解決部は、前記被置換関数の識別子から前記試験対象プログラムの書換え位置のアドレスとして当該被置換関数の先頭アドレスを取得し、
前記プログラム書換え部は、前記先頭アドレスからのメモリ内容を前記置換関数に直接または間接的に分岐する１つ以上の命令に書き換えることを特徴とする請求項１記載の単体試験支援装置。
【請求項３】
前記アドレス解決部は、前記被置換関数の識別子を用いて前記試験対象プログラムのシンボル情報を検索することで、前記被置換関数の先頭アドレスを取得することを特徴とする請求項２記載の単体試験支援装置。
【請求項４】
前記アドレス解決部は、前記被置換関数の名前と当該被置換関数を含むソースファイルの識別子を用いて前記試験対象プログラムのデバッグ情報を検索することで、前記被置換関数の先頭アドレスを取得することを特徴とする請求項２の単体試験支援装置。
【請求項５】
前記アドレス解決部は、前記被置換関数の名前と、前記試験対象プログラムが実行される場合に前記被置換関数を呼び出す試験対象関数のソースプログラム識別子とを用いて前記被置換関数の先頭アドレスを取得し、見つからなければ前記被置換関数の名前のみを用いて当該被置換関数の先頭アドレスを検索することを特徴とする請求項２記載の単体試験支援装置。
【請求項６】
前記アドレス解決部は、前記被置換関数の名前と、前記試験対象プログラムが実行される場合に前記被置換関数を呼び出す試験対象関数のリロケータブルファイルとを用いて前記被置換関数の先頭アドレスを取得し、見つからなければ前記被置換関数の名前のみを用いて当該被置換関数の先頭アドレスを検索することを特徴とする請求項２記載の単体試験支援装置。
【請求項７】
前記アドレス解決部は、前記被置換関数の定義を含むリロケータブルファイルのシンボル情報と前記試験プログラムのシンボル情報とセクション先頭アドレスとから、前記被置換関数の先頭アドレスを算出することを特徴とする請求項２記載の単体試験支援装置。
【請求項８】
前記アドレス解決部は、前記試験対象プログラムのデバッグ情報の有無、または、前記試験対象関数のリロケータブルファイルの有無に基づいて、前記被置換関数の先頭アドレスを取得する手段を選択することを特徴とする請求項１記載の単体試験支援装置。
【請求項９】
前記アドレス解決部は、前記試験対象プログラムのアドレス空間において前記被置換関数を参照している箇所のアドレスを取得し、
前記プログラム書換え部は、前記アドレス解決部が取得したアドレスの箇所を前記置換関数のアドレス参照に書き換えることを特徴とする請求項１記載の単体試験支援装置。
【請求項１０】
前記アドレス解決部は、前記被置換関数が共有ライブラリか否かを判定し、当該共有ライブラリであった場合に前記アドレスの取得を行い、かつ、前記プログラム書換え部による前記アドレスの書換えを行うことを特徴とする請求項９記載の単体試験支援装置。
【請求項１１】
前記被置換関数を前記置換関数で置き換えた場合、置き換えたアドレスとサイズと置き換え前のデータの組を置き換え履歴として記憶すると共に、
前記記憶した置き換え履歴に基づいて、関数置換を復元する関数置換復元部を備えたことを特徴とする請求項１記載の単体試験支援装置。
【請求項１２】
プログラム書換え部によるプログラム書換え処理の前にメモリ保護解除処理を行うメモリ解除部を備えたことを特徴とする請求項１記載の単体試験支援装置。
【請求項１３】
ソフトウェアの単体試験を行うためのコンピュータを、
試験対象プログラム中で書き換えられる被置換関数の識別子と、前記被置換関数を書き換えるための置換関数の識別子を入力する関数置換部と、
前記被置換関数の識別子から前記試験対象プログラムの書換え位置のアドレスを取得するアドレス解決部と、
前記アドレス解決部が取得したアドレスの前記試験対象プログラムを書き換えることで前記被置換関数を前記置換関数で書き換えるプログラム書換え部として機能させるための単体試験支援プログラム。
【発明の詳細な説明】
【技術分野】
【０００１】
この発明は、ソフトウェアの単体試験を行うための単体試験支援装置およびそのプログラムに関するものである。
【背景技術】
【０００２】
ソフトウェア技術者は、ソフトウェアの単体試験において、試験対象プログラムを関数単位で試験をする場合がある。一般に、単体試験は、試験対象関数と試験ドライバと試験スタブとから構成される。試験ドライバは、試験の事前条件を設定し、試験対象関数を呼び出し、事後条件を確認する。試験スタブは、試験対象関数が呼び出す別の関数のダミーである。試験時のソフトウェア構成として試験対象関数が使用する関数をダミーの試験スタブに置き換えることで、試験対象関数の実行フローを制御しやすくなる。この結果、ソフトウェア技術者は、試験を効率化に進めることができる。
【０００３】
一般に、関数のダミースタブによる置換は、被置換関数の定義を含むオブジェクトファイルの代わりに、ダミースタブの定義を含むオブジェクトファイルを試験ドライバや試験対象関数を含むファイルとリンクすることによって行われる。従って、試験対象関数と試験スタブによる被置換候補の関数とが同一ソースファイルに記述されている場合は、被置換候補の関数を試験スタブで置き換えるのは困難である。
【０００４】
このため、試験スタブを用いた試験を行うソフトウェア開発では、ソフトウェアの構成として不自然であっても被置換候補の関数を試験対象プログラムとは別のソースファイル中で記述したり、試験時のみプリプロセッサディレクティブなどで被置換候補の関数をコンパイル対象から除外するなどの工夫が必要であった。結果として、試験スタブを利用することで、場合によってはソフトウェアの保守性が悪くなるなどの問題があった。
【０００５】
このような問題を解決するためには、被置換対象の関数が試験対象プログラムと同じファイルで定義されている場合でも、ソースコードを変更せずにプログラムの挙動を変更する仕組みが必要となる。そのような仕組みを提供する方法としてコードを変換する方法があった（例えば特許文献１）。
【先行技術文献】
【特許文献】
【０００６】
【特許文献１】特開２００９−２３７６１０号公報
【発明の概要】
【発明が解決しようとする課題】
【０００７】
しかしながら、従来の方法では、試験ドライバでの試験記述とは別に、関数フックの条件を記述する必要があった。このため、試験ケースを作成するためにソフトウェア技術者が複数のファイルを編集する必要があり、作業が煩雑であるという問題があった。
【０００８】
この発明は、上記のような問題点を解決するためになされたものであり、ソフトウェアの単体試験における作業を簡略化することのできる単体試験支援装置を得ることを目的とする。
【課題を解決するための手段】
【０００９】
この発明に係る単体試験支援装置は、試験対象プログラム中で書き換えられる被置換関数の識別子と、被置換関数を書き換えるための置換関数の識別子を入力する関数置換部と、被置換関数の識別子から試験対象プログラムの書換え位置のアドレスを取得するアドレス解決部と、アドレス解決部が取得したアドレスの試験対象プログラムを書き換えることで被置換関数を置換関数で書き換えるプログラム書換え部とを備えたものである。
【発明の効果】
【００１０】
この発明の単体試験支援装置は、被置換関数の識別子から試験対象プログラムの書換え位置のアドレスを取得し、このアドレスの試験対象プログラムを書き換えることで被置換関数を置換関数で書き換えるようにしたので、ソフトウェアの単体試験における作業を簡略化することができる。
【図面の簡単な説明】
【００１１】
【図１】この発明の実施の形態１による単体試験支援装置を示す構成図である。
【図２】この発明の実施の形態１による単体試験支援装置の試験ドライバの動作を示すフローチャートである。
【図３】この発明の実施の形態１による単体試験支援装置の関数置換前の関数呼出しを模式的に示す説明図である。
【図４】この発明の実施の形態１による単体試験支援装置の関数先頭書換えによる関数置換後の関数呼出しを模式的に示す説明図である。
【図５】この発明の実施の形態１による単体試験支援装置の関数先頭書換えによる試験支援部の処理を示すフローチャートである。
【図６】この発明の実施の形態１による単体試験支援装置のオブジェクトファイル読出し処理の詳細を示すフローチャートである。
【図７】この発明の実施の形態１による単体試験支援装置のステップＳＴ５０２の関数先頭アドレス取得処理の詳細を示すフローチャートである。
【図８】この発明の実施の形態１による単体試験支援装置のシンボル検索処理の詳細を示すフローチャートである。
【図９】この発明の実施の形態２による単体試験支援装置の関数先頭アドレス取得処理を示すフローチャートである。
【図１０】この発明の実施の形態３による単体試験支援装置の関数先頭アドレス取得処理を示すフローチャートである。
【図１１】この発明の実施の形態４による単体試験支援装置の関数先頭アドレス取得を模式的に示す説明図である。
【図１２】この発明の実施の形態４による単体試験支援装置の関数先頭アドレス取得処理を示すフローチャートである。
【図１３】この発明の実施の形態４による単体試験支援装置のセクション先頭アドレス取得を模式的に示す説明図である。
【図１４】この発明の実施の形態４による単体試験支援装置のセクション先頭アドレス取得処理を示すフローチャートである。
【図１５】この発明の実施の形態５による単体試験支援装置の試験支援部の処理を示すフローチャートである。
【図１６】この発明の実施の形態６による単体試験支援装置の関数置換前の共有ライブラリ関数呼出しを模式的に示す説明図である。
【図１７】この発明の実施の形態６による単体試験支援装置の関数置換後の共有ライブラリ関数呼出しを模式的に示す説明図である。
【図１８】この発明の実施の形態６による単体試験支援装置の試験支援部の処理を示すフローチャートである。
【図１９】この発明の実施の形態６による単体試験支援装置の関数アドレス参照書換え処理を示すフローチャートである。
【図２０】この発明の実施の形態７による単体試験支援装置の関数置換処理を示すフローチャートである。
【図２１】この発明の実施の形態８による単体試験支援装置を示す構成図である。
【図２２】この発明の実施の形態９による単体試験支援装置を示す構成図である。
【発明を実施するための形態】
【００１２】
実施の形態１．
図１は、この発明の実施の形態１による単体試験支援装置を示す構成図である。
図１に示す単体試験支援装置は、試験設定部１００、試験対象プログラム１１０、試験支援部１２０、言語処理部１３０を備えている。この単体試験支援装置は、試験対象プログラム１１０中の試験対象関数１１１の単体試験を想定している。単体試験の目的は、試験対象関数１１１が仕様通りに実装されていることを確認することである。
【００１３】
試験設定部１００は、試験ドライバ１０１と置換関数（試験スタブ）１０２とを含んでいる。試験設定部１００は、試験記述プログラムを計算機が実行することで実現されている。この試験記述プログラムは１つ以上のソースプログラムから構成されているものとする。
【００１４】
試験時には、試験対象プログラム１１０が本来利用する被置換関数１１２を試験スタブである置換関数１０２で置き換えて試験対象関数１１１を実行する。試験対象関数１１１の呼出しを行うのが、試験ドライバ１０１である。また、試験ドライバ１０１は、試験支援部１２０の関数置換部１２１を呼び出すことで、実行時に被置換関数１１２を置換関数１０２に置き換える機能を有している。
【００１５】
試験対象プログラム１１０は、試験対象となるプログラムであり、試験対象関数１１１は、試験対象プログラム１１０における単体試験の対象となる関数である。また、被置換関数１１２は、試験対象関数１１１からcallされ、置換関数１０２で置換される関数である。
【００１６】
試験支援部１２０は、試験ドライバ１０１によって呼び出され、試験対象プログラム１１０における被置換関数１１２を置換関数１０２に置き換える処理を行うものであり、関数置換部１２１、アドレス解決部１２２、プログラム書換え部１２３を備えている。関数置換部１２１は、被置換関数１１２の識別子と置換関数１０２の識別子とを入力し、これらの入力に基づいてアドレス解決部１２２とプログラム書換え部１２３とを実行させる機能を有している。アドレス解決部１２２は、被置換関数１１２の識別子からプログラムの書換え位置のアドレスを取得する処理部である。プログラム書換え部１２３は、アドレス解決部１２２が取得したアドレスのプログラムを書き換えることで被置換関数１１２を置換関数１０２で置き換える処理部である。
【００１７】
言語処理部１３０は、試験設定部１００が用いる試験記述プログラムと、試験対象プログラム１１０と、試験支援部１２０における置換え結果と、必要なライブラリとから試験を行うための実行可能ファイルを作成する処理部であり、試験プログラム１４０は、その実行可能ファイルである。
【００１８】
なお、試験設定部１００および試験支援部１２０におけるそれぞれの機能部と言語処理部１３０は、それぞれの機能を実現するためのプログラムと、これらのプログラムを実行するためのＣＰＵやメモリといったハードウェアによって構成され、コンピュータを用いて実現されている。また、これらのプログラムは、ソースコードやリロケータブルなオブジェクトファイル、静的ライブラリ、共有ライブラリなどの形式で記憶媒体に格納可能である。
【００１９】
次に、試験設定部１００における試験ドライバ１０１について説明する。試験ドライバ１０１の主な処理は、試験対象関数１１１の実行に必要な事前条件を設定した後、試験対象関数１１１を呼び出し、呼出し後の事後条件を確認することである。
図２に、試験ドライバ１０１の処理フローを示す。
試験ドライバ１０１は、ステップＳＴ２０１で試験対象関数が実行の前提とする事前条件を設定する。具体的には、試験対象関数１１１が利用する入力変数の初期化などを行う。ステップＳＴ２０２は、試験対象関数１１１が利用する関数を置換関数１０２に置き換える処理である。試験ドライバ１０１は、試験支援部１２０が提供する関数置換部１２１を呼び出すことで、試験支援部１２０が、被置換関数１１２を置換関数１０２に置き換える。本実施の形態では、試験対象プログラム１１０自身の識別子と被置換関数１１２の識別子と置換関数１０２の識別子とを関数置換部１２１への入力とする。以下の説明では、具体的に試験対象プログラム１１０自身の識別子として試験対象プログラム１１０のファイル名、被置換関数１１２の識別子として被置換関数の名前、置換関数１０２の識別子として置換関数のアドレスを使用するが、識別子の形式をこれに限定するものではない点に注意が必要である。なお、置換関数１０２のアドレスは、C/C++言語によって構成されている試験ドライバ１０１の場合、アドレス参照の演算子である&で取得可能な値である。
【００２０】
ステップＳＴ２０３では、試験対象関数１１１の呼出しを行う。このとき試験ドライバ１０１は、後で試験対象関数１１１の戻り値があれば記憶する。
ステップＳＴ２０４は、試験対象関数１１１が期待通りに動作したか否かを確認する処理である。事後条件として試験対象関数１１１が変更した値やステップＳＴ２０３で記憶した試験対象関数の戻り値などを確認する。一般的なｘＵＮＩＴなどの試験フレームワークを用いる場合、事後条件の確認には、試験ドライバ１０１は、フレームワークが提供するアサーションなどを利用する。
【００２１】
次に、関数置換処理について説明する。
始めに、図３と図４とを用いて、試験対象関数１１１における関数置換前後の関数呼出しの様子を説明した後、関数置換処理の説明を行う。
図３は、関数置換前の関数呼出しの模式図である。試験対象関数３０１と被置換関数３０２とは、計算機の主記憶上に格納されたプログラムである。これら試験対象関数３０１と被置換関数３０２は、図１の試験対象関数１１１と被置換関数１１２に対応している。試験対象関数３０１は、被置換関数３０２への呼出し命令CALLを含んでいる。計算機が被置換関数３０２への呼出し命令を実行すると、計算機のプログラムカウンタは、被置換関数３０２のアドレスに移る。計算機は、被置換関数３０２を実行し、被置換関数３０２の関数復帰命令retを実行すると試験対象関数３０１に制御が移動する。
【００２２】
図４は、本実施の形態における関数先頭書換えによる関数置換後の関数呼出しの模式図である。図４では、図３に加えて主記憶上の置換関数４０３も記載している。ここで、試験対象関数４０１と被置換関数４０２および置換関数４０３は、図１の試験対象関数１１１と被置換関数１１２及び置換関数１０２に対応している。
図３と異なるのは、被置換関数４０２の先頭部分である。被置換関数４０２の先頭部分の命令は、置換関数４０３への無条件分岐命令JMP命令に書き換わっている。計算機は、試験対象関数４０１の被置換関数４０２への呼出し命令CALLを実行すると、プログラムカウンタは、被置換関数４０２の先頭アドレスとなる。先頭には置換関数４０３への無条件分岐命令が存在するため、計算機の制御は直ちに置換関数４０３の先頭に移る。置換関数４０３中で関数復帰命令retが実行されると、制御は試験対象関数４０１中の被置換関数４０２呼出し命令の直後の命令に移る。
【００２３】
次に、図３の関数呼出しを図４の関数呼出しに変更するための関数置換処理について図５を用いて説明する。
図５は、関数先頭書換えによる図１の試験支援部１２０の処理フローである。なお、図１のアドレス解決部１２２の処理がステップＳＴ５０２に対応し、プログラム書換え部１２３の処理がステップＳＴ５０３に対応する。
関数置換処理の入力は、試験対象プログラム１１０のファイル名、被置換関数１１２の名前、置換関数１０２のアドレスである。本処理の概要は、入力となる試験対象プログラム１１０の実行可能ファイルを解析し、プログラム書換え箇所のアドレスを解決し、当該箇所のプログラムを書き換えることである。本実施の形態では、プログラム書換え箇所は被置換関数１１２の先頭アドレスである。また、被置換関数１１２の先頭アドレスの取得を試験対象プログラム１１０のシンボル情報から行う。以下の説明では、シンボル情報として試験対象プログラム１１０の実行可能ファイルに含まれるシンボルテーブルを想定するが、シンボルテーブルの代わりに言語処理系が生成するMAPファイルを用いても同様の結果を得ることができる。
【００２４】
ステップＳＴ５０１は、後で被置換関数１１２の先頭アドレスを取得するために必要なシンボルテーブルなどの情報を主記憶上にロードする処理である。本処理の詳細は図６を用いて後述する。なお、本実施の形態の説明では、オブジェクトファイルの情報を一括してステップＳＴ５０１で主記憶上にロードするものとしたが、情報を用いるときにその都度ファイルにアクセスするようにしてもよい。
【００２５】
ステップＳＴ５０２は、被置換関数１１２の名前を入力とし、ステップＳＴ５０２で読み出したシンボルテーブルから被置換関数１１２の先頭アドレスを取得する処理である。本処理の詳細については図７を用いて後述する。
【００２６】
ステップＳＴ５０３では、ステップＳＴ５０２で取得した被置換関数の先頭アドレスの命令を置換関数への無条件分岐命令に書き換える。具体例として、単体試験をＡＭＤ６４アーキテクチャのＣＰＵの計算機上で行う場合、無条件分岐命令は、オペコードが0xE9のJMP命令である。当該JMP命令は、プログラムカウンタ相対ジャンプ命令である。ここでは、ジャンプ命令のオペランドに、（置換関数のアドレス）−（関数の先頭アドレス）−（JMP命令の命令バイト長）を設定する。なお、JMP命令の命令バイト長の値は５である。
このように、プログラム書換え部１２３の処理であるステップＳＴ５０３において、関数の先頭アドレスのコードを置換関数に無条件分岐する命令に書き換えることによって、試験対象プログラム１１０が被置換関数１１２を呼び出すと、結果的に置換関数が実行される効果を得ることができる。
【００２７】
以下では、ステップＳＴ５０１のオブジェクトファイル読出し処理と、ステップＳＴ５０２の関数先頭アドレス取得処理について説明する。具体的にオブジェクトファイルのフォーマットとしては、ＥＬＦ（Executable and Linkable Format）を想定して説明するが、Ｗｉｎｄｏｗｓ  ＰＥ（Ｗｉｎｄｏｗｓは登録商標／以下省略して記載）やＭａｃｈ−Ｏなどの異なるオブジェクトファイルフォーマットでも本発明は同様に適用可能である。
【００２８】
図６は、図５におけるオブジェクトファイル読出し処理の詳細を示すフローチャートであり、ここでは、ＥＬＦファイルの読出し処理を示している。ＥＬＦファイルの読出し処理フローでは、ＥＬＦファイルの名前を入力として、ＥＬＦファイルの解析に必要となる情報をメモリ上に読み出す。ここで読み出す情報は、本実施の形態では特に図５のステップＳＴ５０２で関数先頭アドレス取得に必要となるシンボルテーブルである。
【００２９】
ステップＳＴ６０１は、ＥＬＦファイル名のファイルのオープン処理である。このとき、ファイルのオープンには、ＥＬＦファイル読出し処理の入力として与えられるファイル名を用いる。オープンしたファイルはクローズする必要があるが、クローズ処理については本発明とは直接関係しないため、クローズ処理の説明は、以後、省略するものとする。
【００３０】
ステップＳＴ６０２では、ＥＬＦファイルの先頭に格納されたファイルヘッダを読み出す処理である。ファイルヘッダは、後の処理で利用するセクションヘッダテーブルのオフセットとセクションヘッダの個数などを保持している。
【００３１】
ステップＳＴ６０３は、セクションヘッダテーブルを読み出す処理である。ステップＳＴ６０２で読み出したＥＬＦヘッダに含まれるセクションヘッダテーブルの位置と個数を参照し、ＥＬＦファイル中の当該位置からセクションデータテーブルをメモリに読み出す。このとき、セクションヘッダテーブルの位置は、ファイルヘッダのe_shoffフィールドに格納されている。また、セクションヘッダの個数はe_shnumに格納されている。
【００３２】
ステップＳＴ６０４は、セクションヘッダ用の文字列テーブルをファイルからメモリに読み出す処理である。まず、ステップＳＴ６０２で読み出したファイルヘッダのe_shstrndxフィールドの値を用いて、ステップＳＴ６０３で読み出したセクションヘッダテーブルの先頭から数えてe_shstrndx番目のセクションヘッダから、セクションヘッダ用文字列テーブルのファイル中でのオフセットとサイズを取得する。セクションヘッダ用文字列テーブルのオフセットとサイズとは、それぞれセクションヘッダのsh_offsetフィールドとsh_sizeフィールドとから取得可能である。
【００３３】
ステップＳＴ６０５は、文字列テーブルを読み出す処理である。ＥＬＦファイルでは、文字列テーブルは、名前が.strtabのセクションに格納されている。まず、セクションヘッダテーブルから名前が.strtabのセクション名を取得する。セクションヘッダの名前は、セクションヘッダのｓh_nameフィールドの値を用いて取得可能である。具体的には、セクションヘッダ用の文字列テーブル先頭からsh_nameフィールドバイト目に存在するゼロ終端文字列がセクションヘッダ名である。
【００３４】
ステップＳＴ６０６は、シンボルテーブルを読み出す処理である。シンボルテーブルは、名前が.symtabのセクションである。ステップＳＴ６０５における文字列テーブルの場合と同様に.symtabをセクションヘッダテーブルから検索し、セクションヘッダに記載された位置からシンボルテーブルをメモリに読み出す。このとき、シンボルの位置とサイズとは、それぞれセクションヘッダのsh_offsetフィールドとsh_sizeフィールドの値である。なおsh_sizeの値とシンボルテーブルエントリの商は、シンボルテーブルのエントリ数となる。ステップＳＴ６０６では、エントリ数もメモリに記憶しておく。
【００３５】
ステップＳＴ６０７は、リロケーションテーブルを読み出す処理である。リロケーションテーブルは、リロケーション対象となるセクションごとに存在する。また、各々のリロケーションテーブルも、独立したセクションに格納されている。リロケーションテーブルが格納されているセクションは、.relまたは.relaに続ききリロケーション対象となるセクション名で始まる名前を持つセクションに格納されている。例えば、.textセクションのリロケーションテーブルであれば、.rel.textまたは.rela.textを名前に持つセクションに格納されている。ステップＳＴ６０７では、.relまたは.relaのセクションをメモリ上にロードする。
【００３６】
次に、図７を用いてステップＳＴ５０２の関数先頭アドレス取得処理の詳細を説明する。図７は、実行可能ファイルシンボル情報による関数先頭アドレス取得処理のフローである。本処理では、被置換関数１１２の名前を入力とし、ステップＳＴ５０１で読み出したシンボルテーブルから被置換関数１１２の名前と一致するシンボルを検索することで、被置換関数１１２のアドレスを取得する。
【００３７】
ステップＳＴ７０１では、被置換関数１１２の名前をシンボル名とするシンボルテーブルエントリを検索する処理である。シンボル検索処理の詳細は、図８を用いて後述する。ここでは、シンボルテーブル検索処理への入力とする検索条件として、シンボルの名前が被置換関数１１２の名前と一致することを指定する。なお、シンボル名は、シンボルテーブルエントリのst_nameフィールドの値を文字列テーブルのインデックスとして読み出せるゼロ終端文字列から得られる。シンボル検索処理からは、検索条件に合致するシンボルの一覧が得られる。
【００３８】
ステップＳＴ７０２は、ステップＳＴ７０１で検索したシンボルがシンボルテーブル中に見つかったか否かに基づく条件分岐処理である。シンボルが見つかったか否かは、ステップＳＴ７０１で得られるシンボル一覧が空でないか否かで判定可能である。シンボルが見つからなかった場合、エラーで終了する。一方、シンボルが見つかっている場合、ステップＳＴ７０３に分岐する。
【００３９】
ステップＳＴ７０３は、ステップＳＴ７０１で検索したシンボルがシンボルテーブル中に２つ以上見つかったか否かに基づく条件分岐処理である。シンボルが２つ以上みつかった場合は、エラーで終了する。一方、見つかったシンボルが１つのみである場合、ステップＳＴ７０４に分岐する。
【００４０】
ステップＳＴ７０４では、見つかったシンボルの値、すなわち被置換関数１１２のアドレスを主力結果に格納する。シンボルの値は、ステップＳＴ７０１で検索の結果として唯一の得られたシンボルテーブルエントリのst_valueフィールドの値である。
【００４１】
このように、アドレス解決部１２２の処理であるステップＳＴ５０２が、被置換関数１１２の名前を用いて試験対象プログラム１１０の実行可能ファイルのシンボル情報を検索することで、被置換関数１１２の先頭アドレスを取得できるため、試験対象プログラム１１０がデバッグ情報を保持していなくても被置換関数１１２の先頭アドレスを取得することができる効果を得ることができる。
【００４２】
次に、ステップＳＴ７０１のシンボル検索処理について図８を用いて説明する。
シンボル検索処理は、シンボルテーブルエントリを巡回し、入力とする検索条件と一致するを持つシンボルテーブルエントリの一覧を返す処理である。ステップＳＴ８０１は、検索結果をクリアする処理である。ステップＳＴ８０２〜ＳＴ８０５は、シンボルテーブルエントリを巡回するループ処理である。すなわち、ステップＳＴ８０３でシンボルが検索条件と一致するかを判定し、一致した場合はステップＳＴ８０４で検索結果にシンボルテーブルエントリを追加し、一致しなかった場合はそのまま次のシンボルテーブルエントリに進む。シンボルテーブルエントリ数は、図６のＥＬＦファイル読出し処理のステップＳＴ６０６処理時に記憶した値を参照することで得られる。
【００４３】
このように、実施の形態１では、関数置換部１２１を試験ドライバ１０１から呼び出せるようにすることで、試験ドライバ１０１の処理中で被置換関数１１２を置換関数１０２で置き換えることができる。また、アドレス解決部１２２を設けることで、プログラム書換え対象となるアドレスを試験対象プログラム１１０の実行時に取得できることができる。また、プログラム書換え部１２３を設けることで、試験ドライバ１０１の実行時に被置換関数１１２の呼出しを置換関数１０２の呼出しとすることができる。
【００４４】
以上説明したように、実施の形態１の単体試験支援装置によれば、試験対象プログラム中で書き換えられる被置換関数の識別子と、被置換関数を書き換えるための置換関数の識別子を入力する関数置換部と、被置換関数の識別子から試験対象プログラムの書換え位置のアドレスを取得するアドレス解決部と、アドレス解決部が取得したアドレスの試験対象プログラムを書き換えることで被置換関数を置換関数で書き換えるプログラム書換え部とを備えたので、ソフトウェアの単体試験における作業を簡略化することができる。
【００４５】
また、実施の形態１の単体試験支援装置によれば、アドレス解決部は、被置換関数の識別子から試験対象プログラムの書換え位置のアドレスとして被置換関数の先頭アドレスを取得し、プログラム書換え部は、先頭アドレスからのメモリ内容を置換関数に直接または間接的に分岐する１つ以上の命令に書き換えるようにしたので、試験対象プログラムが被置換関数を呼び出すと結果的に置換関数が実行されることを容易に実現することができる。
【００４６】
また、実施の形態１の単体試験支援装置によれば、アドレス解決部は、被置換関数の識別子を用いて試験対象プログラムのシンボル情報を検索することで、被置換関数の先頭アドレスを取得するようにしたので、試験対象プログラムがデバッグ情報を備えていなくても被置換関数の先頭アドレスを取得することができる。
【００４７】
また、実施の形態１の単体試験支援プログラムによれば、ソフトウェアの単体試験を行うためのコンピュータを、試験対象プログラム中で書き換えられる被置換関数の識別子と、被置換関数を書き換えるための置換関数の識別子を入力する関数置換部と、被置換関数の識別子から試験対象プログラムの書換え位置のアドレスを取得するアドレス解決部と、アドレス解決部が取得したアドレスの試験対象プログラムを書き換えることで被置換関数を置換関数で書き換えるプログラム書換え部として機能させるようにしたので、ソフトウェアの単体試験における作業を簡略化することができる単体試験支援装置をコンピュータ上に実現することができる。
【００４８】
実施の形態２．
実施の形態１では、試験対象プログラム１１０のソースファイル群のうち、複数のソースファイルで同一名のローカル関数が定義されている場合、シンボルテーブルには、同一のシンボル名を持つエントリが複数存在する。このような場合は、被置換関数１１２の先頭アドレスを正しく解決できないことが問題となる。具体的には、図７のステップＳＴ７０３でシンボルが複数見つかるため、エラーとなる。このような問題を解決するのが実施の形態２である。なお、図面上の構成は図１と同様であるため、図１の構成を用いて説明する。
【００４９】
本実施の形態では、試験ドライバ１０１から被置換関数１１２が定義されているソースファイルを関数置換部１２１に伝達することで、シンボル解決が曖昧になることを回避することが基本的な仕組みである。このようにすることで、１つのソースファイルで同名の関数が複数定義されることはないため、ソースファイルと関数名で試験対象プログラム１１０に含まれる被置換関数１１２を一意に識別することができる。すなわち、実施の形態２では、アドレス解決部１２２が、被置換関数１１２の名前と被置換関数１１２を含むソースファイルの識別子を用いて試験対象プログラム１１０のデバッグ情報を検索することで、被置換関数１１２の先頭アドレスを取得するよう構成されている。その他の構成は実施の形態１と同様である。
【００５０】
以下、本実施の形態の動作説明として実施の形態１との差分を説明する。
まず、実施の形態２では、図５のステップＳＴ５０２の関数先頭アドレス取得処理が、シンボル情報ではなく、試験対象プログラム１１０のデバッグ情報を用いる点と、被置換関数１１２を含むソースファイルの名前を余分に入力とする点とが実施の形態１と異なる。また、関数置換部１２１への入力として、被置換関数１１２を含むソースファイル識別子が入力に加わる。同様に、図２のステップＳＴ２０２では、入力にソースファイル識別子が入力として加わる。
【００５１】
以下の説明では、識別子としてソースファイル名を用いるものとするが、最終的にソースファイル名に変換可能であれば任意の識別子で代用可能である。例えば、一般にオブジェクトファイル名とソースファイル名との相違は拡張子の部分だけであることを利用すると、オブジェクトファイル名やそのベースファイル名を識別子としても容易にソースファイル名を得ることができる。
【００５２】
次に、本実施の形態における、図５のステップＳＴ５０２の関数先頭アドレス取得処理を、図９を用いて説明する。
図９は、デバッグ情報による関数先頭アドレス取得処理のフローである。処理の入力は、被置換関数１１２の名前と被置換関数１１２を含むソースファイルの名前である。
ステップＳＴ９０１では、関数置換処理の入力の試験対象プログラム１１０に含まれるデバッグ情報から、ソース名と被置換関数名をキーとして被置換関数１１２のアドレスを取得する。本処理において、デバッグ情報からソース名と関数名の対で関数のアドレスを取得する処理は公知であるため、ここでは処理の概要のみを示す。デバッグ情報がDWARF形式の場合、デバッグ情報は、.debug_infoセクションに格納されている。ステップＳＴ９０１では、はじめにタグがDW_TAG_compilation_unitであるDIEの中から属性DW_AT_nameがソースファイル名と一致するDIE検索する。次に当該DIEの後続DIEのうちタグがDW_TAG_subprogramの中からDW_AT_nameが関数名と一致するDIEを検索する。結果として見つかったDIEの属性DW_AT_low_pcの値が求める関数先頭アドレスである。
【００５３】
ステップＳＴ９０２は、ステップＳＴ９０１の結果に基づく条件分岐処理である。ステップＳＴ９０１で被置換関数１１２のアドレスを取得することができなかった場合、エラー終了する。一方、ステップＳＴ９０１でアドレスを取得できた場合、ステップＳＴ９０３に分岐する。ステップＳＴ９０３では、ステップＳＴ９０１で取得した被置換関数１１２の値を結果として出力する。
【００５４】
以上説明したように、実施の形態２の単体試験支援装置によれば、アドレス解決部は、被置換関数の名前と被置換関数を含むソースファイルの識別子を用いて試験対象プログラムのデバッグ情報を検索することで、被置換関数の先頭アドレスを取得するようにしたので、試験対象プログラム中に被置換関数と同じ名前を持つ関数が複数存在する場合であっても、被置換関数を一意に取得することができる。
【００５５】
実施の形態３．
実施の形態２では、被置換関数１１２が定義されているファイル名を用いて被置換関数１１２の先頭アドレスを取得した。このため、被置換関数１１２を定義しているソースファイルを変更した場合は、正しく試験が実行できないことになる。具体的には、被置換関数１１２を定義しているソースファイルが変更された場合、図９のステップＳＴ９０２において、被置換関数１１２の情報が見つからないため、エラー終了する。従って、被置換関数１１２を定義しているソースファイルが変更された後も正しく試験を実行するためには、試験ドライバ１０１側の修正も必要となる。具体的には、図２のステップＳＴ２０２の入力の被置換関数１１２を含むソースファイル識別子を更新する必要がある。ソースファイルが変更される例としては、試験対象関数１１１と同じソースファイルに定義されていた被置換関数１１２が、リファクタリングなどで別のソースファイルに移動される場合などが挙げられる。
【００５６】
このような問題を解決するのが、本実施の形態である。すなわち、被置換関数１１２が定義されているファイルが変更されても、試験ドライバ１０１側の変更を不要とする。以下では実施の形態２との差分で本実施の形態を説明する。なお、図面上の構成は図１と同様である。
【００５７】
本実施の形態が実施の形態２とは異なる点は、関数置換部１２１の入力として被置換関数１１２のソースファイル識別子ではなく、試験対象関数１１１のソースファイル識別子を用いる点である。すなわち、実施の形態３のアドレス解決部１２２は、被置換関数１１２の名前と、試験対象関数１１１のソースプログラム識別子とを用いて被置換関数１１２の先頭アドレスを取得し、見つからなければ被置換関数１１２の名前のみを用いて被置換関数１１２の先頭アドレスを検索するよう構成されている。その他の構成は実施の形態１と同様である。
【００５８】
以降の説明では、ソースファイル識別子としてソースファイル名を用いることを想定して説明する。
図５のステップＳＴ５０２の関数先頭アドレス取得処理の入力は、被置換関数１１２を含むソースファイルの名前ではなく、試験対象関数１１１のソースファイル識別子である。また、関数置換部１２１への入力も同様に、被置換関数１１２を含むソースファイル識別子ではなく、試験対象関数１１１のソースファイル識別子である。同様に、図２のステップＳＴ２０２では、試験対象関数１１１のソースファイル識別子を入力とする。
【００５９】
次に、本実施の形態における、図５のステップＳＴ５０２の関数先頭アドレス取得処理を、図１０を用いて説明する。
図１０は、ソースファイル名を用いた関数先頭アドレス取得処理を示すフローチャートである。処理の入力は、被置換関数１１２の名前と、試験対象関数１１１を含むソースファイルの名前である。
ステップＳＴ１００１では、試験対象関数１１１を含むソースファイルの名前と被置換関数１１２の名前とで関数のアドレスを検索する。具体的には、試験対象関数１１１のソースファイルに定義されている関数の中から被置換関数１１２の名前を持つ関数のアドレスを取得する。この処理は実施の形態２におけるステップＳＴ９０１と概ね同じである。
【００６０】
ステップＳＴ１００２では、ステップＳＴ１００１の処理結果に基づく分岐処理である。ステップＳＴ１００１で被置換関数１１２の情報が見つかった場合、ステップＳＴ１００３に分岐する。それ以外の場合は、ステップＳＴ１００４に分岐する。
ステップＳＴ１００３では、ステップＳＴ１００１で見つかった被置換関数の情報から先頭アドレスを取得し、処理の結果として出力する。
【００６１】
ステップＳＴ１００４では、関数名が被置換関数と一致するグローバル関数を検索する処理である。この処理は公知であるため、ここでは処理の概要のみを示す。具体的にデバッグ情報がDWARF形式の場合、タグがDW_TAG_subprogramのDIEの中から、属性DW_AT_nameが被置換関数の名前と一致しており、かつ属性DW_AT_externalが1のDIEを検索すればよい。見つかったDIEの属性DW_AT_low_pcの値が求めるアドレスである。
【００６２】
ステップＳＴ１００５は、ステップＳＴ１００４の結果に基づく分岐処理である。ステップＳＴ１００４で被置換関数１１２の情報が見つかった場合、ステップＳＴ１００６に分岐する。それ以外の場合、エラー終了する。
ステップＳＴ１００６は、ステップＳＴ１００４で見つかった被置換関数１１２の情報から先頭アドレスを取得し、処理の結果として出力する。
【００６３】
このように、アドレス解決部１２２の処理であるステップＳＴ５０２の実現手段として、関数アドレス取得処理において、被置換関数１１２の名前とソースプログラム識別子とを用いて被置換関数１１２の先頭アドレスを取得し（ステップＳＴ１００１）、見つからなければ被置換関数１１２の名前のみを用いて被置換関数１１２の先頭アドレスを検索する（ステップＳＴ１００４）手段を持つことで、被置換関数１１２が定義されているファイルの位置に依存することなく、被置換関数１１２を置換関数１０２で置き換えることができる。このため、試験ドライバ１０１作成後に、被置換関数１１２が別のファイルに移動された場合においても、試験ドライバ１０１を修正する必要がなくなるという効果を得ることができる。
【００６４】
なお、上記説明では、試験対象関数１１１のソースプログラム識別子をソースプログラムのファイル名として説明したが、試験対象関数１１１の名前や試験対象関数１１１のアドレスで代用することも可能である。試験対象プログラム１１０のデバッグ情報を検索すれば、試験対象関数１１１の名前や試験対象関数１１１のアドレスから試験対象関数１１１を定義しているソースプログラムを特定することができるためである。
【００６５】
また、試験対象関数１１１のソースプログラム識別子としてファイル名ではなく試験対象関数１１１のアドレスを用いることで、試験対象関数１１１のソースファイルの名前が変更されても試験ドライバ１０１を変更する必要がなくなるという効果を新たに得ることもできる。
【００６６】
以上説明したように、アドレス解決部は、被置換関数の名前と、試験対象プログラムが実行される場合に被置換関数を呼び出す試験対象関数のソースプログラム識別子とを用いて被置換関数の先頭アドレスを取得し、見つからなければ被置換関数の名前のみを用いて被置換関数の先頭アドレスを検索するようにしたので、被置換関数が定義されているファイルの位置に依存することなく、被置換関数を置換関数で置き換えることができる。このため、試験ドライバ作成後に、被置換関数が別のファイルに移動された場合においても、試験ドライバを修正する必要がないという効果が得られる。
【００６７】
実施の形態４．
実施の形態２、３では、被置換関数１１２のアドレスを取得するためにデバッグ情報を用いた。このため、被置換関数１１２を含むオブジェクトファイル自体がデバッグ情報を保持する必要がある。そのためには、被置換関数１１２を定義するソースファイルのコンパイル時にデバッグ情報の出力オプションがコンパイラに指定されている必要がある。従って、被置換関数１１２を含むオブジェクトファイルのソースコードが得られない場合などに、実施の形態２は適用できないことになる。本実施の形態では、この問題を解決する方法を提供する。なお、本実施の形態においても、図面上の構成は図１と同様である。
【００６８】
本実施の形態では、図１０のステップＳＴ１００１とステップＳＴ１００４でデバッグ情報を用いた関数の先頭アドレス取得処理の代わりにシンボル情報を用いた関数先頭アドレス取得処理を行うことを特徴とする。すなわち、実施の形態４のアドレス解決部１２２は、被置換関数１１２の名前と、試験対象プログラム１１０が実行される場合に被置換関数１１２を呼び出す試験対象関数１１１のリロケータブルファイルとを用いて被置換関数１１２の先頭アドレスを取得し、見つからなければ被置換関数１１２の名前のみを用いて被置換関数１１２の先頭アドレスを検索するよう構成されている。
【００６９】
次に、実施の形態４の単体試験支援装置の動作について説明する。
始めに、処理が単純なステップＳＴ１００４のシンボル情報を用いた関数先頭アドレス取得処理を説明する。ステップＳＴ１００４では、グローバルな被置換関数１１２のアドレスを取得することが目的である。そこで、本実施の形態では、ステップＳＴ５０１で読み出した試験対象プログラム１１０のシンボルテーブルから、被置換関数１１２の名前と一致しかつグローバルなシンボルのエントリを取得すればよい。シンボル検索処理は実施の形態１で示した通りである。なお、シンボルがグローバルであるか否かは、シンボルテーブルエントリのst_infoの上位４ビットがSTB_GLOBALか否かで判定可能である。また、シンボルが関数か否かは、シンボルテーブルエントリのst_infoフィールドの下位４ビットがSTT_FUNCか否かで判定可能である。
【００７０】
次に、図１１と図１２を用いてステップＳＴ１００１に対応する処理について説明する。
図１１は、リロケータブルファイルのシンボル情報による関数先頭アドレス取得の模式図である。アドレス空間１１０１は、試験対象プログラム１１０を含むプロセスのアドレス空間である。また、リロケータブルファイル１１０３は、試験対象関数１１１を含むリロケータブルファイルである。リロケータブルファイル１１０３は、コードを含む.textセクション１１０５を保持している。また、.textセクション１１０５の中に図１の被置換関数１１２に対応する被置換関数１１０６が存在するものとする。.textセクション１１０５は、アドレス空間１１０１においては、セクション先頭アドレスＢ１１０２の位置にマップされている。また、リロケータブルファイル１１０３は、リロケータブルファイルシンボルテーブル１１０４も含んでいる。リロケータブルファイルシンボルテーブル１１０４は、被置換関数１１０６に対応するエントリをもつ。当該シンボルテーブルエントリには、被置換関数の.textセクション１１０５からのオフセットＡが値として記憶されている。本実施の形態では、計算機が（セクション先頭アドレスＢ）＋（オフセットＡ）を計算することで、ロケータブルファイルのシンボル情報による関数先頭アドレス取得処理を実現する。
【００７１】
図１２は、リロケータブルファイルのシンボル情報による関数先頭アドレス取得処理を示すフローチャートである。
ステップＳＴ１２０１は、被置換関数１１０６を含むリロケータブルファイル名を入力としてオブジェクトファイル読出し処理を行う。オブジェクトファイル読出し処理は、実施の形態１で示した通りである。リロケータブルファイル１１０３の名前は、ソースファイル名の拡張子を変更して得られる。例えば、Ｃ言語のソースファイルであれば、拡張子を.cから.oや.objに変更すればよい。
【００７２】
ステップＳＴ１２０２では、被置換関数名のシンボルをステップＳＴ１２０１で読み出したシンボルテーブルから検索する処理である。シンボル検索処理は実施の形態１で示した通りである。ここでは、シンボル検索処理の入力に与える検索条件は、シンボルの名前が被置換関数１１０６の名前と一致することである。シンボルテーブルエントリのシンボル名は、シンボルテーブルエントリのst_nameフィールドを、ステップＳＴ１２０１で読み出した文字列テーブルのインデックスとして読み出せるゼロ終端文字列である。
【００７３】
ステップＳＴ１２０２で得られるシンボルテーブルエントリのst_valueフィールドは、関数が定義されているセクションの先頭からのオフセットである。なお、ここでは説明を単純化するため関数が定義されているセクションが標準的な.textセクションであると想定して説明するが、別のセクションであってもよい。その場合は、シンボルが存在するセクションをステップＳＴ１２０２で記憶し、ステップＳＴ１２０５の処理では記憶したセクションを対象として先頭アドレスを取得すればよい。シンボルテーブルエントリのst_shndxフィールドの参照で行うことで、シンボルが存在するセクションを特定することができる。
【００７４】
ステップＳＴ１２０３では、ステップＳＴ１２０２の処理結果に基づく条件分岐処理である。ステップＳＴ１２０２でシンボルが見つからなかった場合、エラーとして終了する。一方、ステップＳＴ１２０２で条件と一致するシンボルが見つかった場合はステップＳＴ１２０４に分岐する。
【００７５】
ステップＳＴ１２０４では、見つかったシンボルの値を変数Ａに格納する。次に、ステップＳＴ１２０５では、ステップＳＴ１２０１で読み出したリロケータブルファイル１１０３における.textセクション１１０５のアドレス空間１１０１における先頭アドレスを取得する処理である。ステップＳＴ１２０５の詳細については図１３と図１４とを用いて後述する。
【００７６】
ステップＳＴ１２０６では、ステップＳＴ１２０５で取得したセクション先頭アドレスをＢに格納する。また、ステップＳＴ１２０７では、Ａ＋Ｂの計算によって関数先頭アドレスを計算し、結果として出力する。
【００７７】
次に、図１２におけるステップＳＴ１２０５のセクション先頭アドレス取得処理について図１３と図１４とを用いて説明する。
図１３は、セクション先頭アドレス取得の模式図である。アドレス空間１３０１は、試験対象プログラム１１０のプロセスのアドレス空間である。リロケータブルファイル１３０３は、試験対象プログラム１１０を含むリロケータブルファイルであり、.textセクション１３０４とリロケータブルファイルシンボルテーブル１３０６を含む。また、.textセクション１３０４には任意のグローバル関数Ｇ１３０５が１つ含まれるものとする。リロケータブルファイルシンボルテーブル１３０６は、グローバル関数Ｇ１３０５に対応するエントリを保持している。当該エントリは、グローバル関数Ｇ１３０５の.textセクション１３０４からのオフセットＸを保持している。また、実行可能ファイルシンボルテーブル１３０７は、試験対象プログラム１１０の実行可能ファイルのシンボルテーブルである。実行可能ファイルシンボルテーブル１３０７も、グローバル関数Ｇ１３０５に対応するエントリを保持している。当該エントリは、アドレス空間１３０１におけるグローバル関数Ｇ１３０５のアドレスＹを保持している。本実施の形態では、ステップＳＴ１２０５におけるセクション先頭アドレス取得処理が、（グローバル関数Ｇ１３０５のアドレスＹ）−（グローバル関数Ｇ１３０５のセクション先頭１からのオフセットＸ）を計算することでテキスト先頭アドレスを求める。
【００７８】
図１４は、セクション先頭アドレス取得処理を示すフローチャートである。セクション先頭アドレス取得処理の入力は、実行可能ファイルの名前と本処理の呼出し元で読み出したリロケータブルの情報とである。
ステップＳＴ１４０１は、ステップＳＴ１２０１で読み出したリロケータブルファイルからグローバルな関数のシンボルを取得する処理である。シンボル検索処理は、実施の形態１で説明した通りであるが、検索条件としては、シンボルがグローバルな関数であることを指定する。シンボルがグローバルか否かはシンボルテーブルエントリのst_infoの上位４ビットがSTB_GLOBALか否かで判定可能である。また、シンボルが関数か否かは、シンボルテーブルエントリのst_infoフィールドの下位４ビットがSTT_FUNCか否かで判定可能である。
【００７９】
ステップＳＴ１４０２は、ステップＳＴ１４０１の結果に基づく条件分岐処理である。ステップＳＴ１４０１でシンボルが見つからなかった場合、エラー終了する。一方、シンボルが見つかった場合、ステップＳＴ１４０３に分岐する。ステップＳＴ１４０３では、ステップＳＴ１４０１で得られたシンボル一覧の先頭を変数Ｘに代入する処理である。ここではシンボル一覧の先頭を選択したが、実際にはシンボルがグローバルの関数である条件を満足する任意のシンボルをＸに代入してよい。
【００８０】
ステップＳＴ１４０４では、実行可能ファイル名を入力としてオブジェクトファイル読出し処理を行う。オブジェクトファイル読出し処理の詳細は、実施の形態１で説明した通りである。なお、ステップＳＴ５０１でも同じ情報を読み出しているため、その情報をそのまま用いることで本処理を省略することも可能である。
ステップＳＴ１４０５では、ステップＳＴ１４０４で読み出したファイル情報からシンボルを検索する処理である。このとき、検索条件として、ステップＳＴ１４０３で代入したシンボルＸの名前と一致することを指定する。
ステップＳＴ１４０６は、ステップＳＴ１４０５の結果に基づく条件分岐処理である。ステップＳＴ１４０５でシンボルが見つからなかった場合はエラー終了する。一方、シンボルが見つかった場合はステップＳＴ１４０７に分岐する。
【００８１】
ステップＳＴ１４０７では、ステップＳＴ１４０６の結果で得られたシンボル一覧の先頭を変数Ｙに代入する処理である。ステップＳＴ１４０５では、グローバルな関数のシンボルＸの名前で検索しているため、通常結果として得られる一覧は、１つのシンボルのみ含む。
ステップＳＴ１４０８では、Ｙ−Ｘを計算することでセクション先頭アドレスを計算する。シンボルの値は、具体的にはＸとＹとそれぞれのst_valueフィールドの値である。Ｙのシンボル値は、グローバル関数の先頭アドレスであり、Ｘのシンボル値は、セクション先頭からのオフセットである。両者の差をとることで、セクション先頭のアドレスを取得することができる。
【００８２】
以上説明したように、実施の形態４の単体試験支援装置によれば、アドレス解決部は、被置換関数の名前と、試験対象プログラムが実行される場合に被置換関数を呼び出す試験対象関数のリロケータブルファイルとを用いて被置換関数の先頭アドレスを取得し、見つからなければ被置換関数の名前のみを用いて被置換関数の先頭アドレスを検索するようにしたので、被置換関数が定義されているファイルの位置に依存することなく、被置換関数を置換関数で置き換えることができる。このため、試験ドライバ作成後に、被置換関数が別のファイルに移動された場合においても、試験ドライバを修正する必要がなくなるという効果を得ることができる。
【００８３】
また、実施の形態４の単体試験支援装置によれば、アドレス解決部は、被置換関数の定義を含むリロケータブルファイルのシンボル情報と試験プログラムのシンボル情報とセクション先頭アドレスとから、被置換関数の先頭アドレスを算出するようにしたので、デバッグ情報を用いることなく試験対象関数と同じファイルで定義された被置換関数のアドレスを取得することができる。このとき、被置換関数がローカル関数で、同名のローカル関数が試験プログラムに複数含まれていても被置換関数のアドレスを一意に特定することができる。被置換関数がローカル関数の場合、試験対象関数が呼び出す被置換関数は、必ず被置換関数と同じソースファイルで唯一定義されているためである。
【００８４】
実施の形態５．
実施の形態１〜４では、それぞれアドレス解決部１２２は異なる関数先頭アドレスの取得手段を用いて先頭アドレスの取得を行った。具体的には、実施の形態１では、図７に示した実行可能ファイルのシンボル情報を用いた関数先頭アドレス取得手段を用いた。また、実施の形態２では、図９に示したデバッグ情報による関数先頭アドレス取得手段を用いた。また、実施の形態３では、図１０のソースファイル名を用いた関数先頭アドレス取得手段としてデバッグ情報を用いて実現する手段を示した。また、実施の形態４では、図１０のソースファイル名を用いた関数先頭アドレス取得手段としてリロケータブルファイルのシンボル情報を用いて実現する手段について示した。このように、アドレス解決部１２２における関数先頭アドレス取得手段は複数存在する。それぞれの関数先頭アドレス取得手段は、シンボル情報やデバッグ情報など異なる情報を用いて被置換関数１１２のアドレスを取得する。このため、試験ドライバ作成者は、利用可能な情報を元に適切な関数先頭アドレス取得手段を選択する必要があるという問題があった。上記問題を解決するのが本実施の形態である。
【００８５】
本実施の形態では、複数の関数先頭アドレス取得手段の試行手段を備えたアドレス解決部１２２を持つことを特徴とする。すなわち、実施の形態５のアドレス解決部１２２は、試験対象プログラム１１０のデバッグ情報の有無、または、試験対象関数１１１のリロケータブルファイルの有無に基づいて、被置換関数１１２の先頭アドレスを取得する手段を選択するよう構成されている。
【００８６】
本実施の形態における試験支援部１２０の処理フローを図１５に示す。
関数置換部１２１の入力は、試験対象関数１１１のソースファイル名、試験対象プログラム１１０の実行可能ファイル名、被置換関数１１２の名前である。
ステップＳＴ１５０１は、試験対象プログラム１１０の実行可能ファイルの情報を読み出す処理である。本処理は、ステップＳＴ５０１と同じである。ステップＳＴ１５０２は、デバッグ情報があるか否に基づく分岐処理である。デバッグ情報が存在する場合はステップＳＴ１５０５に分岐し、それ以外の場合は、ステップＳＴ１５０４に分岐する。デバッグ情報があるか否かは、ステップＳＴ１５０１で読み出したセクションヘッダテーブルを検索し、デバッグ情報を含むセクションの存在有無を確認することで行うことができる。例えば、実行可能ファイルのフォーマットがELF形式でデバッグ情報のフォーマットがDWARFである場合、.debug_infoセクションがあればデバッグ情報が存在するとみなし、なければ存在しないとみなせばよい。
【００８７】
ステップＳＴ１５０３は、デバッグ情報を用いた関数先頭アドレス取得処理である。本処理は、実施の形態３における図１０の処理と同じである。
【００８８】
ステップＳＴ１５０４は、試験対象関数１１１のソースファイルに対応するリロケータブルファイルの有無による条件分岐処理である。リロケータブルファイルの有無確認は、ソースファイル名から取得したリロケータブルファイル名がファイルシステム上に存在するか否かで行うことができる。リロケータブルファイルが存在すればステップＳＴ１５０５に分岐し、それ以外の場合はステップＳＴ１５０６に分岐する。
【００８９】
ステップＳＴ１５０５は、リロケータブルファイルのシンボル情報を用いた関数先頭アドレス取得処理である。本処理は、実施の形態４の説明に基づく図１０の処理と同じである。
ステップＳＴ１５０６は、実行可能ファイルシンボル情報による関数先頭アドレス取得処理である。本処理は、実施の形態１の図７の処理と同じである。
ステップＳＴ１５０７は、ステップＳＴ１５０３、またはステップＳＴ１５０５またはステップＳＴ１５０６で取得した関数先頭アドレスに置換関数１０２への無条件分岐命令を書き込む処理である。本処理の詳細は実施の形態１で示した通りである。
【００９０】
以上説明したように、実施の形態５の単体試験支援装置によれば、アドレス解決部は、試験対象プログラムのデバッグ情報の有無、または、試験対象関数のリロケータブルファイルの有無に基づいて、被置換関数の先頭アドレスを取得する手段を選択するようにしたので、試験ドライバ作成者は、被置換関数の先頭アドレスを取得する手段が利用可能な情報試験ドライバを作成するといったことがなく、試験ドライバ作成者の負担を軽減することができる。
【００９１】
実施の形態６．
実施の形態１では、被置換関数自体を書き換えることで関数置換処理を実現している。しかし、被置換関数が共有ライブラリ関数の場合には置換できないことが問題となる。一般に、共有ライブラリは複数のプロセスから共有されるため、共有ライブラリ関数が書き換わると試験対象プログラム１１０以外のプログラムにも影響を及ぼすことになる。そこで、被置換関数１１２が共有ライブラリ関数の場合の関数置換処理を実現するのが本実施の形態である。
【００９２】
本実施の形態におけるアドレス解決部１２２は、試験対象プログラム１１０のアドレス空間において被置換関数１１２を参照している箇所のアドレスを取得し、プログラム書換え部１２３は、アドレス解決部１２２が取得したアドレスの箇所を置換関数１０２のアドレス参照に書き換えるよう構成されている。その他の構成は図１に示した実施の形態１と同様である。
【００９３】
始めに、図１６と図１７とを用いて、試験対象関数１１１における関数置換前後の関数呼出しの様子を説明した後、関数置換処理の説明を行う。
図１６は、関数置換前の共有ライブラリ関数呼出しの模式図である。ここでは、具体的にオブジェクトファイルの形式がＥＬＦの場合の共有ライブラリ関数呼出しの様子を示すが、Ｗｉｎｄｏｗｓ  ＰＥやＭａｃｈ−ｏも類似のトランポリンコードを用いた共有ライブラリ関数呼出しとなるため、本発明を適用することが可能である。なお、ここではダイナミックリンクが完了している状態を示す。
【００９４】
試験対象関数１６０１と被置換関数１６０４とＰＬＴ１６０２とＧＯＴ．ＰＬＴ１６０３は、計算機の主記憶上に格納されたプログラムおよびデータである。なお、試験対象関数１６０１と被置換関数１６０４は、図１における試験対象関数１１１と被置換関数１１２に対応するプログラムである。試験対象関数１６０１は、共有ライブラリの被置換関数呼出し時には、被置換関数１６０４を直接呼び出す代わりに被置換関数１６０４に対応するＰＬＴ（Ｐｒｏｃｅｄｕｒｅ  Ｌｉｎｋａｇｅ  Ｔａｂｌｅ）のエントリPLT[n]を呼び出す。当該ＰＬＴのエントリPLT[N]は、被置換関数１６０４に対応するＧＯＴ．ＰＬＴ１６０３のエントリに格納されたアドレスにジャンプする。ＧＯＴ．ＰＬＴ１６０３のエントリGOT[N]が被置換関数１６０４のアドレスを含んでいる。この結果、ＰＬＴエントリPLT[N]のジャンプ命令を介して被置換関数１６０４が呼び出される。被置換関数１６０４中で関数復帰命令RETが実行されると、試験対象関数１６０１の被置換関数１６０４呼出し命令の直後の命令に処理制御が移る。
【００９５】
図１７は、関数置換後の共有ライブラリ関数呼出しの模式図である。なお、図１７において、試験対象関数１７０１、被置換関数１７０４および置換関数１７０５は、それぞれ図１における試験対象関数１１１、被置換関数１１２および置換関数１０２に対応するプログラムである。置換関数１７０５は主記憶に格納された置換関数である。ＧＯＴ．ＰＬＴ１７０３の被置換関数１７０４に対応するエントリGOT[N]が被置換関数１７０４のアドレスではなく、置換関数１７０５のアドレスを保持する。この結果、試験対象関数１７０１がＰＬＴ１７０２のエントリPLT[N]を呼び出すと、GOT.PLT[N]が保持する置換関数１７０５のアドレスに間接ジャンプする。その結果、処理制御は置換関数１７０５の先頭に移る。置換関数１７０５中で関数復帰命令RETが実行されると、処理制御は、試験対象関数１７０１の被置換関数１７０４呼出しの直後の命令に移る。
【００９６】
次に、図１６の共有ライブラリ関数呼出しを図１７の共有ライブラリ関数呼出しに変更する関数置換処理を図１８を用いて説明する。
図１８は、共有ライブラリ関数用の試験支援部１２０の処理フローである。ステップＳＴ１８０１では、試験対象プログラム１１０のファイル名を入力としてオブジェクトファイルを読み出す。本処理の詳細は、実施の形態１に示したので省略する。
【００９７】
ステップＳＴ１８０２は、関数アドレス参照書換え処理である。関数アドレス参照処理への入力としてGOT.PLT用のダイナミックリロケーションテーブルを渡す。ＥＬＦファイルの場合、.rela.pltまたは.rel.pltが関数アドレス参照処理への入力とするリロケーションテーブルを保持している。
【００９８】
次に、ステップＳＴ１８０２の関数アドレス参照書換え処理の詳細を図１９のフローチャートを用いて説明する。
関数アドレス参照書換え処理では、リロケーション情報を利用することで、.GOT.PLT中の被置換関数１１２のエントリを、被置換関数１１２のアドレスから置換関数１０２のアドレスに書き換える。
図１９に示す関数アドレス参照書換え処理では、被置換関数１１２の名前と.textセクション先頭アドレスと置換関数１０２のアドレスとを入力とする。また、関数アドレス参照書換え処理では、ステップＳＴ１９０１で読出した.textセクションのリロケーションテーブルも利用する。
【００９９】
ステップＳＴ１９０１からステップＳＴ１９０５は、ステップＳＴ１９０１で読み出したセクションのリロケーションテーブルのエントリを巡回するループ処理である。
ステップＳＴ１９０２は、巡回中のリロケーションテーブルエントリの対象が被置換関数１１２か否かに基づく条件分岐処理である。巡回エントリの対象が被置換関数１１２でない場合、ステップＳＴ１９０３とステップＳＴ１９０４をスキップする。一方、巡回中のエントリが被置換関数１１２に対応する場合ステップＳＴ１９０３に分岐する。
【０１００】
巡回中のエントリが被置換関数１１２に対応するか否かは、リロケーションテーブルエントリがリンクしているシンボルテーブルエントリの名前が被置換関数１１２の名前と一致しているか否かで確認可能である。リロケーションテーブルエントリがリンクしているシンボルは、リロケーションテーブルエントリのr_infoフィールドを参照することで取得可能である。r_infoフィールドからシンボルテーブルインデックスを取得し、ステップＳＴ１９０１で読み出したシンボルテーブルにアクセスすることで目的とするシンボルを得ることができる。さらにシンボルテーブルエントリのst_nameフィールドから文字列テーブルのインデックスを読出し、ステップＳＴ１９０１で読み出した文字列テーブルにアクセスすることでシンボルの名前を取得できる。
【０１０１】
ステップＳＴ１９０３は、被置換関数の参照箇所のアドレスを取得する処理である。ここでは、被置換関数の呼出し箇所のアドレスをリロケーションテーブルエントリのオフセットから取得する。具体的には、リロケーションテーブルエントリのオフセットは、r_offsetフィールドの値である。この値は、図１６のＧＯＴ．ＰＬＴ１６０３のエントリGOT[N]のアドレスである。
【０１０２】
ステップＳＴ１９０４は、ステップＳＴ１９０３で取得した被置換関数参照箇所のアドレスに存在するデータを置換関数１０２のアドレスに書き換える処理である。この結果として図１７のようにＧＯＴ．ＰＬＴ１７０３のエントリGOT[N]は、置換関数１７０５のアドレスを保持するようになる。
【０１０３】
以上説明したように、実施の形態６の単体試験支援装置によれば、アドレス解決部は、試験プログラムのアドレス空間において被置換関数を参照している箇所のアドレスを取得し、プログラム書換え部は、アドレス解決部が取得したアドレスの箇所を置換関数のアドレス参照に書き換えるようにしたので、被置換関数が共有ライブラリ中の関数である場合にも被置換関数を置換関数に置き換えることができる。
【０１０４】
実施の形態７．
実施の形態６では、被置換関数１１２が共有ライブラリの場合の関数置換処理を示した。しかし、このような場合、試験ドライバ作成者は、被置換関数１１２が共有ライブラリ関数か否かで試験ドライバ１０１の仕様に対応した関数置換処理を選択する必要がある。そこで、本実施の形態では、被置換関数１１２が共有ライブラリ関数か否かに関わらず関数を置換することができる関数置換処理について示す。即ち、実施の形態７におけるアドレス解決部１２２は、被置換関数１１２が共有ライブラリか否かを判定し、共有ライブラリであった場合に実施の形態６の処理を行い、かつ、プログラム書換え部も実施の形態６の処理を行うよう構成されている。その他の構成は図１に示した実施の形態１と同様である。
【０１０５】
本実施の形態による関数置換処理を図２０のフローチャートに示す。
ステップＳＴ２００１では、試験対象プログラム１１０のオブジェクトファイル情報を読み出す処理である。本処理の詳細は実施の形態１の説明で示した通りである。
【０１０６】
ステップＳＴ２００２は、被置換関数１１２が共有ライブラリ関数か否かに基づく分岐処理である。被置換関数１１２が共有ライブラリ関数であれば、ステップＳＴ２００３に分岐し、それ以外の場合はステップＳＴ２００４に分岐する。被置換関数１１２が共有ライブラリ関数か否かは、GOT.PLT用のダイナミックリロケーションテーブルエントリを探索し、被置換関数１１２に対応するエントリの有無で判定可能である。被置換関数１１２に対応するエントリが見つかった場合、被置換関数１１２は共有ライブラリ関数である。
【０１０７】
ステップＳＴ２００３は、関数アドレス参照書換え処理である。本処理は、実施の形態６の図１９の処理と同様であるため、ここでの説明は省略する。
ステップＳＴ２００４は、関数先頭アドレス取得処理である。関数先頭アドレスの取得手段は任意であり、実施の形態１〜５で示した関数先頭アドレス取得処理などを利用すればよい。
ステップＳＴ２００５では、ステップＳＴ２００４で取得した関数先頭アドレスのコードを置換関数１０２への無条件分岐命令に書き換える。本処理の詳細は実施の形態１に示した通りである。
【０１０８】
以上説明したように、実施の形態７の単体試験支援装置によれば、アドレス解決部は、被置換関数が共有ライブラリか否かを判定し、共有ライブラリであった場合にアドレスの取得を行い、かつ、プログラム書換え部によるアドレスの書換えを行うようにしたので、試験ドライバ作成者は、被置換関数が共有ライブラリか否かで関数置換処理を選択することなく、試験ドライバを作成することができる。また、被置換関数が後で共有ライブラリ関数に変更された場合であっても試験ドライバを修正する必要がなくなる。
【０１０９】
実施の形態８．
実施の形態１〜７では、実行中の試験プログラム自体を動的に書き換えることで、被置換関数を置換関数に置き換えた。このため、関数置換処理を呼び出す試験ドライバの実行後に別の試験ドライバを呼び出すと、別の試験ドライバが期待通りに動作しない場合があることが問題となる。そこで、本実施の形態では、置換した関数を元に戻す手段を実施の形態１〜７に加えるようにしたものである。
【０１１０】
本実施の形態の全体構成図を図２１に示す。本構成では、実施の形態１〜７のいずれかの構成に加えて関数置換復元部１２４が存在する。本実施の形態におけるプログラム書換え部１２３は、被置換関数１１２を置換関数１０２で置き換えた場合、置き換えたアドレスとサイズと置き換え前のデータの組を置き換え履歴として記憶するよう構成されている。また、関数置換復元部１２４は、記憶した置き換え履歴に基づいて、関数置換を復元する処理部である。その他の構成は図１と同様であるため、対応する部分に同一符号を付してその説明を省略する。
【０１１１】
本実施の形態において、置換した関数の復元は、プログラム書換え部１２３と関数置換復元部１２４とが協調して動作することで実現される。具体的には関数置換処理では、処理中でプログラムを書き換えると同時に、書き換えたアドレスと書き換えたサイズと書換え前のデータとからなる組を置換履歴としてメモリ上に記憶する。さらに関数置換処理では、関数置換処理実行中においてメモリ上に記憶した置換履歴の識別子を出力する。例として、置換履歴一覧の識別子には、置換換履歴一覧を記憶したメモリのアドレスを用いればよい。
なお、置換履歴を記憶するタイミングを具体的に記述すると、図５におけるステップＳＴ５０３、図１５におけるステップ１５０７、図１９におけるステップＳＴ１９０４、図２０におけるステップＳＴ２００５である。
【０１１２】
次に関数置換復元部１２４の動作について示す。関数置換復元部１２４は、プログラム書換え部１２３が出力した置換履歴識別子を入力とし、対応する置換履歴に格納されたアドレスに対して書換え前のデータを書き換えたデータのサイズ分だけ書き戻すことで、関数置換を復元する。
【０１１３】
以上説明したように、実施の形態８の単体試験支援装置によれば、被置換関数を置換関数で置き換えた場合、置き換えたアドレスとサイズと置き換え前のデータの組を置き換え履歴として記憶すると共に、記憶した置き換え履歴に基づいて、関数置換を復元する関数置換復元部を備えたので、関数置換処理を行った場合でも、試験対象プログラム全体に関数置換処理による影響が及ぶことを防ぐことができる。
【０１１４】
実施の形態９．
実施の形態１では、ジャンプコード挿入処理においてメモリ上の関数アドレスをジャンプコードに入れ換えることで関数の置換を行った。しかし、Ｌｉｎｕｘ（Ｌｉｎｕｘは登録商標／以下省略して記載）などのオペレーティングシステムでは、セキュリティ対策などのため、コード領域への書き込みが禁止されている場合が多い。したがって、Ｌｉｎｕｘのようにコード領域への書込みを禁止しているオペレーティングシステム上では、実施の形態１のジャンプコード挿入処理において、メモリアクセス違反が発生してしまう。この問題を解決するのが本実施の形態である。
【０１１５】
本実施の形態の単体試験支援装置の全体構成図を図２２に示す。
本実施の形態では、試験支援部１２０ｂとして、実施の形態１〜８のいずれかの構成に加えてメモリ解除部１２５が加わっていることが特徴である。なお、図２２では、実施の形態１〜７の構成に対してメモリ解除部１２５を付加した構成を示している。メモリ解除部１２５は、プログラム書換え部１２３によるプログラム書換え処理の前にメモリ保護解除処理を行う処理部である。他の構成は、実施の形態１〜８のいずれかと同様であるため、ここでの説明は省略する。
【０１１６】
本実施の形態において、メモリ解除部１２５は、プログラム書換え部１２３における被置換関数１１２の置換関数１０２での書換え処理の前にメモリ解除処理を実行し、書換え対象領域のメモリ保護を解除する。このメモリ解除処理を実行するタイミングとしては、具体的には図５におけるステップＳＴ５０３を実行する直前などが該当するが、書換え前であれば、どのタイミングであってもよい。
【０１１７】
メモリ解除処理の具体的な例として、Ｌｉｎｕｘの場合のメモリ保護解除処理を以下に示す。Ｌｉｎｕｘの場合、メモリ保護解除をmprotectシステムコールを用いて行う。mprotectの第一引数のアドレスは、シンボルアドレスを含むページの先頭アドレスである必要がある。ページ先頭アドレスは、シンボルアドレスの下位１２ビットの値をゼロとすることで求めえられる。mprotectの第二引数は、権限を設定する対象領域のサイズである。シンボルアドレス＋シンボルサイズーページ先頭アドレスをmprotectの第二引数とする。mprotectの第３引数は、プロテクションの属性である。ここでは、読出し／書込み／実行の権限を与えるために、PROT_READ | PROT_WRITE |PROT_EXECを与える。ここで、｜は、ビットごとの論理和演算を意味する。また、PROT_READ, PROT_WRITE,PROT_EXECは、読出し、書込み、実行の権限を与えることを意味する。
【０１１８】
以上説明したように、実施の形態９の単体試験支援装置によれば、プログラム書換え部によるプログラム書換え処理の前にメモリ保護解除処理を行うメモリ解除部を備えたので、コード領域にメモリ保護をかけているオペレーティングシステム上で被置換関数を置換関数に置き換えることができる。
【０１１９】
なお、本願発明はその発明の範囲内において、各実施の形態の自由な組み合わせ、あるいは各実施の形態の任意の構成要素の変形、もしくは各実施の形態において任意の構成要素の省略が可能である。
【符号の説明】
【０１２０】
１００  試験設定部、１０１  試験ドライバ、１０２  置換関数、１１０  試験対象プログラム、１１１  試験対象関数、１１２  被置換関数、１２０，１２０ａ，１２０ｂ  試験支援部、１２１  関数置換部、１２２  アドレス解決部、１２３  プログラム書換え部、１２４  関数置換復元部、１２５  メモリ解除部、１３０  言語処理部、１４０  試験プログラム。
【図１】
2014203384.tif 000003
【図２】
2014203384.tif 000004
【図３】
2014203384.tif 000005
【図４】
2014203384.tif 000006
【図５】
2014203384.tif 000007
【図６】
2014203384.tif 000008
【図７】
2014203384.tif 000009
【図８】
2014203384.tif 000010
【図９】
2014203384.tif 000011
【図１０】
2014203384.tif 000012
【図１１】
2014203384.tif 000013
【図１２】
2014203384.tif 000014
【図１３】
2014203384.tif 000015
【図１４】
2014203384.tif 000016
【図１５】
2014203384.tif 000017
【図１６】
2014203384.tif 000018
【図１７】
2014203384.tif 000019
【図１８】
2014203384.tif 000020
【図１９】
2014203384.tif 000021
【図２０】
2014203384.tif 000022
【図２１】
2014203384.tif 000023
【図２２】
2014203384.tif 000024