Compare commits

...

23 Commits

Author SHA1 Message Date
Maschell
ed5bdca3b9 Init GX2 to reduce screen corruption if no menu was shown 2024-07-06 15:34:43 +02:00
Maschell
4ad1400f9e Bump version 2024-07-06 10:52:59 +02:00
Maschell
fd803278f2 Update Dockerfile 2024-07-06 10:52:59 +02:00
Maschell
4d9e4f73c9 Limit the number of VPADRead attempts to avoid potential softlocks 2024-07-06 10:52:59 +02:00
Maschell
be78693cb7 Close the quick start menu on the gamepad if not autobooting into an environment 2024-07-06 10:52:59 +02:00
Maschell
f81ecf0178 Avoid screen corruption after displaying the environment selection screen 2024-07-06 10:52:59 +02:00
Maschell
95d0ce1385 Make rpx loading more robust and respect section alignment 2024-05-08 17:57:43 +02:00
dependabot[bot]
d9c743febd Bump wiiu-env/devkitppc from 20240423 to 20240505
Bumps wiiu-env/devkitppc from 20240423 to 20240505.

---
updated-dependencies:
- dependency-name: wiiu-env/devkitppc
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 10:20:31 +02:00
Maschell
95014a2123 Remove debug logs 2024-04-26 10:39:56 +02:00
Maschell
bdf9e079c8 Bump softprops/action-gh-release from 1 to 2 2024-04-26 10:39:56 +02:00
Maschell
7332dcaa1d Bump version to 0.3.0 2024-04-24 17:57:00 +02:00
dependabot[bot]
44d07c0e78 Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-24 17:57:00 +02:00
Maschell
e1d48361b6 Update Dockerfile to use latest devkitPPC and wut version 2024-04-24 17:57:00 +02:00
Maschell
5536bc79ee Fix compiling with debug flag 2024-04-24 17:57:00 +02:00
Maschell
b5f62f552d Make sure to init moduleInfoPtr 2024-04-24 17:57:00 +02:00
Maschell
35b8c0eeb7 Allow Aroma to init early 2024-04-24 17:57:00 +02:00
Maschell
2054d97e62 Avoid spamming the logs 2024-04-24 17:57:00 +02:00
Maschell
bd8992bc11 Update .gitignore to ignore .zip files 2024-04-24 17:57:00 +02:00
Maschell
e4d2dfa5aa Create dependabot.yml 2023-07-23 10:31:39 +02:00
Maschell
83cf3060c8 Bump version 2023-07-20 10:31:19 +02:00
Maschell
f60e8a340d Make input reading more reliable 2023-07-20 10:31:19 +02:00
Maschell
10337d5fba Display current version of EnvironmentLoader in the menu 2023-07-20 10:31:19 +02:00
Maschell
7dbd371d5a Update Dockerfile 2023-07-20 09:06:41 +02:00
17 changed files with 656 additions and 202 deletions

10
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

View File

@ -9,7 +9,7 @@ jobs:
clang-format: clang-format:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: clang-format - name: clang-format
run: | run: |
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source --exclude ./source/elfio docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source --exclude ./source/elfio
@ -17,7 +17,14 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: clang-format needs: clang-format
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: create version.h
run: |
git_hash=$(git rev-parse --short "$GITHUB_SHA")
cat <<EOF > ./source/version.h
#pragma once
#define ENVIRONMENT_LOADER_VERSION_EXTRA " (nightly-$git_hash)"
EOF
- name: build binary - name: build binary
run: | run: |
docker build . -t builder docker build . -t builder
@ -42,7 +49,7 @@ jobs:
- name: zip artifact - name: zip artifact
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip wiiu run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip wiiu
- name: Create Release - name: Create Release
uses: "softprops/action-gh-release@v1" uses: "softprops/action-gh-release@v2"
with: with:
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }} tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
draft: false draft: false

View File

@ -6,7 +6,7 @@ jobs:
clang-format: clang-format:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: clang-format - name: clang-format
run: | run: |
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source --exclude ./source/elfio docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source --exclude ./source/elfio
@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: clang-format needs: clang-format
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: build binary with logging - name: build binary with logging
run: | run: |
docker build . -t builder docker build . -t builder
@ -25,7 +25,14 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: clang-format needs: clang-format
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: create version.h
run: |
git_hash=$(git rev-parse --short "${{ github.event.pull_request.head.sha }}")
cat <<EOF > ./source/version.h
#pragma once
#define ENVIRONMENT_LOADER_VERSION_EXTRA " (nightly-$git_hash)"
EOF
- name: build binary - name: build binary
run: | run: |
docker build . -t builder docker build . -t builder

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ cmake-build-debug/
*.txt *.txt
build1/ build1/
cmake-build-default/ cmake-build-default/
*.zip

View File

@ -1,3 +1,3 @@
FROM ghcr.io/wiiu-env/devkitppc:20230616 FROM ghcr.io/wiiu-env/devkitppc:20240704
WORKDIR project WORKDIR project

View File

@ -7,15 +7,15 @@
#include "ElfUtils.h" #include "ElfUtils.h"
#include "elfio/elfio.hpp" #include "elfio/elfio.hpp"
bool ElfUtils::doRelocation(const std::vector<std::unique_ptr<RelocationData>> &relocData, relocation_trampoline_entry_t *tramp_data, uint32_t tramp_length, std::map<std::string, OSDynLoad_Module> &usedRPls) { bool ElfUtils::doRelocation(const std::vector<RelocationData> &relocData, relocation_trampoline_entry_t *tramp_data, uint32_t tramp_length, std::map<std::string, OSDynLoad_Module> &usedRPls) {
for (auto const &curReloc : relocData) { for (auto const &curReloc : relocData) {
std::string functionName = curReloc->getName(); std::string functionName = curReloc.getName();
std::string rplName = curReloc->getImportRPLInformation()->getRPLName(); std::string rplName = curReloc.getImportRPLInformation()->getRPLName();
int32_t isData = curReloc->getImportRPLInformation()->isData(); int32_t isData = curReloc.getImportRPLInformation()->isData();
OSDynLoad_Module rplHandle = nullptr; OSDynLoad_Module rplHandle = nullptr;
if (!usedRPls.contains(rplName)) { if (!usedRPls.contains(rplName)) {
DEBUG_FUNCTION_LINE_VERBOSE("Acquire %s", rplName.c_str()); //DEBUG_FUNCTION_LINE_VERBOSE("Acquire %s", rplName.c_str());
// Always acquire to increase refcount and make sure it won't get unloaded while we're using it. // Always acquire to increase refcount and make sure it won't get unloaded while we're using it.
OSDynLoad_Error err = OSDynLoad_Acquire(rplName.c_str(), &rplHandle); OSDynLoad_Error err = OSDynLoad_Acquire(rplName.c_str(), &rplHandle);
if (err != OS_DYNLOAD_OK) { if (err != OS_DYNLOAD_OK) {
@ -26,7 +26,7 @@ bool ElfUtils::doRelocation(const std::vector<std::unique_ptr<RelocationData>> &
// They will be released on exit (See: AromaBaseModule) // They will be released on exit (See: AromaBaseModule)
usedRPls[rplName] = rplHandle; usedRPls[rplName] = rplHandle;
} else { } else {
DEBUG_FUNCTION_LINE_VERBOSE("Use from usedRPLs cache! %s", rplName.c_str()); //DEBUG_FUNCTION_LINE_VERBOSE("Use from usedRPLs cache! %s", rplName.c_str());
} }
rplHandle = usedRPls[rplName]; rplHandle = usedRPls[rplName];
@ -35,7 +35,7 @@ bool ElfUtils::doRelocation(const std::vector<std::unique_ptr<RelocationData>> &
DEBUG_FUNCTION_LINE_ERR("Failed to find export for %s %s %d", functionName.c_str(), rplName.c_str(), isData); DEBUG_FUNCTION_LINE_ERR("Failed to find export for %s %s %d", functionName.c_str(), rplName.c_str(), isData);
return false; return false;
} }
if (!ElfUtils::elfLinkOne(curReloc->getType(), curReloc->getOffset(), curReloc->getAddend(), (uint32_t) curReloc->getDestination(), functionAddress, tramp_data, tramp_length, if (!ElfUtils::elfLinkOne(curReloc.getType(), curReloc.getOffset(), curReloc.getAddend(), (uint32_t) curReloc.getDestination(), functionAddress, tramp_data, tramp_length,
RELOC_TYPE_IMPORT)) { RELOC_TYPE_IMPORT)) {
DEBUG_FUNCTION_LINE_ERR("Relocation failed\n"); DEBUG_FUNCTION_LINE_ERR("Relocation failed\n");
return false; return false;
@ -159,7 +159,6 @@ bool ElfUtils::elfLinkOne(char type, size_t offset, int32_t addend, uint32_t des
freeSlot->trampoline[1] = 0x616B0000 | (((uint32_t) value) & 0x0000ffff); // ori r11, r11, real_addr@l freeSlot->trampoline[1] = 0x616B0000 | (((uint32_t) value) & 0x0000ffff); // ori r11, r11, real_addr@l
freeSlot->trampoline[2] = 0x7D6903A6; // mtctr r11 freeSlot->trampoline[2] = 0x7D6903A6; // mtctr r11
freeSlot->trampoline[3] = 0x4E800420; // bctr freeSlot->trampoline[3] = 0x4E800420; // bctr
DCFlushRange((void *) freeSlot->trampoline, sizeof(freeSlot->trampoline));
ICInvalidateRange((unsigned char *) freeSlot->trampoline, sizeof(freeSlot->trampoline)); ICInvalidateRange((unsigned char *) freeSlot->trampoline, sizeof(freeSlot->trampoline));
if (reloc_type == RELOC_TYPE_FIXED) { if (reloc_type == RELOC_TYPE_FIXED) {

View File

@ -51,6 +51,5 @@ public:
static bool elfLinkOne(char type, size_t offset, int32_t addend, uint32_t destination, uint32_t symbol_addr, relocation_trampoline_entry_t *trampolin_data, uint32_t trampolin_data_length, static bool elfLinkOne(char type, size_t offset, int32_t addend, uint32_t destination, uint32_t symbol_addr, relocation_trampoline_entry_t *trampolin_data, uint32_t trampolin_data_length,
RelocationType reloc_type); RelocationType reloc_type);
static bool doRelocation(const std::vector<RelocationData> &relocData, relocation_trampoline_entry_t *tramp_data, uint32_t tramp_length, std::map<std::string, OSDynLoad_Module> &usedRPls);
static bool doRelocation(const std::vector<std::unique_ptr<RelocationData>> &relocData, relocation_trampoline_entry_t *tramp_data, uint32_t tramp_length, std::map<std::string, OSDynLoad_Module> &usedRPls);
}; };

View File

@ -7,14 +7,17 @@
#include <coreinit/filesystem_fsa.h> #include <coreinit/filesystem_fsa.h>
#include <coreinit/foreground.h> #include <coreinit/foreground.h>
#include <coreinit/ios.h> #include <coreinit/ios.h>
#include <coreinit/savedframe.h>
#include <coreinit/screen.h> #include <coreinit/screen.h>
#include <coreinit/title.h> #include <coreinit/title.h>
#include <elfio/elfio.hpp> #include <elfio/elfio.hpp>
#include <fcntl.h> #include <fcntl.h>
#include <gx2/display.h>
#include <gx2/state.h> #include <gx2/state.h>
#include <malloc.h> #include <malloc.h>
#include <memory> #include <memory>
#include <nn/act/client_cpp.h> #include <nn/act/client_cpp.h>
#include <nsysccr/cdc.h>
#include <proc_ui/procui.h> #include <proc_ui/procui.h>
#include <sysapp/launch.h> #include <sysapp/launch.h>
#include <sysapp/title.h> #include <sysapp/title.h>
@ -30,16 +33,18 @@
#include "kernel.h" #include "kernel.h"
#include "module/ModuleDataFactory.h" #include "module/ModuleDataFactory.h"
#include "utils/DrawUtils.h" #include "utils/DrawUtils.h"
#include "utils/FileUtils.h"
#include "utils/InputUtils.h" #include "utils/InputUtils.h"
#include "utils/OnLeavingScope.h"
#include "utils/PairUtils.h" #include "utils/PairUtils.h"
#include "utils/utils.h"
#include "utils/wiiu_zlib.hpp"
#include "version.h"
// clang-format off #define ENVIRONMENT_LOADER_VERSION "v0.3.2"
#define MEMORY_REGION_START 0x00A00000
#define MEMORY_REGION_SIZE 0x00600000
#define MEMORY_REGION_END (MEMORY_REGION_START + MEMORY_REGION_SIZE)
#define AUTOBOOT_CONFIG_PATH "fs:/vol/external01/wiiu/environments/default.cfg" #define MEMORY_REGION_START 0x00800000
// clang-format on #define AUTOBOOT_CONFIG_PATH "fs:/vol/external01/wiiu/environments/default.cfg"
bool CheckRunning() { bool CheckRunning() {
switch (ProcUIProcessMessages(true)) { switch (ProcUIProcessMessages(true)) {
@ -92,6 +97,8 @@ bool writeFileContent(const std::string &path, const std::string &content) {
extern "C" void __fini(); extern "C" void __fini();
extern "C" void __init_wut_malloc(); extern "C" void __init_wut_malloc();
void LoadAndRunModule(std::string_view filepath, std::string_view environment_path);
void ClearSavedFrameBuffers();
int main(int argc, char **argv) { int main(int argc, char **argv) {
// We need to call __init_wut_malloc somewhere so wut_malloc will be used for the memory allocation. // We need to call __init_wut_malloc somewhere so wut_malloc will be used for the memory allocation.
@ -110,28 +117,22 @@ int main(int argc, char **argv) {
DEBUG_FUNCTION_LINE("Hello from EnvironmentLoader!"); DEBUG_FUNCTION_LINE("Hello from EnvironmentLoader!");
char environmentPath[0x100]; char environmentPathFromIOSU[0x100] = {};
memset(environmentPath, 0, sizeof(environmentPath)); auto handle = IOS_Open("/dev/mcp", IOS_OPEN_READ);
auto handle = IOS_Open("/dev/mcp", IOS_OPEN_READ);
if (handle >= 0) { if (handle >= 0) {
int in = 0xF9; // IPC_CUSTOM_COPY_ENVIRONMENT_PATH int in = 0xF9; // IPC_CUSTOM_COPY_ENVIRONMENT_PATH
if (IOS_Ioctl(handle, 100, &in, sizeof(in), environmentPath, sizeof(environmentPath)) == IOS_ERROR_OK) { if (IOS_Ioctl(handle, 100, &in, sizeof(in), environmentPathFromIOSU, sizeof(environmentPathFromIOSU)) == IOS_ERROR_OK) {
DEBUG_FUNCTION_LINE("Boot into %s", environmentPath); DEBUG_FUNCTION_LINE("Boot into %s", environmentPathFromIOSU);
} }
IOS_Close(handle); IOS_Close(handle);
} }
// We substract 0x100 to be safe.
uint32_t textSectionStart = textStart() - 0x100;
auto gModuleData = (module_information_t *) (textSectionStart - sizeof(module_information_t));
bool noEnvironmentsFound = false; bool noEnvironmentsFound = false;
bool shownMenu = false;
std::string environment_path = std::string(environmentPath); std::string environmentPath = std::string(environmentPathFromIOSU);
if (strncmp(environmentPath, "fs:/vol/external01/wiiu/environments/", strlen("fs:/vol/external01/wiiu/environments/")) != 0) { if (!environmentPath.starts_with("fs:/vol/external01/wiiu/environments/")) { // If the environment path in IOSU is empty or unexpected, read config
DirList environmentDirs("fs:/vol/external01/wiiu/environments/", nullptr, DirList::Dirs, 1); DirList environmentDirs("fs:/vol/external01/wiiu/environments/", nullptr, DirList::Dirs, 1);
std::map<std::string, std::string> environmentPaths; std::map<std::string, std::string> environmentPaths;
@ -148,9 +149,9 @@ int main(int argc, char **argv) {
for (auto const &[key, val] : environmentPaths) { for (auto const &[key, val] : environmentPaths) {
if (res.value() == key) { if (res.value() == key) {
DEBUG_FUNCTION_LINE("Found environment %s from config at index %d", res.value().c_str(), i); DEBUG_FUNCTION_LINE("Found environment %s from config at index %d", res.value().c_str(), i);
autobootIndex = i; autobootIndex = i;
environment_path = val; environmentPath = val;
forceMenu = false; forceMenu = false;
break; break;
} }
i++; i++;
@ -164,23 +165,38 @@ int main(int argc, char **argv) {
InputUtils::InputData input = InputUtils::getControllerInput(); InputUtils::InputData input = InputUtils::getControllerInput();
if (forceMenu || ((input.trigger | input.hold) & VPAD_BUTTON_X) == VPAD_BUTTON_X) { if (forceMenu || ((input.trigger | input.hold) & VPAD_BUTTON_X) == VPAD_BUTTON_X) {
shownMenu = true;
DEBUG_FUNCTION_LINE_VERBOSE("Open menu!"); DEBUG_FUNCTION_LINE_VERBOSE("Open menu!");
environment_path = EnvironmentSelectionScreen(environmentPaths, autobootIndex); environmentPath = EnvironmentSelectionScreen(environmentPaths, autobootIndex);
if (environmentPaths.empty()) { if (environmentPaths.empty()) {
noEnvironmentsFound = true; noEnvironmentsFound = true;
} else { } else {
DEBUG_FUNCTION_LINE_VERBOSE("Selected %s", environment_path.c_str()); DEBUG_FUNCTION_LINE_VERBOSE("Selected %s", environmentPath.c_str());
} }
} else {
} }
InputUtils::DeInit(); InputUtils::DeInit();
} }
if (!shownMenu) {
// Clear saved frame buffer to reduce screen corruption
ClearSavedFrameBuffers();
OSScreenInit();
// Call GX2Init to shut down OSScreen
GX2Init(nullptr);
GX2SetTVEnable(FALSE);
GX2SetDRCEnable(FALSE);
}
RevertMainHook(); RevertMainHook();
if (!noEnvironmentsFound) { if (!noEnvironmentsFound) {
DirList setupModules(environment_path + "/modules/setup", ".rpx", DirList::Files, 1); DirList setupModules(environmentPath + "/modules/setup", ".rpx", DirList::Files, 1);
setupModules.SortList(); setupModules.SortList();
std::map<std::string, OSDynLoad_Module> usedRPls;
for (int i = 0; i < setupModules.GetFilecount(); i++) { for (int i = 0; i < setupModules.GetFilecount(); i++) {
//! skip hidden linux and mac files //! skip hidden linux and mac files
if (setupModules.GetFilename(i)[0] == '.' || setupModules.GetFilename(i)[0] == '_') { if (setupModules.GetFilename(i)[0] == '.' || setupModules.GetFilename(i)[0] == '_') {
@ -188,48 +204,7 @@ int main(int argc, char **argv) {
continue; continue;
} }
// Some module may unmount the sd card on exit. LoadAndRunModule(setupModules.GetFilepath(i), environmentPath);
FSAInit();
auto client = FSAAddClient(nullptr);
if (client) {
FSAMount(client, "/dev/sdcard01", "/vol/external01", static_cast<FSAMountFlags>(0), nullptr, 0);
FSADelClient(client);
}
uint32_t destination_address_end = ((uint32_t) gModuleData) & 0xFFFF0000;
memset((void *) gModuleData, 0, sizeof(module_information_t));
DEBUG_FUNCTION_LINE("Trying to run %s.", setupModules.GetFilepath(i), destination_address_end, ((uint32_t) gModuleData) - MEMORY_REGION_START);
auto moduleData = ModuleDataFactory::load(setupModules.GetFilepath(i), destination_address_end, ((uint32_t) gModuleData) - MEMORY_REGION_START, gModuleData->trampolines,
DYN_LINK_TRAMPOLIN_LIST_LENGTH);
if (!moduleData) {
DEBUG_FUNCTION_LINE_ERR("Failed to load %s", setupModules.GetFilepath(i));
OSFatal("EnvironmentLoader: Failed to load module");
continue;
}
DEBUG_FUNCTION_LINE("Loaded module data");
if (!ElfUtils::doRelocation(moduleData.value()->getRelocationDataList(), gModuleData->trampolines, DYN_LINK_TRAMPOLIN_LIST_LENGTH, usedRPls)) {
DEBUG_FUNCTION_LINE_ERR("Relocations failed");
OSFatal("EnvironmentLoader: Relocations failed");
} else {
DEBUG_FUNCTION_LINE("Relocation done");
}
DCFlushRange((void *) moduleData.value()->getStartAddress(), moduleData.value()->getEndAddress() - moduleData.value()->getStartAddress());
ICInvalidateRange((void *) moduleData.value()->getStartAddress(), moduleData.value()->getEndAddress() - moduleData.value()->getStartAddress());
DEBUG_FUNCTION_LINE("Calling entrypoint @%08X", moduleData.value()->getEntrypoint());
char *arr[1];
arr[0] = (char *) environment_path.c_str();
// clang-format off
((int(*)(int, char **)) moduleData.value()->getEntrypoint())(1, arr);
// clang-format on
DEBUG_FUNCTION_LINE("Back from module");
for (auto &rpl : usedRPls) {
DEBUG_FUNCTION_LINE_VERBOSE("Release %s", rpl.first.c_str());
OSDynLoad_Release(rpl.second);
}
usedRPls.clear();
} }
} else { } else {
@ -264,7 +239,239 @@ int main(int argc, char **argv) {
return 0; return 0;
} }
std::optional<HeapWrapper> GetHeapFromMappedMemory(uint32_t heapSize) {
void *(*MEMAllocFromDefaultHeapExForThreads)(uint32_t size, int align) = nullptr;
void (*MEMFreeToDefaultHeapForThreads)(void *ptr) = nullptr;
// Let's try to get the memalign and free functions from the memorymapping module.
OSDynLoad_Module module;
if (OSDynLoad_Acquire("homebrew_memorymapping", &module) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("Failed to acquire homebrew_memorymapping.");
return {};
}
/* Memory allocation functions */
uint32_t *allocPtr = nullptr, *freePtr = nullptr;
if (OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemoryEx", reinterpret_cast<void **>(&allocPtr)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("OSDynLoad_FindExport for MEMAllocFromDefaultHeapEx failed");
return {};
}
if (OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_DATA, "MEMFreeToMappedMemory", reinterpret_cast<void **>(&freePtr)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("OSDynLoad_FindExport for MEMFreeToDefaultHeap failed");
return {};
}
MEMAllocFromDefaultHeapExForThreads = (void *(*) (uint32_t, int) ) * allocPtr;
MEMFreeToDefaultHeapForThreads = (void (*)(void *)) * freePtr;
if (!MEMAllocFromDefaultHeapExForThreads || !MEMFreeToDefaultHeapForThreads) {
DEBUG_FUNCTION_LINE_ERR("MEMAllocFromDefaultHeapExForThreads or MEMFreeToDefaultHeapForThreads is null");
// the mapped memory is not available (yet)
return {};
}
uint32_t size = heapSize;
auto ptr = MEMAllocFromDefaultHeapExForThreads(size, 0x4);
if (!ptr) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory: %d bytes", size);
return {};
}
DEBUG_FUNCTION_LINE("Let's create a memory wrapper for 0x%08X, size: %d", ptr, size);
return HeapWrapper(MemoryWrapper(ptr, size, MEMFreeToDefaultHeapForThreads));
}
std::optional<HeapWrapper> GetHeapForModule(uint32_t heapSize) {
// If Aroma is already loaded, we can't use the region between MEMORY_REGION_START and MEMORY_REGION_END anymore because Aroma is using.
// So instead we check before loading a module if aromas memory mapping module is already usable. If yes, we use this to load the module instead
if (auto heapWrapper = GetHeapFromMappedMemory(heapSize)) {
return heapWrapper;
}
// If Aroma is not already loaded, we use the existing 0x00800000 - 0x01000000 memory region. This is where aroma is loaded to. Note: this region may be only mapped to the main core.
// The environment loader is loaded to the end of 0x00800000 - 0x01000000 memory region. With this helper we know the start of the .text section
uint32_t textSectionStart = textStart() - 0x100;
auto endOfUsableMemory = textSectionStart;
uint32_t startAddress = ((uint32_t) endOfUsableMemory - heapSize) & 0xFFFF0000;
uint32_t size = endOfUsableMemory - startAddress;
if (startAddress < MEMORY_REGION_START) {
DEBUG_FUNCTION_LINE_ERR("Not enough static memory");
return {};
}
DEBUG_FUNCTION_LINE("Let's create a memory wrapper for 0x%08X, size: %d", startAddress, size);
auto res = HeapWrapper(MemoryWrapper((void *) startAddress, size, /* we don't need to free this memory*/ nullptr));
if ((uint32_t) res.GetHeapHandle() != startAddress) {
OSFatal("EnvironmentLoader: Unexpected address");
}
return res;
}
void SetupKernelModule() {
void *(*KernelSetupDefaultSyscalls)() = nullptr;
OSDynLoad_Module module;
if (OSDynLoad_Acquire("homebrew_kernel", &module) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("Failed to acquire homebrew_kernel.");
return;
}
if (OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_FUNC, "KernelSetupDefaultSyscalls", reinterpret_cast<void **>(&KernelSetupDefaultSyscalls)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("OSDynLoad_FindExport for KernelSetupDefaultSyscalls failed");
OSFatal("EnvironmentLoader: KernelModule is missing the export\n"
"\"KernelSetupDefaultSyscalls\"... Please update Aroma!\n"
"\n"
"See https://wiiu.hacks.guide/ for more information.");
return;
}
if (!KernelSetupDefaultSyscalls) {
DEBUG_FUNCTION_LINE_WARN("KernelSetupDefaultSyscalls is null");
OSFatal("EnvironmentLoader: KernelModule is missing the export\n"
"\"KernelSetupDefaultSyscalls\"... Please update Aroma!\n"
"\n"
"See https://wiiu.hacks.guide/ for more information.");
return;
}
DEBUG_FUNCTION_LINE("Call KernelSetupDefaultSyscalls");
KernelSetupDefaultSyscalls();
OSDynLoad_Release(module);
}
void LoadAndRunModule(std::string_view filepath, std::string_view environment_path) {
// Some module may unmount the sd card on exit.
FSAInit();
auto client = FSAAddClient(nullptr);
if (client) {
FSAMount(client, "/dev/sdcard01", "/vol/external01", static_cast<FSAMountFlags>(0), nullptr, 0);
FSADelClient(client);
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to add FSA client");
}
DEBUG_FUNCTION_LINE("Trying to load %s into memory", filepath.data());
uint8_t *buffer = nullptr;
uint32_t fsize = 0;
if (LoadFileToMem(filepath.data(), &buffer, &fsize) < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to load file");
OSFatal("EnvironmentLoader: Failed to load file to memory");
return;
}
auto cleanupBuffer = onLeavingScope([buffer]() { free(buffer); });
ELFIO::elfio reader(new wiiu_zlib);
// Load ELF data
if (!reader.load(reinterpret_cast<const char *>(buffer), fsize)) {
DEBUG_FUNCTION_LINE_ERR("Can't parse .wms from buffer.");
OSFatal("Can't parse .wms from buffer.");
return;
}
uint32_t moduleSize = ModuleDataFactory::GetSizeOfModule(reader);
DEBUG_FUNCTION_LINE_VERBOSE("Module has size: %d", moduleSize);
uint32_t requiredHeapSize = moduleSize + sizeof(module_information_t) + 0x10000; // add another 0x10000 to be safe
DEBUG_FUNCTION_LINE_VERBOSE("Allocate %d bytes for heap (%.2f KiB)", requiredHeapSize, requiredHeapSize / 1024.0f);
if (auto heapWrapperOpt = GetHeapForModule(requiredHeapSize); heapWrapperOpt.has_value()) {
// Frees automatically, must not survive the heapWrapper.
auto moduleInfoOpt = heapWrapperOpt->Alloc(sizeof(module_information_t), 0x4);
if (!moduleInfoOpt) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc module information");
OSFatal("EnvironmentLoader: Failed to alloc module information");
return;
}
auto moduleInfo = std::move(*moduleInfoOpt);
auto moduleInfoPtr = (module_information_t *) moduleInfo.data();
*moduleInfoPtr = {};
// Frees automatically, must not survive the heapWrapper.
auto moduleData = ModuleDataFactory::load(reader, *heapWrapperOpt, moduleInfoPtr->trampolines, sizeof(moduleInfoPtr->trampolines) / sizeof(moduleInfoPtr->trampolines[0]));
if (!moduleData) {
DEBUG_FUNCTION_LINE_ERR("Failed to load %s", filepath);
OSFatal("EnvironmentLoader: Failed to load module");
return;
}
DEBUG_FUNCTION_LINE("Loaded module data");
std::map<std::string, OSDynLoad_Module> usedRPls;
if (!ElfUtils::doRelocation(moduleData.value()->getRelocationDataList(), moduleInfoPtr->trampolines, sizeof(moduleInfoPtr->trampolines) / sizeof(moduleInfoPtr->trampolines[0]), usedRPls)) {
DEBUG_FUNCTION_LINE_ERR("Relocations failed");
OSFatal("EnvironmentLoader: Relocations failed");
} else {
DEBUG_FUNCTION_LINE("Relocation done");
}
char *arr[4];
arr[0] = (char *) environment_path.data();
arr[1] = (char *) "EnvironmentLoader"; //
arr[2] = (char *) 0x02; // Version
/*
* This is a hacky work around to tell Aromas Module Loader which memory region it can use safely. After using it, it's expected to expose new memory region via the
* custom rpl "homebrew_mappedmemory" (See: GetHeapFromMappedMemory). The returned memory is expected to be RWX for user and kernel.
* Once a custom memory allocator is provided, usable_mem_start and usable_mem_end are set to 0.
*/
auto usable_mem_end = (uint32_t) heapWrapperOpt->GetHeapHandle();
if (heapWrapperOpt->IsAllocated()) { // Check if you use memory which is actually allocated. This means we can't give it to the module.
DEBUG_FUNCTION_LINE("Don't give the module a usable memory region because it will be loaded on a custom memory region.");
usable_mem_end = 0;
}
arr[3] = (char *) usable_mem_end; // End of usable memory
DEBUG_FUNCTION_LINE("Calling entrypoint @%08X with: \"%s\", \"%s\", %08X, %08X", moduleData.value()->getEntrypoint(), arr[0], arr[1], arr[2], arr[3]);
// clang-format off
((int(*)(int, char **)) moduleData.value()->getEntrypoint())(sizeof(arr)/ sizeof(arr[0]), arr);
// clang-format on
DEBUG_FUNCTION_LINE("Back from module");
for (auto &rpl : usedRPls) {
DEBUG_FUNCTION_LINE_VERBOSE("Release %s", rpl.first.c_str());
OSDynLoad_Release(rpl.second);
}
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to create heap");
OSFatal("EnvironmentLoader: Failed to create heap");
}
// module may override the syscalls used by the Aroma KernelModule. This (tries to) re-init(s) the KernelModule after a setup module has been run.
SetupKernelModule();
}
void ClearSavedFrameBuffers() {
// If GX2 is running make sure to shut it down and free all existing memory in the saved-frame area.
if (GX2GetMainCoreId() != -1) {
GX2SetTVEnable(FALSE);
GX2SetDRCEnable(FALSE);
GX2Shutdown();
}
__OSClearSavedFrame(OS_SAVED_FRAME_A, OS_SAVED_FRAME_SCREEN_TV);
__OSClearSavedFrame(OS_SAVED_FRAME_A, OS_SAVED_FRAME_SCREEN_DRC);
__OSClearSavedFrame(OS_SAVED_FRAME_B, OS_SAVED_FRAME_SCREEN_TV);
__OSClearSavedFrame(OS_SAVED_FRAME_B, OS_SAVED_FRAME_SCREEN_DRC);
}
void AbortQuickStartMenu() {
CCRCDCDrcState state = {};
CCRCDCSysGetDrcState(CCR_CDC_DESTINATION_DRC0, &state);
if (state.state == CCR_CDC_DRC_STATE_SUBACTIVE) {
state.state = CCR_CDC_DRC_STATE_ACTIVE;
CCRCDCSysSetDrcState(CCR_CDC_DESTINATION_DRC0, &state);
}
}
std::string EnvironmentSelectionScreen(const std::map<std::string, std::string> &payloads, int32_t autobootIndex) { std::string EnvironmentSelectionScreen(const std::map<std::string, std::string> &payloads, int32_t autobootIndex) {
// Close quick start menu is selection screen is displayed
AbortQuickStartMenu();
// Clear saved frame buffer to reduce screen corruption
ClearSavedFrameBuffers();
OSScreenInit(); OSScreenInit();
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV); uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
@ -350,6 +557,8 @@ std::string EnvironmentSelectionScreen(const std::map<std::string, std::string>
DrawUtils::setFontSize(24); DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, "Environment Loader"); DrawUtils::print(16, 6 + 24, "Environment Loader");
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE); DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
DrawUtils::setFontSize(16);
DrawUtils::print(SCREEN_WIDTH - 16, 6 + 24, ENVIRONMENT_LOADER_VERSION ENVIRONMENT_LOADER_VERSION_EXTRA, true);
// draw bottom bar // draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE); DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
@ -376,6 +585,9 @@ std::string EnvironmentSelectionScreen(const std::map<std::string, std::string>
// Call GX2Init to shut down OSScreen // Call GX2Init to shut down OSScreen
GX2Init(nullptr); GX2Init(nullptr);
GX2SetTVEnable(FALSE);
GX2SetDRCEnable(FALSE);
free(screenBuffer); free(screenBuffer);
if (autoBoot != autobootIndex) { if (autoBoot != autobootIndex) {

View File

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "RelocationData.h" #include "RelocationData.h"
#include "utils/MemoryUtils.h"
#include <map> #include <map>
#include <set> #include <set>
#include <string> #include <string>
@ -33,11 +34,11 @@ public:
this->entrypoint = address; this->entrypoint = address;
} }
void addRelocationData(std::unique_ptr<RelocationData> relocation_data) { void addRelocationData(RelocationData relocation_data) {
relocation_data_list.push_back(std::move(relocation_data)); relocation_data_list.push_back(std::move(relocation_data));
} }
[[nodiscard]] const std::vector<std::unique_ptr<RelocationData>> &getRelocationDataList() const { [[nodiscard]] const std::vector<RelocationData> &getRelocationDataList() const {
return relocation_data_list; return relocation_data_list;
} }
@ -45,25 +46,16 @@ public:
return entrypoint; return entrypoint;
} }
void setStartAddress(uint32_t address) { void setTextMemory(ExpHeapMemory &&memory) {
this->startAddress = address; mTextMemory = std::move(memory);
} }
void setDataMemory(ExpHeapMemory &&memory) {
void setEndAddress(uint32_t address) { mDataMemory = std::move(memory);
this->endAddress = address;
}
[[nodiscard]] uint32_t getStartAddress() const {
return startAddress;
}
[[nodiscard]] uint32_t getEndAddress() const {
return endAddress;
} }
private: private:
std::vector<std::unique_ptr<RelocationData>> relocation_data_list; std::vector<RelocationData> relocation_data_list;
uint32_t entrypoint = 0; uint32_t entrypoint = 0;
uint32_t startAddress = 0; ExpHeapMemory mTextMemory;
uint32_t endAddress = 0; ExpHeapMemory mDataMemory;
}; };

View File

@ -24,33 +24,32 @@
#include <coreinit/cache.h> #include <coreinit/cache.h>
#include <map> #include <map>
#include <string> #include <string>
#include <vector>
uint32_t ModuleDataFactory::GetSizeOfModule(const ELFIO::elfio &reader) {
uint32_t sec_num = reader.sections.size();
uint32_t sizeOfModule = 0;
for (uint32_t i = 0; i < sec_num; ++i) {
ELFIO::section *psec = reader.sections[i];
if (psec->get_type() == 0x80000002 || psec->get_name() == ".wut_load_bounds") {
continue;
}
if ((psec->get_type() == ELFIO::SHT_PROGBITS || psec->get_type() == ELFIO::SHT_NOBITS) && (psec->get_flags() & ELFIO::SHF_ALLOC)) {
sizeOfModule += psec->get_size() + psec->get_addr_align();
}
}
return sizeOfModule;
}
std::optional<std::unique_ptr<ModuleData>> std::optional<std::unique_ptr<ModuleData>>
ModuleDataFactory::load(const std::string &path, uint32_t destination_address_end, uint32_t maximum_size, relocation_trampoline_entry_t *trampoline_data, uint32_t trampoline_data_length) { ModuleDataFactory::load(const ELFIO::elfio &reader, const HeapWrapper &heapWrapper, relocation_trampoline_entry_t *trampoline_data, uint32_t trampoline_data_length) {
ELFIO::elfio reader(new wiiu_zlib);
auto moduleData = make_unique_nothrow<ModuleData>(); auto moduleData = make_unique_nothrow<ModuleData>();
if (!moduleData) { if (!moduleData) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate ModuleData"); DEBUG_FUNCTION_LINE_ERR("Failed to allocate ModuleData");
return {}; return {};
} }
uint8_t *buffer = nullptr;
uint32_t fsize = 0;
if (LoadFileToMem(path.c_str(), &buffer, &fsize) < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to load file");
return {};
}
// Load ELF data
if (!reader.load(reinterpret_cast<char *>(buffer), fsize)) {
DEBUG_FUNCTION_LINE_ERR("Can't find or process %s", path.c_str());
free(buffer);
return {};
}
auto cleanupBuffer = onLeavingScope([buffer]() { free(buffer); });
uint32_t sec_num = reader.sections.size(); uint32_t sec_num = reader.sections.size();
auto destinations = make_unique_nothrow<uint8_t *[]>(sec_num); auto destinations = make_unique_nothrow<uint8_t *[]>(sec_num);
@ -59,7 +58,9 @@ ModuleDataFactory::load(const std::string &path, uint32_t destination_address_en
return {}; return {};
} }
uint32_t sizeOfModule = 0; uint32_t text_size = 0;
uint32_t data_size = 0;
for (uint32_t i = 0; i < sec_num; ++i) { for (uint32_t i = 0; i < sec_num; ++i) {
ELFIO::section *psec = reader.sections[i]; ELFIO::section *psec = reader.sections[i];
if (psec->get_type() == 0x80000002) { if (psec->get_type() == 0x80000002) {
@ -67,25 +68,31 @@ ModuleDataFactory::load(const std::string &path, uint32_t destination_address_en
} }
if ((psec->get_type() == ELFIO::SHT_PROGBITS || psec->get_type() == ELFIO::SHT_NOBITS) && (psec->get_flags() & ELFIO::SHF_ALLOC)) { if ((psec->get_type() == ELFIO::SHT_PROGBITS || psec->get_type() == ELFIO::SHT_NOBITS) && (psec->get_flags() & ELFIO::SHF_ALLOC)) {
sizeOfModule += psec->get_size() + 1; uint32_t sectionSize = psec->get_size();
auto address = (uint32_t) psec->get_address();
if ((address >= 0x02000000) && address < 0x10000000) {
text_size += sectionSize + psec->get_addr_align();
} else if ((address >= 0x10000000) && address < 0xC0000000) {
data_size += sectionSize + psec->get_addr_align();
}
} }
} }
if (sizeOfModule > maximum_size) { auto text_dataOpt = heapWrapper.Alloc(text_size, 0x100);
DEBUG_FUNCTION_LINE_ERR("Module is too big."); if (!text_dataOpt) {
return {}; DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory for the .text section (%d bytes)", text_size);
return std::nullopt;
} }
ExpHeapMemory text_data = std::move(*text_dataOpt);
uint32_t baseOffset = (destination_address_end - sizeOfModule) & 0xFFFFFF00; auto data_dataOpt = heapWrapper.Alloc(data_size, 0x100);
uint32_t startAddress = baseOffset; if (!data_dataOpt) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory for the .data section (%d bytes)", data_size);
return std::nullopt;
}
ExpHeapMemory data_data = std::move(*data_dataOpt);
uint32_t offset_text = baseOffset; uint32_t entrypoint = (uint32_t) text_data.data() + (uint32_t) reader.get_entry() - 0x02000000;
uint32_t offset_data = offset_text;
uint32_t entrypoint = offset_text + (uint32_t) reader.get_entry() - 0x02000000;
uint32_t totalSize = 0;
uint32_t endAddress = 0;
for (uint32_t i = 0; i < sec_num; ++i) { for (uint32_t i = 0; i < sec_num; ++i) {
ELFIO::section *psec = reader.sections[i]; ELFIO::section *psec = reader.sections[i];
@ -95,26 +102,34 @@ ModuleDataFactory::load(const std::string &path, uint32_t destination_address_en
if ((psec->get_type() == ELFIO::SHT_PROGBITS || psec->get_type() == ELFIO::SHT_NOBITS) && (psec->get_flags() & ELFIO::SHF_ALLOC)) { if ((psec->get_type() == ELFIO::SHT_PROGBITS || psec->get_type() == ELFIO::SHT_NOBITS) && (psec->get_flags() & ELFIO::SHF_ALLOC)) {
uint32_t sectionSize = psec->get_size(); uint32_t sectionSize = psec->get_size();
auto address = (uint32_t) psec->get_address();
totalSize += sectionSize; uint32_t destination = address;
if (totalSize > maximum_size) {
DEBUG_FUNCTION_LINE_ERR("Couldn't load setup module because it's too big.");
return {};
}
auto address = (uint32_t) psec->get_address();
destinations[psec->get_index()] = (uint8_t *) baseOffset;
uint32_t destination = baseOffset + address;
if ((address >= 0x02000000) && address < 0x10000000) { if ((address >= 0x02000000) && address < 0x10000000) {
destination += (uint32_t) text_data.data();
destination -= 0x02000000; destination -= 0x02000000;
destinations[psec->get_index()] -= 0x02000000; destinations[psec->get_index()] = (uint8_t *) text_data.data();
baseOffset += sectionSize;
offset_data += sectionSize; if (destination + sectionSize > (uint32_t) text_data.data() + text_size) {
DEBUG_FUNCTION_LINE_ERR("Tried to overflow .text buffer. %08X > %08X", destination + sectionSize, (uint32_t) text_data.data() + text_data.size());
OSFatal("EnvironmentLoader: Tried to overflow .text buffer");
} else if (destination < (uint32_t) text_data.data()) {
DEBUG_FUNCTION_LINE_ERR("Tried to underflow .text buffer. %08X < %08X", destination, (uint32_t) text_data.data());
OSFatal("EnvironmentLoader: Tried to underflow .text buffer");
}
} else if ((address >= 0x10000000) && address < 0xC0000000) { } else if ((address >= 0x10000000) && address < 0xC0000000) {
destination += (uint32_t) data_data.data();
destination -= 0x10000000; destination -= 0x10000000;
destinations[psec->get_index()] -= 0x10000000; destinations[psec->get_index()] = (uint8_t *) data_data.data();
if (destination + sectionSize > (uint32_t) data_data.data() + data_data.size()) {
DEBUG_FUNCTION_LINE_ERR("Tried to overflow .data buffer. %08X > %08X", destination + sectionSize, (uint32_t) data_data.data() + data_data.size());
OSFatal("EnvironmentLoader: Tried to overflow .data buffer");
} else if (destination < (uint32_t) data_data.data()) {
DEBUG_FUNCTION_LINE_ERR("Tried to underflow .data buffer. %08X < %08X", destination, (uint32_t) data_data.data());
OSFatal("EnvironmentLoader: Tried to underflow .data buffer");
}
} else if (address >= 0xC0000000) { } else if (address >= 0xC0000000) {
DEBUG_FUNCTION_LINE_ERR("Loading section from 0xC0000000 is NOT supported"); DEBUG_FUNCTION_LINE_ERR("Loading section from 0xC0000000 is NOT supported");
return std::nullopt; return std::nullopt;
@ -123,30 +138,22 @@ ModuleDataFactory::load(const std::string &path, uint32_t destination_address_en
return std::nullopt; return std::nullopt;
} }
const char *p = reader.sections[i]->get_data(); const char *p = psec->get_data();
if (destination + sectionSize > (uint32_t) destination_address_end) { uint32_t address_align = psec->get_addr_align();
DEBUG_FUNCTION_LINE_ERR("Tried to overflow buffer. %08X > %08X", destination + sectionSize, destination_address_end); if ((destination & (address_align - 1)) != 0) {
OSFatal("EnvironmentLoader: Tried to overflow buffer"); DEBUG_FUNCTION_LINE_WARN("Address not aligned: %08X %08X", destination, address_align);
OSFatal("EnvironmentLoader: Address not aligned");
} }
if (psec->get_type() == ELFIO::SHT_NOBITS) { if (psec->get_type() == ELFIO::SHT_NOBITS) {
DEBUG_FUNCTION_LINE("memset section %s %08X to 0 (%d bytes)", psec->get_name().c_str(), destination, sectionSize); DEBUG_FUNCTION_LINE_VERBOSE("memset section %s %08X to 0 (%d bytes)", psec->get_name().c_str(), destination, sectionSize);
memset((void *) destination, 0, sectionSize); memset((void *) destination, 0, sectionSize);
} else if (psec->get_type() == ELFIO::SHT_PROGBITS) { } else if (psec->get_type() == ELFIO::SHT_PROGBITS) {
DEBUG_FUNCTION_LINE("Copy section %s %08X -> %08X (%d bytes)", psec->get_name().c_str(), p, destination, sectionSize); DEBUG_FUNCTION_LINE_VERBOSE("Copy section %s %08X -> %08X (%d bytes)", psec->get_name().c_str(), p, destination, sectionSize);
memcpy((void *) destination, p, sectionSize); memcpy((void *) destination, p, sectionSize);
} }
DEBUG_FUNCTION_LINE_VERBOSE("Saved %s section info. Location: %08X size: %08X", psec->get_name().c_str(), destination, sectionSize);
//nextAddress = ROUNDUP(destination + sectionSize, 0x100);
if (psec->get_name() == ".bss" || psec->get_name() == ".sbss") {
DEBUG_FUNCTION_LINE("memset %s section. Location: %08X size: %08X", psec->get_name().c_str(), destination, sectionSize);
memset(reinterpret_cast<void *>(destination), 0, sectionSize);
}
if (endAddress < destination + sectionSize) {
endAddress = destination + sectionSize;
}
DCFlushRange((void *) destination, sectionSize); DCFlushRange((void *) destination, sectionSize);
ICInvalidateRange((void *) destination, sectionSize); ICInvalidateRange((void *) destination, sectionSize);
@ -157,7 +164,7 @@ ModuleDataFactory::load(const std::string &path, uint32_t destination_address_en
ELFIO::section *psec = reader.sections[i]; ELFIO::section *psec = reader.sections[i];
if ((psec->get_type() == ELFIO::SHT_PROGBITS || psec->get_type() == ELFIO::SHT_NOBITS) && (psec->get_flags() & ELFIO::SHF_ALLOC)) { if ((psec->get_type() == ELFIO::SHT_PROGBITS || psec->get_type() == ELFIO::SHT_NOBITS) && (psec->get_flags() & ELFIO::SHF_ALLOC)) {
DEBUG_FUNCTION_LINE("Linking (%d)... %s", i, psec->get_name().c_str()); DEBUG_FUNCTION_LINE("Linking (%d)... %s", i, psec->get_name().c_str());
if (!linkSection(reader, psec->get_index(), (uint32_t) destinations[psec->get_index()], offset_text, offset_data, trampoline_data, trampoline_data_length)) { if (!linkSection(reader, psec->get_index(), (uint32_t) destinations[psec->get_index()], (uint32_t) (text_data.data()), (uint32_t) (data_data.data()), trampoline_data, trampoline_data_length)) {
DEBUG_FUNCTION_LINE_ERR("elfLink failed"); DEBUG_FUNCTION_LINE_ERR("elfLink failed");
return std::nullopt; return std::nullopt;
} }
@ -165,18 +172,19 @@ ModuleDataFactory::load(const std::string &path, uint32_t destination_address_en
} }
getImportRelocationData(moduleData, reader, destinations.get()); getImportRelocationData(moduleData, reader, destinations.get());
DCFlushRange((void *) baseOffset, totalSize); DCFlushRange((void *) data_data.data(), data_data.size());
ICInvalidateRange((void *) baseOffset, totalSize); ICInvalidateRange((void *) text_data.data(), text_data.size());
moduleData->setStartAddress(startAddress);
moduleData->setEndAddress(endAddress);
moduleData->setEntrypoint(entrypoint); moduleData->setEntrypoint(entrypoint);
moduleData->setTextMemory(std::move(text_data));
moduleData->setDataMemory(std::move(data_data));
DEBUG_FUNCTION_LINE("Saved entrypoint as %08X", entrypoint); DEBUG_FUNCTION_LINE("Saved entrypoint as %08X", entrypoint);
return moduleData; return moduleData;
} }
bool ModuleDataFactory::getImportRelocationData(std::unique_ptr<ModuleData> &moduleData, ELFIO::elfio &reader, uint8_t **destinations) { bool ModuleDataFactory::getImportRelocationData(std::unique_ptr<ModuleData> &moduleData, const ELFIO::elfio &reader, uint8_t **destinations) {
std::map<uint32_t, std::shared_ptr<ImportRPLInformation>> infoMap; std::map<uint32_t, std::shared_ptr<ImportRPLInformation>> infoMap;
uint32_t sec_num = reader.sections.size(); uint32_t sec_num = reader.sections.size();
@ -196,6 +204,7 @@ bool ModuleDataFactory::getImportRelocationData(std::unique_ptr<ModuleData> &mod
for (uint32_t i = 0; i < sec_num; ++i) { for (uint32_t i = 0; i < sec_num; ++i) {
ELFIO::section *psec = reader.sections[i]; ELFIO::section *psec = reader.sections[i];
if (psec->get_type() == ELFIO::SHT_RELA || psec->get_type() == ELFIO::SHT_REL) { if (psec->get_type() == ELFIO::SHT_RELA || psec->get_type() == ELFIO::SHT_REL) {
DEBUG_FUNCTION_LINE_VERBOSE("Found relocation section %s", psec->get_name().c_str());
ELFIO::relocation_section_accessor rel(reader, psec); ELFIO::relocation_section_accessor rel(reader, psec);
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) {
ELFIO::Elf_Word symbol = 0; ELFIO::Elf_Word symbol = 0;
@ -233,28 +242,22 @@ bool ModuleDataFactory::getImportRelocationData(std::unique_ptr<ModuleData> &mod
if (!infoMap.contains(sym_section_index)) { if (!infoMap.contains(sym_section_index)) {
DEBUG_FUNCTION_LINE_ERR("Relocation is referencing a unknown section. %d destination: %08X sym_name %s", section_index, destinations[section_index], sym_name.c_str()); DEBUG_FUNCTION_LINE_ERR("Relocation is referencing a unknown section. %d destination: %08X sym_name %s", section_index, destinations[section_index], sym_name.c_str());
OSFatal("EnvironmentLoader: Relocation is referencing a unknown section."); OSFatal("EnvironmentLoader: Relocation is referencing a unknown section.");
}
auto relocationData = make_unique_nothrow<RelocationData>(type,
offset - 0x02000000,
addend,
(void *) (destinations[section_index] + 0x02000000),
sym_name,
infoMap[sym_section_index]);
if (!relocationData) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc relocation data");
return false; return false;
} }
moduleData->addRelocationData(std::move(relocationData)); moduleData->addRelocationData(RelocationData(type,
offset - 0x02000000,
addend,
(void *) (destinations[section_index]),
sym_name,
infoMap[sym_section_index]));
} }
} }
} }
return true; return true;
} }
bool ModuleDataFactory::linkSection(ELFIO::elfio &reader, uint32_t section_index, uint32_t destination, uint32_t base_text, uint32_t base_data, relocation_trampoline_entry_t *trampoline_data, bool ModuleDataFactory::linkSection(const ELFIO::elfio &reader, uint32_t section_index, uint32_t destination, uint32_t base_text, uint32_t base_data, relocation_trampoline_entry_t *trampoline_data,
uint32_t trampoline_data_length) { uint32_t trampoline_data_length) {
uint32_t sec_num = reader.sections.size(); uint32_t sec_num = reader.sections.size();
@ -307,17 +310,27 @@ bool ModuleDataFactory::linkSection(ELFIO::elfio &reader, uint32_t section_index
return false; return false;
} }
auto adjusted_offset = (uint32_t) offset;
if ((offset >= 0x02000000) && offset < 0x10000000) {
adjusted_offset -= 0x02000000;
} else if ((adjusted_offset >= 0x10000000) && adjusted_offset < 0xC0000000) {
adjusted_offset -= 0x10000000;
} else if (adjusted_offset >= 0xC0000000) {
adjusted_offset -= 0xC0000000;
}
if (sym_section_index == ELFIO::SHN_ABS) { if (sym_section_index == ELFIO::SHN_ABS) {
// //
} else if (sym_section_index > ELFIO::SHN_LORESERVE) { } else if (sym_section_index > ELFIO::SHN_LORESERVE) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED: %04X", sym_section_index); DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED: %04X", sym_section_index);
return false; return false;
} }
if (!ElfUtils::elfLinkOne(type, offset, addend, destination, adjusted_sym_value, trampoline_data, trampoline_data_length, RELOC_TYPE_FIXED)) { if (!ElfUtils::elfLinkOne(type, adjusted_offset, addend, destination, adjusted_sym_value, trampoline_data, trampoline_data_length, RELOC_TYPE_FIXED)) {
DEBUG_FUNCTION_LINE_ERR("Link failed"); DEBUG_FUNCTION_LINE_ERR("Link failed");
return false; return false;
} }
} }
return true;
} }
} }
return true; return true;

View File

@ -20,17 +20,20 @@
#include "../common/relocation_defines.h" #include "../common/relocation_defines.h"
#include "ModuleData.h" #include "ModuleData.h"
#include "elfio/elfio.hpp" #include "elfio/elfio.hpp"
#include "utils/MemoryUtils.h"
#include "utils/utils.h"
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
class ModuleDataFactory { class ModuleDataFactory {
public: public:
static std::optional<std::unique_ptr<ModuleData>> static uint32_t GetSizeOfModule(const ELFIO::elfio &reader);
load(const std::string &path, uint32_t destination_address_end, uint32_t maximum_size, relocation_trampoline_entry_t *trampoline_data, uint32_t trampoline_data_length);
static bool linkSection(ELFIO::elfio &reader, uint32_t section_index, uint32_t destination, uint32_t base_text, uint32_t base_data, relocation_trampoline_entry_t *trampoline_data, static std::optional<std::unique_ptr<ModuleData>> load(const ELFIO::elfio &reader, const HeapWrapper &heapWrapper, relocation_trampoline_entry_t *trampoline_data, uint32_t trampoline_data_length);
static bool linkSection(const ELFIO::elfio &reader, uint32_t section_index, uint32_t destination, uint32_t base_text, uint32_t base_data, relocation_trampoline_entry_t *trampoline_data,
uint32_t trampoline_data_length); uint32_t trampoline_data_length);
static bool getImportRelocationData(std::unique_ptr<ModuleData> &moduleData, ELFIO::elfio &reader, uint8_t **destinations); static bool getImportRelocationData(std::unique_ptr<ModuleData> &moduleData, const ELFIO::elfio &reader, uint8_t **destinations);
}; };

View File

@ -1,4 +1,5 @@
#include "InputUtils.h" #include "InputUtils.h"
#include <coreinit/thread.h>
#include <padscore/kpad.h> #include <padscore/kpad.h>
#include <padscore/wpad.h> #include <padscore/wpad.h>
#include <vpad/input.h> #include <vpad/input.h>
@ -94,17 +95,22 @@ uint32_t remapClassicButtons(uint32_t buttons) {
} }
InputUtils::InputData InputUtils::getControllerInput() { InputUtils::InputData InputUtils::getControllerInput() {
InputData inputData{}; InputData inputData = {};
VPADStatus vpadStatus{}; VPADStatus vpadStatus = {};
VPADReadError vpadError = VPAD_READ_UNINITIALIZED; VPADReadError vpadError = VPAD_READ_UNINITIALIZED;
if (VPADRead(VPAD_CHAN_0, &vpadStatus, 1, &vpadError) > 0 && vpadError == VPAD_READ_SUCCESS) { int maxAttempts = 100;
inputData.trigger = vpadStatus.trigger; do {
inputData.hold = vpadStatus.hold; if (VPADRead(VPAD_CHAN_0, &vpadStatus, 1, &vpadError) > 0 && vpadError == VPAD_READ_SUCCESS) {
inputData.release = vpadStatus.release; inputData.trigger = vpadStatus.trigger;
} inputData.hold = vpadStatus.hold;
inputData.release = vpadStatus.release;
} else {
OSSleepTicks(OSMillisecondsToTicks(1));
}
} while (--maxAttempts > 0 && vpadError == VPAD_READ_NO_SAMPLES);
KPADStatus kpadStatus{}; KPADStatus kpadStatus = {};
KPADError kpadError = KPAD_ERROR_UNINITIALIZED; KPADError kpadError = KPAD_ERROR_UNINITIALIZED;
for (int32_t i = 0; i < 4; i++) { for (int32_t i = 0; i < 4; i++) {
if (KPADReadEx((KPADChan) i, &kpadStatus, 1, &kpadError) > 0) { if (KPADReadEx((KPADChan) i, &kpadStatus, 1, &kpadError) > 0) {
if (kpadError == KPAD_ERROR_OK && kpadStatus.extensionType != 0xFF) { if (kpadError == KPAD_ERROR_OK && kpadStatus.extensionType != 0xFF) {

199
source/utils/MemoryUtils.h Normal file
View File

@ -0,0 +1,199 @@
#pragma once
#include "logger.h"
#include <coreinit/memexpheap.h>
#include <coreinit/memheap.h>
#include <cstdint>
#include <cstring>
#include <optional>
typedef void (*FreeMemoryFn)(void *);
class MemoryWrapper {
public:
MemoryWrapper(void *ptr, uint32_t size, FreeMemoryFn freeFn) : mPtr(ptr), mSize(size), mFreeFn(freeFn) {
}
~MemoryWrapper() {
if (mPtr && mFreeFn) {
memset(mPtr, 0, mSize);
mFreeFn(mPtr);
}
}
MemoryWrapper(const MemoryWrapper &) = delete;
MemoryWrapper &operator=(const MemoryWrapper &) = delete;
MemoryWrapper(MemoryWrapper &&other) noexcept
: mPtr(other.mPtr), mSize(other.mSize), mFreeFn(other.mFreeFn) {
other.mPtr = {};
other.mSize = {};
other.mFreeFn = {};
}
MemoryWrapper &operator=(MemoryWrapper &&other) noexcept {
if (this != &other) {
mPtr = other.mPtr;
mSize = other.mSize;
mFreeFn = other.mFreeFn;
other.mPtr = {};
other.mSize = 0;
other.mFreeFn = {};
}
return *this;
}
[[nodiscard]] void *data() const {
return mPtr;
}
[[nodiscard]] uint32_t size() const {
return mSize;
}
[[nodiscard]] bool IsAllocated() const {
return mFreeFn && mPtr && mSize > 0;
}
private:
void *mPtr = {};
uint32_t mSize = 0;
FreeMemoryFn mFreeFn = {};
};
class ExpHeapMemory {
public:
ExpHeapMemory(MEMHeapHandle heapHandle, void *data, uint32_t size) : mHeapHandle(heapHandle),
mData(data),
mSize(size) {
}
ExpHeapMemory() = default;
~ExpHeapMemory() {
if (mData) {
MEMFreeToExpHeap(mHeapHandle, mData);
}
mData = nullptr;
mSize = 0;
}
// Delete the copy constructor and copy assignment operator
ExpHeapMemory(const ExpHeapMemory &) = delete;
ExpHeapMemory &operator=(const ExpHeapMemory &) = delete;
ExpHeapMemory(ExpHeapMemory &&other) noexcept
: mHeapHandle(other.mHeapHandle), mData(other.mData), mSize(other.mSize) {
other.mHeapHandle = {};
other.mData = {};
other.mSize = 0;
}
ExpHeapMemory &operator=(ExpHeapMemory &&other) noexcept {
if (this != &other) {
mHeapHandle = other.mHeapHandle;
mData = other.mData;
mSize = other.mSize;
other.mHeapHandle = {};
other.mData = {};
other.mSize = 0;
}
return *this;
}
explicit operator bool() const {
return mData != nullptr;
}
explicit operator void *() const {
// Return the desired void* value
return mData;
}
[[nodiscard]] void *data() const {
return mData;
}
[[nodiscard]] std::size_t size() const {
return mSize;
}
static std::optional<ExpHeapMemory> Alloc(MEMHeapHandle heapHandle, uint32_t size, int32_t alignment) {
auto *ptr = MEMAllocFromExpHeapEx(heapHandle, size, alignment);
if (!ptr) {
return {};
}
return ExpHeapMemory(heapHandle, ptr, size);
}
private:
MEMHeapHandle mHeapHandle{};
void *mData = nullptr;
uint32_t mSize{};
};
class HeapWrapper {
public:
explicit HeapWrapper(MemoryWrapper &&memory) : mMemory(std::move(memory)) {
mHeapHandle = MEMCreateExpHeapEx(mMemory.data(), mMemory.size(), MEM_HEAP_FLAG_USE_LOCK);
if (mHeapHandle) {
mSize = mMemory.size();
mPtr = mMemory.data();
}
}
~HeapWrapper() {
if (mHeapHandle) {
MEMDestroyExpHeap(mHeapHandle);
}
if (mPtr) {
memset(mPtr, 0, mSize);
}
}
// Delete the copy constructor and copy assignment operator
HeapWrapper(const HeapWrapper &) = delete;
HeapWrapper &operator=(const HeapWrapper &) = delete;
HeapWrapper(HeapWrapper &&other) noexcept
: mMemory(std::move(other.mMemory)), mHeapHandle(other.mHeapHandle), mPtr(other.mPtr), mSize(other.mSize) {
other.mHeapHandle = {};
other.mPtr = {};
other.mSize = 0;
}
HeapWrapper &operator=(HeapWrapper &&other) noexcept {
if (this != &other) {
mMemory = std::move(other.mMemory);
mHeapHandle = other.mHeapHandle;
mPtr = other.mPtr;
mSize = other.mSize;
other.mHeapHandle = {};
other.mPtr = {};
other.mSize = 0;
}
return *this;
}
[[nodiscard]] MEMHeapHandle GetHeapHandle() const {
return mHeapHandle;
}
[[nodiscard]] uint32_t GetHeapSize() const {
return mSize;
}
[[nodiscard]] bool IsAllocated() const {
return mMemory.IsAllocated();
}
[[nodiscard]] std::optional<ExpHeapMemory> Alloc(uint32_t size, int align) const {
return ExpHeapMemory::Alloc(mHeapHandle, size, align);
}
private:
MemoryWrapper mMemory;
MEMHeapHandle mHeapHandle = {};
void *mPtr = {};
uint32_t mSize = 0;
};

View File

@ -140,8 +140,6 @@ PairMenu::~PairMenu() {
CCRSysExit(); CCRSysExit();
} }
extern "C" bool WPADStartSyncDevice();
bool PairMenu::ProcessPairScreen() { bool PairMenu::ProcessPairScreen() {
switch (mState) { switch (mState) {
case STATE_SYNC_WPAD: { case STATE_SYNC_WPAD: {

View File

@ -34,6 +34,7 @@ extern "C" {
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS) #define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS) #define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
#else #else
@ -44,6 +45,7 @@ extern "C" {
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0) #define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX(OSReport, "##WARN ## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX(OSReport, "##ERROR## ", "\n", FMT, ##ARGS) #define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
#endif #endif

View File

@ -24,3 +24,7 @@ std::string string_format(const std::string &format, Args... args) {
std::snprintf(buf.get(), size, format.c_str(), args...); std::snprintf(buf.get(), size, format.c_str(), args...);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
} }
// those work only in powers of 2
#define ROUNDDOWN(val, align) ((val) & ~(align - 1))
#define ROUNDUP(val, align) ROUNDDOWN(((val) + (align - 1)), align)

2
source/version.h Normal file
View File

@ -0,0 +1,2 @@
#pragma once
#define ENVIRONMENT_LOADER_VERSION_EXTRA ""