I'm trying to recompile the (old) TProfDHTMLEdit unit in 64bit environment with Delphi XE5. The component is old and dismissed but I need it because I could not found anything similar to easily substitute it for my purpose.
I'm having trouble with three internal routines that are hard coded directly in asm. I'm not capable to convert them in pure Pascal. Anyone has already solved a similar problem?
The parts to convert are:
procedure TProfOleControl.GetEventMethod(DispID: TDispID; var Method: TMethod);
asm
{$IFDEF WIN32}
PUSH EBX
PUSH ESI
PUSH EDI
PUSH ECX
MOV EBX,EAX
MOV ECX,[EBX].TProfOleControl.FControlData
MOV EDI,[ECX].TControlData2.EventCount
MOV ESI,[ECX].TControlData2.EventDispIDs
XOR EAX,EAX
JMP ##1
##0: CMP EDX,[ESI].Integer[EAX*4]
JE ##2
INC EAX
##1: CMP EAX,EDI
JNE ##0
XOR EAX,EAX
XOR EDX,EDX
JMP ##3
##2: PUSH EAX
CMP [ECX].TControlData2.Version, 401
JB ##2a
MOV EAX, [ECX].TControlData2.FirstEventOfs
TEST EAX, EAX
JNE ##2b
##2a: MOV EAX, [EBX]
CALL TObject.ClassParent
CALL TObject.InstanceSize
ADD EAX, 7
AND EAX, not 7
##2b: ADD EBX, EAX
POP EAX
MOV EDX,[EBX][EAX*8].TMethod.Data
MOV EAX,[EBX][EAX*8].TMethod.Code
##3: POP ECX
MOV [ECX].TMethod.Code,EAX
MOV [ECX].TMethod.Data,EDX
POP EDI
POP ESI
POP EBX
{$ENDIF}
end;
or
procedure TProfOleControl.InvokeEvent(DispID: TDispID; var Params: TDispParams);
var
EventMethod: TMethod;
begin
GetEventMethod(DispID, EventMethod);
if Integer(EventMethod.Code) < $10000 then Exit;
try
{$IFDEF WIN32}
asm
PUSH EBX
PUSH ESI
MOV ESI, Params
MOV EBX, [ESI].TDispParams.cArgs
TEST EBX, EBX
JZ ##7
MOV ESI, [ESI].TDispParams.rgvarg
MOV EAX, EBX
SHL EAX, 4
XOR EDX, EDX
ADD ESI, EAX
##1: SUB ESI, 16
MOV EAX, dword ptr [ESI]
CMP AX, varSingle
JA ##3
JE ##5
##2: TEST DL,DL
JNE ##2a
MOV ECX, ESI
INC DL
TEST EAX, varArray
JNZ ##6
MOV ECX, dword ptr [ESI+8]
JMP ##6
##2a: TEST EAX, varArray
JZ ##5
PUSH ESI
JMP ##6
##3: CMP AX, varDate
JA ##2
##4: PUSH dword ptr [ESI+12]
##5: PUSH dword ptr [ESI+8]
##6: DEC EBX
JNE ##1
##7: MOV EDX, Self
MOV EAX, EventMethod.Data
CALL EventMethod.Code
POP ESI
POP EBX
end
{$ENDIF}
except
Application.HandleException(Self)
end
end;
There seem to be slightly adjusted versions of the methods of TOleControl with the same name. TOleControl is one of the VCL's own types, you can check the XE5 implementation (which quite possibly does use pure Pascal) to get something that already mostly works.
Related
I'd like to know why my code isn't working when I'm trying to use stack.
Program have to print name of itself.
Here is code without using stack, which is working:
format elf executable
entry _start
segment readable executable
strlen:
mov ebx,0
strlen_loop:
cmp byte [eax+ebx],0
je strlen_end
inc ebx
jmp strlen_loop
strlen_end:
inc ebx
ret
_start:
mov eax,[esp+4]
call strlen
mov eax,4
mov ecx,[esp+4]
mov edx,ebx
mov ebx,0
int 80h
mov eax,1
mov ebx,0
int 80h
Here is code with using stack (program just exits/doing nothing):
format elf executable
entry _start
segment readable executable
strlen:
mov ebx,0
strlen_loop:
cmp byte [eax+ebx],0
je strlen_end
inc ebx
jmp strlen_loop
strlen_end:
inc ebx
ret
_start:
mov eax,[esp+4]
call strlen
push ebx
mov eax,4
mov ebx,0
mov ecx,[esp+4]
pop edx
int 80h
mov eax,1
mov ebx,0
int 80h
I'm new in Linux development, but if I understood your problem, you have a mistake in this block:
mov eax,4
mov ebx,0
mov ecx,[esp+4] ; you wrote 'push' instruction above so value of 4 is incorrect now
pop edx
int 80h
You can just swap these two lines to fix this:
pop edx
mov ecx,[esp+4]
Below is block of the code of an app I am reversing.
Please forgive me I new to world of reversing but I figured out that something is happening here and I thought I must change je to jmp but I am not sure if it's correct.
_unit34::TApplication.Run
Push ebp
ebp,esp
ecx
Push ebx
Push esi
Push edi
Mov dword ptr [ebp-4],eax
Mov. Eax,dword ptr [ebp-4]
Mov byte ptr [eax+0AD],1
XOR edx,edx
Push ebp
Push. 4E2B09
Push dword ptr fs:[edx]
Mov dword ptr fs:[edx],esp
Mov Eax,4D6574; DoneApplication
Call AddExitProc
Mov eax, dword ptr [ebp-4]
Mov eax, dword ptr [ebp+44]
Test eax,eax
Je 004E2AF1
Can someone explain a bit of what's happening there?
And how can I change je to jmp using Interactive Delphi reconstructor?
Can someone explain a bit of what's happening there?
Push ebp
mov ebp,esp
Sets up a stack frame.
??? ecx
Push ebx
Push esi
Push edi
Save non-volatile registers
Mov dword ptr [ebp-4],eax
Save the Self pointer
Mov. Eax,dword ptr [ebp-4]
Unfortunate stack trashing :-(
Mov byte ptr [eax+0AD],1
Self.SomeBoolean:= true
XOR edx,edx
Push ebp
Push. 4E2B09
Push dword ptr fs:[edx]
Mov dword ptr fs:[edx],esp
try
Mov Eax,4D6574; DoneApplication
Call AddExitProc
Mov eax, dword ptr [ebp-4]
Set the exit proc for the application
Mov eax, dword ptr [ebp+44]
SomeObject:= somevar
Test eax,eax
Je 004E2AF1
If (Assigned(SomeObject)) then .....
And how can I change je to jmp using Interactive Delphi reconstructor?
That particular change should make no sense here, because it would invoke an exception. The je is invoked when the object is nil. This is the error condition. You will change the program to always crash with an error.
However if you absolutly must do it, just change the opcode from $74 to $EB (for a byte offset jump) or from $0F84 to $90E9 (for a long jump).
I'd use a hex editor.
None of these changes will have a happy outcome unless you know exactly what you're doing.
Where to start
Get expertise in Delphi coding and view a lot of CPU output.
Press Ctrl + Alt + C to see what code Delphi produces.
This will take a number of months to years to master however.
About reversing
If you want to get into reversing IDA pro is a much better tool.
It has a freeware edition you can start with.
I'm messing around with ASM in Delphi. From my understanding, EAX holds Result. In the following, I have to put RET at the end, otherwise Result is not correct (it is correct if the input is 0). What am I doing wrong, or should I say, what don't I understand about this?
function MSb(const Val: Integer): Integer;
label
Go;
asm
CMP EAX, 0
JNZ Go
MOV EAX, -1
RET
Go:
BSR EBX, EAX
MOV EAX, EBX
RET
end;
If I say the following:
MOV Result, EBX
Then I get the following compilation:
MOV [EPB-$04], EBX
MOV EAX, [EPB-$04]
However, my code above has the following postscript:
MOV EAX, EDX
RET
At least without optimization enabled, your function has a pre-amble and a post-amble. Have a look at it:
Project46.dpr.13: asm
0041A1F4 55 push ebp
0041A1F5 8BEC mov ebp,esp
0041A1F7 83C4F8 add esp,-$08
0041A1FA 8945F8 mov [ebp-$08],eax
Project46.dpr.14: CMP EAX, 0
0041A1FD 83F800 cmp eax,$00
Project46.dpr.15: JNZ Go
0041A200 7506 jnz $0041a208
Project46.dpr.16: MOV EAX, -1
0041A202 B8FFFFFFFF mov eax,$ffffffff
Project46.dpr.17: RET
0041A207 C3 ret
Project46.dpr.19: BSR EBX, EAX
0041A208 0FBDD8 bsr ebx,eax
Project46.dpr.20: MOV EAX, EBX
0041A20B 89D8 mov eax,ebx
Project46.dpr.21: RET
0041A20D C3 ret
Project46.dpr.22: end;
0041A20E 8B45FC mov eax,[ebp-$04]
0041A211 59 pop ecx
0041A212 59 pop ecx
0041A213 5D pop ebp
0041A214 C3 ret
So the pre-amble sets up the stack frame. It saves away the ebp register, and modifies both the ebp and esp registers. Notice also the post-amble. You do need to execute that code to restore the saved registers.
The usual way to deal with this is to jump to the end of the function instead of using ret. So write your code like this:
function MSb(const Val: Integer): Integer;
asm
CMP EAX, 0
JNZ ##go
MOV EAX, -1
JMP ##exit
##go:
BSR EBX, EAX
MOV EAX, EBX
##exit:
end;
This way you ensure that the post-amble is executed. You really should get into the habit of writing the code this way to ensure that any pre-amble is executed.
Now, beyond that I suspect that the problem you mention in the question actually relates to a compiler bug relating to your use of a Pascal label rather than an asm label. Well, perhaps it is a compiler bug, but perhaps it is just a mistake to use a Pascal label. Consider the following program:
{$APPTYPE CONSOLE}
function MSb(const Val: Integer): Integer;
asm
CMP EAX, 0
JNZ ##Go
MOV EAX, -1
JMP ##exit
##Go:
BSR EBX, EAX
MOV EAX, EBX
##exit:
end;
function MSb2(const Val: Integer): Integer;
label
Go;
asm
CMP EAX, 0
JNZ Go
MOV EAX, -1
RET
Go:
BSR EBX, EAX
MOV EAX, EBX
end;
begin
Writeln(Msb(0));
Writeln(Msb(1));
Writeln(Msb2(0));
Writeln(Msb2(1));
Readln;
end.
The output when compiled with optimization is:
-1
0
-1
4
So, what about that rather odd 4. Well, let's look at the assembled code for Msb2, which is essentially your code:
004059E8 83F800 cmp eax,$00
004059EB 7506 jnz $004059f3
004059ED B8FFFFFFFF mov eax,$ffffffff
004059F2 C3 ret
004059F3 0FBDD8 bsr ebx,eax
004059F6 89D8 mov eax,ebx
004059F8 8BC2 mov eax,edx
004059FA C3 ret
Why on earth is the value of edx, a volatile register whose value has not been assigned, being moved into eax just before the function returns. This is the problem that you are reporting. My guess is that the use of a Pascal label is confusing the assembler. Stick to asm labels.
Here is the assembled code for Msb:
004059D4 83F800 cmp eax,$00
004059D7 7506 jnz $004059df
004059D9 B8FFFFFFFF mov eax,$ffffffff
004059DE C3 ret
004059DF 0FBDD8 bsr ebx,eax
004059E2 89D8 mov eax,ebx
004059E4 C3 ret
That's more like it! Notice how the compiler knows that there is no post-able here, and replaces the jmp ##exit with a straight ret.
What am I doing wrong, or should I say, what don't I understand about
this?
Basically as clearly stated the pre and post-ambles but also be careful when using assembler code. Parameters are passed in different ways depending upon calling conventions. Your code will run fine with the Pascal (ironically obsolete) calling convention, if you remove the label, the RET and use assembler labels (##). It is good practice to always indicate a calling convention for assembly code, since Result may refer indeed to EAX or a local variable for holding it. EAX maps directly to Result when using the Register (default) calling convention.
I have a problem. I have following x86 delphi code which is written in ASM. I need to port this to AMD64?
type
TCPUID = array[1..4] of Longint;
function GetCID : TCPUID; assembler; register;
asm
push ebx
push edi
mov edi, eax
mov eax, 1
dw $A20F
stosd
mov eax, ebx
stosd
mov eax, ecx
stosd
mov eax, edx
stosd
pop edi
pop ebx
end;
I have never programmed in assembly, does anyone know what the port would be or how I would go about changing it.
I am not Win64 assembler guru, but the next translation worked for me (tested on 64-bit free pascal):
program project1;
{$mode delphi}
{$asmmode intel}
type
TCPUID = array[1..4] of Longint;
function GetCID: TCPUID;
asm
push rbx
push rdi
mov rdi, rcx
mov eax, 1
cpuid
mov [rdi],eax
add rdi,4
mov [rdi],ebx
add rdi,4
mov [rdi],ecx
add rdi,4
mov [rdi],edx
pop rdi
pop rbx
end;
var ID: TCPUID;
begin
ID:= GetCID;
Writeln(ID[1], '-', ID[2], '-', ID[3], '-', ID[4]);
Readln;
end.
Here's my version, for both x86 and x64:
function GetCPUID: TCPUID;
asm
{$IF Defined(CPUX86)}
push ebx
push edi
mov edi, eax
mov eax, 1
xor ecx,ecx
cpuid
mov [edi+$0], eax
mov [edi+$4], ebx
mov [edi+$8], ecx
mov [edi+$c], edx
pop edi
pop ebx
{$ELSEIF Defined(CPUX64)}
mov r8, rbx
mov r9, rcx
mov eax, 1
cpuid
mov [r9+$0], eax
mov [r9+$4], ebx
mov [r9+$8], ecx
mov [r9+$c], edx
mov rbx, r8
{$IFEND}
end;
One of the nice things about x64 is that there are a lot more registers available, many of which are volatile. So we can make use of that scratch space and avoid touching main memory at all. Well obviously we have to touch main memory to return the result.
Since RBX is nonvolatile we preserve its value. All the other registers that we modify are volatile and so we need not preserve them. I can't think of any way to simplify this further.
This can readily be extended to allow the input to CPUID to be passed as an argument:
function GetCPUID(ID: Integer): TCPUID;
asm
{$IF Defined(CPUX86)}
push ebx
push edi
mov edi, edx
xor ecx,ecx
cpuid
mov [edi+$0], eax
mov [edi+$4], ebx
mov [edi+$8], ecx
mov [edi+$c], edx
pop edi
pop ebx
{$ELSEIF Defined(CPUX64)}
mov r8, rbx
mov r9, rcx
mov eax, edx
cpuid
mov [r9+$0], eax
mov [r9+$4], ebx
mov [r9+$8], ecx
mov [r9+$c], edx
mov rbx, r8
{$ELSE}
{$Message Fatal 'GetCPUID has not been implemented for this architecture.'}
{$IFEND}
end;
This assumes a sub-leaf value of 0, passed in ECX. Again, if you wish to pass that, it is easy enough:
function GetCPUID(Leaf, Subleaf: Integer): TCPUID;
asm
{$IF Defined(CPUX86)}
push ebx
push edi
mov edi, ecx
mov ecx, edx
cpuid
mov [edi+$0], eax
mov [edi+$4], ebx
mov [edi+$8], ecx
mov [edi+$c], edx
pop edi
pop ebx
{$ELSEIF Defined(CPUX64)}
mov r9,rcx
mov ecx,r8d
mov r8,rbx
mov eax,edx
cpuid
mov [r9+$0], eax
mov [r9+$4], ebx
mov [r9+$8], ecx
mov [r9+$c], edx
mov rbx, r8
{$ELSE}
{$Message Fatal 'GetCPUID has not been implemented for this architecture.'}
{$IFEND}
end;
I never worked with CPUID so i don't know for sure what it does.
But from common sense and Wikipedia (if those sources would suffice) my advices are:
Try to
1) remove "assembler;" keyword - obsolete
1.1) optionally remove "register;" - it is by default and has little value for no-parameters function. Also Wikipedia tells that has no effect in Win64.
2) if possible - rephrase this as procedure GetCID (out data: TCPUID);.
If need function - i'd rather made inline wrapper in Pascal - just to keep definition simple and obvious. That is a good advice to author - to keep not-automated things simplistic and leave syntax-sugar automation to Pascal, especially when you have no experience and any not simple trick can confuse you and cause you to type broken code. KISS principle.
3) remove push ebx/pop ebx
3.1) i think push edi/popedi to be removed as well. But to be on safe side - i'd changed them to push rdi and pop rdi
The article does not tell that some registers should be saved or preserved:
http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
Nor that requirement is told at http://docwiki.embarcadero.com/RADStudio/XE3/en/Assembly_Procedures_and_Functions
4) mov edi, eax -> mov rdi, rcx
4.1) you may add cld command next after it, as an extra safety measure. But it should be overcautious and redundant.
The rest should be the same as it seems x64 has the same convention for CPUID as x86 mode - no mentio nof x64 mode at http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits
To sum it up it should be like
type
TCPUID = packed array[1..4] of INT32;
function GetCID : TCPUID; inline;
begin
GetCID_Implementation(Result);
end;
procedure GetCID_Implementation (out buffer: TCPUID);
asm
mov rdi, rcx // mov edi, eax
// RCX/EAX is Delphi/Windows pointer to 1st parameter
// RDI/EDI is CPU pointer to output buffer
// cld - optionally, should not be needed
// state of cld should be preserved by all other functions
xor eax, eax // shorter way for eax := 1
inc eax
dw $A20F // call CPUID(eax==1),
// output (according to wikipedia) is in eax,edx,ecx,ebx 32-bit registers
stosd // *( (INT32*)RDI )++ := eax
mov eax, ebx // xchg eax, ebx would be shorter,on that
stosd
mov eax, ecx // but Delphi XE2 is broken with xchg eax
stosd
mov eax, edx
stosd
end;
How can I check using Delphi 2007 that a box is AVX capable.
My question is only restricted to querying the support in the CPU (Assumption is made that the OS is OK / Windows 7 with SP1).
The PDF document entitled Introduction to IntelĀ® Advanced Vector Extensions
by Chris Lomont explains how to do it and provides an example code implementation but in c++.
It's also available at this page.
Here's a translation of the assembler code given on an Intel blog:
function isAvxSupported: Boolean;
asm
{$IFDEF CPUX86}
push ebx
{$ENDIF}
{$IFDEF CPUX64}
mov r10, rbx
{$ENDIF}
xor eax, eax
cpuid
cmp eax, 1
jb #not_supported
mov eax, 1
cpuid
and ecx, 018000000h
cmp ecx, 018000000h
jne #not_supported
xor ecx, ecx
db 0Fh, 01h, 0D0h //XGETBV
and eax, 110b
cmp eax, 110b
jne #not_supported
mov eax, 1
jmp #done
#not_supported:
xor eax, eax
#done:
{$IFDEF CPUX86}
pop ebx
{$ENDIF}
{$IFDEF CPUX64}
mov rbx, r10
{$ENDIF}
end;
This code will work in both 32 and 64 bit versions of Delphi.
Update: Register saving code added thanks to #PhiS.