вторник, 3 ноября 2009 г.
понедельник, 26 октября 2009 г.
Program language name
I choose the language name. So I call it YAR.
Here is the code sample that was succesfully executed today.
program Helloworld;
var a,b,c:integer;
d,ec,fg:float;
j,z,i:integer;
begin
a:=123;
b:=a;
write b;
write a+2*b;
c:=a+2*(b+2);
write c;
b:=321;
a:=c+100*2-a/(b-100)*200/300-400;
write a;
end
Here is the code sample that was succesfully executed today.
program Helloworld;
var a,b,c:integer;
d,ec,fg:float;
j,z,i:integer;
begin
a:=123;
b:=a;
write b;
write a+2*b;
c:=a+2*(b+2);
write c;
b:=321;
a:=c+100*2-a/(b-100)*200/300-400;
write a;
end
понедельник, 20 июля 2009 г.
Still hard working on compiler
I am still hard working on compiler in my free time.
And more more with my son Yaroslav.
And more more with my son Yaroslav.
понедельник, 16 февраля 2009 г.
For russian fellow Maaacheba or getting method name dynamically
It is still a draft implementation,
but it can be extended for other x86 opcodes.
So it is a base idea.
And it can be extended to support dynamic methods invocation.
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Published
procedure StaticMethod;
procedure VirtualMethod(a,b,c,d:integer);virtual;
end;
THelper= class helper for Tobject
function GetMethodAddress:pointer;
end;
implementation
type
PPDWORD=^PDWORD;
function AnalizeCallMethod(Self:PPDWORD;ReturnAddress:pbyte;SomePlaceInTheMethodCode:pointer):DWORD;
var Operation:DWORD;
VMT:PDWORD;
Offset:Integer;
begin
{$POINTERMATH ON}
//Virtual Call
Operation:=DWORD((@ReturnAddress[-6])^) AND $FF;
case Operation of
$FF:
begin
VMT:=Self[0];
Offset:=DWORD((@ReturnAddress[-4])^);
result:=DWORD((@(Pbyte(VMT)[Offset]))^);
exit;
end;
end;
Operation:=DWORD((@ReturnAddress[-5])^) AND $E8;
//Static Call
case Operation of
$E8:
begin
result:=DWORD(ReturnAddress)+DWORD((@ReturnAddress[-4])^);
exit;
end;
end;
{$POINTERMATH OFF}
end;
{ THelper }
function THelper.GetMethodAddress: pointer;
asm
mov edx,[ebp+04];
mov ecx,[esp];
Call AnalizeCallMethod;
end;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
StaticMethod;
VirtualMethod(1,2,3,4);
end;
procedure TForm1.StaticMethod;
begin
showmessage(MethodName(GetMethodAddress));
end;
procedure TForm1.VirtualMethod(a,b,c,d:integer);
begin
showmessage(MethodName(GetMethodAddress));
end;
but it can be extended for other x86 opcodes.
So it is a base idea.
And it can be extended to support dynamic methods invocation.
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Published
procedure StaticMethod;
procedure VirtualMethod(a,b,c,d:integer);virtual;
end;
THelper= class helper for Tobject
function GetMethodAddress:pointer;
end;
implementation
type
PPDWORD=^PDWORD;
function AnalizeCallMethod(Self:PPDWORD;ReturnAddress:pbyte;SomePlaceInTheMethodCode:pointer):DWORD;
var Operation:DWORD;
VMT:PDWORD;
Offset:Integer;
begin
{$POINTERMATH ON}
//Virtual Call
Operation:=DWORD((@ReturnAddress[-6])^) AND $FF;
case Operation of
$FF:
begin
VMT:=Self[0];
Offset:=DWORD((@ReturnAddress[-4])^);
result:=DWORD((@(Pbyte(VMT)[Offset]))^);
exit;
end;
end;
Operation:=DWORD((@ReturnAddress[-5])^) AND $E8;
//Static Call
case Operation of
$E8:
begin
result:=DWORD(ReturnAddress)+DWORD((@ReturnAddress[-4])^);
exit;
end;
end;
{$POINTERMATH OFF}
end;
{ THelper }
function THelper.GetMethodAddress: pointer;
asm
mov edx,[ebp+04];
mov ecx,[esp];
Call AnalizeCallMethod;
end;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
StaticMethod;
VirtualMethod(1,2,3,4);
end;
procedure TForm1.StaticMethod;
begin
showmessage(MethodName(GetMethodAddress));
end;
procedure TForm1.VirtualMethod(a,b,c,d:integer);
begin
showmessage(MethodName(GetMethodAddress));
end;
четверг, 15 ноября 2007 г.
How to get Entry Point of Method Address Dynamically? No problem
In some cases (may be some sort of code injecting for constructive needs) there is a necessity for getting Method Entry Point Dynamically.
Here is my method
TMethodAddres=class
public
//Compile time binding method
procedure Method;
end;
procedure TMethodAddres.Method;
begin
//Compile time binding method
asm
mov eax,[ebp+04];
add eax,[eax-4]; //Now in eax the Entry point of this method
end;
end;
But there is some restrictions to work this code properly. This trick is based on Method stack frame existence.
To archive this, set the optimization flag of the project to unchecked state, and the stack frames flag to checked state.
And what about virtual methods address?
May be in the next time.
Here is my method
TMethodAddres=class
public
//Compile time binding method
procedure Method;
end;
procedure TMethodAddres.Method;
begin
//Compile time binding method
asm
mov eax,[ebp+04];
add eax,[eax-4]; //Now in eax the Entry point of this method
end;
end;
But there is some restrictions to work this code properly. This trick is based on Method stack frame existence.
To archive this, set the optimization flag of the project to unchecked state, and the stack frames flag to checked state.
And what about virtual methods address?
May be in the next time.
суббота, 27 октября 2007 г.
SEH Dynamic Unwinding with auto correction
To achieve this objective, I have made some modifications.
I have added next fields to the TYieldObject class
InnerSEHCount:DWORD;
InnerSEHOffsets:array[0..$F] of DWORD;
And of course the code has been changed a little.
function TYieldObject.MoveNext: boolean;
asm
push ebp;
push ebx;
push edi;
push esi;
push eax;
xor edx,edx;
mov eax.TYieldObject.IsYield,dl;
push offset @a1
{
Is it first call
}
mov ecx,eax.TYieldObject.BESP;
cmp ecx,edx;
jnz @NotFirstCall;
mov eax.TYieldObject.BESP,esp;
jmp @JustBeforeTheJump;
@NotFirstCall:
cmp eax.TYieldObject.StackFrameSize,edx;
jz @RestoreRegisters;
{
is need any correction
}
mov edx,esp;
sub edx,ecx;
jz @RestoreStackFrame;
{
Correction
}
{
Correct ebp
}
add [eax.TYieldObject.REBP],edx;
{
Is any SEH frames
}
mov ecx,eax.TYieldObject.InnerSEHCount;
jecxz @ChangeBESP;
{
correct SEH frames
}
mov ebx,eax.TYieldObject.REBP;
lea esi,eax.TYieldObject.StackFrame;
add esi,eax.TYieldObject.StackFrameSize;
dec ecx;
mov edi,esi;
sub edi,DWORD PTR eax.TYieldObject.InnerSEHOffsets+4*ecx;
mov [edi+$08],ebx;
@SEHCorrection:
dec ecx;
jl @ChangeBESP
mov edi,esi;
sub edi,DWORD PTR eax.TYieldObject.InnerSEHOffsets+4*ecx;
mov [edi+$08],ebx;
add [edi],edx;
jmp @SEHCorrection;
{
Change BESP
}
@ChangeBESP:
mov eax.TYieldObject.BESP,esp;
{
Restore stack frame
}
@RestoreStackFrame:
mov ecx,eax.TYieldObject.StackFrameSize;
sub esp,ecx;
mov edi,esp;
lea esi,eax.TYieldObject.StackFrame;
rep movsb;
{
Connect Inner SEH frame. Are any inner SEH?
}
mov ecx,eax.TYieldObject.InnerSEHCount;
jecxz @RestoreRegisters;
{
Connect Inner SEH frame
}
xor ecx,ecx;
mov edi,eax.TYieldObject.BESP;
sub edi,DWORD PTR eax.TYieldObject.InnerSEHOffsets+4*ecx;
mov fs:[ecx],edi;
{
Restore Registers
}
@RestoreRegisters:
mov ebx,eax.TYieldObject.REBX;
mov ecx,eax.TYieldObject.RECX;
mov edx,eax.TYieldObject.REDX;
mov esi,eax.TYieldObject.RESI;
mov edi,eax.TYieldObject.REDI;
mov ebp,eax.TYieldObject.REBP;
@JustBeforeTheJump:
push [eax.TYieldObject.NextItemEntryPoint];
mov eax,eax.TYieldObject.REAX;
ret;
@a1:;
pop eax;
pop esi;
pop edi;
pop ebx;
pop ebp;
mov al,eax.TYieldObject.IsYield;
end;
procedure TYieldObject.Yield(const Value);
asm
mov eax.TYieldObject.REBP,ebp;
mov eax.TYieldObject.REAX,eax;
mov eax.TYieldObject.REBX,ebx;
mov eax.TYieldObject.RECX,ecx;
mov eax.TYieldObject.REDX,edx; // This is the Ref to const param
mov eax.TYieldObject.RESI,ESI;
mov eax.TYieldObject.REDI,EDI;
pop ecx;
mov eax.TYieldObject.NextItemEntryPoint,ecx;
//We must do it first for valid const reference
push eax;
mov ecx,[eax];
CALL DWORD PTR [ecx+VMTOFFSET TYieldObject.SaveYieldedValue];
pop eax;
{
Unwind SEH
// There is no need ebx,esi,edi to retain
}
xor ebx,ebx;
mov ecx,fs:[ebx];
@SEHUnwind:
jecxz @JustAfterSEHUnwind;
cmp ecx,eax.TYieldObject.BESP;
jnl @JustAfterSEHUnwind
mov esi,eax.TYieldObject.BESP;
sub esi,ecx;
mov DWORD PTR eax.TYieldObject.InnerSEHOffsets+4*ebx,esi;
inc ebx;
mov ecx,[ecx];
jmp @SEHUnwind;
@JustAfterSEHUnwind:
mov eax.TYieldObject.InnerSEHCount,ebx;
{
Connect Outer SEH frame.
If no local SEH frames next two commands are redundant
}
xor ebx,ebx;
mov fs:[ebx],ecx;
{
Save local stack frame
}
mov ecx,eax.TYieldObject.BESP;
sub ecx,esp;
mov eax.TYieldObject.StackFrameSize,ecx;
jz @AfterSaveStack;
lea esi,[esp];
lea edi,[eax.TYieldObject.StackFrame];
rep movsb;
mov esp,eax.TYieldObject.BESP;
@AfterSaveStack:
mov eax.TYieldObject.IsYield,1;
end;
So for now you may use try/finally and try/except construction in procedure that uses yielding.
Next article will be about additional improvements.
Now I am thinking about separate stack.
I have added next fields to the TYieldObject class
InnerSEHCount:DWORD;
InnerSEHOffsets:array[0..$F] of DWORD;
And of course the code has been changed a little.
function TYieldObject.MoveNext: boolean;
asm
push ebp;
push ebx;
push edi;
push esi;
push eax;
xor edx,edx;
mov eax.TYieldObject.IsYield,dl;
push offset @a1
{
Is it first call
}
mov ecx,eax.TYieldObject.BESP;
cmp ecx,edx;
jnz @NotFirstCall;
mov eax.TYieldObject.BESP,esp;
jmp @JustBeforeTheJump;
@NotFirstCall:
cmp eax.TYieldObject.StackFrameSize,edx;
jz @RestoreRegisters;
{
is need any correction
}
mov edx,esp;
sub edx,ecx;
jz @RestoreStackFrame;
{
Correction
}
{
Correct ebp
}
add [eax.TYieldObject.REBP],edx;
{
Is any SEH frames
}
mov ecx,eax.TYieldObject.InnerSEHCount;
jecxz @ChangeBESP;
{
correct SEH frames
}
mov ebx,eax.TYieldObject.REBP;
lea esi,eax.TYieldObject.StackFrame;
add esi,eax.TYieldObject.StackFrameSize;
dec ecx;
mov edi,esi;
sub edi,DWORD PTR eax.TYieldObject.InnerSEHOffsets+4*ecx;
mov [edi+$08],ebx;
@SEHCorrection:
dec ecx;
jl @ChangeBESP
mov edi,esi;
sub edi,DWORD PTR eax.TYieldObject.InnerSEHOffsets+4*ecx;
mov [edi+$08],ebx;
add [edi],edx;
jmp @SEHCorrection;
{
Change BESP
}
@ChangeBESP:
mov eax.TYieldObject.BESP,esp;
{
Restore stack frame
}
@RestoreStackFrame:
mov ecx,eax.TYieldObject.StackFrameSize;
sub esp,ecx;
mov edi,esp;
lea esi,eax.TYieldObject.StackFrame;
rep movsb;
{
Connect Inner SEH frame. Are any inner SEH?
}
mov ecx,eax.TYieldObject.InnerSEHCount;
jecxz @RestoreRegisters;
{
Connect Inner SEH frame
}
xor ecx,ecx;
mov edi,eax.TYieldObject.BESP;
sub edi,DWORD PTR eax.TYieldObject.InnerSEHOffsets+4*ecx;
mov fs:[ecx],edi;
{
Restore Registers
}
@RestoreRegisters:
mov ebx,eax.TYieldObject.REBX;
mov ecx,eax.TYieldObject.RECX;
mov edx,eax.TYieldObject.REDX;
mov esi,eax.TYieldObject.RESI;
mov edi,eax.TYieldObject.REDI;
mov ebp,eax.TYieldObject.REBP;
@JustBeforeTheJump:
push [eax.TYieldObject.NextItemEntryPoint];
mov eax,eax.TYieldObject.REAX;
ret;
@a1:;
pop eax;
pop esi;
pop edi;
pop ebx;
pop ebp;
mov al,eax.TYieldObject.IsYield;
end;
procedure TYieldObject.Yield(const Value);
asm
mov eax.TYieldObject.REBP,ebp;
mov eax.TYieldObject.REAX,eax;
mov eax.TYieldObject.REBX,ebx;
mov eax.TYieldObject.RECX,ecx;
mov eax.TYieldObject.REDX,edx; // This is the Ref to const param
mov eax.TYieldObject.RESI,ESI;
mov eax.TYieldObject.REDI,EDI;
pop ecx;
mov eax.TYieldObject.NextItemEntryPoint,ecx;
//We must do it first for valid const reference
push eax;
mov ecx,[eax];
CALL DWORD PTR [ecx+VMTOFFSET TYieldObject.SaveYieldedValue];
pop eax;
{
Unwind SEH
// There is no need ebx,esi,edi to retain
}
xor ebx,ebx;
mov ecx,fs:[ebx];
@SEHUnwind:
jecxz @JustAfterSEHUnwind;
cmp ecx,eax.TYieldObject.BESP;
jnl @JustAfterSEHUnwind
mov esi,eax.TYieldObject.BESP;
sub esi,ecx;
mov DWORD PTR eax.TYieldObject.InnerSEHOffsets+4*ebx,esi;
inc ebx;
mov ecx,[ecx];
jmp @SEHUnwind;
@JustAfterSEHUnwind:
mov eax.TYieldObject.InnerSEHCount,ebx;
{
Connect Outer SEH frame.
If no local SEH frames next two commands are redundant
}
xor ebx,ebx;
mov fs:[ebx],ecx;
{
Save local stack frame
}
mov ecx,eax.TYieldObject.BESP;
sub ecx,esp;
mov eax.TYieldObject.StackFrameSize,ecx;
jz @AfterSaveStack;
lea esi,[esp];
lea edi,[eax.TYieldObject.StackFrame];
rep movsb;
mov esp,eax.TYieldObject.BESP;
@AfterSaveStack:
mov eax.TYieldObject.IsYield,1;
end;
So for now you may use try/finally and try/except construction in procedure that uses yielding.
Next article will be about additional improvements.
Now I am thinking about separate stack.
понедельник, 22 октября 2007 г.
Just before improving implementation for dynamic SEH unwinding support
All times we declare local variables of finalized types the Delphi compiler inserts hidden try finally block that forces local variables of finalized types to be finalized under any circumstances.
Look at the next sample.
Delphi code
procedure TForm1.Button1Click(Sender: TObject);
var a:string;
begin
a:='abcdef';
end;
is mapped to appropriate x86 assembler code.
Something like this
push ebp
mov ebp,esp
push $00
xor eax,eax
{
This is the hidden try
}
push ebp
push $XXXXXXXX
push dword ptr fs:[eax]
mov fs:[eax],esp
Unit1.pas. XX: a:='abcdef';
lea eax,[ebp-$04]
mov edx,$XXXXXXXX
call @LStrLAsg
Unit1.pas.XX: end;
xor eax,eax
pop edx
pop ecx
pop ecx
mov fs:[eax],edx
push $XXXXXXXX
{
Local variable Finalization code
}
lea eax,[ebp-$04]
call @LStrClr
ret
{
And this is the hidden finally block
}
jmp @HandleFinally
jmp $XXXXXXXX
{Normal procedure Exit }
pop ecx
pop ebp
ret
Little explanation about SEH.
FS is protected mode selector that indexes descriptor of segment in descriptor tables (descriptors map logical address space to linear) .
Segment that is indexed by FS has special meaning for Windows Operation System.
This segment contains structure known as Thread Environment Block, TEB. But TEB structure includes at start of it a Win32 Thread Information Block (TIB). Win32 Thread Information Block (TIB) is a data structure in Win32 on x86 that stores info about the currently running thread.
First field of TIB (i.e. fs:[0]) is current Structured Exception Handling frame.
This field is used by system to provide your code the ability to react on raised exception.
Because your procedures and functions may consist of not only hidden try/finally blocks, but yours try/finally try/except blocks, I have to implement some special SEH unwinding code and if there is any displacement I need to correct this dynamically.
And because Yield has a very specific behaviour I have to dynamically attach/detach/correct local SEH frames.
Read about this in my next article.
Look at the next sample.
Delphi code
procedure TForm1.Button1Click(Sender: TObject);
var a:string;
begin
a:='abcdef';
end;
is mapped to appropriate x86 assembler code.
Something like this
push ebp
mov ebp,esp
push $00
xor eax,eax
{
This is the hidden try
}
push ebp
push $XXXXXXXX
push dword ptr fs:[eax]
mov fs:[eax],esp
Unit1.pas. XX: a:='abcdef';
lea eax,[ebp-$04]
mov edx,$XXXXXXXX
call @LStrLAsg
Unit1.pas.XX: end;
xor eax,eax
pop edx
pop ecx
pop ecx
mov fs:[eax],edx
push $XXXXXXXX
{
Local variable Finalization code
}
lea eax,[ebp-$04]
call @LStrClr
ret
{
And this is the hidden finally block
}
jmp @HandleFinally
jmp $XXXXXXXX
{Normal procedure Exit }
pop ecx
pop ebp
ret
Little explanation about SEH.
FS is protected mode selector that indexes descriptor of segment in descriptor tables (descriptors map logical address space to linear) .
Segment that is indexed by FS has special meaning for Windows Operation System.
This segment contains structure known as Thread Environment Block, TEB. But TEB structure includes at start of it a Win32 Thread Information Block (TIB). Win32 Thread Information Block (TIB) is a data structure in Win32 on x86 that stores info about the currently running thread.
First field of TIB (i.e. fs:[0]) is current Structured Exception Handling frame.
This field is used by system to provide your code the ability to react on raised exception.
Because your procedures and functions may consist of not only hidden try/finally blocks, but yours try/finally try/except blocks, I have to implement some special SEH unwinding code and if there is any displacement I need to correct this dynamically.
And because Yield has a very specific behaviour I have to dynamically attach/detach/correct local SEH frames.
Read about this in my next article.
Подписаться на:
Сообщения (Atom)