taiHEN  1.0
CFW framework for PS Vita
module.c
1 /* module.c -- nid lookup utilities
2  *
3  * Copyright (C) 2016 Yifan Lu
4  *
5  * This software may be modified and distributed under the terms
6  * of the MIT license. See the LICENSE file for details.
7  */
8 #include <psp2kern/types.h>
9 #include <psp2kern/kernel/cpu.h>
10 #include <psp2kern/kernel/sysmem.h>
11 #include <psp2kern/kernel/modulemgr.h>
12 #include <string.h>
13 #include "error.h"
14 #include "taihen_internal.h"
15 
17  uint16_t size; // size of this structure; 0x34
18  uint16_t version; //
19  uint16_t flags; //
20  uint16_t num_functions; // number of imported functions
21  uint16_t num_vars; // number of imported variables
22  uint16_t num_tls_vars; // number of imported TLS variables
23  uint32_t reserved1; // ?
24  uint32_t lib_nid; // NID of the module to link to
25  char *lib_name; // name of module
26  uint32_t reserved2; // ?
27  uint32_t *func_nid_table; // array of function NIDs (numFuncs)
28  void **func_entry_table; // parallel array of pointers to stubs; they're patched by the loader to jump to the final code
29  uint32_t *var_nid_table; // NIDs of the imported variables (numVars)
30  void **var_entry_table; // array of pointers to "ref tables" for each variable
31  uint32_t *tls_nid_table; // NIDs of the imported TLS variables (numTlsVars)
32  void **tls_entry_table; // array of pointers to ???
33 };
34 
36  uint16_t size; // 0x24
37  uint16_t version;
38  uint16_t flags;
39  uint16_t num_functions;
40  uint32_t reserved1;
41  uint32_t lib_nid;
42  char *lib_name;
43  uint32_t *func_nid_table;
44  void **func_entry_table;
45  uint32_t unk1;
46  uint32_t unk2;
47 };
48 
49 typedef union sce_module_imports {
50  uint16_t size;
51  struct sce_module_imports_1 type1;
52  struct sce_module_imports_2 type2;
54 
55 typedef struct sce_module_exports {
56  uint16_t size; // size of this structure; 0x20 for Vita 1.x
57  uint8_t lib_version[2]; //
58  uint16_t attribute; // ?
59  uint16_t num_functions; // number of exported functions
60  uint16_t num_vars; // number of exported variables
61  uint16_t unk;
62  uint32_t num_tls_vars; // number of exported TLS variables? <-- pretty sure wrong // yifanlu
63  uint32_t lib_nid; // NID of this specific export list; one PRX can export several names
64  char *lib_name; // name of the export module
65  uint32_t *nid_table; // array of 32-bit NIDs for the exports, first functions then vars
66  void **entry_table; // array of pointers to exported functions and then variables
68 
69 typedef struct sce_module_info {
70  uint16_t modattribute; // ??
71  uint16_t modversion; // always 1,1?
72  char modname[27];
73  uint8_t type; // 6 = user-mode prx?
74  void *gp_value; // always 0 on ARM
75  uint32_t ent_top; // beginning of the export list (sceModuleExports array)
76  uint32_t ent_end; // end of same
77  uint32_t stub_top; // beginning of the import list (sceModuleStubInfo array)
78  uint32_t stub_end; // end of same
79  uint32_t module_nid; // ID of the PRX? seems to be unused
80  uint32_t field_38; // unused in samples
81  uint32_t field_3C; // I suspect these may contain TLS info
82  uint32_t field_40; //
83  uint32_t mod_start; // 44 module start function; can be 0 or -1; also present in exports
84  uint32_t mod_stop; // 48 module stop function
85  uint32_t exidx_start; // 4c ARM EABI style exception tables
86  uint32_t exidx_end; // 50
87  uint32_t extab_start; // 54
88  uint32_t extab_end; // 58
89 } sce_module_info_t; // 5c?
90 
91 #define MOD_LIST_SIZE 0x80
92 
94 static uint32_t fw_version = 0;
95 
108 static int sce_to_tai_module_info(SceUID pid, void *sceinfo, tai_module_info_t *taiinfo) {
109  SceKernelFwInfo fwinfo;
110  char *info;
111 
112  if (fw_version == 0) {
113  fwinfo.size = sizeof(fwinfo);
114  if (sceKernelGetSystemSwVersion(&fwinfo) < 0) {
115  fw_version = DEFAULT_FW_VERSION;
116  } else {
117  fw_version = fwinfo.version;
118  }
119  LOG("sceKernelGetSystemSwVersion: 0x%08X", fw_version);
120  }
121 
122  if (taiinfo->size < sizeof(tai_module_info_t)) {
123  LOG("Structure size too small: %d", taiinfo->size);
124  return TAI_ERROR_SYSTEM;
125  }
126 
127  info = (char *)sceinfo;
128  if (fw_version >= 0x3600000) {
129  if (pid == KERNEL_PID) {
130  taiinfo->modid = *(SceUID *)(info + 0xC);
131  } else {
132  taiinfo->modid = *(SceUID *)(info + 0x10);
133  }
134  snprintf(taiinfo->name, 27, "%s", *(const char **)(info + 0x1C));
135  taiinfo->name[26] = '\0';
136  taiinfo->module_nid = *(uint32_t *)(info + 0x30);
137  taiinfo->exports_start = *(uintptr_t *)(info + 0x20);
138  taiinfo->exports_end = *(uintptr_t *)(info + 0x24);
139  taiinfo->imports_start = *(uintptr_t *)(info + 0x28);
140  taiinfo->imports_end = *(uintptr_t *)(info + 0x2C);
141  } else if (fw_version >= 0x1692000) {
142  if (pid == KERNEL_PID) {
143  taiinfo->modid = *(SceUID *)(info + 0x0);
144  } else {
145  taiinfo->modid = *(SceUID *)(info + 0x4);
146  }
147  taiinfo->module_nid = *(uint32_t *)(info + 0x3C);
148  snprintf(taiinfo->name, 27, "%s", (const char *)(info + 0xC));
149  taiinfo->name[26] = '\0';
150  taiinfo->exports_start = *(uintptr_t *)(info + 0x2C);
151  taiinfo->exports_end = *(uintptr_t *)(info + 0x30);
152  taiinfo->imports_start = *(uintptr_t *)(info + 0x34);
153  taiinfo->imports_end = *(uintptr_t *)(info + 0x38);
154  } else {
155  LOG("Unsupported FW 0x%08X", fw_version);
156  return TAI_ERROR_SYSTEM;
157  }
158  return TAI_SUCCESS;
159 }
160 
173 static int find_int_for_user(SceUID pid, uintptr_t src, uint32_t needle, size_t size) {
174  int my_context[3];
175  int *other_context;
176  int flags;
177  uintptr_t end;
178  uint32_t data;
179  int count;
180  int ret;
181 
182  count = 0;
183  end = (src + size) & ~3; // align to last 4 byte boundary
184  src = (src + 3) & ~3; // align to next 4 byte boundary
185  if (end <= src) {
186  return 0;
187  }
188  size = end-src;
189  flags = sceKernelCpuDisableInterrupts();
190  sceKernelCpuSaveContext(my_context);
191  ret = sceKernelGetPidContext(pid, &other_context);
192  if (ret >= 0) {
193  sceKernelCpuRestoreContext(other_context);
194  while (count < size) {
195  asm ("ldrt %0, [%1]" : "=r" (data) : "r" (src+count));
196  if (data == needle) {
197  break;
198  }
199  count += 4;
200  }
201  }
202  sceKernelCpuRestoreContext(my_context);
203  sceKernelCpuEnableInterrupts(flags);
204  if (ret < 0) {
205  LOG("Error trying to get context for %x", pid);
206  count = ret;
207  }
208  if (count >= size) {
209  return -1;
210  } else {
211  return count;
212  }
213 }
214 
235 int module_get_by_name_nid(SceUID pid, const char *name, uint32_t nid, tai_module_info_t *info) {
236  SceUID modlist[MOD_LIST_SIZE];
237  void *sceinfo;
238  size_t count;
239  int ret;
240  int get_cur;
241 
242  get_cur = (name == NULL && nid == TAI_IGNORE_MODULE_NID);
243  count = MOD_LIST_SIZE;
244  ret = sceKernelGetModuleListForKernel(pid, get_cur ? 1 : 0xff, 1, modlist, &count);
245  LOG("sceKernelGetModuleListForKernel(%x): 0x%08X, count: %d", pid, ret, count);
246  if (ret < 0) {
247  return ret;
248  }
249  if (get_cur && count > 1) {
250  LOG("Cannot use TAI_MAIN_MODULE since there are multiple main modules");
251  return TAI_ERROR_INVALID_MODULE;
252  }
253  for (int i = count-1; i >= 0; i--) {
254  ret = sceKernelGetModuleInternal(modlist[i], &sceinfo);
255  //LOG("sceKernelGetModuleInternal(%x): 0x%08X", modlist[i], ret);
256  if (ret < 0) {
257  LOG("Error getting info for mod: %x, ret: %x", modlist[i], ret);
258  continue;
259  }
260  if (sce_to_tai_module_info(pid, sceinfo, info) < 0) {
261  continue;
262  }
263  if (name != NULL && strncmp(name, info->name, 27) == 0) {
264  if (nid == TAI_IGNORE_MODULE_NID || info->modid == nid) {
265  LOG("Found module %s, NID:0x%08X", name, info->modid);
266  return TAI_SUCCESS;
267  }
268  } else if (name == NULL && (get_cur || info->modid == nid)) {
269  LOG("Found module %s, NID:0x%08X", info->name, info->modid);
270  return TAI_SUCCESS;
271  }
272  }
273 
274  return TAI_ERROR_NOT_FOUND;
275 }
276 
288 int module_get_offset(SceUID pid, SceUID modid, int segidx, size_t offset, uintptr_t *addr) {
289  SceKernelModuleInfo sceinfo;
290  size_t count;
291  int ret;
292 
293  if (segidx > 3) {
294  LOG("Invalid segment index: %d", segidx);
295  return TAI_ERROR_INVALID_ARGS;
296  }
297  LOG("Getting offset for pid:%x, modid:%x, segidx:%d, offset:%x", pid, modid, segidx, offset);
298  sceinfo.size = sizeof(sceinfo);
299  ret = sceKernelGetModuleInfoForKernel(pid, modid, &sceinfo);
300  LOG("sceKernelGetModuleInfoForKernel(%x, %x): 0x%08X", pid, modid, ret);
301  if (ret < 0) {
302  LOG("Error getting segment info for %d", modid);
303  return ret;
304  }
305  if (offset > sceinfo.segments[segidx].memsz) {
306  LOG("Offset %x overflows segment size %x", offset, sceinfo.segments[segidx].memsz);
307  return TAI_ERROR_INVALID_ARGS;
308  }
309  *addr = (uintptr_t)sceinfo.segments[segidx].vaddr + offset;
310  LOG("found address: 0x%08X", *addr);
311 
312  return TAI_SUCCESS;
313 }
314 
326 int module_get_export_func(SceUID pid, const char *modname, uint32_t libnid, uint32_t funcnid, uintptr_t *func) {
327  sce_module_exports_t local;
328  tai_module_info_t info;
329  sce_module_exports_t *export;
330  uintptr_t cur;
331  size_t found;
332  int i;
333  int ret;
334 
335  LOG("Getting export for pid:%x, modname:%s, libnid:%x, funcnid:%x", pid, modname, libnid, funcnid);
336  info.size = sizeof(info);
337  if (module_get_by_name_nid(pid, modname, TAI_IGNORE_MODULE_NID, &info) < 0) {
338  LOG("Failed to find module: %s", modname);
339  return TAI_ERROR_NOT_FOUND;
340  }
341 
342  for (cur = info.exports_start; cur < info.exports_end; ) {
343  if (pid == KERNEL_PID) {
344  export = (sce_module_exports_t *)cur;
345  } else {
346  if ((ret = sceKernelMemcpyUserToKernelForPid(pid, &local, cur, sizeof(local))) < 0) {
347  LOG("Error trying to read address %p for %x: %x", cur, pid, ret);
348  return ret;
349  }
350  export = &local;
351  }
352 
353  if (libnid == TAI_ANY_LIBRARY || export->lib_nid == libnid) {
354  if (pid == KERNEL_PID) {
355  for (i = 0; i < export->num_functions; i++) {
356  if (export->nid_table[i] == funcnid) {
357  *func = (uintptr_t)export->entry_table[i];
358  LOG("found kernel address: 0x%08X", *func);
359  return TAI_SUCCESS;
360  }
361  }
362  } else {
363  found = find_int_for_user(pid, (uintptr_t)export->nid_table, funcnid, export->num_functions * 4);
364  if (found >= 0) {
365  if ((ret = sceKernelMemcpyUserToKernelForPid(pid, func, (uintptr_t)export->entry_table + found, 4)) < 0) {
366  LOG("Error trying to read address %p for %x: %x", (uintptr_t)export->entry_table + found, pid, ret);
367  return ret;
368  }
369  LOG("found user address: 0x%08X", *func);
370  return TAI_SUCCESS;
371  }
372  }
373  }
374  cur += export->size;
375  }
376 
377  return TAI_ERROR_NOT_FOUND;
378 }
379 
392 int module_get_import_func(SceUID pid, const char *modname, uint32_t target_libnid, uint32_t funcnid, uintptr_t *stub) {
393  sce_module_imports_t local;
394  tai_module_info_t info;
395  sce_module_imports_t *import;
396  uintptr_t cur;
397  size_t found;
398  int i;
399  int ret;
400 
401  LOG("Getting import for pid:%x, modname:%s, target_libnid:%x, funcnid:%x", pid, modname, target_libnid, funcnid);
402  info.size = sizeof(info);
403  if (module_get_by_name_nid(pid, modname, TAI_IGNORE_MODULE_NID, &info) < 0) {
404  LOG("Failed to find module: %s", modname);
405  return TAI_ERROR_NOT_FOUND;
406  }
407 
408  for (cur = info.imports_start; cur < info.imports_end; ) {
409  if (pid == KERNEL_PID) {
410  import = (sce_module_imports_t *)cur;
411  } else {
412  if ((ret = sceKernelMemcpyUserToKernelForPid(pid, &local.size, cur, sizeof(local.size))) < 0) {
413  LOG("Error trying to read address %p for %x: %x", cur, pid, ret);
414  return ret;
415  }
416  if (local.size <= sizeof(local)) {
417  if ((ret = sceKernelMemcpyUserToKernelForPid(pid, &local, cur, local.size)) < 0) {
418  LOG("Error trying to read address %p for %x: %x", cur, pid, ret);
419  return ret;
420  }
421  }
422  import = &local;
423  }
424 
425  //LOG("import size is 0x%04X", import->size);
426  if (import->size == sizeof(struct sce_module_imports_1)) {
427  if (target_libnid == TAI_ANY_LIBRARY || import->type1.lib_nid == target_libnid) {
428  if (pid == KERNEL_PID) {
429  for (i = 0; i < import->type1.num_functions; i++) {
430  if (import->type1.func_nid_table[i] == funcnid) {
431  *stub = (uintptr_t)import->type1.func_entry_table[i];
432  LOG("found kernel address: 0x%08X", *stub);
433  return TAI_SUCCESS;
434  }
435  }
436  } else {
437  found = find_int_for_user(pid, (uintptr_t)import->type1.func_nid_table, funcnid, import->type1.num_functions * 4);
438  if (found >= 0) {
439  if ((ret = sceKernelMemcpyUserToKernelForPid(pid, stub, (uintptr_t)import->type1.func_entry_table + found, 4)) < 0) {
440  LOG("Error trying to read address %p for %x: %x", (uintptr_t)import->type1.func_entry_table + found, pid, ret);
441  return ret;
442  }
443  LOG("found user address: 0x%08X", *stub);
444  return TAI_SUCCESS;
445  }
446  }
447  }
448  } else if (import->size == sizeof(struct sce_module_imports_2)) {
449  if (target_libnid == TAI_ANY_LIBRARY || import->type2.lib_nid == target_libnid) {
450  if (pid == KERNEL_PID) {
451  for (i = 0; i < import->type2.num_functions; i++) {
452  if (import->type2.func_nid_table[i] == funcnid) {
453  *stub = (uintptr_t)import->type2.func_entry_table[i];
454  LOG("found kernel address: 0x%08X", *stub);
455  return TAI_SUCCESS;
456  }
457  }
458  } else {
459  found = find_int_for_user(pid, (uintptr_t)import->type2.func_nid_table, funcnid, import->type2.num_functions * 4);
460  if (found >= 0) {
461  if ((ret = sceKernelMemcpyUserToKernelForPid(pid, stub, (uintptr_t)import->type2.func_entry_table + found, 4)) < 0) {
462  LOG("Error trying to read address %p for %x: %x", (uintptr_t)import->type2.func_entry_table + found, pid, ret);
463  return ret;
464  }
465  LOG("found user address: 0x%08X", *stub);
466  return TAI_SUCCESS;
467  }
468  }
469  }
470  } else {
471  LOG("Invalid import size: %d", import->size);
472  }
473  cur += import->size;
474  }
475 
476  return TAI_ERROR_NOT_FOUND;
477 }
#define TAI_ANY_LIBRARY
Definition: taihen.h:37
char name[27]
Module name.
Definition: taihen.h:57
uintptr_t imports_start
Pointer to import table in process address space.
Definition: taihen.h:60
Extended module information.
Definition: taihen.h:53
uintptr_t exports_end
Pointer to end of export table.
Definition: taihen.h:59
#define KERNEL_PID
Definition: taihen.h:34
uintptr_t imports_end
Pointer to end of import table.
Definition: taihen.h:61
uint32_t module_nid
Module NID.
Definition: taihen.h:56
int module_get_by_name_nid(SceUID pid, const char *name, uint32_t nid, tai_module_info_t *info)
Gets a loaded module by name or NID or both.
Definition: module.c:235
int module_get_export_func(SceUID pid, const char *modname, uint32_t libnid, uint32_t funcnid, uintptr_t *func)
Gets an exported function address.
Definition: module.c:326
uintptr_t exports_start
Pointer to export table in process address space.
Definition: taihen.h:58
#define TAI_IGNORE_MODULE_NID
Definition: taihen.h:40
int module_get_offset(SceUID pid, SceUID modid, int segidx, size_t offset, uintptr_t *addr)
Gets an offset from a segment in a module.
Definition: module.c:288
size_t size
Structure size, set to sizeof(tai_module_info_t)
Definition: taihen.h:54
SceUID modid
Module UID.
Definition: taihen.h:55
int module_get_import_func(SceUID pid, const char *modname, uint32_t target_libnid, uint32_t funcnid, uintptr_t *stub)
Gets an imported function stub address.
Definition: module.c:392