Remove the global trampoline table and add a small trampoline table next to each .text section instead

This commit is contained in:
Maschell 2025-01-19 10:08:14 +01:00
parent 389b9fc9f1
commit d2015ec59d
13 changed files with 252 additions and 155 deletions

View File

@ -24,10 +24,8 @@
#include <cstdint> #include <cstdint>
static uint32_t sTrampolineID = 0;
std::vector<PluginContainer> std::vector<PluginContainer>
PluginManagement::loadPlugins(const std::vector<PluginLoadWrapper> &pluginDataList, std::vector<relocation_trampoline_entry_t> &trampolineData) { PluginManagement::loadPlugins(const std::vector<PluginLoadWrapper> &pluginDataList) {
std::vector<PluginContainer> plugins; std::vector<PluginContainer> plugins;
for (const auto &pluginDataWrapper : pluginDataList) { for (const auto &pluginDataWrapper : pluginDataList) {
@ -37,7 +35,7 @@ PluginManagement::loadPlugins(const std::vector<PluginLoadWrapper> &pluginDataLi
if (pluginDataWrapper.isLoadAndLink()) { if (pluginDataWrapper.isLoadAndLink()) {
DEBUG_FUNCTION_LINE_INFO("LOAD (ACTIVE) %s", metaInfo->getName().c_str()); DEBUG_FUNCTION_LINE_INFO("LOAD (ACTIVE) %s", metaInfo->getName().c_str());
auto linkInfo = PluginLinkInformationFactory::load(*pluginDataWrapper.getPluginData(), trampolineData, sTrampolineID++); auto linkInfo = PluginLinkInformationFactory::load(*pluginDataWrapper.getPluginData());
if (!linkInfo) { if (!linkInfo) {
auto errMsg = string_format("Failed to load plugin: %s", pluginDataWrapper.getPluginData()->getSource().c_str()); auto errMsg = string_format("Failed to load plugin: %s", pluginDataWrapper.getPluginData()->getSource().c_str());
DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str()); DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str());
@ -68,8 +66,7 @@ PluginManagement::loadPlugins(const std::vector<PluginLoadWrapper> &pluginDataLi
} }
bool PluginManagement::doRelocation(const std::vector<RelocationData> &relocData, bool PluginManagement::doRelocation(const std::vector<RelocationData> &relocData,
std::vector<relocation_trampoline_entry_t> &trampData, std::span<relocation_trampoline_entry_t> trampData,
const uint32_t trampolineID,
std::map<std::string, OSDynLoad_Module> &usedRPls) { std::map<std::string, OSDynLoad_Module> &usedRPls) {
for (auto const &cur : relocData) { for (auto const &cur : relocData) {
uint32_t functionAddress = 0; uint32_t functionAddress = 0;
@ -118,7 +115,7 @@ bool PluginManagement::doRelocation(const std::vector<RelocationData> &relocData
//DEBUG_FUNCTION_LINE("Found export for %s %s", rplName.c_str(), functionName.c_str()); //DEBUG_FUNCTION_LINE("Found export for %s %s", rplName.c_str(), functionName.c_str());
} }
if (!ElfUtils::elfLinkOne(cur.getType(), cur.getOffset(), cur.getAddend(), reinterpret_cast<uint32_t>(cur.getDestination()), functionAddress, trampData, RELOC_TYPE_IMPORT, trampolineID)) { if (!ElfUtils::elfLinkOne(cur.getType(), cur.getOffset(), cur.getAddend(), reinterpret_cast<uint32_t>(cur.getDestination()), functionAddress, trampData, RELOC_TYPE_IMPORT)) {
DEBUG_FUNCTION_LINE_ERR("elfLinkOne failed"); DEBUG_FUNCTION_LINE_ERR("elfLinkOne failed");
return false; return false;
} }
@ -140,13 +137,8 @@ bool PluginManagement::doRelocation(const std::vector<RelocationData> &relocData
} }
bool PluginManagement::doRelocations(const std::vector<PluginContainer> &plugins, bool PluginManagement::doRelocations(const std::vector<PluginContainer> &plugins,
std::vector<relocation_trampoline_entry_t> &trampData,
std::map<std::string, OSDynLoad_Module> &usedRPls) { std::map<std::string, OSDynLoad_Module> &usedRPls) {
for (auto &cur : trampData) {
if (cur.status == RELOC_TRAMP_IMPORT_DONE) {
cur.status = RELOC_TRAMP_FREE;
}
}
OSDynLoadAllocFn prevDynLoadAlloc = nullptr; OSDynLoadAllocFn prevDynLoadAlloc = nullptr;
OSDynLoadFreeFn prevDynLoadFree = nullptr; OSDynLoadFreeFn prevDynLoadFree = nullptr;
@ -158,10 +150,16 @@ bool PluginManagement::doRelocations(const std::vector<PluginContainer> &plugins
if (!pluginContainer.isLinkedAndLoaded()) { if (!pluginContainer.isLinkedAndLoaded()) {
continue; continue;
} }
const auto &trampData = pluginContainer.getPluginLinkInformation().getTrampData();
for (auto &cur : trampData) {
if (cur.status == RELOC_TRAMP_IMPORT_DONE) {
cur.status = RELOC_TRAMP_FREE;
}
}
DEBUG_FUNCTION_LINE_VERBOSE("Doing relocations for plugin: %s", pluginContainer.getMetaInformation().getName().c_str()); DEBUG_FUNCTION_LINE_VERBOSE("Doing relocations for plugin: %s", pluginContainer.getMetaInformation().getName().c_str());
if (!PluginManagement::doRelocation(pluginContainer.getPluginLinkInformation().getRelocationDataList(), if (!PluginManagement::doRelocation(pluginContainer.getPluginLinkInformation().getRelocationDataList(),
trampData, trampData,
pluginContainer.getPluginLinkInformation().getTrampolineId(),
usedRPls)) { usedRPls)) {
return false; return false;
} }

View File

@ -6,6 +6,7 @@
#include <functional> #include <functional>
#include <map> #include <map>
#include <span>
#include <string> #include <string>
class RelocationData; class RelocationData;
@ -15,18 +16,15 @@ class PluginContainer;
class PluginManagement { class PluginManagement {
public: public:
static std::vector<PluginContainer> loadPlugins( static std::vector<PluginContainer> loadPlugins(
const std::vector<PluginLoadWrapper> &pluginDataList, const std::vector<PluginLoadWrapper> &pluginDataList);
std::vector<relocation_trampoline_entry_t> &trampolineData);
static void callInitHooks(const std::vector<PluginContainer> &plugins, const std::function<bool(const PluginContainer &)> &pred); static void callInitHooks(const std::vector<PluginContainer> &plugins, const std::function<bool(const PluginContainer &)> &pred);
static bool doRelocations(const std::vector<PluginContainer> &plugins, static bool doRelocations(const std::vector<PluginContainer> &plugins,
std::vector<relocation_trampoline_entry_t> &trampData,
std::map<std::string, OSDynLoad_Module> &usedRPls); std::map<std::string, OSDynLoad_Module> &usedRPls);
static bool doRelocation(const std::vector<RelocationData> &relocData, static bool doRelocation(const std::vector<RelocationData> &relocData,
std::vector<relocation_trampoline_entry_t> &trampData, std::span<relocation_trampoline_entry_t> trampData,
uint32_t trampolineID,
std::map<std::string, OSDynLoad_Module> &usedRPls); std::map<std::string, OSDynLoad_Module> &usedRPls);
static bool DoFunctionPatches(std::vector<PluginContainer> &plugins); static bool DoFunctionPatches(std::vector<PluginContainer> &plugins);

View File

@ -10,12 +10,10 @@
#include "plugin/RelocationData.h" #include "plugin/RelocationData.h"
#include "plugin/SectionInfo.h" #include "plugin/SectionInfo.h"
StoredBuffer gStoredTVBuffer = {}; StoredBuffer gStoredTVBuffer = {};
StoredBuffer gStoredDRCBuffer = {}; StoredBuffer gStoredDRCBuffer = {};
std::vector<PluginContainer> gLoadedPlugins; std::vector<PluginContainer> gLoadedPlugins;
std::vector<relocation_trampoline_entry_t> gTrampData;
std::set<std::shared_ptr<PluginData>, PluginDataSharedPtrComparator> gLoadedData; std::set<std::shared_ptr<PluginData>, PluginDataSharedPtrComparator> gLoadedData;
std::vector<PluginLoadWrapper> gLoadOnNextLaunch; std::vector<PluginLoadWrapper> gLoadOnNextLaunch;

View File

@ -24,8 +24,6 @@ class PluginLoadWrapper;
extern StoredBuffer gStoredTVBuffer; extern StoredBuffer gStoredTVBuffer;
extern StoredBuffer gStoredDRCBuffer; extern StoredBuffer gStoredDRCBuffer;
#define TRAMP_DATA_SIZE 1024
extern std::vector<relocation_trampoline_entry_t> gTrampData;
extern std::vector<PluginContainer> gLoadedPlugins; extern std::vector<PluginContainer> gLoadedPlugins;
extern std::set<std::shared_ptr<PluginData>, PluginDataSharedPtrComparator> gLoadedData; extern std::set<std::shared_ptr<PluginData>, PluginDataSharedPtrComparator> gLoadedData;

View File

@ -180,13 +180,6 @@ WUMS_APPLICATION_STARTS() {
std::lock_guard lock(gLoadedDataMutex); std::lock_guard lock(gLoadedDataMutex);
if (gTrampData.empty()) {
gTrampData = std::vector<relocation_trampoline_entry_t>(TRAMP_DATA_SIZE);
for (auto &cur : gTrampData) {
cur.status = RELOC_TRAMP_FREE;
}
}
std::vector<PluginContainer> newLoadedPlugins; std::vector<PluginContainer> newLoadedPlugins;
if (gLoadedPlugins.empty()) { if (gLoadedPlugins.empty()) {
@ -197,7 +190,7 @@ WUMS_APPLICATION_STARTS() {
WUPSBackendSettings::LoadSettings(); WUPSBackendSettings::LoadSettings();
auto &inactiveList = WUPSBackendSettings::GetInactivePluginFilenames(); auto &inactiveList = WUPSBackendSettings::GetInactivePluginFilenames();
const auto pluginData = PluginDataFactory::loadDir(pluginPath, inactiveList); const auto pluginData = PluginDataFactory::loadDir(pluginPath, inactiveList);
newLoadedPlugins = PluginManagement::loadPlugins(pluginData, gTrampData); newLoadedPlugins = PluginManagement::loadPlugins(pluginData);
} }
if (!gLoadOnNextLaunch.empty()) { if (!gLoadOnNextLaunch.empty()) {
@ -230,7 +223,7 @@ WUMS_APPLICATION_STARTS() {
CleanupPlugins(std::move(pluginsToDeinit)); CleanupPlugins(std::move(pluginsToDeinit));
DEBUG_FUNCTION_LINE("Load new plugins"); DEBUG_FUNCTION_LINE("Load new plugins");
newLoadedPlugins = PluginManagement::loadPlugins(toBeLoaded, gTrampData); newLoadedPlugins = PluginManagement::loadPlugins(toBeLoaded);
} }
DEBUG_FUNCTION_LINE("Clear plugin data lists."); DEBUG_FUNCTION_LINE("Clear plugin data lists.");
@ -245,7 +238,7 @@ WUMS_APPLICATION_STARTS() {
// Move all new plugin containers into gLoadedPlugins // Move all new plugin containers into gLoadedPlugins
append_move_all_values(gLoadedPlugins, newLoadedPlugins); append_move_all_values(gLoadedPlugins, newLoadedPlugins);
if (!PluginManagement::doRelocations(gLoadedPlugins, gTrampData, gUsedRPLs)) { if (!PluginManagement::doRelocations(gLoadedPlugins, gUsedRPLs)) {
DEBUG_FUNCTION_LINE_ERR("Relocations failed"); DEBUG_FUNCTION_LINE_ERR("Relocations failed");
OSFatal("WiiUPluginLoaderBackend: Relocations failed.\n See crash logs for more information."); OSFatal("WiiUPluginLoaderBackend: Relocations failed.\n See crash logs for more information.");
} }
@ -305,18 +298,6 @@ void CleanupPlugins(std::vector<PluginContainer> &&pluginsToDeinit) {
} }
plugin.DeinitButtonComboData(); plugin.DeinitButtonComboData();
} }
for (const auto &pluginContainer : pluginsToDeinit) {
for (auto &cur : gTrampData) {
if (!pluginContainer.isLinkedAndLoaded() || cur.id != pluginContainer.getPluginLinkInformation().getTrampolineId()) {
continue;
}
cur = {};
}
}
DCFlushRange((void *) gTrampData.data(), gTrampData.size() * sizeof(relocation_trampoline_entry_t));
ICInvalidateRange((void *) gTrampData.data(), gTrampData.size() * sizeof(relocation_trampoline_entry_t));
OSMemoryBarrier();
} }
void CheckCleanupCallbackUsage(const std::vector<PluginContainer> &plugins) { void CheckCleanupCallbackUsage(const std::vector<PluginContainer> &plugins) {
auto *curThread = OSGetCurrentThread(); auto *curThread = OSGetCurrentThread();

View File

@ -4,31 +4,28 @@
#include "HookData.h" #include "HookData.h"
#include "RelocationData.h" #include "RelocationData.h"
#include "SectionInfo.h" #include "SectionInfo.h"
#include "utils/logger.h"
PluginLinkInformation::PluginLinkInformation(PluginLinkInformation &&src) : mHookDataList(std::move(src.mHookDataList)), PluginLinkInformation::PluginLinkInformation(PluginLinkInformation &&src) : mHookDataList(std::move(src.mHookDataList)),
mFunctionDataList(std::move(src.mFunctionDataList)), mFunctionDataList(std::move(src.mFunctionDataList)),
mRelocationDataList(std::move(src.mRelocationDataList)), mRelocationDataList(std::move(src.mRelocationDataList)),
mSymbolDataList(std::move(src.mSymbolDataList)), mSymbolDataList(std::move(src.mSymbolDataList)),
mSectionInfoList(std::move(src.mSectionInfoList)), mSectionInfoList(std::move(src.mSectionInfoList)),
mTrampolineId(src.mTrampolineId), mAllocatedTextAndTrampMemoryAddress(std::move(src.mAllocatedTextAndTrampMemoryAddress)),
mAllocatedTextMemoryAddress(std::move(src.mAllocatedTextMemoryAddress)),
mAllocatedDataMemoryAddress(std::move(src.mAllocatedDataMemoryAddress)) mAllocatedDataMemoryAddress(std::move(src.mAllocatedDataMemoryAddress))
{ {
src.mTrampolineId = {};
} }
PluginLinkInformation &PluginLinkInformation::operator=(PluginLinkInformation &&src) noexcept { PluginLinkInformation &PluginLinkInformation::operator=(PluginLinkInformation &&src) noexcept {
if (this != &src) { if (this != &src) {
this->mHookDataList = std::move(src.mHookDataList); this->mHookDataList = std::move(src.mHookDataList);
this->mFunctionDataList = std::move(src.mFunctionDataList); this->mFunctionDataList = std::move(src.mFunctionDataList);
this->mRelocationDataList = std::move(src.mRelocationDataList); this->mRelocationDataList = std::move(src.mRelocationDataList);
this->mSymbolDataList = std::move(src.mSymbolDataList); this->mSymbolDataList = std::move(src.mSymbolDataList);
this->mSectionInfoList = std::move(src.mSectionInfoList); this->mSectionInfoList = std::move(src.mSectionInfoList);
this->mTrampolineId = src.mTrampolineId; this->mAllocatedTextAndTrampMemoryAddress = std::move(src.mAllocatedTextAndTrampMemoryAddress);
this->mAllocatedTextMemoryAddress = std::move(src.mAllocatedTextMemoryAddress); this->mAllocatedDataMemoryAddress = std::move(src.mAllocatedDataMemoryAddress);
this->mAllocatedDataMemoryAddress = std::move(src.mAllocatedDataMemoryAddress);
src.mTrampolineId = {};
} }
return *this; return *this;
} }
@ -80,15 +77,6 @@ std::optional<SectionInfo> PluginLinkInformation::getSectionInfo(const std::stri
return std::nullopt; return std::nullopt;
} }
void PluginLinkInformation::setTrampolineId(const uint8_t trampolineId) {
this->mTrampolineId = trampolineId;
}
uint8_t PluginLinkInformation::getTrampolineId() const {
return mTrampolineId;
}
const FunctionSymbolData *PluginLinkInformation::getNearestFunctionSymbolData(uint32_t address) const { const FunctionSymbolData *PluginLinkInformation::getNearestFunctionSymbolData(uint32_t address) const {
const FunctionSymbolData *result = nullptr; const FunctionSymbolData *result = nullptr;
@ -109,12 +97,12 @@ const FunctionSymbolData *PluginLinkInformation::getNearestFunctionSymbolData(ui
return result; return result;
} }
const HeapMemoryFixedSize &PluginLinkInformation::getTextMemory() const { HeapMemoryFixedSizePool::MemorySegmentInfo PluginLinkInformation::getTextMemory() const {
return mAllocatedTextMemoryAddress; return mAllocatedTextAndTrampMemoryAddress[0]; // 0 is .text data
} }
const HeapMemoryFixedSize &PluginLinkInformation::getDataMemory() const { HeapMemoryFixedSizePool::MemorySegmentInfo PluginLinkInformation::getDataMemory() const {
return mAllocatedDataMemoryAddress; return mAllocatedDataMemoryAddress[0]; // 0 is .data data
} }
PluginLinkInformation PluginLinkInformation::CreateStub() { PluginLinkInformation PluginLinkInformation::CreateStub() {
@ -122,5 +110,13 @@ PluginLinkInformation PluginLinkInformation::CreateStub() {
} }
bool PluginLinkInformation::hasValidData() const { bool PluginLinkInformation::hasValidData() const {
return mAllocatedDataMemoryAddress.size() > 0 && mAllocatedTextMemoryAddress.size() > 0; return mAllocatedDataMemoryAddress && mAllocatedTextAndTrampMemoryAddress && mAllocatedTextAndTrampMemoryAddress.numberOfSegments() == 2;
}
std::span<relocation_trampoline_entry_t> PluginLinkInformation::getTrampData() const {
if (mAllocatedTextAndTrampMemoryAddress && mAllocatedTextAndTrampMemoryAddress.numberOfSegments() < 2) {
DEBUG_FUNCTION_LINE_ERR("Failed to return trampoline data. Memory is either not valid or has not enough segments");
return {};
}
const auto &entry = mAllocatedTextAndTrampMemoryAddress[1]; // 1 is tramp data
return std::span(static_cast<relocation_trampoline_entry_t *>(entry.data()), entry.size() / sizeof(relocation_trampoline_entry_t));
} }

View File

@ -20,12 +20,14 @@
#include "FunctionSymbolData.h" #include "FunctionSymbolData.h"
#include "utils/HeapMemoryFixedSize.h" #include "utils/HeapMemoryFixedSize.h"
#include <iosfwd>
#include <map> #include <map>
#include <optional> #include <optional>
#include <set> #include <set>
#include <vector> #include <vector>
#include <wums/defines/relocation_defines.h>
class HeapMemoryFixedSize; class HeapMemoryFixedSizePool;
class SectionInfo; class SectionInfo;
class RelocationData; class RelocationData;
class FunctionData; class FunctionData;
@ -60,16 +62,18 @@ public:
[[nodiscard]] std::optional<SectionInfo> getSectionInfo(const std::string &sectionName) const; [[nodiscard]] std::optional<SectionInfo> getSectionInfo(const std::string &sectionName) const;
[[nodiscard]] uint8_t getTrampolineId() const;
[[nodiscard]] const FunctionSymbolData *getNearestFunctionSymbolData(uint32_t address) const; [[nodiscard]] const FunctionSymbolData *getNearestFunctionSymbolData(uint32_t address) const;
[[nodiscard]] const HeapMemoryFixedSize &getTextMemory() const; [[nodiscard]] HeapMemoryFixedSizePool::MemorySegmentInfo getTextMemory() const;
[[nodiscard]] const HeapMemoryFixedSize &getDataMemory() const; [[nodiscard]] HeapMemoryFixedSizePool::MemorySegmentInfo getDataMemory() const;
[[nodiscard]] bool hasValidData() const; [[nodiscard]] bool hasValidData() const;
[[nodiscard]] int numberOfSegments() const;
[[nodiscard]] std::span<relocation_trampoline_entry_t> getTrampData() const;
private: private:
PluginLinkInformation() = default; PluginLinkInformation() = default;
@ -83,18 +87,14 @@ private:
void addSectionInfo(const SectionInfo &sectionInfo); void addSectionInfo(const SectionInfo &sectionInfo);
void setTrampolineId(uint8_t trampolineId);
std::vector<HookData> mHookDataList; std::vector<HookData> mHookDataList;
std::vector<FunctionData> mFunctionDataList; std::vector<FunctionData> mFunctionDataList;
std::vector<RelocationData> mRelocationDataList; std::vector<RelocationData> mRelocationDataList;
std::set<FunctionSymbolData, FunctionSymbolDataComparator> mSymbolDataList; std::set<FunctionSymbolData, FunctionSymbolDataComparator> mSymbolDataList;
std::map<std::string, SectionInfo> mSectionInfoList; std::map<std::string, SectionInfo> mSectionInfoList;
uint8_t mTrampolineId = 0; HeapMemoryFixedSizePool mAllocatedTextAndTrampMemoryAddress;
HeapMemoryFixedSizePool mAllocatedDataMemoryAddress;
HeapMemoryFixedSize mAllocatedTextMemoryAddress;
HeapMemoryFixedSize mAllocatedDataMemoryAddress;
friend class PluginLinkInformationFactory; friend class PluginLinkInformationFactory;
}; };

View File

@ -35,8 +35,70 @@
using namespace ELFIO; using namespace ELFIO;
namespace {
bool predictRequiredTrampolineCount(const elfio &reader, uint32_t &outTrampCount) {
Elf_Word symbol = 0;
Elf64_Addr offset;
Elf_Word type;
Elf_Sxword addend;
std::string sym_name;
Elf64_Addr sym_value;
Elf_Xword size;
unsigned char bind;
unsigned char symbolType;
Elf_Half sym_section_index;
unsigned char other;
for (const auto &parentSec : reader.sections) {
if ((parentSec->get_type() == SHT_PROGBITS || parentSec->get_type() == SHT_NOBITS) && (parentSec->get_flags() & SHF_ALLOC)) {
for (const auto &psec : reader.sections) {
if (psec->get_info() == parentSec->get_info()) {
const relocation_section_accessor rel(reader, psec.get());
for (uint32_t j = 0; j < static_cast<uint32_t>(rel.get_entries_num()); ++j) {
if (!rel.get_entry(j, offset, symbol, type, addend)) {
DEBUG_FUNCTION_LINE_ERR("Failed to get relocation");
return false;
}
const symbol_section_accessor symbols(reader, reader.sections[static_cast<Elf_Half>(psec->get_link())]);
if (!symbols.get_symbol(symbol, sym_name, sym_value, size,
bind, symbolType, sym_section_index, other)) {
DEBUG_FUNCTION_LINE_ERR("Failed to get symbol");
return false;
}
if (sym_section_index == SHN_ABS) {
//
} else if (sym_section_index > SHN_LORESERVE) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED: %04X", sym_section_index);
return false;
}
if (type == R_PPC_REL24) {
const auto target = static_cast<int32_t>(offset);
const auto value = static_cast<int32_t>(sym_value + addend);
const auto newDistance = value - target;
// The tramps are placed right after the .text section. We can cover 0x1FFFFFC
constexpr auto maxJumpDistance = 0x1FFFFFC;
// Subtract 0x10000 because it could be at the end of our tramp section.
constexpr auto maxUsedJumpDistance = maxJumpDistance - 0x10000;
if (newDistance > maxUsedJumpDistance || newDistance < -maxUsedJumpDistance) {
outTrampCount++;
}
}
}
return true;
}
}
}
}
return false;
}
} // namespace
std::optional<PluginLinkInformation> std::optional<PluginLinkInformation>
PluginLinkInformationFactory::load(const PluginData &pluginData, std::vector<relocation_trampoline_entry_t> &trampolineData, uint8_t trampolineId) { PluginLinkInformationFactory::load(const PluginData &pluginData) {
auto buffer = pluginData.getBuffer(); auto buffer = pluginData.getBuffer();
if (buffer.empty()) { if (buffer.empty()) {
DEBUG_FUNCTION_LINE_ERR("Buffer was empty"); DEBUG_FUNCTION_LINE_ERR("Buffer was empty");
@ -85,20 +147,41 @@ PluginLinkInformationFactory::load(const PluginData &pluginData, std::vector<rel
} }
} }
HeapMemoryFixedSize text_data(text_size); uint32_t expectedTramps = 0;
if (!text_data) { if (!predictRequiredTrampolineCount(reader, expectedTramps)) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory for the .text section (%d bytes)", text_size); DEBUG_FUNCTION_LINE_WARN("Failed to predict required trampoline count");
return std::nullopt;
} }
HeapMemoryFixedSize data_data(data_size); // Add 20 tramp slots by default to any plugin to be safe.
if (!data_data) { // This alone should already be more than enough from my current observations
expectedTramps += 20;
size_t trampDataSize = (expectedTramps) * sizeof(relocation_trampoline_entry_t);
// We have to have the .text section and trampolines as close as possible in memory
// Lets create a shared memory pool for both of them
HeapMemoryFixedSizePool textAndTrampDataPool({text_size, trampDataSize});
if (!textAndTrampDataPool) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory for the .text section (%d bytes) and tramp data", text_size, trampDataSize);
return std::nullopt;
}
// Segment 0 is the .text data
const auto &text_data = textAndTrampDataPool[0];
// Segment 1 the tramp data
const auto &tramp_data = textAndTrampDataPool[1];
memset(tramp_data.data(), 0, trampDataSize);
std::span trampolineList(static_cast<relocation_trampoline_entry_t *>(tramp_data.data()), expectedTramps);
// For .data create a separate memory pool with just one try.
HeapMemoryFixedSizePool dataHeapPool({data_size});
if (!dataHeapPool) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory for the .data section (%d bytes)", data_size); DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory for the .data section (%d bytes)", data_size);
return std::nullopt; return std::nullopt;
} }
const auto &data_data = dataHeapPool[0];
for (uint32_t i = 0; i < sec_num; ++i) { for (const auto &psec : reader.sections) {
section *psec = reader.sections[i];
if (psec->get_type() == 0x80000002 || psec->get_name() == ".wut_load_bounds") { if (psec->get_type() == 0x80000002 || psec->get_name() == ".wut_load_bounds") {
continue; continue;
} }
@ -165,12 +248,13 @@ PluginLinkInformationFactory::load(const PluginData &pluginData, std::vector<rel
} }
} }
for (uint32_t i = 0; i < sec_num; ++i) { for (uint32_t i = 0; i < sec_num; ++i) {
section *psec = reader.sections[i]; section *psec = reader.sections[i];
if ((psec->get_type() == SHT_PROGBITS || psec->get_type() == SHT_NOBITS) && (psec->get_flags() & SHF_ALLOC)) { if (psec && (psec->get_type() == SHT_PROGBITS || psec->get_type() == SHT_NOBITS) && (psec->get_flags() & SHF_ALLOC)) {
DEBUG_FUNCTION_LINE_VERBOSE("Linking (%d)... %s at %08X", i, psec->get_name().c_str(), destinations[psec->get_index()]); DEBUG_FUNCTION_LINE_VERBOSE("Linking (%d)... %s at %08X", i, psec->get_name().c_str(), destinations[psec->get_index()]);
if (!linkSection(reader, psec->get_index(), (uint32_t) destinations[psec->get_index()], (uint32_t) text_data.data(), (uint32_t) data_data.data(), trampolineData,
trampolineId)) { if (!linkSection(reader, psec->get_index(), (uint32_t) destinations[psec->get_index()], (uint32_t) text_data.data(), (uint32_t) data_data.data(), trampolineList)) {
DEBUG_FUNCTION_LINE_ERR("linkSection failed"); DEBUG_FUNCTION_LINE_ERR("linkSection failed");
return std::nullopt; return std::nullopt;
} }
@ -187,8 +271,6 @@ PluginLinkInformationFactory::load(const PluginData &pluginData, std::vector<rel
DCFlushRange((void *) data_data.data(), data_data.size()); DCFlushRange((void *) data_data.data(), data_data.size());
ICInvalidateRange((void *) data_data.data(), data_data.size()); ICInvalidateRange((void *) data_data.data(), data_data.size());
pluginInfo.setTrampolineId(trampolineId);
auto secInfo = pluginInfo.getSectionInfo(".wups.hooks"); auto secInfo = pluginInfo.getSectionInfo(".wups.hooks");
if (secInfo && secInfo->getSize() > 0) { if (secInfo && secInfo->getSize() > 0) {
size_t entries_count = secInfo->getSize() / sizeof(wups_loader_hook_t); size_t entries_count = secInfo->getSize() / sizeof(wups_loader_hook_t);
@ -225,11 +307,9 @@ PluginLinkInformationFactory::load(const PluginData &pluginData, std::vector<rel
} }
// Get the symbol for functions. // Get the symbol for functions.
Elf_Half n = reader.sections.size(); for (const auto &sec : reader.sections) {
for (Elf_Half i = 0; i < n; ++i) {
section *sec = reader.sections[i];
if (SHT_SYMTAB == sec->get_type()) { if (SHT_SYMTAB == sec->get_type()) {
symbol_section_accessor symbols(reader, sec); symbol_section_accessor symbols(reader, sec.get());
auto sym_no = (uint32_t) symbols.get_symbols_num(); auto sym_no = (uint32_t) symbols.get_symbols_num();
if (sym_no > 0) { if (sym_no > 0) {
for (Elf_Half j = 0; j < sym_no; ++j) { for (Elf_Half j = 0; j < sym_no; ++j) {
@ -267,8 +347,8 @@ PluginLinkInformationFactory::load(const PluginData &pluginData, std::vector<rel
// Save the addresses for the allocated memory. This way we can free it again :) // Save the addresses for the allocated memory. This way we can free it again :)
pluginInfo.mAllocatedDataMemoryAddress = std::move(data_data); pluginInfo.mAllocatedDataMemoryAddress = std::move(dataHeapPool);
pluginInfo.mAllocatedTextMemoryAddress = std::move(text_data); pluginInfo.mAllocatedTextAndTrampMemoryAddress = std::move(textAndTrampDataPool);
return pluginInfo; return pluginInfo;
} }
@ -289,11 +369,10 @@ bool PluginLinkInformationFactory::addImportRelocationData(PluginLinkInformation
} }
} }
for (uint32_t i = 0; i < sec_num; ++i) { for (const auto &psec : reader.sections) {
section *psec = reader.sections[i];
if (psec->get_type() == SHT_RELA || psec->get_type() == SHT_REL) { if (psec->get_type() == SHT_RELA || psec->get_type() == SHT_REL) {
DEBUG_FUNCTION_LINE_VERBOSE("Found relocation section %s", psec->get_name().c_str()); DEBUG_FUNCTION_LINE_VERBOSE("Found relocation section %s", psec->get_name().c_str());
relocation_section_accessor rel(reader, psec); relocation_section_accessor rel(reader, psec.get());
for (uint32_t j = 0; j < (uint32_t) rel.get_entries_num(); ++j) { for (uint32_t j = 0; j < (uint32_t) rel.get_entries_num(); ++j) {
Elf_Word symbol = 0; Elf_Word symbol = 0;
Elf64_Addr offset; Elf64_Addr offset;
@ -345,14 +424,11 @@ bool PluginLinkInformationFactory::addImportRelocationData(PluginLinkInformation
} }
bool PluginLinkInformationFactory::linkSection(const elfio &reader, uint32_t section_index, uint32_t destination, uint32_t base_text, uint32_t base_data, bool PluginLinkInformationFactory::linkSection(const elfio &reader, uint32_t section_index, uint32_t destination, uint32_t base_text, uint32_t base_data,
std::vector<relocation_trampoline_entry_t> &trampolineData, uint8_t trampolineId) { std::span<relocation_trampoline_entry_t> trampolineData) {
uint32_t sec_num = reader.sections.size(); for (const auto &psec : reader.sections) {
for (uint32_t i = 0; i < sec_num; ++i) {
section *psec = reader.sections[i];
if (psec->get_info() == section_index) { if (psec->get_info() == section_index) {
DEBUG_FUNCTION_LINE_VERBOSE("Found relocation section %s", psec->get_name().c_str()); DEBUG_FUNCTION_LINE_VERBOSE("Found relocation section %s", psec->get_name().c_str());
relocation_section_accessor rel(reader, psec); relocation_section_accessor rel(reader, psec.get());
for (uint32_t j = 0; j < (uint32_t) rel.get_entries_num(); ++j) { for (uint32_t j = 0; j < (uint32_t) rel.get_entries_num(); ++j) {
Elf_Word symbol = 0; Elf_Word symbol = 0;
Elf64_Addr offset; Elf64_Addr offset;
@ -415,11 +491,12 @@ bool PluginLinkInformationFactory::linkSection(const elfio &reader, uint32_t sec
} }
// DEBUG_FUNCTION_LINE_VERBOSE("sym_value %08X adjusted_sym_value %08X offset %08X adjusted_offset %08X", (uint32_t) sym_value, adjusted_sym_value, (uint32_t) offset, adjusted_offset); // DEBUG_FUNCTION_LINE_VERBOSE("sym_value %08X adjusted_sym_value %08X offset %08X adjusted_offset %08X", (uint32_t) sym_value, adjusted_sym_value, (uint32_t) offset, adjusted_offset);
if (!ElfUtils::elfLinkOne(type, adjusted_offset, addend, destination, adjusted_sym_value, trampolineData, RELOC_TYPE_FIXED, trampolineId)) { if (!ElfUtils::elfLinkOne(type, adjusted_offset, addend, destination, adjusted_sym_value, trampolineData, RELOC_TYPE_FIXED)) {
DEBUG_FUNCTION_LINE_ERR("Link failed"); DEBUG_FUNCTION_LINE_ERR("Link failed");
return false; return false;
} }
} }
DEBUG_FUNCTION_LINE_VERBOSE("done"); DEBUG_FUNCTION_LINE_VERBOSE("done");
return true; return true;
} }

View File

@ -30,12 +30,12 @@ class PluginLinkInformation;
class PluginLinkInformationFactory { class PluginLinkInformationFactory {
public: public:
static std::optional<PluginLinkInformation> static std::optional<PluginLinkInformation>
load(const PluginData &pluginData, std::vector<relocation_trampoline_entry_t> &trampolineData, uint8_t trampolineId); load(const PluginData &pluginData);
private: private:
static bool static bool
linkSection(const ELFIO::elfio &reader, uint32_t section_index, uint32_t destination, uint32_t base_text, uint32_t base_data, linkSection(const ELFIO::elfio &reader, uint32_t section_index, uint32_t destination, uint32_t base_text, uint32_t base_data,
std::vector<relocation_trampoline_entry_t> &trampolineData, uint8_t trampolineId); std::span<relocation_trampoline_entry_t> trampolineData);
static bool static bool
addImportRelocationData(PluginLinkInformation &pluginInfo, const ELFIO::elfio &reader, std::span<uint8_t *> destinations); addImportRelocationData(PluginLinkInformation &pluginInfo, const ELFIO::elfio &reader, std::span<uint8_t *> destinations);

View File

@ -1,14 +1,16 @@
#include <coreinit/cache.h>
#include <cstdlib>
#include <cstring>
#include <vector>
#include "ElfUtils.h" #include "ElfUtils.h"
#include "utils/logger.h" #include "utils/logger.h"
#include <coreinit/cache.h>
#include <span>
#include <cstdlib>
#include <cstring>
// See https://github.com/decaf-emu/decaf-emu/blob/43366a34e7b55ab9d19b2444aeb0ccd46ac77dea/src/libdecaf/src/cafe/loader/cafe_loader_reloc.cpp#L144 // See https://github.com/decaf-emu/decaf-emu/blob/43366a34e7b55ab9d19b2444aeb0ccd46ac77dea/src/libdecaf/src/cafe/loader/cafe_loader_reloc.cpp#L144
bool ElfUtils::elfLinkOne(char type, size_t offset, int32_t addend, uint32_t destination, uint32_t symbol_addr, bool ElfUtils::elfLinkOne(char type, size_t offset, int32_t addend, uint32_t destination, uint32_t symbol_addr,
std::vector<relocation_trampoline_entry_t> &trampolineData, RelocationType reloc_type, uint8_t trampolineId) { std::span<relocation_trampoline_entry_t> trampolineData, RelocationType reloc_type) {
if (type == R_PPC_NONE) { if (type == R_PPC_NONE) {
return true; return true;
} }
@ -85,11 +87,10 @@ bool ElfUtils::elfLinkOne(char type, size_t offset, int32_t addend, uint32_t des
relocation_trampoline_entry_t *freeSlot = nullptr; relocation_trampoline_entry_t *freeSlot = nullptr;
for (auto &cur : trampolineData) { for (auto &cur : trampolineData) {
// We want to override "old" relocations of imports // We want to override "old" relocations of imports
// Pending relocations have the status RELOC_TRAMP_IMPORT_IN_PROGRESS.
// When all relocations are done successfully, they will be turned into RELOC_TRAMP_IMPORT_DONE // When all relocations are done successfully, they will be turned into RELOC_TRAMP_IMPORT_DONE
// so they can be overridden/updated/reused on the next application launch. // so they can be overridden/updated/reused on the next application launch.
// //
// Relocations that won't change will have the status RELOC_TRAMP_FIXED and are set to free when the module is unloaded. // Relocations that won't change will have the status RELOC_TRAMP_FIXED and are set to free when the plugin is unloaded.
if (cur.status == RELOC_TRAMP_FREE) { if (cur.status == RELOC_TRAMP_FREE) {
freeSlot = &cur; freeSlot = &cur;
break; break;
@ -119,9 +120,6 @@ bool ElfUtils::elfLinkOne(char type, size_t offset, int32_t addend, uint32_t des
freeSlot->trampoline[3] = 0x4E800420; // bctr freeSlot->trampoline[3] = 0x4E800420; // bctr
ICInvalidateRange((unsigned char *) freeSlot->trampoline, sizeof(freeSlot->trampoline)); ICInvalidateRange((unsigned char *) freeSlot->trampoline, sizeof(freeSlot->trampoline));
freeSlot->id = trampolineId;
ICInvalidateRange((unsigned char *) &freeSlot->id, sizeof(freeSlot->id));
if (reloc_type == RELOC_TYPE_FIXED) { if (reloc_type == RELOC_TYPE_FIXED) {
freeSlot->status = RELOC_TRAMP_FIXED; freeSlot->status = RELOC_TRAMP_FIXED;
} else { } else {

View File

@ -2,6 +2,8 @@
#include <wums/defines/relocation_defines.h> #include <wums/defines/relocation_defines.h>
#include <span>
#include <cstdint> #include <cstdint>
#ifdef __cplusplus #ifdef __cplusplus
@ -47,6 +49,6 @@ uint32_t load_loader_elf(unsigned char *baseAddress, char *elf_data, uint32_t fi
class ElfUtils { class ElfUtils {
public: public:
static bool elfLinkOne(char type, size_t offset, int32_t addend, uint32_t destination, uint32_t symbol_addr, std::vector<relocation_trampoline_entry_t> &trampolineData, static bool elfLinkOne(char type, size_t offset, int32_t addend, uint32_t destination, uint32_t symbol_addr, std::span<relocation_trampoline_entry_t> trampolineData,
RelocationType reloc_type, uint8_t trampolineId); RelocationType reloc_type);
}; };

View File

@ -1,33 +1,69 @@
#include "HeapMemoryFixedSize.h" #include "HeapMemoryFixedSize.h"
#include "logger.h"
#include "utils.h" #include "utils.h"
HeapMemoryFixedSize::HeapMemoryFixedSize() = default; HeapMemoryFixedSizePool::MemorySegmentInfo::MemorySegmentInfo(void *data, const size_t size) : mData(data),
mSize(size) {}
HeapMemoryFixedSize::HeapMemoryFixedSize(const std::size_t size) : mData(make_unique_nothrow<uint8_t[]>(size)), mSize(mData ? size : 0) {} void *HeapMemoryFixedSizePool::MemorySegmentInfo::data() const {
return mData;
HeapMemoryFixedSize::HeapMemoryFixedSize(HeapMemoryFixedSize &&other) noexcept
: mData(std::move(other.mData)), mSize(other.mSize) {
other.mSize = 0;
} }
HeapMemoryFixedSize &HeapMemoryFixedSize::operator=(HeapMemoryFixedSize &&other) noexcept { size_t HeapMemoryFixedSizePool::MemorySegmentInfo::size() const {
return mSize;
}
HeapMemoryFixedSizePool::HeapMemoryFixedSizePool() = default;
HeapMemoryFixedSizePool::HeapMemoryFixedSizePool(const std::initializer_list<size_t> segmentSizes) : HeapMemoryFixedSizePool(std::span(segmentSizes.begin(), segmentSizes.size())) {}
HeapMemoryFixedSizePool::HeapMemoryFixedSizePool(std::span<const std::size_t> segmentSizes) {
assert(!segmentSizes.empty());
size_t totalSize = 0;
for (const auto size : segmentSizes) {
totalSize += size + 0x40; // add 0x40 bytes overhead for each entry to ensure padding to 0x40
}
mData = make_unique_nothrow<uint8_t[]>(totalSize);
mTotalSize = (mData ? totalSize : 0);
if (mData) {
auto address = reinterpret_cast<uint32_t>(mData.get());
for (const auto size : segmentSizes) {
address = ROUNDUP(address, 0x40);
assert(address >= reinterpret_cast<uint32_t>(mData.get()) && address < reinterpret_cast<uint32_t>(mData.get()) + totalSize);
mSegmentInfos.emplace_back(reinterpret_cast<void *>(address), size);
address += size;
}
}
}
HeapMemoryFixedSizePool::HeapMemoryFixedSizePool(HeapMemoryFixedSizePool &&other) noexcept
: mData(std::move(other.mData)), mTotalSize(other.mTotalSize), mSegmentInfos(std::move(other.mSegmentInfos)) {
other.mTotalSize = 0;
}
HeapMemoryFixedSizePool &HeapMemoryFixedSizePool::operator=(HeapMemoryFixedSizePool &&other) noexcept {
if (this != &other) { if (this != &other) {
mData = std::move(other.mData); mData = std::move(other.mData);
mSize = other.mSize; mTotalSize = other.mTotalSize;
other.mSize = 0; mSegmentInfos = std::move(other.mSegmentInfos);
other.mTotalSize = 0;
} }
return *this; return *this;
} }
HeapMemoryFixedSize::operator bool() const { uint32_t HeapMemoryFixedSizePool::numberOfSegments() const {
return mData != nullptr; return mSegmentInfos.size();
} }
[[nodiscard]] const void *HeapMemoryFixedSize::data() const { HeapMemoryFixedSizePool::operator bool() const {
return mData.get(); return mData != nullptr && mTotalSize > 0;
} }
[[nodiscard]] std::size_t HeapMemoryFixedSize::size() const { HeapMemoryFixedSizePool::MemorySegmentInfo HeapMemoryFixedSizePool::operator[](const int idx) const {
return mSize; if (idx < 0 || idx >= static_cast<int>(mSegmentInfos.size())) {
DEBUG_FUNCTION_LINE_ERR("Out of bounce access (tried to access index %d; size is", idx, mSegmentInfos.size());
}
return mSegmentInfos[idx];
} }

View File

@ -1,30 +1,45 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <span>
#include <vector>
#include <cstdint> #include <cstdint>
class HeapMemoryFixedSize { class HeapMemoryFixedSizePool {
public: public:
HeapMemoryFixedSize(); class MemorySegmentInfo {
public:
MemorySegmentInfo(void *data, size_t size);
explicit HeapMemoryFixedSize(std::size_t size); [[nodiscard]] void *data() const;
[[nodiscard]] size_t size() const;
private:
void *mData;
size_t mSize;
};
HeapMemoryFixedSizePool();
HeapMemoryFixedSizePool(std::initializer_list<size_t> segmentSizes);
HeapMemoryFixedSizePool(std::span<const std::size_t> segmentSizes);
// Delete the copy constructor and copy assignment operator // Delete the copy constructor and copy assignment operator
HeapMemoryFixedSize(const HeapMemoryFixedSize &) = delete; HeapMemoryFixedSizePool(const HeapMemoryFixedSizePool &) = delete;
HeapMemoryFixedSize &operator=(const HeapMemoryFixedSize &) = delete; HeapMemoryFixedSizePool &operator=(const HeapMemoryFixedSizePool &) = delete;
HeapMemoryFixedSize(HeapMemoryFixedSize &&other) noexcept; HeapMemoryFixedSizePool(HeapMemoryFixedSizePool &&other) noexcept;
HeapMemoryFixedSize &operator=(HeapMemoryFixedSize &&other) noexcept; HeapMemoryFixedSizePool &operator=(HeapMemoryFixedSizePool &&other) noexcept;
[[nodiscard]] uint32_t numberOfSegments() const;
explicit operator bool() const; explicit operator bool() const;
[[nodiscard]] const void *data() const; MemorySegmentInfo operator[](int idx) const;
[[nodiscard]] std::size_t size() const;
private: private:
std::unique_ptr<uint8_t[]> mData{}; std::unique_ptr<uint8_t[]> mData{};
std::size_t mSize{}; std::size_t mTotalSize{};
std::vector<MemorySegmentInfo> mSegmentInfos;
}; };