Recent

Author Topic: [SOLVED] Executing x86_64 code from dynamic array of byte  (Read 6551 times)

wizzwizz4

  • New Member
  • *
  • Posts: 20
[SOLVED] Executing x86_64 code from dynamic array of byte
« on: August 24, 2024, 06:56:04 pm »
I would like to write a scripting language that can call DLLs, implemented by dynamically-compiling to x86_64 code and then executing that from memory.

Writing a compiler is a well-understood problem. For dynamically-loading DLLs, I can just write a (conditionally-compiled) Pascal function that makes the necessary library/system calls, and call that from the generated code. For holding the generated x86_64 code in memory and running it, I'm a little stuck. Heap memory isn't usually executable by default, and many systems enforce W^X. We can work around this using VirtualProtect (on Windows) or mprotect (on POSIX), but reading their respective documentation, it looks like this only works for specially-allocated memory (VirtualAlloc and mmap, respectively). I don't think that's how Free Pascal makes heap allocations.

Is there a way to run x86_64 code from a dynamic array on a system with W^X? (I suspect not, since the reference count would have to be in a writeable page. Even if you constructed that manually, I doubt Free Pascal's built-in deallocation logic would deallocate that properly.) If not, what's the best approach for having in-memory x86_64 code that's reference counted, which you can call from Pascal code?



Context: I've been having an argument. One side claims that Free Pascal can do anything you'd ever want to do, and there's no point learning anything else. The other side claims that different programming languages are suited for different tasks, and it's worth learning other programming languages. This argument is not yet settled. I looked for an area where Free Pascal (not Lazarus) is clearly superior. Free Pascal's FFI has always seemed particularly strong to me: memory layout of everything is well-defined (documented in Free Pascal Programmer’s Guide), and calling conventions are mostly comprehensible (unlike C++). This seems like a way to show off some of Free Pascal's strengths.
« Last Edit: August 30, 2024, 02:54:02 pm by wizzwizz4 »

MarkMLl

  • Hero Member
  • *****
  • Posts: 8306
Re: Executing x86_64 code from dynamic array of byte
« Reply #1 on: August 24, 2024, 08:05:15 pm »
I think that's going to be a problem, for two reasons.

The first is that most OSes are adopting increasingly-strict measures to avoid "stack smashing" attacks caused by- inter alia- buffer overruns. My suspicion is that in order to get something that "behaves like code" you'll have to go via something that "looks like a file". That might also imply that somebody/something more privileged than the initial user has to "bless" it to be executable, or you might find that something like named shared memory is usable... which would probably be a good BlackHat presentation if you could demonstrate the concept working as a generic hack ** across unrelated OSes.

The second is that handling the calling convention for an arbitrary number of parameters and an arbitrary return type tends to be difficult. You might need to predeclare a large number of templates.

I've done quite a lot of (Linux/ELF) Shared Object interfacing and somewhat less for DLLs, but my usual technique is to declare the interface as a .inc file and use an unpublished utility to wrap the library into an object (an example is in the Asound project in my Github repo). That works fine for fixed numbers and types of parameters, but rapidly goes bad for (C-style) variable numbers of parameters. I remember discussing that hereabouts with PascalDragon, who I'm sure wouldn't object to being called the Local Expert.

** Ambiguous term used intentionally.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

440bx

  • Hero Member
  • *****
  • Posts: 5066
Re: Executing x86_64 code from dynamic array of byte
« Reply #2 on: August 24, 2024, 08:34:31 pm »
It's very unlikely you'll be able to run code stored in an array  that resides  in the data segment or the heap (dynamic array), this is because, as Mark already mentioned, most O/Ss are disallowing running code that does not reside in a code/executable segment.

The safe route is to allocate a block of memory using VirtualAlloc (in Windows) and use VirtualProtect to give the block of memory the attributes required to hold executable code.

If you need an example, I can supply one (from your post, I gather you probably don't need one but, just in case, if you do, I can provide one.)

HTH.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

wizzwizz4

  • New Member
  • *
  • Posts: 20
Re: Executing x86_64 code from dynamic array of byte
« Reply #3 on: August 24, 2024, 10:46:42 pm »
My suspicion is that in order to get something that "behaves like code" you'll have to go via something that "looks like a file".

I believe that's the case for spawning processes in Windows.

which would probably be a good BlackHat presentation

Probably not: many web browsers do this (JavaScript JIT). There are some setups where it's allegedly impossible (e.g. appropriately-configured SELinux), but this is a toy project, so I don't need to support those. A real program would fall back to an interpreter, in that case, but since I'm not doing that, I shouldn't need squillions of templates.

If you need an example, I can supply one

I would appreciate that. I don't have a Windows setup to test on, so seeing known-working code would be nice.

I was mostly trying to ask about organising the code. In Rust, I'd use something like
Code: [Select]
Rc<Vec<u8, mmap_alloc::MapAlloc>>, but obviously that's not possible in Free Pascal. If dynamic arrays are out, is there a better way than creating an advanced record with a Pointer field and a destructor, and doing all the bookkeeping manually?

I'm also not quite certain how to cast the pointer to an array type. I don't see anything in the Programmer's Guide about open arrays, but they've got to be some kind of fat pointer, so I assume I can't use them here. I'm not too worried about fpc inserting out-of-bounds accesses if I cast to a massive static array (not like I would be in C)… but that still seems like a hack. Should I just cast to a ^byte and do pointer arithmetic?

440bx

  • Hero Member
  • *****
  • Posts: 5066
Re: Executing x86_64 code from dynamic array of byte
« Reply #4 on: August 24, 2024, 11:03:58 pm »
I would appreciate that. I don't have a Windows setup to test on, so seeing known-working code would be nice.

Here is code that is tested and works in 64 bit (since it generates 64 bit executable code it cannot be fully run in a 32 bit environment):
Code: Pascal  [Select][+][-]
  1. { --------------------------------------------------------------------------- }
  2. { @file                                                                       }
  3. { Example on assembling a basic function returning `0x1337` in `rax`.         }
  4.  
  5.  
  6.  
  7. {$APPTYPE       CONSOLE}
  8.  
  9. {$TYPEDADDRESS       ON}
  10. {$LONGSTRINGS       OFF}
  11.  
  12. {$WRITEABLECONST    OFF}
  13.  
  14.  
  15. { --------------------------------------------------------------------------- }
  16.  
  17. program EncodeFromScratchA;
  18.  
  19. uses
  20.   ZyDis,
  21.  
  22.   winapi
  23.   ;
  24.  
  25.  
  26. {$include Z900_zydis_inlines.inc}
  27.  
  28. { --------------------------------------------------------------------------- }
  29.  
  30. procedure ExpectSuccess(const InStatus : ZyanStatus); cdecl;
  31. const
  32.   EXIT_FAILURE = 1;    { defined in C's stdlib                                }
  33.  
  34. var
  35.   Buffer : packed array[0..511] of char = #0;
  36.  
  37. begin
  38.   sprintf(Buffer, 'Something failed: 0x08X', InStatus);
  39.  
  40.   if ZYAN_FAILED(InStatus) then
  41.   begin
  42.     writeln(Buffer);
  43.  
  44.     halt(EXIT_FAILURE);
  45.   end;
  46. end;
  47.  
  48. { --------------------------------------------------------------------------- }
  49.  
  50. type
  51.   PPZyanU8 = ^PZyanU8;
  52.  
  53. procedure AppendInstruction
  54.             (
  55.              const InReq              : PZydisEncoderRequest;
  56.                    InoutBuffer        : PPZyanU8;
  57.                    InoutBuffer_length : PZyanUSize
  58.             ); cdecl;
  59. var
  60.   Instr_length : ZyanUSize = 0;
  61.  
  62. begin
  63.   Assert(InReq              <> nil, 'InReg parameter cannot be nil');
  64.   Assert(InoutBuffer        <> nil, 'InoutBuffer cannot be nil');
  65.   Assert(InoutBuffer_length <> nil, 'InoutBufferLength cannot be nil');
  66.  
  67.   Instr_length := InoutBuffer_length^;
  68.  
  69.   ExpectSuccess
  70.     (
  71.      ZydisEncoderEncodeInstruction
  72.        (
  73.         InReq, InoutBuffer^, @Instr_length
  74.        )
  75.     );
  76.  
  77.   inc(InoutBuffer^,        Instr_length);
  78.   dec(InoutBuffer_length^, Instr_length);
  79. end;
  80.  
  81. { --------------------------------------------------------------------------- }
  82.  
  83. function AssembleCode
  84.            (
  85.             InoutBuffer     : PZyanU8;
  86.             InBuffer_length : ZyanUSize
  87.            )
  88.          : ZyanUSize; cdecl;
  89. var
  90.   write_ptr        : PZyanU8   = nil;
  91.   remaining_length : ZyanUSize =   0;
  92.  
  93.   req              : ZydisEncoderRequest = ();
  94.  
  95. begin
  96.   Assert(InoutBuffer     <> nil, 'InoutBuffer cannot be nil');
  97.   Assert(InBuffer_length  > 0,   'InBuffer_length must be greater than zero');
  98.  
  99.   write_ptr        := InoutBuffer;
  100.   remaining_length := InBuffer_length;
  101.  
  102.   // Assemble `mov rax, 0x1337`.
  103.  
  104.   RtlZeroMemory(@req, sizeof(req));
  105.  
  106.   with req do
  107.   begin
  108.     mnemonic              := ZYDIS_MNEMONIC_MOV;
  109.     machine_mode          := ZYDIS_MACHINE_MODE_LONG_64;
  110.     operand_count         := 2;
  111.     operands[0].&type     := ZYDIS_OPERAND_TYPE_REGISTER;
  112.     operands[0].reg.value := ZYDIS_REGISTER_RAX;
  113.     operands[1].&type     := ZYDIS_OPERAND_TYPE_IMMEDIATE;
  114.     operands[1].imm.u     := $1337;
  115.   end;
  116.  
  117.   AppendInstruction(@req, @write_ptr, @remaining_length);
  118.  
  119.   // Assemble `ret`.
  120.  
  121.   RtlZeroMemory(@req, sizeof(req));
  122.  
  123.   with req do
  124.   begin
  125.     mnemonic     := ZYDIS_MNEMONIC_RET;
  126.     machine_mode := ZYDIS_MACHINE_MODE_LONG_64;
  127.   end;
  128.  
  129.   AppendInstruction(@req, @write_ptr, @remaining_length);
  130.  
  131.   result := InBuffer_length - remaining_length;
  132. end;
  133.  
  134. { --------------------------------------------------------------------------- }
  135.  
  136. type
  137.   pbyte       = ^byte;
  138.  
  139. const
  140.   page_size   = $1000;
  141.   alloc_size  = page_size * 2;
  142.  
  143. var
  144.   buffer      : pointer   = nil;
  145.  
  146.   length      : ZyanUSize =   0;
  147.  
  148.   sprintf_buf : packed array[0..511] of char = #0;
  149.  
  150.   i           : integer;
  151.  
  152. type
  153.   TFunction   = function () : ZyanU64; { will use the correct 64bit ABI }
  154.  
  155. var
  156.   TheFunction : TFunction;
  157.  
  158.  
  159. begin
  160.   // Allocate 2 pages of memory. We won't need nearly as much, but it simplifies
  161.   // re-protecting the memory to RWX later.
  162.  
  163.   buffer := VirtualAlloc(nil, alloc_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  164.  
  165.   // Assemble our function.
  166.  
  167.   length := AssembleCode(buffer, alloc_size);
  168.  
  169.   // Print a hex-dump of the assembled code.
  170.  
  171.   for i := 0 to length - 1 do
  172.   begin
  173.     sprintf(sprintf_buf, '%02X ', pbyte(buffer)[i]);
  174.  
  175.     write(sprintf_buf);
  176.   end;
  177.   writeln;
  178.  
  179.  
  180.   {$ifdef WIN64}         // original C source uses an O/S independent test
  181.  
  182.     // ------------------------------------------------------------------------
  183.     // Align pointer to typical page size.
  184.  
  185.     // this step is unnecessary because we used VirtualAlloc which aligns
  186.     // the allocation
  187.  
  188.  
  189.     // ------------------------------------------------------------------------
  190.     // Re-protect the heap region as RWX. Don't do this at home, kids!
  191.  
  192.     // this step is unnecessary because we used VirtualAlloc with protection
  193.     // settings that already include execute, read and write
  194.  
  195.  
  196.     // ------------------------------------------------------------------------
  197.     // Create a function pointer for our buffer.
  198.  
  199.     TheFunction := TFunction(buffer);
  200.  
  201.     // ------------------------------------------------------------------------
  202.     { call the function                                                       }
  203.  
  204.     writeln;
  205.     sprintf(sprintf_buf,
  206.            'Return value of JITed code: 0x%016llX',
  207.             TheFunction());                           { the JITed function    }
  208.     writeln(sprintf_buf);
  209.   {$endif}
  210.  
  211.  
  212.   writeln;
  213.   writeln;
  214.   writeln('press ENTER/RETURN to end this program');
  215.   readln;
  216.  
  217.   { there does not seem to be a way to cause this message to be the last one  }
  218.   { in the Lazarus message window.                                            }
  219.  
  220.   {$ifndef WIN64}
  221.     {$message warning this program has limited operation as a 32bit program   }
  222.   {$endif}
  223. end.
  224.  
  225.  
  226. //
  227. // end of file
  228. // ----------------------------------------------------------------------------
You can get the units "ZyDis" and "winapi" from this link: https://forum.lazarus.freepascal.org/index.php/topic,67665.msg521439.html#msg521439
along with a bunch of other stuff that may not be interesting to you.  The thread provides all the necessary information and code to compile and run the example above and a good number of other examples.

HTH.

NOTE: that code is, mostly, a straight port from an example that is part of the Zydis decoding engine.  Google "Zydis dis-assembler" to get more information if interested.  (factoid: x32/64dbg uses Zydis as it decoding engine.)  I said "mostly" because the C code does not use VirtualAlloc, it uses the C malloc function and while it works I wouldn't trust it to always work (because of security concerns.)



(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

440bx

  • Hero Member
  • *****
  • Posts: 5066
Re: Executing x86_64 code from dynamic array of byte
« Reply #5 on: August 24, 2024, 11:14:28 pm »
I forgot to mention that FPC treats pointers the same as C, by that I mean, in FPC a pointer (any typed pointer) _is_ an array (of the type.)

Also, in many cases an array name can be used as if it were a pointer (just as in C.)

As far as pointers go, C and FPC are pretty much kindred spirits.

There is one feature FPC has that C does not have and, it's a great feature, it is "absolute".  "absolute" allows you to overlay multiple variables of different types on the same memory (similar to "org" in assembler.)  That can come in very handy when a particular location in memory should be seen as being of different types.

Specifically, if you need to "see" a location in memory as if it were an array, just "absolute" an array on top of whatever variable/structure is at that location (and treat the array name as a pointer if you need to... I'd say you get the best of both worlds but, it's actually better than the Borg :) )

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8306
Re: Executing x86_64 code from dynamic array of byte
« Reply #6 on: August 25, 2024, 12:09:21 am »
I'm also not quite certain how to cast the pointer to an array type. I don't see anything in the Programmer's Guide about open arrays, but they've got to be some kind of fat pointer, so I assume I can't use them here. I'm not too worried about fpc inserting out-of-bounds accesses if I cast to a massive static array (not like I would be in C)… but that still seems like a hack. Should I just cast to a ^byte and do pointer arithmetic?

I'm pretty sure that an open array will also pass the current length as a (hidden) parameter, since High() is usable. Dynamic arrays are, of course, distinct since they're represented by an opaque structure with the main storage being on the heap.

One of the things I'm getting at there is that while you might find that ^<open array> refers to the start of the data ^<dynamic array> doesn't: instead you need ^<dynamic array>[0].

And of course dynamic arrays and strings (other than shortstrings) are closely related other than being based on [0] and [1] respectively.

But you've still got the problem of whether the underlying OS will allow you to treat a data area as code. Interpretive languages are a special case here (since, let's face it, /nothing/ is really code) but JIT- where data literally becomes executable- is interesting: I wonder how Qemu does it?

Array bounds checking- and I'm speaking as somebody who's done OS work on protected-mode x86- is a different can of worms :-(

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

ASerge

  • Hero Member
  • *****
  • Posts: 2389
Re: Executing x86_64 code from dynamic array of byte
« Reply #7 on: August 25, 2024, 01:18:41 am »
Is there a way to run x86_64 code from a dynamic array on a system with W^X? (I suspect not, since the reference count would have to be in a writeable page. Even if you constructed that manually, I doubt Free Pascal's built-in deallocation logic would deallocate that properly.) If not, what's the best approach for having in-memory x86_64 code that's reference counted, which you can call from Pascal code?
Example:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$MODESWITCH ADVANCEDRECORDS}
  3. {$APPTYPE CONSOLE}
  4.  
  5. uses Windows;
  6.  
  7. type
  8.   TExecutableMem = record
  9.     Memory: CodePointer;
  10.     Size: Cardinal; // Real allocated size, system page based
  11.     class function Create(ASize: Cardinal): TExecutableMem; static;
  12.     class operator Initialize(var Item: TExecutableMem);
  13.     class operator Finalize(var Item: TExecutableMem);
  14.     procedure PrepareBeforeExecute;
  15.   end;
  16.  
  17. class operator TExecutableMem.Finalize(var Item: TExecutableMem);
  18. begin
  19.   if Item.Memory = nil then
  20.     Exit;
  21.   VirtualFree(Item.Memory, 0, MEM_RELEASE);
  22.   Initialize(Item);
  23. end;
  24.  
  25. class operator TExecutableMem.Initialize(var Item: TExecutableMem);
  26. begin
  27.   Item.Memory := nil;
  28.   Item.Size := 0;
  29. end;
  30.  
  31. class function TExecutableMem.Create(ASize: Cardinal): TExecutableMem;
  32. var
  33.   Info: TMEMORYBASICINFORMATION;
  34. begin
  35.   Result.Memory := VirtualAlloc(nil, ASize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  36.   if Result.Memory = nil then
  37.     Exit;
  38.   if VirtualQuery(Result.Memory, @Info, SizeOf(Info)) <> 0 then
  39.     Result.Size := Info.RegionSize
  40.   else
  41.     Result.Size := ASize;
  42. end;
  43.  
  44. procedure TExecutableMem.PrepareBeforeExecute;
  45. begin
  46.   if Memory <> nil then
  47.     FlushInstructionCache(MainInstance, Memory, Size);
  48. end;
  49.  
  50. function SampleFunc: SizeInt;
  51. begin
  52.   Result := 2;
  53. end;
  54.  
  55. type
  56.   TFunction = function: SizeInt;
  57. const
  58.   CSampleProcedureSize = 1000; // More then SizeOf(SampleFunc)
  59. var
  60.   A: specialize TArray<TExecutableMem>;
  61.   i: SizeInt;
  62.   ExeBlock: TExecutableMem;
  63. begin
  64.   SetLength(A, 3);
  65.   // Prepare
  66.   for i := 0 to High(A) do
  67.   begin
  68.     ExeBlock := TExecutableMem.Create(CSampleProcedureSize);
  69.     Move(Pointer(@SampleFunc)^, ExeBlock.Memory^, CSampleProcedureSize);
  70.     A[i] := ExeBlock;
  71.   end;
  72.   // Use
  73.   for ExeBlock in A do
  74.   begin
  75.     ExeBlock.PrepareBeforeExecute;
  76.     Writeln(TFunction(ExeBlock.Memory));
  77.   end;
  78.   ReadLn;
  79. end.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5899
  • Compiler Developer
Re: Executing x86_64 code from dynamic array of byte
« Reply #8 on: August 25, 2024, 12:10:12 pm »
Writing a compiler is a well-understood problem. For dynamically-loading DLLs, I can just write a (conditionally-compiled) Pascal function that makes the necessary library/system calls, and call that from the generated code. For holding the generated x86_64 code in memory and running it, I'm a little stuck. Heap memory isn't usually executable by default, and many systems enforce W^X. We can work around this using VirtualProtect (on Windows) or mprotect (on POSIX), but reading their respective documentation, it looks like this only works for specially-allocated memory (VirtualAlloc and mmap, respectively). I don't think that's how Free Pascal makes heap allocations.

Look at Rtti.AllocateMemory and Rtti.ProtectMemory. These two functions are used inside the Rtti unit to allocate a block of memory where first code is copied to (in case of the Rtti unit a thunk for interface methods), then adjusted (to match the dynamically generated interface instance) and then the protection is adjusted so that it's executable instead of writable.

Khrys

  • Full Member
  • ***
  • Posts: 177
Re: Executing x86_64 code from dynamic array of byte
« Reply #9 on: August 26, 2024, 07:27:39 am »
The following minimal example compiles and runs just fine on Windows 10 x64, FPC 3.2.0:

Code: Pascal  [Select][+][-]
  1. program ExecuteByteArray;
  2.  
  3. {$mode objfpc}
  4.  
  5. uses
  6.   SysUtils, Windows;
  7.  
  8. function Test(): Integer;
  9. type
  10.   TExampleFunction = function(X: Integer): Integer; SYSV_ABI_Default;
  11. const
  12.   EXAMPLE_SQUARE_PLUS_ONE: TBytes = ($0f, $af, $ff, $ff, $c7, $89, $f8, $c3);
  13. var                                 // imul edi, edi
  14.   CodeBuffer: TBytes;               // inc edi
  15.   OldProtect: DWORD;                // mov eax, edi
  16. begin                               // ret
  17.   SetLength(CodeBuffer, Length(EXAMPLE_SQUARE_PLUS_ONE));
  18.   Move(EXAMPLE_SQUARE_PLUS_ONE[0], CodeBuffer[0], Length(CodeBuffer));
  19.   VirtualProtect(@CodeBuffer[0], Length(CodeBuffer), PAGE_EXECUTE, OldProtect);
  20.   FlushInstructionCache(0, @CodeBuffer[0], Length(CodeBuffer));
  21.   Result := TExampleFunction(CodeBuffer)(7);
  22.   VirtualProtect(@CodeBuffer[0], Length(CodeBuffer), OldProtect, OldProtect);
  23.   WriteLn(Result); // Prints 50 (as expected)
  24. end;
  25.  
  26. begin
  27.   Test();
  28. end.

440bx

  • Hero Member
  • *****
  • Posts: 5066
Re: Executing x86_64 code from dynamic array of byte
« Reply #10 on: August 26, 2024, 11:53:26 am »
@Khrys,

There is a _potential_ problem with the solution you presented, which is, using VirtualProtect using a heap address.

Effectively that modifies the attributes of an entire page, not just the memory block that contains the instructions.  Changing the attributes of other memory blocks can have unexpected consequences (particularly when removing their write attribute, it would be, in some ways, "safer" to specify PAGE_READWRITE in addition to execute to ensure the rest of the block remains writeable - which it was to start with since it is a heap block.)

This is the main reason why code should reside in a dedicated memory block. 

HTH.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Joanna from IRC

  • Hero Member
  • *****
  • Posts: 1343
Re: Executing x86_64 code from dynamic array of byte
« Reply #11 on: August 26, 2024, 03:04:04 pm »
Hi I’m curious what type of program would need code like this?
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

Thaddy

  • Hero Member
  • *****
  • Posts: 16628
  • Kallstadt seems a good place to evict Trump to.
Re: Executing x86_64 code from dynamic array of byte
« Reply #12 on: August 26, 2024, 03:26:16 pm »
Hi I’m curious what type of program would need code like this?
Read PascalDragon's answer?
But I am sure they don't want the Trumps back...

MarkMLl

  • Hero Member
  • *****
  • Posts: 8306
Re: Executing x86_64 code from dynamic array of byte
« Reply #13 on: August 26, 2024, 06:25:29 pm »
Hi I’m curious what type of program would need code like this?
Read PascalDragon's answer?

Or the first para of OP's question.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1477
    • Lebeau Software
Re: Executing x86_64 code from dynamic array of byte
« Reply #14 on: August 26, 2024, 11:12:04 pm »
There is one feature FPC has that C does not have and, it's a great feature, it is "absolute".  "absolute" allows you to overlay multiple variables of different types on the same memory (similar to "org" in assembler.)  That can come in very handy when a particular location in memory should be seen as being of different types.

While C does not have an exact equivalent to absolute, that effect can be achieved in C using a union, similar to a Pascal variant record.  Or, you could always just declare a pointer of one type and point it to the memory of another type to get the same effect as absolute.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

 

TinyPortal © 2005-2018