aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Memory/MemoryManagementUnix.cs
blob: 9fe1aef0947ed9e08defc4b720023ccddcd14acc (plain) (blame)
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
using Mono.Unix.Native;
using System;

namespace ChocolArm64.Memory
{
    static class MemoryManagementUnix
    {
        public static IntPtr Allocate(ulong size)
        {
            ulong pageSize = (ulong)Syscall.sysconf(SysconfName._SC_PAGESIZE);

            const MmapProts prot = MmapProts.PROT_READ | MmapProts.PROT_WRITE;

            const MmapFlags flags = MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS;

            IntPtr ptr = Syscall.mmap(IntPtr.Zero, size + pageSize, prot, flags, -1, 0);

            if (ptr == IntPtr.Zero)
            {
                throw new OutOfMemoryException();
            }

            unsafe
            {
                ptr = new IntPtr(ptr.ToInt64() + (long)pageSize);

                *((ulong*)ptr - 1) = size;
            }

            return ptr;
        }

        public static bool Reprotect(IntPtr address, ulong size, Memory.MemoryProtection protection)
        {
            MmapProts prot = GetProtection(protection);

            return Syscall.mprotect(address, size, prot) == 0;
        }

        private static MmapProts GetProtection(Memory.MemoryProtection protection)
        {
            switch (protection)
            {
                case Memory.MemoryProtection.None:           return MmapProts.PROT_NONE;
                case Memory.MemoryProtection.Read:           return MmapProts.PROT_READ;
                case Memory.MemoryProtection.ReadAndWrite:   return MmapProts.PROT_READ | MmapProts.PROT_WRITE;
                case Memory.MemoryProtection.ReadAndExecute: return MmapProts.PROT_READ | MmapProts.PROT_EXEC;
                case Memory.MemoryProtection.Execute:        return MmapProts.PROT_EXEC;

                default: throw new ArgumentException($"Invalid permission \"{protection}\".");
            }
        }

        public static bool Free(IntPtr address)
        {
            ulong pageSize = (ulong)Syscall.sysconf(SysconfName._SC_PAGESIZE);

            ulong size;

            unsafe
            {
                size = *((ulong*)address - 1);

                address = new IntPtr(address.ToInt64() - (long)pageSize);
            }

            return Syscall.munmap(address, size + pageSize) == 0;
        }
    }
}