-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathdynarmic.cpp
More file actions
2223 lines (2064 loc) · 81.2 KB
/
dynarmic.cpp
File metadata and controls
2223 lines (2064 loc) · 81.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <array>
#include <cstdint>
#include <cstdio>
#include <exception>
#include <iostream>
#include <assert.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <mach-o/getsect.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/reloc.h>
#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
#include <dirent.h>
#include <dlfcn.h>
#include <signal.h>
#include <sys/attr.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/xattr.h>
#include <libgen.h>
#include "mach_private.h"
#include "codesign.h"
#include "dynarmic.h"
#include "32bit.h"
#define IGNORE_BAD_MEM_ACCESS 1
#define TRACE_RW 0
#define TRACE_BRANCH 0
#define TRACE_SVC 0
//#define TRACE_ALLOC 0
//#define fprintf(...)
//#define printf(...)
#define CS_OPS_STATUS 0
#define CS_ENFORCEMENT 0x00001000
#define msgh_request_port msgh_remote_port
#define msgh_reply_port msgh_local_port
struct symbolicated_call {
u32 address;
u32 symbolOffset;
const char *symbolName;
const char *imageName;
};
extern "C"
int return_with_carry(int result, bool carry) {
threadHandle.cpsr->setCarry(carry);
return carry ? errno : result;
}
extern "C"
int return_with_carry_direct(int result, bool carry) {
threadHandle.cpsr->setCarry(carry);
return result;
}
extern "C"
int syscallRetCarry(long syscall, ...);
__asm__(" \
_syscallRetCarry: \n \
mov x16, x0 \n \
ldp x0, x1, [sp] \n \
ldp x2, x3, [sp, #0x10] \n \
ldp x4, x5, [sp, #0x20] \n \
ldr x6, [sp, #0x30] \n \
svc #0x80 \n \
mov x1, #0 \n \
b.lo LcarryClear\n \
mov x1, #1 \n \
LcarryClear: \n \
b _return_with_carry_direct \n \
");
// guest syscalls
int guest_csops(pid_t pid, unsigned int ops, u32 guest_useraddr, size_t usersize) {
char *host_useraddr = (char *)malloc(usersize);
int result = syscallRetCarry(SYS_csops, pid, ops, host_useraddr, usersize, 0,0,0);
if(ops == CS_OPS_STATUS) {
// remove code signature enforcement
*(uint32_t *)host_useraddr &= ~CS_ENFORCEMENT;
}
Dynarmic_mem_1write(guest_useraddr, usersize, host_useraddr);
free(host_useraddr);
return result;
}
int guest_getrlimit(int resource, u32 guest_rlp) {
struct rlimit host_rlp;
int result = syscallRetCarry(SYS_getrlimit, resource, &host_rlp, 0,0,0,0,0);
Dynarmic_mem_1write(guest_rlp, sizeof(host_rlp), (char *)&host_rlp);
return result;
}
u32 guest_mmap(u32 guest_addr, size_t len, int prot, int flags, int fildes, off_t offset) {
len = ALIGN_DYN_SIZE(len);
u32 result = Dynarmic_mmap(guest_addr, len, prot, flags, fildes, offset);
if(result == -1) {
threadHandle.cpsr->setCarry(true);
return errno;
}
return result;
}
int guest___sysctl(u32 guest_name, u_int namelen, u32 guest_oldp, u32 guest_oldlenp, u32 guest_newp, size_t newlen) {
// TODO: fake stuff like CPU architecture and KERN_USRSTACK32
int host_name[0x10];
assert(namelen < sizeof(host_name));
Dynarmic_mem_1read(guest_name, sizeof(int) * namelen, (char *)host_name);
// Guess nothing is larger than 1kb
size_t host_oldlenp;
char host_oldp[0x400];
char host_newp[0x400];
assert(newlen <= sizeof(host_newp));
if(guest_newp) {
Dynarmic_mem_1read(guest_newp, newlen, host_newp);
}
int result = syscallRetCarry(SYS_sysctl,
host_name, namelen,
guest_oldp ? &host_oldp : NULL,
guest_oldlenp ? &host_oldlenp : 0,
guest_newp ? (int *)host_newp : NULL, newlen,
0
);
if(guest_oldp) {
Dynarmic_mem_1write(guest_oldp, host_oldlenp, host_oldp);
sharedHandle.ucb->MemoryWrite32(guest_oldlenp, host_oldlenp);
}
return result;
}
int guest___sysctlbyname(u32 guest_name, u_int namelen, u32 guest_oldp, u32 guest_oldlenp, u32 guest_newp, size_t newlen) {
// TODO: fake stuff like CPU architecture and KERN_USRSTACK32
DynarmicHostString host_name(guest_name);
// Guess nothing is larger than 1kb
size_t host_oldlenp;
char host_oldp[0x400];
char host_newp[0x400];
assert(newlen <= sizeof(host_newp));
if(guest_newp) {
Dynarmic_mem_1read(guest_newp, newlen, host_newp);
}
int result = syscallRetCarry(SYS_sysctlbyname,
host_name.hostPtr, namelen,
guest_oldp ? &host_oldp : NULL,
guest_oldlenp ? &host_oldlenp : 0,
guest_newp ? (int *)host_newp : NULL, newlen,
0
);
if(guest_oldp) {
Dynarmic_mem_1write(guest_oldp, host_oldlenp, host_oldp);
sharedHandle.ucb->MemoryWrite32(guest_oldlenp, host_oldlenp);
}
return result;
}
int guest_getattrlist(u32 guest_path, u32 guest_attrList, u32 guest_attrBuf, size_t attrBufSize, unsigned long options) {
char host_path[PATH_MAX];
sharedHandle.fs->pathGuestToHost(guest_path, host_path);
struct attrlist host_attrList;
Dynarmic_mem_1read(guest_attrList, sizeof(struct attrlist), (char *)&host_attrList);
char *host_attrBuf = (char *)malloc(attrBufSize);
int result = syscallRetCarry(SYS_getattrlist, host_path, &host_attrList, host_attrBuf, attrBufSize, options, 0,0);
Dynarmic_mem_1write(guest_attrBuf, attrBufSize, host_attrBuf);
free(host_attrBuf);
return result;
}
int guest_shm_open(u32 guest_name, int oflag, int mode) {
DynarmicHostString host_name(guest_name);
printf("LC32: shm_open %s\n", host_name.hostPtr);
return syscallRetCarry(SYS_shm_open, host_name.hostPtr, oflag, mode);
}
int guest_pthread_getugid_np(u32 uid, u32 gid) {
uid_t host_uid, host_gid;
int result = pthread_getugid_np(&host_uid, &host_gid);
sharedHandle.ucb->MemoryWrite32(uid, host_uid);
sharedHandle.ucb->MemoryWrite32(gid, host_gid);
return result;
}
#define MACH_MSG_UNION(function, name) \
union MachMessage_##function { \
__Request__##function##_t In; \
__Reply__##function##_t Out; \
} *name = (MachMessage_##function *)host_header
// FIXME: cannot call mach_msg(2)_trap directly
mach_msg_return_t
guest_mach_msg_trap(u32 guest_msg,
mach_msg_option_t option,
mach_msg_size_t send_size,
mach_msg_size_t rcv_size,
mach_port_t rcv_name,
mach_msg_timeout_t timeout,
mach_port_t notify) {
mach_msg_return_t result = MACH_MSG_SUCCESS;
char *host_msg = (char *)malloc(MAX(send_size, rcv_size));
Dynarmic_mem_1read(guest_msg, send_size, host_msg);
mach_msg_header_t *host_header = (mach_msg_header_t *)host_msg;
printf("LC32: mach_msg_trap id %d\n", host_header->msgh_id);
// pre-process reply header
host_header->msgh_bits &= 0xff;
switch(host_header->msgh_id) {
case 0: {
result = MACH_SEND_INVALID_HEADER; // TODO
break;
}
case 200: {
MACH_MSG_UNION(host_info, Mess);
if(Mess->In.flavor == HOST_PRIORITY_INFO) {
result = host_info(Mess->In.Head.msgh_request_port, Mess->In.flavor, (host_info_t)Mess->Out.host_info_out, &Mess->Out.host_info_outCnt);
host_header->msgh_size = sizeof(Mess->Out) - sizeof(Mess->Out.host_info_out) + sizeof(Mess->Out.host_info_out[0])*Mess->Out.host_info_outCnt;
Mess->Out.RetCode = result;
} else {
printf("LC32: Unhandled flavor %d\n", Mess->In.flavor);
sharedHandle.ucb->ExceptionRaised(0xDEADDEAD, Dynarmic::A32::Exception::Yield);
}
break;
}
case 206: {
MACH_MSG_UNION(host_get_clock_service, Mess);
host_header->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
host_header->msgh_size = sizeof(Mess->Out);
result = host_get_clock_service(Mess->In.Head.msgh_request_port, Mess->In.clock_id, &Mess->Out.clock_serv.name);
Mess->Out.clock_serv.type = MACH_MSG_PORT_DESCRIPTOR;
Mess->Out.clock_serv.disposition = 17;
Mess->Out.msgh_body.msgh_descriptor_count = 1;
break;
}
case 412: {
MACH_MSG_UNION(host_get_special_port, Mess);
host_header->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
host_header->msgh_size = sizeof(Mess->Out);
result = host_get_special_port(Mess->In.Head.msgh_request_port, Mess->In.node, Mess->In.which, &Mess->Out.port.name);
Mess->Out.port.type = MACH_MSG_PORT_DESCRIPTOR;
Mess->Out.port.disposition = 17;
Mess->Out.msgh_body.msgh_descriptor_count = 1;
break;
}
case DYLD_PROCESS_INFO_NOTIFY_LOAD_ID: {
const dyld_process_info_notify_header *Mess = (dyld_process_info_notify_header *)host_header;
const dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)((uintptr_t)Mess + Mess->imagesOffset);
uintptr_t stringPool = (uintptr_t)Mess + Mess->stringsOffset;
for(unsigned i=0; i < Mess->imageCount; ++i) {
u32 imageAddress = entries[i].loadAddress;
char *imagePath = (char *)(stringPool + entries[i].pathStringOffset);
// Find __TEXT size
struct segment_command *seg = (struct segment_command *)((uintptr_t)get_memory(imageAddress) + sizeof(struct mach_header));
while(seg->cmd != LC_SEGMENT || strcmp(seg->segname, SEG_TEXT) != 0){
seg = (struct segment_command *)((uintptr_t)seg + seg->cmdsize);
}
guestMappings[guestMappingLen].name = strdup(basename(imagePath));
guestMappings[guestMappingLen].start = imageAddress;
guestMappings[guestMappingLen].end = imageAddress + seg->vmsize;
guestMappings[guestMappingLen].hostAddr = (uintptr_t)get_memory(imageAddress);
printf("LC32: added image %s (0x%08x-0x%08x)\n", guestMappings[guestMappingLen].name, guestMappings[guestMappingLen].start, guestMappings[guestMappingLen].end);
guestMappingLen++;
}
__attribute__((fallthrough));
}
case DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID:
case DYLD_PROCESS_INFO_NOTIFY_MAIN_ID: {
host_header->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
host_header->msgh_id = 0;
host_header->msgh_local_port = MACH_PORT_NULL;
host_header->msgh_reserved = 0;
host_header->msgh_size = sizeof(*host_header);
break;
}
case 3409: {
MACH_MSG_UNION(task_get_special_port, Mess);
host_header->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
host_header->msgh_size = sizeof(Mess->Out);
result = task_get_special_port(Mess->In.Head.msgh_request_port, Mess->In.which_port, &Mess->Out.special_port.name);
Mess->Out.special_port.type = MACH_MSG_PORT_DESCRIPTOR;
Mess->Out.special_port.disposition = 17;
Mess->Out.msgh_body.msgh_descriptor_count = 1;
break;
}
case 3410: {
MACH_MSG_UNION(task_set_special_port, Mess);
Mess->Out.RetCode = task_set_special_port(Mess->In.Head.msgh_request_port, Mess->In.which_port, Mess->In.special_port.name);
break;
}
case 3418: {
MACH_MSG_UNION(semaphore_create, Mess);
host_header->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
host_header->msgh_size = sizeof(Mess->Out);
result = semaphore_create(Mess->In.Head.msgh_request_port, &Mess->Out.semaphore.name, Mess->In.policy, Mess->In.value);
Mess->Out.semaphore.type = MACH_MSG_PORT_DESCRIPTOR;
Mess->Out.semaphore.disposition = 17;
Mess->Out.msgh_body.msgh_descriptor_count = 1;
break;
}
case 3444: {
MACH_MSG_UNION(task_register_dyld_image_infos, Mess);
host_header->msgh_size = sizeof(Mess->Out);
Mess->Out.RetCode = KERN_SUCCESS;
break;
}
case 3447: {
MACH_MSG_UNION(task_register_dyld_shared_cache_image_info, Mess);
host_header->msgh_size = sizeof(Mess->Out);
Mess->Out.RetCode = KERN_SUCCESS;
break;
}
case 78945670: {
MACH_MSG_UNION(_notify_server_register_check, Mess);
// this Mach trap is missing on arm64
host_header->msgh_size = sizeof(Mess->Out);
Mess->Out.size = 0;
Mess->Out.slot = 0;
Mess->Out.token = 0;
Mess->Out.status = 0;
Mess->Out.RetCode = 0;
break;
}
case 0x10000000: {
// _xpc_send_serializer: we can pass directly
result = mach_msg_send(host_header);
// this function does not modify the message buffer, return directly
free(host_msg);
return result;
}
default:
printf("LC32: Unhandled msgh_id\n");
sharedHandle.ucb->ExceptionRaised(0xDEADDEAD, Dynarmic::A32::Exception::Yield);
break;
}
host_header->msgh_reply_port = rcv_name;
host_header->msgh_request_port = 0;
host_header->msgh_id += 100; // reply Id always equals reqId+100
Dynarmic_mem_1write(guest_msg, rcv_size, host_msg);
free(host_msg);
return result;
}
int guest_getdirentries64(int fd, u32 guest_buf, int nbytes, u32 guest_basep) {
char *host_buf = (char *)malloc(nbytes);
__darwin_off_t host_basep = (__darwin_off_t)sharedHandle.ucb->MemoryRead32(guest_basep); // is reading needed?
// FIXME: is this correct?
int result = syscallRetCarry(SYS_getdirentries64, fd, host_buf, nbytes, &host_basep, 0,0,0);
Dynarmic_mem_1write(guest_buf, nbytes, host_buf);
sharedHandle.ucb->MemoryWrite64(guest_basep, host_basep);
free(host_buf);
return result;
}
void guest_stat_copy(struct stat *host_buf, struct stat_32 *host_buf_32) {
host_buf_32->st_dev = host_buf->st_dev;
host_buf_32->st_mode = host_buf->st_mode;
host_buf_32->st_nlink = host_buf->st_nlink;
host_buf_32->st_ino = host_buf->st_ino;
host_buf_32->st_uid = host_buf->st_uid;
host_buf_32->st_gid = host_buf->st_gid;
host_buf_32->st_rdev = host_buf->st_rdev;
// Y2038???
host_buf_32->st_atimespec.tv_sec = host_buf->st_atimespec.tv_sec;
host_buf_32->st_atimespec.tv_nsec = host_buf->st_atimespec.tv_nsec;
host_buf_32->st_mtimespec.tv_sec = host_buf->st_mtimespec.tv_sec;
host_buf_32->st_mtimespec.tv_nsec = host_buf->st_mtimespec.tv_nsec;
host_buf_32->st_ctimespec.tv_sec = host_buf->st_ctimespec.tv_sec;
host_buf_32->st_ctimespec.tv_nsec = host_buf->st_ctimespec.tv_nsec;
host_buf_32->st_birthtimespec.tv_sec = host_buf->st_birthtimespec.tv_sec;
host_buf_32->st_birthtimespec.tv_nsec = host_buf->st_birthtimespec.tv_nsec;
host_buf_32->st_size = host_buf->st_size;
host_buf_32->st_blocks = host_buf->st_blocks;
host_buf_32->st_blksize = host_buf->st_blksize;
host_buf_32->st_flags = host_buf->st_flags;
host_buf_32->st_gen = host_buf->st_gen;
host_buf_32->st_lspare = host_buf->st_lspare;
host_buf_32->st_qspare[0] = host_buf->st_qspare[0];
host_buf_32->st_qspare[1] = host_buf->st_qspare[1];
}
int guest_stat64(u32 guest_path, u32 guest_buf) {
char host_path[PATH_MAX];
sharedHandle.fs->pathGuestToHost(guest_path, host_path);
struct stat host_buf;
struct stat_32 host_buf_32;
int result = stat(host_path, &host_buf);
if(result == 0) {
guest_stat_copy(&host_buf, &host_buf_32);
Dynarmic_mem_1write(guest_buf, sizeof(struct stat_32), (char *)&host_buf_32);
}
return return_with_carry(result, result != 0);
}
int guest_fstat(int fildes, u32 guest_buf) {
struct stat host_buf;
struct stat_32 host_buf_32;
int result = fstat(fildes, &host_buf);
if(result == 0) {
guest_stat_copy(&host_buf, &host_buf_32);
Dynarmic_mem_1write(guest_buf, sizeof(struct stat_32), (char *)&host_buf_32);
}
return return_with_carry(result, result != 0);
}
int guest_lstat(u32 guest_path, u32 guest_buf) {
struct stat host_buf;
struct stat_32 host_buf_32;
char host_path[PATH_MAX];
sharedHandle.fs->pathGuestToHost(guest_path, host_path);
int result = lstat(host_path, &host_buf);
if(result == 0) {
guest_stat_copy(&host_buf, &host_buf_32);
Dynarmic_mem_1write(guest_buf, sizeof(struct stat_32), (char *)&host_buf_32);
}
return return_with_carry(result, result != 0);
}
int guest_statfs64(u32 guest_path, u32 guest_buf) {
char host_path[PATH_MAX];
sharedHandle.fs->pathGuestToHost(guest_path, host_path);
struct statfs host_buf;
int result = syscallRetCarry(SYS_statfs, host_path, &host_buf, 0,0,0,0,0);
if(result == 0) {
Dynarmic_mem_1write(guest_buf, sizeof(struct statfs), (char *)&host_buf);
}
return result;
}
int guest_fstatfs64(int fildes, u32 guest_buf) {
struct statfs host_buf;
int result = syscallRetCarry(SYS_fstatfs, fildes, &host_buf, 0,0,0,0,0);
if(result == 0) {
Dynarmic_mem_1write(guest_buf, sizeof(struct statfs), (char *)&host_buf);
}
return result;
}
u32 guest_bsdthread_thread_start;
int guest_bsdthread_pthread_size;
int guest_bsdthread_register(u32 guest_func_thread_start, u32 guest_func_start_wqthread, int pthread_size, u32 data, int32_t datasize, off_t offset) {
guest_bsdthread_thread_start = guest_func_thread_start;
guest_bsdthread_pthread_size = pthread_size;
return return_with_carry(PTHREAD_FEATURE_FINEPRIO | PTHREAD_FEATURE_BSDTHREADCTL | PTHREAD_FEATURE_SETSELF | PTHREAD_FEATURE_QOS_MAINTENANCE | PTHREAD_FEATURE_QOS_DEFAULT, false);
}
int guest_sandbox_ms(u32 guest_policyname, int call, u32 guest_arg) {
// TODO: ???
char host_policyname[0x20];
Dynarmic_mem_1read(guest_policyname, sizeof(host_policyname), host_policyname);
printf("sandbox(%s, %d)\n", host_policyname, call);
return 0;
}
int guest_getentropy(u32 guest_buffer, u32 length) {
char *host_buffer = (char *)malloc(length);
int result = syscallRetCarry(SYS_getentropy, (void *)host_buffer, length, 0,0,0,0,0);
Dynarmic_mem_1write(guest_buffer, length, host_buffer);
free(host_buffer);
return result;
}
int guest_connect(int socket, u32 guest_address, socklen_t address_len) {
// See https://developer.apple.com/forums/thread/756756?answerId=790507022#790507022
// sockaddr_un.sun_path has an artificial limit is 104 bytes, however it allows up to 253 bytes
if(address_len > SOCK_MAXADDRLEN) {
return return_with_carry_direct(EINVAL, true);
}
char host_address[SOCK_MAXADDRLEN];
Dynarmic_mem_1read(guest_address, address_len, host_address);
int type;
socklen_t length = sizeof(int);
getsockopt(socket, SOL_SOCKET, SO_TYPE, &type, &length);
if(type == SOCK_DGRAM) {
char host_path[PATH_MAX];
sockaddr_un *sock = (sockaddr_un *)host_address;
sharedHandle.fs->pathGuestToHost(sock->sun_path, host_path);
if(strlen(host_path) > SOCK_MAXADDRLEN - offsetof(sockaddr_un, sun_path)) {
return return_with_carry_direct(EINVAL, true);
}
strcpy(sock->sun_path, host_path);
address_len = SUN_LEN(sock);
}
return syscallRetCarry(SYS_connect, socket, (const sockaddr *)host_address, address_len, 0,0,0,0);
}
int guest_gettimeofday(u32 guest_tp, u32 guest_tzp) {
// tzp is always null since it's no longer used
//assert(!guest_tzp);
struct timeval host_tp;
int result = syscallRetCarry(SYS_gettimeofday, &host_tp, NULL, 0,0,0,0,0);
Dynarmic_mem_1write(guest_tp, sizeof(host_tp), (char *)&host_tp);
return result;
}
int guest_rename(u32 guest_old, u32 guest_new) {
char host_old[PATH_MAX], host_new[PATH_MAX];
sharedHandle.fs->pathGuestToHost(guest_old, host_old);
sharedHandle.fs->pathGuestToHost(guest_new, host_new);
return syscallRetCarry(SYS_rename, host_old, host_new, 0,0,0,0,0);
}
ssize_t guest_sendto(int socket, const u32 guest_buffer, size_t length, int flags, u32 guest_dest_addr, socklen_t dest_len) {
char *host_buffer = (char *)malloc(length);
char *host_dest_addr = (char *)malloc(dest_len);
Dynarmic_mem_1read(guest_buffer, length, host_buffer);
Dynarmic_mem_1read(guest_dest_addr, dest_len, host_dest_addr);
int result = syscallRetCarry(SYS_sendto, socket, host_buffer, length, flags, (const sockaddr *)host_dest_addr, dest_len, 0);
free(host_buffer);
free(host_dest_addr);
return result;
}
ssize_t guest_pread(int NR, int fildes, u32 guest_buf, size_t nbyte, off_t offset) {
char *host_buf = (char *)malloc(nbyte);
ssize_t result = syscallRetCarry(NR, fildes, host_buf, nbyte, offset, 0,0,0);
Dynarmic_mem_1write(guest_buf, nbyte, host_buf);
free(host_buf);
return result;
}
ssize_t guest_read(int NR, int fildes, u32 guest_buf, size_t nbyte) {
char *host_buf = (char *)malloc(nbyte);
ssize_t result = syscallRetCarry(NR, fildes, host_buf, nbyte, 0,0,0,0);
Dynarmic_mem_1write(guest_buf, nbyte, host_buf);
free(host_buf);
return result;
}
ssize_t guest_write(int NR, int fildes, u32 guest_buf, size_t nbyte) {
char *host_buf = (char *)malloc(nbyte);
Dynarmic_mem_1read(guest_buf, nbyte, host_buf);
ssize_t result = syscallRetCarry(NR, fildes, host_buf, nbyte, 0,0,0,0);
free(host_buf);
return result;
}
ssize_t guest_writev(int NR, int fildes, u32 guest_iov, int iovcnt) {
size_t iovsize = sizeof(iovec_32) * iovcnt;
iovec_32 *host_iov = (iovec_32 *)malloc(iovsize);
Dynarmic_mem_1read(guest_iov, iovsize, (char *)host_iov);
ssize_t result = 0;
for (int i = 0; i < iovcnt; i++) {
result += guest_write(NR == SYS_writev ? SYS_write : SYS_write_nocancel, fildes, host_iov[i].guest_iov_base, host_iov[i].iov_len);
}
free(host_iov);
return result;
}
int guest_open(int NR, u32 guest_path, int oflag, int mode) {
char host_path[PATH_MAX];
sharedHandle.fs->pathGuestToHost(guest_path, host_path);
int result = syscallRetCarry(NR, host_path, oflag, mode, 0,0,0,0);
return result;
}
int guest_unlink(u32 guest_path) {
char host_path[PATH_MAX];
sharedHandle.fs->pathGuestToHost(guest_path, host_path);
return syscallRetCarry(SYS_unlink, host_path, 0,0,0,0,0,0);
}
int guest_chmod(u32 guest_path, mode_t mode) {
char host_path[PATH_MAX];
sharedHandle.fs->pathGuestToHost(guest_path, host_path);
return syscallRetCarry(SYS_chmod, host_path, mode, 0,0,0,0,0);
}
int guest_chown(u32 guest_path, uid_t owner, gid_t group) {
char host_path[PATH_MAX];
sharedHandle.fs->pathGuestToHost(guest_path, host_path);
return syscallRetCarry(SYS_chown, host_path, owner, group, 0,0,0,0);
}
int guest_access(u32 guest_path, int mode) {
char host_path[PATH_MAX];
sharedHandle.fs->pathGuestToHost(guest_path, host_path);
return syscallRetCarry(SYS_access, host_path, mode, 0,0,0,0,0);
}
int guest_sigaction(int sig, u32 guest_act, u32 guest_oact) {
static sigaction_32 host_actions[SIGUSR2 + 1];
if (guest_oact) {
Dynarmic_mem_1write(guest_oact, sizeof(sigaction_32), (char *)&host_actions[sig]);
}
if (guest_act) {
printf("LC32: sigaction: 0x%08x -> ", host_actions[sig]._sa_handler);
Dynarmic_mem_1read(guest_act, sizeof(sigaction_32), (char *)&host_actions[sig]);
printf("LC32: 0x%08x\n", host_actions[sig]._sa_handler);
}
return 0;
}
int guest_sigprocmask(int how, u32 guest_set, u32 guest_oldset) {
sigset_t host_set = guest_set ? sharedHandle.ucb->MemoryRead32(guest_set) : 0;
sigset_t host_oldset = 0;
int result = syscallRetCarry(SYS_sigprocmask, how, guest_set ? &host_set : NULL, &host_oldset, 0,0,0,0);
if (guest_oldset) {
sharedHandle.ucb->MemoryWrite32(guest_oldset, host_oldset);
}
return result;
}
int guest_ioctl(int fildes, u32 request, u32 guest_r2) {
switch(request) {
case TIOCSCTTY:
case TIOCEXCL:
case TIOCSBRK:
case TIOCCBRK:
case TIOCPTYGRANT:
case TIOCPTYUNLK:
//case DTRACEHIOC_REMOVE:
//case BIOCFLUSH:
//case BIOCPROMISC:
return syscallRetCarry(SYS_ioctl, fildes, request, guest_r2, 0,0,0,0);
case FIODTYPE:
int host_r2;
int result = syscallRetCarry(SYS_ioctl, fildes, request, &host_r2, 0,0,0,0);
sharedHandle.ucb->MemoryWrite32(guest_r2, host_r2);
return result;
}
printf("Unhandled ioctl request: %d (0x%x)\n", request, request);
sharedHandle.ucb->ExceptionRaised(0xDEADDEAD, Dynarmic::A32::Exception::Yield);
return -1;
}
int guest_pthread_sigmask(int how, u32 guest_set, u32 guest_oldset) {
sigset_t host_set = guest_set ? sharedHandle.ucb->MemoryRead32(guest_set) : 0;
sigset_t host_oldset = 0;
int result = pthread_sigmask(how, guest_set ? &host_set : NULL, &host_oldset);
if (guest_oldset) {
sharedHandle.ucb->MemoryWrite32(guest_oldset, host_oldset);
}
// FIXME: does it need carry bit upon error?
return result;
}
ssize_t guest_readlink(u32 guest_pathname, u32 guest_buf, size_t bufsiz) {
char host_pathname[PATH_MAX];
sharedHandle.fs->pathGuestToHost(guest_pathname, host_pathname);
char *host_buf = (char *)malloc(bufsiz);
int result = syscallRetCarry(SYS_readlink, host_pathname, host_buf, bufsiz, 0,0,0,0);
sharedHandle.fs->pathHostToGuest(host_buf, guest_buf);
Dynarmic_mem_1write(guest_buf, bufsiz, host_buf);
free(host_buf);
return result;
}
int guest_munmap(u32 guest_addr, size_t len) {
int result = Dynarmic_munmap(guest_addr, len);
if(result == -1) {
threadHandle.cpsr->setCarry(true);
return errno;
}
return result;
}
int guest_mprotect(u32 guest_addr, size_t len, int prot) {
int result = Dynarmic_mprotect(guest_addr, len, prot);
if(result == -1) {
threadHandle.cpsr->setCarry(true);
return errno;
}
return result;
}
int guest_fcntl(int fildes, int cmd, u32 guest_r2) {
switch (cmd) {
// r2 is null or is a literal
case F_DUPFD:
case F_GETFD:
case F_SETFD:
case F_GETFL:
case F_SETFL:
case F_GETOWN:
case F_SETOWN:
case F_RDAHEAD:
case F_NOCACHE:
case F_FULLFSYNC:
return syscallRetCarry(SYS_fcntl, fildes, cmd, guest_r2, 0,0,0,0);
case F_ADDFILESIGS_RETURN:
// fsig->fs_file_start = 0xFFFFFFFF;
sharedHandle.ucb->MemoryWrite32(guest_r2, 0xFFFFFFFF);
return 0;
case F_CHECK_LV:
return 0;
// r2 is a pointer
case F_GETPATH: {
char host_r2[PATH_MAX];
int result = syscallRetCarry(SYS_fcntl, fildes, cmd, host_r2, 0,0,0,0);
sharedHandle.fs->pathHostToGuest(host_r2, guest_r2);
return result;
}
case F_PREALLOCATE: {
fstore_t host_r2;
Dynarmic_mem_1read(guest_r2, sizeof(fstore_t), (char *)&host_r2);
return syscallRetCarry(SYS_fcntl, fildes, cmd, &host_r2, 0,0,0,0);
}
case F_SETSIZE: {
off_t host_r2 = sharedHandle.ucb->MemoryRead64(guest_r2);
return syscallRetCarry(SYS_fcntl, fildes, cmd, &host_r2);
}
case F_RDADVISE: {
struct radvisory host_r2;
Dynarmic_mem_1read(guest_r2, sizeof(struct radvisory), (char *)&host_r2);
return syscallRetCarry(SYS_fcntl, fildes, cmd, &host_r2, 0,0,0,0);
}
//case F_READBOOTSTRAP:
//case F_WRITEBOOTSTRAP:
case F_LOG2PHYS: {
struct log2phys host_r2;
Dynarmic_mem_1read(guest_r2, sizeof(struct log2phys), (char *)&host_r2);
int result = syscallRetCarry(SYS_fcntl, fildes, cmd, &host_r2, 0,0,0,0);
Dynarmic_mem_1write(guest_r2, sizeof(struct log2phys), (char *)&host_r2);
return result;
}
default:
printf("Unhandled fcntl command: %d\n", cmd);
sharedHandle.ucb->ExceptionRaised(0xDEADDEAD, Dynarmic::A32::Exception::Yield);
return syscallRetCarry(SYS_fcntl, fildes, cmd, guest_r2, 0,0,0,0);
}
}
int guest_proc_info(int callnum, int pid, int flavor, uint64_t arg, u32 guest_buffer, int buffersize) {
// FIXME: check buffer size
char *host_buffer = (char *)malloc(buffersize);
int result = syscallRetCarry(SYS_proc_info, callnum, pid, flavor, arg, host_buffer, buffersize, 0);
if(callnum == 2 && flavor == PROC_PIDT_SHORTBSDINFO) {
proc_bsdshortinfo *info = (proc_bsdshortinfo *)host_buffer;
info->pbsi_flags |= 2; // set PROC_FLAG_TRACED. FIXME: without this, it will crash
info->pbsi_flags &= ~0x10; // unset PROC_FLAG_LP64
}
Dynarmic_mem_1write(guest_buffer, buffersize, host_buffer);
free(host_buffer);
return result;
}
int guest_mach_timebase_info(u32 guest_info) {
struct mach_timebase_info host_info;
int result = mach_timebase_info(&host_info);
Dynarmic_mem_1write(guest_info, sizeof(host_info), (char *)&host_info);
return result;
}
kern_return_t guest_host_create_mach_voucher_trap(mach_port_name_t host, u32 guest_recipes, int recipes_size, u32 guest_voucher) {
// array of bytes
mach_voucher_attr_raw_recipe_array_t host_recipes = (mach_voucher_attr_raw_recipe_array_t)malloc(recipes_size);
Dynarmic_mem_1read(guest_recipes, recipes_size, (char *)host_recipes);
mach_port_name_t host_voucher;
kern_return_t result = host_create_mach_voucher_trap(host, host_recipes, recipes_size, &host_voucher);
sharedHandle.ucb->MemoryWrite32(guest_voucher, host_voucher);
return result;
}
kern_return_t guest__kernelrpc_mach_vm_allocate_trap(u32 target, u32 guest_address, mach_vm_size_t size, int flags) {
if (target != mach_task_self()) {
return KERN_FAILURE;
}
// FIXME: change the behavior of this to ensure re-mmapping works
//int tag = flags >> 24;
bool anywhere = (flags & VM_FLAGS_ANYWHERE) != 0;
if (anywhere) {
// Sometimes the address pointer will contain garbage value, change it to 0
sharedHandle.ucb->MemoryWrite32(guest_address, 0);
}
u32 result = Dynarmic_mmap(sharedHandle.ucb->MemoryRead32(guest_address), size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | (anywhere ? 0 : MAP_FIXED), -1, 0);
if (result == -1) {
/*
if (!anywhere && tag != VM_MEMORY_REALLOC) {
printf("IllegalStateException\n");
abort();
}
*/
return KERN_NO_SPACE;
}
sharedHandle.ucb->MemoryWrite32(guest_address, result);
return KERN_SUCCESS;
}
kern_return_t guest__kernelrpc_mach_port_construct_trap(mach_port_name_t target, u32 guest_options, u64 context, u32 guest_name) {
mach_port_options_t host_options;
mach_port_name_t host_name;
Dynarmic_mem_1read(guest_options, sizeof(host_options), (char *)&host_options);
kern_return_t result = _kernelrpc_mach_port_construct_trap(target, &host_options, context, &host_name);
sharedHandle.ucb->MemoryWrite32(guest_name, host_name);
return result;
}
kern_return_t guest__kernelrpc_mach_port_allocate_trap(mach_port_name_t target, mach_port_right_t right, u32 guest_name) {
mach_port_name_t host_name;
kern_return_t result = _kernelrpc_mach_port_allocate_trap(target, right, &host_name);
sharedHandle.ucb->MemoryWrite32(guest_name, host_name);
return result;
}
kern_return_t guest__kernelrpc_mach_vm_map_trap(mach_port_name_t target, u32 guest_address, mach_vm_size_t size, mach_vm_offset_t mask, int flags, vm_prot_t cur_protection) {
// TODO: verify and round mask accordingly
if (target != mach_task_self()) {
return KERN_FAILURE;
}
bool anywhere = (flags & VM_FLAGS_ANYWHERE) != 0;
if (!anywhere) {
printf("LC32: BackendException: _kernelrpc_mach_vm_map_trap fixed\n");
return KERN_FAILURE;
}
u32 result = Dynarmic_mmap(sharedHandle.ucb->MemoryRead32(guest_address), size, cur_protection, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, mask ?: DYN_PAGE_MASK);
if (result == -1) {
return KERN_NO_SPACE;
}
sharedHandle.ucb->MemoryWrite32(guest_address, result);
return KERN_SUCCESS;
}
kern_return_t guest__kernelrpc_mach_vm_deallocate_trap(u32 target, vm_address_t address, mach_vm_size_t size) {
if (target != mach_task_self()) {
return KERN_FAILURE;
}
return Dynarmic_munmap(address, size) == 0 ? KERN_SUCCESS : KERN_FAILURE;
}
int guest_abort_with_payload(u32 reason_namespace, u64 reason_code, u32 guest_payload, u32 payload_size, u32 guest_reason_string, u64 reason_flags) {
DynarmicHostString host_reason_string(guest_reason_string);
printf("abort_with_payload called with namespace=0x%x, code=0x%llx, reason=%s\n", reason_namespace, reason_code, host_reason_string.hostPtr);
return 0;
}
////////
int guestMappingLen = 0;
guest_file_mapping guestMappings[1000];
static void load_symbols_for_image(guest_file_mapping *mapping, void(^iterator)(u32 address, const char *name)) {
u32 slide = mapping->start; // FIXME: properly calculate this slide
const struct mach_header *header = (const struct mach_header *)mapping->hostAddr;
u32 crashInfoSize;
u64 crash_info = (u32)(u64)getsectdatafromheader(header, SEG_DATA, "__crash_info", &crashInfoSize);
if (crash_info) {
crash_info += slide;
crashreporter_annotations_t host_gCRAnnotations;
Dynarmic_mem_1read(crash_info, sizeof(crashreporter_annotations_t), (char *)&host_gCRAnnotations);
if (host_gCRAnnotations.message) {
char message[0x1000];
Dynarmic_mem_1read(host_gCRAnnotations.message, sizeof(message), message);
printf("Crash message from %s: %s\n", mapping->name, message);
} else if (host_gCRAnnotations.message2) {
printf("gCRAnnotations has message2 but unhandled. Crashing to raise attention\n");
}
}
segment_command *cur_seg_cmd;
struct symtab_command* symtab_cmd = NULL;
uintptr_t cur = (uintptr_t)header + sizeof(mach_header);
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
cur_seg_cmd = (segment_command *)cur;
if (cur_seg_cmd->cmd == LC_SYMTAB) {
symtab_cmd = (struct symtab_command*)cur_seg_cmd;
}
}
if (!symtab_cmd) {
return;
}
// Find base symbol/string table addresses
// FIXME: symbol resolution for dyld shared cache
#if 1
struct nlist *host_symtab = (struct nlist *)((uintptr_t)header + symtab_cmd->symoff);
u64 host_strtab = (u32)((uintptr_t)header + symtab_cmd->stroff);
struct nlist *symtab = (struct nlist *)(mapping->start + symtab_cmd->symoff);
u32 strtab = (u32)(mapping->start + symtab_cmd->stroff);
iterator(mapping->start, "(unknown symbol)");
if(!get_memory(strtab)) {
return;
}
for(int i=0; i < symtab_cmd->nsyms; i++) {
u32 addr = strtab + sharedHandle.ucb->MemoryRead32((u32)(u64)&symtab[i].n_un.n_strx);
//u32 host_addr = host_strtab +
if(!get_memory(addr)) continue;
u64 symbolAddr = sharedHandle.ucb->MemoryRead32((u32)(u64)&symtab[i].n_value) + slide;
u64 hostSymbolAddr = host_symtab[i].n_value + slide;
DynarmicHostString host_sym(addr);
if(*host_sym.hostPtr) {
iterator(symbolAddr, (const char *)host_sym.hostPtr);
} else {
iterator(symbolAddr, (const char *)symbolAddr);
}
}
#else
struct nlist *symtab = (struct nlist *)((uintptr_t)header + symtab_cmd->symoff);
char *strtab = (char *)((uintptr_t)header + symtab_cmd->stroff);
for(int i=0; i < symtab_cmd->nsyms; i++) {
iterator(symtab[i].n_value + slide, (const char *)(strtab + symtab[i].n_un.n_strx));
}
#endif
}
void symbolicate_call_stack(symbolicated_call *callStack, int callStackLen) {
for (int n = 0; n < guestMappingLen; n++) {
load_symbols_for_image(&guestMappings[n], ^(u32 address, const char *name){
//printf("[0x%08x-0x%08x] %s`%s\n", address, guestMappings[n].name, name);
for (int i = 0; i < callStackLen; i++) {
if (callStack[i].address >= address && (!callStack[i].symbolName || callStack[i].address - address < callStack[i].symbolOffset)) {
//printf("0x%08x [0x%08x-0x%08x]\n", callStack[i].address, start, end);
callStack[i].imageName = guestMappings[n].name;
callStack[i].symbolName = name;
callStack[i].symbolOffset = callStack[i].address - address;
}
}
});
}
}
char *get_memory_page(u64 vaddr) {
size_t num_page_table_entries = sharedHandle.num_page_table_entries;
void **page_table = sharedHandle.page_table;
khash_t(memory) *memory = sharedHandle.memory;
u64 idx = vaddr >> DYN_PAGE_BITS;
if(page_table && idx < num_page_table_entries) {
return (char *)page_table[idx];
}
u64 base = vaddr & ~DYN_PAGE_MASK;
khiter_t k = kh_get(memory, memory, base);
if(k == kh_end(memory)) {
return NULL;
}
t_memory_page page = kh_value(memory, k);
return (char *)page->addr;
}
inline void *get_memory(u64 vaddr) {
char *page = get_memory_page(vaddr);
return page ? &page[vaddr & DYN_PAGE_MASK] : NULL;
}
class DynarmicCallbacks32 final : public Dynarmic::A32::UserCallbacks {
private:
~DynarmicCallbacks32() = default;
public:
void destroy() {
this->cp15 = nullptr;
delete this;
}
DynarmicCallbacks32(khash_t(memory) *memory)
: memory{memory}, cp15(std::make_shared<DynarmicCP15>()) {}
bool IsReadOnlyMemory(u32 vaddr) override {
// u32 idx;
// return mem_map && (idx = vaddr >> DYN_PAGE_BITS) < num_page_table_entries && mem_map[idx] & PAGE_EXISTS_BIT && (mem_map[idx] & UC_PROT_WRITE) == 0;
return false;
}
std::optional<uint32_t> MemoryReadCode(u32 vaddr) override {
#if TRACE_BRANCH
static u32 lastRead;
if (vaddr - lastRead != 4 && vaddr == cpu->Regs()[15]) {
lastRead = vaddr;
DumpBacktrace(false);
}
#endif
return MemoryRead32(vaddr, false);
}
u16 MemoryReadThumbCode(u32 vaddr) {
u16 code = MemoryRead16(vaddr, false);
// printf("MemoryReadThumbCode[%s->%s:%d]: vaddr=0x%x, code=0x%04x\n", __FILE__, __func__, __LINE__, vaddr, code);
return code;
}