[rpcsx-os] [orbis-kernel] implement lazy symbol binding

Use libSceSysmodule to resolve dependencies
Stub /dev/camera
This commit is contained in:
DH 2023-10-28 03:55:15 +03:00
parent 08a097e46e
commit 3232e57445
10 changed files with 345 additions and 79 deletions

View File

@ -69,6 +69,121 @@ static void allocateTlsOffset(orbis::Process *process, orbis::Module *module) {
module->isTlsDone = true;
}
static orbis::SysResult doPltRelocation(orbis::Process *process,
orbis::Module *module,
orbis::Relocation rel) {
auto symbol = module->symbols.at(rel.symbolIndex);
auto A = rel.addend;
auto B = reinterpret_cast<std::uint64_t>(module->base);
auto where = reinterpret_cast<std::uint64_t *>(B + rel.offset);
auto where32 = reinterpret_cast<std::uint32_t *>(B + rel.offset);
auto P = reinterpret_cast<std::uintptr_t>(where);
auto findDefModule = [module,
symbol] -> std::pair<orbis::Module *, std::uint64_t> {
if (symbol.moduleIndex == -1 || symbol.bind == orbis::SymbolBind::Local) {
return std::pair(module, symbol.address);
}
auto &defModule = module->importedModules.at(symbol.moduleIndex);
if (!defModule) {
// std::printf(
// "Delaying plt relocation '%s' ('%s'), symbol '%llx' in %s
// module\n", module->moduleName, module->soName, (unsigned long
// long)symbol.id,
// module->neededModules[symbol.moduleIndex].name.c_str());
return {};
}
auto library = module->neededLibraries.at(symbol.libraryIndex);
std::vector<std::string> foundInLibs;
for (auto defSym : defModule->symbols) {
if (defSym.id != symbol.id || defSym.bind == orbis::SymbolBind::Local) {
continue;
}
if (defSym.visibility == orbis::SymbolVisibility::Hidden) {
std::printf("Ignoring hidden symbol\n");
continue;
}
auto defLib = defModule->neededLibraries.at(defSym.libraryIndex);
if (defLib.name == library.name) {
return std::pair(defModule.get(), defSym.address);
}
foundInLibs.emplace_back(std::string_view(defLib.name));
}
for (auto nsDefModule : defModule->namespaceModules) {
for (auto defSym : nsDefModule->symbols) {
if (defSym.id != symbol.id || defSym.bind == orbis::SymbolBind::Local) {
continue;
}
if (defSym.visibility == orbis::SymbolVisibility::Hidden) {
std::printf("Ignoring hidden symbol\n");
continue;
}
auto defLib = nsDefModule->neededLibraries.at(defSym.libraryIndex);
if (defLib.name == library.name) {
return std::pair(nsDefModule.get(), defSym.address);
}
}
}
std::printf(
"'%s' ('%s') uses undefined symbol '%llx' in '%s' ('%s') module\n",
module->moduleName, module->soName, (unsigned long long)symbol.id,
defModule->moduleName, defModule->soName);
if (foundInLibs.size() > 0) {
std::printf("Requested library is '%s', exists in libraries: [",
library.name.c_str());
for (bool isFirst = true; auto &lib : foundInLibs) {
if (isFirst) {
isFirst = false;
} else {
std::printf(", ");
}
std::printf("'%s'", lib.c_str());
}
std::printf("]\n");
}
return std::pair(module, symbol.address);
};
switch (rel.relType) {
case kRelJumpSlot: {
bool isLazyBind = false; // TODO
if (isLazyBind) {
*where += B;
} else {
auto [defObj, S] = findDefModule();
if (defObj == nullptr) {
return orbis::ErrorCode::INVAL;
}
*where = reinterpret_cast<std::uintptr_t>(defObj->base) + S;
}
return {};
}
}
std::fprintf(stderr, "unimplemented relocation type %u\n",
(unsigned)rel.relType);
std::abort();
return {};
}
static orbis::SysResult doRelocation(orbis::Process *process,
orbis::Module *module,
orbis::Relocation rel) {
@ -80,18 +195,22 @@ static orbis::SysResult doRelocation(orbis::Process *process,
auto where32 = reinterpret_cast<std::uint32_t *>(B + rel.offset);
auto P = reinterpret_cast<std::uintptr_t>(where);
auto findDefModule = [module, symbol] {
auto findDefModule = [module, symbol,
rel] -> std::pair<orbis::Module *, std::uint64_t> {
if (symbol.moduleIndex == -1 || symbol.bind == orbis::SymbolBind::Local) {
return std::pair(module, symbol.address);
}
auto &defModule = module->importedModules.at(symbol.moduleIndex);
if (!defModule) {
std::printf(
"'%s' ('%s') uses undefined symbol '%llx' in unloaded module\n",
module->moduleName, module->soName, (unsigned long long)symbol.id);
std::printf("'%s' ('%s') uses undefined symbol '%llx' in unloaded module "
"'%s', rel %u\n",
module->moduleName, module->soName,
(unsigned long long)symbol.id,
module->neededModules.at(symbol.moduleIndex).name.c_str(),
rel.relType);
return std::pair(module, symbol.address);
return {};
}
auto library = module->neededLibraries.at(symbol.libraryIndex);
@ -162,12 +281,20 @@ static orbis::SysResult doRelocation(orbis::Process *process,
return {};
case kRel64: {
auto [defObj, S] = findDefModule();
if (defObj == nullptr) {
return orbis::ErrorCode::INVAL;
}
*where = reinterpret_cast<std::uintptr_t>(defObj->base) + S + A;
return {};
}
return {};
case kRelPc32: {
auto [defObj, S] = findDefModule();
if (defObj == nullptr) {
return orbis::ErrorCode::INVAL;
}
*where32 = reinterpret_cast<std::uintptr_t>(defObj->base) + S + A - P;
return {};
}
@ -175,37 +302,39 @@ static orbis::SysResult doRelocation(orbis::Process *process,
// return{};
case kRelGlobDat: {
auto [defObj, S] = findDefModule();
if (defObj == nullptr) {
return orbis::ErrorCode::INVAL;
}
*where = reinterpret_cast<std::uintptr_t>(defObj->base) + S;
return {};
}
case kRelJumpSlot: {
bool isLazyBind = false; // TODO
if (isLazyBind) {
*where += B;
} else {
auto [defObj, S] = findDefModule();
*where = reinterpret_cast<std::uintptr_t>(defObj->base) + S;
}
return {};
}
case kRelRelative:
*where = B + A;
return {};
case kRelDtpMod64: {
auto [defObj, S] = findDefModule();
if (defObj == nullptr) {
return orbis::ErrorCode::INVAL;
}
*where += defObj->tlsIndex;
return {};
}
case kRelDtpOff64: {
auto [defObj, S] = findDefModule();
if (defObj == nullptr) {
return orbis::ErrorCode::INVAL;
}
*where += S + A;
return {};
}
case kRelTpOff64: {
auto [defObj, S] = findDefModule();
if (defObj == nullptr) {
return orbis::ErrorCode::INVAL;
}
if (!defObj->isTlsDone) {
allocateTlsOffset(process, module);
allocateTlsOffset(process, defObj);
}
*where = S - defObj->tlsOffset + A;
return {};
@ -217,8 +346,11 @@ static orbis::SysResult doRelocation(orbis::Process *process,
}
case kRelTpOff32: {
auto [defObj, S] = findDefModule();
if (defObj == nullptr) {
return orbis::ErrorCode::INVAL;
}
if (!defObj->isTlsDone) {
allocateTlsOffset(process, module);
allocateTlsOffset(process, defObj);
}
*where32 = S - defObj->tlsOffset + A;
return {};
@ -232,25 +364,45 @@ static orbis::SysResult doRelocation(orbis::Process *process,
}
orbis::SysResult orbis::Module::relocate(Process *process) {
for (auto rel : pltRelocations) {
auto result = doRelocation(process, this, rel);
if (!pltRelocations.empty()) {
kvector<Relocation> delayedRelocations;
std::size_t resolved = 0;
std::size_t delayed = 0;
for (auto rel : pltRelocations) {
auto result = doPltRelocation(process, this, rel);
if (result.isError()) {
return result;
if (result.isError()) {
delayedRelocations.push_back(rel);
++delayed;
} else {
++resolved;
}
}
std::printf("plt relocation of %s: delayed/resolved: %zu/%zu\n", moduleName,
delayed, resolved);
pltRelocations = std::move(delayedRelocations);
}
pltRelocations = {};
if (!nonPltRelocations.empty()) {
kvector<Relocation> delayedRelocations;
std::size_t resolved = 0;
std::size_t delayed = 0;
for (auto rel : nonPltRelocations) {
auto result = doRelocation(process, this, rel);
for (auto rel : nonPltRelocations) {
auto result = doRelocation(process, this, rel);
if (result.isError()) {
return result;
if (result.isError()) {
delayedRelocations.push_back(rel);
++delayed;
} else {
++resolved;
}
}
}
nonPltRelocations = {};
std::printf("non-plt relocation of %s: delayed/resolved: %zu/%zu\n",
moduleName, delayed, resolved);
nonPltRelocations = std::move(delayedRelocations);
}
return {};
}

View File

@ -93,7 +93,10 @@ orbis::SysResult orbis::sys_regmgr_call(Thread *thread, uint32_t op,
if (int_value->encoded_id == 0x12356328ECF5617B ||
int_value->encoded_id == 0x22666251FE7BECFF) {
int_value->value = 1;
return {};
}
int_value->value = 0;
}
return {};
@ -623,14 +626,30 @@ orbis::SysResult orbis::sys_dmem_container(Thread *thread, uint id) {
orbis::SysResult orbis::sys_get_authinfo(Thread *thread, pid_t pid,
ptr<void> info) {
struct authinfo {
uint64_t a;
uint64_t b;
uint64_t unk0;
uint64_t caps[4];
uint64_t attrs[4];
uint64_t unk[8];
};
static_assert(sizeof(authinfo) == 136);
authinfo result {
.unk0 = 0x3100000000000001,
.caps = {
0x2000038000000000,
0x000000000000FF00,
0x0000000000000000,
0x0000000000000000,
},
.attrs = {
0x4000400040000000,
0x4000000000000000,
0x0080000000000002,
0xF0000000FFFF4000,
},
};
std::memset(info, 0, 136);
((authinfo *)info)->b = ~0;
return {};
return uwrite((ptr<authinfo>)info, result);
}
orbis::SysResult orbis::sys_mname(Thread *thread, uint64_t addr, uint64_t len,
ptr<const char[32]> name) {
@ -688,11 +707,9 @@ orbis::SysResult orbis::sys_dynlib_get_list(Thread *thread,
if (actualNum >= numArray) {
break;
}
pArray[actualNum++] = id;
}
uwrite(pActualNum, actualNum);
return {};
return uwrite(pActualNum, actualNum);
}
orbis::SysResult orbis::sys_dynlib_get_info(Thread *thread,
SceKernelModule handle,
@ -847,7 +864,8 @@ orbis::SysResult orbis::sys_dl_get_metadata(Thread *thread /* TODO */) {
return ErrorCode::NOSYS;
}
orbis::SysResult orbis::sys_workaround8849(Thread *thread /* TODO */) {
return ErrorCode::NOSYS;
thread->retval[0] = 1;
return {};
}
orbis::SysResult orbis::sys_is_development_mode(Thread *thread /* TODO */) {
return ErrorCode::NOSYS;
@ -887,12 +905,12 @@ orbis::sys_dynlib_get_info_ex(Thread *thread, SceKernelModule handle,
std::memcpy(result.segments, module->segments,
sizeof(ModuleSegment) * module->segmentCount);
result.segmentCount = module->segmentCount;
result.refCount = module->references.load(std::memory_order::relaxed);
result.refCount = 1;
ORBIS_LOG_WARNING(__FUNCTION__, result.id, result.name, result.tlsIndex,
result.tlsInit, result.tlsInitSize, result.tlsSize,
result.tlsOffset, result.tlsAlign, result.initProc,
result.finiProc, result.ehFrameHdr, result.ehFrame,
result.ehFrameHdrSize, result.ehFrameSize);
result.ehFrameHdrSize, result.ehFrameSize, result.segmentCount, result.refCount);
return uwrite(destModuleInfoEx, result);
}
orbis::SysResult orbis::sys_budget_getid(Thread *thread) {

View File

@ -64,6 +64,23 @@ orbis::SysResult orbis::sys___sysctl(Thread *thread, ptr<sint> name,
memset(old, 0, sizeof(app_info));
return {};
}
if (name[0] == 1 && name[1] == 14 && name[2] == 44) {
// GetLibkernelTextLocation
if (*oldlenp != 16) {
return ErrorCode::INVAL;
}
auto *dest = (uint64_t *)old;
for (auto [id, mod] : thread->tproc->modulesMap) {
if (std::string_view("libkernel") == mod->moduleName) {
dest[0] = (uint64_t)mod->segments[0].addr;
dest[1] = mod->segments[0].size;
return{};
}
}
}
}
if (namelen == 2) {
@ -169,7 +186,7 @@ orbis::SysResult orbis::sys___sysctl(Thread *thread, ptr<sint> name,
}
*(uint32_t *)old = 1;
break;
return{};
case sysctl_kern::sdk_version: {
if (*oldlenp != 4 || new_ != nullptr || newlen != 0) {
@ -187,7 +204,7 @@ orbis::SysResult orbis::sys___sysctl(Thread *thread, ptr<sint> name,
std::printf("Reporting SDK version %x\n",
*reinterpret_cast<uint32_t *>(sdkVersion));
*(uint32_t *)old = *reinterpret_cast<uint32_t *>(sdkVersion);
break;
return{};
}
case sysctl_kern::sched_cpusetsize:
@ -196,7 +213,7 @@ orbis::SysResult orbis::sys___sysctl(Thread *thread, ptr<sint> name,
}
*(std::uint32_t *)old = 4;
break;
return{};
case sysctl_kern::rng_pseudo:
if (*oldlenp != 0x40 || new_ != nullptr || newlen != 0) {
@ -205,7 +222,7 @@ orbis::SysResult orbis::sys___sysctl(Thread *thread, ptr<sint> name,
std::memset(old, 0, 0x40);
break;
return{};
case sysctl_kern::kern_37: {
struct kern37_value {
@ -220,7 +237,7 @@ orbis::SysResult orbis::sys___sysctl(Thread *thread, ptr<sint> name,
auto value = (kern37_value *)old;
value->size = sizeof(kern37_value);
break;
return{};
}
case sysctl_kern::proc_ptc: {
@ -229,7 +246,7 @@ orbis::SysResult orbis::sys___sysctl(Thread *thread, ptr<sint> name,
}
*(std::uint64_t *)old = 1357;
break;
return{};
}
case sysctl_kern::cpu_mode: {
@ -241,7 +258,7 @@ orbis::SysResult orbis::sys___sysctl(Thread *thread, ptr<sint> name,
// 1 - 7 cpu, low power
// 5 - 7 cpu, normal
*(std::uint32_t *)old = 5;
break;
return{};
}
default:
@ -263,7 +280,7 @@ orbis::SysResult orbis::sys___sysctl(Thread *thread, ptr<sint> name,
}
*(uint32_t *)old = 0x4000;
break;
return{};
default:
break;
@ -289,5 +306,15 @@ orbis::SysResult orbis::sys___sysctl(Thread *thread, ptr<sint> name,
}
}
std::string concatName;
for (unsigned int i = 0; i < namelen; ++i) {
if (i != 0) {
concatName += '.';
}
concatName += std::to_string(name[i]);
}
ORBIS_LOG_TODO(__FUNCTION__, concatName);
return {};
}

View File

@ -5,6 +5,7 @@ add_library(orbis::kernel::config ALIAS standalone-config)
add_executable(rpcsx-os
iodev/ajm.cpp
iodev/blockpool.cpp
iodev/camera.cpp
iodev/console.cpp
iodev/dce.cpp
iodev/dipsw.cpp

View File

@ -22,3 +22,4 @@ IoDevice *createSblSrvCharacterDevice();
IoDevice *createShmDevice();
IoDevice *createBlockPoolDevice();
IoDevice *createUrandomCharacterDevice();
IoDevice *createCameraCharacterDevice();

32
rpcsx-os/iodev/camera.cpp Normal file
View File

@ -0,0 +1,32 @@
#include "io-device.hpp"
#include "orbis/KernelAllocator.hpp"
#include "orbis/file.hpp"
#include "orbis/utils/Logs.hpp"
struct CameraFile : orbis::File {};
static orbis::ErrorCode camera_ioctl(orbis::File *file, std::uint64_t request,
void *argp, orbis::Thread *thread) {
ORBIS_LOG_FATAL("Unhandled camera ioctl", request);
return {};
}
static const orbis::FileOps fileOps = {
.ioctl = camera_ioctl,
};
struct CameraDevice : IoDevice {
orbis::ErrorCode open(orbis::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode,
orbis::Thread *thread) override {
auto newFile = orbis::knew<CameraFile>();
newFile->ops = &fileOps;
newFile->device = this;
*file = newFile;
return {};
}
};
IoDevice *createCameraCharacterDevice() { return orbis::knew<CameraDevice>(); }

View File

@ -2,18 +2,25 @@
#include "orbis/KernelAllocator.hpp"
#include "orbis/file.hpp"
#include "orbis/utils/Logs.hpp"
#include "orbis/thread/Thread.hpp"
struct DipswFile : public orbis::File {};
static orbis::ErrorCode dipsw_ioctl(orbis::File *file, std::uint64_t request,
void *argp, orbis::Thread *thread) {
if (request == 0x40048806) { // is connected?
ORBIS_LOG_ERROR("dipsw ioctl 0x40048806", argp);
if (request == 0x40048806) { // isDevelopmentMode
ORBIS_LOG_ERROR("dipsw ioctl isDevelopmentMode", argp);
*reinterpret_cast<std::uint32_t *>(argp) = 0;
return {};
}
if (request == 0x40048807) {
ORBIS_LOG_ERROR("dipsw ioctl 0x40048807", argp);
*reinterpret_cast<std::uint32_t *>(argp) = 1;
return {};
}
// 0x40088808
// 0x40088809
@ -38,6 +45,7 @@ static orbis::ErrorCode dipsw_ioctl(orbis::File *file, std::uint64_t request,
}
ORBIS_LOG_FATAL("Unhandled dipsw ioctl", request);
thread->where();
//__builtin_trap();
return {};
}

View File

@ -659,20 +659,20 @@ Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
std::memcpy(result->soName + name.size(), ".prx", sizeof(".prx"));
}
if (dyn.d_tag == kElfDynamicTypeNeeded) {
auto name = std::string_view(
sceStrtab + static_cast<std::uint32_t>(dyn.d_un.d_val));
if (name == "STREQUAL") {
// HACK for broken FWs
result->needed.push_back("libSceDolbyVision.prx");
} else {
name = patchSoName(name);
if (name != "libSceFreeTypeOptBm") { // TODO
result->needed.emplace_back(name);
result->needed.back() += ".prx";
}
}
}
// if (dyn.d_tag == kElfDynamicTypeNeeded) {
// auto name = std::string_view(
// sceStrtab + static_cast<std::uint32_t>(dyn.d_un.d_val));
// if (name == "STREQUAL") {
// // HACK for broken FWs
// result->needed.push_back("libSceDolbyVision.prx");
// } else {
// name = patchSoName(name);
// if (name != "libSceFreeTypeOptBm") { // TODO
// result->needed.emplace_back(name);
// result->needed.back() += ".prx";
// }
// }
// }
if (dyn.d_tag == kElfDynamicTypeSceModuleInfo) {
idToModuleIndex[dyn.d_un.d_val >> 48] = -1;
@ -1054,16 +1054,5 @@ Ref<orbis::Module> rx::linker::loadModuleByName(std::string_view name,
}
}
// HACK: implement lazy bind support
for (auto path : { "/app0/Media/Modules/" }) {
auto filePath = std::string(path);
filePath += name;
filePath += ".prx";
if (auto result = rx::linker::loadModuleFile(filePath.c_str(), thread)) {
return result;
}
}
return {};
}

View File

@ -326,6 +326,7 @@ static int ps4Exec(orbis::Thread *mainThread,
rx::vfs::mount("/dev/npdrm", createNpdrmCharacterDevice());
rx::vfs::mount("/dev/icc_configuration", createIccConfigurationCharacterDevice());
rx::vfs::mount("/dev/console", createConsoleCharacterDevice());
rx::vfs::mount("/dev/camera", createCameraCharacterDevice());
rx::vfs::mount("/dev/dmem1", dmem1);
rx::vfs::mount("/dev/dmem2", createDmemCharacterDevice(2));
rx::vfs::mount("/dev/stdout", createFdWrapDevice(STDOUT_FILENO));
@ -363,6 +364,16 @@ static int ps4Exec(orbis::Thread *mainThread,
std::vector<std::uint64_t> argvOffsets;
std::vector<std::uint64_t> envpOffsets;
auto libSceLibcInternal = rx::linker::loadModuleFile(
"/system/common/lib/libSceLibcInternal.sprx", mainThread);
if (libSceLibcInternal == nullptr) {
std::fprintf(stderr, "libSceLibcInternal not found\n");
return 1;
}
libSceLibcInternal->id = mainThread->tproc->modulesMap.insert(libSceLibcInternal);
auto libkernel = rx::linker::loadModuleFile(
"/system/common/lib/libkernel_sys.sprx", mainThread);

View File

@ -85,6 +85,7 @@ loadPrx(orbis::Thread *thread, std::string_view name, bool relocate,
}
}
module->importedModules.clear();
module->importedModules.reserve(module->neededModules.size());
for (auto mod : module->neededModules) {
@ -182,8 +183,7 @@ orbis::SysResult dmem_mmap(orbis::Thread *thread, orbis::caddr_t addr,
orbis::off_t directMemoryStart) {
auto dmem = static_cast<DmemDevice *>(orbis::g_context.dmemDevice.get());
void *address = addr;
auto result =
dmem->mmap(&address, len, prot, flags, directMemoryStart);
auto result = dmem->mmap(&address, len, prot, flags, directMemoryStart);
if (result != ErrorCode{}) {
return result;
}
@ -405,6 +405,32 @@ orbis::SysResult dynlib_load_prx(orbis::Thread *thread,
return result;
}
{
std::map<std::string, Module *, std::less<>> loadedModules;
for (auto [id, module] : thread->tproc->modulesMap) {
// std::fprintf(stderr, "%u: %s\n", (unsigned)id, module->moduleName);
loadedModules[module->moduleName] = module;
}
for (auto [id, module] : thread->tproc->modulesMap) {
module->importedModules.clear();
module->importedModules.reserve(module->neededModules.size());
for (auto mod : module->neededModules) {
if (auto it = loadedModules.find(std::string_view(mod.name));
it != loadedModules.end()) {
module->importedModules.emplace_back(it->second);
continue;
}
module->importedModules.push_back({});
}
module->relocate(thread->tproc);
}
}
*pHandle = module->id;
return {};
}
@ -554,6 +580,7 @@ SysResult processNeeded(Thread *thread) {
}
for (auto [id, module] : thread->tproc->modulesMap) {
module->importedModules.clear();
module->importedModules.reserve(module->neededModules.size());
for (auto mod : module->neededModules) {