「アセンブリではじめるPSPセミナー」の編集履歴(バックアップ)一覧はこちら
「アセンブリではじめるPSPセミナー」(2009/05/29 (金) 20:40:56) の最新版変更点
追加された行は緑色になります。
削除された行は赤色になります。
*PSP Seminar from Assembly
ps2dev.orgにあるPSPデベロップチュートリアル資料の翻訳。2006年のドキュメントなのでやや古いネタだがメモリマップ以外は基本的に同じだと思う。用語や仕様などで詳しい訳が分からなかったところもあるので随時修正する予定。
http://ps2dev.org/psp/Tutorials/PSP_Seminar_from_Assembly
訳にあたってはMFMのアーノルド氏に技術的な突っ込みに答えてもらった。
Special thanks to the technical advice of Arnold Bronson
----
*裸のPSP
&br()
#center(){{{
&big(){TyRaNiD著}
[[ps2dev.org>http://ps2dev.org/]]
&b(){August 4, 2006}
}}}
&br()
----
*このプレゼンテーションで扱われる内容
***テーマは以下のとおり
-C言語で書くPSP
-PSPの内部構造
-開発ツール
**以下については扱わない
-ゲーム
-Piracy
-海賊版
-Modチップ
----
*もくじ
-1. PSP Home brewの歴史
--PSPのリビジョン
-2. PSPのハードウェア
--システムコントローラ
-3. PSPソフトウェア
--Toolchain
--カーネルモジュール
--スレッドモデル
--インターフェイスプログラミング
--PSPのセキュリティ
-4. デバッグ
--デバッグツール
*PSP Home brewの歴史
#image(fig001.jpg)
至高の存在:PSP
**&u(){PSP年表}
>2004年12月:日本でPSPがリリースされる。FW 1.0
>2005年3月:米国でリリースされる。FW 1.5
>2005年4月:Nemによる最初のhomebrew“Hello World!”。FW 1.0のみ対応
>2005年6月:チームPSP-DEVがFW 1.5上で動くエクスプロイトを開発(kxploit)
>2005年9月:EUでPSPがリリースされる
>2005年9月:LibTIFFのエクスプロイトを使ったFW 2.0用のダウングレーダー開発
>2006年4月:FW 2.0以降でGTAを使ったHome brewがはじまる
>2006年6月:FW 2.5と2.6のカーネルバグが発見され、ダウングレーダーを開発
**&u(){ファームウェアのリビジョン}
リリースされたPSPファームウェアの歴史を少したどってみたい。いくつかはエクスプロイトの穴に対策したバグフィックスだ。
-1.0 コード実行に若干の制限が加えられた(日本のみ)
-1.5 kxploitの実行に制限はなかった(米国のみ)
-1.51と1.52 とくにハックするところはない。2.0からのダウングレード可
-2.0 ダウングレードでなければユーザーモードのみ(libTIFFのeLoader)
-2.01 バグフィックスされたリリース。2.5か2.6にアップグレード
-2.5と2.6 カーネルバグがある。カーネルモードでダウングレードが可能(GTAのeLoader)
-2.7 デベロップは難しい
**&u(){自作のコードを実行する}
どのPSPファームウェアのリビジョンでコードを実行するか
-1.0と1.5 特別なEBOOT.PBPを作れば直接実行可能
-2.0 libTIFFモードからeLoaderを使う
-2.01+ グランドセフトオートUMDとeLoaderを使う
-2.7+ 今のところデベロップの方法はない
----
*PSPハードウェア
**&u(){ハードウェアの仕様について}
PSPはいくつかの複雑なカスタムコンポーネントで構成されている
-MIPSシステムコントローラ(ALLEGREX)
-Media Engine(MIPS+VME+AVCデコーダ)
-メインメモリ 32Mバイト
-Embedded DRAM(Media Engine用)2Mバイト
-VRAM 2Mバイト
-多用途オーディオシステム
-480×272ピクセルのフルカラースクリーン液晶(およそ1.8:1)
-13ボタン、十字キー、アナログスティックの入力
-メモリスティックデュオ用スロット
**&u(){そのほかの仕様}
いくつかのハードウェアは一般的な開発にはそれほど必要ではない
-カスタマイズされた1.7Gバイト容量の光学ドライブ(UMD)
-USBコントローラと802.11bワイヤレスイーサネット
-オーディオコーデックをコントロールするためのI2Cバス
-IrDAトランシーバ
-マルチプルUARTS(IrDA、リモートコントロール、ハードウェアのデバッグ用)
-周辺コントロールのDMAコントローラ(ユーザーアプリのDMAチャンネル用を含む)
-電力とバッテリのコントローラ
-UMDドライブのATAコントローラ
**&u(){ALLEGREX}
ALLEGREXはPSPのメインプロセッサの名前である
-カスタマイズされたMIPS32コア
-追加された命令セット(ビット操作、ユーザーモード割り込みコントロール、CPU停止)
-メモリマネージメント機構(MMU)(TLBキャッシュではない)
-動作クロックの変更(およそ33MHzから333MHzまで)
-オンボードの単精度32ビットFPU
-オンボードのベクタ/マトリックス演算用コプロセッサ(VFPU)
-ハードウェアによるデバッグユニット
-16KバイトスクラッチパッドRAM(使い物にならない)
-オンボードプロファイラ(キャッシュのヒット率の監視、命令の実行など)
-分離された命令とキャッシュ領域
**&u(){ベクタプロセッサ}
メインCPUの3Dグラフィックの性能を補うものとしてベクタ演算用コプロセッサ(VFPU)が用意されている
-128個の32ビットレジスタ
-複数のレジスタをひとつの値のようにして、2×2、3×3、4×4で再設定できる
-列、行、マトリクス、マトリクスの置換
-1回の演算ですべてのマトリクスの乗算ができる
-頻繁に使われる三角関数と平方根の演算命令
-オンボードでの擬似ランダム値生成
**&u(){VFPUレジスタ}
#image(fig002.jpg)
画像:レジスタマップ
**&u(){VFPUコードの開発}
VFPUはアセンブリで書かれていて、ToolchainアセンブラはVFPUのすべての命令をサポートする。
#image(fig003.jpg)
**&u(){グラフィックエンジン}
GEはPSPコアのグラフィック機能である。
-簡素化したGLのAPI風のものが基になっている
-描画はメモリ中のディスプレイリストで行われる
-フレームバッファ、テクスチャ、ディスプレイリストのための2MバイトのVRAM
-メインメモリからテクスチャとディスプレイリストを引っ張り出すためのバスマスターモード
-VRAMの中に必要なのはフレームバッファのみ
**&u(){そのほかのGEの機能}
-多様なテクスチャフォーマット、CLUT(Color LookUp Table)モード、DXT圧縮
-2Dか3Dのモードで演算
-オンボード3Dのクリッピング機能
-スプラインとベジエ面の演算
-モーフィングとスキニング
-ライティングとステンシルバッファ
-アルファブレンドと論理演算
**&u(){ディスプレイリスト}
画面描画はVRAMに直接アクセスできる。適度なパフォーマンスと3D描画のためにはディスプレイリストを使うとよい。
-ディスプレイリストの各エントリは32ビット幅で、8ビットのコマンド、24ビットのデータがある
-24ビットのデータは整数型で表現される。ポインタか切り捨てられた浮動小数点数が入る
-メインリストの中にサブリストを作ることができる
-カスタマイズされたvertexのタイプ、異なるサイズ(8ビットか16ビット固定のポイント、32ビット浮動小数点数)を明示的に利用できる
-それぞれのvertexのタイプに座標、色、ノーマルか太いかを含めることができる
-vertexはリストに直接明示するかインデックスをつけることができる
-浮動小数点は24ビットに変換されるときその精度を失う
**&u(){ディスプレイリスト}
ディスプレイリストはGEへの転送にDMAを用いるが、これはリストを作るときに重要な分岐を得る。
-リストは"Stall"アドレスで作られているときに転送できる
-リストはキャッシュされていないメモリ領域に書かれるか、あるいはリストを使う前にキャッシュデータが書き戻される必要がある
-リストはFINISHコマンドで終了されなければいけない
#image(fig004.jpg)
**&u(){PSPメモリマップ}
PSPのメインプロセッサがMMU(メモリマネジメントユニット)を操作していないとき、メモリセグメンテーションの機能がある
-カーネルモードとユーザーモードの仕様上、ユーザーアプリケーションがカーネルメモリに変更を加えることは防止される
-メインメモリの32Mバイトを分割し、4Mバイトのカーネル、4Mバイトの一時メモリ、24Mバイトのユーザスペースに割り当てている
-4Mバイトの一時メモリはシステムコールのみで使用される(FW 1.0の場合はいつでも使える)。
-デバイスはメモリスペースにマップされる
-仮想メモリシステムはない。カーネル権限を持っていればすべてのメモリエリアにアクセス可能
**&u(){PSPメモリマップ}
いくつかのキーになるアドレスはソースコード中で見かけるかもしれない
|先頭アドレス |サイズ |解説|
|0x00010000 |16K |スクラッチパッド|
|0x04000000 |2M |VRAM|
|0x44000000 |2M |VRAM (キャッシュされない)|
|0x08800000 |24M |メインRAM|
|0x48800000 |24M |メインRAM (キャッシュされない)|
|0x88000000 |4M |カーネルRAM|
----
*PSPソフトウェア
**&u(){コンパイラ}
PSPコンパイラはPS2開発ツールにおける経験を継承する形で開発された。
-GCC 4.0.2をベースにしている
-CとC++の開発環境がある
-VFPUを含むALLEGREXの命令セットのサポート
-標準libcとstdc++関数(stdioやiostreamなど)をサポートしたNewlibのポート
-UNIXライクなシステム用として設計されており、Windows上で使うためにはCygwinかMingWが必要(DevkitPRO)
**&u(){PSPDSK}
フリーのソフトウェアデベロップメントキットはPSPライブラリへのアクセスを提供する。多くのライブラリとツールを使って簡単に開発できる
-ユーザーとカーネルのライブラリはヘッダをインポートする
-リバースエンジニアリングされたlibGUとlibGUM
-シンプルなPCMオーディオライブラリ
-デバッグをサポートするためのライブラリとユーテリティ関数
-カスタムエクスポートテーブルを作るためのツール。ELFファイルのポストプロセスと実行形式をPRXにするためのコンバータ
-カスタムEBOOT.PBPのための圧縮・解凍ツール
-PSPとSDKを使うための60以上のサンプルコード
**&u(){そのほかの開発環境}
今のところCとC++はPSPソフトウェア開発の主な言語である。これでソースコードを書くことに制限はない。
***そのほかのオプション:
-LUA(LUAPlayer)- PSPのためにポートされたLUA。分かりやすいインターフェイスとライブラリ
-Python - PSPのためにポートされたPythonと拡張された部分。
-Flash - FW 2.7以降のPSPで稼動する機能限定版フラッシュプレイヤー
**&u(){カーネルモジュール}
PSPカーネルは多くのモジュールで構成されている。モノリシックカーネルではない。ユーザーアプリケーションはよく出来たモジュールである。
-モジュールはいつでもロードでき実行できる
-モジュールをロード後にロケーションの修正かあるいはリロケートができる
-ほかのモジュールから関数のエクスポートとインポートができる
-カーネルには、ほかのOSにあるようなプロセスという概念はない
**&u(){実行形式のファイル}
PSPはELF形式のモジュールをベースにしている
カーネルはネイティブコードで書かれた3つのタイプのファイルを実行できる
-ELF - ベーシックELF形式。ハードコードされたアドレスにリンクされる(一般的には0x8900000)
-PRX - カスタマイズされたELF形式。メモリ中のどこにでもリロケートできる
-~PSP - 暗号化されたELFかPRX
PSPSDKはELFとPRXを生成できるが暗号化された~PSPは生成できない。
**&u(){モジュールの情報を得る}
モジュールを正しくロードし、また実行を追跡するためには、モジュール情報のセクションが含まれていなければならない
***モジュール情報は以下の内容から成る:
-モジュール名
-モジュールの属性。カーネルモジュールなど
-関数テーブル
-バージョン
PSPSDKは十分役に立つ。ただソースコード中にPSP_MODULE_INFOと明記すればよい。
**&u(){インポートとエクスポートのライブラリ}
カーネルモジュールを正しく使えるようにするために、ユーザーモードアプリケーションへライブラリセットをエクスポートできる
インポート関数はファイルに断片化されている。これはSDKが提供する手段である。
-ユーザーからカーネルへの遷移はシステムコールゲートウェイを経由する
-ユーザーからユーザーへのコールは直接ジャンプすればよい
-カーネルライブラリのために、関数名の先頭32ビットがSHA1でハッシュされており、NIDで認証されている
-かつて多くの問題を生んだ経緯があって、ユーザーライブラリからカーネルモジュールへはリンクできないようになっている
エクスポートファイルを使って自分のコードからエクスポート関数が呼べるなら、通常それは必要ない
**&u(){モジュールのロード}
モジュールのロードと実行は理論的には非常にシンプルだが、注意すべき点もいくつかある。
-ユーザーモードでモジュールをロードできる場所には厳密な制限がある
-カーネルモードでライブラリにリンクしようとするのにはいくつか制限がある
-カーネルはELF形式のモジュールはロードできるが、プレーンテキストのPRXカーネルモジュールはロードできない。SDKにはこれらの制限事項へのパッチが用意されている
#image(fig005.jpg)
**&u(){カーネルのリセット}
カーネルはモジュールのための最適なリソース管理をしないので、PSPをクリーンな状態にするためにカーネルをリセットする
***通常2通りの手順がある:
+1 sceKernelLoadExec - カーネルをリブートして新しく実行する
+2 sceKernelExitGame - カーネルをリブートしてXMBのメニューに戻る
**&u(){システムメモリマネージャ}
おそらくこれがカーネルの最重要点である。リブート後に即ロードされる。
***以下のような感じだ:
-PSPのメモリアロケートを管理する
-メインメモリのセグメントを3つのパーテーションに分割する(カーネル/Media Engine/ユーザエリア)
-リソース管理(UIDテーブル)
-システムイベントハンドラ
-デバッグ機能の実装
*リソース管理
PSPのカーネルはシステムの状態を示すさまざまな情報を保持しており、それらは一貫したUIDテーブルで管理されている。
-UIDテーブルはツリー階層構造を保持している
-どのUIDにも名前が割り当てられており、名前は一意でなくてもよい
-どのカーネルモジュールもそれぞれのUIDタイプを明記できる
-どのエントリもUIDタイプの情報が入ったコントロールブロックを保持している
-カーネルは特定のタスクを実行するUIDをコールできる
-UIDの数値はランダムなものではなくエンコードされたアドレスである
> void *cntladdr = 0x88000000 + ((uid >> 5) & ˜ 3) ;
**&u(){スレッドの概要}
PSPのスレッドモデルはかなりシンプルである。PS2で使われていたIOPカーネルのものに似ている。
-スレッドは互いに強調する
-スレッドの優先度はスケジューリングにより、数値の低いほうが優先度が高い
-少量のスレッドローカルストレージ(TLS)。CPUのK0レジスタを参照する
-VFPUを使うためにはスレッドを作成し、PSP_THREAD_ATTR_VFPU属性を設定しなければならない
**&u(){スレッドの作成}
スレッドは実行の前にまず作成されなければならない。スレッドが一度作成されたら、実行できるようにUIDが与えられる。
#image(fig006.jpg)
**&u(){スレッドの状態}
#image(fig007.jpg)
画像:簡単な状態遷移のダイアグラム
**&u(){同期プリミティブ}
PSPのカーネルはスレッドの同期とコントロールの手段をいくつか提供する。すべての待機関数はオプションでタイムアウトが使える。
-排他制御
-- セマフォ
-- 割り込みコントロール
-メッセージング
-- イベントフラグ
-- メッセージボックスとパイプ
-- コールバック
-同期メモリプール
-- 変数のプール
-- 固定値のプール
-タイマー
-- アラーム
-- 仮想タイマー
**&u(){排他制御(mutex)}
セマフォは同期されたカウンタである。PSPにはmutexタイプはないが、セマフォはそれをシミュレートできる。
-sceKernelCreateSemaとsceKernelDeleteSemaでそれぞれ生成と削除ができる
-sceKernelWaitSemaとsceKernelSignalSemaでそれぞれロックとアンロックができる
割り込みの無効化はローコストのmutexで実現できる
#image(fig008.jpg)
**&u(){イベントフラグ}
イベントフラグは2つ以上のスレッド間で“イベント”をやりとりするもの
-1個のイベントフラグは32×1ビットのイベントから成る
-1個かそれ以上のビットは1回にまとめて待機する
-各ビットはオートかマニュアルでリセットモードにセットできる
#image(fig009.jpg)
**&u(){コールバック}
コールバックはカーネルが特別なイベントをスレッドコンテキストに送るために使われる。スレッドはコールバックの発生に応じるか、あるいは特定のwait関数を呼ばなければならない。コールバック関数の一般的な添え字はCBである。
#image(fig010.jpg)
**&u(){割り込み}
カーネルはユーザーモードで正確に割り込みするためのコールバックメカニズムを提供する
-割り込みコールバックは独立した状態のスレッドでコールされる
-呼ぶと待機状態になりそうな関数はコールしてはいけない
#image(fig011.jpg)
**&u(){スレッドプログラミングのヒント}
***スレッドプログラミングを簡単にするいくつかのヒント:
-過密なループはさせず、可能ならどこかで待機状態にさせるのがよい
-スレッドの優先度は慎重に選び、割り込みのあるスレッドを優先させる
-waitシステムコールの戻り値を常にチェックする
-待機関数を呼ぶときはデッドブロックを検知するためにタイムアウト機能を使うとよい
-スレッドが削除されて終了したのならリソースに問題がある
**&u(){ファイルIO}
カーネルは、有効なデバイスのファイルにアクセスするための標準的なIOサブシステムを提供する
-異なるタイプのIOデバイスを作成できる
-ファイルIOデバイスはプリフィクス(接頭辞)のついたデバイス名でアクセスできる
-デバイスの別名を設定できる
-ブロックデバイスはsceIoAssignを使ってファイルシステムデバイスにマウントできる
-POSIXに似たシステムコール(例:sceIoOpenでファイルオープン、sceIoWriteで書き込みなど)
ファイルIOデバイスのプリフィクスは2つの部分から成る
-デバイス名
-オプションでファイルシステムユニットの番号
***たとえばこんな感じで:
|ms0:| メモリスティックファイルシステム|
|host0:| ホストファイルシステム (PSPLinkエクステンション)|
|flash0:| フラッシュファイルシステム|
|irda0:| IrDAトランシーバのキャラクタドライバ|
|tty0:| テキスト端末キャラクタドライバ|
**&u(){グラフィックライブラリ}
VRAMにピクセルを直接描くような簡単な描画のためのライブラリ
-フレームバッファに16ビット(565, 5551, 4444)か32ビットカラーを設定できる
-sceDisplaySetFrameBufでフレームバッファを設定できる
-sceDisplayWaitVblankStartは現在のスレッドを次のvblank割り込みまで待機状態にし、同期を有効にしたりほかのスレッドが実行されるのを可能にする
#image(fig012.jpg)
**&u(){グラフィックライブラリ}
libGUはPSPのハードウェアアクセラレータによる描画の主要な開発手段である。
これはリバースエンジニアリングされたオフィシャルライブラリをベースにしている。
-libGUはGraphic Engineとディスプレイハードウェアを回避するためのラッパーの役目をする
-自動的にディスプレイリストを管理しオンラインとオフラインのリストを作成できる
-フレームバッファとディスプレイの切り替えを設定できる
libGUMはlibGUでマトリクス操作を直接扱うための補助ライブラリである
-浮動小数点とVFPUによる演算
-GLのようにマトリクスのスタックを保持する
ライブラリは図形マトリクスを設定するためのユーティリティ関数を提供する。図の回転など
PSPで画面描画する方法はLibGUとlibGUMだけではない。
***ほかにも重要なライブラリが利用可能:
-PSPGLはGLスタイルのインターフェイスを持った、ハードウェアアクセラレータのためのライブラリである
-SDLは2Dグラフィックで有用なライブラリである
***ほかにもポートされた画面描画のためのライブラリがある:
-libPNGはPNGデコード用
-libJPEGはJPEGデコード用
-FreeTypeとTrueTypeFont用ライブラリもある
**&u(){オーディオライブラリ}
PSPのオーディオハードウェアにはエクスポートされたライブラリがいくつかある。Media Engineとの組み合わせによりほとんどCPUを使わずにatrac3オーディオを出力できる。
-PSPSDKにはシンプルなストリーミングオーディオライブラリがある。コールバックは次のデータが要求されたときに発動する
-カーネルはatrac3デコーダは利用できる。atrac3のエンコーダは今のところWindowsのみで利用可能である
-libmadのポートとMP3と、そしてOgg/Vorbisデコードのためのlibogg/libvorbisがそれぞれ用意されている
-モジュールプレイバックのためのmikmodのポート。fmodのポートは商用のみしかない
**&u(){ジョイパッド}
ジョイパッドを使うのは簡単で、直接的にではなくてもデモはしやすくデバッグもしやすい
-すべてのデジタルボタンはビットマスクにマッピングされている
-アナログスティックは2つの符号なし8ビットにマッピングされている
#image(fig013.jpg)
**&u(){セキュリティ対策とは?}
PSPの開発者は非公式なコードがシステムを乗っ取るのを阻止するためにいくつかのセキュリティ対策を実装している
-ハードウェア暗号化エンジン
-連続した認証付きブートプロセス
-ゲーム中のバグを緩和するための、カーネルモードとユーザモードの分離
-カーネルはモジュールのタイプによって、ロードの可否とどこからロードされるかについての制限を与える
-実行ファイルとブートリストをカスタム暗号化/署名するラッパのフォーマット
**&u(){PSPのブートプロセス}
書き換えられたカーネルがフラッシュROMからロードされることは可能なのだが、開発者は一連の認証でカーネルの書き換えを阻止しようとしている
-カーネルは暗号化されたフラッシュROMに置かれている
-カーネルのブートは以下の通り:
--1. ブートストラップがフラッシュからステージ1のIPLを復号化する
--2. ステージ1のIPLはメモリ上のステージ2ローダーを復号化する
--3. ステージ2のローダーはカーネルをフラッシュからロードして復号化する
-ステージ2のローダーはいくつかの重要なモジュールを実行前にチェックする(FW 1.5+のみ)
-スクラッチからカスタムファームウェアを作るのでない限り(デベロップは)難しい
**&u(){スレッドの保護}
スレッドには特権レベルがあり、それはカーネルモードかユーザーモードのどちらで実行できるかに関わる。これはカーネルメモリをユーザーアプリケーションから保護する役割がある。
-新しいスレッドを生成するときに特権レベルを拡大することはできない
-スレッドプリミティブは自分を生成した親の権限を継承する
-同じ特権レベルにないとスレッドの列挙やスレッドプリミティブを使うことはできない
-分割されたスタックがシステムコールによって遷移されているとき、アタックやデータの漏洩を防止する
-すべてのカーネル関数は引数のポインタをスレッドの特権レベルでチェックする
**&u(){ユーザアプリケーションの実行形式をロードする}
特権レベルが異なればモジュールのロードも異なる。カーネルモードではチェックが入ったりする。
***モジュールがどのようにロードされるかの例:
-1. 実行形式のチェック(ユーザーモード、カーネルモード、暗号化コード、非暗号化など)
-2. そのモードで許可されている特定デバイスからロードされたかチェック(UMDからのロードはユーザーモードでのみ可能)
-3. 実行ファイルをメモリにロード(もしくは復号化して)する
-4 モジュールを正しいタイプのスレッドで実行する(例えばカーネルモジュールはカーネルモードで)
**&u(){本当にセキュアなのか?}
セキュリティの程度はその脆弱性と同じくらいだ
-PSPの脆弱性の連鎖はソフトウェアにあり、唯一の保護機能はカーネルモードにアクセスできない
-セキュリティは曖昧になってしまっている(訳注:obscurity、ダジャレだと思う)
-システムを完全にコントロールするにはPSPが実行する2つのソフトウェアの欠陥が必要である
-- 1 ゲームをエクスプロイトして任意のコードを実行できる(GTAとか)。ユーザーモードコントロール。
-- 2 カーネルエクスプロイトはすべてのシステムをコントロールできる(FW 2.5と2.6のLoadExecバグ)
-FW 1.0と1.5はまったくセキュリティ対策がされておらず、カーネルモードで直接ELFを実行できる。
----
*デバッグ
**&u(){デバッグ}
CかC++でソースコードを書いているとmakeやデバッグ中にバグが這い出るのはまったく避けようがない
コンシューマ機器であるがゆえに本当の意味で直接デバッグできる手法はない
-例外ハンドラのデフォルトの動作はループある。そのうち電源オフになってしまう
-デバック時のテキストを即座に画面に出力する手段がない
-もちろんすべてがダメというわけじゃない。バカ高いソニーのデベロップメントキットでなくてもデバッグする方法はある。
**&u(){ブラインドデバッグ}
当初、唯一の選択肢は手探り状態でデバッグすることだった。アプリケーションをビルドし、PSPにコピーして実行する。
SDKにはこのプロセスを助けるためのいくつかの機能がある(FW 1.0と1.5用)
-オンスクリーンとSIOで文字を出力する
-Basic SIOベースのGDBスタブ
-特定の例外ハンドラを追加する関数
-スタックトレースとスレッドの列挙のための関数群
厳密には正しい開発手法ではないが、すべてのアプリケーションはメモリスティックに配置されなければならない。FW 2.00以降だけかもしれないが。
**&u(){PSP Inside}
開発とハッキングのためのツール。Hitmen作。
#image(fig014.jpg)
画像:PSP Insideの操作
-PSPの実行状態を表示する
-命令をリアルタイムで逆アセンブルできる
-メモリスティックかフラッシュなどから新しいモジュールをロードできる
**&u(){PSPLink}
PSPLinkはPSP上でテキスト端末環境を提供する開発ツールである
***提供される機能のいくつかを示す:
-SIOとWiFiとUSBを経由して端末からアクセスできる
-USBとhost:deviceからPCのファイルシステムにアクセスできる
-あらかじめ設定されたtty(stdinとstdout、stderr)
-ソフトウェアのバグを捕まえる例外ハンドラ
-WiFiとUSB経由のリモートでGDBスタブを使いソースレベルでデバッグできる
-リアルタイムでカーネルを監視(スレッド、モジュールリスト、メモリダンプとパッチ)
**&u(){例外}
回避できないクラッシュが発生する場合、PSPLinkかPSP Insideを使えばCPU例外を分離できる。この情報を元にクラッシュする場所を特定して修正できる。
-アプリケーションをビルドするときコンパイラに-gオプションを指定しておけばデバッグ情報を有効にできる
-例外の発生にはそれがなぜ起こるかの要因を示す兆候がある
-例外はEPCレジスタも出力するが、これは例外が発生したアドレスを示す
-EPCの値をpsp-addr2lineに渡すと実行形式からソースの行番号を得られる
-例外が出たら以下を試してさらにGDBを使うとよい
> psp−addr2line −e program.elf 0x08900340
> main.c : 68
**&u(){リソースとリファレンス}
PSPプログラミングに役立ついくつかの情報源
-http://www.pspdev.org/ - Toolchainとsubversionによるアーカイブ
-http://psp.jim.sh/pspsdk-doc - PSPSDKドキュメントのオンライン版
-FreeNode IRC(irc.freenode.net)の#pspdevチャンネル - ここに来ればヘルプしてやれると思う、たぶん
----
*PSP Seminar from Assembly
ps2dev.orgにあるPSPデベロップチュートリアル資料の翻訳。2006年のドキュメントなのでやや古いネタだがメモリマップ以外は基本的に同じだと思う。用語や仕様などで詳しい訳が分からなかったところもあるので随時修正する予定。
http://ps2dev.org/psp/Tutorials/PSP_Seminar_from_Assembly
訳にあたってはMFMのアーノルド氏に技術的な突っ込みに答えてもらった。
Special thanks to the technical advice of Arnold Bronson
----
*裸のPSP
&br()
#center(){{{
&big(){TyRaNiD著}
[[ps2dev.org>http://ps2dev.org/]]
&b(){August 4, 2006}
}}}
&br()
----
*このプレゼンテーションで扱われる内容
***テーマは以下のとおり
-C言語で書くPSP
-PSPの内部構造
-開発ツール
**以下については扱わない
-ゲーム
-Piracy
-海賊版
-Modチップ
----
*もくじ
-1. PSP Home brewの歴史
--PSPのリビジョン
-2. PSPのハードウェア
--システムコントローラ
-3. PSPソフトウェア
--Toolchain
--カーネルモジュール
--スレッドモデル
--インターフェイスプログラミング
--PSPのセキュリティ
-4. デバッグ
--デバッグツール
*PSP Home brewの歴史
#image(fig001.jpg)
至高の存在:PSP
**&u(){PSP年表}
>2004年12月:日本でPSPがリリースされる。FW 1.0
>2005年3月:米国でリリースされる。FW 1.5
>2005年4月:Nemによる最初のhomebrew“Hello World!”。FW 1.0のみ対応
>2005年6月:チームPSP-DEVがFW 1.5上で動くエクスプロイトを開発(kxploit)
>2005年9月:EUでPSPがリリースされる
>2005年9月:LibTIFFのエクスプロイトを使ったFW 2.0用のダウングレーダー開発
>2006年4月:FW 2.0以降でGTAを使ったHome brewがはじまる
>2006年6月:FW 2.5と2.6のカーネルバグが発見され、ダウングレーダーを開発
**&u(){ファームウェアのリビジョン}
リリースされたPSPファームウェアの歴史を少したどってみたい。いくつかはエクスプロイトの穴に対策したバグフィックスだ。
-1.0 コード実行に若干の制限が加えられた(日本のみ)
-1.5 kxploitの実行に制限はなかった(米国のみ)
-1.51と1.52 とくにハックするところはない。2.0からのダウングレード可
-2.0 ダウングレードでなければユーザーモードのみ(libTIFFのeLoader)
-2.01 バグフィックスされたリリース。2.5か2.6にアップグレード
-2.5と2.6 カーネルバグがある。カーネルモードでダウングレードが可能(GTAのeLoader)
-2.7 デベロップは難しい
**&u(){自作のコードを実行する}
どのPSPファームウェアのリビジョンでコードを実行するか
-1.0と1.5 特別なEBOOT.PBPを作れば直接実行可能
-2.0 libTIFFモードからeLoaderを使う
-2.01+ グランドセフトオートUMDとeLoaderを使う
-2.7+ 今のところデベロップの方法はない
----
*PSPハードウェア
**&u(){ハードウェアの仕様について}
PSPはいくつかの複雑なカスタムコンポーネントで構成されている
-MIPSシステムコントローラ(ALLEGREX)
-Media Engine(MIPS+VME+AVCデコーダ)
-メインメモリ 32Mバイト
-Embedded DRAM(Media Engine用)2Mバイト
-VRAM 2Mバイト
-多用途オーディオシステム
-480×272ピクセルのフルカラースクリーン液晶(およそ1.8:1)
-13ボタン、十字キー、アナログスティックの入力
-メモリスティックデュオ用スロット
**&u(){そのほかの仕様}
いくつかのハードウェアは一般的な開発にはそれほど必要ではない
-カスタマイズされた1.7Gバイト容量の光学ドライブ(UMD)
-USBコントローラと802.11bワイヤレスイーサネット
-オーディオコーデックをコントロールするためのI2Cバス
-IrDAトランシーバ
-マルチプルUARTS(IrDA、リモートコントロール、ハードウェアのデバッグ用)
-周辺コントロールのDMAコントローラ(ユーザーアプリのDMAチャンネル用を含む)
-電力とバッテリのコントローラ
-UMDドライブのATAコントローラ
**&u(){ALLEGREX}
ALLEGREXはPSPのメインプロセッサの名前である
-カスタマイズされたMIPS32コア
-追加された命令セット(ビット操作、ユーザーモード割り込みコントロール、CPU停止)
-メモリマネージメント機構(MMU)(TLBキャッシュではない)
-動作クロックの変更(およそ33MHzから333MHzまで)
-オンボードの単精度32ビットFPU
-オンボードのベクタ/マトリックス演算用コプロセッサ(VFPU)
-ハードウェアによるデバッグユニット
-16KバイトスクラッチパッドRAM(使い物にならない)
-オンボードプロファイラ(キャッシュのヒット率の監視、命令の実行など)
-分離された命令とキャッシュ領域
**&u(){ベクタプロセッサ}
メインCPUの3Dグラフィックの性能を補うものとしてベクタ演算用コプロセッサ(VFPU)が用意されている
-128個(8バンク×16)の32ビットレジスタ
-複数のレジスタをひとつの値のようにして、2×2、3×3、4×4で再設定できる
-列、行、マトリクス、マトリクスの置換
-1回の演算ですべてのマトリクスの乗算ができる
-頻繁に使われる三角関数と平方根の演算命令
-オンボードでの擬似ランダム値生成
**&u(){VFPUレジスタ}
#image(fig002.jpg)
画像:レジスタマップ
**&u(){VFPUコードの開発}
VFPUはアセンブリで書かれていて、ToolchainアセンブラはVFPUのすべての命令をサポートする。
#image(fig003.jpg)
**&u(){グラフィックエンジン}
GEはPSPコアのグラフィック機能である。
-簡素化したGLのAPI風のものが基になっている
-描画はメモリ中のディスプレイリストで行われる
-フレームバッファ、テクスチャ、ディスプレイリストのための2MバイトのVRAM
-メインメモリからテクスチャとディスプレイリストを引っ張り出すためのバスマスターモード
-VRAMの中に必要なのはフレームバッファのみ
**&u(){そのほかのGEの機能}
-多様なテクスチャフォーマット、CLUT(Color LookUp Table)モード、DXT圧縮
-2Dか3Dのモードで演算
-オンボード3Dのクリッピング機能
-スプラインとベジエ面の演算
-モーフィングとスキニング
-ライティングとステンシルバッファ
-アルファブレンドと論理演算
**&u(){ディスプレイリスト}
画面描画はVRAMに直接アクセスできる。適度なパフォーマンスと3D描画のためにはディスプレイリストを使うとよい。
-ディスプレイリストの各エントリは32ビット幅で、8ビットのコマンド、24ビットのデータがある
-24ビットのデータは整数型で表現される。ポインタか切り捨てられた浮動小数点数が入る
-メインリストの中にサブリストを作ることができる
-カスタマイズされたvertexのタイプ、異なるサイズ(8ビットか16ビット固定のポイント、32ビット浮動小数点数)を明示的に利用できる
-それぞれのvertexのタイプに座標、色、ノーマルか太いかを含めることができる
-vertexはリストに直接明示するかインデックスをつけることができる
-浮動小数点は24ビットに変換されるときその精度を失う
**&u(){ディスプレイリスト}
ディスプレイリストはGEへの転送にDMAを用いるが、これはリストを作るときに重要な分岐を得る。
-リストは"Stall"アドレスで作られているときに転送できる
-リストはキャッシュされていないメモリ領域に書かれるか、あるいはリストを使う前にキャッシュデータが書き戻される必要がある
-リストはFINISHコマンドで終了されなければいけない
#image(fig004.jpg)
**&u(){PSPメモリマップ}
PSPのメインプロセッサがMMU(メモリマネジメントユニット)を操作していないとき、メモリセグメンテーションの機能がある
-カーネルモードとユーザーモードの仕様上、ユーザーアプリケーションがカーネルメモリに変更を加えることは防止される
-メインメモリの32Mバイトを分割し、4Mバイトのカーネル、4Mバイトの一時メモリ、24Mバイトのユーザスペースに割り当てている
-4Mバイトの一時メモリはシステムコールのみで使用される(FW 1.0の場合はいつでも使える)。
-デバイスはメモリスペースにマップされる
-仮想メモリシステムはない。カーネル権限を持っていればすべてのメモリエリアにアクセス可能
**&u(){PSPメモリマップ}
いくつかのキーになるアドレスはソースコード中で見かけるかもしれない
|先頭アドレス |サイズ |解説|
|0x00010000 |16K |スクラッチパッド|
|0x04000000 |2M |VRAM|
|0x44000000 |2M |VRAM (キャッシュされない)|
|0x08800000 |24M |メインRAM|
|0x48800000 |24M |メインRAM (キャッシュされない)|
|0x88000000 |4M |カーネルRAM|
----
*PSPソフトウェア
**&u(){コンパイラ}
PSPコンパイラはPS2開発ツールにおける経験を継承する形で開発された。
-GCC 4.0.2をベースにしている
-CとC++の開発環境がある
-VFPUを含むALLEGREXの命令セットのサポート
-標準libcとstdc++関数(stdioやiostreamなど)をサポートしたNewlibのポート
-UNIXライクなシステム用として設計されており、Windows上で使うためにはCygwinかMingWが必要(DevkitPRO)
**&u(){PSPDSK}
フリーのソフトウェアデベロップメントキットはPSPライブラリへのアクセスを提供する。多くのライブラリとツールを使って簡単に開発できる
-ユーザーとカーネルのライブラリはヘッダをインポートする
-リバースエンジニアリングされたlibGUとlibGUM
-シンプルなPCMオーディオライブラリ
-デバッグをサポートするためのライブラリとユーテリティ関数
-カスタムエクスポートテーブルを作るためのツール。ELFファイルのポストプロセスと実行形式をPRXにするためのコンバータ
-カスタムEBOOT.PBPのための圧縮・解凍ツール
-PSPとSDKを使うための60以上のサンプルコード
**&u(){そのほかの開発環境}
今のところCとC++はPSPソフトウェア開発の主な言語である。これでソースコードを書くことに制限はない。
***そのほかのオプション:
-LUA(LUAPlayer)- PSPのためにポートされたLUA。分かりやすいインターフェイスとライブラリ
-Python - PSPのためにポートされたPythonと拡張された部分。
-Flash - FW 2.7以降のPSPで稼動する機能限定版フラッシュプレイヤー
**&u(){カーネルモジュール}
PSPカーネルは多くのモジュールで構成されている。モノリシックカーネルではない。ユーザーアプリケーションはよく出来たモジュールである。
-モジュールはいつでもロードでき実行できる
-モジュールをロード後にロケーションの修正かあるいはリロケートができる
-ほかのモジュールから関数のエクスポートとインポートができる
-カーネルには、ほかのOSにあるようなプロセスという概念はない
**&u(){実行形式のファイル}
PSPはELF形式のモジュールをベースにしている
カーネルはネイティブコードで書かれた3つのタイプのファイルを実行できる
-ELF - ベーシックELF形式。ハードコードされたアドレスにリンクされる(一般的には0x8900000)
-PRX - カスタマイズされたELF形式。メモリ中のどこにでもリロケートできる
-~PSP - 暗号化されたELFかPRX
PSPSDKはELFとPRXを生成できるが暗号化された~PSPは生成できない。
**&u(){モジュールの情報を得る}
モジュールを正しくロードし、また実行を追跡するためには、モジュール情報のセクションが含まれていなければならない
***モジュール情報は以下の内容から成る:
-モジュール名
-モジュールの属性。カーネルモジュールなど
-関数テーブル
-バージョン
PSPSDKは十分役に立つ。ただソースコード中にPSP_MODULE_INFOと明記すればよい。
**&u(){インポートとエクスポートのライブラリ}
カーネルモジュールを正しく使えるようにするために、ユーザーモードアプリケーションへライブラリセットをエクスポートできる
インポート関数はファイルに断片化されている。これはSDKが提供する手段である。
-ユーザーからカーネルへの遷移はシステムコールゲートウェイを経由する
-ユーザーからユーザーへのコールは直接ジャンプすればよい
-カーネルライブラリのために、関数名の先頭32ビットがSHA1でハッシュされており、NIDで認証されている
-かつて多くの問題を生んだ経緯があって、ユーザーライブラリからカーネルモジュールへはリンクできないようになっている
エクスポートファイルを使って自分のコードからエクスポート関数が呼べるなら、通常それは必要ない
**&u(){モジュールのロード}
モジュールのロードと実行は理論的には非常にシンプルだが、注意すべき点もいくつかある。
-ユーザーモードでモジュールをロードできる場所には厳密な制限がある
-カーネルモードでライブラリにリンクしようとするのにはいくつか制限がある
-カーネルはELF形式のモジュールはロードできるが、プレーンテキストのPRXカーネルモジュールはロードできない。SDKにはこれらの制限事項へのパッチが用意されている
#image(fig005.jpg)
**&u(){カーネルのリセット}
カーネルはモジュールのための最適なリソース管理をしないので、PSPをクリーンな状態にするためにカーネルをリセットする
***通常2通りの手順がある:
+1 sceKernelLoadExec - カーネルをリブートして新しく実行する
+2 sceKernelExitGame - カーネルをリブートしてXMBのメニューに戻る
**&u(){システムメモリマネージャ}
おそらくこれがカーネルの最重要点である。リブート後に即ロードされる。
***以下のような感じだ:
-PSPのメモリアロケートを管理する
-メインメモリのセグメントを3つのパーテーションに分割する(カーネル/Media Engine/ユーザエリア)
-リソース管理(UIDテーブル)
-システムイベントハンドラ
-デバッグ機能の実装
*リソース管理
PSPのカーネルはシステムの状態を示すさまざまな情報を保持しており、それらは一貫したUIDテーブルで管理されている。
-UIDテーブルはツリー階層構造を保持している
-どのUIDにも名前が割り当てられており、名前は一意でなくてもよい
-どのカーネルモジュールもそれぞれのUIDタイプを明記できる
-どのエントリもUIDタイプの情報が入ったコントロールブロックを保持している
-カーネルは特定のタスクを実行するUIDをコールできる
-UIDの数値はランダムなものではなくエンコードされたアドレスである
> void *cntladdr = 0x88000000 + ((uid >> 5) & ˜ 3) ;
**&u(){スレッドの概要}
PSPのスレッドモデルはかなりシンプルである。PS2で使われていたIOPカーネルのものに似ている。
-スレッドは互いに強調する
-スレッドの優先度はスケジューリングにより、数値の低いほうが優先度が高い
-少量のスレッドローカルストレージ(TLS)。CPUのK0レジスタを参照する
-VFPUを使うためにはスレッドを作成し、PSP_THREAD_ATTR_VFPU属性を設定しなければならない
**&u(){スレッドの作成}
スレッドは実行の前にまず作成されなければならない。スレッドが一度作成されたら、実行できるようにUIDが与えられる。
#image(fig006.jpg)
**&u(){スレッドの状態}
#image(fig007.jpg)
画像:簡単な状態遷移のダイアグラム
**&u(){同期プリミティブ}
PSPのカーネルはスレッドの同期とコントロールの手段をいくつか提供する。すべての待機関数はオプションでタイムアウトが使える。
-排他制御
-- セマフォ
-- 割り込みコントロール
-メッセージング
-- イベントフラグ
-- メッセージボックスとパイプ
-- コールバック
-同期メモリプール
-- 変数のプール
-- 固定値のプール
-タイマー
-- アラーム
-- 仮想タイマー
**&u(){排他制御(mutex)}
セマフォは同期されたカウンタである。PSPにはmutexタイプはないが、セマフォはそれをシミュレートできる。
-sceKernelCreateSemaとsceKernelDeleteSemaでそれぞれ生成と削除ができる
-sceKernelWaitSemaとsceKernelSignalSemaでそれぞれロックとアンロックができる
割り込みの無効化はローコストのmutexで実現できる
#image(fig008.jpg)
**&u(){イベントフラグ}
イベントフラグは2つ以上のスレッド間で“イベント”をやりとりするもの
-1個のイベントフラグは32×1ビットのイベントから成る
-1個かそれ以上のビットは1回にまとめて待機する
-各ビットはオートかマニュアルでリセットモードにセットできる
#image(fig009.jpg)
**&u(){コールバック}
コールバックはカーネルが特別なイベントをスレッドコンテキストに送るために使われる。スレッドはコールバックの発生に応じるか、あるいは特定のwait関数を呼ばなければならない。コールバック関数の一般的な添え字はCBである。
#image(fig010.jpg)
**&u(){割り込み}
カーネルはユーザーモードで正確に割り込みするためのコールバックメカニズムを提供する
-割り込みコールバックは独立した状態のスレッドでコールされる
-呼ぶと待機状態になりそうな関数はコールしてはいけない
#image(fig011.jpg)
**&u(){スレッドプログラミングのヒント}
***スレッドプログラミングを簡単にするいくつかのヒント:
-過密なループはさせず、可能ならどこかで待機状態にさせるのがよい
-スレッドの優先度は慎重に選び、割り込みのあるスレッドを優先させる
-waitシステムコールの戻り値を常にチェックする
-待機関数を呼ぶときはデッドブロックを検知するためにタイムアウト機能を使うとよい
-スレッドが削除されて終了したのならリソースに問題がある
**&u(){ファイルIO}
カーネルは、有効なデバイスのファイルにアクセスするための標準的なIOサブシステムを提供する
-異なるタイプのIOデバイスを作成できる
-ファイルIOデバイスはプリフィクス(接頭辞)のついたデバイス名でアクセスできる
-デバイスの別名を設定できる
-ブロックデバイスはsceIoAssignを使ってファイルシステムデバイスにマウントできる
-POSIXに似たシステムコール(例:sceIoOpenでファイルオープン、sceIoWriteで書き込みなど)
ファイルIOデバイスのプリフィクスは2つの部分から成る
-デバイス名
-オプションでファイルシステムユニットの番号
***たとえばこんな感じで:
|ms0:| メモリスティックファイルシステム|
|host0:| ホストファイルシステム (PSPLinkエクステンション)|
|flash0:| フラッシュファイルシステム|
|irda0:| IrDAトランシーバのキャラクタドライバ|
|tty0:| テキスト端末キャラクタドライバ|
**&u(){グラフィックライブラリ}
VRAMにピクセルを直接描くような簡単な描画のためのライブラリ
-フレームバッファに16ビット(565, 5551, 4444)か32ビットカラーを設定できる
-sceDisplaySetFrameBufでフレームバッファを設定できる
-sceDisplayWaitVblankStartは現在のスレッドを次のvblank割り込みまで待機状態にし、同期を有効にしたりほかのスレッドが実行されるのを可能にする
#image(fig012.jpg)
**&u(){グラフィックライブラリ}
libGUはPSPのハードウェアアクセラレータによる描画の主要な開発手段である。
これはリバースエンジニアリングされたオフィシャルライブラリをベースにしている。
-libGUはGraphic Engineとディスプレイハードウェアを回避するためのラッパーの役目をする
-自動的にディスプレイリストを管理しオンラインとオフラインのリストを作成できる
-フレームバッファとディスプレイの切り替えを設定できる
libGUMはlibGUでマトリクス操作を直接扱うための補助ライブラリである
-浮動小数点とVFPUによる演算
-GLのようにマトリクスのスタックを保持する
ライブラリは図形マトリクスを設定するためのユーティリティ関数を提供する。図の回転など
PSPで画面描画する方法はLibGUとlibGUMだけではない。
***ほかにも重要なライブラリが利用可能:
-PSPGLはGLスタイルのインターフェイスを持った、ハードウェアアクセラレータのためのライブラリである
-SDLは2Dグラフィックで有用なライブラリである
***ほかにもポートされた画面描画のためのライブラリがある:
-libPNGはPNGデコード用
-libJPEGはJPEGデコード用
-FreeTypeとTrueTypeFont用ライブラリもある
**&u(){オーディオライブラリ}
PSPのオーディオハードウェアにはエクスポートされたライブラリがいくつかある。Media Engineとの組み合わせによりほとんどCPUを使わずにatrac3オーディオを出力できる。
-PSPSDKにはシンプルなストリーミングオーディオライブラリがある。コールバックは次のデータが要求されたときに発動する
-カーネルはatrac3デコーダは利用できる。atrac3のエンコーダは今のところWindowsのみで利用可能である
-libmadのポートとMP3と、そしてOgg/Vorbisデコードのためのlibogg/libvorbisがそれぞれ用意されている
-モジュールプレイバックのためのmikmodのポート。fmodのポートは商用のみしかない
**&u(){ジョイパッド}
ジョイパッドを使うのは簡単で、直接的にではなくてもデモはしやすくデバッグもしやすい
-すべてのデジタルボタンはビットマスクにマッピングされている
-アナログスティックは2つの符号なし8ビットにマッピングされている
#image(fig013.jpg)
**&u(){セキュリティ対策とは?}
PSPの開発者は非公式なコードがシステムを乗っ取るのを阻止するためにいくつかのセキュリティ対策を実装している
-ハードウェア暗号化エンジン
-連続した認証付きブートプロセス
-ゲーム中のバグを緩和するための、カーネルモードとユーザモードの分離
-カーネルはモジュールのタイプによって、ロードの可否とどこからロードされるかについての制限を与える
-実行ファイルとブートリストをカスタム暗号化/署名するラッパのフォーマット
**&u(){PSPのブートプロセス}
書き換えられたカーネルがフラッシュROMからロードされることは可能なのだが、開発者は一連の認証でカーネルの書き換えを阻止しようとしている
-カーネルは暗号化されたフラッシュROMに置かれている
-カーネルのブートは以下の通り:
--1. ブートストラップがフラッシュからステージ1のIPLを復号化する
--2. ステージ1のIPLはメモリ上のステージ2ローダーを復号化する
--3. ステージ2のローダーはカーネルをフラッシュからロードして復号化する
-ステージ2のローダーはいくつかの重要なモジュールを実行前にチェックする(FW 1.5+のみ)
-スクラッチからカスタムファームウェアを作るのでない限り(デベロップは)難しい
**&u(){スレッドの保護}
スレッドには特権レベルがあり、それはカーネルモードかユーザーモードのどちらで実行できるかに関わる。これはカーネルメモリをユーザーアプリケーションから保護する役割がある。
-新しいスレッドを生成するときに特権レベルを拡大することはできない
-スレッドプリミティブは自分を生成した親の権限を継承する
-同じ特権レベルにないとスレッドの列挙やスレッドプリミティブを使うことはできない
-分割されたスタックがシステムコールによって遷移されているとき、アタックやデータの漏洩を防止する
-すべてのカーネル関数は引数のポインタをスレッドの特権レベルでチェックする
**&u(){ユーザアプリケーションの実行形式をロードする}
特権レベルが異なればモジュールのロードも異なる。カーネルモードではチェックが入ったりする。
***モジュールがどのようにロードされるかの例:
-1. 実行形式のチェック(ユーザーモード、カーネルモード、暗号化コード、非暗号化など)
-2. そのモードで許可されている特定デバイスからロードされたかチェック(UMDからのロードはユーザーモードでのみ可能)
-3. 実行ファイルをメモリにロード(もしくは復号化して)する
-4 モジュールを正しいタイプのスレッドで実行する(例えばカーネルモジュールはカーネルモードで)
**&u(){本当にセキュアなのか?}
セキュリティの程度はその脆弱性と同じくらいだ
-PSPの脆弱性の連鎖はソフトウェアにあり、唯一の保護機能はカーネルモードにアクセスできない
-セキュリティは曖昧になってしまっている(訳注:obscurity、ダジャレだと思う)
-システムを完全にコントロールするにはPSPが実行する2つのソフトウェアの欠陥が必要である
-- 1 ゲームをエクスプロイトして任意のコードを実行できる(GTAとか)。ユーザーモードコントロール。
-- 2 カーネルエクスプロイトはすべてのシステムをコントロールできる(FW 2.5と2.6のLoadExecバグ)
-FW 1.0と1.5はまったくセキュリティ対策がされておらず、カーネルモードで直接ELFを実行できる。
----
*デバッグ
**&u(){デバッグ}
CかC++でソースコードを書いているとmakeやデバッグ中にバグが這い出るのはまったく避けようがない
コンシューマ機器であるがゆえに本当の意味で直接デバッグできる手法はない
-例外ハンドラのデフォルトの動作はループある。そのうち電源オフになってしまう
-デバック時のテキストを即座に画面に出力する手段がない
-もちろんすべてがダメというわけじゃない。バカ高いソニーのデベロップメントキットでなくてもデバッグする方法はある。
**&u(){ブラインドデバッグ}
当初、唯一の選択肢は手探り状態でデバッグすることだった。アプリケーションをビルドし、PSPにコピーして実行する。
SDKにはこのプロセスを助けるためのいくつかの機能がある(FW 1.0と1.5用)
-オンスクリーンとSIOで文字を出力する
-Basic SIOベースのGDBスタブ
-特定の例外ハンドラを追加する関数
-スタックトレースとスレッドの列挙のための関数群
厳密には正しい開発手法ではないが、すべてのアプリケーションはメモリスティックに配置されなければならない。FW 2.00以降だけかもしれないが。
**&u(){PSP Inside}
開発とハッキングのためのツール。Hitmen作。
#image(fig014.jpg)
画像:PSP Insideの操作
-PSPの実行状態を表示する
-命令をリアルタイムで逆アセンブルできる
-メモリスティックかフラッシュなどから新しいモジュールをロードできる
**&u(){PSPLink}
PSPLinkはPSP上でテキスト端末環境を提供する開発ツールである
***提供される機能のいくつかを示す:
-SIOとWiFiとUSBを経由して端末からアクセスできる
-USBとhost:deviceからPCのファイルシステムにアクセスできる
-あらかじめ設定されたtty(stdinとstdout、stderr)
-ソフトウェアのバグを捕まえる例外ハンドラ
-WiFiとUSB経由のリモートでGDBスタブを使いソースレベルでデバッグできる
-リアルタイムでカーネルを監視(スレッド、モジュールリスト、メモリダンプとパッチ)
**&u(){例外}
回避できないクラッシュが発生する場合、PSPLinkかPSP Insideを使えばCPU例外を分離できる。この情報を元にクラッシュする場所を特定して修正できる。
-アプリケーションをビルドするときコンパイラに-gオプションを指定しておけばデバッグ情報を有効にできる
-例外の発生にはそれがなぜ起こるかの要因を示す兆候がある
-例外はEPCレジスタも出力するが、これは例外が発生したアドレスを示す
-EPCの値をpsp-addr2lineに渡すと実行形式からソースの行番号を得られる
-例外が出たら以下を試してさらにGDBを使うとよい
> psp−addr2line −e program.elf 0x08900340
> main.c : 68
**&u(){リソースとリファレンス}
PSPプログラミングに役立ついくつかの情報源
-http://www.pspdev.org/ - Toolchainとsubversionによるアーカイブ
-http://psp.jim.sh/pspsdk-doc - PSPSDKドキュメントのオンライン版
-FreeNode IRC(irc.freenode.net)の#pspdevチャンネル - ここに来ればヘルプしてやれると思う、たぶん
----
表示オプション
横に並べて表示:
変化行の前後のみ表示: