views:

369

answers:

2

Hi all.

I have this code below, and I want to translate it to ASM, to use in Delphi too.

var
    FunctionAddressList: Array of Integer;

type TFunction = function(parameter: Integer): Integer; cdecl;

function Function(parameter: Integer): Integer;
var
    ExternFunction: TFunction;
begin
    ExternFunction := TFunction(FunctionAddressList[5]);
    Result := ExternFunction(parameter);
end;

It works normaly, but when I try its Assembly version:

function Function(parameter: Integer): Integer; cdecl;
asm
  mov eax, FunctionAddressList
  jmp dword ptr [eax + 5 * 4]
end;

It is supposed to work, because, in C++ it works in both ways:

void *FunctionAddressList;

_declspec(naked) int Function(int parameter)
{
    _asm mov eax, FunctionAddressList;
    _asm jmp dword ptr [eax + 5 * 4];
}

typedef int (*TFunction)(int parameter);
int Function(int parameter)
{
    TFunction ExternFunction = ((TFunction *)FunctionAddressList)[5];
    return ExternFunction(parameter);
}

But it doesn't work in Delphi.

In the Assembly version, it multiplies the array to 4, because it's the offset size between each element of the array, so both versions are equivalent.

So, I want to know why it doesn't work with Delphi. In Delphi, the offset size between Integer values in a array is different than C++?

I've already tried many offsets, as 1, 2, 4, 6, 8, etc. And many types of Array (Array of Pointer; only Pointer; Array of Integer, etc), and I've tried many calling conventions, and cdecl was the only that worked with the non-asm version, but with ASM, all the tests didn't work.

Thanks.

+1  A: 

Array of Integer is not what you think it is. It's an automatically managed dynamic array.

You should try the same using FunctionAddressList: ^Pointer; -- note however that you will have to do manual allocation and deallocation.

Kornel Kisielewicz
I've allocated FunctionAddressList, but it still doesn't work, Do I have to change the '* 4' value to another one?
Flávio Toribio
@Edward - don't use 4, use SizeOf(Pointer). If you're on a 64 bit system it won't be 4.
Kornel Kisielewicz
I've tried this too, but I'm thinking it's an Delphi's Assembler error, but I can't find it out. So thanks in advance, but I'll still use the pointer's way.
Flávio Toribio
+3  A: 

First test app to reproduce error:

var
  FunctionAddressList: Array of Integer;

function Bar(parameter: Integer): Integer; cdecl;
begin
  ShowMessage('Bar '+IntToStr(parameter));
end;

function Foo(parameter: Integer): Integer; cdecl;
asm
  mov eax, FunctionAddressList
  jmp dword ptr [eax + 5 * 4]
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetLength(FunctionAddressList, 6);
  FunctionAddressList[5]:= Integer(@Bar);
  Foo(25);
end;

The Bar address is defined correctly, but the problem is that Delphi compiler generates prologue and epilog for Foo, so real Foo code is

0046CD30 55               push ebp
0046CD31 8BEC             mov ebp,esp
Unit1.pas.46:             mov eax, FunctionAddressList
Unit1.pas.47:             jmp dword ptr [eax + 5 * 4]
0046CD3B 5D               pop ebp
0046CD3C C3               ret

As a result the stack is corrupted, the parameter is wrong and Bar return address is wrong. If you still want to do the trick, use

function Foo(parameter: Integer): Integer; cdecl;
asm
  pop ebp
  mov eax, FunctionAddressList
  jmp dword ptr [eax + 5 * 4]
end;
Serg
As I said above, it's really a Delphi's Assembler difference, thank you very much man, this worked perfectly! I've debugged it too, but not noticed about "pop ebp".
Flávio Toribio
I think the difference is when I use "naked" in C++, witch determines that the function won't have any value on the stack, so it doesn't put "ebp" in the stack with "naked", but in Delphi, I did not know how to use "naked" in functions, so it would put "ebp" in the stack, and my ASM stays corrupted. Thanks.
Flávio Toribio
I am not using C++ now, but I think that your C++ (I presume you are using Visual C++) code for the naked function also assumes the argument on stack. You must write prologue and epilog yourself (in asm) to reach the argument in naked function that uses cdecl calling convention. You don't do it since you don't need the argument here and pass it on stack to the function in address list.
Serg