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
use crate::result::*;
use crate::sync;
use crate::svc;
use crate::mem::alloc;
#[derive(Copy, Clone)]
pub struct VirtualRegion {
pub start: usize,
pub end: usize
}
impl VirtualRegion {
pub const fn new() -> Self {
Self { start: 0, end: 0 }
}
pub const fn contains(&self, address: usize) -> bool {
(address >= self.start) && (address < self.end)
}
}
pub enum VirtualRegionType {
Stack,
Heap,
LegacyAlias
}
static mut G_STACK_REGION: VirtualRegion = VirtualRegion::new();
static mut G_HEAP_REGION: VirtualRegion = VirtualRegion::new();
static mut G_LEGACY_ALIAS_REGION: VirtualRegion = VirtualRegion::new();
static mut G_ADDRESS_SPACE: VirtualRegion = VirtualRegion::new();
static mut G_CURRENT_ADDRESS: usize = 0;
static mut G_LOCK: sync::Mutex = sync::Mutex::new(false);
pub fn get_address_space() -> VirtualRegion {
unsafe {
let _ = sync::ScopedLock::new(&mut G_LOCK);
G_ADDRESS_SPACE
}
}
pub fn get_stack_region() -> VirtualRegion {
unsafe {
let _ = sync::ScopedLock::new(&mut G_LOCK);
G_STACK_REGION
}
}
pub fn get_heap_region() -> VirtualRegion {
unsafe {
let _ = sync::ScopedLock::new(&mut G_LOCK);
G_HEAP_REGION
}
}
pub fn get_legacy_alias_region() -> VirtualRegion {
unsafe {
let _ = sync::ScopedLock::new(&mut G_LOCK);
G_LEGACY_ALIAS_REGION
}
}
fn read_region_info(region: &mut VirtualRegion, address_info_id: svc::InfoId, size_info_id: svc::InfoId) -> Result<()> {
let address = svc::get_info(address_info_id, svc::CURRENT_PROCESS_PSEUDO_HANDLE, 0)? as usize;
let size = svc::get_info(size_info_id, svc::CURRENT_PROCESS_PSEUDO_HANDLE, 0)? as usize;
region.start = address;
region.end = address + size;
Ok(())
}
pub fn initialize() -> Result<()> {
unsafe {
let _ = sync::ScopedLock::new(&mut G_LOCK);
read_region_info(&mut G_ADDRESS_SPACE, svc::InfoId::AslrRegionAddress, svc::InfoId::AslrRegionSize)?;
read_region_info(&mut G_STACK_REGION, svc::InfoId::StackRegionAddress, svc::InfoId::StackRegionSize)?;
read_region_info(&mut G_HEAP_REGION, svc::InfoId::HeapRegionAddress, svc::InfoId::HeapRegionSize)?;
read_region_info(&mut G_LEGACY_ALIAS_REGION, svc::InfoId::AliasRegionAddress, svc::InfoId::AliasRegionSize)?;
}
Ok(())
}
pub fn allocate(size: usize) -> Result<*mut u8> {
unsafe {
let _ = sync::ScopedLock::new(&mut G_LOCK);
let mut address = G_CURRENT_ADDRESS;
loop {
address += alloc::PAGE_ALIGNMENT;
if !G_ADDRESS_SPACE.contains(address) {
address = G_ADDRESS_SPACE.start;
}
let current_address = address + size;
let (memory_info, _) = svc::query_memory(address as *mut u8)?;
let info_address = memory_info.base_address as usize + memory_info.size as usize;
if memory_info.state != svc::MemoryState::Free {
address = info_address;
continue;
}
if current_address > info_address {
address = info_address;
continue;
}
let end = current_address - 1;
if G_STACK_REGION.contains(address) || G_STACK_REGION.contains(end) {
address = G_STACK_REGION.end;
continue;
}
if G_HEAP_REGION.contains(address) || G_HEAP_REGION.contains(end) {
address = G_HEAP_REGION.end;
continue;
}
if G_LEGACY_ALIAS_REGION.contains(address) || G_LEGACY_ALIAS_REGION.contains(end) {
address = G_LEGACY_ALIAS_REGION.end;
continue;
}
break;
}
G_CURRENT_ADDRESS = address + size;
Ok(address as *mut u8)
}
}