As some people know pspsdk has had limited prx building for a while now, now with a major overhaul and integration into the normal build system I felt it was time to explain a bit about how to build prxes in pspsdk, some things to bear in mind and some information on using the tools.
For people who are too lazy to read a long post, this is a summary of the _improvements_ in the new build system.
- - Now links with the pspsdk crt0. You no longer need to manually setup your initial thread or parse the arguments. Just place a main function in your source code and it will just work. If you don't want an initial thread (e.g. in a pure export library) then you can specify the PSP_NO_CREATE_MAIN_THREAD() in your source, main will then be called from the module loader thread, just don't sleep in it :P.
- No longer need to specify a different template makefile to build a prx, just set BUILD_PRX to 1 in your makefile and it will do everything necessary.
- psp-prxgen now removes undefined re-locations, this was a bug which should have been fixed long ago ;P
- No longer need to write an export file if you are not interested exporting anything other than the default module_start and module_info.
- Sort of related, added a PSP_MAIN_THREAD_NAME(name) definition so you can change the default thread name. Not really required but makes it easier to see what modules are what in the list.
- Oh and malloc should now work, you will need to set PSP_HEAP_SIZE_KB(size_kb) though as prxes have a default heap of 64kb if you need to use more than that. It should be noted on this though that if you are running the prx from a _normal_ app which also uses malloc you dont actually have any memory left for the prx to allocate.
-= And so we begin =-
First off to use some of this you need to update both pspsdk and newlib to the very latest versions.
For a simple example how do you build a prx in pspsdk? If you have got a pretty basic application (ideally a user mode one) you can build a prx by simply specifying BUILD_PRX=1 in your makefile.
Rebuild and if all went well you will get a .elf and a .prx file. The .elf should only be used for extracting debugging information, it almost certainly wont run on the psp. Copy your prx to your psp and run using your favourite method (this is possibly more complicated than it sounds of course :P).
To make a kernel mode prx just specify 0x1000 in your PSP_MODULE_INFO attribute. The sdk will worry about the rest for you.
Before going further some other useful new makefile definitions.
USE_KERNEL_LIBS=[1,0] - Only link in kernel mode libraries. If you are building a kernel mode prx this is highly recommended. You can also use it for normal apps but it is inadvisable. The reason for this is the default build mode will link in user libraries before kernel libraries where it can, this allows you to run 100% native kernel mode, eliminating the syscalls for abit extra speed. It should be noted however that you can use user mode libs as well, but you will need to specify them manually.
USE_KERNEL_LIBC=[1,0] - This is a counterpart to USE_PSPSDK_LIBC, it will use the kernel's very limited libc instead of using newlib or pspsdk libc. Use this only if you are making very limited usage of libc and want to go for size.
PRX_EXPORTS=file.exp - This specifies the exports file to use for your prx. It must have an exp extension for the default build rules to work. NOTE: This has superseded the old method where you specified it in the OBJS. For the file format see below on the section about psp-build-exports.
-= The tools =-
There are two tools in pspsdk in support of prxes. One you should never need to worry about but might as well explain it as I go.
psp-prxgen - This is the guts of the prx generation, without it you are screwed. It takes a specially linked elf file (you cannot run it on an elf built using the normal method) and converts it to a valid prx file. That's it, the make system will handle it for you, it would only be called outside of this in order to diagnose problems (run with the -v flag to dump alot of debug output).
psp-build-exports - This tool takes a text file and builds a .c file containing code to establish the exports for the prx. Even if you are not exporting any functions it is still important because standard executables need at least two exports in order to run.
The text file consists of a list of commands which are read sequentially in order to make your exports, the testprx sample has a simple one to demonstrate usage, but I will reproduce it here.
This should be placed in a file named something like exports.exp, and referenced in your makefile with the PRX_EXPORTS directive. If you only want to export module_start and module_info you can use the default exports table which will get linked in if you don't specify PRX_EXPORTS.PSP_BEGIN_EXPORTS
PSP_EXPORT_START(syslib, 0, 0x8000)
PSP_EXPORT_FUNC(module_start)
PSP_EXPORT_VAR(module_info)
PSP_EXPORT_END
PSP_EXPORT_START(MyLib, 0, 0x0001)
PSP_EXPORT_FUNC(getModuleInfo)
PSP_EXPORT_END
PSP_END_EXPORTS
A description of the commands.
- - PSP_BEGIN_EXPORTS - This just starts the export tables, it is really just a place marker.
- PSP_EXPORT_START(name, ver, attributes) - Begin an export library with the specified name, version and attributes. When defining the default export the prx does not use a name (it sets the pointer to 0) so the made up name of syslib is used for this as a place marker. The version is major/minor arrangement, one in each byte of this 16bit value. It is important as you can use this to ensure you can only link to a certain library version. For our purposes it isn't really important. The attributes are not quite clear, 0x8000 is a special case for syslib. For exporting a set of functions from a library to be used by another prx (i.e. user to user) of the same type specify 0x0001. For exporting functions from a kernel prx to be used by user and kernel mode applications through a syscall gateway specify 0x4001.
- PSP_EXPORT_FUNC(name) - Export a function. The SHA1 nid will be autogenerated from the name you give it.
- PSP_EXPORT_VAR(name) - Same as above but for a variable.
- PSP_EXPORT_FUNC_NID(name, nid) - Export a function with a specific name and SHA1 hash value. This is only needed if you either want to export a function with a randomish hash value or you don't know the
real name of the function to generate the _proper_ nid.
- PSP_EXPORT_VAR_NID(name, nid) - Same as above but for variables.
- PSP_EXPORT_FUNC_HASH(name)/PSP_EXPORT_VAR_HASH(name) - Synonyms for PSP_EXPORT_FUNC/VAR, there due to legacy :P
- PSP_EXPORT_END - End the current export library.
- PSP_END_EXPORTS - Place holder for the end of all the exports.
While in normal operation you shouldn't need to call psp-build-exports manually there is one important time when you do. There would be no point in exporting functions from your module if nothing can import it. This is where the --build-stubs and --build-stubs-new options come into play. By running the tool with one or other of these options and providing the name of the export text file each of the libraries in the file (barring syslib) will be written to a file of the form libname.S. These can then be included in your project which needs the functions imported. The difference between them is --build-stubs generates the old style assembly files which only have to be included in your project, --build-stubs-new generates the new style files which need to be specially built to get them to work, the benefit to using this is along with psp-fixup-imports it eliminates unused functions from the modules imports (saving space). Unless you are a glutton for punishment or you are adding these to pspsdk (where the usage of this form is close the mandatory) it probably isn't worth the effort :)
Okay that's enough for one day. Have fun coding.