Skip to content

Commit b12c352

Browse files
committed
payload args: optional pre-resolved API table (v2 extension)
Launchers that cannot rely on the payload's own dlsym resolver (syscall 0x24f is refused for a WebProcess, kexp would trap) now pass a table of pre-resolved libkernel/libc addresses via a trailing extension of payload_args_t: api_magic ('KXP2'), api_count (12) and api_entries[]. When present, init_libkernel_api/init_libc_api assign every import from the table and perform zero dlsym calls; getpid is exported as lk_getpid and consumed by the loader. Launchers passing the legacy 0x28-byte block are unaffected: the extension is validated (magic, count, pointer) before use and otherwise the internal dlsym resolver runs exactly as before.
1 parent c3d0fd9 commit b12c352

5 files changed

Lines changed: 129 additions & 11 deletions

File tree

include/api.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,15 @@
1212
#include "libkernel-imports.h"
1313
#undef API
1414

15-
void init_libkernel_api();
16-
void init_libc_api();
15+
/* Both take the payload args: when a valid KEXP_API table is present the
16+
* function pointers are taken from it (zero dlsym calls), otherwise each
17+
* import is resolved through syscall 0x24f as before. */
18+
void init_libkernel_api(payload_args_t *args);
19+
void init_libc_api(payload_args_t *args);
20+
21+
/* libkernel getpid, resolved either from the API table or via dlsym in
22+
* init_libkernel_api(). Named lk_getpid because `getpid` collides with the
23+
* raw syscall stub generated from syscalls-imports.h. */
24+
extern int (*lk_getpid)(void);
1725

1826
#endif

include/types.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define TYPES_H
33

44
#include <stdarg.h>
5+
#include <stddef.h>
56
#include <stdint.h>
67
#include <sys/types.h>
78

@@ -47,14 +48,76 @@ typedef struct {
4748
char unk[0x400];
4849
} NotificationRequest;
4950

51+
/* ---------------------------------------------------------------------------
52+
* Optional pre-resolved API table (v2 args extension).
53+
*
54+
* Some launchers cannot let the payload resolve its own imports: syscall 0x24f
55+
* (dlsym) is refused for a WebProcess, so a kexp spawned from the PS5 browser
56+
* would hit __builtin_trap() in resolve_symbol(). Such launchers instead fill
57+
* api_entries[] with the runtime addresses of every imported function and set
58+
* api_magic/api_count; kexp then performs zero dlsym calls. Launchers that do
59+
* not know these addresses simply leave the extension zeroed (legacy 0x28
60+
* block) and kexp falls back to its internal resolver as before.
61+
*
62+
* Table layout is a fixed contract (index -> function), matching the order of
63+
* libkernel-imports.h + libc-imports.h below:
64+
* [0] sceKernelSendNotificationRequest (libkernel)
65+
* [1] sysctlbyname (libkernel)
66+
* [2] pthread_create (libkernel)
67+
* [3] pthread_join (libkernel)
68+
* [4] getpid (libkernel)
69+
* [5] malloc (libSceLibcInternal)
70+
* [6] free (libSceLibcInternal)
71+
* [7] memcpy (libSceLibcInternal)
72+
* [8] memset (libSceLibcInternal)
73+
* [9] strcmp (libSceLibcInternal)
74+
* [10] memcmp (libSceLibcInternal)
75+
* [11] vsnprintf (libSceLibcInternal)
76+
* ------------------------------------------------------------------------- */
77+
#define KEXP_API_MAGIC 0x4B585032U /* 'KXP2' */
78+
#define KEXP_API_COUNT 12
79+
80+
enum {
81+
KEXP_API_NOTIFY = 0,
82+
KEXP_API_SYSCTLBYNAME,
83+
KEXP_API_PTHREAD_CREATE,
84+
KEXP_API_PTHREAD_JOIN,
85+
KEXP_API_GETPID,
86+
KEXP_API_MALLOC,
87+
KEXP_API_FREE,
88+
KEXP_API_MEMCPY,
89+
KEXP_API_MEMSET,
90+
KEXP_API_STRCMP,
91+
KEXP_API_MEMCMP,
92+
KEXP_API_VSNPRINTF,
93+
};
94+
5095
typedef struct {
5196
int master_pipe[2];
5297
int victim_pipe[2];
5398
uintptr_t allproc;
5499
char *elfldr_ptr;
55100
size_t elfldr_size;
101+
/* v2 extension: optional pre-resolved API table (see above). Legacy
102+
* launchers pass only the 0x28-byte prefix; reading past it is safe on the
103+
* heap and misinterpretation is ruled out by validating magic, count and
104+
* pointer before use. */
105+
uint32_t api_magic;
106+
uint32_t api_count;
107+
void **api_entries;
56108
} payload_args_t;
57109

110+
_Static_assert(offsetof(payload_args_t, elfldr_size) == 0x20,
111+
"legacy prefix must stay 0x28 bytes");
112+
_Static_assert(offsetof(payload_args_t, api_magic) == 0x28,
113+
"api_magic offset broke the v2 args contract");
114+
_Static_assert(offsetof(payload_args_t, api_count) == 0x2C,
115+
"api_count offset broke the v2 args contract");
116+
_Static_assert(offsetof(payload_args_t, api_entries) == 0x30,
117+
"api_entries offset broke the v2 args contract");
118+
_Static_assert(sizeof(payload_args_t) == 0x38,
119+
"payload_args_t size broke the v2 args contract");
120+
58121
typedef struct {
59122
uint32_t cnt;
60123
uint32_t in;

src/api.c

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,75 @@
55
#include "libkernel-imports.h"
66
#undef API
77

8+
int (*lk_getpid)(void) = 0;
9+
810
static inline void *resolve_symbol(SceKernelModule handle, const char *name) {
911
void *addrp;
1012
if (dlsym(handle, name, &addrp))
1113
__builtin_trap();
1214
return addrp;
1315
}
1416

15-
void init_libkernel_api() {
17+
/* Fill every import from the launcher-provided table. Returns 0 when the
18+
* extension is present and valid (and everything was assigned), -1 when the
19+
* caller must fall back to the dlsym resolver. Idempotent: once the table has
20+
* been applied, later calls report success without touching anything. */
21+
static int api_try_table(payload_args_t *args) {
22+
static int table_applied = 0;
23+
24+
if (table_applied)
25+
return 0;
26+
27+
if (args == 0 || args->api_entries == 0 ||
28+
args->api_magic != KEXP_API_MAGIC || args->api_count != KEXP_API_COUNT)
29+
return -1;
30+
31+
if (((uintptr_t)args->api_entries & (sizeof(void *) - 1)) != 0)
32+
return -1;
33+
34+
void **table = args->api_entries;
35+
36+
sceKernelSendNotificationRequest =
37+
(typeof(sceKernelSendNotificationRequest))table[KEXP_API_NOTIFY];
38+
sysctlbyname = (typeof(sysctlbyname))table[KEXP_API_SYSCTLBYNAME];
39+
pthread_create = (typeof(pthread_create))table[KEXP_API_PTHREAD_CREATE];
40+
pthread_join = (typeof(pthread_join))table[KEXP_API_PTHREAD_JOIN];
41+
lk_getpid = (typeof(lk_getpid))table[KEXP_API_GETPID];
42+
43+
malloc = (typeof(malloc))table[KEXP_API_MALLOC];
44+
free = (typeof(free))table[KEXP_API_FREE];
45+
memcpy = (typeof(memcpy))table[KEXP_API_MEMCPY];
46+
memset = (typeof(memset))table[KEXP_API_MEMSET];
47+
strcmp = (typeof(strcmp))table[KEXP_API_STRCMP];
48+
memcmp = (typeof(memcmp))table[KEXP_API_MEMCMP];
49+
vsnprintf = (typeof(vsnprintf))table[KEXP_API_VSNPRINTF];
50+
51+
table_applied = 1;
52+
return 0;
53+
}
54+
55+
void init_libkernel_api(payload_args_t *args) {
56+
if (api_try_table(args) == 0)
57+
return;
58+
1659
#define API(ret, name, args) \
1760
name = (ret(*) args)resolve_symbol(LIBKERNEL_HANDLE, #name);
1861

1962
#include "libkernel-imports.h"
2063

2164
#undef API
65+
66+
lk_getpid = (int (*)(void))resolve_symbol(LIBKERNEL_HANDLE, "getpid");
2267
}
2368

24-
void init_libc_api() {
69+
void init_libc_api(payload_args_t *args) {
70+
if (api_try_table(args) == 0)
71+
return;
72+
2573
#define API(ret, name, args) \
2674
name = (ret(*) args)resolve_symbol(LIBC_HANDLE, #name);
2775

2876
#include "libc-imports.h"
2977

3078
#undef API
31-
}
79+
}

src/loader.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,8 @@ int init_loader_args() {
220220

221221
kread(&rpipe_f_data, rpipe_fp, sizeof(rpipe_f_data));
222222

223-
void *kernel_getpid;
224-
if (dlsym(LIBKERNEL_HANDLE, "getpid", &kernel_getpid) == -1) {
225-
log("unable to dlsym getpid !!");
223+
if (lk_getpid == 0) {
224+
log("getpid was not resolved !!");
226225
return -1;
227226
}
228227

@@ -232,7 +231,7 @@ int init_loader_args() {
232231
return -1;
233232
}
234233

235-
loader_ctx.args.syscall_wrapper = kernel_getpid;
234+
loader_ctx.args.syscall_wrapper = (void *)lk_getpid;
236235
loader_ctx.args.rwpipe = rwpipe;
237236
loader_ctx.args.rwpair = rwpair;
238237
loader_ctx.args.pipe_f_data = rpipe_f_data;

src/main.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ __attribute__((section(".entry"))) int main(payload_args_t *args) {
88
if (args == 0)
99
return -1;
1010

11-
init_libkernel_api();
12-
init_libc_api();
11+
init_libkernel_api(args);
12+
init_libc_api(args);
1313

1414
if (logger_init() != 0) {
1515
notify("unable to init logger !!");

0 commit comments

Comments
 (0)