1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
use crate::libc::{c_void, size_t, strlen};
use crate::error::{SwitchResult, Error, ErrorKind};
use crate::hooks::{Region, getRegionAddress};
#[cfg(not(feature = "std"))]
use alloc::string::String;
extern "C" {
pub fn sky_memcpy(dst: *const c_void, src: *const c_void, size: size_t) -> SwitchResult;
}
pub unsafe fn patch_str(offset: usize, string: &str) -> Result<(), Error> {
let text_ptr = getRegionAddress(Region::Text) as *const u8;
let str_ptr = text_ptr.offset(offset as isize);
let len = strlen(str_ptr);
if len < string.len() {
return Err(Error::Skyline { kind: ErrorKind::StringTooLong })
}
let string = String::from(string) + "\0";
sky_memcpy(str_ptr as _, string.as_ptr() as _, string.len()).ok()?;
Ok(())
}
pub unsafe fn patch_data<T: Sized + Copy>(offset: usize, val: &T) -> Result<(), Error> {
let text_ptr = getRegionAddress(Region::Text) as *const u8;
patch_data_from_text(text_ptr, offset, val)
}
pub unsafe fn patch_data_from_text<T: Sized + Copy>(text_offset: *const u8, offset: usize, val: &T) -> Result<(), Error> {
let text_ptr = text_offset;
let data_ptr = text_ptr.offset(offset as isize);
sky_memcpy(data_ptr as _, val as *const _ as _, core::mem::size_of::<T>()).ok()?;
Ok(())
}
enum BranchType {
Branch,
BranchLink
}
pub struct BranchBuilder {
branch_type: BranchType,
offset: Option<usize>,
ptr: Option<*const ()>,
}
impl BranchBuilder {
fn internal_new() -> Self {
Self {
branch_type: BranchType::Branch,
offset: None,
ptr: None
}
}
pub fn branch() -> Self {
Self {
branch_type: BranchType::Branch,
..BranchBuilder::internal_new()
}
}
pub fn branch_link() -> Self {
Self {
branch_type: BranchType::BranchLink,
..BranchBuilder::internal_new()
}
}
pub fn branch_offset(mut self, offset: usize) -> Self {
self.offset = Some(offset);
self
}
pub fn branch_to_offset(mut self, offset: usize) -> Self {
unsafe {
self.ptr = Some(
(getRegionAddress(Region::Text) as *const u8)
.offset(offset as isize) as *const ()
);
}
self
}
pub fn branch_to_ptr<T>(mut self, ptr: *const T) -> Self {
self.ptr = Some(ptr as *const ());
self
}
#[track_caller]
pub fn replace(self) {
let offset = match self.offset {
Some(offset) => offset,
None => panic!("Offset is required to replace")
};
let instr_magic = match self.branch_type {
BranchType::Branch => 0b000101,
BranchType::BranchLink => 0b100101,
} << 26;
let branch_ptr = unsafe {
(getRegionAddress(Region::Text) as *const u8).offset(offset as isize)
} as isize;
let branch_to_ptr = match self.ptr {
Some(ptr) => ptr as *const u8,
None => panic!("Either branch_to_ptr or branch_to_offset is required to replace")
} as isize;
let imm26 = match (branch_to_ptr - branch_ptr) / 4 {
distance if within_branch_range(distance)
=> ((branch_to_ptr - branch_ptr) as usize) >> 2,
_ => panic!("Branch target is out of range, must be within +/- 128 MiB")
};
let instr: u64 = (instr_magic | imm26) as u64;
unsafe {
if let Err(err) = patch_data(offset, &instr) {
panic!("Failed to patch data, error: {:?}", err)
}
}
}
}
#[allow(non_upper_case_globals)]
const MiB: isize = 0x100000;
const BRANCH_RANGE: isize = 128 * MiB;
fn within_branch_range(distance: isize) -> bool {
(-BRANCH_RANGE..BRANCH_RANGE).contains(&distance)
}