Delphi xe2 Error compiling ASM code with x64 compiler. Unsupported language feature: 'ASM'

2.6k views Asked by At

as the title says I am facing a problem on compiling a Delphi XE2 project with x64 compiler witch contains ASM code. When I try to compile it I get error "Unsupported language feature: 'ASM'".

I tried to remove delphi code from procedure witch contains ASM code then I get "Invalid combination of opcode and operands".

Here is some part of the code..

type
  TDllLoadInfo = record
    Module: pointer;
    EntryPoint: pointer;
  end;

  TGetProcAddrExInfo = record
    pExitThread: pointer;
    pGetProcAddress: pointer;
    pGetModuleHandle: pointer;
    lpModuleName: pointer;
    lpProcName: pointer;
  end;

  TInjectLibraryInfo = record
    pLoadLibrary: pointer;
    lpModuleName: pointer;
    pSleep: pointer;
  end;



procedure DllEntryPoint(lpParameter: pointer); stdcall;
var
  LoadInfo: TDllLoadInfo;
begin
  LoadInfo := TDllLoadInfo(lpParameter^);
  asm
    xor eax, eax
    push eax
    push DLL_PROCESS_ATTACH
    push LoadInfo.Module
    call LoadInfo.EntryPoint
  end;
end;

procedure GetProcAddrExThread(lpParameter: pointer); stdcall;
var
  GetProcAddrExInfo: TGetProcAddrExInfo;
begin
  GetProcAddrExInfo := TGetProcAddrExInfo(lpParameter^);
  asm
    push GetProcAddrExInfo.lpModuleName
    call GetProcAddrExInfo.pGetModuleHandle
    push GetProcAddrExInfo.lpProcName
    push eax
    call GetProcAddrExInfo.pGetProcAddress
    push eax
    call GetProcAddrExInfo.pExitThread
  end;
end;



procedure InjectLibraryThread(lpParameter: pointer); stdcall;
var
  InjectLibraryInfo: TInjectLibraryInfo;
begin
  InjectLibraryInfo := TInjectLibraryInfo(lpParameter^);
  asm
    push InjectLibraryInfo.lpModuleName
    call InjectLibraryInfo.pLoadLibrary
    @noret:
    mov eax, $FFFFFFFF
    push eax
    call InjectLibraryInfo.pSleep
    jmp @noret
  end;
end;

Is there any way to compile this project without getting any error or convert asm code to delphi/pascal? Thanks for your time. Btw, I don't know ASM.

2

There are 2 answers

0
Remy Lebeau On

You cannot use inline assembly inside of a non-assembly procedure in 64-bit, only in 32-bit. This is clearly stated in Embarcadero's documentation:

Converting 32-bit Delphi Applications to 64-bit Windows | Inline Assembly code

If your application contains inline assembly (ASM) code, you need to examine the ASM code and make the following changes:

  • Mixing of assembly statements with Pascal code is not supported in 64-bit applications. Replace assembly statements with either Pascal code or functions written completely in assembly.
    ...

In your example, there is no reason to use inline assembly at all. You can (and should) use pure Pascal code instead to support both 32bit and 64bit:

type
  TDllEntryPointFunc = function(hinstDLL: HINSTANCE; fdwReason: DWORD; lpvReserved: Pointer): BOOL; stdcall;
  TGetModuleHandleFunc = function(const lpModuleName: PChar): HMODULE; stdcall;
  TGetProcAddressFunc = function(hModule: HMODULE; lpProcName: PAnsiChar): Pointer; stdcall;
  TExitThreadFunc = procedure(dwExitCode: DWORD); stdcall;
  TLoadLibraryFunc = function(const lpFileName: PChar): HMODULE; stdcall;
  TSleepFunc = procedure(dwMilliseconds: DWORD); stdcall;

procedure DllEntryPoint(lpParameter: Pointer); stdcall;
var
  LoadInfo: TDllLoadInfo;
  pEntryPoint: TDllEntryPointFunc;
begin
  LoadInfo := TDllLoadInfo(lpParameter^);
  @pEntryPoint := LoadInfo.EntryPoint;
  pEntryPoint(LoadInfo.Module, DLL_PROCESS_ATTACH, nil);
end;

procedure GetProcAddrExThread(lpParameter: Pointer); stdcall;
var
  GetProcAddrExInfo: TGetProcAddrExInfo;
  pGetModuleHandle: TGetModuleHandleFunc;
  pGetProcAddress: TGetProcAddressFunc;
  pExitThread: TExitThreadFunc;
  Module: HMODULE;
  Proc: Pointer;
begin
  GetProcAddrExInfo := TGetProcAddrExInfo(lpParameter^);
  @pGetModuleHandle := GetProcAddrExInfo.pGetModuleHandle;
  @pGetProcAddress := GetProcAddrExInfo.pGetProcAddress;
  @pExitThread := GetProcAddrExInfo.pExitThread;

  Module := pGetModuleHandle(PChar(GetProcAddrExInfo.lpModuleName));
  Proc := pGetProcAddress(Module, PAnsiChar(GetProcAddrExInfo.lpProcName));
  pExitThread(DWORD(Proc));
end;

procedure InjectLibraryThread(lpParameter: Pointer); stdcall;
var
  InjectLibraryInfo: TInjectLibraryInfo;
  pLoadLibrary: TLoadLibraryFunc;
  pSleep: TSleepFunc;
begin
  InjectLibraryInfo := TInjectLibraryInfo(lpParameter^);
  @pLoadLibrary := InjectLibraryInfo.pLoadLibrary;
  @pSleep := InjectLibraryInfo.pSleep;

  pLoadLibrary(PChar(InjectLibraryInfo.lpModuleName));
  repeat
    pSleep(-1);
  until False;
end;
4
David Heffernan On

The code cannot be correctly ported directly to x64 because it will perform a 64 bit pointer truncation – see below for details.


The x64 compiler does not support inline assembly:

Mixing of assembly statements with Pascal code is not supported in 64-bit applications. Replace assembly statements with either Pascal code or functions written completely in assembly.

The use of assembly is needless here. I'm not really sure why the original author would have elected to go to this trouble. It's always best to deal with such porting problems by converting to Pascal so that the compiler can do all the work.

You can write the code along these lines:

type
  TDllLoadInfo = record
    Module: HMODULE;
    EntryPoint: function(hinstDLL: HMODULE; fdwReason: DWORD;
      lpvReserved: Pointer): BOOL; stdcall;
  end;

  TGetProcAddrExInfo = record
    ExitThread: procedure(dwExitCode: DWORD); stdcall;
    GetProcAddress: function(hModule: HMODULE;
      lpProcName: PAnsiChar): Pointer; stdcall;
    GetModuleHandle: function(lpModuleName: PWideChar): HMODULE; stdcall;
    lpModuleName: PWideChar;
    lpProcName: PAnsiChar;
  end;

  TInjectLibraryInfo = record
    LoadLibrary: function(lpFileName: PWideChar): HMODULE; stdcall;
    lpModuleName: PWideChar;
    Sleep: procedure(dwMilliseconds: DWORD); stdcall;
  end;

procedure DllEntryPoint(lpParameter: pointer); stdcall;
var
  LoadInfo: ^TDllLoadInfo absolute lpParameter;
begin
  LoadInfo.EntryPoint(LoadInfo.Module, DLL_PROCESS_ATTACH, nil);
end;

procedure GetProcAddrExThread(lpParameter: pointer); stdcall;
var
  GetProcAddrExInfo: ^TGetProcAddrExInfo absolute lpParameter;
  ModuleHandle: HMODULE;
  ProcAddress: Pointer;
begin
  ModuleHandle := GetProcAddrExInfo.GetModuleHandle(GetProcAddrExInfo.lpModuleName);
  ProcAddress := GetProcAddrExInfo.GetProcAddress(ModuleHandle,
    GetProcAddrExInfo.lpProcName);
  GetProcAddrExInfo.ExitThread(DWORD(ProcAddress)); // !!!! x64 pointer truncation !!!!
end;

procedure InjectLibraryThread(lpParameter: Pointer); stdcall;
var
  InjectLibraryInfo: ^TInjectLibraryInfo absolute lpParameter;
begin
  InjectLibraryInfo.LoadLibrary(InjectLibraryInfo.lpModuleName);
  while True do // rather pointless to loop ....
    InjectLibraryInfo.Sleep(INFINITE);
end;

The code assumes that you've got function pointers of the W suffixed Unicode API functions. If not, then use PAnsiChar instead of PWideChar.

However, at this point we do need to take stock and consider what we have just done. We have attempted to push a 64 bit pointer into a 32 bit DWORD thread exit code. That will not necessarily fit. If the library is loaded into an address above 4GB then you will suffer pointer truncation.

So, the bottom line here is that you cannot correctly port this code to x64. You will need to find an variation on this code that is capable of returning a 64 bit value from the thread. But it will have to do so in a different way because you cannot fit a 64 bit value into a thread return value.