如何在 x64 中使用 StackAlloc?
How to use StackAlloc in x64?
我正在尝试在 X64
中的 DelphiXE7 中使用 Graphics32
中的 StackAlloc
,但是它因错误而崩溃。我尝试将 NOFRAME
添加到代码中,但也没有帮助。
First chance exception at [=5=]0000000013FF10. Exception class $C0000005 with message 'c0000005 ACCESS_VIOLATION'. Process Stack.exe (4536)
program Stack;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Classes;
function StackAlloc(Size: Integer): Pointer; register;
asm
{$IFDEF CPUX86}
POP ECX // return address
MOV EDX, ESP
ADD EAX, 3
AND EAX, not 3 // round up to keep ESP dword aligned
CMP EAX, 4092
JLE @@2
@@1:
SUB ESP, 4092
PUSH EAX // make sure we touch guard page, to grow stack
SUB EAX, 4096
JNS @@1
ADD EAX, 4096
@@2:
SUB ESP, EAX
MOV EAX, ESP // function result = low memory address of block
PUSH EDX // save original SP, for cleanup
MOV EDX, ESP
SUB EDX, 4
PUSH EDX // save current SP, for sanity check (sp = [sp])
PUSH ECX // return to caller
{$ELSE}
.NOFRAME
MOV RAX, RCX
POP R8 // return address
MOV RDX, RSP // original SP
ADD ECX, 15
AND ECX, NOT 15 // round up to keep SP dqword aligned
CMP ECX, 4092
JLE @@2
@@1:
SUB RSP, 4092
PUSH RCX // make sure we touch guard page, to grow stack
SUB ECX, 4096
JNS @@1
ADD ECX, 4096
@@2:
SUB RSP, RCX
MOV RAX, RSP // function result = low memory address of block
PUSH RDX // save original SP, for cleanup
MOV RDX, RSP
SUB RDX, 8
PUSH RDX // save current SP, for sanity check (sp = [sp])
{$ENDIF}
end;
{ StackFree pops the memory allocated by StackAlloc off the stack.
- Calling StackFree is optional - SP will be restored when the calling routine
exits, but it's a good idea to free the stack allocated memory ASAP anyway.
- StackFree must be called in the same stack context as StackAlloc - not in
a subroutine or finally block.
- Multiple StackFree calls must occur in reverse order of their corresponding
StackAlloc calls.
- Built-in sanity checks guarantee that an improper call to StackFree will not
corrupt the stack. Worst case is that the stack block is not released until
the calling routine exits. }
procedure StackFree(P: Pointer); register;
asm
{$IFDEF CPUX86}
POP ECX { return address }
MOV EDX, DWORD PTR [ESP]
SUB EAX, 8
CMP EDX, ESP { sanity check #1 (SP = [SP]) }
JNE @Exit
CMP EDX, EAX { sanity check #2 (P = this stack block) }
JNE @Exit
MOV ESP, DWORD PTR [ESP+4] { restore previous SP }
@Exit:
PUSH ECX { return to caller }
{$ELSE}
POP R8 { return address }
MOV RDX, QWORD PTR [RSP]
SUB RCX, 16
CMP RDX, RSP { sanity check #1 (SP = [SP]) }
JNE @Exit
CMP RDX, RCX { sanity check #2 (P = this stack block) }
JNE @Exit
MOV RSP, QWORD PTR [RSP + 8] { restore previous SP }
@Exit:
PUSH R8 { return to caller }
{$ENDIF}
end;
var
SL: ^TStringList;
begin
SL := StackAlloc(SizeOf(TStringList)); // Crashes here.
SL^ := TStringList.Create;
SL^.Add('sda');
FreeAndNil(SL^);
StackFree(sl);
Readln;
end.
您的 StackAlloc
版本在 x64 版本末尾缺少 PUSH R8
。
因此,return 地址不会放回堆栈。
我正在尝试在 X64
中的 DelphiXE7 中使用 Graphics32
中的 StackAlloc
,但是它因错误而崩溃。我尝试将 NOFRAME
添加到代码中,但也没有帮助。
First chance exception at [=5=]0000000013FF10. Exception class $C0000005 with message 'c0000005 ACCESS_VIOLATION'. Process Stack.exe (4536)
program Stack;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Classes;
function StackAlloc(Size: Integer): Pointer; register;
asm
{$IFDEF CPUX86}
POP ECX // return address
MOV EDX, ESP
ADD EAX, 3
AND EAX, not 3 // round up to keep ESP dword aligned
CMP EAX, 4092
JLE @@2
@@1:
SUB ESP, 4092
PUSH EAX // make sure we touch guard page, to grow stack
SUB EAX, 4096
JNS @@1
ADD EAX, 4096
@@2:
SUB ESP, EAX
MOV EAX, ESP // function result = low memory address of block
PUSH EDX // save original SP, for cleanup
MOV EDX, ESP
SUB EDX, 4
PUSH EDX // save current SP, for sanity check (sp = [sp])
PUSH ECX // return to caller
{$ELSE}
.NOFRAME
MOV RAX, RCX
POP R8 // return address
MOV RDX, RSP // original SP
ADD ECX, 15
AND ECX, NOT 15 // round up to keep SP dqword aligned
CMP ECX, 4092
JLE @@2
@@1:
SUB RSP, 4092
PUSH RCX // make sure we touch guard page, to grow stack
SUB ECX, 4096
JNS @@1
ADD ECX, 4096
@@2:
SUB RSP, RCX
MOV RAX, RSP // function result = low memory address of block
PUSH RDX // save original SP, for cleanup
MOV RDX, RSP
SUB RDX, 8
PUSH RDX // save current SP, for sanity check (sp = [sp])
{$ENDIF}
end;
{ StackFree pops the memory allocated by StackAlloc off the stack.
- Calling StackFree is optional - SP will be restored when the calling routine
exits, but it's a good idea to free the stack allocated memory ASAP anyway.
- StackFree must be called in the same stack context as StackAlloc - not in
a subroutine or finally block.
- Multiple StackFree calls must occur in reverse order of their corresponding
StackAlloc calls.
- Built-in sanity checks guarantee that an improper call to StackFree will not
corrupt the stack. Worst case is that the stack block is not released until
the calling routine exits. }
procedure StackFree(P: Pointer); register;
asm
{$IFDEF CPUX86}
POP ECX { return address }
MOV EDX, DWORD PTR [ESP]
SUB EAX, 8
CMP EDX, ESP { sanity check #1 (SP = [SP]) }
JNE @Exit
CMP EDX, EAX { sanity check #2 (P = this stack block) }
JNE @Exit
MOV ESP, DWORD PTR [ESP+4] { restore previous SP }
@Exit:
PUSH ECX { return to caller }
{$ELSE}
POP R8 { return address }
MOV RDX, QWORD PTR [RSP]
SUB RCX, 16
CMP RDX, RSP { sanity check #1 (SP = [SP]) }
JNE @Exit
CMP RDX, RCX { sanity check #2 (P = this stack block) }
JNE @Exit
MOV RSP, QWORD PTR [RSP + 8] { restore previous SP }
@Exit:
PUSH R8 { return to caller }
{$ENDIF}
end;
var
SL: ^TStringList;
begin
SL := StackAlloc(SizeOf(TStringList)); // Crashes here.
SL^ := TStringList.Create;
SL^.Add('sda');
FreeAndNil(SL^);
StackFree(sl);
Readln;
end.
您的 StackAlloc
版本在 x64 版本末尾缺少 PUSH R8
。
因此,return 地址不会放回堆栈。