17
17
(* Morgan Martinet (p4d@mmm-experts.com) *)
18
18
(* Samuel Iseli (iseli@vertec.ch) *)
19
19
(* Andrey Gruzdev (andrey.gruzdev@gmail.com) *)
20
+ (* Lucas Belo (lucas.belo@live.com) *)
20
21
(* *************************************************************************)
21
22
(* This source code is distributed with no WARRANTY, for no reason or use.*)
22
23
(* Everyone is allowed to use and change this code free, as long as this *)
@@ -32,7 +33,7 @@ interface
32
33
uses SysUtils;
33
34
34
35
type
35
- TCallType = (ctSTDCALL, ctCDECL);
36
+ TCallType = (ctSTDCALL, ctCDECL, ctARMSTD );
36
37
TCallBack = procedure of object ;
37
38
38
39
function GetCallBack ( self: TObject; method: Pointer;
@@ -127,6 +128,9 @@ implementation
127
128
PtrCalcType = NativeInt;
128
129
{ $ENDIF}
129
130
131
+ EMProtectError = class (Exception)
132
+ end ;
133
+
130
134
{ $IFNDEF MSWINDOWS}
131
135
{ $IFDEF FPC}
132
136
function 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);
151
155
var
152
156
page: PCodeMemPage;
153
157
block: PCodeMemBlock;
158
+ { $IFNDEF MSWINDOWS}
159
+ flags: integer;
160
+ { $ENDIF}
154
161
begin
155
162
// ---allocates Block from executable memory
156
163
// executable memory is requested in pages via VirtualAlloc
@@ -174,13 +181,40 @@ procedure GetCodeMem(var ptr: PByte; size: integer);
174
181
ptr := nil ;
175
182
exit;
176
183
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}
179
199
page^.next:=CodeMemPages;
180
200
CodeMemPages:=page;
181
201
// init pointer to end of page
182
202
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())]);
183
214
end ;
215
+ { $ELSE}
216
+ end ;
217
+ { $IFEND}
184
218
185
219
// ---blocks are assigned starting from the end of the page
186
220
block:=Pointer(PtrCalcType(page^.codeBlocks) - (size + sizeof(PCodeMemBlock)));
@@ -258,6 +292,34 @@ function CodeMemPageCount: integer;
258
292
end ;
259
293
end ;
260
294
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
+
261
323
function GetOfObjectCallBack ( CallBack: TCallBack;
262
324
argnum: Integer; calltype: TCallType): Pointer;
263
325
begin
@@ -266,15 +328,17 @@ function GetOfObjectCallBack( CallBack: TCallBack;
266
328
argnum, calltype);
267
329
end ;
268
330
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}
278
342
279
343
{ $IFDEF SIMPLE_32_BIT_CALLBACK}
280
344
// win32 inplementation
@@ -565,35 +629,138 @@ function GetCallBack( self: TObject; method: Pointer;
565
629
end ;
566
630
{ $ENDIF}
567
631
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;
569
680
begin
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
571
697
end ;
698
+ { $ENDIF CPUARM32}
572
699
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
+ );
574
728
var
575
- page, nextpage: PCodeMemPage;
729
+ P, Q: PByte;
730
+ LLiteralPool: TArray<pointer>;
731
+ I: Integer;
576
732
begin
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));
582
736
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);
590
738
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));
592
743
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
594
758
end ;
759
+ { $ENDIF CPUARM64}
595
760
596
761
initialization
762
+
597
763
finalization
598
764
FreeCallBacks;
765
+
599
766
end .
0 commit comments