1717(* Morgan Martinet (p4d@mmm-experts.com) *)
1818(* Samuel Iseli (iseli@vertec.ch) *)
1919(* Andrey Gruzdev (andrey.gruzdev@gmail.com) *)
20+ (* Lucas Belo (lucas.belo@live.com) *)
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