mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-03 21:41:26 +12:00
- PS3 games include both PPU and SPU code in their PPU executables, so to make patching games that make use of the same SPU libraries easier, we add a system to find and patch them. - Patches for this system still use SPU LS (Local Storage) addresses despite the fact that we aren't loading anything into SPU LS at this time. The patches are checked against each segment and patched in place.
1814 lines
51 KiB
C++
1814 lines
51 KiB
C++
#include "stdafx.h"
|
|
#include "Emu/Cell/PPUModule.h"
|
|
|
|
#include "Utilities/VirtualMemory.h"
|
|
#include "Utilities/bin_patch.h"
|
|
#include "Crypto/sha1.h"
|
|
#include "Crypto/unself.h"
|
|
#include "Loader/ELF.h"
|
|
#include "Emu/System.h"
|
|
#include "Emu/IdManager.h"
|
|
|
|
#include "Emu/Cell/PPUOpcodes.h"
|
|
#include "Emu/Cell/PPUAnalyser.h"
|
|
|
|
#include "Emu/Cell/lv2/sys_process.h"
|
|
#include "Emu/Cell/lv2/sys_prx.h"
|
|
#include "Emu/Cell/lv2/sys_memory.h"
|
|
#include "Emu/Cell/lv2/sys_overlay.h"
|
|
|
|
#include "Emu/Cell/Modules/StaticHLE.h"
|
|
|
|
#include <map>
|
|
#include <set>
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
extern void ppu_initialize_syscalls();
|
|
extern std::string ppu_get_function_name(const std::string& module, u32 fnid);
|
|
extern std::string ppu_get_variable_name(const std::string& module, u32 vnid);
|
|
extern void ppu_register_range(u32 addr, u32 size);
|
|
extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr);
|
|
extern void ppu_initialize(const ppu_module& info);
|
|
extern void ppu_initialize();
|
|
|
|
extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32);
|
|
|
|
// HLE function name cache
|
|
std::vector<std::string> g_ppu_function_names;
|
|
|
|
template <>
|
|
void fmt_class_string<lib_loading_type>::format(std::string& out, u64 arg)
|
|
{
|
|
format_enum(out, arg, [](lib_loading_type value)
|
|
{
|
|
switch (value)
|
|
{
|
|
case lib_loading_type::manual: return "Manually load selected libraries";
|
|
case lib_loading_type::hybrid: return "Load automatic and manual selection";
|
|
case lib_loading_type::liblv2only: return "Load liblv2.sprx only";
|
|
case lib_loading_type::liblv2both: return "Load liblv2.sprx and manual selection";
|
|
case lib_loading_type::liblv2list: return "Load liblv2.sprx and strict selection";
|
|
}
|
|
|
|
return unknown;
|
|
});
|
|
}
|
|
|
|
extern u32 ppu_generate_id(const char* name)
|
|
{
|
|
// Symbol name suffix
|
|
const auto suffix = "\x67\x59\x65\x99\x04\x25\x04\x90\x56\x64\x27\x49\x94\x89\x74\x1A";
|
|
|
|
sha1_context ctx;
|
|
u8 output[20];
|
|
|
|
// Compute SHA-1 hash
|
|
sha1_starts(&ctx);
|
|
sha1_update(&ctx, reinterpret_cast<const u8*>(name), std::strlen(name));
|
|
sha1_update(&ctx, reinterpret_cast<const u8*>(suffix), std::strlen(suffix));
|
|
sha1_finish(&ctx, output);
|
|
|
|
return reinterpret_cast<le_t<u32>&>(output[0]);
|
|
}
|
|
|
|
ppu_static_module::ppu_static_module(const char* name)
|
|
: name(name)
|
|
{
|
|
ppu_module_manager::register_module(this);
|
|
}
|
|
|
|
std::unordered_map<std::string, ppu_static_module*>& ppu_module_manager::access()
|
|
{
|
|
static std::unordered_map<std::string, ppu_static_module*> map;
|
|
|
|
return map;
|
|
}
|
|
|
|
void ppu_module_manager::register_module(ppu_static_module* module)
|
|
{
|
|
access().emplace(module->name, module);
|
|
}
|
|
|
|
ppu_static_function& ppu_module_manager::access_static_function(const char* module, u32 fnid)
|
|
{
|
|
auto& res = access().at(module)->functions[fnid];
|
|
|
|
if (res.name)
|
|
{
|
|
fmt::throw_exception("PPU FNID duplication in module %s (%s, 0x%x)", module, res.name, fnid);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
ppu_static_variable& ppu_module_manager::access_static_variable(const char* module, u32 vnid)
|
|
{
|
|
auto& res = access().at(module)->variables[vnid];
|
|
|
|
if (res.name)
|
|
{
|
|
fmt::throw_exception("PPU VNID duplication in module %s (%s, 0x%x)", module, res.name, vnid);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
const ppu_static_module* ppu_module_manager::get_module(const std::string& name)
|
|
{
|
|
const auto& map = access();
|
|
const auto found = map.find(name);
|
|
return found != map.end() ? found->second : nullptr;
|
|
}
|
|
|
|
// Global linkage information
|
|
struct ppu_linkage_info
|
|
{
|
|
struct module
|
|
{
|
|
struct info
|
|
{
|
|
ppu_static_function* static_func = nullptr;
|
|
ppu_static_variable* static_var = nullptr;
|
|
u32 export_addr = 0;
|
|
std::set<u32> imports;
|
|
std::set<u32> frefss;
|
|
};
|
|
|
|
// FNID -> (export; [imports...])
|
|
std::unordered_map<u32, info, value_hash<u32>> functions;
|
|
std::unordered_map<u32, info, value_hash<u32>> variables;
|
|
|
|
// Obsolete
|
|
bool imported = false;
|
|
};
|
|
|
|
// Module map
|
|
std::unordered_map<std::string, module> modules;
|
|
};
|
|
|
|
// Initialize static modules.
|
|
static void ppu_initialize_modules(ppu_linkage_info* link)
|
|
{
|
|
if (!link->modules.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
ppu_initialize_syscalls();
|
|
|
|
const std::initializer_list<const ppu_static_module*> registered
|
|
{
|
|
&ppu_module_manager::cellAdec,
|
|
&ppu_module_manager::cellAtrac,
|
|
&ppu_module_manager::cellAtracMulti,
|
|
&ppu_module_manager::cellAudio,
|
|
&ppu_module_manager::cellAvconfExt,
|
|
&ppu_module_manager::cellBGDL,
|
|
&ppu_module_manager::cellCamera,
|
|
&ppu_module_manager::cellCelp8Enc,
|
|
&ppu_module_manager::cellCelpEnc,
|
|
&ppu_module_manager::cellCrossController,
|
|
&ppu_module_manager::cellDaisy,
|
|
&ppu_module_manager::cellDmux,
|
|
&ppu_module_manager::cellDtcpIpUtility,
|
|
&ppu_module_manager::cellFiber,
|
|
&ppu_module_manager::cellFont,
|
|
&ppu_module_manager::cellFontFT,
|
|
&ppu_module_manager::cell_FreeType2,
|
|
&ppu_module_manager::cellFs,
|
|
&ppu_module_manager::cellGame,
|
|
&ppu_module_manager::cellGameExec,
|
|
&ppu_module_manager::cellGcmSys,
|
|
&ppu_module_manager::cellGem,
|
|
&ppu_module_manager::cellGifDec,
|
|
&ppu_module_manager::cellHttp,
|
|
&ppu_module_manager::cellHttps,
|
|
&ppu_module_manager::cellHttpUtil,
|
|
&ppu_module_manager::cellImeJp,
|
|
&ppu_module_manager::cellJpgDec,
|
|
&ppu_module_manager::cellJpgEnc,
|
|
&ppu_module_manager::cellKey2char,
|
|
&ppu_module_manager::cellL10n,
|
|
&ppu_module_manager::cellLibprof,
|
|
&ppu_module_manager::cellMic,
|
|
&ppu_module_manager::cellMusic,
|
|
&ppu_module_manager::cellMusicDecode,
|
|
&ppu_module_manager::cellMusicExport,
|
|
&ppu_module_manager::cellNetAoi,
|
|
&ppu_module_manager::cellNetCtl,
|
|
&ppu_module_manager::cellOskDialog,
|
|
&ppu_module_manager::cellOvis,
|
|
&ppu_module_manager::cellPamf,
|
|
&ppu_module_manager::cellPesmUtility,
|
|
&ppu_module_manager::cellPhotoDecode,
|
|
&ppu_module_manager::cellPhotoExport,
|
|
&ppu_module_manager::cellPhotoImportUtil,
|
|
&ppu_module_manager::cellPngDec,
|
|
&ppu_module_manager::cellPngEnc,
|
|
&ppu_module_manager::cellPrint,
|
|
&ppu_module_manager::cellRec,
|
|
&ppu_module_manager::cellRemotePlay,
|
|
&ppu_module_manager::cellResc,
|
|
&ppu_module_manager::cellRtc,
|
|
&ppu_module_manager::cellRtcAlarm,
|
|
&ppu_module_manager::cellRudp,
|
|
&ppu_module_manager::cellSail,
|
|
&ppu_module_manager::cellSailRec,
|
|
&ppu_module_manager::cellSaveData,
|
|
&ppu_module_manager::cellMinisSaveData,
|
|
&ppu_module_manager::cellScreenShot,
|
|
&ppu_module_manager::cellSearch,
|
|
&ppu_module_manager::cellSheap,
|
|
&ppu_module_manager::cellSpudll,
|
|
&ppu_module_manager::cellSpurs,
|
|
&ppu_module_manager::cellSpursJq,
|
|
&ppu_module_manager::cellSsl,
|
|
&ppu_module_manager::cellSubDisplay,
|
|
&ppu_module_manager::cellSync,
|
|
&ppu_module_manager::cellSync2,
|
|
&ppu_module_manager::cellSysconf,
|
|
&ppu_module_manager::cellSysmodule,
|
|
&ppu_module_manager::cellSysutil,
|
|
&ppu_module_manager::cellSysutilAp,
|
|
&ppu_module_manager::cellSysutilAvc2,
|
|
&ppu_module_manager::cellSysutilAvcExt,
|
|
&ppu_module_manager::cellSysutilNpEula,
|
|
&ppu_module_manager::cellSysutilMisc,
|
|
&ppu_module_manager::cellUsbd,
|
|
&ppu_module_manager::cellUsbPspcm,
|
|
&ppu_module_manager::cellUserInfo,
|
|
&ppu_module_manager::cellVdec,
|
|
&ppu_module_manager::cellVideoExport,
|
|
&ppu_module_manager::cellVideoPlayerUtility,
|
|
&ppu_module_manager::cellVideoUpload,
|
|
&ppu_module_manager::cellVoice,
|
|
&ppu_module_manager::cellVpost,
|
|
&ppu_module_manager::libad_async,
|
|
&ppu_module_manager::libad_core,
|
|
&ppu_module_manager::libmedi,
|
|
&ppu_module_manager::libmixer,
|
|
&ppu_module_manager::libsnd3,
|
|
&ppu_module_manager::libsynth2,
|
|
&ppu_module_manager::sceNp,
|
|
&ppu_module_manager::sceNp2,
|
|
&ppu_module_manager::sceNpClans,
|
|
&ppu_module_manager::sceNpCommerce2,
|
|
&ppu_module_manager::sceNpMatchingInt,
|
|
&ppu_module_manager::sceNpSns,
|
|
&ppu_module_manager::sceNpTrophy,
|
|
&ppu_module_manager::sceNpTus,
|
|
&ppu_module_manager::sceNpUtil,
|
|
&ppu_module_manager::sys_io,
|
|
&ppu_module_manager::sys_net,
|
|
&ppu_module_manager::sysPrxForUser,
|
|
&ppu_module_manager::sys_libc,
|
|
&ppu_module_manager::sys_lv2dbg,
|
|
&ppu_module_manager::static_hle,
|
|
};
|
|
|
|
// Initialize double-purpose fake OPD array for HLE functions
|
|
const auto& hle_funcs = ppu_function_manager::get();
|
|
|
|
// Allocate memory for the array (must be called after fixed allocations)
|
|
ppu_function_manager::addr = vm::alloc(::size32(hle_funcs) * 8, vm::main);
|
|
|
|
// Initialize as PPU executable code
|
|
ppu_register_range(ppu_function_manager::addr, ::size32(hle_funcs) * 8);
|
|
|
|
// Fill the array (visible data: self address and function index)
|
|
for (u32 addr = ppu_function_manager::addr, index = 0; index < hle_funcs.size(); addr += 8, index++)
|
|
{
|
|
// Function address = current address, RTOC = BLR instruction for the interpreter
|
|
vm::write32(addr + 0, addr);
|
|
vm::write32(addr + 4, ppu_instructions::BLR());
|
|
|
|
// Register the HLE function directly
|
|
ppu_register_function_at(addr + 0, 4, hle_funcs[index]);
|
|
ppu_register_function_at(addr + 4, 4, nullptr);
|
|
}
|
|
|
|
// Set memory protection to read-only
|
|
vm::page_protect(ppu_function_manager::addr, ::align(::size32(hle_funcs) * 8, 0x1000), 0, 0, vm::page_writable);
|
|
|
|
// Initialize function names
|
|
const bool is_first = g_ppu_function_names.empty();
|
|
|
|
if (is_first)
|
|
{
|
|
g_ppu_function_names.resize(hle_funcs.size());
|
|
g_ppu_function_names[0] = "INVALID";
|
|
g_ppu_function_names[1] = "HLE RETURN";
|
|
}
|
|
|
|
// For HLE variable allocation
|
|
u32 alloc_addr = 0;
|
|
|
|
// "Use" all the modules for correct linkage
|
|
for (auto& module : registered)
|
|
{
|
|
LOG_TRACE(LOADER, "Registered static module: %s", module->name);
|
|
}
|
|
|
|
for (auto& pair : ppu_module_manager::get())
|
|
{
|
|
const auto module = pair.second;
|
|
auto& linkage = link->modules[module->name];
|
|
|
|
for (auto& function : module->functions)
|
|
{
|
|
LOG_TRACE(LOADER, "** 0x%08X: %s", function.first, function.second.name);
|
|
|
|
if (is_first)
|
|
{
|
|
g_ppu_function_names[function.second.index] = fmt::format("%s.%s", module->name, function.second.name);
|
|
}
|
|
|
|
if ((function.second.flags & MFF_HIDDEN) == 0)
|
|
{
|
|
auto& flink = linkage.functions[function.first];
|
|
|
|
flink.static_func = &function.second;
|
|
flink.export_addr = ppu_function_manager::addr + 8 * function.second.index;
|
|
function.second.export_addr = &flink.export_addr;
|
|
}
|
|
}
|
|
|
|
for (auto& variable : module->variables)
|
|
{
|
|
LOG_TRACE(LOADER, "** &0x%08X: %s (size=0x%x, align=0x%x)", variable.first, variable.second.name, variable.second.size, variable.second.align);
|
|
|
|
// Allocate HLE variable
|
|
if (variable.second.size >= 4096 || variable.second.align >= 4096)
|
|
{
|
|
variable.second.addr = vm::alloc(variable.second.size, vm::main, std::max<u32>(variable.second.align, 0x10000));
|
|
}
|
|
else
|
|
{
|
|
const u32 next = ::align(alloc_addr, variable.second.align);
|
|
const u32 end = next + variable.second.size;
|
|
|
|
if (!next || (end >> 12 != alloc_addr >> 12))
|
|
{
|
|
alloc_addr = vm::alloc(4096, vm::main);
|
|
}
|
|
else
|
|
{
|
|
alloc_addr = next;
|
|
}
|
|
|
|
variable.second.addr = alloc_addr;
|
|
alloc_addr += variable.second.size;
|
|
}
|
|
|
|
if (variable.second.var)
|
|
{
|
|
variable.second.var->set(variable.second.addr);
|
|
}
|
|
|
|
LOG_TRACE(LOADER, "Allocated HLE variable %s.%s at 0x%x", module->name, variable.second.name, variable.second.var->addr());
|
|
|
|
// Initialize HLE variable
|
|
if (variable.second.init)
|
|
{
|
|
variable.second.init();
|
|
}
|
|
|
|
if ((variable.second.flags & MFF_HIDDEN) == 0)
|
|
{
|
|
auto& vlink = linkage.variables[variable.first];
|
|
|
|
vlink.static_var = &variable.second;
|
|
vlink.export_addr = variable.second.addr;
|
|
variable.second.export_addr = &vlink.export_addr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Resolve relocations for variable/function linkage.
|
|
static void ppu_patch_refs(std::vector<ppu_reloc>* out_relocs, u32 fref, u32 faddr)
|
|
{
|
|
struct ref_t
|
|
{
|
|
be_t<u32> type;
|
|
be_t<u32> addr;
|
|
be_t<u32> addend; // Note: Treating it as addend seems to be correct for now, but still unknown if theres more in this variable
|
|
};
|
|
|
|
for (auto ref = vm::ptr<ref_t>::make(fref); ref->type; ref++)
|
|
{
|
|
if (ref->addend) LOG_WARNING(LOADER, "**** REF(%u): Addend value(0x%x, 0x%x)", ref->type, ref->addr, ref->addend);
|
|
|
|
const u32 raddr = ref->addr;
|
|
const u32 rtype = ref->type;
|
|
const u32 rdata = faddr + ref->addend;
|
|
|
|
if (out_relocs)
|
|
{
|
|
// Register relocation with unpredictable target (data=0)
|
|
ppu_reloc _rel;
|
|
_rel.addr = raddr;
|
|
_rel.type = rtype;
|
|
_rel.data = 0;
|
|
out_relocs->emplace_back(_rel);
|
|
}
|
|
|
|
// OPs must be similar to relocations
|
|
switch (rtype)
|
|
{
|
|
case 1:
|
|
{
|
|
const u32 value = vm::_ref<u32>(ref->addr) = rdata;
|
|
LOG_TRACE(LOADER, "**** REF(1): 0x%x <- 0x%x", ref->addr, value);
|
|
break;
|
|
}
|
|
|
|
case 4:
|
|
{
|
|
const u16 value = vm::_ref<u16>(ref->addr) = static_cast<u16>(rdata);
|
|
LOG_TRACE(LOADER, "**** REF(4): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr);
|
|
break;
|
|
}
|
|
|
|
case 6:
|
|
{
|
|
const u16 value = vm::_ref<u16>(ref->addr) = static_cast<u16>(rdata >> 16) + (rdata & 0x8000 ? 1 : 0);
|
|
LOG_TRACE(LOADER, "**** REF(6): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr);
|
|
break;
|
|
}
|
|
|
|
case 57:
|
|
{
|
|
const u16 value = vm::_ref<ppu_bf_t<be_t<u16>, 0, 14>>(ref->addr) = static_cast<u16>(rdata) >> 2;
|
|
LOG_TRACE(LOADER, "**** REF(57): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr);
|
|
break;
|
|
}
|
|
|
|
default: LOG_ERROR(LOADER, "**** REF(%u): Unknown/Illegal type (0x%x, 0x%x)", rtype, raddr, ref->addend);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Export or import module struct
|
|
struct ppu_prx_module_info
|
|
{
|
|
u8 size;
|
|
u8 unk0;
|
|
be_t<u16> version;
|
|
be_t<u16> attributes;
|
|
be_t<u16> num_func;
|
|
be_t<u16> num_var;
|
|
be_t<u16> num_tlsvar;
|
|
u8 info_hash;
|
|
u8 info_tlshash;
|
|
u8 unk1[2];
|
|
vm::bcptr<char> name;
|
|
vm::bcptr<u32> nids; // Imported FNIDs, Exported NIDs
|
|
vm::bptr<u32> addrs;
|
|
vm::bcptr<u32> vnids; // Imported VNIDs
|
|
vm::bcptr<u32> vstubs;
|
|
be_t<u32> unk4;
|
|
be_t<u32> unk5;
|
|
};
|
|
|
|
// Load and register exports; return special exports found (nameless module)
|
|
static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 exports_end)
|
|
{
|
|
std::unordered_map<u32, u32> result;
|
|
|
|
for (u32 addr = exports_start; addr < exports_end;)
|
|
{
|
|
const auto& lib = vm::_ref<const ppu_prx_module_info>(addr);
|
|
|
|
if (!lib.name)
|
|
{
|
|
// Set special exports
|
|
for (u32 i = 0, end = lib.num_func + lib.num_var; i < end; i++)
|
|
{
|
|
const u32 nid = lib.nids[i];
|
|
const u32 addr = lib.addrs[i];
|
|
|
|
if (i < lib.num_func)
|
|
{
|
|
LOG_NOTICE(LOADER, "** Special: [%s] at 0x%x", ppu_get_function_name({}, nid), addr);
|
|
}
|
|
else
|
|
{
|
|
LOG_NOTICE(LOADER, "** Special: &[%s] at 0x%x", ppu_get_variable_name({}, nid), addr);
|
|
}
|
|
|
|
result.emplace(nid, addr);
|
|
}
|
|
|
|
addr += lib.size ? lib.size : sizeof(ppu_prx_module_info);
|
|
continue;
|
|
}
|
|
|
|
const std::string module_name(lib.name.get_ptr());
|
|
|
|
LOG_NOTICE(LOADER, "** Exported module '%s' (0x%x, 0x%x, 0x%x, 0x%x)", module_name, lib.vnids, lib.vstubs, lib.unk4, lib.unk5);
|
|
|
|
if (lib.num_tlsvar)
|
|
{
|
|
LOG_FATAL(LOADER, "Unexpected num_tlsvar (%u)!", lib.num_tlsvar);
|
|
}
|
|
|
|
// Static module
|
|
const auto _sm = ppu_module_manager::get_module(module_name);
|
|
|
|
// Module linkage
|
|
auto& mlink = link->modules[module_name];
|
|
|
|
const auto fnids = +lib.nids;
|
|
const auto faddrs = +lib.addrs;
|
|
|
|
// Get functions
|
|
for (u32 i = 0, end = lib.num_func; i < end; i++)
|
|
{
|
|
const u32 fnid = fnids[i];
|
|
const u32 faddr = faddrs[i];
|
|
LOG_NOTICE(LOADER, "**** %s export: [%s] at 0x%x", module_name, ppu_get_function_name(module_name, fnid), faddr);
|
|
|
|
// Function linkage info
|
|
auto& flink = mlink.functions[fnid];
|
|
|
|
if (flink.static_func && flink.export_addr == ppu_function_manager::addr + 8 * flink.static_func->index)
|
|
{
|
|
flink.export_addr = 0;
|
|
}
|
|
|
|
if (flink.export_addr)
|
|
{
|
|
LOG_ERROR(LOADER, "Already linked function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name);
|
|
}
|
|
//else
|
|
{
|
|
// Static function
|
|
const auto _sf = _sm && _sm->functions.count(fnid) ? &_sm->functions.at(fnid) : nullptr;
|
|
|
|
if (_sf && (_sf->flags & MFF_FORCED_HLE))
|
|
{
|
|
// Inject a branch to the HLE implementation
|
|
const u32 _entry = vm::read32(faddr);
|
|
const u32 target = ppu_function_manager::addr + 8 * _sf->index;
|
|
|
|
if ((target <= _entry && _entry - target <= 0x2000000) || (target > _entry && target - _entry < 0x2000000))
|
|
{
|
|
// Use relative branch
|
|
vm::write32(_entry, ppu_instructions::B(target - _entry));
|
|
}
|
|
else if (target < 0x2000000)
|
|
{
|
|
// Use absolute branch if possible
|
|
vm::write32(_entry, ppu_instructions::B(target, true));
|
|
}
|
|
else
|
|
{
|
|
LOG_FATAL(LOADER, "Failed to patch function at 0x%x (0x%x)", _entry, target);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set exported function
|
|
flink.export_addr = faddr;
|
|
|
|
// Fix imports
|
|
for (const u32 addr : flink.imports)
|
|
{
|
|
vm::write32(addr, faddr);
|
|
//LOG_WARNING(LOADER, "Exported function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name);
|
|
}
|
|
|
|
for (const u32 fref : flink.frefss)
|
|
{
|
|
ppu_patch_refs(nullptr, fref, faddr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const auto vnids = lib.nids + lib.num_func;
|
|
const auto vaddrs = lib.addrs + lib.num_func;
|
|
|
|
// Get variables
|
|
for (u32 i = 0, end = lib.num_var; i < end; i++)
|
|
{
|
|
const u32 vnid = vnids[i];
|
|
const u32 vaddr = vaddrs[i];
|
|
LOG_NOTICE(LOADER, "**** %s export: &[%s] at 0x%x", module_name, ppu_get_variable_name(module_name, vnid), vaddr);
|
|
|
|
// Variable linkage info
|
|
auto& vlink = mlink.variables[vnid];
|
|
|
|
if (vlink.static_var && vlink.export_addr == vlink.static_var->addr)
|
|
{
|
|
vlink.export_addr = 0;
|
|
}
|
|
|
|
if (vlink.export_addr)
|
|
{
|
|
LOG_ERROR(LOADER, "Already linked variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name);
|
|
}
|
|
//else
|
|
{
|
|
// Set exported variable
|
|
vlink.export_addr = vaddr;
|
|
|
|
// Fix imports
|
|
for (const auto vref : vlink.imports)
|
|
{
|
|
ppu_patch_refs(nullptr, vref, vaddr);
|
|
//LOG_WARNING(LOADER, "Exported variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
addr += lib.size ? lib.size : sizeof(ppu_prx_module_info);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static auto ppu_load_imports(std::vector<ppu_reloc>& relocs, ppu_linkage_info* link, u32 imports_start, u32 imports_end)
|
|
{
|
|
std::unordered_map<u32, void*> result;
|
|
|
|
for (u32 addr = imports_start; addr < imports_end;)
|
|
{
|
|
const auto& lib = vm::_ref<const ppu_prx_module_info>(addr);
|
|
|
|
const std::string module_name(lib.name.get_ptr());
|
|
|
|
LOG_NOTICE(LOADER, "** Imported module '%s' (ver=0x%x, attr=0x%x, 0x%x, 0x%x) [0x%x]", module_name, lib.version, lib.attributes, lib.unk4, lib.unk5, addr);
|
|
|
|
if (lib.num_tlsvar)
|
|
{
|
|
LOG_FATAL(LOADER, "Unexpected num_tlsvar (%u)!", lib.num_tlsvar);
|
|
}
|
|
|
|
// Static module
|
|
const auto _sm = ppu_module_manager::get_module(module_name);
|
|
|
|
// Module linkage
|
|
auto& mlink = link->modules[module_name];
|
|
|
|
const auto fnids = +lib.nids;
|
|
const auto faddrs = +lib.addrs;
|
|
|
|
for (u32 i = 0, end = lib.num_func; i < end; i++)
|
|
{
|
|
const u32 fnid = fnids[i];
|
|
const u32 fstub = faddrs[i];
|
|
const u32 faddr = (faddrs + i).addr();
|
|
LOG_NOTICE(LOADER, "**** %s import: [%s] -> 0x%x", module_name, ppu_get_function_name(module_name, fnid), fstub);
|
|
|
|
// Function linkage info
|
|
auto& flink = link->modules[module_name].functions[fnid];
|
|
|
|
// Add new import
|
|
result.emplace(faddr, &flink);
|
|
flink.imports.emplace(faddr);
|
|
mlink.imported = true;
|
|
|
|
// Link address (special HLE function by default)
|
|
const u32 link_addr = flink.export_addr ? flink.export_addr : ppu_function_manager::addr;
|
|
|
|
// Write import table
|
|
vm::write32(faddr, link_addr);
|
|
|
|
// Patch refs if necessary (0x2000 seems to be correct flag indicating the presence of additional info)
|
|
if (const u32 frefs = (lib.attributes & 0x2000) ? +fnids[i + lib.num_func] : 0)
|
|
{
|
|
result.emplace(frefs, &flink);
|
|
flink.frefss.emplace(frefs);
|
|
ppu_patch_refs(&relocs, frefs, link_addr);
|
|
}
|
|
|
|
//LOG_WARNING(LOADER, "Imported function '%s' in module '%s' (0x%x)", ppu_get_function_name(module_name, fnid), module_name, faddr);
|
|
}
|
|
|
|
const auto vnids = +lib.vnids;
|
|
const auto vstubs = +lib.vstubs;
|
|
|
|
for (u32 i = 0, end = lib.num_var; i < end; i++)
|
|
{
|
|
const u32 vnid = vnids[i];
|
|
const u32 vref = vstubs[i];
|
|
LOG_NOTICE(LOADER, "**** %s import: &[%s] (ref=*0x%x)", module_name, ppu_get_variable_name(module_name, vnid), vref);
|
|
|
|
// Variable linkage info
|
|
auto& vlink = link->modules[module_name].variables[vnid];
|
|
|
|
// Add new import
|
|
result.emplace(vref, &vlink);
|
|
vlink.imports.emplace(vref);
|
|
mlink.imported = true;
|
|
|
|
// Link if available
|
|
ppu_patch_refs(&relocs, vref, vlink.export_addr);
|
|
|
|
//LOG_WARNING(LOADER, "Imported variable '%s' in module '%s' (0x%x)", ppu_get_variable_name(module_name, vnid), module_name, vlink.first);
|
|
}
|
|
|
|
addr += lib.size ? lib.size : sizeof(ppu_prx_module_info);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& path)
|
|
{
|
|
// Create new PRX object
|
|
const auto prx = idm::make_ptr<lv2_obj, lv2_prx>();
|
|
|
|
// Access linkage information object
|
|
const auto link = g_fxo->get<ppu_linkage_info>();
|
|
|
|
// Initialize HLE modules
|
|
ppu_initialize_modules(link);
|
|
|
|
// Library hash
|
|
sha1_context sha;
|
|
sha1_starts(&sha);
|
|
|
|
for (const auto& prog : elf.progs)
|
|
{
|
|
LOG_NOTICE(LOADER, "** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags);
|
|
|
|
// Hash big-endian values
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_type), sizeof(prog.p_type));
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_flags), sizeof(prog.p_flags));
|
|
|
|
switch (const u32 p_type = prog.p_type)
|
|
{
|
|
case 0x1: // LOAD
|
|
{
|
|
if (prog.p_memsz)
|
|
{
|
|
const u32 mem_size = ::narrow<u32>(prog.p_memsz, "p_memsz" HERE);
|
|
const u32 file_size = ::narrow<u32>(prog.p_filesz, "p_filesz" HERE);
|
|
const u32 init_addr = ::narrow<u32>(prog.p_vaddr, "p_vaddr" HERE);
|
|
|
|
// Alloc segment memory
|
|
const u32 addr = vm::alloc(mem_size, vm::main);
|
|
|
|
if (!addr)
|
|
{
|
|
fmt::throw_exception("vm::alloc() failed (size=0x%x)", mem_size);
|
|
}
|
|
|
|
// Copy segment data
|
|
std::memcpy(vm::base(addr), prog.bin.data(), file_size);
|
|
LOG_WARNING(LOADER, "**** Loaded to 0x%x (size=0x%x)", addr, mem_size);
|
|
|
|
// Hash segment
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_vaddr), sizeof(prog.p_vaddr));
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_memsz), sizeof(prog.p_memsz));
|
|
sha1_update(&sha, prog.bin.data(), prog.bin.size());
|
|
|
|
// Initialize executable code if necessary
|
|
if (prog.p_flags & 0x1)
|
|
{
|
|
ppu_register_range(addr, mem_size);
|
|
}
|
|
|
|
ppu_segment _seg;
|
|
_seg.addr = addr;
|
|
_seg.size = mem_size;
|
|
_seg.filesz = file_size;
|
|
_seg.type = p_type;
|
|
_seg.flags = prog.p_flags;
|
|
prx->segs.emplace_back(_seg);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 0x700000a4: break; // Relocations
|
|
|
|
default: LOG_ERROR(LOADER, "Unknown segment type! 0x%08x", p_type);
|
|
}
|
|
}
|
|
|
|
for (const auto& s : elf.shdrs)
|
|
{
|
|
LOG_NOTICE(LOADER, "** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", s.sh_type, s.sh_addr, s.sh_size, s.sh_flags);
|
|
|
|
const u32 addr = vm::cast(s.sh_addr);
|
|
const u32 size = vm::cast(s.sh_size);
|
|
|
|
if (s.sh_type == 1 && addr && size) // TODO: some sections with addr=0 are valid
|
|
{
|
|
for (auto i = 0; i < prx->segs.size(); i++)
|
|
{
|
|
const u32 saddr = static_cast<u32>(elf.progs[i].p_vaddr);
|
|
if (addr >= saddr && addr < saddr + elf.progs[i].p_memsz)
|
|
{
|
|
// "Relocate" section
|
|
ppu_segment _sec;
|
|
_sec.addr = addr - saddr + prx->segs[i].addr;
|
|
_sec.size = size;
|
|
_sec.type = s.sh_type;
|
|
_sec.flags = s.sh_flags & 7;
|
|
_sec.filesz = 0;
|
|
prx->secs.emplace_back(_sec);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do relocations
|
|
for (auto& prog : elf.progs)
|
|
{
|
|
switch (const u32 p_type = prog.p_type)
|
|
{
|
|
case 0x700000a4:
|
|
{
|
|
// Relocation information of the SCE_PPURELA segment
|
|
struct ppu_prx_relocation_info
|
|
{
|
|
be_t<u64> offset;
|
|
be_t<u16> unk0;
|
|
u8 index_value;
|
|
u8 index_addr;
|
|
be_t<u32> type;
|
|
vm::bptr<void, u64> ptr;
|
|
};
|
|
|
|
for (uint i = 0; i < prog.p_filesz; i += sizeof(ppu_prx_relocation_info))
|
|
{
|
|
const auto& rel = reinterpret_cast<const ppu_prx_relocation_info&>(prog.bin[i]);
|
|
|
|
ppu_reloc _rel;
|
|
const u32 raddr = _rel.addr = vm::cast(prx->segs.at(rel.index_addr).addr + rel.offset, HERE);
|
|
const u32 rtype = _rel.type = rel.type;
|
|
const u64 rdata = _rel.data = rel.index_value == 0xFF ? rel.ptr.addr().value() : prx->segs.at(rel.index_value).addr + rel.ptr.addr();
|
|
prx->relocs.emplace_back(_rel);
|
|
|
|
switch (rtype)
|
|
{
|
|
case 1: // R_PPC64_ADDR32
|
|
{
|
|
const u32 value = vm::_ref<u32>(raddr) = static_cast<u32>(rdata);
|
|
LOG_TRACE(LOADER, "**** RELOCATION(1): 0x%x <- 0x%08x (0x%llx)", raddr, value, rdata);
|
|
break;
|
|
}
|
|
|
|
case 4: //R_PPC64_ADDR16_LO
|
|
{
|
|
const u16 value = vm::_ref<u16>(raddr) = static_cast<u16>(rdata);
|
|
LOG_TRACE(LOADER, "**** RELOCATION(4): 0x%x <- 0x%04x (0x%llx)", raddr, value, rdata);
|
|
break;
|
|
}
|
|
|
|
case 5: //R_PPC64_ADDR16_HI
|
|
{
|
|
const u16 value = vm::_ref<u16>(raddr) = static_cast<u16>(rdata >> 16);
|
|
LOG_TRACE(LOADER, "**** RELOCATION(5): 0x%x <- 0x%04x (0x%llx)", raddr, value, rdata);
|
|
break;
|
|
}
|
|
|
|
case 6: //R_PPC64_ADDR16_HA
|
|
{
|
|
const u16 value = vm::_ref<u16>(raddr) = static_cast<u16>(rdata >> 16) + (rdata & 0x8000 ? 1 : 0);
|
|
LOG_TRACE(LOADER, "**** RELOCATION(6): 0x%x <- 0x%04x (0x%llx)", raddr, value, rdata);
|
|
break;
|
|
}
|
|
|
|
case 10: //R_PPC64_REL24
|
|
{
|
|
const u32 value = vm::_ref<ppu_bf_t<be_t<u32>, 6, 24>>(raddr) = static_cast<u32>(rdata - raddr) >> 2;
|
|
LOG_WARNING(LOADER, "**** RELOCATION(10): 0x%x <- 0x%06x (0x%llx)", raddr, value, rdata);
|
|
break;
|
|
}
|
|
|
|
case 11: //R_PPC64_REL14
|
|
{
|
|
const u32 value = vm::_ref<ppu_bf_t<be_t<u32>, 16, 14>>(raddr) = static_cast<u32>(rdata - raddr) >> 2;
|
|
LOG_WARNING(LOADER, "**** RELOCATION(11): 0x%x <- 0x%06x (0x%llx)", raddr, value, rdata);
|
|
break;
|
|
}
|
|
|
|
case 38: //R_PPC64_ADDR64
|
|
{
|
|
const u64 value = vm::_ref<u64>(raddr) = rdata;
|
|
LOG_TRACE(LOADER, "**** RELOCATION(38): 0x%x <- 0x%016llx (0x%llx)", raddr, value, rdata);
|
|
break;
|
|
}
|
|
|
|
case 44: //R_PPC64_REL64
|
|
{
|
|
const u64 value = vm::_ref<u64>(raddr) = rdata - raddr;
|
|
LOG_TRACE(LOADER, "**** RELOCATION(44): 0x%x <- 0x%016llx (0x%llx)", raddr, value, rdata);
|
|
break;
|
|
}
|
|
|
|
case 57: //R_PPC64_ADDR16_LO_DS
|
|
{
|
|
const u16 value = vm::_ref<ppu_bf_t<be_t<u16>, 0, 14>>(raddr) = static_cast<u16>(rdata) >> 2;
|
|
LOG_TRACE(LOADER, "**** RELOCATION(57): 0x%x <- 0x%04x (0x%llx)", raddr, value, rdata);
|
|
break;
|
|
}
|
|
|
|
default: LOG_ERROR(LOADER, "**** RELOCATION(%u): Illegal/Unknown type! (addr=0x%x; 0x%llx)", rtype, raddr, rdata);
|
|
}
|
|
|
|
if (rdata == 0)
|
|
{
|
|
LOG_TODO(LOADER, "**** RELOCATION(%u): 0x%x <- (zero-based value)", rtype, raddr);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!elf.progs.empty() && elf.progs[0].p_paddr)
|
|
{
|
|
struct ppu_prx_library_info
|
|
{
|
|
be_t<u16> attributes;
|
|
u8 version[2];
|
|
char name[28];
|
|
be_t<u32> toc;
|
|
be_t<u32> exports_start;
|
|
be_t<u32> exports_end;
|
|
be_t<u32> imports_start;
|
|
be_t<u32> imports_end;
|
|
};
|
|
|
|
// Access library information (TODO)
|
|
const auto& lib_info = vm::cptr<ppu_prx_library_info>(vm::cast(prx->segs[0].addr + elf.progs[0].p_paddr - elf.progs[0].p_offset, HERE));
|
|
const auto& lib_name = std::string(lib_info->name);
|
|
|
|
std::memcpy(prx->module_info_name, lib_info->name, sizeof(prx->module_info_name));
|
|
prx->module_info_version[0] = lib_info->version[0];
|
|
prx->module_info_version[1] = lib_info->version[1];
|
|
prx->module_info_attributes = lib_info->attributes;
|
|
|
|
LOG_WARNING(LOADER, "Library %s (rtoc=0x%x):", lib_name, lib_info->toc);
|
|
|
|
prx->specials = ppu_load_exports(link, lib_info->exports_start, lib_info->exports_end);
|
|
prx->imports = ppu_load_imports(prx->relocs, link, lib_info->imports_start, lib_info->imports_end);
|
|
std::stable_sort(prx->relocs.begin(), prx->relocs.end());
|
|
prx->analyse(lib_info->toc, 0);
|
|
}
|
|
else
|
|
{
|
|
LOG_FATAL(LOADER, "Library %s: PRX library info not found");
|
|
}
|
|
|
|
prx->start.set(prx->specials[0xbc9a0086]);
|
|
prx->stop.set(prx->specials[0xab779874]);
|
|
prx->exit.set(prx->specials[0x3ab9a95e]);
|
|
prx->prologue.set(prx->specials[0x0d10fd3f]);
|
|
prx->epilogue.set(prx->specials[0x330f7005]);
|
|
prx->name = path.substr(path.find_last_of('/') + 1);
|
|
prx->path = path;
|
|
|
|
sha1_finish(&sha, prx->sha1);
|
|
|
|
// Format patch name
|
|
std::string hash("PRX-0000000000000000000000000000000000000000");
|
|
for (u32 i = 0; i < 20; i++)
|
|
{
|
|
constexpr auto pal = "0123456789abcdef";
|
|
hash[4 + i * 2] = pal[prx->sha1[i] >> 4];
|
|
hash[5 + i * 2] = pal[prx->sha1[i] & 15];
|
|
}
|
|
|
|
// Apply the patch
|
|
auto applied = g_fxo->get<patch_engine>()->apply(hash, vm::g_base_addr);
|
|
|
|
if (!Emu.GetTitleID().empty())
|
|
{
|
|
// Alternative patch
|
|
applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
|
|
}
|
|
|
|
LOG_NOTICE(LOADER, "PRX library hash: %s (<- %u)", hash, applied);
|
|
|
|
if (Emu.IsReady() && g_fxo->get<ppu_module>()->segs.empty())
|
|
{
|
|
// Special loading mode
|
|
ppu_thread_params p{};
|
|
p.stack_addr = vm::cast(vm::alloc(0x100000, vm::stack, 4096));
|
|
p.stack_size = 0x100000;
|
|
|
|
auto ppu = idm::make_ptr<named_thread<ppu_thread>>("PPU[0x1000000] Thread (test_thread)", p, "test_thread", 0);
|
|
|
|
ppu->cmd_push({ppu_cmd::initialize, 0});
|
|
}
|
|
|
|
return prx;
|
|
}
|
|
|
|
void ppu_unload_prx(const lv2_prx& prx)
|
|
{
|
|
// Clean linkage info
|
|
for (auto& imp : prx.imports)
|
|
{
|
|
auto pinfo = static_cast<ppu_linkage_info::module::info*>(imp.second);
|
|
pinfo->frefss.erase(imp.first);
|
|
pinfo->imports.erase(imp.first);
|
|
}
|
|
|
|
//for (auto& exp : prx.exports)
|
|
//{
|
|
// auto pinfo = static_cast<ppu_linkage_info::module::info*>(exp.second);
|
|
// if (pinfo->static_func)
|
|
// {
|
|
// pinfo->export_addr = ppu_function_manager::addr + 8 * pinfo->static_func->index;
|
|
// }
|
|
// else if (pinfo->static_var)
|
|
// {
|
|
// pinfo->export_addr = pinfo->static_var->addr;
|
|
// }
|
|
// else
|
|
// {
|
|
// pinfo->export_addr = 0;
|
|
// }
|
|
//}
|
|
|
|
for (auto& seg : prx.segs)
|
|
{
|
|
vm::dealloc(seg.addr, vm::main);
|
|
}
|
|
}
|
|
|
|
void ppu_load_exec(const ppu_exec_object& elf)
|
|
{
|
|
// Set for delayed initialization in ppu_initialize()
|
|
const auto _main = g_fxo->get<ppu_module>();
|
|
|
|
// Access linkage information object
|
|
const auto link = g_fxo->init<ppu_linkage_info>();
|
|
|
|
// TLS information
|
|
u32 tls_vaddr = 0;
|
|
u32 tls_fsize = 0;
|
|
u32 tls_vsize = 0;
|
|
|
|
// Process information
|
|
u32 sdk_version = 0xffffffff;
|
|
s32 primary_prio = 1001;
|
|
u32 primary_stacksize = 0x100000;
|
|
u32 malloc_pagesize = 0x100000;
|
|
u32 ppc_seg = 0;
|
|
|
|
// Executable hash
|
|
sha1_context sha;
|
|
sha1_starts(&sha);
|
|
|
|
// Allocate memory at fixed positions
|
|
for (const auto& prog : elf.progs)
|
|
{
|
|
LOG_NOTICE(LOADER, "** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags);
|
|
|
|
ppu_segment _seg;
|
|
const u32 addr = _seg.addr = vm::cast(prog.p_vaddr, HERE);
|
|
const u32 size = _seg.size = ::narrow<u32>(prog.p_memsz, "p_memsz" HERE);
|
|
const u32 type = _seg.type = prog.p_type;
|
|
const u32 flag = _seg.flags = prog.p_flags;
|
|
_seg.filesz = ::narrow<u32>(prog.p_filesz, "p_filesz" HERE);
|
|
|
|
// Hash big-endian values
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_type), sizeof(prog.p_type));
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_flags), sizeof(prog.p_flags));
|
|
|
|
if (type == 0x1 /* LOAD */ && prog.p_memsz)
|
|
{
|
|
if (prog.bin.size() > size || prog.bin.size() != prog.p_filesz)
|
|
fmt::throw_exception("Invalid binary size (0x%llx, memsz=0x%x)", prog.bin.size(), size);
|
|
|
|
if (!vm::falloc(addr, size))
|
|
fmt::throw_exception("vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size);
|
|
|
|
// Copy segment data, hash it
|
|
std::memcpy(vm::base(addr), prog.bin.data(), prog.bin.size());
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_vaddr), sizeof(prog.p_vaddr));
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_memsz), sizeof(prog.p_memsz));
|
|
sha1_update(&sha, prog.bin.data(), prog.bin.size());
|
|
|
|
// Initialize executable code if necessary
|
|
if (prog.p_flags & 0x1)
|
|
{
|
|
ppu_register_range(addr, size);
|
|
}
|
|
|
|
// Store only LOAD segments (TODO)
|
|
_main->segs.emplace_back(_seg);
|
|
}
|
|
}
|
|
|
|
// Load section list, used by the analyser
|
|
for (const auto& s : elf.shdrs)
|
|
{
|
|
LOG_NOTICE(LOADER, "** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", s.sh_type, s.sh_addr, s.sh_size, s.sh_flags);
|
|
|
|
ppu_segment _sec;
|
|
const u32 addr = _sec.addr = vm::cast(s.sh_addr);
|
|
const u32 size = _sec.size = vm::cast(s.sh_size);
|
|
const u32 type = _sec.type = s.sh_type;
|
|
const u32 flag = _sec.flags = s.sh_flags & 7;
|
|
_sec.filesz = 0;
|
|
|
|
if (s.sh_type == 1 && addr && size)
|
|
{
|
|
_main->secs.emplace_back(_sec);
|
|
}
|
|
}
|
|
|
|
sha1_finish(&sha, _main->sha1);
|
|
|
|
// Format patch name
|
|
std::string hash("PPU-0000000000000000000000000000000000000000");
|
|
for (u32 i = 0; i < 20; i++)
|
|
{
|
|
constexpr auto pal = "0123456789abcdef";
|
|
hash[4 + i * 2] = pal[_main->sha1[i] >> 4];
|
|
hash[5 + i * 2] = pal[_main->sha1[i] & 15];
|
|
}
|
|
|
|
// Apply the patch
|
|
auto applied = g_fxo->get<patch_engine>()->apply(hash, vm::g_base_addr);
|
|
|
|
if (!Emu.GetTitleID().empty())
|
|
{
|
|
// Alternative patch
|
|
applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
|
|
}
|
|
|
|
LOG_NOTICE(LOADER, "PPU executable hash: %s (<- %u)", hash, applied);
|
|
|
|
// Initialize HLE modules
|
|
ppu_initialize_modules(link);
|
|
|
|
// Embedded SPU elf patching
|
|
for (u32 i = _main->segs[0].addr; i < (_main->segs[0].addr + _main->segs[0].size); i += 4)
|
|
{
|
|
uchar* elf_header = vm::_ptr<u8>(i);
|
|
const spu_exec_object obj(fs::file(vm::base(vm::cast(i, HERE)), (_main->segs[0].addr + _main->segs[0].size) - i));
|
|
|
|
if (obj != elf_error::ok)
|
|
{
|
|
// This address does not have an SPU elf
|
|
continue;
|
|
}
|
|
|
|
// Segment info dump
|
|
std::string dump;
|
|
|
|
applied = 0;
|
|
|
|
// Executable hash
|
|
sha1_context sha2;
|
|
sha1_starts(&sha2);
|
|
u8 sha1_hash[20];
|
|
|
|
for (const auto& prog : obj.progs)
|
|
{
|
|
// Only hash the data, we are not loading it
|
|
sha1_update(&sha2, reinterpret_cast<const uchar*>(&prog.p_vaddr), sizeof(prog.p_vaddr));
|
|
sha1_update(&sha2, reinterpret_cast<const uchar*>(&prog.p_memsz), sizeof(prog.p_memsz));
|
|
sha1_update(&sha2, reinterpret_cast<const uchar*>(&prog.p_filesz), sizeof(prog.p_filesz));
|
|
|
|
fmt::append(dump, "\n\tSegment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, p_offset=0x%llx", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_offset);
|
|
|
|
if (prog.p_type == 0x1 /* LOAD */ && prog.p_filesz > 0)
|
|
{
|
|
sha1_update(&sha2, (elf_header + prog.p_offset), prog.p_filesz);
|
|
}
|
|
|
|
else if (prog.p_type == 0x4 /* NOTE */ && prog.p_filesz > 0)
|
|
{
|
|
sha1_update(&sha2, (elf_header + prog.p_offset), prog.p_filesz);
|
|
|
|
// We assume that the string SPUNAME exists 0x14 bytes into the NOTE segment
|
|
const auto spu_name = reinterpret_cast<const char*>(elf_header + prog.p_offset + 0x14);
|
|
fmt::append(dump, "\n\tSPUNAME: '%s'", spu_name);
|
|
}
|
|
}
|
|
|
|
sha1_finish(&sha2, sha1_hash);
|
|
|
|
// Format patch name
|
|
std::string hash("SPU-0000000000000000000000000000000000000000");
|
|
for (u32 i = 0; i < sizeof(sha1_hash); i++)
|
|
{
|
|
constexpr auto pal = "0123456789abcdef";
|
|
hash[4 + i * 2] = pal[sha1_hash[i] >> 4];
|
|
hash[5 + i * 2] = pal[sha1_hash[i] & 15];
|
|
}
|
|
|
|
// Try to patch each segment, will only succeed if the address exists in SPU local storage
|
|
for (const auto& prog : obj.progs)
|
|
{
|
|
// Apply the patch
|
|
applied += g_fxo->get<patch_engine>()->apply_with_ls_check(hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr);
|
|
|
|
if (!Emu.GetTitleID().empty())
|
|
{
|
|
// Alternative patch
|
|
applied += g_fxo->get<patch_engine>()->apply_with_ls_check(Emu.GetTitleID() + '-' + hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr);
|
|
}
|
|
}
|
|
|
|
LOG_NOTICE(LOADER, "SPU executable hash: %s (<- %u)%s", hash, applied, dump);
|
|
|
|
}
|
|
|
|
// Static HLE patching
|
|
if (g_cfg.core.hook_functions)
|
|
{
|
|
auto shle = g_fxo->init<statichle_handler>(0);
|
|
|
|
for (u32 i = _main->segs[0].addr; i < (_main->segs[0].addr + _main->segs[0].size); i += 4)
|
|
{
|
|
vm::cptr<u8> _ptr = vm::cast(i);
|
|
shle->check_against_patterns(_ptr, (_main->segs[0].addr + _main->segs[0].size) - i, i);
|
|
}
|
|
}
|
|
|
|
// Read control flags (0 if doesn't exist)
|
|
g_ps3_process_info.ctrl_flags1 = 0;
|
|
|
|
if (bool not_found = g_ps3_process_info.self_info.valid)
|
|
{
|
|
for (const auto& ctrl : g_ps3_process_info.self_info.ctrl_info)
|
|
{
|
|
if (ctrl.type == 1)
|
|
{
|
|
if (!std::exchange(not_found, false))
|
|
{
|
|
LOG_ERROR(LOADER, "More than one control flags header found! (flags1=0x%x)",
|
|
ctrl.control_flags.ctrl_flag1);
|
|
break;
|
|
}
|
|
|
|
g_ps3_process_info.ctrl_flags1 |= ctrl.control_flags.ctrl_flag1;
|
|
}
|
|
}
|
|
|
|
LOG_NOTICE(LOADER, "SELF header information found: ctrl_flags1=0x%x, authid=0x%llx",
|
|
g_ps3_process_info.ctrl_flags1, g_ps3_process_info.self_info.app_info.authid);
|
|
}
|
|
|
|
// Load other programs
|
|
for (auto& prog : elf.progs)
|
|
{
|
|
switch (const u32 p_type = prog.p_type)
|
|
{
|
|
case 0x00000001: break; // LOAD (already loaded)
|
|
|
|
case 0x00000007: // TLS
|
|
{
|
|
tls_vaddr = vm::cast(prog.p_vaddr, HERE);
|
|
tls_fsize = ::narrow<u32>(prog.p_filesz, "p_filesz" HERE);
|
|
tls_vsize = ::narrow<u32>(prog.p_memsz, "p_memsz" HERE);
|
|
break;
|
|
}
|
|
|
|
case 0x60000001: // LOOS+1
|
|
{
|
|
if (prog.p_filesz)
|
|
{
|
|
struct process_param_t
|
|
{
|
|
be_t<u32> size;
|
|
be_t<u32> magic;
|
|
be_t<u32> version;
|
|
be_t<u32> sdk_version;
|
|
be_t<s32> primary_prio;
|
|
be_t<u32> primary_stacksize;
|
|
be_t<u32> malloc_pagesize;
|
|
be_t<u32> ppc_seg;
|
|
//be_t<u32> crash_dump_param_addr;
|
|
};
|
|
|
|
const auto& info = vm::_ref<process_param_t>(vm::cast(prog.p_vaddr, HERE));
|
|
|
|
if (info.size < sizeof(process_param_t))
|
|
{
|
|
LOG_WARNING(LOADER, "Bad process_param size! [0x%x : 0x%x]", info.size, sizeof(process_param_t));
|
|
}
|
|
|
|
if (info.magic != 0x13bcc5f6)
|
|
{
|
|
LOG_ERROR(LOADER, "Bad process_param magic! [0x%x]", info.magic);
|
|
}
|
|
else
|
|
{
|
|
sdk_version = info.sdk_version;
|
|
|
|
if (s32 prio = info.primary_prio; prio < 3072
|
|
&& (prio >= (g_ps3_process_info.debug_or_root() ? 0 : -512)))
|
|
{
|
|
primary_prio = prio;
|
|
}
|
|
|
|
primary_stacksize = info.primary_stacksize;
|
|
malloc_pagesize = info.malloc_pagesize;
|
|
ppc_seg = info.ppc_seg;
|
|
|
|
LOG_NOTICE(LOADER, "*** sdk version: 0x%x", info.sdk_version);
|
|
LOG_NOTICE(LOADER, "*** primary prio: %d", info.primary_prio);
|
|
LOG_NOTICE(LOADER, "*** primary stacksize: 0x%x", info.primary_stacksize);
|
|
LOG_NOTICE(LOADER, "*** malloc pagesize: 0x%x", info.malloc_pagesize);
|
|
LOG_NOTICE(LOADER, "*** ppc seg: 0x%x", info.ppc_seg);
|
|
//LOG_NOTICE(LOADER, "*** crash dump param addr: 0x%x", info.crash_dump_param_addr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x60000002: // LOOS+2
|
|
{
|
|
if (prog.p_filesz)
|
|
{
|
|
struct ppu_proc_prx_param_t
|
|
{
|
|
be_t<u32> size;
|
|
be_t<u32> magic;
|
|
be_t<u32> version;
|
|
be_t<u32> unk0;
|
|
be_t<u32> libent_start;
|
|
be_t<u32> libent_end;
|
|
be_t<u32> libstub_start;
|
|
be_t<u32> libstub_end;
|
|
be_t<u16> ver;
|
|
be_t<u16> unk1;
|
|
be_t<u32> unk2;
|
|
};
|
|
|
|
const auto& proc_prx_param = vm::_ref<const ppu_proc_prx_param_t>(vm::cast(prog.p_vaddr, HERE));
|
|
|
|
LOG_NOTICE(LOADER, "* libent_start = *0x%x", proc_prx_param.libent_start);
|
|
LOG_NOTICE(LOADER, "* libstub_start = *0x%x", proc_prx_param.libstub_start);
|
|
LOG_NOTICE(LOADER, "* unk0 = 0x%x", proc_prx_param.unk0);
|
|
LOG_NOTICE(LOADER, "* unk2 = 0x%x", proc_prx_param.unk2);
|
|
|
|
if (proc_prx_param.magic != 0x1b434cec)
|
|
{
|
|
fmt::throw_exception("Bad magic! (0x%x)", proc_prx_param.magic);
|
|
}
|
|
|
|
ppu_load_exports(link, proc_prx_param.libent_start, proc_prx_param.libent_end);
|
|
ppu_load_imports(_main->relocs, link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
|
|
std::stable_sort(_main->relocs.begin(), _main->relocs.end());
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
LOG_ERROR(LOADER, "Unknown phdr type (0x%08x)", p_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize process
|
|
std::vector<std::shared_ptr<lv2_prx>> loaded_modules;
|
|
|
|
// Get LLE module list
|
|
std::set<std::string> load_libs;
|
|
|
|
if (g_cfg.core.lib_loading == lib_loading_type::manual)
|
|
{
|
|
// Load required set of modules (lib_loading_type::both processed in sys_prx.cpp)
|
|
load_libs = g_cfg.core.load_libraries.get_set();
|
|
}
|
|
else
|
|
{
|
|
if (g_cfg.core.lib_loading != lib_loading_type::hybrid || g_cfg.core.load_libraries.get_set().count("liblv2.sprx"))
|
|
{
|
|
// Will load libsysmodule.sprx internally
|
|
load_libs.emplace("liblv2.sprx");
|
|
}
|
|
else
|
|
{
|
|
// Load only libsysmodule.sprx
|
|
load_libs.emplace("libsysmodule.sprx");
|
|
}
|
|
}
|
|
|
|
// Program entry
|
|
u32 entry = 0;
|
|
|
|
if (!load_libs.empty())
|
|
{
|
|
const std::string lle_dir = vfs::get("/dev_flash/sys/external/");
|
|
|
|
if (!fs::is_dir(lle_dir) || !fs::is_file(lle_dir + "libsysmodule.sprx"))
|
|
{
|
|
LOG_ERROR(GENERAL, "PS3 firmware is not installed or the installed firmware is invalid."
|
|
"\nYou should install the PS3 Firmware (Menu: File -> Install Firmware)."
|
|
"\nVisit https://rpcs3.net/ for Quickstart Guide and more information.");
|
|
}
|
|
|
|
for (const auto& name : load_libs)
|
|
{
|
|
const ppu_prx_object obj = decrypt_self(fs::file(lle_dir + name));
|
|
|
|
if (obj == elf_error::ok)
|
|
{
|
|
LOG_WARNING(LOADER, "Loading library: %s", name);
|
|
|
|
auto prx = ppu_load_prx(obj, lle_dir + name);
|
|
|
|
if (prx->funcs.empty())
|
|
{
|
|
LOG_FATAL(LOADER, "Module %s has no functions!", name);
|
|
}
|
|
else
|
|
{
|
|
// TODO: fix arguments
|
|
prx->validate(prx->funcs[0].addr);
|
|
}
|
|
|
|
if (name == "liblv2.sprx")
|
|
{
|
|
// Run liblv2.sprx entry point (TODO)
|
|
entry = prx->start.addr();
|
|
}
|
|
else
|
|
{
|
|
loaded_modules.emplace_back(std::move(prx));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fmt::throw_exception("Failed to load /dev_flash/sys/external/%s: %s", name, obj.get_error());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set path (TODO)
|
|
_main->name.clear();
|
|
_main->path = vfs::get(Emu.argv[0]);
|
|
|
|
// Analyse executable (TODO)
|
|
_main->analyse(0, static_cast<u32>(elf.header.e_entry));
|
|
|
|
// Validate analyser results (not required)
|
|
_main->validate(0);
|
|
|
|
// Set SDK version
|
|
g_ps3_process_info.sdk_ver = sdk_version;
|
|
|
|
// Set ppc fixed allocations segment permission
|
|
g_ps3_process_info.ppc_seg = ppc_seg;
|
|
|
|
if (ppc_seg != 0x0)
|
|
{
|
|
if (ppc_seg != 0x1)
|
|
{
|
|
LOG_TODO(LOADER, "Unknown ppc_seg flag value = 0x%x", ppc_seg);
|
|
}
|
|
|
|
// Additional segment for fixed allocations
|
|
if (!vm::map(0x30000000, 0x10000000, 0x200))
|
|
{
|
|
fmt::throw_exception("Failed to map ppc_seg's segment!" HERE);
|
|
}
|
|
}
|
|
|
|
// Initialize process arguments
|
|
auto args = vm::ptr<u64>::make(vm::alloc(u32{sizeof(u64)} * (::size32(Emu.argv) + ::size32(Emu.envp) + 2), vm::main));
|
|
auto argv = args;
|
|
|
|
for (const auto& arg : Emu.argv)
|
|
{
|
|
const u32 arg_size = ::align(::size32(arg) + 1, 0x10);
|
|
const u32 arg_addr = vm::alloc(arg_size, vm::main);
|
|
|
|
std::memcpy(vm::base(arg_addr), arg.data(), arg_size);
|
|
|
|
*args++ = arg_addr;
|
|
}
|
|
|
|
*args++ = 0;
|
|
auto envp = args;
|
|
|
|
for (const auto& arg : Emu.envp)
|
|
{
|
|
const u32 arg_size = ::align(::size32(arg) + 1, 0x10);
|
|
const u32 arg_addr = vm::alloc(arg_size, vm::main);
|
|
|
|
std::memcpy(vm::base(arg_addr), arg.data(), arg_size);
|
|
|
|
*args++ = arg_addr;
|
|
}
|
|
|
|
// Fix primary stack size
|
|
switch (u32 sz = primary_stacksize)
|
|
{
|
|
case 0x10: primary_stacksize = 32 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_32K
|
|
case 0x20: primary_stacksize = 64 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_64K
|
|
case 0x30: primary_stacksize = 96 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_96K
|
|
case 0x40: primary_stacksize = 128 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_128K
|
|
case 0x50: primary_stacksize = 256 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_256K
|
|
case 0x60: primary_stacksize = 512 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_512K
|
|
case 0x70: primary_stacksize = 1024 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_1M
|
|
default:
|
|
{
|
|
primary_stacksize = ::align<u32>(std::clamp<u32>(sz, 0x10000, 0x100000), 4096);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Initialize main thread
|
|
ppu_thread_params p{};
|
|
p.stack_addr = vm::cast(vm::alloc(primary_stacksize, vm::stack, 4096));
|
|
p.stack_size = primary_stacksize;
|
|
|
|
auto ppu = idm::make_ptr<named_thread<ppu_thread>>("PPU[0x1000000] Thread (main_thread)", p, "main_thread", primary_prio, 1);
|
|
|
|
// Write initial data (exitspawn)
|
|
if (Emu.data.size())
|
|
{
|
|
std::memcpy(vm::base(ppu->stack_addr + ppu->stack_size - ::size32(Emu.data)), Emu.data.data(), Emu.data.size());
|
|
ppu->gpr[1] -= Emu.data.size();
|
|
}
|
|
|
|
// Initialize memory stats (according to sdk version)
|
|
// TODO: This is probably wrong with vsh.self
|
|
u32 mem_size;
|
|
if (sdk_version > 0x0021FFFF)
|
|
{
|
|
mem_size = 0xD500000;
|
|
}
|
|
else if (sdk_version > 0x00192FFF)
|
|
{
|
|
mem_size = 0xD300000;
|
|
}
|
|
else if (sdk_version > 0x0018FFFF)
|
|
{
|
|
mem_size = 0xD100000;
|
|
}
|
|
else if (sdk_version > 0x0017FFFF)
|
|
{
|
|
mem_size = 0xD000000;
|
|
}
|
|
else if (sdk_version > 0x00154FFF)
|
|
{
|
|
mem_size = 0xCC00000;
|
|
}
|
|
else
|
|
{
|
|
mem_size = 0xC800000;
|
|
}
|
|
|
|
if (g_cfg.core.debug_console_mode)
|
|
{
|
|
// TODO: Check for all sdk versions
|
|
mem_size += 0xC000000;
|
|
}
|
|
|
|
g_fxo->init<lv2_memory_container>(mem_size);
|
|
|
|
ppu->cmd_push({ppu_cmd::initialize, 0});
|
|
|
|
if (!entry)
|
|
{
|
|
// Set TLS args, call sys_initialize_tls
|
|
ppu->cmd_list
|
|
({
|
|
{ ppu_cmd::set_args, 4 }, u64{ppu->id}, u64{tls_vaddr}, u64{tls_fsize}, u64{tls_vsize},
|
|
{ ppu_cmd::hle_call, FIND_FUNC(sys_initialize_tls) },
|
|
});
|
|
|
|
entry = static_cast<u32>(elf.header.e_entry); // Run entry from elf
|
|
}
|
|
|
|
// Run start functions
|
|
for (const auto& prx : loaded_modules)
|
|
{
|
|
if (!prx->start)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Reset arguments, run module entry point function
|
|
ppu->cmd_list
|
|
({
|
|
{ ppu_cmd::set_args, 2 }, u64{0}, u64{0},
|
|
{ ppu_cmd::lle_call, prx->start.addr() },
|
|
});
|
|
}
|
|
|
|
// Set command line arguments, run entry function
|
|
ppu->cmd_list
|
|
({
|
|
{ ppu_cmd::set_args, 8 }, u64{Emu.argv.size()}, u64{argv.addr()}, u64{envp.addr()}, u64{0}, u64{ppu->id}, u64{tls_vaddr}, u64{tls_fsize}, u64{tls_vsize},
|
|
{ ppu_cmd::set_gpr, 11 }, u64{elf.header.e_entry},
|
|
{ ppu_cmd::set_gpr, 12 }, u64{malloc_pagesize},
|
|
{ ppu_cmd::lle_call, entry },
|
|
});
|
|
|
|
// Set actual memory protection (experimental)
|
|
for (const auto& prog : elf.progs)
|
|
{
|
|
const u32 addr = static_cast<u32>(prog.p_vaddr);
|
|
const u32 size = static_cast<u32>(prog.p_memsz);
|
|
|
|
if (prog.p_type == 0x1 /* LOAD */ && prog.p_memsz && (prog.p_flags & 0x2) == 0 /* W */)
|
|
{
|
|
// Set memory protection to read-only when necessary
|
|
verify(HERE), vm::page_protect(addr, ::align(size, 0x1000), 0, 0, vm::page_writable);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<lv2_overlay> ppu_load_overlay(const ppu_exec_object& elf, const std::string& path)
|
|
{
|
|
const auto ovlm = idm::make_ptr<lv2_obj, lv2_overlay>();
|
|
|
|
// Access linkage information object
|
|
const auto link = g_fxo->get<ppu_linkage_info>();
|
|
|
|
// Executable hash
|
|
sha1_context sha;
|
|
sha1_starts(&sha);
|
|
|
|
// Allocate memory at fixed positions
|
|
for (const auto& prog : elf.progs)
|
|
{
|
|
LOG_NOTICE(LOADER, "** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags);
|
|
|
|
ppu_segment _seg;
|
|
const u32 addr = _seg.addr = vm::cast(prog.p_vaddr, HERE);
|
|
const u32 size = _seg.size = ::narrow<u32>(prog.p_memsz, "p_memsz" HERE);
|
|
const u32 type = _seg.type = prog.p_type;
|
|
const u32 flag = _seg.flags = prog.p_flags;
|
|
_seg.filesz = ::narrow<u32>(prog.p_filesz, "p_filesz" HERE);
|
|
|
|
// Hash big-endian values
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_type), sizeof(prog.p_type));
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_flags), sizeof(prog.p_flags));
|
|
|
|
if (type == 0x1 /* LOAD */ && prog.p_memsz)
|
|
{
|
|
if (prog.bin.size() > size || prog.bin.size() != prog.p_filesz)
|
|
fmt::throw_exception("Invalid binary size (0x%llx, memsz=0x%x)", prog.bin.size(), size);
|
|
|
|
if (!vm::falloc(addr, size))
|
|
fmt::throw_exception("vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size);
|
|
|
|
// Copy segment data, hash it
|
|
std::memcpy(vm::base(addr), prog.bin.data(), prog.bin.size());
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_vaddr), sizeof(prog.p_vaddr));
|
|
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_memsz), sizeof(prog.p_memsz));
|
|
sha1_update(&sha, prog.bin.data(), prog.bin.size());
|
|
|
|
// Initialize executable code if necessary
|
|
if (prog.p_flags & 0x1)
|
|
{
|
|
ppu_register_range(addr, size);
|
|
}
|
|
|
|
// Store only LOAD segments (TODO)
|
|
ovlm->segs.emplace_back(_seg);
|
|
}
|
|
}
|
|
|
|
// Load section list, used by the analyser
|
|
for (const auto& s : elf.shdrs)
|
|
{
|
|
LOG_NOTICE(LOADER, "** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", s.sh_type, s.sh_addr, s.sh_size, s.sh_flags);
|
|
|
|
ppu_segment _sec;
|
|
const u32 addr = _sec.addr = vm::cast(s.sh_addr);
|
|
const u32 size = _sec.size = vm::cast(s.sh_size);
|
|
const u32 type = _sec.type = s.sh_type;
|
|
const u32 flag = _sec.flags = s.sh_flags & 7;
|
|
_sec.filesz = 0;
|
|
|
|
if (s.sh_type == 1 && addr && size)
|
|
{
|
|
ovlm->secs.emplace_back(_sec);
|
|
}
|
|
}
|
|
|
|
sha1_finish(&sha, ovlm->sha1);
|
|
|
|
// Format patch name
|
|
std::string hash("OVL-0000000000000000000000000000000000000000");
|
|
for (u32 i = 0; i < 20; i++)
|
|
{
|
|
constexpr auto pal = "0123456789abcdef";
|
|
hash[4 + i * 2] = pal[ovlm->sha1[i] >> 4];
|
|
hash[5 + i * 2] = pal[ovlm->sha1[i] & 15];
|
|
}
|
|
|
|
// Apply the patch
|
|
auto applied = g_fxo->get<patch_engine>()->apply(hash, vm::g_base_addr);
|
|
|
|
if (!Emu.GetTitleID().empty())
|
|
{
|
|
// Alternative patch
|
|
applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
|
|
}
|
|
|
|
LOG_NOTICE(LOADER, "OVL executable hash: %s (<- %u)", hash, applied);
|
|
|
|
// Load other programs
|
|
for (auto& prog : elf.progs)
|
|
{
|
|
switch (const u32 p_type = prog.p_type)
|
|
{
|
|
case 0x00000001: break; // LOAD (already loaded)
|
|
|
|
case 0x60000001: // LOOS+1
|
|
{
|
|
if (prog.p_filesz)
|
|
{
|
|
struct process_param_t
|
|
{
|
|
be_t<u32> size; //0x60
|
|
be_t<u32> magic; //string OVLM
|
|
be_t<u32> version; //0x17000
|
|
be_t<u32> sdk_version; //seems to be correct
|
|
//string "stage_ovlm"
|
|
//and a lot of zeros.
|
|
};
|
|
|
|
const auto& info = vm::_ref<process_param_t>(vm::cast(prog.p_vaddr, HERE));
|
|
|
|
if (info.size < sizeof(process_param_t))
|
|
{
|
|
LOG_WARNING(LOADER, "Bad process_param size! [0x%x : 0x%x]", info.size, u32{sizeof(process_param_t)});
|
|
}
|
|
|
|
if (info.magic != 0x4f564c4d) //string "OVLM"
|
|
{
|
|
LOG_ERROR(LOADER, "Bad process_param magic! [0x%x]", info.magic);
|
|
}
|
|
else
|
|
{
|
|
LOG_NOTICE(LOADER, "*** sdk version: 0x%x", info.sdk_version);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x60000002: // LOOS+2 seems to be 0x0 in size for overlay elfs, at least in known cases
|
|
{
|
|
if (prog.p_filesz)
|
|
{
|
|
struct ppu_proc_prx_param_t
|
|
{
|
|
be_t<u32> size;
|
|
be_t<u32> magic;
|
|
be_t<u32> version;
|
|
be_t<u32> unk0;
|
|
be_t<u32> libent_start;
|
|
be_t<u32> libent_end;
|
|
be_t<u32> libstub_start;
|
|
be_t<u32> libstub_end;
|
|
be_t<u16> ver;
|
|
be_t<u16> unk1;
|
|
be_t<u32> unk2;
|
|
};
|
|
|
|
const auto& proc_prx_param = vm::_ref<const ppu_proc_prx_param_t>(vm::cast(prog.p_vaddr, HERE));
|
|
|
|
LOG_NOTICE(LOADER, "* libent_start = *0x%x", proc_prx_param.libent_start);
|
|
LOG_NOTICE(LOADER, "* libstub_start = *0x%x", proc_prx_param.libstub_start);
|
|
LOG_NOTICE(LOADER, "* unk0 = 0x%x", proc_prx_param.unk0);
|
|
LOG_NOTICE(LOADER, "* unk2 = 0x%x", proc_prx_param.unk2);
|
|
|
|
if (proc_prx_param.magic != 0x1b434cec)
|
|
{
|
|
fmt::throw_exception("Bad magic! (0x%x)", proc_prx_param.magic);
|
|
}
|
|
|
|
ppu_load_exports(link, proc_prx_param.libent_start, proc_prx_param.libent_end);
|
|
ppu_load_imports(ovlm->relocs, link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
LOG_ERROR(LOADER, "Unknown phdr type (0x%08x)", p_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
ovlm->entry = static_cast<u32>(elf.header.e_entry);
|
|
|
|
// Analyse executable (TODO)
|
|
ovlm->analyse(0, ovlm->entry);
|
|
|
|
// Validate analyser results (not required)
|
|
ovlm->validate(0);
|
|
|
|
// Set path (TODO)
|
|
ovlm->name = path.substr(path.find_last_of('/') + 1);
|
|
ovlm->path = path;
|
|
|
|
return ovlm;
|
|
}
|