Search code examples
c++arraysdelphiassemblybasm

Translate a code using pointer, to Assembly in Pascal - Delphi


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.


Solution

  • 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;