mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 19:21:55 +01:00
Implement ldr:ro LoadModule
This commit is contained in:
parent
e571066409
commit
01e27bd2dd
@ -264,6 +264,12 @@ namespace skyline::util {
|
|||||||
FillRandomBytes(std::span(reinterpret_cast<typename IntegerFor<T>::Type *>(&object), IntegerFor<T>::Count));
|
FillRandomBytes(std::span(reinterpret_cast<typename IntegerFor<T>::Type *>(&object), IntegerFor<T>::Count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<IsPointerOrUnsignedIntegral T>
|
||||||
|
T RandomNumber(T min, T max) {
|
||||||
|
std::uniform_int_distribution dist(PointerValue(min), PointerValue(max));
|
||||||
|
return ValuePointer<T>(dist(detail::generator));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A temporary shim for C++ 20's bit_cast to make transitioning to it easier
|
* @brief A temporary shim for C++ 20's bit_cast to make transitioning to it easier
|
||||||
*/
|
*/
|
||||||
@ -353,4 +359,8 @@ namespace skyline::util {
|
|||||||
std::array<T, Size> MergeInto(TSrcs &&... srcs) {
|
std::array<T, Size> MergeInto(TSrcs &&... srcs) {
|
||||||
return MergeInto<T>(std::make_index_sequence<Size>(), std::forward<TSrcs>(srcs)...);
|
return MergeInto<T>(std::make_index_sequence<Size>(), std::forward<TSrcs>(srcs)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string HexDump(std::span<u8> data) {
|
||||||
|
return std::accumulate(data.begin(), data.end(), std::string{}, [](std::string str, u8 el) { return std::move(str) + fmt::format("{:02X}", el); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,6 @@
|
|||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
|
||||||
namespace skyline::loader {
|
namespace skyline::loader {
|
||||||
/**
|
|
||||||
* @brief The NroLoader class abstracts access to an NRO file through the Loader interface
|
|
||||||
* @url https://switchbrew.org/wiki/NRO
|
|
||||||
*/
|
|
||||||
class NroLoader : public Loader {
|
|
||||||
private:
|
|
||||||
struct NroSegmentHeader {
|
struct NroSegmentHeader {
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u32 size;
|
u32 size;
|
||||||
@ -39,7 +33,15 @@ namespace skyline::loader {
|
|||||||
NroSegmentHeader apiInfo; //!< The .apiInfo segment header
|
NroSegmentHeader apiInfo; //!< The .apiInfo segment header
|
||||||
NroSegmentHeader dynstr; //!< The .dynstr segment header
|
NroSegmentHeader dynstr; //!< The .dynstr segment header
|
||||||
NroSegmentHeader dynsym; //!< The .dynsym segment header
|
NroSegmentHeader dynsym; //!< The .dynsym segment header
|
||||||
} header{};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The NroLoader class abstracts access to an NRO file through the Loader interface
|
||||||
|
* @url https://switchbrew.org/wiki/NRO
|
||||||
|
*/
|
||||||
|
class NroLoader : public Loader {
|
||||||
|
private:
|
||||||
|
NroHeader header{};
|
||||||
|
|
||||||
struct NroAssetSection {
|
struct NroAssetSection {
|
||||||
u64 offset;
|
u64 offset;
|
||||||
|
@ -1,8 +1,110 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include <fmt/ranges.h>
|
||||||
|
#include <mbedtls/sha256.h>
|
||||||
|
#include <loader/nro.h>
|
||||||
|
#include <nce.h>
|
||||||
#include "IRoInterface.h"
|
#include "IRoInterface.h"
|
||||||
|
|
||||||
namespace skyline::service::ro {
|
namespace skyline::service::ro {
|
||||||
IRoInterface::IRoInterface(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
|
IRoInterface::IRoInterface(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
|
||||||
|
|
||||||
|
Result IRoInterface::LoadModule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
|
u64 pid{request.Pop<u64>()};
|
||||||
|
u64 nroAddress{request.Pop<u64>()};
|
||||||
|
u64 nroSize{request.Pop<u64>()};
|
||||||
|
u64 bssAddress{request.Pop<u64>()};
|
||||||
|
u64 bssSize{request.Pop<u64>()};
|
||||||
|
|
||||||
|
if (!util::IsPageAligned(nroAddress) || !util::IsPageAligned(bssAddress))
|
||||||
|
return result::InvalidAddress;
|
||||||
|
|
||||||
|
if (!util::IsPageAligned(nroSize) || !nroSize || !util::IsPageAligned(bssSize))
|
||||||
|
return result::InvalidSize;
|
||||||
|
|
||||||
|
auto data{span{reinterpret_cast<u8 *>(nroAddress), nroSize}};
|
||||||
|
auto &header{data.as<loader::NroHeader>()};
|
||||||
|
|
||||||
|
std::array<u8, 0x20> hash{};
|
||||||
|
mbedtls_sha256_ret(data.data(), data.size(), hash.data(), 0);
|
||||||
|
|
||||||
|
if (!loadedNros.emplace(hash).second)
|
||||||
|
return result::AlreadyLoaded;
|
||||||
|
|
||||||
|
// We don't handle NRRs here since they're purely used for signature verification which we will never do
|
||||||
|
if (bssSize != header.bssSize)
|
||||||
|
return result::InvalidNro;
|
||||||
|
|
||||||
|
loader::Executable executable{};
|
||||||
|
|
||||||
|
executable.text.offset = 0;
|
||||||
|
executable.text.contents.resize(header.text.size);
|
||||||
|
span(executable.text.contents).copy_from(data.subspan(header.text.offset, header.text.size));
|
||||||
|
|
||||||
|
executable.ro.offset = header.text.size;
|
||||||
|
executable.ro.contents.resize(header.ro.size);
|
||||||
|
span(executable.ro.contents).copy_from(data.subspan(header.ro.offset, header.ro.size));
|
||||||
|
|
||||||
|
executable.data.offset = header.text.size + header.ro.size;
|
||||||
|
executable.data.contents.resize(header.data.size);
|
||||||
|
span(executable.data.contents).copy_from(data.subspan(header.data.offset, header.data.size));
|
||||||
|
|
||||||
|
executable.bssSize = header.bssSize;
|
||||||
|
|
||||||
|
if (header.dynsym.offset > header.ro.offset && header.dynsym.offset + header.dynsym.size < header.ro.offset + header.ro.size && header.dynstr.offset > header.ro.offset && header.dynstr.offset + header.dynstr.size < header.ro.offset + header.ro.size) {
|
||||||
|
executable.dynsym = {header.dynsym.offset, header.dynsym.size};
|
||||||
|
executable.dynstr = {header.dynstr.offset, header.dynstr.size};
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 textSize{executable.text.contents.size()};
|
||||||
|
u64 roSize{executable.ro.contents.size()};
|
||||||
|
u64 dataSize{executable.data.contents.size() + executable.bssSize};
|
||||||
|
|
||||||
|
auto patch{state.nce->GetPatchData(executable.text.contents)};
|
||||||
|
auto size{patch.size + textSize + roSize + dataSize};
|
||||||
|
|
||||||
|
u8 *ptr{};
|
||||||
|
do {
|
||||||
|
ptr = util::AlignDown(util::RandomNumber(state.process->memory.base.data(), std::prev(state.process->memory.base.end()).base()), constant::PageSize) - size;
|
||||||
|
|
||||||
|
if (state.process->memory.heap.contains(ptr) || state.process->memory.alias.contains(ptr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto desc{state.process->memory.Get(ptr)};
|
||||||
|
if (!desc || desc->state != memory::states::Unmapped || (static_cast<size_t>(ptr - desc->ptr) + size) < desc->size)
|
||||||
|
continue;
|
||||||
|
} while (!ptr);
|
||||||
|
|
||||||
|
auto loadInfo{state.loader->LoadExecutable(state.process, state, executable, static_cast<size_t>(ptr - state.process->memory.base.data()), util::HexDump(hash) + ".nro")};
|
||||||
|
|
||||||
|
response.Push(loadInfo.entry);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IRoInterface::UnloadModule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
|
Logger::Error("Module unloading is unimplemented!");
|
||||||
|
return {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IRoInterface::RegisterModuleInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
|
return {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IRoInterface::UnregisterModuleInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
|
return {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IRoInterface::RegisterProcessHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
|
return {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IRoInterface::RegisterProcessModuleInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
|
return {};
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,73 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
#include <services/serviceman.h>
|
#include <services/serviceman.h>
|
||||||
|
|
||||||
namespace skyline::service::ro {
|
namespace skyline::service::ro {
|
||||||
|
namespace result {
|
||||||
|
constexpr Result AlreadyLoaded{22, 3};
|
||||||
|
constexpr Result InvalidNro{22, 4};
|
||||||
|
constexpr Result InvalidNrr{22, 6};
|
||||||
|
constexpr Result InvalidAddress{22, 1025};
|
||||||
|
constexpr Result InvalidSize{22, 1026};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief IRoInterface or ldr:ro is used by applications to dynamically load nros
|
* @brief IRoInterface or ldr:ro is used by applications to dynamically load nros
|
||||||
* @url https://switchbrew.org/wiki/RO_services#LoadModule
|
* @url https://switchbrew.org/wiki/RO_services#LoadModule
|
||||||
*/
|
*/
|
||||||
class IRoInterface : public BaseService {
|
class IRoInterface : public BaseService {
|
||||||
|
private:
|
||||||
|
enum class NrrKind : u8 {
|
||||||
|
User = 0,
|
||||||
|
JitPlugin = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_set<std::array<u8, 0x20>, util::ObjectHash<std::array<u8, 0x20>>> loadedNros{};
|
||||||
|
|
||||||
|
Result RegisterModuleInfoImpl(u64 nrrAddress, u64 nrrSize, NrrKind nrrKind);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IRoInterface(const DeviceState &state, ServiceManager &manager);
|
IRoInterface(const DeviceState &state, ServiceManager &manager);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://switchbrew.org/wiki/RO_services#LoadModule
|
||||||
|
*/
|
||||||
|
Result LoadModule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://switchbrew.org/wiki/RO_services#UnloadModule
|
||||||
|
*/
|
||||||
|
Result UnloadModule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://switchbrew.org/wiki/RO_services#RegisterModuleInfo
|
||||||
|
*/
|
||||||
|
Result RegisterModuleInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://switchbrew.org/wiki/RO_services#UnregisterModuleInfo
|
||||||
|
*/
|
||||||
|
Result UnregisterModuleInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://switchbrew.org/wiki/RO_services#Initialize
|
||||||
|
*/
|
||||||
|
Result RegisterProcessHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://switchbrew.org/wiki/RO_services#RegisterProcessModuleInfo
|
||||||
|
*/
|
||||||
|
Result RegisterProcessModuleInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||||
|
|
||||||
|
SERVICE_DECL(
|
||||||
|
SFUNC(0x0, IRoInterface, LoadModule),
|
||||||
|
SFUNC(0x1, IRoInterface, UnloadModule),
|
||||||
|
SFUNC(0x2, IRoInterface, RegisterModuleInfo),
|
||||||
|
SFUNC(0x3, IRoInterface, UnregisterModuleInfo),
|
||||||
|
SFUNC(0x4, IRoInterface, RegisterProcessHandle),
|
||||||
|
SFUNC(0xA, IRoInterface, RegisterProcessModuleInfo)
|
||||||
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user