rtld: initial experiment with homebrew shared objects

This commit is contained in:
John Törnblom committed 2024-11-14 13:20:37 +01:00
1 parent f78074899a
commit 7c3a832633
19 files changed
+2159 -838

No files matched your search

+6 -1
View File
@@ -33,12 +33,17 @@ endif
CFLAGS += -ffreestanding -fno-builtin -nostdlib -fPIC
CFLAGS += -target x86_64-sie-ps5 -fno-plt -fno-stack-protector
CFLAGS += -fvisibility-nodllstorageclass=default -Wall -Werror
CFLAGS += -fms-extensions -Wno-microsoft-anon-tag
CFLAGS += -g
OBJ := crt.o syscall.o klog.o nid.o kernel.o rtld.o rtld_so.o rtld_sprx.o \
rtld_payload.o patch.o mdbg.o
ARCHIVE := libcrt.a
all: $(ARCHIVE)
$(ARCHIVE): crt.o syscall.o klog.o nid.o kernel.o rtld.o patch.o mdbg.o
$(ARCHIVE): $(OBJ)
$(AR) -rsc $@ $^
%.o: %.c
+10 -13
View File
@@ -19,12 +19,6 @@ along with this program; see the file COPYING. If not, see
#include "payload.h"
/**
* Convenient macro for loading symbols.
**/
#define DLSYM(handle, sym) (sym=(void*)kernel_dynlib_dlsym(-1, handle, #sym))
/**
* Dependencies provided by the ELF linker.
**/
@@ -44,6 +38,9 @@ extern unsigned char __bss_end[] __attribute__((weak));
extern int main(int argc, char* argv[], char *envp[]);
/**
* Init functrions for crt modules.
**/
int __syscall_init(payload_args_t* args);
int __kernel_init(payload_args_t* args);
int __klog_init(void);
@@ -77,7 +74,7 @@ pre_init(payload_args_t *args) {
klog_puts("Unable to initialize patches");
return error;
}
if(!DLSYM(0x2, __isthreaded)) {
if(!KERNEL_DLSYM(0x2, __isthreaded)) {
klog_puts("Unable to resolve the symbol '__isthreaded'");
return -1;
}
@@ -101,7 +98,7 @@ terminate(payload_args_t *args) {
return;
}
if(DLSYM(0x2, exit)) {
if(KERNEL_DLSYM(0x2, exit)) {
exit(*args->payloadout);
}
}
@@ -124,16 +121,16 @@ _start(payload_args_t *args) {
*bss = 0;
}
// Init runtime
// Init runtime.
if((*args->payloadout=pre_init(args))) {
terminate(args);
return;
}
// Obtain argc, argv and envp from libkernel
if((DLSYM(0x1, getargc) || DLSYM(0x2001, getargc)) &&
(DLSYM(0x1, getargv) || DLSYM(0x2001, getargv)) &&
(DLSYM(0x1, environ) || DLSYM(0x2001, environ))) {
// Obtain argc, argv and envp from libkernel.
if((KERNEL_DLSYM(0x1, getargc) || KERNEL_DLSYM(0x2001, getargc)) &&
(KERNEL_DLSYM(0x1, getargv) || KERNEL_DLSYM(0x2001, getargv)) &&
(KERNEL_DLSYM(0x1, environ) || KERNEL_DLSYM(0x2001, environ))) {
argc = getargc();
argv = getargv();
}
+115
View File
@@ -0,0 +1,115 @@
/* Copyright (C) 2024 John Törnblom
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>. */
#define ELF64_R_SYM(info) ((info) >> 32)
#define ELF64_ST_BIND(info) ((info) >> 4)
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define DT_NULL 0
#define DT_NEEDED 1
#define DT_STRTAB 5
#define DT_SYMTAB 6
#define DT_RELA 7
#define DT_RELASZ 8
#define DT_GNU_HASH 0x6ffffef5
#define R_X86_64_64 1
#define R_X86_64_GLOB_DAT 6
#define R_X86_64_JMP_SLOT 7
#define R_X86_64_RELATIVE 8
#define ET_DYN 3
#define STB_WEAK 2
#define SHT_RELA 4
#define PF_X (1 << 0)
#define PF_W (1 << 1)
#define PF_R (1 << 2)
typedef struct {
unsigned char e_ident[16];
unsigned short e_type;
unsigned short e_machine;
unsigned int e_version;
unsigned long e_entry;
unsigned long e_phoff;
unsigned long e_shoff;
unsigned int e_flags;
unsigned short e_ehsize;
unsigned short e_phentsize;
unsigned short e_phnum;
unsigned short e_shentsize;
unsigned short e_shnum;
unsigned short e_shstrndx;
} Elf64_Ehdr;
typedef struct {
unsigned int p_type;
unsigned int p_flags;
unsigned long p_offset;
unsigned long p_vaddr;
unsigned long p_paddr;
unsigned long p_filesz;
unsigned long p_memsz;
unsigned long p_align;
} Elf64_Phdr;
typedef struct {
unsigned int sh_name;
unsigned int sh_type;
unsigned long sh_flags;
unsigned long sh_addr;
unsigned long sh_offset;
unsigned long sh_size;
unsigned int sh_link;
unsigned int sh_info;
unsigned long sh_addralign;
unsigned long sh_entsize;
} Elf64_Shdr;
typedef struct {
long d_tag;
union {
unsigned long d_val;
unsigned long d_ptr;
} d_un;
} Elf64_Dyn;
typedef struct {
unsigned int st_name;
unsigned char st_info;
unsigned char st_other;
unsigned short st_shndx;
unsigned long st_value;
unsigned long st_size;
} Elf64_Sym;
typedef struct {
unsigned long r_offset;
unsigned long r_info;
long r_addend;
} Elf64_Rela;
+4
View File
@@ -17,6 +17,10 @@ along with this program; see the file COPYING. If not, see
#pragma once
#define KERNEL_DLSYM(handle, sym) \
(sym=(void*)kernel_dynlib_dlsym(-1, handle, #sym))
/**
* dynamic library loaded in kernel memory.
**/
+135 -822
View File
File diff suppressed because it is too large. Load diff
+75
View File
@@ -0,0 +1,75 @@
/* Copyright (C) 2024 John Törnblom
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>. */
#pragma once
/**
* Prototypes for different kinds of libs (.sprx or .so).
**/
typedef struct rtld_lib {
int (*open)(struct rtld_lib* ctx);
void* (*sym)(struct rtld_lib* ctx, const char* name);
int (*close)(struct rtld_lib* ctx);
void (*destroy)(struct rtld_lib* ctx);
char *soname;
struct rtld_lib* next;
struct rtld_lib* prev;
} rtld_lib_t;
/**
* Allocate a new lib object with the given soname.
**/
rtld_lib_t* __rtld_lib_new(rtld_lib_t* prev, const char* soname);
/**
* Open a lib and run its constructor.
**/
int __rtld_lib_open(rtld_lib_t* ctx);
/**
* Resolve a symbol in the lib.
**/
void* __rtld_lib_sym(rtld_lib_t* ctx, const char* name);
/**
* Close the lib and run its destructor.
**/
int __rtld_lib_close(rtld_lib_t* ctx);
/**
* Free up all memory associated with the lib.
**/
void __rtld_lib_destroy(rtld_lib_t* ctx);
/**
* Initialize the lib loader.
**/
int __rtld_init(void);
/**
* Finalize the lib loader.
**/
int __rtld_fini(void);
+275
View File
@@ -0,0 +1,275 @@
/* Copyright (C) 2024 John Törnblom
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>. */
#include "elf.h"
#include "kernel.h"
#include "klog.h"
#include "syscall.h"
#include "rtld.h"
/**
* Dependencies to standard libraries.
**/
static void* (*memcpy)(void*, const void*, unsigned long) = 0;
/**
* Dependencies provided by the ELF linker.
**/
extern unsigned char __image_start[] __attribute__((weak));
extern Elf64_Dyn _DYNAMIC[];
/**
* Global variables.
**/
static rtld_lib_t* g_libhead = 0;
static Elf64_Sym* g_symtab = 0;
static char* g_strtab = 0;
/**
* Process the DT_NEEDED entry in the .dynamic section.
**/
static int
dt_needed(const char* soname) {
rtld_lib_t* it = g_libhead;
rtld_lib_t* lib;
while(it && it->next) {
it = it->next;
}
if(!(lib=__rtld_lib_new(it, soname))) {
klog_printf("Unable to load %s\n", soname);
return -1;
} else if(__rtld_lib_open(lib) < 0) {
klog_printf("unable to load '%s'\n", soname);
__rtld_lib_destroy(lib);
return -1;
} else if(!g_libhead) {
g_libhead = lib;
} else if(it) {
it->next = lib;
}
return 0;
}
/**
* Process the R_X86_64_GLOB_DAT relocation type.
**/
static int
r_glob_dat(Elf64_Rela* rela) {
Elf64_Sym* sym = g_symtab + ELF64_R_SYM(rela->r_info);
const char* name = g_strtab + sym->st_name;
void* loc = __image_start + rela->r_offset;
void* val = 0;
for(rtld_lib_t *lib=g_libhead; lib; lib=lib->next) {
if((val=__rtld_lib_sym(lib, name))) {
memcpy(loc, &val, sizeof(val));
return 0;
}
}
if(ELF64_ST_BIND(sym->st_info) == STB_WEAK) {
return 0;
}
klog_printf("Unable to resolve '%s'\n", name);
return -1;
}
/**
* Process the R_X86_64_JMP_SLOT relocation type.
**/
static int
r_jmp_slot(Elf64_Rela* rela) {
return r_glob_dat(rela);
}
/**
* Process the R_X86_64_64 relocation type.
**/
static int
r_direct_64(Elf64_Rela* rela) {
Elf64_Sym* sym = g_symtab + ELF64_R_SYM(rela->r_info);
const char* name = g_strtab + sym->st_name;
void* loc = __image_start + rela->r_offset;
void* val = 0;
for(rtld_lib_t *lib=g_libhead; lib!=0; lib=lib->next) {
if((val=__rtld_lib_sym(lib, name))) {
val += rela->r_addend;
memcpy(loc, &val, sizeof(val));
return 0;
}
}
if(ELF64_ST_BIND(sym->st_info) == STB_WEAK) {
return 0;
}
klog_printf("Unable to resolve '%s'\n", name);
return -1;
}
/**
* Process the R_X86_64_RELATIVE relocation type.
**/
static int
r_relative(Elf64_Rela* rela) {
void* loc = __image_start + rela->r_offset;
void* val = __image_start + rela->r_addend;
memcpy(loc, &val, sizeof(val));
return 0;
}
/**
*
**/
static int
payload_load(void) {
Elf64_Rela* rela = 0;
long relasz = 0;
// find lookup tables
for(int i=0; _DYNAMIC[i].d_tag!=DT_NULL; i++) {
switch(_DYNAMIC[i].d_tag) {
case DT_SYMTAB:
g_symtab = (Elf64_Sym*)(__image_start + _DYNAMIC[i].d_un.d_ptr);
break;
case DT_STRTAB:
g_strtab = (char*)(__image_start + _DYNAMIC[i].d_un.d_ptr);
break;
case DT_RELA:
rela = (Elf64_Rela*)(__image_start + _DYNAMIC[i].d_un.d_ptr);
break;
case DT_RELASZ:
relasz = _DYNAMIC[i].d_un.d_val;
break;
}
}
// load needed libraries
for(int i=0; _DYNAMIC[i].d_tag!=DT_NULL; i++) {
switch(_DYNAMIC[i].d_tag) {
case DT_NEEDED:
if(dt_needed(g_strtab + _DYNAMIC[i].d_un.d_val)) {
return -1;
}
break;
}
}
// apply relocations
for(int i=0; i<relasz/sizeof(Elf64_Rela); i++) {
switch(rela[i].r_info & 0xffffffffl) {
case R_X86_64_JMP_SLOT:
if(r_jmp_slot(&rela[i])) {
return -1;
}
break;
case R_X86_64_64:
if(r_direct_64(&rela[i])) {
return -1;
}
break;
case R_X86_64_GLOB_DAT:
if(r_glob_dat(&rela[i])) {
return -1;
}
break;
case R_X86_64_RELATIVE:
if(r_relative(&rela[i])) {
return -1;
}
break;
}
}
return 0;
}
int
__rtld_payload_init(void) {
static const unsigned char privcaps[16] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
int pid = __syscall(SYS_getpid);
unsigned char caps[16];
unsigned long rootdir;
int err;
if(!(rootdir=kernel_get_proc_rootdir(pid))) {
return -1;
}
if(kernel_get_ucred_caps(pid, caps)) {
return -1;
}
if(kernel_set_proc_rootdir(pid, kernel_get_root_vnode())) {
return -1;
}
if(kernel_set_ucred_caps(pid, privcaps)) {
return -1;
}
if(!KERNEL_DLSYM(0x2, memcpy)) {
return -1;
}
err = payload_load();
if(kernel_set_proc_rootdir(pid, rootdir)) {
return -1;
}
if(kernel_set_ucred_caps(pid, caps)) {
return -1;
}
return err;
}
int
__rtld_payload_fini(void) {
int err;
err =__rtld_lib_close(g_libhead);
__rtld_lib_destroy(g_libhead);
return err;
}
+738
View File
@@ -0,0 +1,738 @@
/* Copyright (C) 2024 John Törnblom
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>. */
#include "elf.h"
#include "kernel.h"
#include "klog.h"
#include "rtld.h"
#include "syscall.h"
/**
* Dependencies to standard libraries.
**/
static char* (*strdup)(const char*) = 0;
static int (*strcmp)(const char*, const char*) = 0;
static char* (*strcat)(char*, const char*) = 0;
static char* (*strcpy)(char*, const char*) = 0;
static void* (*malloc)(unsigned long) = 0;
static void* (*calloc)(unsigned long, unsigned long) = 0;
static void* (*memcpy)(void*, const void*, unsigned long) = 0;
static void (*free)(void*) = 0;
static char* (*getcwd)(char*, unsigned long) = 0;
static char* (*getenv)(const char*) = 0;
/**
* Standard libc macros.
**/
#define PROT_NONE 0x00
#define PROT_READ 0x01
#define PROT_WRITE 0x02
#define PROT_EXEC 0x04
#define MAP_SHARED 0x0001
#define MAP_PRIVATE 0x0002
#define MAP_FIXED 0x0010
#define MAP_ANON 0x1000
#define MAP_FAILED ((void*)-1)
#define SEEK_SET 0
#define SEEK_END 2
/**
* Convenient macros.
**/
#define PAGE_SIZE 0x4000
#define ROUND_PG(x) (((x) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1))
#define TRUNC_PG(x) ((x) & ~(PAGE_SIZE - 1))
#define PFLAGS(x) ((((x) & PF_R) ? PROT_READ : 0) | \
(((x) & PF_W) ? PROT_WRITE : 0) | \
(((x) & PF_X) ? PROT_EXEC : 0))
/**
* Extend the rtld_lib_t type with auxiliary parameters needed by .so loader.
* This requires -fms-extensions.
**/
typedef struct rtld_so_lib {
struct rtld_lib;
unsigned char* image;
Elf64_Ehdr *ehdr;
Elf64_Phdr *phdr;
Elf64_Shdr *shdr;
char* strtab;
Elf64_Sym* symtab;
unsigned long symtab_size;
void* base_addr;
unsigned long base_size;
} rtld_so_lib_t;
/**
* Convenient function for a custom sony syscall that allocates shared memory
* with JIT code execution permissions.
**/
static int
jitshm_create(const char* name, unsigned long size, int flags) {
return (int)__syscall(0x215, name, size, flags);
}
/**
* Convenient function for a custom sony syscall that creates an alias to
* JIT memory for write permissions.
**/
static int
jitshm_alias(int fd, int flags) {
return (int)__syscall(0x216, fd, flags);
}
/**
* Convenient function for the standard mmap syscall.
**/
static void*
mmap(void* addr, unsigned long len, int prot, int flags, int fd,
unsigned long offset) {
return (void*)__syscall(SYS_mmap, addr, len, prot, flags, fd, offset);
}
/**
* Convenient function for the standard mprotect syscall.
**/
static int
mprotect(void* addr, unsigned long len, int prot) {
return (int)__syscall(SYS_mprotect, addr, len, prot);
}
/**
* Convenient function for the standard munmap syscall.
**/
static int
munmap(void* addr, unsigned long len) {
return (int)__syscall(SYS_munmap, addr, len);
}
/**
* Read a file from disk at the given path.
**/
static unsigned char*
readfile(const char* path) {
unsigned char* buf;
long len;
int fd;
if((fd=(int)__syscall(SYS_open, path, 0)) < 0) {
klog_perror("open");
return 0;
}
if((len=__syscall(SYS_lseek, fd, 0, SEEK_END)) < 0) {
klog_perror("lseek");
__syscall(SYS_close, fd);
return 0;
}
if(__syscall(SYS_lseek, fd, 0, SEEK_SET) < 0) {
klog_perror("lseek");
__syscall(SYS_close, fd);
return 0;
}
if(!(buf=malloc(len))) {
klog_perror("malloc");
__syscall(SYS_close, fd);
return 0;
}
if(__syscall(SYS_read, fd, buf, len) != len) {
klog_perror("read");
free(buf);
__syscall(SYS_close, fd);
return 0;
}
if(__syscall(SYS_close, fd) < 0) {
klog_perror("close");
free(buf);
return 0;
}
return buf;
}
/**
* Find a shared object image with the given soname.
*
* The search order is as follows:
* 1 - absolute path
* 2 - current work dir (cwd)
* 3 - paths in the env var LD_LIBRARY_PATH
* 4 - /user/homebrew/lib/
**/
static int
so_find(const char* cwd, const char *soname, char* path) {
unsigned long off = 0;
const char *ldpaths;
int err = 0;
if(!soname || !*soname || !path) {
return -1;
}
path[0] = 0;
if(soname[0] == '/') {
if(!(err=__syscall(SYS_access, soname, 0))) {
strcpy(path, soname);
}
return err;
}
if(cwd && *cwd) {
strcpy(path, cwd);
strcat(path, "/");
strcat(path, soname);
if(!__syscall(SYS_access, path, 0)) {
return 0;
}
}
if((ldpaths=getenv("LD_LIBRARY_PATH"))) {
for(int i=0; ldpaths[i]; i++) {
path[off++] = ldpaths[i];
if(path[off] == ':' || ldpaths[i+1] == 0) {
path[off] = '/';
path[off+1] = 0;
strcat(path, soname);
off = 0;
if(!__syscall(SYS_access, path, 0)) {
return 0;
}
}
}
}
strcpy(path, "/user/homebrew/lib/");
strcat(path, soname);
if(!__syscall(SYS_access, path, 0)) {
return 0;
}
path[0] = 0;
return -1;
}
/**
* Process the R_X86_64_GLOB_DAT relocation type.
**/
static int
r_glob_dat(rtld_so_lib_t* lib, Elf64_Rela* rela) {
Elf64_Sym* sym = lib->symtab + ELF64_R_SYM(rela->r_info);
const char* name = lib->strtab + sym->st_name;
void* loc = lib->base_addr + rela->r_offset;
void* val = 0;
for(rtld_lib_t *l=(rtld_lib_t *)lib->next; l!=0; l=l->next) {
if((val=__rtld_lib_sym(l, name))) {
memcpy(loc, &val, sizeof(val));
return 0;
}
}
if(ELF64_ST_BIND(sym->st_info) == STB_WEAK) {
return 0;
}
klog_printf("%s: unable to resolve '%s'\n", lib->soname, name);
return -1;
}
/**
* Process the R_X86_64_GLOB_DAT relocation type.
**/
static int
r_jmp_slot(rtld_so_lib_t* lib, Elf64_Rela* rela) {
return r_glob_dat(lib, rela);
}
/**
* Process the R_X86_64_64 relocation type.
**/
static int
r_direct_64(rtld_so_lib_t* lib, Elf64_Rela* rela) {
Elf64_Sym* sym = lib->symtab + ELF64_R_SYM(rela->r_info);
const char* name = lib->strtab + sym->st_name;
void* loc = lib->base_addr + rela->r_offset;
void* val = 0;
for(rtld_lib_t *l=(rtld_lib_t *)lib->next; l!=0; l=l->next) {
if((val=__rtld_lib_sym(l, name))) {
val += rela->r_addend;
memcpy(loc, &val, sizeof(val));
return 0;
}
}
if(ELF64_ST_BIND(sym->st_info) == STB_WEAK) {
return 0;
}
klog_printf("%s: unable to resolve '%s'\n", lib->soname, name);
return -1;
}
/**
* Process the R_X86_64_RELATIVE relocation type.
**/
static int
r_relative(rtld_so_lib_t *lib, Elf64_Rela* rela) {
void* loc = lib->base_addr + rela->r_offset;
void* val = lib->base_addr + rela->r_addend;
memcpy(loc, &val, sizeof(val));
return 0;
}
/**
* Process the DT_NEEDED entry in the .dynamic section.
**/
static int
dt_needed(rtld_so_lib_t *lib, const char* soname) {
rtld_lib_t* it = (rtld_lib_t*)lib;
rtld_lib_t* needed;
if(!(needed=__rtld_lib_new(it, soname))) {
return -1;
}
if(__rtld_lib_open(needed)) {
klog_printf("%s: unable to load '%s'\n", lib->soname, soname);
__rtld_lib_destroy(needed);
return -1;
}
while(it->next) {
it = it->next;
}
it->next = needed;
return 0;
}
/**
* Process a program header with the entry type PT_LOAD.
**/
static int
pt_load(rtld_so_lib_t *lib, Elf64_Phdr *phdr) {
if(!phdr->p_memsz) {
return 0;
}
if(!phdr->p_filesz) {
return 0;
}
memcpy(lib->base_addr + phdr->p_vaddr,
lib->image + phdr->p_offset,
phdr->p_filesz);
return 0;
}
/**
* Reload a PT_LOAD program header that needs executable permissions.
*
* This is required to circumvent restrictions Sony put into place when user
* space processes attempts to allocate memory with execute permissions.
* We circumvent the protection using JIT capabillities provided by a couple of
* custom Sony syscalls.
*
* Note: code allocated via JIT are not permitted to run syscall instructions.
* To make syscalls, payloads can instead slide into a page that can,
* e.g., at getpid+0xa in libkernel.sprx.
**/
static int
pt_reload(rtld_so_lib_t *lib, Elf64_Phdr *phdr) {
unsigned long memsz = ROUND_PG(phdr->p_memsz);
void* addr = lib->base_addr + phdr->p_vaddr;
int prot = PFLAGS(phdr->p_flags);
int alias_fd = -1;
int shm_fd = -1;
int error = 0;
void* data;
void* addrx = 0;
void* addrw = 0;
// Backup data.
if(!(data=malloc(memsz))) {
klog_perror("malloc");
return -1;
}
memcpy(data, addr, memsz);
// Create shm with executable permissions.
if((shm_fd=jitshm_create(0, memsz, prot | PROT_WRITE | PROT_EXEC)) < 0) {
klog_perror("jitshm_create");
error = -1;
}
// Create an shm alias fd with write permissions.
else if((alias_fd=jitshm_alias(shm_fd, PROT_WRITE)) < 0) {
klog_perror("jitshm_alias");
error = -1;
}
// Map shm into an executable address space.
else if((addrx=mmap(addr, memsz, prot, MAP_FIXED | MAP_PRIVATE,
shm_fd, 0)) == MAP_FAILED) {
klog_perror("mmap");
error = -1;
}
// Map shm alias into a writable address space.
else if((addrw=mmap(0, memsz, PROT_WRITE, MAP_SHARED,
alias_fd, 0)) == MAP_FAILED) {
klog_perror("mmap");
error = -1;
}
// Resore data.
else {
memcpy(addrw, data, memsz);
munmap(addrw, memsz);
}
// Cleanup.
free(data);
if(alias_fd != -1) {
__syscall(SYS_close, alias_fd);
}
if(shm_fd != -1) {
__syscall(SYS_close, shm_fd);
}
return error;
}
/**
* Process a program header with the entry type PT_DYNAMIC.
**/
static int
pt_dynamic(rtld_so_lib_t *lib, Elf64_Phdr *phdr) {
Elf64_Dyn *dyn = (Elf64_Dyn *)(lib->image + phdr->p_offset);
unsigned int *gnu_hash = 0;
// find lookup tables
for(unsigned long i=0; dyn[i].d_tag!=DT_NULL; i++) {
switch (dyn[i].d_tag) {
case DT_SYMTAB:
lib->symtab = (Elf64_Sym*)(lib->image + lib->phdr->p_offset +
dyn[i].d_un.d_ptr);
break;
case DT_GNU_HASH:
gnu_hash = (unsigned int*)(lib->image + lib->phdr->p_offset +
dyn[i].d_un.d_ptr);
break;
case DT_STRTAB:
lib->strtab = (char*)(lib->image + lib->phdr->p_offset +
dyn[i].d_un.d_ptr);
break;
}
}
// figure out the symtab size
if(gnu_hash) {
lib->symtab_size = gnu_hash[0] + gnu_hash[1] + gnu_hash[2];
lib->symtab_size *= sizeof(Elf64_Sym);
}
// load needed libraries
for(unsigned long i=0; dyn[i].d_tag!=DT_NULL; i++) {
switch (dyn[i].d_tag) {
case DT_NEEDED:
if(dt_needed(lib, lib->strtab + dyn[i].d_un.d_ptr)) {
return -1;
}
break;
}
}
return 0;
}
/**
* Load a shared object into memory, and parse its attributes.
**/
static int
so_load(rtld_so_lib_t* lib, const char* path) {
unsigned long min_vaddr = -1;
unsigned long max_vaddr = 0;
Elf64_Rela* rela;
int error = 0;
if(!(lib->image=readfile(path))) {
return -1;
}
lib->ehdr = (Elf64_Ehdr*)lib->image;
lib->phdr = (Elf64_Phdr*)(lib->image + lib->ehdr->e_phoff);
lib->shdr = (Elf64_Shdr*)(lib->image + lib->ehdr->e_shoff);
// compute size of virtual memory region
for(unsigned long i=0; i<lib->ehdr->e_phnum; i++) {
if(lib->phdr[i].p_vaddr < min_vaddr) {
min_vaddr = lib->phdr[i].p_vaddr;
}
if(max_vaddr < lib->phdr[i].p_vaddr + lib->phdr[i].p_memsz) {
max_vaddr = lib->phdr[i].p_vaddr + lib->phdr[i].p_memsz;
}
}
min_vaddr = TRUNC_PG(min_vaddr);
max_vaddr = ROUND_PG(max_vaddr);
lib->base_size = max_vaddr - min_vaddr;
int flags = MAP_PRIVATE | MAP_ANON;
int prot = PROT_READ | PROT_WRITE;
if(lib->ehdr->e_type != ET_DYN) {
klog_printf("%s: Not a shared object\n", lib->soname);
return -1;
}
// reserve an address space of sufficient size
if((lib->base_addr=mmap(0, lib->base_size, prot, flags,
-1, 0)) == MAP_FAILED) {
return -1;
}
for(unsigned long i=0; i<lib->ehdr->e_phnum && !error; i++) {
switch(lib->phdr[i].p_type) {
case PT_LOAD:
error = pt_load(lib, &lib->phdr[i]);
break;
case PT_DYNAMIC:
error = pt_dynamic(lib, &lib->phdr[i]);
break;
}
}
for(unsigned long i=0; i<lib->ehdr->e_shnum && !error; i++) {
if(lib->shdr[i].sh_type != SHT_RELA) {
continue;
}
rela = (Elf64_Rela*)(lib->image + lib->shdr[i].sh_offset);
for(int j=0; j<lib->shdr[i].sh_size/sizeof(Elf64_Rela); j++) {
switch(rela[j].r_info & 0xffffffffl) {
case R_X86_64_GLOB_DAT:
error = r_glob_dat(lib, &rela[j]);
break;
case R_X86_64_64:
error = r_direct_64(lib, &rela[j]);
break;
case R_X86_64_JMP_SLOT:
error = r_jmp_slot(lib, &rela[j]);
break;
case R_X86_64_RELATIVE:
error = r_relative(lib, &rela[j]);
break;
}
}
}
// set protection bits on mapped memory
for(int i=0; i<lib->ehdr->e_phnum && !error; i++) {
if(lib->phdr[i].p_type != PT_LOAD || lib->phdr[i].p_memsz == 0) {
continue;
}
if(lib->phdr[i].p_flags & PF_X) {
error = pt_reload(lib, &lib->phdr[i]);
} else {
if(mprotect(lib->base_addr + lib->phdr[i].p_vaddr,
ROUND_PG(lib->phdr[i].p_memsz),
PFLAGS(lib->phdr[i].p_flags))) {
klog_perror("mprotect");
error = 1;
}
}
}
if(error) {
return -1;
}
return 0;
}
static int
so_open(rtld_lib_t* ctx) {
rtld_so_lib_t* lib = (rtld_so_lib_t*)ctx;
char path[0x400];
char cwd[0x400];
if(!getcwd(cwd, sizeof(cwd))) {
klog_perror("getcwd");
return -1;
}
if(so_find(cwd, lib->soname, path)) {
return -1;
}
return so_load(lib, path);
}
static int
so_close(rtld_lib_t* ctx) {
rtld_so_lib_t* lib = (rtld_so_lib_t*)ctx;
if(lib->image) {
free(lib->image);
}
if(lib->base_addr) {
munmap(lib->base_addr, lib->base_size);
}
lib->image = 0;
lib->ehdr = 0;
lib->phdr = 0;
lib->shdr = 0;
lib->strtab = 0;
lib->symtab = 0;
lib->symtab_size = 0;
lib->base_addr = 0;
lib->base_size = 0;
return 0;
}
static void*
so_sym(rtld_lib_t* ctx, const char* name) {
rtld_so_lib_t* lib = (rtld_so_lib_t*)ctx;
if(!lib->symtab || !lib->strtab || !lib->base_addr) {
return 0;
}
for(unsigned long i=0; i<lib->symtab_size/sizeof(Elf64_Sym); i++) {
if(!lib->symtab[i].st_size) {
continue;
}
if(!strcmp(name, lib->strtab + lib->symtab[i].st_name)) {
return lib->base_addr + lib->symtab[i].st_value;
}
}
return 0;
}
static void
so_destroy(rtld_lib_t* ctx) {
rtld_so_lib_t* lib = (rtld_so_lib_t*)ctx;
so_close(ctx);
free(lib->soname);
free(lib);
}
rtld_lib_t*
__rtld_so_new(rtld_lib_t* prev, const char *soname) {
rtld_so_lib_t* lib = calloc(1, sizeof(rtld_so_lib_t));
lib->soname = strdup(soname);
lib->prev = prev;
lib->open = so_open;
lib->sym = so_sym;
lib->close = so_close;
lib->destroy = so_destroy;
return (rtld_lib_t*)lib;
}
int
__rtld_so_init(void) {
unsigned int libc = 0x2;
if(!KERNEL_DLSYM(libc, strdup)) {
return -1;
}
if(!KERNEL_DLSYM(libc, strcmp)) {
return -1;
}
if(!KERNEL_DLSYM(libc, strcat)) {
return -1;
}
if(!KERNEL_DLSYM(libc, strcpy)) {
return -1;
}
if(!KERNEL_DLSYM(libc, malloc)) {
return -1;
}
if(!KERNEL_DLSYM(libc, calloc)) {
return -1;
}
if(!KERNEL_DLSYM(libc, memcpy)) {
return -1;
}
if(!KERNEL_DLSYM(libc, free)) {
return -1;
}
if(!KERNEL_DLSYM(libc, getcwd)) {
return -1;
}
if(!KERNEL_DLSYM(libc, getenv)) {
return -1;
}
return 0;
}
+481
View File
@@ -0,0 +1,481 @@
/* Copyright (C) 2024 John Törnblom
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>. */
#include "elf.h"
#include "klog.h"
#include "kernel.h"
#include "nid.h"
#include "rtld.h"
#include "syscall.h"
/**
* Dependencies to standard libraries.
**/
static char* (*strdup)(const char*) = 0;
static int (*strcmp)(const char*, const char*) = 0;
static int (*strncmp)(const char*, const char*, unsigned long) = 0;
static int (*sprintf)(char*, const char*, ...) = 0;
static void* (*calloc)(unsigned long, unsigned long) = 0;
static void* (*malloc)(unsigned long) = 0;
static void (*free)(void*) = 0;
static char* (*getcwd)(char*, unsigned long) = 0;
static int (*sceKernelLoadStartModule)(const char*, unsigned long, const void*,
unsigned int, void*, int*) = 0;
static int (*sceKernelStopUnloadModule)(int, unsigned long, const void*, unsigned int,
const void*, void*) = 0;
static int (*sceSysmoduleLoadModuleInternal)(unsigned int) = 0;
/**
* Extend the rtld_lib_t type with auxiliary parameters needed by .sprx loader.
* This requires -fms-extensions.
**/
typedef struct rtld_sprx_lib {
struct rtld_lib;
int handle;
char* strtab;
Elf64_Sym* symtab;
unsigned long symtab_size;
void* base_addr;
unsigned long base_size;
int unload_on_close;
} rtld_sprx_lib_t;
/**
*
**/
typedef struct sysmodtab {
const char* name;
unsigned int handle;
} sysmodtab_t;
/**
* Lookup table for sceSysmoduleLoadModuleInternal().
**/
static const sysmodtab_t sysmodtab[] = {
{"libSceAbstractLocal.sprx", 0x8000005f},
{"libSceAbstractStorage.sprx", 0x80000058},
{"libSceAbstractTcs.sprx", 0x800000a1},
{"libSceAbstractTwitter.sprx", 0x80000062},
{"libSceAbstractYoutube.sprx", 0x80000061},
{"libSceAc3Enc.sprx", 0x80000013},
{"libSceAgc.sprx", 0x80000094},
{"libSceAgcDriver.sprx", 0x80000080},
{"libSceAgcResourceRegistration.sprx", 0x80000093},
{"libSceAgcVsh.sprx", 0x80000086},
{"libSceAgcVshDebug.sprx", 0x80000087},
{"libSceAjm.native.sprx", 0x80000023},
{"libSceAjmi.sprx", 0x8000007e},
{"libSceAmpr.sprx", 0x800000b7},
{"libSceAppChecker.sprx", 0x80000032},
{"libSceAppDbShellCoreClient.sprx", 0x800000a7},
{"libSceAppInstUtil.sprx", 0x80000014},
{"libSceAsyncStorageInternal.sprx", 0x80000077},
{"libSceAudioIn.sprx", 0x80000002},
{"libSceAudioOut.sprx", 0x80000001},
{"libSceAudioSystem.sprx", 0x80000083},
{"libSceAudiodecCpuDtsHdMa.sprx", 0x8000002d},
{"libSceAudiodecCpuLpcm.sprx", 0x8000002e},
{"libSceAudiodecCpuTrhd.sprx", 0x80000082},
{"libSceAvSetting.sprx", 0x80000021},
{"libSceAvcap2.sprx", 0x80000085},
{"libSceBackupRestoreUtil.sprx", 0x8000003f},
{"libSceBgft.sprx", 0x8000002a},
{"libSceBgsStorage.sprx", 0x800000a3},
{"libSceCamera.sprx", 0x8000001a},
{"libSceCdlgUtilServer.sprx", 0x80000007},
{"libSceCommonDialog.sprx", 0x80000018},
{"libSceComposite.sprx", 0x8000008a},
{"libSceCompositeExt.sprx", 0x8000008b},
{"libSceContentListController.sprx", 0x800000ad},
{"libSceDataTransfer.sprx", 0x80000057},
{"libSceDbg.sprx", 0x80000025},
{"libSceDipsw.sprx", 0x80000029},
{"libSceDseehx.sprx", 0x80000056},
{"libSceDtsEnc.sprx", 0x80000028},
{"libSceEmbeddedTts.sprx", 0x8000009c},
{"libSceEmbeddedTtsCoreG3.sprx", 0x8000009b},
{"libSceFsInternalForVsh.sprx", 0x80000066},
{"libSceGLSlimVSH.sprx", 0x800000a9},
{"libSceGifParser.sprx", 0x8000005e},
{"libSceGpuCapture.sprx", 0x8000007f},
{"libSceGpuTrace.sprx", 0x8000007b},
{"libSceGvMp4Parser.sprx", 0x8000005c},
{"libSceHidControl.sprx", 0x80000017},
{"libSceHttp.sprx", 0x8000000a},
{"libSceHttp2.sprx", 0x8000008c},
{"libSceHttpCache.sprx", 0x80000078},
{"libSceIcu.sprx", 0x800000a8},
{"libSceIdu.sprx", 0x800000a6},
{"libSceImageUtil.sprx", 0x80000059},
{"libSceIpmi.sprx", 0x8000001d},
{"libSceJemspace.sprx", 0x8000009e},
{"libSceJitBridge.sprx", 0x8000006f},
{"libSceJpegParser.sprx", 0x8000005b},
{"libSceJsc.sprx", 0x800000b0},
{"libSceJscCompiler.sprx", 0x80000070},
{"libSceJxr.sprx", 0x800000b4},
{"libSceJxrParser.sprx", 0x800000b5},
{"libSceKbEmulate.sprx", 0x80000031},
{"libSceLibreSsl.sprx", 0x80000065},
{"libSceLibreSsl3.sprx", 0x800000b8},
{"libSceLoginMgrServer.sprx", 0x80000045},
{"libSceMarlin.sprx", 0x80000027},
{"libSceMat.sprx", 0x80000048},
{"libSceMbus.sprx", 0x8000001e},
{"libSceMediaFrameworkInterface.sprx", 0x80000095},
{"libSceMediaFrameworkUtil.sprx", 0x800000b6},
{"libSceMetadataReaderWriter.sprx", 0x8000005a},
{"libSceNKWeb.sprx", 0x80000079},
{"libSceNKWebKit.sprx", 0x8000007a},
{"libSceNet.sprx", 0x8000001c},
{"libSceNetCtl.sprx", 0x80000009},
{"libSceNgs2.sprx", 0x80000090},
{"libSceNpCommon.sprx", 0x8000000c},
{"libSceNpGameIntent.sprx", 0x8000008d},
{"libSceNpManager.sprx", 0x8000000d},
{"libSceNpRemotePlaySessionSignaling.sprx", 0x8000009a},
{"libSceNpSns.sprx", 0x8000001b},
{"libSceNpTcs.sprx", 0x800000a0},
{"libSceNpWebApi.sprx", 0x8000000e},
{"libSceNpWebApi2.sprx", 0x8000008f},
{"libSceOpusCeltDec.sprx", 0x80000044},
{"libSceOpusCeltEnc.sprx", 0x80000043},
{"libSceOpusDec.sprx", 0x80000069},
{"libSceOpusSilkEnc.sprx", 0x80000068},
{"libSceOrbisCompat.sprx", 0x80000071},
{"libScePad.sprx", 0x80000024},
{"libScePngParser.sprx", 0x8000005d},
{"libScePosixForWebKit.sprx", 0x80000098},
{"libScePsm.sprx", 0x80000030},
{"libSceRazorCpu_debug.sprx", 0x80000075},
{"libSceRegMgr.sprx", 0x8000001f},
{"libSceRemotePlayClientIpc.sprx", 0x800000b9},
{"libSceResourceArbitrator.sprx", 0x80000092},
{"libSceRnpsAppMgr.sprx", 0x80000076},
{"libSceRtc.sprx", 0x80000020},
{"libSceSaveData.sprx", 0x8000000f},
{"libSceShareInternal.native.sprx", 0x8000008e},
{"libSceSocialScreen.sprx", 0x800000ae},
{"libSceSsl.sprx", 0x8000000b},
{"libSceSulphaDrv.sprx", 0x8000003b},
{"libSceSysCore.sprx", 0x80000004},
{"libSceSysUtil.sprx", 0x80000026},
{"libSceSystemLogger2.sprx", 0x800000b3},
{"libSceSystemLogger2Delivery.sprx", 0x80000089},
{"libSceSystemLogger2Game.sprx", 0x8000009f},
{"libSceSystemLogger2NativeQueueClient.sprx", 0x80000088},
{"libSceSystemService.sprx", 0x80000010},
{"libSceSystemTts.sprx", 0x80000097},
{"libSceTEEClient.sprx", 0x800000a2},
{"libSceUserService.sprx", 0x80000011},
{"libSceVcodec.sprx", 0x80000091},
{"libSceVdecCore.native.sprx", 0x80000015},
{"libSceVdecSavc2.native.sprx", 0x80000036},
{"libSceVdecShevc.native.sprx", 0x8000003c},
{"libSceVdecSvp9.native.sprx", 0x800000af},
{"libSceVenc.sprx", 0x80000084},
{"libSceVideoOut.sprx", 0x80000022},
{"libSceVideoOutSecondary.sprx", 0x80000046},
{"libSceVideoStreamingEngine_sys.sprx", 0x800000b2},
{"libSceVisionManager.sprx", 0x80000012},
{"libSceVnaInternal.sprx", 0x8000007c},
{"libSceVnaWebsocket.sprx", 0x8000007d},
{"libSceVoiceCommand.sprx", 0x80000099},
{"libSceWeb.sprx", 0x80000072},
{"libSceWebKit2.sprx", 0x80000073},
{"libSceWebKit2Secure.sprx", 0x80000074},
{"libSceWebmParserMdrw.sprx", 0x800000a4},
{"libcairo.sprx", 0x800000ac},
{"libcurl.sprx", 0x800000b1},
{"libicu.sprx", 0x800000aa},
{"libpng16.sprx", 0x800000ab}
};
/**
* Figure out the absolute path to an sprx file.
**/
static int
sprx_find(const char* cwd, const char* soname, char *path) {
if(*soname == '/') {
sprintf(path, "%s", soname);
} else if(cwd) {
sprintf(path, "%s/%s", cwd, soname);
} else {
sprintf(path, "%s", soname);
}
if(!__syscall(SYS_access, path, 0)) {
return 0;
}
sprintf(path, "/system/priv/lib/%s", soname);
if(!__syscall(SYS_access, path, 0)) {
return 0;
}
sprintf(path, "/system/common/lib/%s", soname);
if(!__syscall(SYS_access, path, 0)) {
return 0;
}
sprintf(path, "/system_ex/priv_ex/lib/%s", soname);
if(!__syscall(SYS_access, path, 0)) {
return 0;
}
sprintf(path, "/system_ex/common_ex/lib/%s", soname);
if(!__syscall(SYS_access, path, 0)) {
return 0;
}
return -1;
}
static int
sprx_open(rtld_lib_t* ctx) {
rtld_sprx_lib_t* lib = (rtld_sprx_lib_t*)ctx;
unsigned int handle = 0;
dynlib_dynsec_t dynsec;
dynlib_obj_t obj;
char path[0x400];
char cwd[0x400];
int unload = 0;
int error = 0;
// check if lib is is an alias for libkernel
if(!strcmp(lib->soname, "libkernel.sprx") ||
!strcmp(lib->soname, "libkernel_web.sprx") ||
!strcmp(lib->soname, "libkernel_sys.sprx") ||
!strcmp(lib->soname, "libdl.sprx") ||
!strcmp(lib->soname, "libpthread.sprx")) {
if(kernel_dynlib_dlsym(-1, 0x1, "sceKernelDlsym")) {
handle = 1;
} else {
handle = 0x2001;
}
}
// check if lib is already opened
else if(kernel_dynlib_handle(-1, lib->soname, &handle) < 0) {
if(!getcwd(cwd, sizeof(cwd))) {
klog_perror("getcwd");
return -1;
}
if(sprx_find(cwd, lib->soname, path)) {
return -1;
}
for(int i=0; i<sizeof(sysmodtab)/sizeof(sysmodtab[0]); i++) {
if(!strcmp(lib->soname, sysmodtab[i].name)) {
if((error=sceSysmoduleLoadModuleInternal(sysmodtab[i].handle))) {
klog_printf("sceSysmoduleLoadModuleInternal: 0x%x\n", error);
return -1;
}
}
}
if((handle=sceKernelLoadStartModule(path, 0, 0, 0, 0, 0)) < 0) {
klog_printf("sceKernelLoadStartModule: 0x%x\n", handle);
return -1;
}
unload = 1;
}
if(kernel_dynlib_obj(-1, handle, &obj) < 0) {
error = 1;
}
else if(kernel_copyout(obj.dynsec, &dynsec, sizeof(dynsec)) < 0) {
error = 1;
}
else if(!(lib->strtab=malloc(dynsec.strtabsize))) {
klog_perror("malloc");
return -1;
}
else if(kernel_copyout(dynsec.strtab, lib->strtab, dynsec.strtabsize) < 0) {
error = 1;
}
else if(!(lib->symtab=malloc(dynsec.symtabsize))) {
klog_perror("malloc");
return -1;
}
else if(kernel_copyout(dynsec.symtab, lib->symtab, dynsec.symtabsize) < 0) {
error = 1;
}
if(error) {
klog_puts("Unknown kernel I/O error");
return -1;
}
lib->handle = handle;
lib->unload_on_close = unload;
lib->base_addr = (void*)obj.mapbase;
lib->symtab_size = dynsec.symtabsize;
return 0;
}
static void*
sprx_sym(rtld_lib_t* ctx, const char* name) {
rtld_sprx_lib_t* lib = (rtld_sprx_lib_t*)ctx;
char nid[12];
if(!lib->symtab || !lib->strtab || !lib->base_addr) {
return 0;
}
nid_encode(name, nid);
for(unsigned long i=0; i<lib->symtab_size/sizeof(Elf64_Sym); i++) {
if(!lib->symtab[i].st_size) {
continue;
}
if(!strncmp(nid, lib->strtab + lib->symtab[i].st_name, 11)) {
return lib->base_addr + lib->symtab[i].st_value;
}
}
return 0;
}
static int
sprx_close(rtld_lib_t* ctx) {
rtld_sprx_lib_t* lib = (rtld_sprx_lib_t*)ctx;
int err = 0;
if(lib->unload_on_close) {
if((err=sceKernelStopUnloadModule(lib->handle, 0, 0, 0, 0, 0))) {
return err;
}
}
if(lib->strtab) {
free(lib->strtab);
}
if(lib->symtab) {
free(lib->symtab);
}
lib->handle = -1;
lib->strtab = 0;
lib->symtab = 0;
lib->symtab_size = 0;
lib->base_addr = 0;
lib->base_size = 0;
lib->unload_on_close = 0;
return 0;
}
static void
sprx_destroy(rtld_lib_t* ctx) {
rtld_sprx_lib_t* lib = (rtld_sprx_lib_t*)ctx;
if(sprx_close(ctx)) {
lib->unload_on_close = 0;
sprx_close(ctx);
}
free(lib->soname);
free(lib);
}
rtld_lib_t*
__rtld_sprx_new(rtld_lib_t* prev, const char *soname) {
rtld_sprx_lib_t* lib = calloc(1, sizeof(rtld_sprx_lib_t));
lib->soname = strdup(soname);
lib->prev = prev;
lib->open = sprx_open;
lib->sym = sprx_sym;
lib->close = sprx_close;
lib->destroy = sprx_destroy;
return (rtld_lib_t*)lib;
}
int
__rtld_sprx_init(void) {
unsigned int libSceSysmodule;
unsigned int libkernel = 0x1;
unsigned int libc = 0x2;
if(!kernel_dynlib_dlsym(-1, libkernel, "sceKernelDlsym")) {
libkernel = 0x2001;
}
if(!KERNEL_DLSYM(libkernel, sceKernelLoadStartModule)) {
return -1;
}
if(!KERNEL_DLSYM(libkernel, sceKernelStopUnloadModule)) {
return -1;
}
if(!KERNEL_DLSYM(libc, strdup)) {
return -1;
}
if(!KERNEL_DLSYM(libc, strcmp)) {
return -1;
}
if(!KERNEL_DLSYM(libc, strncmp)) {
return -1;
}
if(!KERNEL_DLSYM(libc, sprintf)) {
return -1;
}
if(!KERNEL_DLSYM(libc, calloc)) {
return -1;
}
if(!KERNEL_DLSYM(libc, malloc)) {
return -1;
}
if(!KERNEL_DLSYM(libc, free)) {
return -1;
}
if(!KERNEL_DLSYM(libc, getcwd)) {
return -1;
}
if(kernel_dynlib_handle(-1, "libSceSysmodule.sprx", &libSceSysmodule)) {
if((libSceSysmodule=sceKernelLoadStartModule("/system/common/lib/libSceSysmodule.sprx",
0, 0, 0, 0, 0)) <= 0) {
return -1;
}
}
if(!KERNEL_DLSYM(libSceSysmodule, sceSysmoduleLoadModuleInternal)) {
return -1;
}
return 0;
}
+6
View File
@@ -18,10 +18,16 @@ along with this program; see the file COPYING. If not, see
#define SYS_read 3
#define SYS_write 4
#define SYS_open 5
#define SYS_close 6
#define SYS_getpid 20
#define SYS_access 33
#define SYS_munmap 73
#define SYS_mprotect 74
#define SYS_setsockopt 105
#define SYS_sysctl 202
#define SYS_mmap 477
#define SYS_lseek 478
#define SYS_mdbg_call 573
#define SYS_sprx_dlsym 591
#define SYS_sprx_load 594
+1 -1
View File
@@ -47,7 +47,7 @@ SECTIONS {
PROVIDE_HIDDEN(__fini_array_end = .);
} : ph_rodata
.eh_frame_hdr : {
.eh_frame_hdr : ALIGN(CONSTANT(MAXPAGESIZE)) {
PROVIDE_HIDDEN(__eh_frame_hdr_start = .);
KEEP(*(.eh_frame_hdr))
PROVIDE_HIDDEN(__eh_frame_hdr_end = .);
+1 -1
View File
@@ -48,7 +48,7 @@ set(CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "")
#
# We only support positional independent executables
#
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Shared libs not available")
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Shared libs turned off by default")
set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "")
#
+121
View File
@@ -0,0 +1,121 @@
/* Copyright (C) 2024 John Törnblom
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>. */
#include <dlfcn.h>
#include <sys/errno.h>
/**
* See crt/rtld.h.
**/
struct rtld_lib;
typedef struct rtld_lib rtld_lib_t;
rtld_lib_t* __rtld_lib_new(rtld_lib_t* prev, const char* soname);
int __rtld_lib_open(rtld_lib_t* ctx);
void* __rtld_lib_sym(rtld_lib_t* ctx, const char* name);
int __rtld_lib_close(rtld_lib_t* ctx);
void __rtld_lib_destroy(rtld_lib_t* ctx);
char* _Strerror(int, char*);
static int g_dlerrno = 0;
void*
dlopen(const char *filename, int flags) {
rtld_lib_t* lib;
if(!(flags & RTLD_MODEMASK)) {
g_dlerrno = EINVAL;
return 0;
}
if(flags & RTLD_TRACE) {
g_dlerrno = ENOSYS;
return 0;
}
if(flags & RTLD_NOW) {
g_dlerrno = ENOSYS;
return 0;
}
if(flags & RTLD_GLOBAL) {
g_dlerrno = ENOSYS;
return 0;
}
if(!(lib=__rtld_lib_new(0, filename))) {
g_dlerrno = ENOMEM;
return 0;
}
if((g_dlerrno=__rtld_lib_open(lib))) {
__rtld_lib_destroy(lib);
return 0;
}
g_dlerrno = 0;
return lib;
}
void*
dlsym(void *handle, const char *symbol) {
void* addr;
if(handle == 0) {
g_dlerrno = ENOSYS;
return 0;
}
if(handle == RTLD_DEFAULT) {
g_dlerrno = ENOSYS;
return 0;
}
if(handle == RTLD_NEXT) {
g_dlerrno = ENOSYS;
return 0;
}
if(handle == RTLD_SELF) {
g_dlerrno = ENOSYS;
return 0;
}
if(!(addr=__rtld_lib_sym(handle, symbol))) {
g_dlerrno = EINVAL;
return 0;
}
g_dlerrno = 0;
return addr;
}
int
dlclose(void *handle) {
if(!(g_dlerrno=__rtld_lib_close(handle))) {
__rtld_lib_destroy(handle);
}
return g_dlerrno;
}
char*
dlerror(void) {
if(g_dlerrno) {
return _Strerror(g_dlerrno, 0);
}
return 0;
}
+4
View File
@@ -0,0 +1,4 @@
*.elf
*.o
*~
+71
View File
@@ -0,0 +1,71 @@
# Copyright (C) 2024 John Törnblom
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not see
# <http://www.gnu.org/licenses/>.
PS5_HOST ?= ps5
PS5_PORT ?= 9021
ifdef PS5_PAYLOAD_SDK
include $(PS5_PAYLOAD_SDK)/toolchain/prospero.mk
else
$(error PS5_PAYLOAD_SDK is undefined)
endif
ELF := hello_so.elf
CFLAGS := -Wall -g -Wl,--as-needed
# DAG:
# hello_so.elf
# /data/libtest.so
# libSceLibcInternal.sprx
# /data/libA.so
# libSceLibcInternal.sprx
# /data/libB.so
# libSceRandom.sprx
all: $(ELF) libtest.so
$(ELF): main.c libtest.so
$(CC) $(CFLAGS) -o $@ $^
libA.so: libA.c
$(CC) $(CFLAGS) -shared -o $@ $^ -Wl,-soname=/data/$@
libB.so: libB.c
$(CC) $(CFLAGS) -shared -lSceRandom -o $@ $^ -Wl,-soname=/data/$@
libtest.so: libtest.c libA.so libB.so
$(CC) $(CFLAGS) -shared -o $@ $^ -Wl,-soname=/data/$@
clean:
rm -f *.so *.o *.elf
upload:
curl -T libA.so ftp://$(PS5_HOST):2121/data/libA.so
curl -T libB.so ftp://$(PS5_HOST):2121/data/libB.so
curl -T libtest.so ftp://$(PS5_HOST):2121/data/libtest.so
test: $(ELF)
$(PS5_DEPLOY) -h $(PS5_HOST) -p $(PS5_PORT) $^
debug: $(ELF)
gdb \
-ex "target extended-remote $(PS5_HOST):2159" \
-ex "file $(ELF)" \
-ex "remote put $(ELF) /data/$(ELF)" \
-ex "set remote exec-file /data/$(ELF)" \
-ex "start"
+31
View File
@@ -0,0 +1,31 @@
/* Copyright (C) 2024 John Törnblom
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <unistd.h>
void
libA_hexdump(uint8_t* buf, size_t size) {
for(int i=0; i<size; i++) {
if(i%16 == 0 && i!= 0) {
printf("\n");
}
printf("%02x ", buf[i]);
}
printf("\n");
}
+27
View File
@@ -0,0 +1,27 @@
/* Copyright (C) 2024 John Törnblom
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>. */
#include <unistd.h>
int sceRandomGetRandomNumber(uint8_t* buf, size_t size);
int
libB_random(uint8_t* buf, size_t size) {
return sceRandomGetRandomNumber(buf, size);
}
+33
View File
@@ -0,0 +1,33 @@
/* Copyright (C) 2024 John Törnblom
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>. */
#include <string.h>
#include <unistd.h>
void libA_hexdump(uint8_t* buf, size_t size);
int libB_random(uint8_t* buf, size_t size);
void
libtest(void) {
uint8_t buf[0x40];
memset(buf, 0, sizeof(buf));
libB_random(buf, sizeof(buf));
libA_hexdump(buf, sizeof(buf));
}
+25
View File
@@ -0,0 +1,25 @@
/* Copyright (C) 2024 John Törnblom
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>. */
void libtest(void);
int main() {
libtest();
return 0;
}