-
Notifications
You must be signed in to change notification settings - Fork 10
Add advanced techniques documentation on PRX Module #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
5573bd6
74d88db
1b44b98
a8932ac
46c2330
bda133f
889e703
e14bd0c
67b61c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| --- | ||
| title: Advanced Techniques | ||
| layout: home | ||
| nav_order: 7 | ||
| --- | ||
|
|
||
| # Advanced Techniques | ||
| {: .fs-8 .fw-700 .text-center } | ||
|
|
||
| Here are links to more in depth explanations of specific more advanced PSP programming topics. | ||
|
|
||
| - [Creating PRX Modules](techniques/prx_modules.html) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| --- | ||
| title: Contributing | ||
| layout: home | ||
| nav_order: 8 | ||
| nav_order: 9 | ||
| --- | ||
|
|
||
| # Contributing | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| --- | ||
| title: Downloads | ||
| layout: home | ||
| nav_order: 7 | ||
| nav_order: 8 | ||
| --- | ||
|
|
||
| # Downloads | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| --- | ||
| title: Creating PRX Modules | ||
| layout: home | ||
| nav_exclude: true | ||
| --- | ||
| # Creating PRX Modules | ||
| {: .fs-8 .fw-700 .text-center } | ||
|
|
||
| ## Basic Concepts | ||
| {: .fs-6 .fw-700 } | ||
|
|
||
| Multiple modules can be loaded at the same time on the PSP. The main module is the one which is launched when starting a homebrew, but additional modules can be loaded and unloaded at runtime. These additional modules are called PRX modules and have the `.prx` file extension. They are similar to DLLs on Windows or shared objects on Linux. | ||
|
|
||
| ### Stub Library | ||
| {: .fs-4 .fw-700 } | ||
|
|
||
| To load a PRX module, the main module needs to know which functions it can call within that module. This is done by creating a stub library, a static library containing the signatures of the functions in the PRX module. The main module links against this stub library and calls the functions in the PRX module just like regular functions. | ||
|
|
||
| The stub library only contains the Module Name, the NID (hash of the function name) of the function and a stub implementation of the function. | ||
|
|
||
| ## Basic Workflow | ||
| {: .fs-6 .fw-700 } | ||
|
|
||
| > PRX modules can act as a Resident Library, which exposes their own functions inside and export them for other modules to use. | ||
|
|
||
| The core steps for creating a PRX module that others can load are as follows: | ||
| 1. PRX module developers declare which functions are accessible from external modules by writing an **Export Table**. | ||
| 2. Users can establish a reference to these external functions in their modules by using an **Import Table**, which is generated from the PRX module's export table and can be compiled into a **Stub Library**. | ||
|
|
||
| ## Writing Code and Export Table | ||
| {: .fs-6 .fw-700 } | ||
|
|
||
| ### Writing Code | ||
| {: .fs-4 .fw-700 } | ||
|
|
||
| Writing code for a PRX module is largely identical to writing code for a regular module. The primary difference is the entry point. | ||
|
|
||
| Instead of writing a standard `main` function, you write a `module_start` function (called when the module is loaded) and a `module_stop` function (called when unloaded). These functions should return 0 on success and a negative value on failure. | ||
|
|
||
| > Make sure to use at least one function from other modules (for example, `sceIoOpen`), otherwise `psp-fixup-imports` will complain `Error, no .lib.stub section found`. | ||
|
|
||
| Also it is not recommended to use any Standard C Library functions in your code. The `newlib` implementation in the PSPSDK often causes issues when used in PRX modules, and it is generally better to avoid it altogether. Instead, you can use the PSPSDK's own functions with `sce` prefix for memory management, string manipulation, etc. | ||
|
|
||
| Due to similar reasons, C++ is also not recommended for PRX modules. | ||
|
|
||
| ```c | ||
| #include <pspuser.h> | ||
|
|
||
| // Module Name, Attributes, Major Version, Minor Version | ||
| // This module is a user module | ||
| PSP_MODULE_INFO("SamplePlugin", PSP_MODULE_USER, 1, 0); | ||
| PSP_NO_CREATE_MAIN_THREAD(); | ||
|
|
||
| int module_start() { | ||
| SceUID fd = sceIoOpen("log.txt", PSP_O_WRONLY | PSP_O_CREAT | PSP_O_APPEND, 0777); | ||
| if (fd >= 0) { | ||
| sceIoWrite(fd, "Hello World!", 12); | ||
| sceIoClose(fd); | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| int module_stop() { | ||
| SceUID fd = sceIoOpen("log.txt", PSP_O_WRONLY | PSP_O_CREAT | PSP_O_APPEND, 0777); | ||
| if (fd >= 0) { | ||
| sceIoWrite(fd, "Goodbye World!", 14); | ||
| sceIoClose(fd); | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| // This function will be exported and can be called from other modules | ||
| int sum(int a, int b) { | ||
| return a + b; | ||
| } | ||
| ``` | ||
|
|
||
| ### Write the Export Table | ||
| {: .fs-4 .fw-700 } | ||
|
|
||
| Create a file with an `.exp` extension. It is used to declare which functions are accessible from external modules. The format is as follows: | ||
|
|
||
| > Guidelines | ||
| > 1. `syslib` is an essential keyword that must be included in the export table. | ||
| > 2. It is generally not recommended to export global variables. | ||
|
|
||
| Here is an example export table: | ||
|
|
||
| ``` | ||
| # Define the exports for the prx | ||
| PSP_BEGIN_EXPORTS | ||
|
|
||
| # These lines are mandatory | ||
| PSP_EXPORT_START(syslib, 0, 0x8000) | ||
| PSP_EXPORT_FUNC(module_start) | ||
| PSP_EXPORT_FUNC(module_stop) | ||
| PSP_EXPORT_VAR(module_info) | ||
| PSP_EXPORT_END | ||
|
|
||
| # Export our function | ||
| # 'MyLib' is the name of the library, it doesn't have to match the module name | ||
| PSP_EXPORT_START(MyLib, 0, 0x0001) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it would be good to specify where MyLib comes from. |
||
| PSP_EXPORT_FUNC_HASH(sum) | ||
| PSP_EXPORT_END | ||
|
|
||
| PSP_END_EXPORTS | ||
| ``` | ||
|
|
||
|
sharkwouter marked this conversation as resolved.
|
||
| ### Compile the PRX Module | ||
| {: .fs-4 .fw-700 } | ||
|
|
||
| If you are using Makefiles, you can add the following lines to your Makefile to compile the PRX module: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to also add instructions for CMake? We have an example here: https://psp-re.github.io/blog/nid-cracking/ Since we mostly use CMake in our examples on the website.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pretty sure yes. CMake can do that. I choose Makefile because most existing projects still use them. I think I will provide 2 versions if possible. |
||
|
|
||
| ```makefile | ||
| # Define to build this as a prx | ||
| BUILD_PRX=1 | ||
| # Define the name of our custom exports | ||
| PRX_EXPORTS=exports.exp | ||
| ``` | ||
|
|
||
| When `PRX_EXPORTS` is defined, the build system calls `psp-build-exports -b` and automatically generates the code to compile into the PRX module. | ||
|
|
||
| > The `.rodata.sceResident` segment contains the exported NIDs and function addresses. | ||
| > | ||
| > The `.lib.ent` segment contains the module name and export table metadata, including a pointer to the export table in the `.rodata.sceResident` segment. | ||
| > | ||
| > You can inspect the generated code by running `psp-build-exports -b exports.exp` in your terminal. | ||
|
|
||
| Also, when building a PRX module, you should add the `-nostartfiles` flag to your `LDFLAGS` to prevent the linker from including the startup files, which are not needed and can cause issues in PRX modules. | ||
|
|
||
| ```makefile | ||
| LDFLAGS = -nostartfiles | ||
| ``` | ||
|
|
||
| The whole Makefile looks like this: | ||
|
|
||
| ```makefile | ||
| TARGET = SamplePlugin | ||
|
|
||
| OBJS = main.o | ||
|
|
||
| CFLAGS = -O2 -Wall | ||
| LDFLAGS = -nostartfiles | ||
|
|
||
| BUILD_PRX = 1 | ||
| PRX_EXPORTS = exports.exp | ||
|
|
||
| PSPSDK=$(shell psp-config --pspsdk-path) | ||
| include $(PSPSDK)/lib/build.mak | ||
| ``` | ||
|
|
||
| ### Generate the Stub Library | ||
| {: .fs-4 .fw-700 } | ||
|
|
||
| To generate the stub library, first run the following command in the terminal: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it might be good to specify in this section how this relates to the Makefile/CMake and building more. |
||
|
|
||
| ```sh | ||
| psp-build-exports -k exports.exp | ||
| ``` | ||
|
|
||
| This generates a stub file in `.S` assembly format. It's name is `MyLib.S` as the library name in the export table is `MyLib`. | ||
|
|
||
| ```c | ||
| .set noreorder | ||
|
|
||
| #include "pspimport.s" | ||
|
|
||
| // Build files | ||
| // MyLib_0000.o MyLib_0001.o | ||
|
|
||
| #ifdef F_MyLib_0000 | ||
| IMPORT_START "MyLib",0x00090000 | ||
| #endif | ||
| #ifdef F_MyLib_0001 | ||
| IMPORT_FUNC "MyLib",0x55F3DFE9,sum | ||
| #endif | ||
| ``` | ||
|
|
||
| This file can be compiled into a static library using `psp-gcc` followed by `psp-ar`, or distributed directly. When others want to use your PRX module, they can simply include this `.S` file as a source file. | ||
|
|
||
| ## Dynamically Loading PRX Modules | ||
| {: .fs-6 .fw-700 } | ||
|
|
||
| To dynamically load a PRX module, you can use the `sceKernelLoadModule` and `sceKernelStartModule` functions. | ||
|
|
||
| When building your programs, include the generated `.S` stub file (or the `.a` stub library) in your `OBJS` or `LIBS` for linking. | ||
|
|
||
| The generated `.S` file uses several macros along with the post-processing tool `psp-fixup-imports` to construct the import table. | ||
|
|
||
| ## Further Reading | ||
| {: .fs-6 .fw-700 } | ||
|
|
||
| - [PSP Modules, Exports, Imports and Patches](https://uofw.github.io/upspd/docs/software/ModuleTutorialv1.pdf): A short tutorial released in 2007 describing modules used in Sony’s PSP by Anissian. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to cover the coding limitations for prx modules as well? From what I understand using libraries in a module is difficult.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seems to be many restrictions. I haven't figured out the exact restrictions yet, only some suggestions.
It would be better if others in the channel can help verify them
But as for my observation, most problem comes from using standard C lib or libstdc++
Use only PSPSDK functions and writing the code in C without crt0 can avoid most problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I would still merge this if this is not yet included here now. It can be added later. Maybe having one sentence about the limitation would be good for now. I'll leave it up to you.