1717(*       Morgan Martinet     ([email protected] )                         *) 1818(*       Samuel Iseli        ([email protected] )                             *) 1919(*       Andrey Gruzdev      ([email protected] )                    *) 20+ (*       Lucas Belo          ([email protected] )                         *) 2021(* *************************************************************************) 
2122(*  This source code is distributed with no WARRANTY, for no reason or use.*) 
2223(*  Everyone is allowed to use and change this code free, as long as this  *) 
@@ -32,7 +33,7 @@ interface
3233uses  SysUtils;
3334
3435type 
35-   TCallType = (ctSTDCALL, ctCDECL);
36+   TCallType = (ctSTDCALL, ctCDECL, ctARMSTD );
3637  TCallBack = procedure  of  object ;
3738
3839  function   GetCallBack ( self: TObject; method: Pointer;
@@ -127,6 +128,9 @@ implementation
127128  PtrCalcType = NativeInt;
128129{ $ENDIF} 
129130
131+   EMProtectError = class (Exception)
132+   end ;
133+ 
130134{ $IFNDEF MSWINDOWS} 
131135{ $IFDEF FPC} 
132136function  mmap (Addr: Pointer; Len: Integer; Prot: Integer; Flags: Integer; FileDes: Integer; Off: Integer): Pointer; cdecl;
@@ -151,6 +155,9 @@ procedure GetCodeMem(var ptr: PByte; size: integer);
151155var 
152156  page: PCodeMemPage;
153157  block: PCodeMemBlock;
158+   { $IFNDEF MSWINDOWS} 
159+   flags: integer;
160+   { $ENDIF} 
154161begin 
155162  // ---allocates Block from executable memory
156163  //  executable memory is requested in pages via VirtualAlloc
@@ -174,13 +181,40 @@ procedure GetCodeMem(var ptr: PByte; size: integer);
174181      ptr := nil ;
175182      exit;
176183    end ;
177-     mprotect(page, PageSize, PROT_READ or  PROT_WRITE or  PROT_EXEC);
178- 	{ $ENDIF} 
184+     { 
185+         macOS for M1 has a bug (Apple Feedback FB8994773) in which mprotect 
186+         rejects a permission change from NONE -> RWX, resulting a "Permission 
187+         Denied" error. 
188+         Solution: give RW permission, make memory changes, then change RW to X 
189+     }  
190+     { $IF DEFINED(OSX) AND DEFINED(CPUARM64)} 
191+     flags := PROT_READ or  PROT_WRITE;
192+     { $ELSE} 
193+     flags := PROT_READ or  PROT_WRITE or  PROT_EXEC;
194+     { $IFEND} 
195+     if  mprotect(page, PageSize, flags) <> 0  then 
196+       raise EMProtectError.CreateFmt(' MProtect error: %s'  , [
197+         SysErrorMessage(GetLastError())]);
198+   { $ENDIF} 
179199    page^.next:=CodeMemPages;
180200    CodeMemPages:=page;
181201    //  init pointer to end of page
182202    page^.CodeBlocks:=Pointer(PtrCalcType(page) + PageSize);
203+   { $IF DEFINED(OSX) AND DEFINED(CPUARM64)} 
204+   end  else  begin 
205+     { 
206+       macOS for M1 has a bug (Apple Feedback FB8994773) in which mprotect 
207+       rejects a permission change from NONE -> RWX. 
208+       Solution: give RW permission, make memory changes, then change RW to X 
209+     }  
210+     // RW permission to the entire page for new changes...
211+     if  mprotect(page, PageSize, PROT_READ or  PROT_WRITE) <> 0  then 
212+       raise EMProtectError.CreateFmt(' MProtect error: %s'  , [
213+         SysErrorMessage(GetLastError())]);
183214  end ;
215+   { $ELSE} 
216+   end ;
217+   { $IFEND} 
184218
185219  // ---blocks are assigned starting from the end of the page
186220  block:=Pointer(PtrCalcType(page^.codeBlocks) - (size + sizeof(PCodeMemBlock)));
@@ -258,6 +292,34 @@ function CodeMemPageCount: integer;
258292  end ;
259293end ;
260294
295+ procedure  DeleteCallBack ( Proc: Pointer);
296+ begin 
297+   FreeCodeMem(Proc);
298+ end ;
299+ 
300+ procedure  FreeCallBacks ;
301+ var 
302+   page, nextpage: PCodeMemPage;
303+ begin 
304+   //  free each allocated page
305+   page := CodeMemPages;
306+   while  page <> nil  do 
307+   begin 
308+     nextpage := page^.Next;
309+ 
310+     //  free the memory
311+   { $IFDEF MSWINDOWS} 
312+     VirtualFree(page, 0 , MEM_RELEASE);
313+   { $ELSE} 
314+ 	// FreeMem(page);
315+     munmap(page,PageSize);
316+   { $ENDIF} 
317+ 
318+     page := nextpage;
319+   end ;
320+   CodeMemPages := nil ;
321+ end ;
322+ 
261323function   GetOfObjectCallBack ( CallBack: TCallBack;
262324                               argnum: Integer; calltype: TCallType): Pointer;
263325begin 
@@ -266,15 +328,17 @@ function  GetOfObjectCallBack( CallBack: TCallBack;
266328                         argnum, calltype);
267329end ;
268330
269- { $IFDEF CPUX64} 
270- { $DEFINE 64_BIT_CALLBACK} 
271- { $ELSE} 
272- { $IFDEF MACOS} 
273- { $DEFINE ALIGNED_32_BIT_CALLBACK} 
274- { $ELSE} 
275- { $DEFINE SIMPLE_32_BIT_CALLBACK} 
276- { $ENDIF MACOS} 
277- { $ENDIF CPUX64} 
331+ { $IFNDEF CPUARM} 
332+   { $IFDEF CPUX64} 
333+     { $DEFINE 64_BIT_CALLBACK} 
334+   { $ELSE} 
335+     { $IFDEF MACOS} 
336+       { $DEFINE ALIGNED_32_BIT_CALLBACK} 
337+     { $ELSE} 
338+       { $DEFINE SIMPLE_32_BIT_CALLBACK} 
339+     { $ENDIF MACOS} 
340+   { $ENDIF CPUX64} 
341+ { $ENDIF CPUARM} 
278342
279343{ $IFDEF SIMPLE_32_BIT_CALLBACK} 
280344//  win32 inplementation
@@ -565,35 +629,138 @@ function  GetCallBack( self: TObject; method: Pointer;
565629end ;
566630{ $ENDIF} 
567631
568- procedure  DeleteCallBack ( Proc: Pointer);
632+ { $IFDEF CPUARM32} 
633+ function   GetCallBack (Self: TObject; Method: Pointer; ArgNum: Integer;
634+   CallType: TCallType): Pointer;
635+ const 
636+   S1: array [0 ..123 ] of  byte = (
637+ // big-endian
638+ // offset     <start>:
639+  { +  0:}  	 $80 , $40 , $2d, $e9, //  push	{r7, lr}
640+  { +  4:}  	 $0d, $70 , $a0, $e1, //  mov	r7, sp
641+  { +  8:}  	 $1e, $04 , $2d, $e9, //  push	{r1, r2, r3, r4, sl}
642+  { +  c:}  	 $5c, $40 , $9f , $e5, //  ldr	r4, [pc, #92]	; 70 <loop+0x1c>
643+  { + 10:}  	 $00 , $00 , $54 , $e3, //  cmp	r4, #0
644+  { + 14:}  	 $04 , $d0, $4d, $c0, //  subgt	sp, sp, r4
645+  { + 18:}  	 $04 , $50 , $a0, $c1, //  movgt	r5, r4
646+  { + 1c:}  	 $04 , $50 , $85 , $c2, //  addgt	r5, r5, #4
647+  { + 20:}  	 $04 , $60 , $a0, $c1, //  movgt	r6, r4
648+  { + 24:}  	 $04 , $60 , $46 , $c2, //  subgt	r6, r6, #4
649+  { + 28:}  	 $09 , $00 , $00 , $cb, //  blgt	54 <loop>
650+  { + 2c:}  	 $0f , $00 , $2d, $e9, //  push	{r0, r1, r2, r3}
651+  { + 30:}  	 $3c, $00 , $9f , $e5, //  ldr	r0, [pc, #60]	; 74 <loop+0x20>
652+  { + 34:}  	 $0e, $00 , $bd, $e8, //  pop	{r1, r2, r3}
653+  { + 38:}  	 $38 , $a0, $9f , $e5, //  ldr	sl, [pc, #56]	; 78 <loop+0x24>
654+  { + 3c:}  	 $3a, $ff, $2f , $e1, //  blx	sl
655+  { + 40:}  	 $00 , $00 , $54 , $e3, //  cmp	r4, #0
656+  { + 44:}  	 $04 , $d0, $8d, $c0, //  addgt	sp, sp, r4
657+  { + 48:}  	 $04 , $40 , $9d, $e4, //  pop	{r4}		; (ldr r4, [sp], #4)
658+  { + 4c:}  	 $1e, $04 , $bd, $e8, //  pop	{r1, r2, r3, r4, sl}
659+  { + 50:}  	 $80 , $80 , $bd, $e8, //  pop	{r7, pc}
660+ // offset + 00000054   <loop>:
661+  { + 54:}  	 $05 , $a0, $97 , $e7, //  ldr	sl, [r7, r5]
662+  { + 58:}  	 $06 , $a0, $8d, $e7, //  str	sl, [sp, r6]
663+  { + 5c:}  	 $04 , $50 , $45 , $e2, //  sub	r5, r5, #4
664+  { + 60:}  	 $04 , $60 , $46 , $e2, //  sub	r6, r6, #4
665+  { + 64:}  	 $00 , $00 , $56 , $e3, //  cmp	r6, #0
666+  { + 68:}  	 $f9, $ff, $ff, $aa, //  bge	54 <loop>
667+  { + 6c:}  	 $1e, $ff, $2f , $e1, //  bx	lr
668+ // offset + 00000070   <literal pool>
669+  { + 70:}  	 $00 , $00 , $00 , $00 , //  stack space for stack parameters
670+  { + 74:}  	 $00 , $00 , $00 , $00 , //  Self
671+  { + 78:}  	 $00 , $00 , $00 , $00   //  Method
672+ );
673+ const 
674+   ARM_INSTRUCTION_SIZE = 4 ;
675+   ARM_ARGUMENT_COUNT_IN_REGISTERS = 4 ;
676+ var 
677+   P, Q: PByte;
678+   LLiteralPool: TArray<pointer>;
679+   I: Integer;
569680begin 
570-   FreeCodeMem(Proc);
681+   GetCodeMem(Q, SizeOf(S1));
682+   P := Q;
683+   Move(S1, P^, SizeOf(S1));
684+ 
685+   LLiteralPool := TArray<pointer>.Create(
686+     Pointer((ArgNum - ARM_ARGUMENT_COUNT_IN_REGISTERS) * ARM_INSTRUCTION_SIZE),
687+     Self,
688+     Method);
689+ 
690+   Inc(P, Length(S1) - (Length(LLiteralPool) * SizeOf(pointer)));
691+   for  I := Low(LLiteralPool) to  High(LLiteralPool) do  begin 
692+     Move(LLiteralPool[I], P^, SizeOf(pointer));
693+     Inc(P, SizeOf(pointer));
694+   end ;
695+ 
696+   Result := Pointer(Q); // set arm mode
571697end ;
698+ { $ENDIF CPUARM32} 
572699
573- procedure  FreeCallBacks ;
700+ { $IFDEF CPUARM64} 
701+ function   GetCallBack (Self: TObject; Method: Pointer; ArgNum: Integer;
702+   CallType: TCallType): Pointer;
703+ const 
704+   S1: array [0 ..79 ] of  byte = (
705+ // big-endian
706+ // offset  <_start>:
707+   $fd, $7b, $bf, $a9,  // 	stp	x29, x30, [sp, #-16]!
708+   $fd, $03 , $00 , $91 ,  // 	mov	x29, sp
709+   $e0, $07 , $bf, $a9,  // 	stp	x0, x1, [sp, #-16]!
710+   $e2, $0f , $bf, $a9,  // 	stp	x2, x3, [sp, #-16]!
711+   $e4, $17 , $bf, $a9,  // 	stp	x4, x5, [sp, #-16]!
712+   $e6, $1f , $bf, $a9,  // 	stp	x6, x7, [sp, #-16]!
713+   $0a, $00 , $00 , $10 ,  // 	adr	x10, #0 <_start+0x18>
714+   $40 , $15 , $40 , $f9,  // 	ldr	x0, [x10, #40]
715+   $49 , $19 , $40 , $f9,  // 	ldr	x9, [x10, #48]
716+   $e7, $2f , $c1, $a8,  // 	ldp	x7, x11, [sp], #16
717+   $e5, $1b, $c1, $a8,  // 	ldp	x5, x6, [sp], #16
718+   $e3, $13 , $c1, $a8,  // 	ldp	x3, x4, [sp], #16
719+   $e1, $0b, $c1, $a8,  // 	ldp	x1, x2, [sp], #16
720+   $20 , $01 , $3f , $d6,  // 	blr	x9
721+   $fd, $7b, $c1, $a8,  // 	ldp	x29, x30, [sp], #16
722+   $c0, $03 , $5f , $d6,  // 	ret
723+   $00 , $00 , $00 , $00 ,  // 	.word	0x00000000 //Self
724+   $00 , $00 , $00 , $00 ,  // 	.word	0x00000000
725+   $00 , $00 , $00 , $00 ,  // 	.word	0x00000000 //Method
726+   $00 , $00 , $00 , $00    // 	.word	0x00000000
727+ );
574728var 
575-   page, nextpage: PCodeMemPage;
729+   P, Q: PByte;
730+   LLiteralPool: TArray<pointer>;
731+   I: Integer;
576732begin 
577-   //  free each allocated page
578-   page := CodeMemPages;
579-   while  page <> nil  do 
580-   begin 
581-     nextpage := page^.Next;
733+   GetCodeMem(Q, SizeOf(S1));
734+   P := Q;
735+   Move(S1, P^, SizeOf(S1));
582736
583-     //  free the memory
584-   { $IFDEF MSWINDOWS} 
585-     VirtualFree(page, 0 , MEM_RELEASE);
586-   { $ELSE} 
587- 	// FreeMem(page);
588-     munmap(page,PageSize);
589-   { $ENDIF}  		  
737+   LLiteralPool := TArray<pointer>.Create(Self, Method);
590738
591-     page := nextpage;
739+   Inc(P, Length(S1) - (Length(LLiteralPool) * SizeOf(pointer)));
740+   for  I := Low(LLiteralPool) to  High(LLiteralPool) do  begin 
741+     Move(LLiteralPool[I], P^, SizeOf(pointer));
742+     Inc(P, SizeOf(pointer));
592743  end ;
593-   CodeMemPages := nil ;
744+ 
745+   { $IF DEFINED(OSX) AND DEFINED(CPUARM64)} 
746+     { 
747+       macOS for M1 has a bug (Apple Feedback FB8994773) in which mprotect 
748+       rejects a permission change from NONE -> RWX. 
749+       Solution: give RW permission, make memory changes, then change RW to X 
750+     }  
751+     // X permission to the entire page for executions...
752+     if  mprotect(CodeMemPages, PageSize, PROT_EXEC) <> 0  then 
753+       raise EMProtectError.CreateFmt(' MProtect error: %s'  , [
754+         SysErrorMessage(GetLastError())]);
755+   { $IFEND} 
756+ 
757+   Result := Pointer(Q); // set arm mode
594758end ;
759+ { $ENDIF CPUARM64} 
595760
596761initialization 
762+ 
597763finalization 
598764  FreeCallBacks;
765+ 
599766end .
0 commit comments