mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-29 22:04:17 +01:00
Introduce Basic Junction Patching
This commit introduces basic junction patching or patching code to jump to a junction then execute intermediate functions and jump back.
This commit is contained in:
parent
f7ad017726
commit
3e9bfaec0e
3
.idea/codeStyles/Project.xml
generated
3
.idea/codeStyles/Project.xml
generated
@ -15,10 +15,13 @@
|
|||||||
<option name="FUNCTION_TOP_AFTER_RETURN_TYPE_WRAP" value="0" />
|
<option name="FUNCTION_TOP_AFTER_RETURN_TYPE_WRAP" value="0" />
|
||||||
<option name="FUNCTION_PARAMETERS_WRAP" value="5" />
|
<option name="FUNCTION_PARAMETERS_WRAP" value="5" />
|
||||||
<option name="FUNCTION_CALL_ARGUMENTS_WRAP" value="5" />
|
<option name="FUNCTION_CALL_ARGUMENTS_WRAP" value="5" />
|
||||||
|
<option name="SHIFT_OPERATION_WRAP" value="0" />
|
||||||
<option name="TEMPLATE_DECLARATION_STRUCT_WRAP" value="1" />
|
<option name="TEMPLATE_DECLARATION_STRUCT_WRAP" value="1" />
|
||||||
<option name="TEMPLATE_DECLARATION_FUNCTION_WRAP" value="1" />
|
<option name="TEMPLATE_DECLARATION_FUNCTION_WRAP" value="1" />
|
||||||
<option name="TEMPLATE_CALL_ARGUMENTS_WRAP" value="5" />
|
<option name="TEMPLATE_CALL_ARGUMENTS_WRAP" value="5" />
|
||||||
<option name="TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE" value="true" />
|
<option name="TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE" value="true" />
|
||||||
|
<option name="CLASS_CONSTRUCTOR_INIT_LIST_WRAP" value="0" />
|
||||||
|
<option name="SUPERCLASS_LIST_WRAP" value="0" />
|
||||||
<option name="ALIGN_INIT_LIST_IN_COLUMNS" value="false" />
|
<option name="ALIGN_INIT_LIST_IN_COLUMNS" value="false" />
|
||||||
<option name="HEADER_GUARD_STYLE_PATTERN" value="${PROJECT_NAME}_${PROJECT_REL_PATH}_${FILE_NAME}_${EXT}" />
|
<option name="HEADER_GUARD_STYLE_PATTERN" value="${PROJECT_NAME}_${PROJECT_REL_PATH}_${FILE_NAME}_${EXT}" />
|
||||||
<option name="MACROS_NAMING_CONVENTION">
|
<option name="MACROS_NAMING_CONVENTION">
|
||||||
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -43,7 +43,7 @@
|
|||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="JDK" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
@ -7,6 +7,7 @@ set(CMAKE_CXX_STANDARD 17)
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||||
set(source_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
|
set(source_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -flto=full -Wno-unused-command-line-argument")
|
set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -flto=full -Wno-unused-command-line-argument")
|
||||||
|
enable_language(ASM)
|
||||||
if (uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE")
|
if (uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE")
|
||||||
add_compile_definitions(NDEBUG)
|
add_compile_definitions(NDEBUG)
|
||||||
endif()
|
endif()
|
||||||
@ -21,6 +22,7 @@ include_directories(${source_DIR}/skyline)
|
|||||||
add_library(skyline SHARED
|
add_library(skyline SHARED
|
||||||
${source_DIR}/main.cpp
|
${source_DIR}/main.cpp
|
||||||
${source_DIR}/skyline/common.cpp
|
${source_DIR}/skyline/common.cpp
|
||||||
|
${source_DIR}/skyline/guest.S
|
||||||
${source_DIR}/skyline/nce.cpp
|
${source_DIR}/skyline/nce.cpp
|
||||||
${source_DIR}/skyline/jvm.cpp
|
${source_DIR}/skyline/jvm.cpp
|
||||||
${source_DIR}/skyline/gpu.cpp
|
${source_DIR}/skyline/gpu.cpp
|
||||||
|
@ -47,7 +47,7 @@ namespace skyline {
|
|||||||
// Loader
|
// Loader
|
||||||
constexpr u32 NroMagic = 0x304F524E; //!< "NRO0" in reverse, this is written at the start of every NRO file
|
constexpr u32 NroMagic = 0x304F524E; //!< "NRO0" in reverse, this is written at the start of every NRO file
|
||||||
// NCE
|
// 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 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 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
|
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
|
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
|
* Read about ARMv8 registers here: https://developer.arm.com/docs/100878/latest/registers
|
||||||
*/
|
*/
|
||||||
|
46
app/src/main/cpp/skyline/guest.S
Normal file
46
app/src/main/cpp/skyline/guest.S
Normal file
@ -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
|
8
app/src/main/cpp/skyline/guest.h
Normal file
8
app/src/main/cpp/skyline/guest.h
Normal file
@ -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);
|
||||||
|
}
|
@ -21,32 +21,6 @@ namespace skyline::loader {
|
|||||||
pread64(romFd, output, size, offset);
|
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<u8> &code) {
|
|
||||||
u32 *address = reinterpret_cast<u32 *>(code.data());
|
|
||||||
u32 *end = address + (code.size() / sizeof(u32));
|
|
||||||
while (address < end) {
|
|
||||||
auto instrSvc = reinterpret_cast<instr::Svc *>(address);
|
|
||||||
auto instrMrs = reinterpret_cast<instr::Mrs *>(address);
|
|
||||||
if (instrSvc->Verify()) {
|
|
||||||
instr::Brk brk(static_cast<u16>(instrSvc->value));
|
|
||||||
*address = *reinterpret_cast<u32 *>(&brk);
|
|
||||||
} else if (instrMrs->Verify()) {
|
|
||||||
if (instrMrs->srcReg == constant::TpidrroEl0) {
|
|
||||||
instr::Brk brk(static_cast<u16>(constant::SvcLast + 1 + instrMrs->dstReg));
|
|
||||||
*address = *reinterpret_cast<u32 *>(&brk);
|
|
||||||
} else if (instrMrs->srcReg == constant::CntpctEl0) {
|
|
||||||
instr::Mrs mrs(constant::CntvctEl0, instrMrs->dstReg);
|
|
||||||
*address = *reinterpret_cast<u32 *>(&mrs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
address++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @param filePath The path to the ROM file
|
* @param filePath The path to the ROM file
|
||||||
|
@ -17,11 +17,12 @@ namespace skyline::loader {
|
|||||||
ReadOffset(rodata.data(), header.ro.offset, header.ro.size);
|
ReadOffset(rodata.data(), header.ro.offset, header.ro.size);
|
||||||
ReadOffset(data.data(), header.data.offset, header.data.size);
|
ReadOffset(data.data(), header.data.offset, header.data.size);
|
||||||
|
|
||||||
PatchCode(text);
|
std::vector<u32> patch = state.nce->PatchCode(text, header.text.size + header.ro.size + header.data.size + header.bssSize);
|
||||||
|
|
||||||
u64 textSize = text.size();
|
u64 textSize = text.size();
|
||||||
u64 rodataSize = rodata.size();
|
u64 rodataSize = rodata.size();
|
||||||
u64 dataSize = data.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
|
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);
|
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
|
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);
|
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(text.data(), constant::BaseAddr, textSize);
|
||||||
process->WriteMemory(rodata.data(), constant::BaseAddr + textSize, rodataSize);
|
process->WriteMemory(rodata.data(), constant::BaseAddr + textSize, rodataSize);
|
||||||
process->WriteMemory(data.data(), constant::BaseAddr + textSize + rodataSize, dataSize);
|
process->WriteMemory(data.data(), constant::BaseAddr + textSize + rodataSize, dataSize);
|
||||||
|
process->WriteMemory(patch.data(), constant::BaseAddr + textSize + rodataSize + dataSize + header.bssSize, patchSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,182 @@
|
|||||||
#include <linux/elf.h>
|
#include <linux/elf.h>
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "jvm.h"
|
#include "jvm.h"
|
||||||
|
#include "guest.h"
|
||||||
|
|
||||||
extern bool Halt;
|
extern bool Halt;
|
||||||
extern std::mutex jniMtx;
|
extern std::mutex jniMtx;
|
||||||
|
|
||||||
namespace skyline {
|
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<i32>(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<i32>(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 {
|
void NCE::ReadRegisters(user_pt_regs ®isters, pid_t pid) const {
|
||||||
iovec iov = {®isters, sizeof(registers)};
|
iovec iov = {®isters, sizeof(registers)};
|
||||||
long status = ptrace(PTRACE_GETREGSET, pid ? pid : currPid, NT_PRSTATUS, &iov);
|
long status = ptrace(PTRACE_GETREGSET, pid ? pid : currPid, NT_PRSTATUS, &iov);
|
||||||
@ -56,9 +227,11 @@ namespace skyline {
|
|||||||
SetRegister(static_cast<Xreg>(instr.value - (constant::SvcLast + 1)), state.thisThread->tls);
|
SetRegister(static_cast<Xreg>(instr.value - (constant::SvcLast + 1)), state.thisThread->tls);
|
||||||
} else if (instr.value == constant::BrkRdy)
|
} else if (instr.value == constant::BrkRdy)
|
||||||
continue;
|
continue;
|
||||||
else
|
else {
|
||||||
|
ProcessTrace();
|
||||||
throw exception("Received unhandled BRK: 0x{:X}", static_cast<u64>(instr.value));
|
throw exception("Received unhandled BRK: 0x{:X}", static_cast<u64>(instr.value));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
currRegs.pc += sizeof(u32);
|
currRegs.pc += sizeof(u32);
|
||||||
WriteRegisters(currRegs);
|
WriteRegisters(currRegs);
|
||||||
ResumeProcess();
|
ResumeProcess();
|
||||||
@ -181,12 +354,11 @@ namespace skyline {
|
|||||||
raw += fmt::format("{:08X}", instr);
|
raw += fmt::format("{:08X}", instr);
|
||||||
}
|
}
|
||||||
state.logger->Debug("Raw Instructions: 0x{}", raw);
|
state.logger->Debug("Raw Instructions: 0x{}", raw);
|
||||||
state.logger->Debug("CPU Context:");
|
std::string regStr;
|
||||||
state.logger->Debug("SP: 0x{:X}", regs.sp);
|
for (u16 index = 0; index < constant::NumRegs - 1; index+=2) {
|
||||||
state.logger->Debug("PSTATE: 0x{:X}", regs.pstate);
|
regStr += fmt::format("\nX{}: 0x{:X}, X{}: 0x{:X}", index, regs.regs[index], index+1, regs.regs[index+1]);
|
||||||
for (u16 index = 0; index < constant::NumRegs - 2; index++) {
|
|
||||||
state.logger->Debug("X{}: 0x{:X}", index, regs.regs[index]);
|
|
||||||
}
|
}
|
||||||
|
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) {
|
u64 NCE::GetRegister(Xreg regId, pid_t pid) {
|
||||||
@ -228,4 +400,68 @@ namespace skyline {
|
|||||||
registerMap.at(pid).pstate = value;
|
registerMap.at(pid).pstate = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<u32> NCE::PatchCode(std::vector<u8>& code, i64 offset) {
|
||||||
|
u32 *address = reinterpret_cast<u32 *>(code.data());
|
||||||
|
u32 *end = address + (code.size() / sizeof(u32));
|
||||||
|
i64 patchOffset = offset;
|
||||||
|
|
||||||
|
std::vector<u32> patch;
|
||||||
|
patch.resize((guest::saveCtxSize + guest::loadCtxSize) / sizeof(u32));
|
||||||
|
std::memcpy(patch.data(), reinterpret_cast<void*>(&guest::saveCtx), guest::saveCtxSize);
|
||||||
|
offset += guest::saveCtxSize;
|
||||||
|
|
||||||
|
std::memcpy(reinterpret_cast<u8*>(patch.data()) + guest::saveCtxSize,
|
||||||
|
reinterpret_cast<void*>(&guest::loadCtx), guest::loadCtxSize);
|
||||||
|
offset += guest::loadCtxSize;
|
||||||
|
|
||||||
|
while (address < end) {
|
||||||
|
auto instrSvc = reinterpret_cast<instr::Svc *>(address);
|
||||||
|
auto instrMrs = reinterpret_cast<instr::Mrs *>(address);
|
||||||
|
|
||||||
|
if (instrSvc->Verify()) {
|
||||||
|
instr::B bjunc(offset);
|
||||||
|
constexpr u32 strLr = 0xF81F0FFE; // STR LR, [SP, #-16]!
|
||||||
|
offset += sizeof(strLr);
|
||||||
|
instr::Brk brk(static_cast<u16>(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<u16>(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
#include "kernel/types/KSharedMemory.h"
|
#include "kernel/types/KSharedMemory.h"
|
||||||
|
|
||||||
namespace skyline {
|
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
|
* @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)
|
* @param pid The PID of the process (Defaults to currPid)
|
||||||
*/
|
*/
|
||||||
void SetRegister(Sreg regId, u32 value, pid_t pid = 0);
|
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<u32> PatchCode(std::vector<u8> &code, i64 offset);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user