Delphi 10.3.1 编译器生成的代码在编译为 64 位时会发出异常
Delphi 10.3.1 compiler generates code that issues an exception when compiled to 64 bits
以下代码仅在编译为 64 位时在 Delphi 10.3.1 中生成异常 (c0000005 ACCESS_VIOLATION)。
但是,相同的代码在 Delphi 10.3.1 中编译为 32 位时不会生成异常。此外,当编译为 32 位或 64 位时,它在 Delphi 10.2.3 中不会失败。
program CrashOn64;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TMyBaseClass = class
protected
procedure Setup(aParams: array of const); virtual;
public
end;
type
TMyWorkClass = class(TMyBaseClass)
protected
procedure DoSetup; virtual;
public
procedure Setup(aParams: array of const); override;
end;
{ TMyBaseClass }
procedure TMyBaseClass.Setup(aParams: array of const);
begin
end;
{ TMyWorkClass }
procedure TMyWorkClass.DoSetup;
begin
inherited;
end;
procedure TMyWorkClass.Setup(aParams: array of const);
begin
inherited;
DoSetup
end;
// main
var
myClass: TMyWorkClass;
begin
try
myClass:=TMyWorkClass.Create;
try
myClass.Setup([123]); // <-- Crash on Windows 64-bit
writeln('OK!')
finally
myClass.Free
end
except
on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;
readln; // Wait for Enter key
end.
问题似乎出在参数类型 array of const
上。如果我们将 array of const
更改为 array of integer
,该代码对于 64 位仍然会失败,因此新的 Delphi 编译器似乎存在参数数量未知的数组问题。我们找到了通过为 array of integer
创建类型来避免编译器错误的技巧,但是这个技巧不适用于我们需要的 array of const
.
这是根据 CPU 视图 :
在 Delphi 10.3.1 中为 64 位生成的汇编代码
CrashOn64.dpr.41: inherited;
0000000000428888 488B7528 mov rsi,[rbp+]
000000000042888C 488D7D20 lea rdi,[rbp+]
0000000000428890 48B9FFFFFFFFFFFFFF1F mov rcx,fffffffffffffff <<< What????????
000000000042889A F348A5 rep movsq <<< Crashes here.
000000000042889D A5 movsd
000000000042889E 66A5 movsw
00000000004288A0 A4 movsb
00000000004288A1 488B4D50 mov rcx,[rbp+]
00000000004288A5 488D5520 lea rdx,[rbp+]
00000000004288A9 448B4560 mov r8d,[rbp+]
00000000004288AD E8CEFEFFFF call TMyBaseClass.Setup
这是在 Delphi 10.2.3 中为相同功能生成的 64 位代码:
CrashOn64.dpr.41: inherited;
0000000000427329 488B4D50 mov rcx,[rbp+]
000000000042732D 488B5528 mov rdx,[rbp+]
0000000000427331 448B4560 mov r8d,[rbp+]
0000000000427335 E8E6FEFFFF call TMyBaseClass.Setup
这是 Delphi 10.3.1 中的 64 位编译器错误还是我们遗漏了什么?有什么解决方法吗?
这是一个错误,应该报告1。如问题中所述,每种类型的 open array.
都失败
解决方法是在方法中将数组定义为 const
:
procedure Setup(const aParams: array of const);
将开放数组声明为const
,通过引用传递数组,而没有常量,它将作为副本通过值传递。在这种情况下,Rio 版本失败。
1 报告为:Access violation when calling an inherited function with an open array parameter in Rio
此错误更为广泛,它不处理 procedures/functions 的 VARed 短字符串。
consider this SIMPLE code...
procedure Copyit(var s: shortstring); // VAR is important, pass by value ok
begin
debugform(s); // break point here
end;
procedure TForm1.Button1Click(Sender: TObject);
var
stuff: shortstring;
begin
stuff:= 'This is a demo string';
CopyIt(stuff);
end;
在 32 位模式下,它会生成您期望的代码。
在 64 位模式下,它会生成如上所示的代码,
将 rcx 设置为 $1FFFFFFFFFFFFFFFF 的巨大内存移动!!!
以下代码仅在编译为 64 位时在 Delphi 10.3.1 中生成异常 (c0000005 ACCESS_VIOLATION)。
但是,相同的代码在 Delphi 10.3.1 中编译为 32 位时不会生成异常。此外,当编译为 32 位或 64 位时,它在 Delphi 10.2.3 中不会失败。
program CrashOn64;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TMyBaseClass = class
protected
procedure Setup(aParams: array of const); virtual;
public
end;
type
TMyWorkClass = class(TMyBaseClass)
protected
procedure DoSetup; virtual;
public
procedure Setup(aParams: array of const); override;
end;
{ TMyBaseClass }
procedure TMyBaseClass.Setup(aParams: array of const);
begin
end;
{ TMyWorkClass }
procedure TMyWorkClass.DoSetup;
begin
inherited;
end;
procedure TMyWorkClass.Setup(aParams: array of const);
begin
inherited;
DoSetup
end;
// main
var
myClass: TMyWorkClass;
begin
try
myClass:=TMyWorkClass.Create;
try
myClass.Setup([123]); // <-- Crash on Windows 64-bit
writeln('OK!')
finally
myClass.Free
end
except
on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;
readln; // Wait for Enter key
end.
问题似乎出在参数类型 array of const
上。如果我们将 array of const
更改为 array of integer
,该代码对于 64 位仍然会失败,因此新的 Delphi 编译器似乎存在参数数量未知的数组问题。我们找到了通过为 array of integer
创建类型来避免编译器错误的技巧,但是这个技巧不适用于我们需要的 array of const
.
这是根据 CPU 视图 :
在 Delphi 10.3.1 中为 64 位生成的汇编代码CrashOn64.dpr.41: inherited;
0000000000428888 488B7528 mov rsi,[rbp+]
000000000042888C 488D7D20 lea rdi,[rbp+]
0000000000428890 48B9FFFFFFFFFFFFFF1F mov rcx,fffffffffffffff <<< What????????
000000000042889A F348A5 rep movsq <<< Crashes here.
000000000042889D A5 movsd
000000000042889E 66A5 movsw
00000000004288A0 A4 movsb
00000000004288A1 488B4D50 mov rcx,[rbp+]
00000000004288A5 488D5520 lea rdx,[rbp+]
00000000004288A9 448B4560 mov r8d,[rbp+]
00000000004288AD E8CEFEFFFF call TMyBaseClass.Setup
这是在 Delphi 10.2.3 中为相同功能生成的 64 位代码:
CrashOn64.dpr.41: inherited;
0000000000427329 488B4D50 mov rcx,[rbp+]
000000000042732D 488B5528 mov rdx,[rbp+]
0000000000427331 448B4560 mov r8d,[rbp+]
0000000000427335 E8E6FEFFFF call TMyBaseClass.Setup
这是 Delphi 10.3.1 中的 64 位编译器错误还是我们遗漏了什么?有什么解决方法吗?
这是一个错误,应该报告1。如问题中所述,每种类型的 open array.
都失败解决方法是在方法中将数组定义为 const
:
procedure Setup(const aParams: array of const);
将开放数组声明为const
,通过引用传递数组,而没有常量,它将作为副本通过值传递。在这种情况下,Rio 版本失败。
1 报告为:Access violation when calling an inherited function with an open array parameter in Rio
此错误更为广泛,它不处理 procedures/functions 的 VARed 短字符串。
consider this SIMPLE code...
procedure Copyit(var s: shortstring); // VAR is important, pass by value ok
begin
debugform(s); // break point here
end;
procedure TForm1.Button1Click(Sender: TObject);
var
stuff: shortstring;
begin
stuff:= 'This is a demo string';
CopyIt(stuff);
end;
在 32 位模式下,它会生成您期望的代码。 在 64 位模式下,它会生成如上所示的代码, 将 rcx 设置为 $1FFFFFFFFFFFFFFFF 的巨大内存移动!!!