fix patch cache clear behavior on GC versions that need it

This commit is contained in:
Martin Michelsen
2023-05-10 18:31:45 -07:00
parent 90a3be7803
commit fc672978d8
13 changed files with 228 additions and 91 deletions
+3
View File
@@ -75,6 +75,9 @@ struct Client {
ENCRYPTED_SEND_FUNCTION_CALL = 0x00000800, ENCRYPTED_SEND_FUNCTION_CALL = 0x00000800,
// Client supports send_function_call but does not actually run the code // Client supports send_function_call but does not actually run the code
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x00001000, SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x00001000,
// Client supports send_function_call but doesn't clear its caches properly
// before calling the function (so we need to patch it)
SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH = 0x00020000,
// Client is vulnerable to a buffer overflow that we can use to enable // Client is vulnerable to a buffer overflow that we can use to enable
// send_function_call // send_function_call
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x00008000, USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x00008000,
+17 -6
View File
@@ -15,14 +15,20 @@
using namespace std; using namespace std;
static bool is_function_compiler_available = true;
bool function_compiler_available() { bool function_compiler_available() {
#ifndef HAVE_RESOURCE_FILE #ifndef HAVE_RESOURCE_FILE
return false; return false;
#else #else
return true; return is_function_compiler_available;
#endif #endif
} }
void set_function_compiler_available(bool is_available) {
is_function_compiler_available = is_available;
}
const char* name_for_architecture(CompiledFunctionCode::Architecture arch) { const char* name_for_architecture(CompiledFunctionCode::Architecture arch) {
switch (arch) { switch (arch) {
case CompiledFunctionCode::Architecture::POWERPC: case CompiledFunctionCode::Architecture::POWERPC:
@@ -37,7 +43,8 @@ const char* name_for_architecture(CompiledFunctionCode::Architecture arch) {
template <typename FooterT> template <typename FooterT>
string CompiledFunctionCode::generate_client_command_t( string CompiledFunctionCode::generate_client_command_t(
const unordered_map<string, uint32_t>& label_writes, const unordered_map<string, uint32_t>& label_writes,
const string& suffix) const { const string& suffix,
uint32_t override_relocations_offset) const {
FooterT footer; FooterT footer;
footer.num_relocations = this->relocation_deltas.size(); footer.num_relocations = this->relocation_deltas.size();
footer.unused1.clear(0); footer.unused1.clear(0);
@@ -63,13 +70,16 @@ string CompiledFunctionCode::generate_client_command_t(
w.put_u8(0); w.put_u8(0);
} }
footer.relocations_offset = w.size(); if (override_relocations_offset) {
footer.relocations_offset = override_relocations_offset;
} else {
for (uint16_t delta : this->relocation_deltas) { for (uint16_t delta : this->relocation_deltas) {
w.put<typename FooterT::U16T>(delta); w.put<typename FooterT::U16T>(delta);
} }
if (this->relocation_deltas.size() & 1) { if (this->relocation_deltas.size() & 1) {
w.put_u16(0); w.put_u16(0);
} }
}
w.put(footer); w.put(footer);
return move(w.str()); return move(w.str());
@@ -77,13 +87,14 @@ string CompiledFunctionCode::generate_client_command_t(
string CompiledFunctionCode::generate_client_command( string CompiledFunctionCode::generate_client_command(
const unordered_map<string, uint32_t>& label_writes, const unordered_map<string, uint32_t>& label_writes,
const string& suffix) const { const string& suffix,
uint32_t override_relocations_offset) const {
if (this->arch == Architecture::POWERPC) { if (this->arch == Architecture::POWERPC) {
return this->generate_client_command_t<S_ExecuteCode_Footer_GC_B2>( return this->generate_client_command_t<S_ExecuteCode_Footer_GC_B2>(
label_writes, suffix); label_writes, suffix, override_relocations_offset);
} else if ((this->arch == Architecture::X86) || (this->arch == Architecture::SH4)) { } else if ((this->arch == Architecture::X86) || (this->arch == Architecture::SH4)) {
return this->generate_client_command_t<S_ExecuteCode_Footer_DC_PC_XB_BB_B2>( return this->generate_client_command_t<S_ExecuteCode_Footer_DC_PC_XB_BB_B2>(
label_writes, suffix); label_writes, suffix, override_relocations_offset);
} else { } else {
throw logic_error("invalid architecture"); throw logic_error("invalid architecture");
} }
+5 -2
View File
@@ -11,6 +11,7 @@
#include "Menu.hh" #include "Menu.hh"
bool function_compiler_available(); bool function_compiler_available();
void set_function_compiler_available(bool is_available);
// TODO: Support x86 and SH4 function calls in the future. Currently we only // TODO: Support x86 and SH4 function calls in the future. Currently we only
// support PPC32 because I haven't written an appropriate x86 assembler yet. // support PPC32 because I haven't written an appropriate x86 assembler yet.
@@ -36,10 +37,12 @@ struct CompiledFunctionCode {
template <typename FooterT> template <typename FooterT>
std::string generate_client_command_t( std::string generate_client_command_t(
const std::unordered_map<std::string, uint32_t>& label_writes, const std::unordered_map<std::string, uint32_t>& label_writes,
const std::string& suffix) const; const std::string& suffix,
uint32_t override_relocations_offset = 0) const;
std::string generate_client_command( std::string generate_client_command(
const std::unordered_map<std::string, uint32_t>& label_writes = {}, const std::unordered_map<std::string, uint32_t>& label_writes = {},
const std::string& suffix = "") const; const std::string& suffix = "",
uint32_t override_relocations_offset = 0) const;
}; };
const char* name_for_architecture(CompiledFunctionCode::Architecture arch); const char* name_for_architecture(CompiledFunctionCode::Architecture arch);
+4
View File
@@ -1194,6 +1194,10 @@ int main(int argc, char** argv) {
use_terminal_colors = true; use_terminal_colors = true;
} }
if (is_replay) {
set_function_compiler_available(false);
}
shared_ptr<struct event_base> base(event_base_new(), event_base_free); shared_ptr<struct event_base> base(event_base_new(), event_base_free);
shared_ptr<ServerState> state(new ServerState(config_filename, is_replay)); shared_ptr<ServerState> state(new ServerState(config_filename, is_replay));
+11 -1
View File
@@ -156,7 +156,8 @@ static void send_proxy_destinations_menu(shared_ptr<ServerState> s, shared_ptr<C
static bool send_enable_send_function_call_if_applicable( static bool send_enable_send_function_call_if_applicable(
shared_ptr<ServerState> s, shared_ptr<Client> c) { shared_ptr<ServerState> s, shared_ptr<Client> c) {
if (c->flags & Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL) { if (function_compiler_available() &&
(c->flags & Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL)) {
if (s->episode_3_send_function_call_enabled) { if (s->episode_3_send_function_call_enabled) {
send_quest_buffer_overflow(s, c); send_quest_buffer_overflow(s, c);
} else { } else {
@@ -231,6 +232,15 @@ void on_login_complete(shared_ptr<ServerState> s, shared_ptr<Client> c) {
send_change_event(c, s->pre_lobby_event); send_change_event(c, s->pre_lobby_event);
} }
if (function_compiler_available() && (c->flags & Client::Flag::SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH)) {
send_function_call(
c, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2634EC);
send_function_call(
c, s->function_code_index->name_to_function.at("CacheClearFix-Phase2"));
c->flags &= ~Client::Flag::SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH;
send_update_client_config(c);
}
if (s->welcome_message.empty() || if (s->welcome_message.empty() ||
(c->flags & Client::Flag::NO_D6) || (c->flags & Client::Flag::NO_D6) ||
!(c->flags & Client::Flag::AT_WELCOME_MESSAGE)) { !(c->flags & Client::Flag::AT_WELCOME_MESSAGE)) {
+7 -4
View File
@@ -316,7 +316,8 @@ void send_function_call(
const unordered_map<string, uint32_t>& label_writes, const unordered_map<string, uint32_t>& label_writes,
const string& suffix, const string& suffix,
uint32_t checksum_addr, uint32_t checksum_addr,
uint32_t checksum_size) { uint32_t checksum_size,
uint32_t override_relocations_offset) {
return send_function_call( return send_function_call(
c->channel, c->channel,
c->flags, c->flags,
@@ -324,7 +325,8 @@ void send_function_call(
label_writes, label_writes,
suffix, suffix,
checksum_addr, checksum_addr,
checksum_size); checksum_size,
override_relocations_offset);
} }
void send_function_call( void send_function_call(
@@ -334,7 +336,8 @@ void send_function_call(
const unordered_map<string, uint32_t>& label_writes, const unordered_map<string, uint32_t>& label_writes,
const string& suffix, const string& suffix,
uint32_t checksum_addr, uint32_t checksum_addr,
uint32_t checksum_size) { uint32_t checksum_size,
uint32_t override_relocations_offset) {
if (client_flags & Client::Flag::NO_SEND_FUNCTION_CALL) { if (client_flags & Client::Flag::NO_SEND_FUNCTION_CALL) {
throw logic_error("client does not support function calls"); throw logic_error("client does not support function calls");
} }
@@ -345,7 +348,7 @@ void send_function_call(
string data; string data;
uint32_t index = 0; uint32_t index = 0;
if (code.get()) { if (code.get()) {
data = code->generate_client_command(label_writes, suffix); data = code->generate_client_command(label_writes, suffix, override_relocations_offset);
index = code->index; index = code->index;
if (client_flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) { if (client_flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) {
+4 -2
View File
@@ -139,14 +139,16 @@ void send_function_call(
const std::unordered_map<std::string, uint32_t>& label_writes = {}, const std::unordered_map<std::string, uint32_t>& label_writes = {},
const std::string& suffix = "", const std::string& suffix = "",
uint32_t checksum_addr = 0, uint32_t checksum_addr = 0,
uint32_t checksum_size = 0); uint32_t checksum_size = 0,
uint32_t override_relocations_offset = 0);
void send_function_call( void send_function_call(
std::shared_ptr<Client> c, std::shared_ptr<Client> c,
std::shared_ptr<CompiledFunctionCode> code, std::shared_ptr<CompiledFunctionCode> code,
const std::unordered_map<std::string, uint32_t>& label_writes = {}, const std::unordered_map<std::string, uint32_t>& label_writes = {},
const std::string& suffix = "", const std::string& suffix = "",
uint32_t checksum_addr = 0, uint32_t checksum_addr = 0,
uint32_t checksum_size = 0); uint32_t checksum_size = 0,
uint32_t override_relocations_offset = 0);
void send_reconnect(std::shared_ptr<Client> c, uint32_t address, uint16_t port); void send_reconnect(std::shared_ptr<Client> c, uint32_t address, uint16_t port);
void send_pc_console_split_reconnect( void send_pc_console_split_reconnect(
+4 -2
View File
@@ -15,7 +15,7 @@ const vector<string> version_to_lobby_port_name = {
const vector<string> version_to_proxy_port_name = { const vector<string> version_to_proxy_port_name = {
"", "dc-proxy", "pc-proxy", "gc-proxy", "xb-proxy", "bb-proxy"}; "", "dc-proxy", "pc-proxy", "gc-proxy", "xb-proxy", "bb-proxy"};
uint16_t flags_for_version(GameVersion version, int64_t sub_version) { uint32_t flags_for_version(GameVersion version, int64_t sub_version) {
switch (sub_version) { switch (sub_version) {
case -1: // Initial check (before sub_version recognition) case -1: // Initial check (before sub_version recognition)
switch (version) { switch (version) {
@@ -55,7 +55,9 @@ uint16_t flags_for_version(GameVersion version, int64_t sub_version) {
case 0x30: // GC Ep1&2 JP v1.02, at least one version of PSO XB case 0x30: // GC Ep1&2 JP v1.02, at least one version of PSO XB
case 0x31: // GC Ep1&2 US v1.00, GC US v1.01, GC EU v1.00, GC JP v1.00 case 0x31: // GC Ep1&2 US v1.00, GC US v1.01, GC EU v1.00, GC JP v1.00
case 0x34: // GC Ep1&2 JP v1.03 case 0x34: // GC Ep1&2 JP v1.03
return 0; return ((version == GameVersion::GC) && function_compiler_available())
? Client::Flag::SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH
: 0;
case 0x32: // GC Ep1&2 EU 50Hz case 0x32: // GC Ep1&2 EU 50Hz
case 0x33: // GC Ep1&2 EU 60Hz case 0x33: // GC Ep1&2 EU 60Hz
return Client::Flag::NO_D6_AFTER_LOBBY; return Client::Flag::NO_D6_AFTER_LOBBY;
+1 -1
View File
@@ -28,7 +28,7 @@ extern const std::vector<std::string> version_to_login_port_name;
extern const std::vector<std::string> version_to_lobby_port_name; extern const std::vector<std::string> version_to_lobby_port_name;
extern const std::vector<std::string> version_to_proxy_port_name; extern const std::vector<std::string> version_to_proxy_port_name;
uint16_t flags_for_version(GameVersion version, int64_t sub_version); uint32_t flags_for_version(GameVersion version, int64_t sub_version);
const char* name_for_version(GameVersion version); const char* name_for_version(GameVersion version);
GameVersion version_for_name(const char* name); GameVersion version_for_name(const char* name);
+4
View File
@@ -0,0 +1,4 @@
entry_ptr:
.data 0x8000C274
start:
.include CacheClearFix
+5
View File
@@ -0,0 +1,5 @@
entry_ptr:
reloc0:
.offsetof start
start:
.include CacheClearFix
+89
View File
@@ -0,0 +1,89 @@
start:
mflr r7
# If this patch has already been run, then the opcode that led here will
# not be bctrl (4E800421). In that case, do nothing.
lis r3, 0x4E80
ori r3, r3, 0x0421
lwz r4, [r7 - 4]
cmp r3, r4
beq apply_patch
blr
apply_patch:
bl patch_end
.offsetof patch
.offsetof patch_end
patch:
mfctr r6
mr r3, r6
li r4, 0x7C00
.include FlushCachedCode
mtctr r6
bctr
patch_end:
mflr r4
addi r4, r4, 8
lwz r3, [r4 - 8]
lwz r5, [r4 - 4]
sub r5, r5, r3
# At this point:
# r4 = address of patch label
# r5 = patch size in bytes
# r7 = saved LR
# Find a spot in the interrupt handlers with enough memory for the patch
lis r3, 0x8000
ori r3, r3, 0x0200
sub r3, r3, r5
check_location:
rlwinm r0, r5, 30, 2, 31
mtctr r0 # ctr = patch size in words
subi r8, r3, 4
check_location_next_word:
lwzu r0, [r8 + 4]
cmpwi r0, 0
beq check_location_word_ok
addi r3, r3, 0x0100
rlwinm r0, r3, 0, 16, 31
cmpwi r0, 0x1800
blt check_location
# No suitable location was found - return null
li r3, 0
mtlr r7
blr
check_location_word_ok:
bdnz check_location_next_word
location_ok:
mr r6, r3
# Now:
# r3 = destination location
# r4 = patch src data
# r5 = patch size in bytes
# r6 = destination location
# r7 = saved LR
.include CopyCode
setup_branch:
# Replace the bctrl opcode that led to this call with a bl opcode that
# leads to the copied patch code
subi r3, r7, 4
sub r4, r6, r3
rlwinm r4, r4, 0, 6, 31
oris r4, r4, 0x4800
ori r4, r4, 0x0001
stw [r3], r4
dcbst r0, r3
sync
icbi r0, r3
isync
# Return the address that the patch was copied to
mr r3, r6
mtlr r7
blr
+1
View File
@@ -1,6 +1,7 @@
# r3 = dest ptr # r3 = dest ptr
# r4 = src ptr # r4 = src ptr
# r5 = size # r5 = size
# Clobbers r0, r3, r4, r5
addi r5, r5, 3 addi r5, r5, 3
rlwinm r5, r5, 30, 2, 31 # r5 = number of words to copy rlwinm r5, r5, 30, 2, 31 # r5 = number of words to copy
mtctr r5 mtctr r5