I have made some very simple code in Pascal that is getting me this error:
Project BugFixing.exe raised exception class EAccessViolation with message 'Access violation at address 0040F1EE in module 'BugFixing.exe'. Write of address 00000004'.
The program consists of 2 modules:
BugFixing.dpr:
program BugFixing;
{$APPTYPE CONSOLE}
uses
SysUtils, uLinearProgrammingMainLogic in 'uLinearProgrammingMainLogic.pas', math;
var
MinOrMax : integer ;
Question : TQuestion ;
begin
try
Randomize ;
MinOrMax := RandomRange(0,2) ;
Question.SetMaximiseQuestion(MinOrMax);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
And uLinearProgrammingMainLogic.pas:
unit uLinearProgrammingMainLogic;
interface
uses sysUtils, math ;
type
TQuestion = class
private
MaximiseQuestion : boolean ;
public
procedure SetMaximiseQuestion (MinOrMax : integer) ;
end;
implementation
procedure TQuestion.SetMaximiseQuestion(MinOrMax : integer);
begin
if MinOrMax = 0 then
MaximiseQuestion := true
else
MaximiseQuestion := false ;
end;
end.
If anyone could explain to me why this is creating an access violation, that'd be appreciated. Thanks in advance. :)
A class must always be instantiated (TClassType.create) before use. The only exception to that are class/static methods, but you don't declare them that way (and that is not basic usage anyway)
Related
I have this code (that runs under iOS with Delphi Tokyo):
procedure TMainForm.Button1Click(Sender: TObject);
var aData: NSData;
begin
try
try
aData := nil;
finally
// this line triggers an exception
aData.release;
end;
except
on E: Exception do begin
exit;
end;
end;
end;
Normally the exception should be caught in the except end block, but in this case it is not caught by the handler and it is propagated to the Application.OnException handler.
Access violation at address 0000000100EE9A8C, accessing address
0000000000000000
Did I miss something?
This is a bug (actually, a feature) on iOS and Android platforms (possibly on others with LLVM backend - though they are not explicitly documented).
Core issue is that exception caused by virtual method call on nil reference constitutes hardware exception that is not captured by nearest exception handler and it is propagated to the next exception handler (in this case to Application exception handler).
Use a Function Call in a try-except Block to Prevent Uncaught Hardware Exceptions
With compilers for iOS devices, except blocks can catch a hardware
exception only if the try block contains a method or function call.
This is a difference related to the LLVM backend of the compiler,
which cannot return if no method/function is called in the try block.
The simplest code that exhibits the issue on iOS and Android platform is:
var
aData: IInterface;
begin
try
aData._Release;
except
end;
end;
Executing above code on Windows platform works as expected and the exception is caught by exception handler. There is no nil assignment in above code, because aData is interface reference and they are automatically nilled by compiler on all platforms. Adding nil assignment is redundant and does not change the outcome.
To show that exceptions are caused by virtual method calls
type
IFoo = interface
procedure Foo;
end;
TFoo = class(TInterfacedObject, IFoo)
public
procedure Foo; virtual;
end;
procedure TFoo.Foo;
var
x, y: integer;
begin
y := 0;
// division by zero causes exception here
x := 5 div y;
end;
In all following code variants, exception escapes exception handler.
var
aData: IFoo;
begin
try
aData.Foo;
except
end;
end;
var
aData: TFoo;
begin
try
aData.Foo;
except
end;
end;
Even if we change Foo method implementation and remove all code from it, it will still cause escaping exception.
If we change Foo declaration from virtual to static, exception caused by division to zero will be properly caught because call to static methods on nil references is allowed and call itself does not throw any exceptions - thus constitutes function call mentioned in documentation.
type
TFoo = class(TInterfacedObject, IFoo)
public
procedure Foo;
end;
TFoo = class(TObject)
public
procedure Foo;
end;
Another static method variant that also causes exception that is properly handled is declaring x as TFoo class field and accessing that field in Foo method.
TFoo = class(TObject)
public
x: Integer;
procedure Foo;
end;
procedure TFoo.Foo;
var
x: integer;
begin
x := 5;
end;
Back to the original question that involved NSData reference. NSData is Objective-C class and those are represented as interfaces in Delphi.
// root interface declaration for all Objective-C classes and protocols
IObjectiveC = interface(IInterface)
[IID_IObjectiveC_Name]
end;
Since calling methods on interface reference is always virtual call that goes through VMT table, in this case behaves in similar manner (exhibits same issue) as virtual method call invoked directly on object reference. The call itself throws an exception and is not caught by nearest exception handler.
Workarounds:
One of the workarounds in code where reference might be nil is checking it for nil before calling virtual method on it. If needed, in case of nil reference we can also raise regular exception that will be properly caught by enclosing exception handler.
var
aData: NSData;
begin
try
if Assigned(aData) then
aData.release
else
raise Exception.Create('NSData is nil');
except
end;
end;
Another workaround as mentioned in documentation is to put code in additional function (method)
procedure SafeCall(const aData: NSData);
begin
aData.release;
end;
var
aData: NSData;
begin
try
SafeCall(aData);
except
end;
end;
I'm attempting to create a class in Pascal, I am a bit confused about the declaration and syntax. the main thing is an error I'm getting "Forward declaration not solved Tetromino.Rotate(LongInt)", I read that I need to declare my procedure in the implementation section but I'm not sure where I'm meant to be putting that. also if you notice anything else wrong with my class declaration please tell me.
program Tetris;
{$MODE OBJFPC}
uses crt, sysutils;
type
Tetromino = class
private
TempFace : array [0..15] of char;
public
Face : array[0..15] of char;
//constructor create(); (idk what this is but read somewhere that you need it)
procedure Rotate(rotation : integer);
end;
var
a,b,c,d,e,f,g : tetromino;
begin
ReadKey();
end.
In a program module there is no need for division into interface and implementation. Therefore the error description (to implement the procedure in the implementation section) is a little bit misleading. Still, it indicates that the implementation of the Rotate() procedure is missing.
So, the error is that you have declared a procedure in the Tetromino class, but the implementation of that procedure is missing. You need to implement it somewhere between the class declaration and the begin .. end block of the program.
In a unit module, which has named sections: interface and implementation, you declare classes in the interface section (if those classes are to be accessible from other modules) and implement them in the implementation section.
In the following I outline what you need to do in your program, including the constructor for Tetromino
program Tetris;
{$MODE OBJFPC}
uses crt, sysutils;
type
Tetromino = class
private
TempFace : array [0..15] of char;
public
Face : array[0..15] of char;
constructor create(); (idk what this is but read somewhere that you need it)
procedure Rotate(rotation : integer);
end;
var
a,b,c,d,e,f,g : tetromino;
constructor Tetromino.create;
begin
// constructor (automatically) aquires a block of memory
// to hold members of the class
// to do: initialize member fields of the instance
end;
procedure Tetromino.Rotate(rotation: Integer);
begin
// implementation of the Rotate() method
end;
begin
ReadKey();
end.
Why doesn't this code result in a compiler error? I would have expected error for example 'ambiguous call to "CallMe"'. Is this a bug in the compiler or in the language? This can worked around by using the unit name and a dot in front of the function call but this not shield user code and library code against name collisions. You think that your code did something but it did something else and that's bad.
uses
Unit2, Unit3;
{$R *.lfm}
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(CallMe(5)));
end;
unit Unit2;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
function CallMe(A: Integer) : Integer;
implementation
function CallMe(A: Integer) : Integer;
begin
Result := A * 2;
end;
end.
unit Unit3;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
function CallMe(A: Integer) : Integer;
implementation
function CallMe(A: Integer) : Integer;
begin
Result := A * -1;
end;
end.
From documentation:
If two units declare a variable, constant, type, procedure, or function with the same name, the compiler uses the one from the unit listed last in the uses clause. (To access the identifier from the other unit, you would have to add a qualifier: UnitName.Identifier.)
As said it is by design, the compiler loads symbols from units using a stack based approach, and parses through the stack from last loaded to first loaded to search for a symbol. Preprocessor state is directly merged into the global state though.
Cross unit overloading is an exception though. If you mark both functions with overload; directive, you get an error (bla was the name of the function in the test)
[dcc32 Error] test.dpr: E2251 Ambiguous overloaded call to 'bla'
Unit1.pas(8): Related method: procedure bla;
Unit2.pas(8): Related method: procedure bla;
if you have two different signatures, it will select the best matching one.
cross overloading is a newer feature, but I don't remember exactly when. My guess is D2006.
Uing the following class code in Lazarus I get the following error on the writeln(woman.name, 'has been born'); line: "Project My Application raised exception class 'External SIGSEV'. Other pascal code seems to work ok
program project1;
uses wincrt;
type human = class
private
health : integer;
public
name : string;
constructor born(n: string);
end;
constructor human.born(n: string);
begin
name := n;
health := 100;
end;
var
woman : human;
begin
woman.born('Tracy');
writeln(woman.name, 'has been born');
end.
You need to instantiate object this way:
woman := human.born('Tracy');
I have an application which can log a stacktrace, which can be later used for debugging.
On Windows, I've gotten by using the excellent JCLDebug unit provided by the JEDI project.
Now that my application is running on OSX, I've hit a bit of a hitch - I don't know how to obtain the correct stacktrace when an exception occurs.
I have got the basics down -
1) I can get a stacktrace using 'backtrace' (found in libSystem.dylib)
2) The resulting backtrace can be converted into line numbers using the .map file provided by Delphi's linker
The issue I'm left with is - I don't know where to call backtrace from. I know that Delphi uses Mach exceptions (on a separate thread), and that I cannot use posix signals, but that's all that I've managed to sort out.
I can get a backtrace in the 'try...except' block, but unfortunately, by that point the stack has already wound down.
How can I install a proper exception logger which will run right after the exception occurs?
Update:
As per 'Honza R's suggestion, I've taken a look at the 'GetExceptionStackInfoProc' procedure.
This function does get me 'inside' of the exception handling process, but unfortunately leaves me with some of the same issues I had previously.
First of all - on desktop platforms, this function 'GetExceptionStackInfoProc' is just a function pointer, which you can assign with your own exception info handler. So out of the box, Delphi doesn't provide any stack information provider.
If I assign a function to 'GetExceptionStackInfoProc' and then run a 'backtrace' inside of it, I receive a stacktrace, but that trace is relative to the exception handler, not the thread which caused the exception.
'GetExceptionStackInfoProc' does contain a pointer to a 'TExceptionRecord', but there's very limited documentation available on this.
I might be going beyond my depth, but how can I get a stacktrace from the correct thread? Would it be possible for me to inject my own 'backtrace' function into the exception handler and then return to the standard exception handler from there?
Update 2
Some more details. One thing to clear up - this question is about exceptions that are handled by MACH messages, not software exceptions that are handled entirely within the RTL.
Embarcadero has laid out some comments along with these functions -
System.Internal.MachExceptions.pas -> catch_exception_raise_state_identity
{
Now we set up the thread state for the faulting thread so that when we
return, control will be passed to the exception dispatcher on that thread,
and this POSIX thread will continue watching for Mach exception messages.
See the documentation at <code>DispatchMachException()</code> for more
detail on the parameters loaded in EAX, EDX, and ECX.
}
System.Internal.ExcUtils.pas -> SignalConverter
{
Here's the tricky part. We arrived here directly by virtue of our
signal handler tweaking the execution context with our address. That
means there's no return address on the stack. The unwinder needs to
have a return address so that it can unwind past this function when
we raise the Delphi exception. We will use the faulting instruction
pointer as a fake return address. Because of the fencepost conditions
in the Delphi unwinder, we need to have an address that is strictly
greater than the actual faulting instruction, so we increment that
address by one. This may be in the middle of an instruction, but we
don't care, because we will never be returning to that address.
Finally, the way that we get this address onto the stack is important.
The compiler will generate unwind information for SignalConverter that
will attempt to undo any stack modifications that are made by this
function when unwinding past it. In this particular case, we don't want
that to happen, so we use some assembly language tricks to get around
the compiler noticing the stack modification.
}
Which seem to be responsible for the issue I'm having.
When I do a stacktrace after this exception system has handed control over to the RTL, it looks like this - (bearing in mind, the stack unwinder has been superseded by a backtrace routine. The backtrace will hand control over to the unwinder once it is completed)
0: MyExceptionBacktracer
1: initunwinder in System.pas
2: RaiseSignalException in System.Internal.ExcUtils.pas
Since RaiseSignalException is called by SignalConverter, I'm led to believe that the backtrace function provided by libc is not compatible with the modifications made to the stack. So, it's incapable of reading the stack beyond that point, but the stack is still present underneath.
Does anyone know what to do about that (or whether my hypothesis is correct)?
Update 3
I've finally managed to get proper stacktraces on OSX. Huge thanks to both Honza and Sebastian. By combining both of their techniques, I found something that works.
For anyone else who could benefit from this, here's the basic source. Bear in mind that I'm not quite sure if it's 100% correct, if you can suggest improvements, go ahead. This technique hooks onto an exception right before Delphi unwinds the stack on the faulting thread, and compensates for any stack frame corruption that might have taken place beforehand.
unit MyExceptionHandler;
interface
implementation
uses
SysUtils;
var
PrevRaiseException: function(Exc: Pointer): LongBool; cdecl;
function backtrace2(base : NativeUInt; buffer : PPointer; size : Integer) : Integer;
var SPMin : NativeUInt;
begin
SPMin:=base;
Result:=0;
while (size > 0) and (base >= SPMin) and (base <> 0) do begin
buffer^:=PPointer(base + 4)^;
base:=PNativeInt(base)^;
//uncomment to test stacktrace
//WriteLn(inttohex(NativeUInt(buffer^), 8));
Inc(Result);
Inc(buffer);
Dec(size);
end;
if (size > 0) then buffer^:=nil;
end;
procedure UnInstallExceptionHandler; forward;
var
InRaiseException: Boolean;
function RaiseException(Exc: Pointer): LongBool; cdecl;
var b : NativeUInt;
c : Integer;
buff : array[0..7] of Pointer;
begin
InRaiseException := True;
asm
mov b, ebp
end;
c:=backtrace2(b - $4 {this is the compiler dependent value}, #buff, Length(buff));
//... do whatever you want to do with the stacktrace
Result := PrevRaiseException(Exc);
InRaiseException := False;
end;
procedure InstallExceptionHandler;
var
U: TUnwinder;
begin
GetUnwinder(U);
Assert(Assigned(U.RaiseException));
PrevRaiseException := U.RaiseException;
U.RaiseException := RaiseException;
SetUnwinder(U);
end;
procedure UnInstallExceptionHandler;
var
U: TUnwinder;
begin
GetUnwinder(U);
U.RaiseException := PrevRaiseException;
SetUnwinder(U);
end;
initialization
InstallExceptionHandler;
end.
You can use GetExceptionStackInfoProc, CleanUpStackInfoProc and GetStackInfoStringProc in Exception class you need to save stack trace in GetExceptionStackInfoProc and then retrieve it with GetStackInfoStringProc which will get called by RTL if you use StackTrace property of the Exception. Maybe you could also take look at https://bitbucket.org/shadow_cs/delphi-arm-backtrace which demonstrates this on Android.
To do this properly on Mac OS X the libc backtrace function cannot be used because Delphi will corrupt stack frame when calling the GetExceptionStackInfoProc from Exception.RaisingException. Own implementation must be used that is capable of walking the stack from different base address which can be corrected by hand.
Your GetExceptionStackInfoProc would then look like this (I used XE5 for this example the value added to EBP bellow may differ based on which compiler you use and this example was only tested on Mac OS X, Windows implementation may or may not differ):
var b : NativeUInt;
c : Integer;
buff : array[0..7] of Pointer;
begin
asm
mov b, ebp
end;
c:=backtrace2(b - $14 {this is the compiler dependent value}, #buff, Length(buff));
//... do whatever you want to do with the stacktrace
end;
And the backtrace2 function would look like this (note that stop conditions and other validations are missing in the implementation to ensure that AVs are not caused during stack walking):
function backtrace2(base : NativeUInt; buffer : PPointer; size : Integer) : Integer;
var SPMin : NativeUInt;
begin
SPMin:=base;
Result:=0;
while (size > 0) and (base >= SPMin) and (base <> 0) do begin
buffer^:=PPointer(base + 4)^;
base:=PNativeInt(base)^;
Inc(Result);
Inc(buffer);
Dec(size);
end;
if (size > 0) then buffer^:=nil;
end;
You could hook yourself into the Exception Unwinder. Then you can call backtrace where the exception happens. Here's an example. The unit SBMapFiles is what I use for reading the mapfiles. It is not required to get the exception call stack.
unit MyExceptionHandler;
interface
implementation
uses
Posix.Base, SysUtils, SBMapFiles;
function backtrace(result: PNativeUInt; size: Integer): Integer; cdecl; external libc name '_backtrace';
function _NSGetExecutablePath(buf: PAnsiChar; BufSize: PCardinal): Integer; cdecl; external libc name '__NSGetExecutablePath';
var
PrevRaiseException: function(Exc: Pointer): LongBool; cdecl;
MapFile: TSBMapFile;
const
MaxDepth = 20;
SkipFrames = 3;
procedure ShowCurrentStack;
var
StackLog: PNativeUInt; //array[0..10] of Pointer;
Cnt: Integer;
I: Integer;
begin
{$POINTERMATH ON}
GetMem(StackLog, SizeOf(Pointer) * MaxDepth);
try
Cnt := backtrace(StackLog, MaxDepth);
for I := SkipFrames to Cnt - 1 do
begin
if StackLog[I] = $BE00EF00 then
begin
WriteLn('---');
Break;
end;
WriteLn(IntToHex(StackLog[I], 8), ' ', MapFile.GetFunctionName(StackLog[I]));
end;
finally
FreeMem(StackLog);
end;
{$POINTERMATH OFF}
end;
procedure InstallExceptionHandler; forward;
procedure UnInstallExceptionHandler; forward;
var
InRaiseException: Boolean;
function RaiseException(Exc: Pointer): LongBool; cdecl;
begin
InRaiseException := True;
ShowCurrentStack;
Result := PrevRaiseException(Exc);
InRaiseException := False;
end;
procedure InstallExceptionHandler;
var
U: TUnwinder;
begin
GetUnwinder(U);
Assert(Assigned(U.RaiseException));
PrevRaiseException := U.RaiseException;
U.RaiseException := RaiseException;
SetUnwinder(U);
end;
procedure UnInstallExceptionHandler;
var
U: TUnwinder;
begin
GetUnwinder(U);
U.RaiseException := PrevRaiseException;
SetUnwinder(U);
end;
procedure LoadMapFile;
var
FileName: array[0..255] of AnsiChar;
Len: Integer;
begin
if MapFile = nil then
begin
MapFile := TSBMapFile.Create;
Len := Length(FileName);
_NSGetExecutablePath(#FileName[0], #Len);
if FileExists(ChangeFileExt(FileName, '.map')) then
MapFile.LoadFromFile(ChangeFileExt(FileName, '.map'));
end;
end;
initialization
LoadMapFile;
InstallExceptionHandler;
end.