diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index a21dfa7e..7e6f4752 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -15,10 +15,13 @@
+
+
+
-
+
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index 0e38efee..852c4ca0 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -7,6 +7,7 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(source_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -flto=full -Wno-unused-command-line-argument")
+enable_language(ASM)
if (uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE")
add_compile_definitions(NDEBUG)
endif()
@@ -21,6 +22,7 @@ include_directories(${source_DIR}/skyline)
add_library(skyline SHARED
${source_DIR}/main.cpp
${source_DIR}/skyline/common.cpp
+ ${source_DIR}/skyline/guest.S
${source_DIR}/skyline/nce.cpp
${source_DIR}/skyline/jvm.cpp
${source_DIR}/skyline/gpu.cpp
diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h
index a269a986..999bdf21 100644
--- a/app/src/main/cpp/skyline/common.h
+++ b/app/src/main/cpp/skyline/common.h
@@ -47,7 +47,7 @@ namespace skyline {
// Loader
constexpr u32 NroMagic = 0x304F524E; //!< "NRO0" in reverse, this is written at the start of every NRO file
// NCE
- constexpr u8 NumRegs = 31; //!< The amount of registers that ARMv8 has
+ constexpr u8 NumRegs = 30; //!< The amount of registers that ARMv8 has
constexpr u16 SvcLast = 0x7F; //!< The index of the last SVC
constexpr u16 BrkRdy = 0xFF; //!< This is reserved for our kernel's to know when a process/thread is ready
constexpr u32 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_EL0 in MRS
@@ -105,86 +105,6 @@ namespace skyline {
NSP, //!< The NSP format from "nspwn" exploit: https://switchbrew.org/wiki/Switch_System_Flaws
};
- namespace instr {
- /**
- * @brief A bit-field struct that encapsulates a BRK instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction.
- */
- struct Brk {
- /**
- * @brief Creates a BRK instruction with a specific immediate value, used for generating BRK opcodes
- * @param val The immediate value of the instruction
- */
- Brk(u16 val) {
- start = 0x0; // First 5 bits of a BRK instruction are 0
- value = val;
- end = 0x6A1; // Last 11 bits of a BRK instruction stored as u16
- }
-
- /**
- * @brief Returns if the opcode is valid or not
- * @return If the opcode represents a valid BRK instruction
- */
- bool Verify() {
- return (start == 0x0 && end == 0x6A1);
- }
-
- u8 start : 5;
- u32 value : 16;
- u16 end : 11;
- };
-
- static_assert(sizeof(Brk) == sizeof(u32));
-
- /**
- * @brief A bit-field struct that encapsulates a SVC instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/svc-supervisor-call.
- */
- struct Svc {
- /**
- * @brief Returns if the opcode is valid or not
- * @return If the opcode represents a valid SVC instruction
- */
- bool Verify() {
- return (start == 0x1 && end == 0x6A0);
- }
-
- u8 start : 5;
- u32 value : 16;
- u16 end : 11;
- };
-
- static_assert(sizeof(Svc) == sizeof(u32));
-
- /**
- * @brief A bit-field struct that encapsulates a MRS instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/mrs-move-system-register.
- */
- struct Mrs {
- /**
- * @brief Creates a MRS instruction, used for generating BRK opcodes
- * @param srcReg The source system register
- * @param dstReg The destination Xn register
- */
- Mrs(u32 srcReg, u8 dstReg) {
- this->srcReg = srcReg;
- this->dstReg = dstReg;
- end = 0xD53; // Last 12 bits of a MRS instruction stored as u16
- }
-
- /**
- * @brief Returns if the opcode is valid or not
- * @return If the opcode represents a valid MRS instruction
- */
- bool Verify() {
- return (end == 0xD53);
- }
-
- u8 dstReg : 5;
- u32 srcReg : 15;
- u16 end : 12;
- };
-
- static_assert(sizeof(Mrs) == sizeof(u32));
- };
-
/**
* Read about ARMv8 registers here: https://developer.arm.com/docs/100878/latest/registers
*/
diff --git a/app/src/main/cpp/skyline/guest.S b/app/src/main/cpp/skyline/guest.S
new file mode 100644
index 00000000..27976782
--- /dev/null
+++ b/app/src/main/cpp/skyline/guest.S
@@ -0,0 +1,46 @@
+.text
+.global saveCtx
+saveCtx:
+ STR LR, [SP, #-16]!
+ MRS LR, TPIDR_EL0
+ #LDR LR, [LR]
+ STP X0, X1, [LR, #0]
+ STP X2, X3, [LR, #16]
+ STP X4, X5, [LR, #32]
+ STP X6, X7, [LR, #48]
+ STP X8, X9, [LR, #64]
+ STP X10, X11, [LR, #80]
+ STP X12, X13, [LR, #96]
+ STP X14, X15, [LR, #112]
+ STP X16, X17, [LR, #128]
+ STP X18, X19, [LR, #144]
+ STP X20, X21, [LR, #160]
+ STP X22, X23, [LR, #176]
+ STP X24, X25, [LR, #192]
+ STP X26, X27, [LR, #208]
+ STP X28, X29, [LR, #224]
+ LDR LR, [SP], #16
+ RET
+
+.global loadCtx
+loadCtx:
+ STR LR, [SP, #-16]!
+ MRS LR, TPIDR_EL0
+ #LDR LR, [LR]
+ LDP X0, X1, [LR, #0]
+ LDP X2, X3, [LR, #16]
+ LDP X4, X5, [LR, #32]
+ LDP X6, X7, [LR, #48]
+ LDP X8, X9, [LR, #64]
+ LDP X10, X11, [LR, #80]
+ LDP X12, X13, [LR, #96]
+ LDP X14, X15, [LR, #112]
+ LDP X16, X17, [LR, #128]
+ LDP X18, X19, [LR, #144]
+ LDP X20, X21, [LR, #160]
+ LDP X22, X23, [LR, #176]
+ LDP X24, X25, [LR, #192]
+ LDP X26, X27, [LR, #208]
+ LDP X28, X29, [LR, #224]
+ LDR LR, [SP], #16
+ RET
diff --git a/app/src/main/cpp/skyline/guest.h b/app/src/main/cpp/skyline/guest.h
new file mode 100644
index 00000000..9deb5919
--- /dev/null
+++ b/app/src/main/cpp/skyline/guest.h
@@ -0,0 +1,8 @@
+#pragma once
+
+namespace skyline::guest {
+ constexpr size_t saveCtxSize = 20 * sizeof(u32);
+ constexpr size_t loadCtxSize = 20 * sizeof(u32);
+ extern "C" void saveCtx(void);
+ extern "C" void loadCtx(void);
+}
diff --git a/app/src/main/cpp/skyline/loader/loader.h b/app/src/main/cpp/skyline/loader/loader.h
index 41f91463..d3b94649 100644
--- a/app/src/main/cpp/skyline/loader/loader.h
+++ b/app/src/main/cpp/skyline/loader/loader.h
@@ -21,32 +21,6 @@ namespace skyline::loader {
pread64(romFd, output, size, offset);
}
- /**
- * @brief This patches specific parts of the code
- * @param code A vector with the code to be patched
- */
- inline void PatchCode(std::vector &code) {
- u32 *address = reinterpret_cast(code.data());
- u32 *end = address + (code.size() / sizeof(u32));
- while (address < end) {
- auto instrSvc = reinterpret_cast(address);
- auto instrMrs = reinterpret_cast(address);
- if (instrSvc->Verify()) {
- instr::Brk brk(static_cast(instrSvc->value));
- *address = *reinterpret_cast(&brk);
- } else if (instrMrs->Verify()) {
- if (instrMrs->srcReg == constant::TpidrroEl0) {
- instr::Brk brk(static_cast(constant::SvcLast + 1 + instrMrs->dstReg));
- *address = *reinterpret_cast(&brk);
- } else if (instrMrs->srcReg == constant::CntpctEl0) {
- instr::Mrs mrs(constant::CntvctEl0, instrMrs->dstReg);
- *address = *reinterpret_cast(&mrs);
- }
- }
- address++;
- }
- }
-
public:
/**
* @param filePath The path to the ROM file
diff --git a/app/src/main/cpp/skyline/loader/nro.cpp b/app/src/main/cpp/skyline/loader/nro.cpp
index 4d645dde..f4d12622 100644
--- a/app/src/main/cpp/skyline/loader/nro.cpp
+++ b/app/src/main/cpp/skyline/loader/nro.cpp
@@ -17,11 +17,12 @@ namespace skyline::loader {
ReadOffset(rodata.data(), header.ro.offset, header.ro.size);
ReadOffset(data.data(), header.data.offset, header.data.size);
- PatchCode(text);
+ std::vector patch = state.nce->PatchCode(text, header.text.size + header.ro.size + header.data.size + header.bssSize);
u64 textSize = text.size();
u64 rodataSize = rodata.size();
u64 dataSize = data.size();
+ u64 patchSize = patch.size() * sizeof(u32);
process->MapPrivateRegion(constant::BaseAddr, textSize, {true, true, true}, memory::Type::CodeStatic, memory::Region::Text); // R-X
state.logger->Debug("Successfully mapped region .text @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr, textSize);
@@ -35,8 +36,12 @@ namespace skyline::loader {
process->MapPrivateRegion(constant::BaseAddr + textSize + rodataSize + dataSize, header.bssSize, {true, true, true}, memory::Type::CodeMutable, memory::Region::Bss); // RWX
state.logger->Debug("Successfully mapped region .bss @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + textSize + rodataSize + dataSize, header.bssSize);
+ process->MapPrivateRegion(constant::BaseAddr + textSize + rodataSize + dataSize + header.bssSize, patchSize, {true, true, true}, memory::Type::CodeStatic, memory::Region::Text); // RWX
+ state.logger->Debug("Successfully mapped region .patch @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + textSize + rodataSize + dataSize + header.bssSize, patchSize);
+
process->WriteMemory(text.data(), constant::BaseAddr, textSize);
process->WriteMemory(rodata.data(), constant::BaseAddr + textSize, rodataSize);
process->WriteMemory(data.data(), constant::BaseAddr + textSize + rodataSize, dataSize);
+ process->WriteMemory(patch.data(), constant::BaseAddr + textSize + rodataSize + dataSize + header.bssSize, patchSize);
}
}
diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp
index 38dae7c2..616fb57f 100644
--- a/app/src/main/cpp/skyline/nce.cpp
+++ b/app/src/main/cpp/skyline/nce.cpp
@@ -3,11 +3,182 @@
#include
#include "os.h"
#include "jvm.h"
+#include "guest.h"
extern bool Halt;
extern std::mutex jniMtx;
namespace skyline {
+
+ namespace instr {
+ /**
+ * @brief A bit-field struct that encapsulates a BRK instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction.
+ */
+ struct Brk {
+ /**
+ * @brief Creates a BRK instruction with a specific immediate value, used for generating BRK opcodes
+ * @param value The immediate value of the instruction
+ */
+ explicit Brk(u16 value) {
+ start = 0x0; // First 5 bits of a BRK instruction are 0
+ this->value = value;
+ end = 0x6A1; // Last 11 bits of a BRK instruction stored as u16
+ }
+
+ /**
+ * @brief Returns if the opcode is valid or not
+ * @return If the opcode represents a valid BRK instruction
+ */
+ inline bool Verify() {
+ return (start == 0x0 && end == 0x6A1);
+ }
+
+ union {
+ struct {
+ u8 start : 5;
+ u32 value : 16;
+ u16 end : 11;
+ };
+ u32 raw{};
+ };
+ };
+
+ static_assert(sizeof(Brk) == sizeof(u32));
+
+ /**
+ * @brief A bit-field struct that encapsulates a SVC instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/svc-supervisor-call.
+ */
+ struct Svc {
+ /**
+ * @brief Returns if the opcode is valid or not
+ * @return If the opcode represents a valid SVC instruction
+ */
+ inline bool Verify() {
+ return (start == 0x1 && end == 0x6A0);
+ }
+
+ union {
+ struct {
+ u8 start : 5;
+ u32 value : 16;
+ u16 end : 11;
+ };
+ u32 raw{};
+ };
+ };
+
+ static_assert(sizeof(Svc) == sizeof(u32));
+
+ /**
+ * @brief A bit-field struct that encapsulates a MRS instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/mrs-move-system-register.
+ */
+ struct Mrs {
+ /**
+ * @brief Creates a MRS instruction, used for generating BRK opcodes
+ * @param srcReg The source system register
+ * @param dstReg The destination Xn register
+ */
+ Mrs(u32 srcReg, u8 dstReg) {
+ this->srcReg = srcReg;
+ this->dstReg = dstReg;
+ end = 0xD53; // Last 12 bits of a MRS instruction stored as u16
+ }
+
+ /**
+ * @brief Returns if the opcode is valid or not
+ * @return If the opcode represents a valid MRS instruction
+ */
+ inline bool Verify() {
+ return (end == 0xD53);
+ }
+
+ union {
+ struct {
+ u8 dstReg : 5;
+ u32 srcReg : 15;
+ u16 end : 12;
+ };
+ u32 raw{};
+ };
+ };
+
+ static_assert(sizeof(Mrs) == sizeof(u32));
+
+ /**
+ * @brief A bit-field struct that encapsulates a B instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/b-branch.
+ */
+ struct B {
+ public:
+ explicit B(i64 offset) {
+ this->offset = static_cast(offset / 4);
+ end = 0x5;
+ }
+
+ /**
+ * @brief Returns the offset of the instruction
+ * @return The offset encoded within the instruction
+ */
+ inline i32 Offset() {
+ return offset * 4;
+ }
+
+ /**
+ * @brief Returns if the opcode is valid or not
+ * @return If the opcode represents a valid Branch instruction
+ */
+ inline bool Verify() {
+ return (end == 0x5);
+ }
+
+ union {
+ struct {
+ i32 offset : 26;
+ u8 end : 6;
+ };
+ u32 raw{};
+ };
+ };
+
+ static_assert(sizeof(B) == sizeof(u32));
+
+ /**
+ * @brief A bit-field struct that encapsulates a BL instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/b-branch.
+ */
+ struct BL {
+ public:
+ explicit BL(i64 offset) {
+ this->offset = static_cast(offset / 4);
+ end = 0x25;
+ }
+
+ /**
+ * @brief Returns the offset of the instruction
+ * @return The offset encoded within the instruction
+ */
+ inline i32 Offset() {
+ return offset * 4;
+ }
+
+ /**
+ * @brief Returns if the opcode is valid or not
+ * @return If the opcode represents a valid Branch instruction
+ */
+ inline bool Verify() {
+ return (end == 0x85);
+ }
+
+ union {
+ struct {
+ i32 offset : 26;
+ u8 end : 6;
+ };
+ u32 raw{};
+ };
+ };
+
+ static_assert(sizeof(BL) == sizeof(u32));
+ }
+
void NCE::ReadRegisters(user_pt_regs ®isters, pid_t pid) const {
iovec iov = {®isters, sizeof(registers)};
long status = ptrace(PTRACE_GETREGSET, pid ? pid : currPid, NT_PRSTATUS, &iov);
@@ -56,8 +227,10 @@ namespace skyline {
SetRegister(static_cast(instr.value - (constant::SvcLast + 1)), state.thisThread->tls);
} else if (instr.value == constant::BrkRdy)
continue;
- else
+ else {
+ ProcessTrace();
throw exception("Received unhandled BRK: 0x{:X}", static_cast(instr.value));
+ }
}
currRegs.pc += sizeof(u32);
WriteRegisters(currRegs);
@@ -181,12 +354,11 @@ namespace skyline {
raw += fmt::format("{:08X}", instr);
}
state.logger->Debug("Raw Instructions: 0x{}", raw);
- state.logger->Debug("CPU Context:");
- state.logger->Debug("SP: 0x{:X}", regs.sp);
- state.logger->Debug("PSTATE: 0x{:X}", regs.pstate);
- for (u16 index = 0; index < constant::NumRegs - 2; index++) {
- state.logger->Debug("X{}: 0x{:X}", index, regs.regs[index]);
+ std::string regStr;
+ for (u16 index = 0; index < constant::NumRegs - 1; index+=2) {
+ regStr += fmt::format("\nX{}: 0x{:X}, X{}: 0x{:X}", index, regs.regs[index], index+1, regs.regs[index+1]);
}
+ state.logger->Debug("CPU Context:\nSP: 0x{:X}\nLR: 0x{:X}\nPSTATE: 0x{:X}{}", regs.sp, regs.regs[30], regs.pstate, regStr);
}
u64 NCE::GetRegister(Xreg regId, pid_t pid) {
@@ -228,4 +400,68 @@ namespace skyline {
registerMap.at(pid).pstate = value;
}
}
+
+ std::vector NCE::PatchCode(std::vector& code, i64 offset) {
+ u32 *address = reinterpret_cast(code.data());
+ u32 *end = address + (code.size() / sizeof(u32));
+ i64 patchOffset = offset;
+
+ std::vector patch;
+ patch.resize((guest::saveCtxSize + guest::loadCtxSize) / sizeof(u32));
+ std::memcpy(patch.data(), reinterpret_cast(&guest::saveCtx), guest::saveCtxSize);
+ offset += guest::saveCtxSize;
+
+ std::memcpy(reinterpret_cast(patch.data()) + guest::saveCtxSize,
+ reinterpret_cast(&guest::loadCtx), guest::loadCtxSize);
+ offset += guest::loadCtxSize;
+
+ while (address < end) {
+ auto instrSvc = reinterpret_cast(address);
+ auto instrMrs = reinterpret_cast(address);
+
+ if (instrSvc->Verify()) {
+ instr::B bjunc(offset);
+ constexpr u32 strLr = 0xF81F0FFE; // STR LR, [SP, #-16]!
+ offset += sizeof(strLr);
+ instr::Brk brk(static_cast(instrSvc->value));
+ offset += sizeof(brk);
+ instr::BL bSvCtx(patchOffset - offset);
+ offset += sizeof(bSvCtx);
+ instr::BL bLdCtx((patchOffset + guest::saveCtxSize) - offset);
+ offset += sizeof(bLdCtx);
+ constexpr u32 ldrLr = 0xF84107FE; // LDR LR, [SP], #16
+ offset += sizeof(ldrLr);
+ instr::B bret(-offset + sizeof(u32));
+ offset += sizeof(bret);
+
+ *address = bjunc.raw;
+ patch.push_back(strLr);
+ patch.push_back(brk.raw);
+ patch.push_back(bSvCtx.raw);
+ patch.push_back(bLdCtx.raw);
+ patch.push_back(ldrLr);
+ patch.push_back(bret.raw);
+ } else if (instrMrs->Verify()) {
+ if (instrMrs->srcReg == constant::TpidrroEl0) {
+ instr::B bjunc(offset);
+ instr::Brk brk(static_cast(constant::SvcLast + 1 + instrMrs->dstReg));
+ offset += sizeof(u32);
+ instr::B bret(-offset + sizeof(u32));
+ offset += sizeof(u32);
+
+ *address = bjunc.raw;
+ patch.push_back(brk.raw);
+ patch.push_back(bret.raw);
+ } else if (instrMrs->srcReg == constant::CntpctEl0) {
+ instr::Mrs mrs(constant::CntvctEl0, instrMrs->dstReg);
+ *address = mrs.raw;
+ }
+ }
+ address++;
+ offset -= sizeof(u32);
+ patchOffset -= sizeof(u32);
+ }
+ return patch;
+ }
}
+
diff --git a/app/src/main/cpp/skyline/nce.h b/app/src/main/cpp/skyline/nce.h
index c1ecbfd1..3c66ad7e 100644
--- a/app/src/main/cpp/skyline/nce.h
+++ b/app/src/main/cpp/skyline/nce.h
@@ -9,6 +9,9 @@
#include "kernel/types/KSharedMemory.h"
namespace skyline {
+ namespace instr {
+ struct Brk;
+ }
/**
* @brief The NCE (Native Code Execution) class is responsible for managing the state of catching instructions and directly controlling processes/threads
*/
@@ -140,5 +143,11 @@ namespace skyline {
* @param pid The PID of the process (Defaults to currPid)
*/
void SetRegister(Sreg regId, u32 value, pid_t pid = 0);
+
+ /**
+ * @brief This patches specific parts of the code
+ * @param code A vector with the code to be patched
+ */
+ std::vector PatchCode(std::vector &code, i64 offset);
};
}