[fpc-pascal] assign code to a method

Andrew Haines AndrewD207 at aol.com
Fri Feb 18 17:59:48 CET 2011


On 02/18/11 03:14, Angel Montesinos wrote:
> While waiting for a 64 bits Delphi, I am doing experimentation with
> fpc-Lazarus 64 bits. And this is my problem:
> 
> In most of my programs I use my own library for parsing and evaluating
> floating point functions like
> 
>   'x sin(y z)'
> 
> passed by the user as strings. The basic organization is as follows:

> In the debugger the exception is raised before the effective call of the
> function code. If then I press F7, then the debugger jumps to the
> following assemblies
> 
>    push    %rbp
>    mov     %esp,%ebp
>    fldpi                       {loads pi to the fpu}
>    ftspl   -0x18(%rbp)         {copy pi to stack}
>    movsd   -0x18(%rbp),%xmm0   {copies pi to XMM0, so caller
>                                {may find also there the result}
>    leaveq
>    retq
> 
> I have tried all possible combinations like V:= PChar(code), V:=
> @code[1], etc. to no avail. Thus I think something is wrong in my
> understanding of the whole business in 64 bits or in fpc-Lazarus,
> because this is not a problem in Delphi nor it was in an old version of
> fpc-Lazarus 32 bits. Please give me a tip.
> Many thanks in advance.
> 

>From the other comments it seems like you are writing some assembly to
memory at runtime then calling that code? If so then maybe the following
can help you.

I made some trampoline procedures where I wrote some executable code at
runtime and I had to, both on Window and Linux, allocate some memory
that was marked as executable. Here's a snippet of how I allocated the
memory. The code worked on Windows 7 x64/32 and Gentoo Linux x64/32

 PTrampolineBlock = ^TTrampolineBlock;
  TTrampolineBlock = record
    NextBlock: Pointer;
    Cursor: DWord;
    Size: DWord;
    Code: PtrUint; // this is actually the start of the remainder of the
allocated size
  end;

procedure TTrampolineManager.AllocateBlock;
const
  AllocSize = $1000;
var
  NewBlock: PTrampolineBlock;
begin
  {$IFDEF UNIX}
  NewBlock := fpmmap(nil, AllocSize, PROT_EXEC or PROT_READ or
PROT_WRITE, MAP_ANONYMOUS or MAP_PRIVATE, -1, 0);
  {$ENDIF}
  {$IFDEF MSWINDOWS}
  NewBlock := VirtualAlloc(nil,AllocSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
  {$ENDIF}

NewBlock^.Cursor:=PtrUint(@NewBlock^.Code)-PtrUint(@NewBlock^.NextBlock);
  NewBlock^.Size:=AllocSize;
  NewBlock^.NextBlock:=Block;
  Block := NewBlock;
end;

procedure TTrampolineManager.FreeBlock(var ABlock: PTrampolineBlock);
begin
  if ABlock = nil then
    Exit;
  FreeBlock(ABlock^.NextBlock);
  {$IFDEF UNIX}
  Fpmunmap(ABlock, ABlock^.Size);
  {$ENDIF}
  {$IFDEF MSWINDOWS}
  VirtualFree(ABlock, 0, MEM_RELEASE);
  {$ENDIF}
  ABlock := nil;
end;

procedure TTrampolineManager.WriteData(const AData; ASize: Byte);
begin
  if Block = nil then AllocateBlock;
  //WriteLn('Writing: ', hexStr(PtrUint(AData), ASize*2));
  if Block^.Cursor + ASize >= Block^.Size then
    raise TrampolineBlockFullException.Create('');
  Move(AData, PByte(@Block^.Code)[Block^.Cursor], ASize);
  Inc(Block^.Cursor, ASize);
end;

so the usage would be like so
function TTrampolineManager.GenerateCode(args: ....): Pointer;
begin
try
  Result := CurrentBlock.Position; // = @Block + Block.Cursor
  repeat
    WriteData(your_data, size_of_data);
  until done;

except
    on e: TrampolineBlockFullException do
      begin
        TrampolineManager.AllocateBlock;
        Result := GenerateCode(args);
      end;
  end;
end;


Hope this helps :)

Regards,

Andrew Haines



More information about the fpc-pascal mailing list