HLE linkage rewritten

This commit is contained in:
Nekotekina 2017-04-13 02:31:42 +03:00
parent 14f02b23c2
commit 7428bb3025
8 changed files with 163 additions and 157 deletions

View File

@ -1435,5 +1435,5 @@ DECLARE(ppu_module_manager::cellGcmSys)("cellGcmSys", []()
REG_FUNC(cellGcmSys, cellGcmGpadCaptureSnapshot);
// Special
REG_FNID(cellGcmSys, 0x00000000, cellGcmCallback);
REG_FUNC(cellGcmSys, cellGcmCallback).flags = MFF_HIDDEN;
});

View File

@ -2149,6 +2149,8 @@ void PPUDisAsm::FCFID(ppu_opcode_t op)
DisAsm_F2_RC("fcfid", op.frd, op.frb, op.rc);
}
extern std::vector<std::string> g_ppu_function_names;
void PPUDisAsm::UNK(ppu_opcode_t op)
{
if (op.opcode == dump_pc && ppu_function_manager::addr)
@ -2158,7 +2160,7 @@ void PPUDisAsm::UNK(ppu_opcode_t op)
if (index < ppu_function_manager::get().size())
{
Write(fmt::format("Function : (index %u)", index));
Write(fmt::format("Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index));
return;
}
}

View File

@ -125,6 +125,8 @@ cfg::bool_entry g_cfg_load_libreq(cfg::root.core, "Load required libraries", tru
cfg::set_entry g_cfg_load_libs(cfg::root.core, "Load libraries");
extern cfg::map_entry<ppu_decoder_type> g_cfg_ppu_decoder;
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);
@ -136,6 +138,9 @@ extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32);
extern u32 g_ps3_sdk_version;
// HLE function name cache
std::vector<std::string> g_ppu_function_names;
extern u32 ppu_generate_id(const char* name)
{
// Symbol name suffix
@ -195,13 +200,16 @@ struct ppu_linkage_info
{
struct info
{
bool hle = false;
u32 export_addr = 0;
std::set<u32> imports;
};
// FNID -> (export; [imports...])
std::map<u32, info> functions;
std::map<u32, info> variables;
std::unordered_map<u32, info, value_hash<u32>> functions;
std::unordered_map<u32, info, value_hash<u32>> variables;
bool imported = false;
};
// Module map
@ -209,7 +217,7 @@ struct ppu_linkage_info
};
// Initialize static modules.
static void ppu_initialize_modules()
static void ppu_initialize_modules(const std::shared_ptr<ppu_linkage_info>& link)
{
const std::initializer_list<const ppu_static_module*> registered
{
@ -312,23 +320,6 @@ static void ppu_initialize_modules()
&ppu_module_manager::sys_lv2dbg,
};
// "Use" all the modules for correct linkage
for (auto& module : registered)
{
LOG_TRACE(LOADER, "Registered static module: %s", module->name);
for (auto& function : module->functions)
{
LOG_TRACE(LOADER, "** 0x%08X: %s", function.first, function.second.name);
}
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);
variable.second.var->set(0);
}
}
// Initialize double-purpose fake OPD array for HLE functions
const auto& hle_funcs = ppu_function_manager::get();
@ -352,6 +343,86 @@ static void ppu_initialize_modules()
// 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);
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.hle = true;
flink.export_addr = ppu_function_manager::addr + 8 * function.second.index;
}
}
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.var->set(vm::alloc(variable.second.size, vm::main, std::max<u32>(variable.second.align, 4096)));
}
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.var->set(alloc_addr);
alloc_addr += variable.second.size;
}
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();
}
auto& vlink = linkage.variables[variable.first];
vlink.hle = true;
vlink.export_addr = variable.second.var->addr();
}
}
}
// Link variable
@ -452,6 +523,9 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
// 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;
@ -463,13 +537,13 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
LOG_NOTICE(LOADER, "**** %s export: [%s] at 0x%x", module_name, ppu_get_function_name(module_name, fnid), faddr);
// Function linkage info
auto& flink = link->modules[module_name].functions[fnid];
auto& flink = mlink.functions[fnid];
if (flink.export_addr)
if (flink.export_addr && !flink.hle)
{
LOG_FATAL(LOADER, "Already linked function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name);
LOG_ERROR(LOADER, "Already linked function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name);
}
else
//else
{
// Static function
const auto _sf = _sm && _sm->functions.count(fnid) ? &_sm->functions.at(fnid) : nullptr;
@ -499,6 +573,7 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
{
// Set exported function
flink.export_addr = faddr;
flink.hle = false;
// Fix imports
for (const u32 addr : flink.imports)
@ -521,16 +596,17 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
LOG_NOTICE(LOADER, "**** %s export: &[%s] at 0x%x", module_name, ppu_get_variable_name(module_name, vnid), vaddr);
// Variable linkage info
auto& vlink = link->modules[module_name].variables[vnid];
auto& vlink = mlink.variables[vnid];
if (vlink.export_addr)
if (vlink.export_addr && !vlink.hle)
{
LOG_FATAL(LOADER, "Already linked variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name);
LOG_ERROR(LOADER, "Already linked variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name);
}
else
//else
{
// Set exported variable
vlink.export_addr = vaddr;
vlink.hle = false;
// Fix imports
for (const auto vref : vlink.imports)
@ -565,6 +641,9 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
// 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;
@ -580,12 +659,17 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
// Add new import
flink.imports.emplace(faddr);
mlink.imported = true;
// Link if available
if (flink.export_addr)
{
vm::write32(faddr, flink.export_addr);
}
else
{
vm::write32(faddr, ppu_function_manager::addr);
}
//LOG_WARNING(LOADER, "Imported function '%s' in module '%s' (0x%x)", ppu_get_function_name(module_name, fnid), module_name, faddr);
}
@ -604,6 +688,7 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
// Add new import
vlink.imports.emplace(vref);
mlink.imported = true;
// Link if available
if (vlink.export_addr)
@ -620,6 +705,12 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& name)
{
if (g_cfg_ppu_decoder.get() == ppu_decoder_type::llvm && name == "libfiber.sprx")
{
LOG_FATAL(PPU, "libfiber.sprx is not compatible with PPU LLVM Recompiler. Use PPU Interpreter.");
Emu.Pause();
}
std::vector<std::pair<u32, u32>> segments;
std::vector<std::pair<u32, u32>> sections;
@ -889,7 +980,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
// Initialize HLE modules
ppu_initialize_modules();
ppu_initialize_modules(link);
// Load other programs
for (auto& prog : elf.progs)
@ -1120,8 +1211,14 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
}
// TODO: recursively scan all SPRX files in /app_home/ for imports
for (const auto& pair : link->modules)
{
if (!pair.second.imported)
{
continue;
}
for (auto range = sprx_map.equal_range(pair.first); range.first != range.second; ++range.first)
{
load_libs.emplace(range.first->second);
@ -1169,106 +1266,6 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
}
// Check unlinked functions and variables
for (auto& module : link->modules)
{
const auto _sm = ppu_module_manager::get_module(module.first);
if (!_sm)
{
LOG_ERROR(LOADER, "Unknown module '%s'", module.first);
}
else
{
// Allocate HLE variables (TODO)
for (auto& var : _sm->variables)
{
var.second.var->set(vm::alloc(var.second.size, vm::main, std::max<u32>(var.second.align, 4096)));
LOG_WARNING(LOADER, "Allocated variable '%s' in module '%s' at *0x%x", var.second.name, module.first, var.second.var->addr());
}
// Initialize HLE variables (TODO)
for (auto& var : _sm->variables)
{
var.second.init();
}
}
for (auto& entry : module.second.functions)
{
const u32 fnid = entry.first;
const u32 faddr = entry.second.export_addr;
if (faddr == 0)
{
const std::string fname = ppu_get_function_name(module.first, fnid);
// Link HLE implementation if available
if (const auto _sf = _sm && _sm->functions.count(fnid) ? &_sm->functions.at(fnid) : nullptr)
{
LOG_NOTICE(LOADER, "Linking HLE function '%s' in module '%s' (index %u)", fname, module.first, _sf->index);
for (const u32 import : entry.second.imports)
{
LOG_TRACE(LOADER, "** Linked at *0x%x (0x%x)", import, vm::read32(import));
vm::write32(import, ppu_function_manager::addr + 8 * _sf->index);
}
}
else
{
LOG_ERROR(LOADER, "Unknown function '%s' in module '%s'", fname, module.first);
for (const u32 import : entry.second.imports)
{
LOG_WARNING(LOADER, "** Not linked at *0x%x (0x%x)", import, vm::read32(import));
vm::write32(import, ppu_function_manager::addr);
}
}
}
}
for (auto& entry : module.second.variables)
{
const u32 vnid = entry.first;
const u32 vaddr = entry.second.export_addr;
if (vaddr == 0)
{
const std::string vname = ppu_get_variable_name(module.first, vnid);
// Link HLE variable if available
if (const auto _sv = _sm && _sm->variables.count(vnid) ? &_sm->variables.at(vnid) : nullptr)
{
LOG_NOTICE(LOADER, "Linking HLE variable '%s' in module '%s' (*0x%x):", vname, module.first, _sv->var->addr());
for (const u32 ref : entry.second.imports)
{
ppu_patch_variable_refs(ref, _sv->var->addr());
LOG_NOTICE(LOADER, "** Linked at ref=*0x%x", ref);
}
}
else
{
LOG_ERROR(LOADER, "Unknown variable '%s' in module '%s'", vname, module.first);
for (const u32 ref : entry.second.imports)
{
LOG_WARNING(LOADER, "** Not linked at ref=*0x%x", ref);
}
}
}
else
{
// Retro-link LLE variable (TODO: HLE must not be allocated/initialized in this case)
if (const auto _sv = _sm && _sm->variables.count(vnid) ? &_sm->variables.at(vnid) : nullptr)
{
_sv->var->set(vaddr);
LOG_NOTICE(LOADER, "Linked LLE variable '%s' in module '%s' -> 0x%x", ppu_get_variable_name(module.first, vnid), module.first, vaddr);
}
}
}
}
{
// Analyse executable
std::vector<ppu_function> main_funcs = ppu_analyse(segments, sections, 0, elf.header.e_entry);
@ -1307,7 +1304,9 @@ void ppu_load_exec(const ppu_exec_object& elf)
ppu->cmd_push({ppu_cmd::initialize, 0});
// TODO: adjust for liblv2 loading option
if (!g_cfg_load_liblv2)
u32 entry = static_cast<u32>(elf.header.e_entry);
if (!g_cfg_load_liblv2 || g_cfg_load_libreq)
{
// Set TLS args, call sys_initialize_tls
ppu->cmd_list
@ -1316,6 +1315,13 @@ void ppu_load_exec(const ppu_exec_object& elf)
{ ppu_cmd::hle_call, FIND_FUNC(sys_initialize_tls) },
});
}
else
{
// Run liblv2.sprx entry point (TODO)
entry = loaded_modules[0]->start.addr();
loaded_modules.clear();
}
// Run start functions
for (const auto& prx : loaded_modules)
@ -1337,9 +1343,9 @@ void ppu_load_exec(const ppu_exec_object& elf)
ppu->cmd_list
({
{ ppu_cmd::set_args, 8 }, u64{args.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{0xabadcafe},
{ ppu_cmd::set_gpr, 11 }, u64{elf.header.e_entry},
{ ppu_cmd::set_gpr, 12 }, u64{malloc_pagesize},
{ ppu_cmd::lle_call, static_cast<u32>(elf.header.e_entry) },
{ ppu_cmd::lle_call, entry },
});
// Set actual memory protection (experimental)

View File

@ -14,6 +14,7 @@ enum ppu_static_function_flags : u32
{
MFF_FORCED_HLE = (1 << 0), // Always call HLE function
MFF_PERFECT = (1 << 1), // Indicates complete implementation and LLE interchangeability
MFF_HIDDEN = (1 << 2), // Invisible function for internal use (TODO)
};
// HLE function information
@ -43,8 +44,8 @@ public:
task_stack on_load;
task_stack on_unload;
std::map<u32, ppu_static_function> functions;
std::map<u32, ppu_static_variable> variables;
std::unordered_map<u32, ppu_static_function, value_hash<u32>> functions;
std::unordered_map<u32, ppu_static_variable, value_hash<u32>> variables;
public:
ppu_static_module(const char* name);

View File

@ -82,13 +82,6 @@ void fmt_class_string<join_status>::format(std::string& out, u64 arg)
});
}
enum class ppu_decoder_type
{
precise,
fast,
llvm,
};
cfg::map_entry<ppu_decoder_type> g_cfg_ppu_decoder(cfg::root.core, "PPU Decoder", 1,
{
{ "Interpreter (precise)", ppu_decoder_type::precise },
@ -868,17 +861,6 @@ extern void ppu_initialize()
return;
}
if (g_cfg_ppu_decoder.get() == ppu_decoder_type::llvm)
{
idm::select<lv2_obj, lv2_prx>([](u32, lv2_prx& prx)
{
if (prx.name == "libfiber.sprx")
{
fmt::raw_error("libfiber.sprx is not compatible with PPU LLVM Recompiler.");
}
});
}
std::size_t fpos = 0;
while (fpos < _funcs->size())

View File

@ -18,6 +18,13 @@ enum class ppu_cmd : u32
sleep,
};
enum class ppu_decoder_type
{
precise,
fast,
llvm,
};
class ppu_thread : public cpu_thread
{
public:

View File

@ -363,7 +363,15 @@ void Emulator::Load()
}
debug::autopause::reload();
if (g_cfg_autostart) Run();
if (g_cfg_autostart && IsReady())
{
Run();
}
else if (IsPaused())
{
m_status = Ready;
}
}
catch (const std::exception& e)
{
@ -412,7 +420,7 @@ bool Emulator::Pause()
// Try to pause
if (!m_status.compare_and_swap_test(Running, Paused))
{
return false;
return m_status.compare_and_swap_test(Ready, Paused);
}
rpcs3::on_pause()();

View File

@ -193,7 +193,7 @@ void KernelExplorer::Update()
case SYS_PRX_OBJECT:
{
auto& prx = static_cast<lv2_prx&>(obj);
m_tree->AppendItem(node, fmt::format("PRX: ID = 0x%08x", id));
m_tree->AppendItem(node, fmt::format("PRX: ID = 0x%08x '%s'", id, prx.name));
break;
}
case SYS_SPUPORT_OBJECT: