mirror of https://github.com/red-prig/fpPS4.git
537 lines
12 KiB
Plaintext
537 lines
12 KiB
Plaintext
unit md_map;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
sysutils,
|
|
ntapi,
|
|
vm,
|
|
windows;
|
|
|
|
const
|
|
MD_PAGE_SHIFT = 12;
|
|
MD_PAGE_SIZE = 4*1024;
|
|
MD_PAGE_MASK = MD_PAGE_SIZE-1;
|
|
MD_ALLOC_GRANULARITY= 64*1024;
|
|
|
|
const
|
|
VM_RW =VM_PROT_READ or VM_PROT_WRITE;
|
|
VM_RWX=VM_PROT_READ or VM_PROT_WRITE or VM_PROT_EXECUTE;
|
|
|
|
function md_reserve_ex(hProcess:THandle;var base:Pointer;size:QWORD):Integer;
|
|
function md_reserve_ex(var base:Pointer;size:QWORD):Integer;
|
|
|
|
function md_unmap_ex(base:Pointer;size:QWORD):Integer;
|
|
|
|
function md_split (base:Pointer;size:QWORD):Integer;
|
|
function md_union (base:Pointer;size:QWORD):Integer;
|
|
|
|
function md_memfd_create(var hMem:THandle;size:QWORD;maxprot:Byte):Integer;
|
|
function md_memfd_open (var hMem:THandle;hFile:THandle;maxprot:Byte):Integer;
|
|
function md_memfd_close (hMem:THandle):Integer;
|
|
|
|
function md_protect(hProcess:THandle;base:Pointer;size:QWORD;prot:Integer):Integer;
|
|
function md_protect(base:Pointer;size:QWORD;prot:Integer):Integer;
|
|
|
|
function md_dontneed(base:Pointer;size:QWORD):Integer;
|
|
function md_activate(base:Pointer;size:QWORD):Integer;
|
|
|
|
function md_mmap (hProcess:THandle;var base:Pointer;size:QWORD;prot:Integer):Integer;
|
|
function md_unmap (hProcess:THandle;base:Pointer;size:QWORD):Integer;
|
|
|
|
function md_mmap (var base:Pointer;size:QWORD;prot:Integer):Integer;
|
|
function md_unmap (base:Pointer;size:QWORD):Integer;
|
|
|
|
function md_file_mmap (handle:THandle;var base:Pointer;offset,size:QWORD;prot:Integer):Integer;
|
|
function md_file_unmap(base:Pointer;size:QWORD):Integer;
|
|
|
|
function md_file_mmap_ex (handle:THandle;base:Pointer;offset,size:QWORD;prot:Integer):Integer;
|
|
function md_file_unmap_ex(base:Pointer):Integer;
|
|
|
|
const
|
|
ICACHE=1; //Flush the instruction cache.
|
|
DCACHE=2; //Write back to memory and invalidate the affected valid cache lines.
|
|
BCACHE=ICACHE or DCACHE;
|
|
|
|
procedure md_cacheflush(addr:Pointer;nbytes,cache:Integer);
|
|
|
|
Function md_create_swap_file(const FNAME:RawByteString;SIZE:QWORD;Var FD:THandle):DWORD;
|
|
|
|
implementation
|
|
|
|
const
|
|
MD_PROT_NONE=PAGE_NOACCESS;
|
|
MD_PROT_R =PAGE_READONLY;
|
|
MD_PROT_W =PAGE_READWRITE;
|
|
MD_PROT_RW =PAGE_READWRITE;
|
|
MD_PROT_X =PAGE_EXECUTE;
|
|
MD_PROT_RX =PAGE_EXECUTE_READ;
|
|
MD_PROT_WX =PAGE_EXECUTE_READWRITE;
|
|
MD_PROT_RWX =PAGE_EXECUTE_READWRITE;
|
|
|
|
wprots:array[0..7] of Byte=(
|
|
MD_PROT_NONE,//___
|
|
MD_PROT_R ,//__R
|
|
MD_PROT_W ,//_W_
|
|
MD_PROT_RW ,//_WR
|
|
MD_PROT_X ,//X__
|
|
MD_PROT_RX ,//X_R
|
|
MD_PROT_WX ,//XW_
|
|
MD_PROT_RWX //XWR
|
|
);
|
|
|
|
waccess:array[0..7] of Byte=(
|
|
SECTION_QUERY, //___
|
|
SECTION_QUERY or SECTION_MAP_READ, //__R
|
|
SECTION_QUERY or SECTION_MAP_WRITE, //_W_
|
|
SECTION_QUERY or SECTION_MAP_WRITE or SECTION_MAP_READ, //_WR
|
|
SECTION_QUERY or SECTION_MAP_EXECUTE, //X__
|
|
SECTION_QUERY or SECTION_MAP_EXECUTE or SECTION_MAP_READ, //X_R
|
|
SECTION_QUERY or SECTION_MAP_EXECUTE or SECTION_MAP_WRITE, //XW_
|
|
SECTION_QUERY or SECTION_MAP_EXECUTE or SECTION_MAP_WRITE or SECTION_MAP_READ //XWR
|
|
);
|
|
|
|
function md_alloc_page(x:Pointer):Pointer; inline;
|
|
begin
|
|
Result:=Pointer(QWORD(x) and (not (MD_ALLOC_GRANULARITY-1)));
|
|
end;
|
|
|
|
function md_dw_page(x:QWORD):QWORD; inline;
|
|
begin
|
|
Result:=x and (not (MD_PAGE_SIZE-1));
|
|
end;
|
|
|
|
function md_up_page(x:QWORD):QWORD; inline;
|
|
begin
|
|
Result:=(x+(MD_PAGE_SIZE-1)) and (not (MD_PAGE_SIZE-1));
|
|
end;
|
|
|
|
function md_reserve_ex(hProcess:THandle;var base:Pointer;size:QWORD):Integer;
|
|
var
|
|
info:TMemoryBasicInformation;
|
|
len:ULONG_PTR;
|
|
begin
|
|
base:=md_alloc_page(base);
|
|
size:=md_up_page(size);
|
|
|
|
if (base<>nil) then
|
|
begin
|
|
len:=0;
|
|
Result:=NtQueryVirtualMemory(
|
|
hProcess,
|
|
base,
|
|
0,
|
|
@info,
|
|
sizeof(info),
|
|
@len);
|
|
|
|
//Writeln('NtQueryVirtualMemory:','0x',HexStr(Result,8));
|
|
|
|
if (Result=0) then
|
|
begin
|
|
{
|
|
Writeln('Query:','0x',HexStr(info.AllocationBase),'..',
|
|
'0x',HexStr(info.BaseAddress),'..',
|
|
'0x',HexStr(info.BaseAddress+info.RegionSize),':',
|
|
'0x',HexStr(info.RegionSize,11),' ',
|
|
info.State,' ',
|
|
info.Protect,' ',
|
|
info._Type
|
|
);
|
|
}
|
|
if (info.State=MEM_RESERVE) then
|
|
begin
|
|
if (base>=info.BaseAddress) and
|
|
((base+size)<=(info.BaseAddress+info.RegionSize)) then
|
|
begin
|
|
base:=info.AllocationBase;
|
|
Exit(0);
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
Result:=NtAllocateVirtualMemoryEx(
|
|
hProcess,
|
|
@base,
|
|
@size,
|
|
MEM_RESERVE or MEM_RESERVE_PLACEHOLDER,
|
|
PAGE_NOACCESS,
|
|
nil,
|
|
0
|
|
);
|
|
|
|
end;
|
|
|
|
function md_reserve_ex(var base:Pointer;size:QWORD):Integer;
|
|
begin
|
|
Result:=md_reserve_ex(NtCurrentProcess,base,size);
|
|
end;
|
|
|
|
function md_unmap_ex(base:Pointer;size:QWORD):Integer;
|
|
var
|
|
pend:Pointer;
|
|
|
|
addr,prev:Pointer;
|
|
info:TMemoryBasicInformation;
|
|
len:ULONG_PTR;
|
|
begin
|
|
pend:=base+size;
|
|
addr:=base;
|
|
|
|
repeat
|
|
|
|
len:=0;
|
|
NtQueryVirtualMemory(
|
|
NtCurrentProcess,
|
|
addr,
|
|
0,
|
|
@info,
|
|
sizeof(info),
|
|
@len);
|
|
if (len=0) then Break;
|
|
|
|
if (info.State<>MEM_FREE) then
|
|
begin
|
|
//unmap
|
|
Result:=NtUnmapViewOfSectionEx(NtCurrentProcess,addr,MEM_PRESERVE_PLACEHOLDER);
|
|
if (Result<>0) then Exit;
|
|
end;
|
|
|
|
prev:=addr;
|
|
addr:=addr+Info.RegionSize;
|
|
|
|
if (addr>=pend) then Break;
|
|
|
|
until (prev>=addr);
|
|
|
|
//union
|
|
Result:=NtFreeVirtualMemory(
|
|
NtCurrentProcess,
|
|
@base,
|
|
@size,
|
|
MEM_RELEASE or MEM_COALESCE_PLACEHOLDERS
|
|
);
|
|
//ignore errors
|
|
|
|
//free
|
|
size:=0;
|
|
Result:=NtFreeVirtualMemory(
|
|
NtCurrentProcess,
|
|
@base,
|
|
@size,
|
|
MEM_RELEASE
|
|
);
|
|
end;
|
|
|
|
function md_split(base:Pointer;size:QWORD):Integer;
|
|
begin
|
|
Result:=NtFreeVirtualMemory(
|
|
NtCurrentProcess,
|
|
@base,
|
|
@size,
|
|
MEM_RELEASE or MEM_PRESERVE_PLACEHOLDER
|
|
);
|
|
end;
|
|
|
|
function md_union(base:Pointer;size:QWORD):Integer;
|
|
begin
|
|
Result:=NtFreeVirtualMemory(
|
|
NtCurrentProcess,
|
|
@base,
|
|
@size,
|
|
MEM_RELEASE or MEM_COALESCE_PLACEHOLDERS
|
|
);
|
|
end;
|
|
|
|
function md_memfd_create(var hMem:THandle;size:QWORD;maxprot:Byte):Integer;
|
|
var
|
|
Access:DWORD;
|
|
prot:DWORD;
|
|
begin
|
|
Access:=waccess[maxprot and VM_RWX];
|
|
prot :=wprots [maxprot and VM_RWX];
|
|
|
|
hMem:=0;
|
|
Result:=NtCreateSectionEx(
|
|
@hMem,
|
|
Access,
|
|
nil,
|
|
@size,
|
|
prot,
|
|
SEC_COMMIT,
|
|
THandle(0),
|
|
nil,
|
|
0
|
|
);
|
|
end;
|
|
|
|
function md_memfd_open(var hMem:THandle;hFile:THandle;maxprot:Byte):Integer;
|
|
var
|
|
Access:DWORD;
|
|
prot:DWORD;
|
|
size:QWORD;
|
|
begin
|
|
Access:=waccess[maxprot and VM_RWX];
|
|
prot :=wprots [maxprot and VM_RWX];
|
|
|
|
size:=0;
|
|
hMem:=0;
|
|
Result:=NtCreateSectionEx(
|
|
@hMem,
|
|
Access,
|
|
nil,
|
|
@size,
|
|
prot,
|
|
SEC_COMMIT,
|
|
hFile,
|
|
nil,
|
|
0
|
|
);
|
|
end;
|
|
|
|
function md_memfd_close(hMem:THandle):Integer;
|
|
begin
|
|
Result:=NtClose(hMem);
|
|
end;
|
|
|
|
function md_protect(hProcess:THandle;base:Pointer;size:QWORD;prot:Integer):Integer;
|
|
var
|
|
old:Integer;
|
|
begin
|
|
prot:=wprots[prot and VM_RWX];
|
|
|
|
old:=0;
|
|
Result:=NtProtectVirtualMemory(
|
|
hProcess,
|
|
@base,
|
|
@size,
|
|
prot,
|
|
@old
|
|
);
|
|
end;
|
|
|
|
function md_protect(base:Pointer;size:QWORD;prot:Integer):Integer;
|
|
begin
|
|
Result:=md_protect(NtCurrentProcess,base,size,prot);
|
|
end;
|
|
|
|
function md_dontneed(base:Pointer;size:QWORD):Integer;
|
|
begin
|
|
Result:=NtAllocateVirtualMemory(
|
|
NtCurrentProcess,
|
|
@base,
|
|
0,
|
|
@size,
|
|
MEM_RESET,
|
|
PAGE_NOACCESS
|
|
);
|
|
end;
|
|
|
|
function md_activate(base:Pointer;size:QWORD):Integer;
|
|
begin
|
|
Result:=NtAllocateVirtualMemory(
|
|
NtCurrentProcess,
|
|
@base,
|
|
0,
|
|
@size,
|
|
MEM_RESET_UNDO,
|
|
PAGE_NOACCESS
|
|
);
|
|
end;
|
|
|
|
function md_mmap(hProcess:THandle;var base:Pointer;size:QWORD;prot:Integer):Integer;
|
|
begin
|
|
prot:=wprots[prot and VM_RWX];
|
|
|
|
base:=md_alloc_page(base);
|
|
size:=md_up_page(size);
|
|
|
|
Result:=NtAllocateVirtualMemory(
|
|
hProcess,
|
|
@base,
|
|
0,
|
|
@size,
|
|
MEM_COMMIT or MEM_RESERVE,
|
|
prot
|
|
);
|
|
end;
|
|
|
|
function md_unmap(hProcess:THandle;base:Pointer;size:QWORD):Integer;
|
|
begin
|
|
base:=md_alloc_page(base);
|
|
size:=0;
|
|
|
|
Result:=NtFreeVirtualMemory(
|
|
hProcess,
|
|
@base,
|
|
@size,
|
|
MEM_RELEASE
|
|
);
|
|
end;
|
|
|
|
function md_mmap(var base:Pointer;size:QWORD;prot:Integer):Integer;
|
|
begin
|
|
Result:=md_mmap(NtCurrentProcess,base,size,prot);
|
|
end;
|
|
|
|
function md_unmap(base:Pointer;size:QWORD):Integer;
|
|
begin
|
|
Result:=md_unmap(NtCurrentProcess,base,size);
|
|
end;
|
|
|
|
function md_file_mmap(handle:THandle;var base:Pointer;offset,size:QWORD;prot:Integer):Integer;
|
|
var
|
|
CommitSize:ULONG_PTR;
|
|
SectionOffset:ULONG_PTR;
|
|
begin
|
|
prot:=wprots[prot and VM_RWX];
|
|
|
|
base:=md_alloc_page(base);
|
|
|
|
CommitSize:=size;
|
|
SectionOffset:=offset and (not (MD_ALLOC_GRANULARITY-1));
|
|
|
|
Result:=NtMapViewOfSection(handle,
|
|
NtCurrentProcess,
|
|
@base,
|
|
0,
|
|
CommitSize,
|
|
@SectionOffset,
|
|
@CommitSize,
|
|
ViewUnmap,
|
|
0,
|
|
prot
|
|
);
|
|
end;
|
|
|
|
function md_file_unmap(base:Pointer;size:QWORD):Integer;
|
|
begin
|
|
base:=md_alloc_page(base);
|
|
|
|
Result:=NtUnmapViewOfSection(NtCurrentProcess,base);
|
|
end;
|
|
|
|
//
|
|
|
|
function md_file_mmap_ex(handle:THandle;base:Pointer;offset,size:QWORD;prot:Integer):Integer;
|
|
begin
|
|
prot:=wprots[prot and VM_RWX];
|
|
|
|
Result:=NtMapViewOfSectionEx(
|
|
handle,
|
|
NtCurrentProcess,
|
|
@base,
|
|
@offset,
|
|
@size,
|
|
MEM_REPLACE_PLACEHOLDER,
|
|
prot,
|
|
nil,
|
|
0
|
|
);
|
|
end;
|
|
|
|
function md_file_unmap_ex(base:Pointer):Integer;
|
|
begin
|
|
Result:=NtUnmapViewOfSectionEx(NtCurrentProcess,base,MEM_PRESERVE_PLACEHOLDER);
|
|
end;
|
|
|
|
//
|
|
|
|
procedure md_cacheflush(addr:Pointer;nbytes,cache:Integer);
|
|
begin
|
|
if ((cache and ICACHE)<>0) then
|
|
begin
|
|
FlushInstructionCache(NtCurrentProcess,addr,nbytes);
|
|
end;
|
|
if ((cache and DCACHE)<>0) then
|
|
begin
|
|
FlushViewOfFile(addr,nbytes);
|
|
end;
|
|
end;
|
|
|
|
Function NtTruncate(FD:THandle;SIZE:QWORD):DWORD; inline;
|
|
var
|
|
BLK:IO_STATUS_BLOCK;
|
|
begin
|
|
Result:=NtSetInformationFile(
|
|
FD,
|
|
@BLK,
|
|
@SIZE,
|
|
SizeOf(Int64),
|
|
FileEndOfFileInformation);
|
|
|
|
if (Result<>0) then
|
|
begin
|
|
Result:=NtSetInformationFile(
|
|
FD,
|
|
@BLK,
|
|
@SIZE,
|
|
SizeOf(Int64),
|
|
FileAllocationInformation);
|
|
end;
|
|
end;
|
|
|
|
Function md_create_swap_file(const FNAME:RawByteString;SIZE:QWORD;Var FD:THandle):DWORD;
|
|
var
|
|
W:WideString;
|
|
|
|
OBJ :OBJECT_ATTRIBUTES;
|
|
UPATH:UNICODE_STRING;
|
|
BLK :IO_STATUS_BLOCK;
|
|
|
|
begin
|
|
W:=UTF8Decode(FNAME);
|
|
W:='\??\'+W;
|
|
|
|
OBJ:=Default(OBJECT_ATTRIBUTES);
|
|
OBJ.Length :=SizeOf(OBJECT_ATTRIBUTES);
|
|
OBJ.ObjectName:=@UPATH;
|
|
|
|
UPATH:=Default(UNICODE_STRING);
|
|
UPATH.Length :=strlen(PWideChar(w))*SizeOf(WideChar);
|
|
UPATH.MaximumLength:=UPATH.Length+SizeOf(WideChar);
|
|
UPATH.Buffer :=PWideChar(w);
|
|
|
|
BLK:=Default(IO_STATUS_BLOCK);
|
|
FD:=0;
|
|
|
|
Result:=NtCreateFile(@FD,
|
|
FILE_READ_DATA or
|
|
FILE_WRITE_DATA or
|
|
FILE_APPEND_DATA or
|
|
FILE_READ_ATTRIBUTES or
|
|
FILE_WRITE_ATTRIBUTES or
|
|
FILE_CAN_DELETE or
|
|
SYNCHRONIZE,
|
|
@OBJ,
|
|
@BLK,
|
|
nil,
|
|
FILE_ATTRIBUTE_TEMPORARY,
|
|
0,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_SYNCHRONOUS_IO_NONALERT or
|
|
FILE_OPEN_REPARSE_POINT or
|
|
FILE_NON_DIRECTORY_FILE or
|
|
FILE_DELETE_ON_CLOSE,
|
|
nil,
|
|
0);
|
|
|
|
if (Result<>0) then Exit;
|
|
|
|
Result:=NtTruncate(FD,SIZE);
|
|
|
|
if (Result<>0) then
|
|
begin
|
|
NtClose(FD);
|
|
FD:=0;
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|