PRX

From Vita Development Wiki
Revision as of 08:26, 2 October 2024 by CelesteBlue (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

SceModuleInfo

It contains information on the module, and on its imports/exports.

Location

  • For non-stripped modules, SceModuleInfo structure is located in ".sceModuleInfo.rodata" memory block.
  • For ET_SCE_EXEC modules, when e_entry is not null, SceModuleInfo structure is located in text segment at offset e_entry. Else it is located in text segment (first LOAD segment) at offset Elf32_Phdr[text_seg_id].p_paddr - Elf32_Phdr[text_seg_id].p_offset.
  • For ET_SCE_RELEXEC modules, SceModuleInfo structure is located in the segment indexed by the upper two bits of e_entry of the ELF header. The structure is stored at the base offset of the segment plus the offset defined by the bottom 30 bits of e_entry.

Structure

Some fields are optional and can be set to zero. The other fields determine how this module is loaded and linked. All offset fields are formatted as follows: top 2 bits is an index to the segment to start at and bottom 30 bits is an offset from the segment start. Currently, the segment start index must match the segment that the module information structure is in.

Name Description
modattribute Attributes of the module. See #Module attribute
modversion Major version of the module (usually set to 1) followed by Minor version of the module (usually set to 1).
modname Name of the module. Not necessarily null-terminated string.
terminal Null-terminating character (0x00). Name was "terminal" since PSP.
infoversion SceModuleInfo version. Seen values are: 0 on PSP and PS3, 1 never seen, 2 on PS Vita FW 0.902, 3 on PS Vita FW 0.931.010, 4 and 5 never seen, 6 on PS Vita FWs 0.940-3.740.011.
gp_value / resreve global pointer value for MIPS, TOC address (address of .toc) for PowerPC, always 0 for ARM.
ent_top Offset to top of exports array
ent_end Offset to end of exports array
stub_top Offset to top of imports array
stub_end Offset to end of imports array
dbg_fingerprint It was wrongly named module NID. It is a sort of hash to ensure integrity and versioning.
tls_top Offset to top of the Thread Local Storage (TLS).
tls_filesz Certainly equals (tls_end - tls_top).
tls_memsz Certainly equals (tls_initialized_data_end - tls_top).
start_entry Offset to module_start function. -1 to disable.
stop_entry Offset to module_stop function. -1 to disable.
arm_exidx_top Offset to top of ARM EXIDX (optional)
arm_exidx_end Offset to end of ARM EXIDX (optional)
arm_extab_top Offset to top of ARM EXTAB (optional)
arm_extab_end Offset to end of ARM EXTAB (optional)
#define MODULE_NAME_MAX_LEN 27

typedef struct SceModuleInfo_common { // size is 0x20
  unsigned short modattribute;
  unsigned char modversion[2];
  char modname[26]; // MODULE_NAME_MAX_LEN - 1
  char terminal; // \0
  char infoversion;
} SceModuleInfo_common;

typedef struct _scemoduleinfo { // size is 0x34
  SceModuleInfo_common c; // c.infoversion must be 0
  Elf32_Addr gp_value;
  Elf32_Addr ent_top;
  Elf32_Addr ent_end;
  Elf32_Addr stub_top;
  Elf32_Addr stub_end;
} sceModuleInfo;

typedef struct _scemoduleinfo1 { // size is 0x40
  SceModuleInfo_common c; // c.infoversion must be 1
  Elf32_Addr gp_value;
  Elf32_Addr ent_top;
  Elf32_Addr ent_end;
  Elf32_Addr stub_top;
  Elf32_Addr stub_end;
  Elf32_Word dbg_fingerprint;
  Elf32_Addr start_entry;
  Elf32_Addr stop_entry;
} sceModuleInfo1;

typedef struct _scemoduleinfo_arm { // size is 0x48
  SceModuleInfo_common c; // c.infoversion must be 2
  Elf32_Addr resreve;
  Elf32_Addr ent_top;
  Elf32_Addr ent_end;
  Elf32_Addr stub_top;
  Elf32_Addr stub_end;
  Elf32_Word dbg_fingerprint;
  Elf32_Addr start_entry;
  Elf32_Addr stop_entry;
  Elf32_Addr arm_exidx_top;
  Elf32_Addr arm_exidx_end;
} sceModuleInfo_arm;

typedef struct _scemoduleinfo_arm_tls { // size is 0x54
  SceModuleInfo_common c; // c.infoversion must be 3
  Elf32_Addr resreve;
  Elf32_Addr ent_top;
  Elf32_Addr ent_end;
  Elf32_Addr stub_top;
  Elf32_Addr stub_end;
  Elf32_Word dbg_fingerprint;
  Elf32_Addr start_entry;
  Elf32_Addr stop_entry;
  Elf32_Addr arm_exidx_top;
  Elf32_Addr arm_exidx_end;
  Elf32_Addr tls_top;
  Elf32_Addr tls_filesz;
  Elf32_Addr tls_memsz;
} sceModuleInfo_arm_tls;

// Note: There is no sceModuleInfo format for ARM that has version 4 or 5 so these versions may be
// the sceModuleInfo_ppu32, sceModuleInfo_ppu64 or sceModuleInfo1_ppu32 formats.

typedef struct _scemoduleinfo_prx2arm { // size is 0x5C
  SceModuleInfo_common c; // c.infoversion must be 6
  Elf32_Addr resreve;
  Elf32_Addr ent_top;
  Elf32_Addr ent_end;
  Elf32_Addr stub_top;
  Elf32_Addr stub_end;
  Elf32_Word dbg_fingerprint;
  Elf32_Addr tls_top;
  Elf32_Addr tls_filesz;
  Elf32_Addr tls_memsz;
  Elf32_Addr start_entry;
  Elf32_Addr stop_entry;
  Elf32_Addr arm_exidx_top;
  Elf32_Addr arm_exidx_end;
  Elf32_Addr arm_extab_top;
  Elf32_Addr arm_extab_end;
} sceModuleInfo_prx2arm;

Module attribute

The attributes of a module (PSP, PS Vita, ?PS3 to check?). Bitwise OR'ed values from ::SceModuleAttribute and ::SceModulePrivilegeLevel. Since PS Vita SDK 0.940, SceModulePrivilegeLevel are not set anymore and unused in PS Vita OS.

  • Most usermode modules and a few kernel modules have it set to 0.
  • Most kernel modules have it set to 7.
  • Attribute 0x800 was seen in FW 0.902 cui_update_starter_module.self and FW 0.995 debug comicreader.elf. That attribute was set because these ELFs were compiled with an old PS Vita SDK (<= 0.930) that inherited privilege level attributes from the PSP SDK.
Value Name Comments
?0x20? SCE_MODULE_ATTR_CANT_SHARE
?0x10? SCE_MODULE_ATTR_CAN_RELOCATE
?0x8? SCE_MODULE_ATTR_CAN_RESTART
0x4 SCE_MODULE_ATTR_EXCLUSIVE_START Only one instance of the module can be started. If you want to start another version of that module, you have to stop the currently running version first.
0x2 SCE_MODULE_ATTR_EXCLUSIVE_LOAD Only one instance of the module can be loaded. If you want to load another version of that module, you have to unload the currently loaded version first.
0x1 SCE_MODULE_ATTR_CANT_STOP Resident module i.e. module that stays in memory til poweroff. Such a module cannot be unloaded.
0x0 SCE_MODULE_ATTR_NONE No module attributes.
/** 
 * Module type attributes. 
 */
enum SceModuleAttribute {
    SCE_MODULE_ATTR_NONE             = 0x0000,
    SCE_MODULE_ATTR_CANT_STOP        = 0x0001,
    SCE_MODULE_ATTR_EXCLUSIVE_LOAD   = 0x0002,
    SCE_MODULE_ATTR_EXCLUSIVE_START  = 0x0004,
};

/** 
 * Module Privilege Levels - These levels define the permissions a 
 * module can have.
 */
enum SceModulePrivilegeLevel {
    /** Lowest permission. */
    SCE_MODULE_USER                 = 0x0000,
    /** MS modeul. POPS/Demo. */
    SCE_MODULE_MS                   = 0x0200,
    /** USB WLAN module. Gamesharing */
    SCE_MODULE_USBWLAN              = 0x0400,
    /** Application module. */
    SCE_MODULE_APP                  = 0x0600,
    /** VSH module. */
    SCE_MODULE_VSH                  = 0x0800,
    /** Kernel module. Highest permission. */
    SCE_MODULE_KERNEL               = 0x1000,
    /** The module uses KIRK's memlmd resident library. */
    SCE_MODULE_KIRK_MEMLMD_LIB      = 0x2000,
    /** The module uses KIRK's semaphore resident library. */
    SCE_MODULE_KIRK_SEMAPHORE_LIB   = 0x4000
};

Modules imports-exports

Each module contains zero or more library exports and zero or more library imports. An exported library groups together related functions along with their NID and exports it for SceKernelModulemgr to link with a library import in another module.

Exports

An array of export entries defines all the libraries exported by the module.

Structure

Kernel modules that export syscalls (user accessible libraries) also get entries added to the syscall table. The syscall table is randomized on each boot so the same function will likely get a different syscall number assigned each time.

Name Description
structsize Size of the structure in bytes (usually 0x1C or 0x20)
auxattribute Unknown. Was added recently. "reserved1" in SDK 0.945, may be reserved since.
version Library version (usually 1)
attribute Library attributes bitflag
nfunc Number of exported functions
nvar Number of exported variables
ntlsvar Number of exported TLS variables
hashinfo Hash info (funcinfo + varinfo<<4). See Modules#Hash_Info.
hashinfotls TLS hash info. See Modules#Hash_Info.
reserved2 Reserved.
nidaltsets Unknown. Usually 0.
libname Pointer to library name. 0 for NONAME library.
libname_nid Library NID (=NID computed from library name)
nidtable Pointer to array of the exports' NIDs
addtable Pointer to array of the exports' addresses
typedef struct _scelibent_psp { // size is 0x10 or 0x14 | Unofficial name? 0x14 form may be sceKernelLibraryEntryTable
	Elf32_Addr libname; /* <libname> (0 if NONAME) */
	unsigned short version; /* <version> */
	unsigned short attribute; /* <attribute> */
	unsigned char size; /* struct size in dwords */
	unsigned char nvar; /* number of variables */
	unsigned short nfunc; /* number of functions */
	Elf32_Addr entry_table; /* <entry_table> addr (in .rodata.sceResident) */
	union {
		unsigned int unk_0x10; /* Not always present. Might be alias_table. ex: 0x20000 */
	};
} SceLibEntryTable_psp;

typedef struct _scelibent { //Not used on PSP2. Used for PSP when structsize == 0x14?
  Elf32_Addr libname;
  unsigned short version;
  unsigned short attribute;
  unsigned char structsize;
  unsigned char nvar;
  unsigned short nfunc;
  Elf32_Addr entrytable;
  unsigned short nvar2;
  unsigned char hashinfo;
  unsigned char nidaltsets;
} sceKernelLibraryEntryTable;


typedef struct _scelibent_ppu_common {
    unsigned char structsize;
    unsigned char reserved1[1]; //a.k.a. 'auxattribute'
    unsigned short version;
    unsigned short attribute;
    unsigned short nfunc;
    unsigned short nvar;
    unsigned short ntlsvar;
    unsigned char hashinfo;
    unsigned char hashinfotls;
    unsigned char reserved2[1];
    unsigned char nidaltsets;
} sceKernelLibraryEntryTable_ppu_common;

typedef struct _scelibent_ppu32 {
  sceKernelLibraryEntryTable_ppu_common c;
  Elf32_Addr libname;
  Elf32_Addr nidtable;
  Elf32_Addr addtable;
} sceKernelLibraryEntryTable_ppu32;

//Old name: SceLibEntryTable_1C. Used in PRX1 format.
typedef struct _scelibent_ppu32 sceKernelLibraryEntryTable_arm;

typedef struct _scelibent_ppu_common sceKernelLibraryEntryTable_prx2_common;
typedef struct _scelibent_prx2arm {
    sceKernelLibraryEntryTable_prx2_common c;
    Elf32_Word libname_nid;
    Elf32_Addr libname;
    Elf32_Addr nidtable;
    Elf32_Addr addtable;
} sceKernelLibraryEntryTable_prx2arm; //Old name: SceLibEntryTable_20. Used in PRX2 format.

Hash Info

Hash info is a number which depends of the number of exports. It can have values: 0, 2, 4 or 6.

It is related to NID Hash Table, which is still a mystery.

#define HASHINFO_LIMIT 0x10
int getHashInfo(uint16_t num) {
	if ((HASHINFO_LIMIT == 0) || (num < HASHINFO_LIMIT))
		return 0;
	else if (num < 0x40)
		return 2;
	else if (num < 0x100)
		return 4;
	else
		return 6;
}

int getHashInfo(SceLibEntryTable_common *table) {
	return getHashInfo(table->nfunc) + getHashInfo(table->nvar) << 4;
}

int getHashInfoTls(SceLibEntryTable_common *table) {
	return getHashInfo(table->ntls);
}

NONAME exports

Old name was "syslib", short name for "system library".

There is a special export entry that always shows up (even when the module exports no libraries) with attribute 0x8000 and NID 0x00000000 that exports the module_start, module_stop, module_exit functions for example.

The NIDs for these exports are common for all modules. See here for NID generation algorithm.

For PS Vita:

Name Type NID Definition
module_start Function 0x935CD196 int module_start(SceSize arglen, const void *argp);
module_stop Function 0x79F8E492 int module_stop(SceSize arglen, const void *argp);
module_exit Function 0x913482A9 int module_exit(SceSize arglen, const void *argp);
module_bootstart Function 0x5C424D40 int module_bootstart(SceSize arglen, const void *argp);
module_suspend Function 0xDD42FA37 int module_suspend(void); Supported in FW 0.990.030, unsupported as of firmware 3.65. Present in FW 0.990.030 SceKernelModulemgr.
module_proc_create Function 0xE640E30C SceInt32 module_proc_create(ScePID pid, void* pParam, void* pCommon); Supported in FW 0.931.010-0.990.030, unsupported as of firmware 3.65. Seemingly never used.
module_proc_exit Function 0x4F0EE5BD SceInt32 module_proc_exit(ScePID pid, void* pParam, void* pCommon); Supported in FW 0.931.010-0.990.030, unsupported as of firmware 3.65. Seemingly never used.
module_proc_kill Function 0xDF0212B9 SceInt32 module_proc_kill(ScePID pid, void* pParam, void* pCommon); Supported in FW 0.931.010-0.990.030, unsupported as of firmware 3.65. Seemingly never used.
module_info Variable 0x6C2224BA SceModuleInfo structure
module_proc_param Variable 0x70FBA1E7 SceProcessParam structure
module_sdk_version Variable 0x936C8A78 SceUInt32 variable - Used since at least System Software version 2.10.
module_dtrace_probes_info Variable 0x9318D9DD sdt_probes_info_t structure. Present in SceKernelThreadMgr, SceProcessmgr, SceDisplay. Reserved for kernel modules. Used by SceDeci4pDtracep.
module_dtrace_probes Variable 0x8CE938B1 An array of sdt_probes_info_t.count pointers to sdt_probedesc_t. Present in SceKernelThreadMgr, SceProcessmgr, SceDisplay. Reserved for kernel modules. Used by SceDeci4pDtracep.
sce_module_start_thread_parameter Variable 0x1A9822A4 Pointer to SceModuleThreadParameter. Defines the parameters used to create the thread that runs the module_start function. Reserved for usermode modules.
sce_module_stop_thread_parameter Variable 0xD20886EB Pointer to SceModuleThreadParameter. Defines the parameters used to create the thread that runs the module_stop function. Reserved for usermode modules.

For PSP:

Name Type NID Definition
module_start Function 0xD632ACDB int module_start(int arglen, const void *argp);
module_stop Function 0xCEE8593C int module_stop(int arglen, const void *argp);
module_bootstart Function 0xD3744BE0 int module_bootstart(int arglen, const void *argp);
module_reboot_before Function 0x2F064FA6 int module_reboot_before(void);
module_reboot_phase Function 0xADF12745 int module_reboot_phase(void)
module_info Variable 0xF01D73A7 SceModuleInfo
module_start_thread_parameter Variable 0x0F7C276C SceModuleThreadParameter
module_stop_thread_parameter Variable 0xCF0CC697 SceModuleThreadParameter
module_reboot_before_thread_parameter Variable 0xF4F4299D SceModuleThreadParameter
module_sdk_version Variable 0x11B97506 int
module_linked Variable 0x900DADE1
module_unlinked Variable 0x592743D8
# Temporary name was: SceModuleEntryThread
typedef struct SceModuleThreadParameter { // Size is 0x10 bytes on PSP, 0x14 on Vita
    /* The number of thread parameters. Must be 4 on Vita. */
    SceUInt32 numParams;
    /* The initial priority of the entry thread. Default value is SCE_KERNEL_DEFAULT_PRIORITY. */
    SceUInt32 initPriority;
    /* The stack size of the entry thread. Default value is SCE_KERNEL_THREAD_STACK_SIZE_DEFAULT_USER_MAIN (256KiB). */
    SceSize stackSize;
    /* The attributes of the entry thread. Always ignored on Vita. */
    SceUInt32 attr;
    /* The CPU affinity mask of the entry thread. Only present on Vita. Default value is SCE_KERNEL_THREAD_CPU_AFFINITY_MASK_DEFAULT. */
    SceInt32 cpuAffinityMask;
} SceModuleThreadParameter;

// Temporary names were: SceModuleStaticProbesInfo, SceModuleDTraceProbesInfo 
typedef struct sdt_probes_info { // size is 0x8 bytes on FWs 0.931.010-3.740.011
    unsigned int version; // Always 1
    unsigned int count; // Number of probes. ex: 1, 5, 7, 16
} sdt_probes_info_t;

typedef union {
	void (*handler0)(const struct sdt_probedesc *desc);
	void (*handler1)(const struct sdt_probedesc *desc, uintptr_t arg0);
	void (*handler2)(const struct sdt_probedesc *desc, uintptr_t arg0, uintptr_t arg1);
	void (*handler3)(const struct sdt_probedesc *desc, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2);
	void (*handler4)(const struct sdt_probedesc *desc, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
	void (*handler5)(const struct sdt_probedesc *desc, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4);
	void (*handler6)(const struct sdt_probedesc *desc, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5);
	void (*handler7)(const struct sdt_probedesc *desc, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6);
	void (*handler8)(const struct sdt_probedesc *desc, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6, uintptr_t arg7);
	void (*handler9)(const struct sdt_probedesc *desc, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6, uintptr_t arg7, uintptr_t arg8);
	void (*handler10)(const struct sdt_probedesc *desc, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6, uintptr_t arg7, uintptr_t arg8, uintptr_t arg9);
} sdpd_handler_t;

// Temporary names were: SceModuleStaticProbe, SceModuleDTraceProbe
typedef struct sdt_probedesc { // size is 0x28 on FWs 0.931.010-3.740.011
	unsigned int *sdpd_id; // probe ID
	const char *sdpd_provider; // name of provider
	const char *sdpd_name; // name of probe
	void *sdpd_offset; // instrumentation point (address)
	sdpd_handler_t *sdpd_handler_fn; // probe handler_fn function (NULL if disabled)
	void *sdpd_private; // probe private data
	void (*sdpd_create_fn)(const struct sdt_probedesc *desc); // probe create helper function (NULL if unused)
	void (*sdpd_enable_fn)(const struct sdt_probedesc *desc); // probe enable helper function (NULL if unused)
	void (*sdpd_disable_fn)(const struct sdt_probedesc *desc); // probe disable helper function (NULL f unused)
	void (*sdpd_destroy_fn)(const struct sdt_probedesc *desc); // probe destroy helper function (NULL if unused)
} sdt_probedesc_t;

typedef struct SceLibcParam { // size is 0x1C on FW 0.930, 0x30 on FWs 1.80-1.81, 0x38 on FWs 2.00-3.740.011
	SceSize size; // Size of this structure
	SceLibEntryTable_1C *_SceLibcParam_0001_ent_head; // Pointer to SceLibcParam library information
	SceSize *sceLibcHeapSize; // Pointer to heap size
	SceSize *__sceLibcHeapSizeDefault; // Pointer to heap size
	SceUInt32 *sceLibcHeapExtendedAlloc; // Pointer to flag to dynamically extend heap size
	SceUInt32 *sceLibcHeapDelayedAlloc; // Pointer to flag to allocate heap on first call to malloc
	SceUInt32 sdk_version; // SDK version
	SceUInt32 unk_1C; // ex: 9
	void *__sce_libc_alloc_replace; // Pointer to replacement functions for Libc memory allocation functions
	void *__sce_libcxx_alloc_replace; // Pointer to replacement functions for Libcxx (C++) memory allocation functions
	SceSize *heap_initial_size; // Pointer to dynamically allocated heap initial size
	SceUInt32 *heap_unit_1mb; // Pointer to flag to change alloc unit size from 64kB to 1MB
	union {
		struct {
			SceUInt32 *heap_detect_overrun; // Pointer to flag to detect heap buffer overruns
			void *__sce_libc_tls_alloc_replace; // Pointer to replacement functions for TLS memory allocation functions
		};
	};
} SceLibcParam;

typedef struct SceProcessParam { // size is 0x20 on FW 0.895, 0x2C on FW 0.931.010, 0x30 on FW 0.945, 0x34 on FW 3.60
	SceSize size; // Size of this structure
	SceUInt32 magic; // "PSP2"
	SceUInt32 version; // Version of this structure. Implies number of extended entries. ex: 1 on FW 0.895, 4 on FW 0.931.010, 5 on FW 0.945, 6 on FW 3.60
	SceUInt32 sdk_version; // ex: 0x00895000 on FW 0.895.000, 0x00931010 on FW 0.931.010
	union {
		struct {
			char *sceUserMainThreadName; // ex: "main_thread"
			SceInt32 sceUserMainThreadPriority; // ex: 0x20, 0xA0, 0x10000100
			SceUInt32 sceUserMainThreadStackSize; // ex: 256 * 1024, 1024 * 1024
			SceUInt32 sceUserMainThreadAttribute;
			char *sceProcessName;
			SceUInt32 sce_process_preload_disabled;
			SceUInt32 sceUserMainThreadCpuAffinityMask;
			SceLibcParam *sce_libcparam;
			SceUInt32 unk_0x30;
		};
	};
} SceProcessParam;

Imports

Each module can import any number of libraries from other modules by specifying the library NID to import along with a list of functions and variables NIDs to import. SceKernelModulemgr does the dynamic linking by overwriting the import thunk (with an absolute address jump if both modules are in the same domain (usermode or kernel), or a syscall sequence for a usermode module importing a kernel-exported library). Kernel modules cannot import usermode libraries.

Name Description
structsize Size of the structure (usually 0x24, 0x2C or 0x34)
version Library version (ex: 1, 5)
attribute Library attributes bitflag
nfunc Number of imported functions
nvar Number of imported variables
ntlsvar Number of imported TLS variables
reserved Reserved
libname_nid Library NID
libname Pointer to library name
sce_sdk_version Usually 0
func_nidtable Pointer to array of imported functions NIDs
func_table Pointer to array of imported functions thunks
var_nidtable Pointer to array of imported variables NIDs
var_table Pointer to array of pointers to imported variables' reftables
tls_nidtable Pointer to array of imported TLS variables NIDs
tls_table Pointer to array of pointers to imported TLS variables' reftables
typedef struct _scelibstub_psp { // size is 0x14 or 0x18
  Elf32_Addr libname; /* library name */
  unsigned short version; /* version */
  unsigned short attribute; /* attribute */
  unsigned char size; /* struct size */
  unsigned char nvar; /* number of variables */
  unsigned short nfunc; /* number of function stubs */
  Elf32_Addr nid_table; /* functions/variables NID table */
  Elf32_Addr stub_table; /* functions stub table */
  union {
    Elf32_Addr var_table; /* variables table */
  };
} SceLibStubTable_psp;

typedef struct _scelibstub { // size is 0x1C. Not used on PSP2. Maybe used on PSP.
  Elf32_Addr libname;
  unsigned short version;
  unsigned short attribute;
  unsigned char structsize;
  unsigned char nvar;
  unsigned short nfunc;
  Elf32_Addr nidtable;
  Elf32_Addr stubtable;
  Elf32_Addr vstubtable;
  unsigned short nvar2;
  unsigned char reverse1;
  unsigned char reverse2;
} sceKernelLibraryStubTable;

typedef struct _scelibstub_ppu_common {
  unsigned char structsize;
  unsigned char reserved1[1];
  unsigned short version;
  unsigned short attribute;
  unsigned short nfunc;
  unsigned short nvar;
  unsigned short ntlsvar;
  unsigned char reserved2[4];
} sceKernelLibraryStubTable_ppu_common;

typedef struct _scelibstub_ppu32 {
  sceKernelLibraryStubTable_ppu_common c;
  Elf32_Addr libname;
  Elf32_Addr func_nidtable;
  Elf32_Addr func_table;
  Elf32_Addr var_nidtable;
  Elf32_Addr var_table;
  Elf32_Addr tls_nidtable;
  Elf32_Addr tls_table;
} sceKernelLibraryStubTable_ppu32;

typedef struct _scelibstub_ppu32 sceKernelLibraryStubTable_arm; // Old name: SceLibStubTable_2C. Used in PRX1 format.

typedef struct _scelibstub_ppu_common sceKernelLibraryStubTable_prx2_common;

typedef struct _scelibstub_prx2arm { // size is 0x34 - Used in PRX2 format.
  sceKernelLibraryStubTable_prx2_common c;
  Elf32_Word libname_nid;
  Elf32_Addr libname;
  Elf32_Word sce_sdk_version;
  Elf32_Addr func_nidtable;
  Elf32_Addr func_table;
  Elf32_Addr var_nidtable;
  Elf32_Addr var_table;
  Elf32_Addr tls_nidtable;
  Elf32_Addr tls_table;
} sceKernelLibraryStubTable_prx2arm; // Old name: SceLibStubTable_34

typedef struct _scelibstub_prx2arm_new { // size is 0x24 - Used in PRX2 format. This is a guessed name.
  unsigned char structsize;
  unsigned char reserved1[1];
  unsigned short version;
  unsigned short attribute;
  unsigned short nfunc;
  unsigned short nvar;
  unsigned short ntlsvar;
  Elf32_Word libname_nid;
  Elf32_Addr libname;
  Elf32_Addr func_nidtable;
  Elf32_Addr func_table;
  Elf32_Addr var_nidtable;  // Variable + TLS variable NIDs (?)
  Elf32_Addr var_table;     // Variable + TLS variable entries (?)
} sceKernelLibraryStubTable_prx2arm_new; // Old name: SceLibStubTable_24

reftable (Variable/function-address imports)

Variable imports are handled differently from function imports. While the entry in the function import table points to an import thunk, the entry in the variable import table points to a so-called reftable (probably reference table). If an imported function is used for other purposes than a function call, a reftable can also be generated for this function.

For example, the following C code verifies if a loosely imported function has been successfully bound to before calling it:

//NOTE: this may work with non-loosely imported functions but is non-sensical:
//Modulemgr will never start a module if non-loose imports can't be bound, 
//so the check will always evaluate to true for such functions.
//(The exception is app modules (e.g. eboot.bin) since ALL their imports are considered loose)
if (&loose_import_func != NULL) { 
  loose_import_func();
}

There are two reftable formats corresponding to the two executable formats supported by the OS.

Prx1 format

Prx1 is the old format of PS vita executables. These executables use either the standard ELF types ET_EXEC and ET_REL, or the legacy 0xFFA5 (ET_SCE_ARMRELEXEC) as the value in the ELF header's e_type.

In this format, the reftable is simply an array of Prx1RefInfos terminated by a NULL entry.

//8 bytes
typedef struct Prx1RefInfo {
    //N.B. bit fields are in little-endian order
    //For example, reltype occupies bits 7-0 of the SceUInt32
    SceUInt32 reltype:8; //Relocation type (r_type)
    SceUInt32 addend:24; //Relocation addend (r_addend) - sign-extended to 32-bits
    void* P;             //Virtual address where the reference lies
} Prx1RefInfo;

typedef struct ScePrx1RefTable {
    Prx1RefInfo info[];
} ScePrx1RefTable;

//Sample code to walk Prx1 reftable
void walk_Prx1_reftable(ScePrx1RefTable* pTable) {
    unsigned* infoptr = (unsigned*)&reftable->info;
    while (*infoptr != 0) {
      Prx1RefInfo* info = (Prx1RefInfo*)infoptr;

      //...do something with info...

      infoptr += sizeof(Prx1RefInfo)/sizeof(unsigned);
    }
}
Prx2 format

Prx2 is the new format of PS Vita executables, in use since around PS Vita SDK 0.940. These executables use 0xFE00 (ET_SCE_EXEC) or 0xFE04 (ET_SCE_RELEXEC) as e_type.

In this format, a 32-bit value is prepended to the table and there are two possible forms for refinfo instead of a single one.

There are two revisions of the reftable that change the meaning of this 32-bit value (note that the size includes the 32-bit value in it):

Bit mask Header text
New firmwares
0xF0000000 Must be 0
0x0FFFFFF0 reftable size (in bytes)
0x0000000F Reftable version? Must be 0
Old firmwares
0xFFFF0000 Unused
0x0000FFF0 reftable size (in bytes)
0x0000000F Reftable version? Must be 0
//8 bytes
typedef struct Prx2RefInfoForm1 {
    SceUInt32 form:4;    //1
    SceUInt32 segment:4; //Segment where reference lies
    SceUInt32 reltype:8; //Relocation type (r_type)
    SceUInt32 addend:16; //Relocation addend (r_addend) - sign-extended to 32-bits
    SceUInt32 offset;    //Offset where reference lies in segment (r_offset)
} Prx2RefInfoForm1;

//12 bytes
typedef struct Prx2RefInfoForm2 {
    SceUInt32 form:4;    //2
    SceUInt32 segment:4; //Segment where reference lies
    SceUInt32 reltype:8; //Relocation type (r_type)
    SceUInt32 unused:16; //Unused (padding)
    SceInt32 addend;     //Relocation addend (r_addend)
    SceUInt32 offset;    //Offset where reference lies in segment (r_offset)
} Prx2RefInfoForm2;

typedef struct ScePrx2RefTable_old { //Used until firmware ?.??
	SceUInt32 version:4; //Must be 0
        SceUInt32 size:12;
        SceUInt32 unused:16;
	unsigned char info[/* size - sizeof(SceUInt32) */];
} ScePrx2RefTable_old;

typedef struct ScePrx2RefTable { //Used since firmware ?.??
	SceUInt32 version:4; //Must be 0
        SceUInt32 size:24;
        SceUInt32 unk0_28:4; //Must be 0
	unsigned char info[/* size - sizeof(SceUInt32) */];
} ScePrx2RefTable;

//Sample code to walk Prx2 reftable
void walk_Prx2_reftable(ScePrx2RefTable* pTable) {
  /*assert(pTable->unk0_28 == 0); //for new format only */
    assert(pTable->version == 0);
    unsigned size = pTable->size;

    unsigned* infoptr = (unsigned*)pTable->info;
    unsigned* maxptr  = (unsigned*)(pTable->info + size);
    while(infoptr < maxptr) {
        unsigned form = *infoptr & 0xF;
        unsigned infosize;
        switch(form) {
          case 1:
            infosize = sizeof(Prx2RefInfoForm1);
            Prx2RefInfoForm1* info = (Prx2RefInfoForm1*)infoptr;
            //...do something with info...
            break;
          case 2:
            infosize = sizeof(Prx2RefInfoForm2);
            Prx2RefInfoForm2* info = (Prx2RefInfoForm2*)infoptr;
            //...do something with info...
            break;
          default:
            assert(0);
        }
        infoptr += size/sizeof(unsigned);
    }
    assert(infoptr == maxptr);
}
Function reftables

To obtain the pointer to a function's associated reftable, use the following code:

void* get_function_reftable(SceLibStubTable_xxx* libstub, int function_index) {
    uintptr_t import_thunk = (uintptr_t)libstub->func_entry_table[function_index];

    //Import thunks are made of 3 ARM instructions (4 bytes each)
    //and a 4-byte pointer to the reftable (may be NULL)
    return *(void**)(import_thunk + 3 * 4);
}

Library attribute

Library attribute flags are ORed together and identified as follows:

Bit Name Comments
0x8000 ?MAIN_EXPORT? Set for main NONAME export.
0x4000 SYSCALL_EXPORT In kernel modules only. Allow syscall export to usermode.
0x2000 PLUGIN_LINK Lets the library's exports table be managed on-the-fly by the module itself. If used together with --plugin-header option, switches armlibgen.exe to some special mode, making it generate headers for use with SceLibKernel#sceKernelLinkFunctionTable. It also requires function prototypes to be defined in the EMD file. On PS3, it seems to indicate a non-PRX library (like "stdc" or "allocator") that comes from somewhere else (LV2?).
0x8 LOOSE_IMPORT ?Set when the library has the WEAK_EXPORT flag in its exporting module.?
0x4 NOLINK_EXPORT
0x2 WEAK_EXPORT ?kernel non-driver export?
0x1 AUTO_EXPORT Makes the library importable. Should be set unless it is the main export.

Relocations

See [1] for more information on how PS Vita relocations work.

Relocations are stored within the PT_SCE_RELA segment.

There are 10 relocation entry formats. It is determined by r_format (the first 4 bits of the relocation entry).

Warning! In PS Vita FWs <= 3.00, only relocation entry format 0 is available. Using any other relocation entry format gives error 0x8002D019.

// assuming LSB of bitfield is listed first
union {
	Elf32Word r_format: 4;

	struct {
		Elf32Word r_format : 4;
		Elf32Word r_symbol_segment : 4;
		Elf32Word r_type : 8;
		Elf32Word r_patch_segment : 4;
		Elf32Word r_type2 : 8;
		Elf32Word r_dist2 : 4;

		Elf32Word r_addend;

		Elf32Word r_offset;
	} r_entry_0;

	struct {
		Elf32Word r_format : 4;
		Elf32Word r_symbol_segment : 4;
		Elf32Word r_type : 8;
		Elf32Word r_patch_segment : 4;
		Elf32Word r_offset_lo : 12;

		Elf32Word r_offset_hi : 10;
		Elf32Word r_addend : 22;
	} r_entry_1;

	// Used by var import relocations
	struct {
		Elf32Word r_format : 4;
		Elf32Word r_symbol_segment : 4;
		Elf32Word r_type : 8;
		Elf32Word r_patch_segment : 2;
		Elf32Word r_unk : 2;
		Elf32Word r_pad : 4;

		Elf32Word r_offset;
	} r_entry_1_alt;

	struct {
		Elf32Word r_format : 4;
		Elf32Word r_symbol_segment : 4;
		Elf32Word r_type : 8;
		Elf32Word r_offset : 16;

		Elf32Word r_addend;
	} r_entry_2;

	struct {
		Elf32Word r_format : 4;
		Elf32Word r_symbol_segment : 4;
		Elf32Word r_ins_mode : 1; // ARM = 0, THUMB = 1
		Elf32Word r_offset : 18;
		Elf32Word r_dist2 : 5;

		Elf32Word r_addend : 22;
	} r_entry_3;

	struct {
		Elf32Word r_format : 4;
		Elf32Word r_offset : 23;
		Elf32Word r_dist2 : 5;
	} r_entry_4;

	struct {
		Elf32Word r_format : 4;
		Elf32Word r_dist1 : 9;
		Elf32Word r_dist2 : 5;
		Elf32Word r_dist3 : 9;
		Elf32Word r_dist4 : 5;
	} r_entry_5;

	struct {
		Elf32Word r_format : 4;
		Elf32Word r_offset : 28;
	} r_entry_6;

	struct {
		Elf32Word r_format : 4;
		// r_format 7 has 4 offsets, 7 bits each
		// r_format 8 has 7 offsets, 4 bits each
		// r_format 9 has 14 offsets, 2 bits each
		Elf32Word offsets : 28;
	} r_entry_7_8_9;
} SceRel;

Format 0

Start End Description
0 3 Entry format. Set to 0.
4 7 Symbol segment index
8 15 Relocation type
16 19 Patch segment index
20 26 Second relocation type
27 31 Distance from offset (used for second relocation)
32 63 Addend
64 95 Offset

Format 1

Start End Description
0 3 Entry format. Set to 1.
4 7 Symbol segment index
8 15 Relocation type
16 19 Patch segment index
20 41 Offset
42 63 Addend

Format 2

Start End Description
0 3 Entry format. Set to 2.
4 7 Symbol segment index
8 15 Relocation type
16 31 Offset
32 64 Addend

Format 3

Start End Description
0 3 Entry format. Set to 3
4 7 Symbol segment index
8 8 Mode (ARM = 0, THUMB = 1)
9 26 Offset
27 31 Distance from offset
32 64 Addend

Format 4

Start End Description
0 3 Entry format. Set to 4.
4 26 Offset
27 31 Distance from offset

Format 5

Start End Description
0 3 Entry format. Set to 5.
4 12 Distance 1
13 17 Distance 2
18 26 Distance 3
27 31 Distance 4

Format 6

Start End Description
0 3 Entry format. Set to 6.
4 31 Offset

Format 7

Start End Description
0 3 Entry format. Set to 7.
4 10 Offset 1
11 17 Offset 2
18 24 Offset 3
25 31 Offset 4

Format 8

Start End Description
0 3 Entry format. Set to 8.
4 7 Offset 1
8 11 Offset 2
12 15 Offset 3
16 19 Offset 4
20 23 Offset 5
24 27 Offset 6
28 31 Offset 7

Format 9

Start End Description
0 3 Entry format. Set to 9.
4 5 Offset 1
6 7 Offset 2
8 9 Offset 3
10 11 Offset 4
12 13 Offset 5
14 15 Offset 6
16 17 Offset 7
18 19 Offset 8
20 21 Offset 9
22 23 Offset 10
24 25 Offset 11
26 27 Offset 12
28 29 Offset 13
30 31 Offset 14

Supported Relocation Codes (as of FW 3.60)

Code Name Description
0 R_ARM_NONE No relocation
2 R_ARM_ABS32 Direct 32-bits address
3 R_ARM_REL32 N/A
10 R_ARM_THM_CALL bl/blx on Thumb mode
28 R_ARM_CALL bl/blx on ARM mode
29 R_ARM_JUMP24 N/A
38 R_ARM_TARGET1 same as R_ARM_ABS32
40 R_ARM_V4BX same as R_ARM_NONE
41 R_ARM_TARGET2 same as R_ARM_REL32
42 R_ARM_PREL31 N/A
43 R_ARM_MOVW_ABS_NC movw on ARM mode
44 R_ARM_MOVT_ABS movt on ARM mode
47 R_ARM_THM_MOVW_ABS_NC movw on Thumb mode
48 R_ARM_THM_MOVT_ABS movt on Thumb mode
255 R_ARM_RBASE Ignored

Tools

Tools for IDA PRO

Tools for Ghidra

Tools for Binary Ninja