authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-19 00:30:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-19 00:30:32-07:00
log8c4a2dc1dfe05a67eb9811b8c8291c5541816a99
tree9437f16152597b75b6a8b7ec77abbba1252c937d
parente8c4e79499fbb2a83a0f4fe2cac0d80e5d12a07e

move non-libc stuff out of std.c


3 files changed, 743 insertions(+), 713 deletions(-)

lib/std/c.zig+35
......@@ -9541,10 +9541,24 @@ pub const CPUFAMILY = darwin.CPUFAMILY;
95419541pub const DB_RECORDTYPE = darwin.DB_RECORDTYPE;
95429542pub const EXC = darwin.EXC;
95439543pub const EXCEPTION = darwin.EXCEPTION;
9544pub const MACH_MSG_TYPE = darwin.MACH_MSG_TYPE;
9545pub const MACH_PORT_RIGHT = darwin.MACH_PORT_RIGHT;
9546pub const MACH_TASK_BASIC_INFO = darwin.MACH_TASK_BASIC_INFO;
9547pub const MACH_TASK_BASIC_INFO_COUNT = darwin.MACH_TASK_BASIC_INFO_COUNT;
9548pub const MATTR = darwin.MATTR;
95449549pub const NSVersionOfRunTimeLibrary = darwin.NSVersionOfRunTimeLibrary;
95459550pub const OPEN_MAX = darwin.OPEN_MAX;
9551pub const POSIX_SPAWN = darwin.POSIX_SPAWN;
9552pub const TASK_NULL = darwin.TASK_NULL;
9553pub const TASK_VM_INFO = darwin.TASK_VM_INFO;
9554pub const TASK_VM_INFO_COUNT = darwin.TASK_VM_INFO_COUNT;
9555pub const THREAD_BASIC_INFO = darwin.THREAD_BASIC_INFO;
9556pub const THREAD_BASIC_INFO_COUNT = darwin.THREAD_BASIC_INFO_COUNT;
9557pub const THREAD_IDENTIFIER_INFO_COUNT = darwin.THREAD_IDENTIFIER_INFO_COUNT;
9558pub const THREAD_NULL = darwin.THREAD_NULL;
95469559pub const THREAD_STATE_NONE = darwin.THREAD_STATE_NONE;
95479560pub const UL = darwin.UL;
9561pub const VM = darwin.VM;
95489562pub const _NSGetExecutablePath = darwin._NSGetExecutablePath;
95499563pub const __getdirentries64 = darwin.__getdirentries64;
95509564pub const __ulock_wait = darwin.__ulock_wait;
......@@ -9562,16 +9576,21 @@ pub const dispatch_semaphore_signal = darwin.dispatch_semaphore_signal;
95629576pub const dispatch_semaphore_wait = darwin.dispatch_semaphore_wait;
95639577pub const dispatch_time = darwin.dispatch_time;
95649578pub const fcopyfile = darwin.fcopyfile;
9579pub const kern_return_t = darwin.kern_return_t;
95659580pub const kevent64 = darwin.kevent64;
95669581pub const mach_absolute_time = darwin.mach_absolute_time;
95679582pub const mach_continuous_time = darwin.mach_continuous_time;
95689583pub const mach_hdr = darwin.mach_hdr;
95699584pub const mach_host_self = darwin.mach_host_self;
95709585pub const mach_msg = darwin.mach_msg;
9586pub const mach_msg_type_number_t = darwin.mach_msg_type_number_t;
95719587pub const mach_port_allocate = darwin.mach_port_allocate;
9588pub const mach_port_array_t = darwin.mach_port_array_t;
95729589pub const mach_port_deallocate = darwin.mach_port_deallocate;
95739590pub const mach_port_insert_right = darwin.mach_port_insert_right;
9591pub const mach_port_name_t = darwin.mach_port_name_t;
95749592pub const mach_port_t = darwin.mach_port_t;
9593pub const mach_task_basic_info = darwin.mach_task_basic_info;
95759594pub const mach_task_self = darwin.mach_task_self;
95769595pub const mach_timebase_info = darwin.mach_timebase_info;
95779596pub const mach_vm_protect = darwin.mach_vm_protect;
......@@ -9602,10 +9621,12 @@ pub const posix_spawn_file_actions_addinherit_np = darwin.posix_spawn_file_actio
96029621pub const posix_spawn_file_actions_addopen = darwin.posix_spawn_file_actions_addopen;
96039622pub const posix_spawn_file_actions_destroy = darwin.posix_spawn_file_actions_destroy;
96049623pub const posix_spawn_file_actions_init = darwin.posix_spawn_file_actions_init;
9624pub const posix_spawn_file_actions_t = darwin.posix_spawn_file_actions_t;
96059625pub const posix_spawnattr_destroy = darwin.posix_spawnattr_destroy;
96069626pub const posix_spawnattr_getflags = darwin.posix_spawnattr_getflags;
96079627pub const posix_spawnattr_init = darwin.posix_spawnattr_init;
96089628pub const posix_spawnattr_setflags = darwin.posix_spawnattr_setflags;
9629pub const posix_spawnattr_t = darwin.posix_spawnattr_t;
96099630pub const posix_spawnp = darwin.posix_spawnp;
96109631pub const pthread_attr_get_qos_class_np = darwin.pthread_attr_get_qos_class_np;
96119632pub const pthread_attr_set_qos_class_np = darwin.pthread_attr_set_qos_class_np;
......@@ -9616,17 +9637,31 @@ pub const sigaddset = darwin.sigaddset;
96169637pub const task_for_pid = darwin.task_for_pid;
96179638pub const task_get_exception_ports = darwin.task_get_exception_ports;
96189639pub const task_info = darwin.task_info;
9640pub const task_info_t = darwin.task_info_t;
96199641pub const task_resume = darwin.task_resume;
96209642pub const task_set_exception_ports = darwin.task_set_exception_ports;
96219643pub const task_suspend = darwin.task_suspend;
96229644pub const task_threads = darwin.task_threads;
9645pub const task_vm_info_data_t = darwin.task_vm_info_data_t;
9646pub const thread_basic_info = darwin.thread_basic_info;
96239647pub const thread_get_state = darwin.thread_get_state;
9648pub const thread_identifier_info = darwin.thread_identifier_info;
96249649pub const thread_info = darwin.thread_info;
9650pub const thread_info_t = darwin.thread_info_t;
96259651pub const thread_resume = darwin.thread_resume;
96269652pub const thread_set_state = darwin.thread_set_state;
96279653pub const vm_deallocate = darwin.vm_deallocate;
96289654pub const vm_machine_attribute = darwin.vm_machine_attribute;
9655pub const vm_machine_attribute_val_t = darwin.vm_machine_attribute_val_t;
9656pub const vm_offset_t = darwin.vm_offset_t;
96299657pub const vm_prot_t = darwin.vm_prot_t;
9658pub const vm_region_basic_info_64 = darwin.vm_region_basic_info_64;
9659pub const vm_region_extended_info = darwin.vm_region_extended_info;
9660pub const vm_region_info_t = darwin.vm_region_info_t;
9661pub const vm_region_recurse_info_t = darwin.vm_region_recurse_info_t;
9662pub const vm_region_submap_info_64 = darwin.vm_region_submap_info_64;
9663pub const vm_region_submap_short_info_64 = darwin.vm_region_submap_short_info_64;
9664pub const vm_region_top_info = darwin.vm_region_top_info;
96309665
96319666pub const _ksiginfo = netbsd._ksiginfo;
96329667pub const _lwp_self = netbsd._lwp_self;
lib/std/c/darwin.zig+63-711
......@@ -355,31 +355,22 @@ pub const MACH_PORT_RIGHT = enum(mach_port_right_t) {
355355pub const MACH_MSG_TYPE = enum(mach_msg_type_name_t) {
356356 /// Must hold receive right
357357 MOVE_RECEIVE = 16,
358
359358 /// Must hold send right(s)
360359 MOVE_SEND = 17,
361
362360 /// Must hold sendonce right
363361 MOVE_SEND_ONCE = 18,
364
365362 /// Must hold send right(s)
366363 COPY_SEND = 19,
367
368364 /// Must hold receive right
369365 MAKE_SEND = 20,
370
371366 /// Must hold receive right
372367 MAKE_SEND_ONCE = 21,
373
374368 /// NOT VALID
375369 COPY_RECEIVE = 22,
376
377370 /// Must hold receive right
378371 DISPOSE_RECEIVE = 24,
379
380372 /// Must hold send right(s)
381373 DISPOSE_SEND = 25,
382
383374 /// Must hold sendonce right
384375 DISPOSE_SEND_ONCE = 26,
385376};
......@@ -469,38 +460,45 @@ pub const vm_behavior_t = i32;
469460pub const vm32_object_id_t = u32;
470461pub const vm_object_id_t = u64;
471462
472pub const VM_INHERIT_SHARE: vm_inherit_t = 0;
473pub const VM_INHERIT_COPY: vm_inherit_t = 1;
474pub const VM_INHERIT_NONE: vm_inherit_t = 2;
475pub const VM_INHERIT_DONATE_COPY: vm_inherit_t = 3;
476pub const VM_INHERIT_DEFAULT = VM_INHERIT_COPY;
477
478pub const VM_BEHAVIOR_DEFAULT: vm_behavior_t = 0;
479pub const VM_BEHAVIOR_RANDOM: vm_behavior_t = 1;
480pub const VM_BEHAVIOR_SEQUENTIAL: vm_behavior_t = 2;
481pub const VM_BEHAVIOR_RSEQNTL: vm_behavior_t = 3;
482
483pub const VM_BEHAVIOR_WILLNEED: vm_behavior_t = 4;
484pub const VM_BEHAVIOR_DONTNEED: vm_behavior_t = 5;
485pub const VM_BEHAVIOR_FREE: vm_behavior_t = 6;
486pub const VM_BEHAVIOR_ZERO_WIRED_PAGES: vm_behavior_t = 7;
487pub const VM_BEHAVIOR_REUSABLE: vm_behavior_t = 8;
488pub const VM_BEHAVIOR_REUSE: vm_behavior_t = 9;
489pub const VM_BEHAVIOR_CAN_REUSE: vm_behavior_t = 10;
490pub const VM_BEHAVIOR_PAGEOUT: vm_behavior_t = 11;
491
492pub const VM_REGION_BASIC_INFO_64 = 9;
493pub const VM_REGION_EXTENDED_INFO = 13;
494pub const VM_REGION_TOP_INFO = 12;
495pub const VM_REGION_SUBMAP_INFO_COUNT_64: mach_msg_type_number_t = @sizeOf(vm_region_submap_info_64) / @sizeOf(natural_t);
496pub const VM_REGION_SUBMAP_SHORT_INFO_COUNT_64: mach_msg_type_number_t = @sizeOf(vm_region_submap_short_info_64) / @sizeOf(natural_t);
497pub const VM_REGION_BASIC_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_basic_info_64) / @sizeOf(c_int);
498pub const VM_REGION_EXTENDED_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_extended_info) / @sizeOf(natural_t);
499pub const VM_REGION_TOP_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_top_info) / @sizeOf(natural_t);
500
501pub fn VM_MAKE_TAG(tag: u8) u32 {
502 return @as(u32, tag) << 24;
503}
463pub const VM = struct {
464 pub const INHERIT = struct {
465 pub const SHARE: vm_inherit_t = 0;
466 pub const COPY: vm_inherit_t = 1;
467 pub const NONE: vm_inherit_t = 2;
468 pub const DONATE_COPY: vm_inherit_t = 3;
469 pub const DEFAULT = COPY;
470 };
471
472 pub const BEHAVIOR = struct {
473 pub const DEFAULT: vm_behavior_t = 0;
474 pub const RANDOM: vm_behavior_t = 1;
475 pub const SEQUENTIAL: vm_behavior_t = 2;
476 pub const RSEQNTL: vm_behavior_t = 3;
477 pub const WILLNEED: vm_behavior_t = 4;
478 pub const DONTNEED: vm_behavior_t = 5;
479 pub const FREE: vm_behavior_t = 6;
480 pub const ZERO_WIRED_PAGES: vm_behavior_t = 7;
481 pub const REUSABLE: vm_behavior_t = 8;
482 pub const REUSE: vm_behavior_t = 9;
483 pub const CAN_REUSE: vm_behavior_t = 10;
484 pub const PAGEOUT: vm_behavior_t = 11;
485 };
486
487 pub const REGION = struct {
488 pub const BASIC_INFO_64 = 9;
489 pub const EXTENDED_INFO = 13;
490 pub const TOP_INFO = 12;
491 pub const SUBMAP_INFO_COUNT_64: mach_msg_type_number_t = @sizeOf(vm_region_submap_info_64) / @sizeOf(natural_t);
492 pub const SUBMAP_SHORT_INFO_COUNT_64: mach_msg_type_number_t = @sizeOf(vm_region_submap_short_info_64) / @sizeOf(natural_t);
493 pub const BASIC_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_basic_info_64) / @sizeOf(c_int);
494 pub const EXTENDED_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_extended_info) / @sizeOf(natural_t);
495 pub const TOP_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_top_info) / @sizeOf(natural_t);
496 };
497
498 pub fn MAKE_TAG(tag: u8) u32 {
499 return @as(u32, tag) << 24;
500 }
501};
504502
505503pub const vm_region_basic_info_64 = extern struct {
506504 protection: vm_prot_t,
......@@ -678,29 +676,30 @@ pub const thread_identifier_info = extern struct {
678676 dispatch_qaddr: u64,
679677};
680678
681/// Cachability
682pub const MATTR_CACHE = 1;
683/// Migrability
684pub const MATTR_MIGRATE = 2;
685/// Replicability
686pub const MATTR_REPLICATE = 4;
687
688/// (Generic) turn attribute off
689pub const MATTR_VAL_OFF = 0;
690/// (Generic) turn attribute on
691pub const MATTR_VAL_ON = 1;
692/// (Generic) return current value
693pub const MATTR_VAL_GET = 2;
694/// Flush from all caches
695pub const MATTR_VAL_CACHE_FLUSH = 6;
696/// Flush from data caches
697pub const MATTR_VAL_DCACHE_FLUSH = 7;
698/// Flush from instruction caches
699pub const MATTR_VAL_ICACHE_FLUSH = 8;
700/// Sync I+D caches
701pub const MATTR_VAL_CACHE_SYNC = 9;
702/// Get page info (stats)
703pub const MATTR_VAL_GET_INFO = 10;
679pub const MATTR = struct {
680 /// Cachability
681 pub const CACHE = 1;
682 /// Migrability
683 pub const MIGRATE = 2;
684 /// Replicability
685 pub const REPLICATE = 4;
686 /// (Generic) turn attribute off
687 pub const VAL_OFF = 0;
688 /// (Generic) turn attribute on
689 pub const VAL_ON = 1;
690 /// (Generic) return current value
691 pub const VAL_GET = 2;
692 /// Flush from all caches
693 pub const VAL_CACHE_FLUSH = 6;
694 /// Flush from data caches
695 pub const VAL_DCACHE_FLUSH = 7;
696 /// Flush from instruction caches
697 pub const VAL_ICACHE_FLUSH = 8;
698 /// Sync I+D caches
699 pub const VAL_CACHE_SYNC = 9;
700 /// Get page info (stats)
701 pub const VAL_GET_INFO = 10;
702};
704703
705704pub const TASK_VM_INFO = 22;
706705pub const TASK_VM_INFO_COUNT: mach_msg_type_number_t = @sizeOf(task_vm_info_data_t) / @sizeOf(natural_t);
......@@ -804,19 +803,14 @@ pub extern "c" fn task_info(
804803pub const mach_task_basic_info = extern struct {
805804 /// Virtual memory size (bytes)
806805 virtual_size: mach_vm_size_t,
807
808806 /// Resident memory size (bytes)
809807 resident_size: mach_vm_size_t,
810
811808 /// Total user run time for terminated threads
812809 user_time: time_value_t,
813
814810 /// Total system run time for terminated threads
815811 system_time: time_value_t,
816
817812 /// Default policy for new threads
818813 policy: policy_t,
819
820814 /// Suspend count for task
821815 suspend_count: mach_vm_size_t,
822816};
......@@ -925,486 +919,6 @@ pub extern "c" fn os_unfair_lock_trylock(o: os_unfair_lock_t) bool;
925919pub extern "c" fn os_unfair_lock_assert_owner(o: os_unfair_lock_t) void;
926920pub extern "c" fn os_unfair_lock_assert_not_owner(o: os_unfair_lock_t) void;
927921
928pub fn getKernError(err: kern_return_t) KernE {
929 return @as(KernE, @enumFromInt(@as(u32, @truncate(@as(usize, @intCast(err))))));
930}
931
932pub fn unexpectedKernError(err: KernE) std.posix.UnexpectedError {
933 if (std.posix.unexpected_error_tracing) {
934 std.debug.print("unexpected error: {d}\n", .{@intFromEnum(err)});
935 std.debug.dumpCurrentStackTrace(null);
936 }
937 return error.Unexpected;
938}
939
940pub const MachError = error{
941 /// Not enough permissions held to perform the requested kernel
942 /// call.
943 PermissionDenied,
944} || std.posix.UnexpectedError;
945
946pub const MachTask = extern struct {
947 port: mach_port_name_t,
948
949 pub fn isValid(self: MachTask) bool {
950 return self.port != TASK_NULL;
951 }
952
953 pub fn pidForTask(self: MachTask) MachError!std.c.pid_t {
954 var pid: std.c.pid_t = undefined;
955 switch (getKernError(pid_for_task(self.port, &pid))) {
956 .SUCCESS => return pid,
957 .FAILURE => return error.PermissionDenied,
958 else => |err| return unexpectedKernError(err),
959 }
960 }
961
962 pub fn allocatePort(self: MachTask, right: MACH_PORT_RIGHT) MachError!MachTask {
963 var out_port: mach_port_name_t = undefined;
964 switch (getKernError(mach_port_allocate(
965 self.port,
966 @intFromEnum(right),
967 &out_port,
968 ))) {
969 .SUCCESS => return .{ .port = out_port },
970 .FAILURE => return error.PermissionDenied,
971 else => |err| return unexpectedKernError(err),
972 }
973 }
974
975 pub fn deallocatePort(self: MachTask, port: MachTask) void {
976 _ = getKernError(mach_port_deallocate(self.port, port.port));
977 }
978
979 pub fn insertRight(self: MachTask, port: MachTask, msg: MACH_MSG_TYPE) !void {
980 switch (getKernError(mach_port_insert_right(
981 self.port,
982 port.port,
983 port.port,
984 @intFromEnum(msg),
985 ))) {
986 .SUCCESS => return,
987 .FAILURE => return error.PermissionDenied,
988 else => |err| return unexpectedKernError(err),
989 }
990 }
991
992 pub const PortInfo = struct {
993 mask: exception_mask_t,
994 masks: [EXC.TYPES_COUNT]exception_mask_t,
995 ports: [EXC.TYPES_COUNT]mach_port_t,
996 behaviors: [EXC.TYPES_COUNT]exception_behavior_t,
997 flavors: [EXC.TYPES_COUNT]thread_state_flavor_t,
998 count: mach_msg_type_number_t,
999 };
1000
1001 pub fn getExceptionPorts(self: MachTask, mask: exception_mask_t) !PortInfo {
1002 var info = PortInfo{
1003 .mask = mask,
1004 .masks = undefined,
1005 .ports = undefined,
1006 .behaviors = undefined,
1007 .flavors = undefined,
1008 .count = 0,
1009 };
1010 info.count = info.ports.len / @sizeOf(mach_port_t);
1011
1012 switch (getKernError(task_get_exception_ports(
1013 self.port,
1014 info.mask,
1015 &info.masks,
1016 &info.count,
1017 &info.ports,
1018 &info.behaviors,
1019 &info.flavors,
1020 ))) {
1021 .SUCCESS => return info,
1022 .FAILURE => return error.PermissionDenied,
1023 else => |err| return unexpectedKernError(err),
1024 }
1025 }
1026
1027 pub fn setExceptionPorts(
1028 self: MachTask,
1029 mask: exception_mask_t,
1030 new_port: MachTask,
1031 behavior: exception_behavior_t,
1032 new_flavor: thread_state_flavor_t,
1033 ) !void {
1034 switch (getKernError(task_set_exception_ports(
1035 self.port,
1036 mask,
1037 new_port.port,
1038 behavior,
1039 new_flavor,
1040 ))) {
1041 .SUCCESS => return,
1042 .FAILURE => return error.PermissionDenied,
1043 else => |err| return unexpectedKernError(err),
1044 }
1045 }
1046
1047 pub const RegionInfo = struct {
1048 pub const Tag = enum {
1049 basic,
1050 extended,
1051 top,
1052 };
1053
1054 base_addr: u64,
1055 tag: Tag,
1056 info: union {
1057 basic: vm_region_basic_info_64,
1058 extended: vm_region_extended_info,
1059 top: vm_region_top_info,
1060 },
1061 };
1062
1063 pub fn getRegionInfo(
1064 task: MachTask,
1065 address: u64,
1066 len: usize,
1067 tag: RegionInfo.Tag,
1068 ) MachError!RegionInfo {
1069 var info: RegionInfo = .{
1070 .base_addr = address,
1071 .tag = tag,
1072 .info = undefined,
1073 };
1074 switch (tag) {
1075 .basic => info.info = .{ .basic = undefined },
1076 .extended => info.info = .{ .extended = undefined },
1077 .top => info.info = .{ .top = undefined },
1078 }
1079 var base_len: mach_vm_size_t = if (len == 1) 2 else len;
1080 var objname: mach_port_t = undefined;
1081 var count: mach_msg_type_number_t = switch (tag) {
1082 .basic => VM_REGION_BASIC_INFO_COUNT,
1083 .extended => VM_REGION_EXTENDED_INFO_COUNT,
1084 .top => VM_REGION_TOP_INFO_COUNT,
1085 };
1086 switch (getKernError(mach_vm_region(
1087 task.port,
1088 &info.base_addr,
1089 &base_len,
1090 switch (tag) {
1091 .basic => VM_REGION_BASIC_INFO_64,
1092 .extended => VM_REGION_EXTENDED_INFO,
1093 .top => VM_REGION_TOP_INFO,
1094 },
1095 switch (tag) {
1096 .basic => @as(vm_region_info_t, @ptrCast(&info.info.basic)),
1097 .extended => @as(vm_region_info_t, @ptrCast(&info.info.extended)),
1098 .top => @as(vm_region_info_t, @ptrCast(&info.info.top)),
1099 },
1100 &count,
1101 &objname,
1102 ))) {
1103 .SUCCESS => return info,
1104 .FAILURE => return error.PermissionDenied,
1105 else => |err| return unexpectedKernError(err),
1106 }
1107 }
1108
1109 pub const RegionSubmapInfo = struct {
1110 pub const Tag = enum {
1111 short,
1112 full,
1113 };
1114
1115 tag: Tag,
1116 base_addr: u64,
1117 info: union {
1118 short: vm_region_submap_short_info_64,
1119 full: vm_region_submap_info_64,
1120 },
1121 };
1122
1123 pub fn getRegionSubmapInfo(
1124 task: MachTask,
1125 address: u64,
1126 len: usize,
1127 nesting_depth: u32,
1128 tag: RegionSubmapInfo.Tag,
1129 ) MachError!RegionSubmapInfo {
1130 var info: RegionSubmapInfo = .{
1131 .base_addr = address,
1132 .tag = tag,
1133 .info = undefined,
1134 };
1135 switch (tag) {
1136 .short => info.info = .{ .short = undefined },
1137 .full => info.info = .{ .full = undefined },
1138 }
1139 var nesting = nesting_depth;
1140 var base_len: mach_vm_size_t = if (len == 1) 2 else len;
1141 var count: mach_msg_type_number_t = switch (tag) {
1142 .short => VM_REGION_SUBMAP_SHORT_INFO_COUNT_64,
1143 .full => VM_REGION_SUBMAP_INFO_COUNT_64,
1144 };
1145 switch (getKernError(mach_vm_region_recurse(
1146 task.port,
1147 &info.base_addr,
1148 &base_len,
1149 &nesting,
1150 switch (tag) {
1151 .short => @as(vm_region_recurse_info_t, @ptrCast(&info.info.short)),
1152 .full => @as(vm_region_recurse_info_t, @ptrCast(&info.info.full)),
1153 },
1154 &count,
1155 ))) {
1156 .SUCCESS => return info,
1157 .FAILURE => return error.PermissionDenied,
1158 else => |err| return unexpectedKernError(err),
1159 }
1160 }
1161
1162 pub fn getCurrProtection(task: MachTask, address: u64, len: usize) MachError!vm_prot_t {
1163 const info = try task.getRegionSubmapInfo(address, len, 0, .short);
1164 return info.info.short.protection;
1165 }
1166
1167 pub fn setMaxProtection(task: MachTask, address: u64, len: usize, prot: vm_prot_t) MachError!void {
1168 return task.setProtectionImpl(address, len, true, prot);
1169 }
1170
1171 pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: vm_prot_t) MachError!void {
1172 return task.setProtectionImpl(address, len, false, prot);
1173 }
1174
1175 fn setProtectionImpl(task: MachTask, address: u64, len: usize, set_max: bool, prot: vm_prot_t) MachError!void {
1176 switch (getKernError(mach_vm_protect(task.port, address, len, @intFromBool(set_max), prot))) {
1177 .SUCCESS => return,
1178 .FAILURE => return error.PermissionDenied,
1179 else => |err| return unexpectedKernError(err),
1180 }
1181 }
1182
1183 /// Will write to VM even if current protection attributes specifically prohibit
1184 /// us from doing so, by temporarily setting protection level to a level with VM_PROT_COPY
1185 /// variant, and resetting after a successful or unsuccessful write.
1186 pub fn writeMemProtected(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
1187 const curr_prot = try task.getCurrProtection(address, buf.len);
1188 try task.setCurrProtection(
1189 address,
1190 buf.len,
1191 PROT.READ | PROT.WRITE | PROT.COPY,
1192 );
1193 defer {
1194 task.setCurrProtection(address, buf.len, curr_prot) catch {};
1195 }
1196 return task.writeMem(address, buf, arch);
1197 }
1198
1199 pub fn writeMem(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
1200 const count = buf.len;
1201 var total_written: usize = 0;
1202 var curr_addr = address;
1203 const page_size = try getPageSize(task); // TODO we probably can assume value here
1204 var out_buf = buf[0..];
1205
1206 while (total_written < count) {
1207 const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_written);
1208 switch (getKernError(mach_vm_write(
1209 task.port,
1210 curr_addr,
1211 @intFromPtr(out_buf.ptr),
1212 @as(mach_msg_type_number_t, @intCast(curr_size)),
1213 ))) {
1214 .SUCCESS => {},
1215 .FAILURE => return error.PermissionDenied,
1216 else => |err| return unexpectedKernError(err),
1217 }
1218
1219 switch (arch) {
1220 .aarch64 => {
1221 var mattr_value: vm_machine_attribute_val_t = MATTR_VAL_CACHE_FLUSH;
1222 switch (getKernError(vm_machine_attribute(
1223 task.port,
1224 curr_addr,
1225 curr_size,
1226 MATTR_CACHE,
1227 &mattr_value,
1228 ))) {
1229 .SUCCESS => {},
1230 .FAILURE => return error.PermissionDenied,
1231 else => |err| return unexpectedKernError(err),
1232 }
1233 },
1234 .x86_64 => {},
1235 else => unreachable,
1236 }
1237
1238 out_buf = out_buf[curr_size..];
1239 total_written += curr_size;
1240 curr_addr += curr_size;
1241 }
1242
1243 return total_written;
1244 }
1245
1246 pub fn readMem(task: MachTask, address: u64, buf: []u8) MachError!usize {
1247 const count = buf.len;
1248 var total_read: usize = 0;
1249 var curr_addr = address;
1250 const page_size = try getPageSize(task); // TODO we probably can assume value here
1251 var out_buf = buf[0..];
1252
1253 while (total_read < count) {
1254 const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_read);
1255 var curr_bytes_read: mach_msg_type_number_t = 0;
1256 var vm_memory: vm_offset_t = undefined;
1257 switch (getKernError(mach_vm_read(task.port, curr_addr, curr_size, &vm_memory, &curr_bytes_read))) {
1258 .SUCCESS => {},
1259 .FAILURE => return error.PermissionDenied,
1260 else => |err| return unexpectedKernError(err),
1261 }
1262
1263 @memcpy(out_buf[0..curr_bytes_read], @as([*]const u8, @ptrFromInt(vm_memory)));
1264 _ = vm_deallocate(mach_task_self(), vm_memory, curr_bytes_read);
1265
1266 out_buf = out_buf[curr_bytes_read..];
1267 curr_addr += curr_bytes_read;
1268 total_read += curr_bytes_read;
1269 }
1270
1271 return total_read;
1272 }
1273
1274 fn maxBytesLeftInPage(page_size: usize, address: u64, count: usize) usize {
1275 var left = count;
1276 if (page_size > 0) {
1277 const page_offset = address % page_size;
1278 const bytes_left_in_page = page_size - page_offset;
1279 if (count > bytes_left_in_page) {
1280 left = bytes_left_in_page;
1281 }
1282 }
1283 return left;
1284 }
1285
1286 fn getPageSize(task: MachTask) MachError!usize {
1287 if (task.isValid()) {
1288 var info_count = TASK_VM_INFO_COUNT;
1289 var vm_info: task_vm_info_data_t = undefined;
1290 switch (getKernError(task_info(
1291 task.port,
1292 TASK_VM_INFO,
1293 @as(task_info_t, @ptrCast(&vm_info)),
1294 &info_count,
1295 ))) {
1296 .SUCCESS => return @as(usize, @intCast(vm_info.page_size)),
1297 else => {},
1298 }
1299 }
1300 var page_size: vm_size_t = undefined;
1301 switch (getKernError(_host_page_size(mach_host_self(), &page_size))) {
1302 .SUCCESS => return page_size,
1303 else => |err| return unexpectedKernError(err),
1304 }
1305 }
1306
1307 pub fn basicTaskInfo(task: MachTask) MachError!mach_task_basic_info {
1308 var info: mach_task_basic_info = undefined;
1309 var count = MACH_TASK_BASIC_INFO_COUNT;
1310 switch (getKernError(task_info(
1311 task.port,
1312 MACH_TASK_BASIC_INFO,
1313 @as(task_info_t, @ptrCast(&info)),
1314 &count,
1315 ))) {
1316 .SUCCESS => return info,
1317 else => |err| return unexpectedKernError(err),
1318 }
1319 }
1320
1321 pub fn @"resume"(task: MachTask) MachError!void {
1322 switch (getKernError(task_resume(task.port))) {
1323 .SUCCESS => {},
1324 else => |err| return unexpectedKernError(err),
1325 }
1326 }
1327
1328 pub fn @"suspend"(task: MachTask) MachError!void {
1329 switch (getKernError(task_suspend(task.port))) {
1330 .SUCCESS => {},
1331 else => |err| return unexpectedKernError(err),
1332 }
1333 }
1334
1335 const ThreadList = struct {
1336 buf: []MachThread,
1337
1338 pub fn deinit(list: ThreadList) void {
1339 const self_task = machTaskForSelf();
1340 _ = vm_deallocate(
1341 self_task.port,
1342 @intFromPtr(list.buf.ptr),
1343 @as(vm_size_t, @intCast(list.buf.len * @sizeOf(mach_port_t))),
1344 );
1345 }
1346 };
1347
1348 pub fn getThreads(task: MachTask) MachError!ThreadList {
1349 var thread_list: mach_port_array_t = undefined;
1350 var thread_count: mach_msg_type_number_t = undefined;
1351 switch (getKernError(task_threads(task.port, &thread_list, &thread_count))) {
1352 .SUCCESS => return ThreadList{ .buf = @as([*]MachThread, @ptrCast(thread_list))[0..thread_count] },
1353 else => |err| return unexpectedKernError(err),
1354 }
1355 }
1356};
1357
1358pub const MachThread = extern struct {
1359 port: mach_port_t,
1360
1361 pub fn isValid(thread: MachThread) bool {
1362 return thread.port != THREAD_NULL;
1363 }
1364
1365 pub fn getBasicInfo(thread: MachThread) MachError!thread_basic_info {
1366 var info: thread_basic_info = undefined;
1367 var count = THREAD_BASIC_INFO_COUNT;
1368 switch (getKernError(thread_info(
1369 thread.port,
1370 THREAD_BASIC_INFO,
1371 @as(thread_info_t, @ptrCast(&info)),
1372 &count,
1373 ))) {
1374 .SUCCESS => return info,
1375 else => |err| return unexpectedKernError(err),
1376 }
1377 }
1378
1379 pub fn getIdentifierInfo(thread: MachThread) MachError!thread_identifier_info {
1380 var info: thread_identifier_info = undefined;
1381 var count = THREAD_IDENTIFIER_INFO_COUNT;
1382 switch (getKernError(thread_info(
1383 thread.port,
1384 THREAD_IDENTIFIER_INFO,
1385 @as(thread_info_t, @ptrCast(&info)),
1386 &count,
1387 ))) {
1388 .SUCCESS => return info,
1389 else => |err| return unexpectedKernError(err),
1390 }
1391 }
1392};
1393
1394pub fn machTaskForPid(pid: std.c.pid_t) MachError!MachTask {
1395 var port: mach_port_name_t = undefined;
1396 switch (getKernError(task_for_pid(mach_task_self(), pid, &port))) {
1397 .SUCCESS => {},
1398 .FAILURE => return error.PermissionDenied,
1399 else => |err| return unexpectedKernError(err),
1400 }
1401 return MachTask{ .port = port };
1402}
1403
1404pub fn machTaskForSelf() MachTask {
1405 return .{ .port = mach_task_self() };
1406}
1407
1408922pub const os_signpost_id_t = u64;
1409923
1410924pub const OS_SIGNPOST_ID_NULL: os_signpost_id_t = 0;
......@@ -1503,168 +1017,6 @@ pub const KEVENT_FLAG_ERROR_EVENTS = 0x002;
15031017
15041018pub const SYSPROTO_EVENT = 1;
15051019pub const SYSPROTO_CONTROL = 2;
1506/// Kernel return values
1507pub const KernE = enum(u32) {
1508 SUCCESS = 0,
1509 /// Specified address is not currently valid
1510 INVALID_ADDRESS = 1,
1511 /// Specified memory is valid, but does not permit the
1512 /// required forms of access.
1513 PROTECTION_FAILURE = 2,
1514 /// The address range specified is already in use, or
1515 /// no address range of the size specified could be
1516 /// found.
1517 NO_SPACE = 3,
1518 /// The function requested was not applicable to this
1519 /// type of argument, or an argument is invalid
1520 INVALID_ARGUMENT = 4,
1521 /// The function could not be performed. A catch-all.
1522 FAILURE = 5,
1523 /// A system resource could not be allocated to fulfill
1524 /// this request. This failure may not be permanent.
1525 RESOURCE_SHORTAGE = 6,
1526 /// The task in question does not hold receive rights
1527 /// for the port argument.
1528 NOT_RECEIVER = 7,
1529 /// Bogus access restriction.
1530 NO_ACCESS = 8,
1531 /// During a page fault, the target address refers to a
1532 /// memory object that has been destroyed. This
1533 /// failure is permanent.
1534 MEMORY_FAILURE = 9,
1535 /// During a page fault, the memory object indicated
1536 /// that the data could not be returned. This failure
1537 /// may be temporary; future attempts to access this
1538 /// same data may succeed, as defined by the memory
1539 /// object.
1540 MEMORY_ERROR = 10,
1541 /// The receive right is already a member of the portset.
1542 ALREADY_IN_SET = 11,
1543 /// The receive right is not a member of a port set.
1544 NOT_IN_SET = 12,
1545 /// The name already denotes a right in the task.
1546 NAME_EXISTS = 13,
1547 /// The operation was aborted. Ipc code will
1548 /// catch this and reflect it as a message error.
1549 ABORTED = 14,
1550 /// The name doesn't denote a right in the task.
1551 INVALID_NAME = 15,
1552 /// Target task isn't an active task.
1553 INVALID_TASK = 16,
1554 /// The name denotes a right, but not an appropriate right.
1555 INVALID_RIGHT = 17,
1556 /// A blatant range error.
1557 INVALID_VALUE = 18,
1558 /// Operation would overflow limit on user-references.
1559 UREFS_OVERFLOW = 19,
1560 /// The supplied (port) capability is improper.
1561 INVALID_CAPABILITY = 20,
1562 /// The task already has send or receive rights
1563 /// for the port under another name.
1564 RIGHT_EXISTS = 21,
1565 /// Target host isn't actually a host.
1566 INVALID_HOST = 22,
1567 /// An attempt was made to supply "precious" data
1568 /// for memory that is already present in a
1569 /// memory object.
1570 MEMORY_PRESENT = 23,
1571 /// A page was requested of a memory manager via
1572 /// memory_object_data_request for an object using
1573 /// a MEMORY_OBJECT_COPY_CALL strategy, with the
1574 /// VM_PROT_WANTS_COPY flag being used to specify
1575 /// that the page desired is for a copy of the
1576 /// object, and the memory manager has detected
1577 /// the page was pushed into a copy of the object
1578 /// while the kernel was walking the shadow chain
1579 /// from the copy to the object. This error code
1580 /// is delivered via memory_object_data_error
1581 /// and is handled by the kernel (it forces the
1582 /// kernel to restart the fault). It will not be
1583 /// seen by users.
1584 MEMORY_DATA_MOVED = 24,
1585 /// A strategic copy was attempted of an object
1586 /// upon which a quicker copy is now possible.
1587 /// The caller should retry the copy using
1588 /// vm_object_copy_quickly. This error code
1589 /// is seen only by the kernel.
1590 MEMORY_RESTART_COPY = 25,
1591 /// An argument applied to assert processor set privilege
1592 /// was not a processor set control port.
1593 INVALID_PROCESSOR_SET = 26,
1594 /// The specified scheduling attributes exceed the thread's
1595 /// limits.
1596 POLICY_LIMIT = 27,
1597 /// The specified scheduling policy is not currently
1598 /// enabled for the processor set.
1599 INVALID_POLICY = 28,
1600 /// The external memory manager failed to initialize the
1601 /// memory object.
1602 INVALID_OBJECT = 29,
1603 /// A thread is attempting to wait for an event for which
1604 /// there is already a waiting thread.
1605 ALREADY_WAITING = 30,
1606 /// An attempt was made to destroy the default processor
1607 /// set.
1608 DEFAULT_SET = 31,
1609 /// An attempt was made to fetch an exception port that is
1610 /// protected, or to abort a thread while processing a
1611 /// protected exception.
1612 EXCEPTION_PROTECTED = 32,
1613 /// A ledger was required but not supplied.
1614 INVALID_LEDGER = 33,
1615 /// The port was not a memory cache control port.
1616 INVALID_MEMORY_CONTROL = 34,
1617 /// An argument supplied to assert security privilege
1618 /// was not a host security port.
1619 INVALID_SECURITY = 35,
1620 /// thread_depress_abort was called on a thread which
1621 /// was not currently depressed.
1622 NOT_DEPRESSED = 36,
1623 /// Object has been terminated and is no longer available
1624 TERMINATED = 37,
1625 /// Lock set has been destroyed and is no longer available.
1626 LOCK_SET_DESTROYED = 38,
1627 /// The thread holding the lock terminated before releasing
1628 /// the lock
1629 LOCK_UNSTABLE = 39,
1630 /// The lock is already owned by another thread
1631 LOCK_OWNED = 40,
1632 /// The lock is already owned by the calling thread
1633 LOCK_OWNED_SELF = 41,
1634 /// Semaphore has been destroyed and is no longer available.
1635 SEMAPHORE_DESTROYED = 42,
1636 /// Return from RPC indicating the target server was
1637 /// terminated before it successfully replied
1638 RPC_SERVER_TERMINATED = 43,
1639 /// Terminate an orphaned activation.
1640 RPC_TERMINATE_ORPHAN = 44,
1641 /// Allow an orphaned activation to continue executing.
1642 RPC_CONTINUE_ORPHAN = 45,
1643 /// Empty thread activation (No thread linked to it)
1644 NOT_SUPPORTED = 46,
1645 /// Remote node down or inaccessible.
1646 NODE_DOWN = 47,
1647 /// A signalled thread was not actually waiting.
1648 NOT_WAITING = 48,
1649 /// Some thread-oriented operation (semaphore_wait) timed out
1650 OPERATION_TIMED_OUT = 49,
1651 /// During a page fault, indicates that the page was rejected
1652 /// as a result of a signature check.
1653 CODESIGN_ERROR = 50,
1654 /// The requested property cannot be changed at this time.
1655 POLICY_STATIC = 51,
1656 /// The provided buffer is of insufficient size for the requested data.
1657 INSUFFICIENT_BUFFER_SIZE = 52,
1658 /// Denied by security policy
1659 DENIED = 53,
1660 /// The KC on which the function is operating is missing
1661 MISSING_KC = 54,
1662 /// The KC on which the function is operating is invalid
1663 INVALID_KC = 55,
1664 /// A search or query operation did not return a result
1665 NOT_FOUND = 56,
1666 _,
1667};
16681020
16691021pub const mach_msg_return_t = kern_return_t;
16701022
src/link/MachO.zig+645-2
......@@ -3779,7 +3779,7 @@ pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {
37793779pub fn ptraceAttach(self: *MachO, pid: std.posix.pid_t) !void {
37803780 if (!is_hot_update_compatible) return;
37813781
3782 const mach_task = try std.c.machTaskForPid(pid);
3782 const mach_task = try machTaskForPid(pid);
37833783 log.debug("Mach task for pid {d}: {any}", .{ pid, mach_task });
37843784 self.hot_state.mach_task = mach_task;
37853785
......@@ -4058,7 +4058,7 @@ pub const LiteralPool = struct {
40584058};
40594059
40604060const HotUpdateState = struct {
4061 mach_task: ?std.c.MachTask = null,
4061 mach_task: ?MachTask = null,
40624062};
40634063
40644064pub const SymtabCtx = struct {
......@@ -4562,3 +4562,646 @@ const UnwindInfo = @import("MachO/UnwindInfo.zig");
45624562const WeakBind = bind.WeakBind;
45634563const ZigGotSection = synthetic.ZigGotSection;
45644564const ZigObject = @import("MachO/ZigObject.zig");
4565
4566pub const MachError = error{
4567 /// Not enough permissions held to perform the requested kernel
4568 /// call.
4569 PermissionDenied,
4570} || std.posix.UnexpectedError;
4571
4572pub const MachTask = extern struct {
4573 port: std.c.mach_port_name_t,
4574
4575 pub fn isValid(self: MachTask) bool {
4576 return self.port != std.c.TASK_NULL;
4577 }
4578
4579 pub fn pidForTask(self: MachTask) MachError!std.c.pid_t {
4580 var pid: std.c.pid_t = undefined;
4581 switch (getKernError(std.c.pid_for_task(self.port, &pid))) {
4582 .SUCCESS => return pid,
4583 .FAILURE => return error.PermissionDenied,
4584 else => |err| return unexpectedKernError(err),
4585 }
4586 }
4587
4588 pub fn allocatePort(self: MachTask, right: std.c.MACH_PORT_RIGHT) MachError!MachTask {
4589 var out_port: std.c.mach_port_name_t = undefined;
4590 switch (getKernError(std.c.mach_port_allocate(
4591 self.port,
4592 @intFromEnum(right),
4593 &out_port,
4594 ))) {
4595 .SUCCESS => return .{ .port = out_port },
4596 .FAILURE => return error.PermissionDenied,
4597 else => |err| return unexpectedKernError(err),
4598 }
4599 }
4600
4601 pub fn deallocatePort(self: MachTask, port: MachTask) void {
4602 _ = getKernError(std.c.mach_port_deallocate(self.port, port.port));
4603 }
4604
4605 pub fn insertRight(self: MachTask, port: MachTask, msg: std.c.MACH_MSG_TYPE) !void {
4606 switch (getKernError(std.c.mach_port_insert_right(
4607 self.port,
4608 port.port,
4609 port.port,
4610 @intFromEnum(msg),
4611 ))) {
4612 .SUCCESS => return,
4613 .FAILURE => return error.PermissionDenied,
4614 else => |err| return unexpectedKernError(err),
4615 }
4616 }
4617
4618 pub const PortInfo = struct {
4619 mask: std.c.exception_mask_t,
4620 masks: [std.c.EXC.TYPES_COUNT]std.c.exception_mask_t,
4621 ports: [std.c.EXC.TYPES_COUNT]std.c.mach_port_t,
4622 behaviors: [std.c.EXC.TYPES_COUNT]std.c.exception_behavior_t,
4623 flavors: [std.c.EXC.TYPES_COUNT]std.c.thread_state_flavor_t,
4624 count: std.c.mach_msg_type_number_t,
4625 };
4626
4627 pub fn getExceptionPorts(self: MachTask, mask: std.c.exception_mask_t) !PortInfo {
4628 var info: PortInfo = .{
4629 .mask = mask,
4630 .masks = undefined,
4631 .ports = undefined,
4632 .behaviors = undefined,
4633 .flavors = undefined,
4634 .count = 0,
4635 };
4636 info.count = info.ports.len / @sizeOf(std.c.mach_port_t);
4637
4638 switch (getKernError(std.c.task_get_exception_ports(
4639 self.port,
4640 info.mask,
4641 &info.masks,
4642 &info.count,
4643 &info.ports,
4644 &info.behaviors,
4645 &info.flavors,
4646 ))) {
4647 .SUCCESS => return info,
4648 .FAILURE => return error.PermissionDenied,
4649 else => |err| return unexpectedKernError(err),
4650 }
4651 }
4652
4653 pub fn setExceptionPorts(
4654 self: MachTask,
4655 mask: std.c.exception_mask_t,
4656 new_port: MachTask,
4657 behavior: std.c.exception_behavior_t,
4658 new_flavor: std.c.thread_state_flavor_t,
4659 ) !void {
4660 switch (getKernError(std.c.task_set_exception_ports(
4661 self.port,
4662 mask,
4663 new_port.port,
4664 behavior,
4665 new_flavor,
4666 ))) {
4667 .SUCCESS => return,
4668 .FAILURE => return error.PermissionDenied,
4669 else => |err| return unexpectedKernError(err),
4670 }
4671 }
4672
4673 pub const RegionInfo = struct {
4674 pub const Tag = enum {
4675 basic,
4676 extended,
4677 top,
4678 };
4679
4680 base_addr: u64,
4681 tag: Tag,
4682 info: union {
4683 basic: std.c.vm_region_basic_info_64,
4684 extended: std.c.vm_region_extended_info,
4685 top: std.c.vm_region_top_info,
4686 },
4687 };
4688
4689 pub fn getRegionInfo(
4690 task: MachTask,
4691 address: u64,
4692 len: usize,
4693 tag: RegionInfo.Tag,
4694 ) MachError!RegionInfo {
4695 var info: RegionInfo = .{
4696 .base_addr = address,
4697 .tag = tag,
4698 .info = undefined,
4699 };
4700 switch (tag) {
4701 .basic => info.info = .{ .basic = undefined },
4702 .extended => info.info = .{ .extended = undefined },
4703 .top => info.info = .{ .top = undefined },
4704 }
4705 var base_len: std.c.mach_vm_size_t = if (len == 1) 2 else len;
4706 var objname: std.c.mach_port_t = undefined;
4707 var count: std.c.mach_msg_type_number_t = switch (tag) {
4708 .basic => std.c.VM.REGION.BASIC_INFO_COUNT,
4709 .extended => std.c.VM.REGION.EXTENDED_INFO_COUNT,
4710 .top => std.c.VM.REGION.TOP_INFO_COUNT,
4711 };
4712 switch (getKernError(std.c.mach_vm_region(
4713 task.port,
4714 &info.base_addr,
4715 &base_len,
4716 switch (tag) {
4717 .basic => std.c.VM.REGION.BASIC_INFO_64,
4718 .extended => std.c.VM.REGION.EXTENDED_INFO,
4719 .top => std.c.VM.REGION.TOP_INFO,
4720 },
4721 switch (tag) {
4722 .basic => @as(std.c.vm_region_info_t, @ptrCast(&info.info.basic)),
4723 .extended => @as(std.c.vm_region_info_t, @ptrCast(&info.info.extended)),
4724 .top => @as(std.c.vm_region_info_t, @ptrCast(&info.info.top)),
4725 },
4726 &count,
4727 &objname,
4728 ))) {
4729 .SUCCESS => return info,
4730 .FAILURE => return error.PermissionDenied,
4731 else => |err| return unexpectedKernError(err),
4732 }
4733 }
4734
4735 pub const RegionSubmapInfo = struct {
4736 pub const Tag = enum {
4737 short,
4738 full,
4739 };
4740
4741 tag: Tag,
4742 base_addr: u64,
4743 info: union {
4744 short: std.c.vm_region_submap_short_info_64,
4745 full: std.c.vm_region_submap_info_64,
4746 },
4747 };
4748
4749 pub fn getRegionSubmapInfo(
4750 task: MachTask,
4751 address: u64,
4752 len: usize,
4753 nesting_depth: u32,
4754 tag: RegionSubmapInfo.Tag,
4755 ) MachError!RegionSubmapInfo {
4756 var info: RegionSubmapInfo = .{
4757 .base_addr = address,
4758 .tag = tag,
4759 .info = undefined,
4760 };
4761 switch (tag) {
4762 .short => info.info = .{ .short = undefined },
4763 .full => info.info = .{ .full = undefined },
4764 }
4765 var nesting = nesting_depth;
4766 var base_len: std.c.mach_vm_size_t = if (len == 1) 2 else len;
4767 var count: std.c.mach_msg_type_number_t = switch (tag) {
4768 .short => std.c.VM.REGION.SUBMAP_SHORT_INFO_COUNT_64,
4769 .full => std.c.VM.REGION.SUBMAP_INFO_COUNT_64,
4770 };
4771 switch (getKernError(std.c.mach_vm_region_recurse(
4772 task.port,
4773 &info.base_addr,
4774 &base_len,
4775 &nesting,
4776 switch (tag) {
4777 .short => @as(std.c.vm_region_recurse_info_t, @ptrCast(&info.info.short)),
4778 .full => @as(std.c.vm_region_recurse_info_t, @ptrCast(&info.info.full)),
4779 },
4780 &count,
4781 ))) {
4782 .SUCCESS => return info,
4783 .FAILURE => return error.PermissionDenied,
4784 else => |err| return unexpectedKernError(err),
4785 }
4786 }
4787
4788 pub fn getCurrProtection(task: MachTask, address: u64, len: usize) MachError!std.c.vm_prot_t {
4789 const info = try task.getRegionSubmapInfo(address, len, 0, .short);
4790 return info.info.short.protection;
4791 }
4792
4793 pub fn setMaxProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void {
4794 return task.setProtectionImpl(address, len, true, prot);
4795 }
4796
4797 pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void {
4798 return task.setProtectionImpl(address, len, false, prot);
4799 }
4800
4801 fn setProtectionImpl(task: MachTask, address: u64, len: usize, set_max: bool, prot: std.c.vm_prot_t) MachError!void {
4802 switch (getKernError(std.c.mach_vm_protect(task.port, address, len, @intFromBool(set_max), prot))) {
4803 .SUCCESS => return,
4804 .FAILURE => return error.PermissionDenied,
4805 else => |err| return unexpectedKernError(err),
4806 }
4807 }
4808
4809 /// Will write to VM even if current protection attributes specifically prohibit
4810 /// us from doing so, by temporarily setting protection level to a level with VM_PROT_COPY
4811 /// variant, and resetting after a successful or unsuccessful write.
4812 pub fn writeMemProtected(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
4813 const curr_prot = try task.getCurrProtection(address, buf.len);
4814 try task.setCurrProtection(
4815 address,
4816 buf.len,
4817 std.c.PROT.READ | std.c.PROT.WRITE | std.c.PROT.COPY,
4818 );
4819 defer {
4820 task.setCurrProtection(address, buf.len, curr_prot) catch {};
4821 }
4822 return task.writeMem(address, buf, arch);
4823 }
4824
4825 pub fn writeMem(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
4826 const count = buf.len;
4827 var total_written: usize = 0;
4828 var curr_addr = address;
4829 const page_size = try MachTask.getPageSize(task); // TODO we probably can assume value here
4830 var out_buf = buf[0..];
4831
4832 while (total_written < count) {
4833 const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_written);
4834 switch (getKernError(std.c.mach_vm_write(
4835 task.port,
4836 curr_addr,
4837 @intFromPtr(out_buf.ptr),
4838 @as(std.c.mach_msg_type_number_t, @intCast(curr_size)),
4839 ))) {
4840 .SUCCESS => {},
4841 .FAILURE => return error.PermissionDenied,
4842 else => |err| return unexpectedKernError(err),
4843 }
4844
4845 switch (arch) {
4846 .aarch64 => {
4847 var mattr_value: std.c.vm_machine_attribute_val_t = std.c.MATTR.VAL_CACHE_FLUSH;
4848 switch (getKernError(std.c.vm_machine_attribute(
4849 task.port,
4850 curr_addr,
4851 curr_size,
4852 std.c.MATTR.CACHE,
4853 &mattr_value,
4854 ))) {
4855 .SUCCESS => {},
4856 .FAILURE => return error.PermissionDenied,
4857 else => |err| return unexpectedKernError(err),
4858 }
4859 },
4860 .x86_64 => {},
4861 else => unreachable,
4862 }
4863
4864 out_buf = out_buf[curr_size..];
4865 total_written += curr_size;
4866 curr_addr += curr_size;
4867 }
4868
4869 return total_written;
4870 }
4871
4872 pub fn readMem(task: MachTask, address: u64, buf: []u8) MachError!usize {
4873 const count = buf.len;
4874 var total_read: usize = 0;
4875 var curr_addr = address;
4876 const page_size = try MachTask.getPageSize(task); // TODO we probably can assume value here
4877 var out_buf = buf[0..];
4878
4879 while (total_read < count) {
4880 const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_read);
4881 var curr_bytes_read: std.c.mach_msg_type_number_t = 0;
4882 var vm_memory: std.c.vm_offset_t = undefined;
4883 switch (getKernError(std.c.mach_vm_read(task.port, curr_addr, curr_size, &vm_memory, &curr_bytes_read))) {
4884 .SUCCESS => {},
4885 .FAILURE => return error.PermissionDenied,
4886 else => |err| return unexpectedKernError(err),
4887 }
4888
4889 @memcpy(out_buf[0..curr_bytes_read], @as([*]const u8, @ptrFromInt(vm_memory)));
4890 _ = std.c.vm_deallocate(std.c.mach_task_self(), vm_memory, curr_bytes_read);
4891
4892 out_buf = out_buf[curr_bytes_read..];
4893 curr_addr += curr_bytes_read;
4894 total_read += curr_bytes_read;
4895 }
4896
4897 return total_read;
4898 }
4899
4900 fn maxBytesLeftInPage(page_size: usize, address: u64, count: usize) usize {
4901 var left = count;
4902 if (page_size > 0) {
4903 const page_offset = address % page_size;
4904 const bytes_left_in_page = page_size - page_offset;
4905 if (count > bytes_left_in_page) {
4906 left = bytes_left_in_page;
4907 }
4908 }
4909 return left;
4910 }
4911
4912 fn getPageSize(task: MachTask) MachError!usize {
4913 if (task.isValid()) {
4914 var info_count = std.c.TASK_VM_INFO_COUNT;
4915 var vm_info: std.c.task_vm_info_data_t = undefined;
4916 switch (getKernError(std.c.task_info(
4917 task.port,
4918 std.c.TASK_VM_INFO,
4919 @as(std.c.task_info_t, @ptrCast(&vm_info)),
4920 &info_count,
4921 ))) {
4922 .SUCCESS => return @as(usize, @intCast(vm_info.page_size)),
4923 else => {},
4924 }
4925 }
4926 var page_size: std.c.vm_size_t = undefined;
4927 switch (getKernError(std.c._host_page_size(std.c.mach_host_self(), &page_size))) {
4928 .SUCCESS => return page_size,
4929 else => |err| return unexpectedKernError(err),
4930 }
4931 }
4932
4933 pub fn basicTaskInfo(task: MachTask) MachError!std.c.mach_task_basic_info {
4934 var info: std.c.mach_task_basic_info = undefined;
4935 var count = std.c.MACH_TASK_BASIC_INFO_COUNT;
4936 switch (getKernError(std.c.task_info(
4937 task.port,
4938 std.c.MACH_TASK_BASIC_INFO,
4939 @as(std.c.task_info_t, @ptrCast(&info)),
4940 &count,
4941 ))) {
4942 .SUCCESS => return info,
4943 else => |err| return unexpectedKernError(err),
4944 }
4945 }
4946
4947 pub fn @"resume"(task: MachTask) MachError!void {
4948 switch (getKernError(std.c.task_resume(task.port))) {
4949 .SUCCESS => {},
4950 else => |err| return unexpectedKernError(err),
4951 }
4952 }
4953
4954 pub fn @"suspend"(task: MachTask) MachError!void {
4955 switch (getKernError(std.c.task_suspend(task.port))) {
4956 .SUCCESS => {},
4957 else => |err| return unexpectedKernError(err),
4958 }
4959 }
4960
4961 const ThreadList = struct {
4962 buf: []MachThread,
4963
4964 pub fn deinit(list: ThreadList) void {
4965 const self_task = machTaskForSelf();
4966 _ = std.c.vm_deallocate(
4967 self_task.port,
4968 @intFromPtr(list.buf.ptr),
4969 @as(std.c.vm_size_t, @intCast(list.buf.len * @sizeOf(std.c.mach_port_t))),
4970 );
4971 }
4972 };
4973
4974 pub fn getThreads(task: MachTask) MachError!ThreadList {
4975 var thread_list: std.c.mach_port_array_t = undefined;
4976 var thread_count: std.c.mach_msg_type_number_t = undefined;
4977 switch (getKernError(std.c.task_threads(task.port, &thread_list, &thread_count))) {
4978 .SUCCESS => return ThreadList{ .buf = @as([*]MachThread, @ptrCast(thread_list))[0..thread_count] },
4979 else => |err| return unexpectedKernError(err),
4980 }
4981 }
4982};
4983
4984pub const MachThread = extern struct {
4985 port: std.c.mach_port_t,
4986
4987 pub fn isValid(thread: MachThread) bool {
4988 return thread.port != std.c.THREAD_NULL;
4989 }
4990
4991 pub fn getBasicInfo(thread: MachThread) MachError!std.c.thread_basic_info {
4992 var info: std.c.thread_basic_info = undefined;
4993 var count = std.c.THREAD_BASIC_INFO_COUNT;
4994 switch (getKernError(std.c.thread_info(
4995 thread.port,
4996 std.c.THREAD_BASIC_INFO,
4997 @as(std.c.thread_info_t, @ptrCast(&info)),
4998 &count,
4999 ))) {
5000 .SUCCESS => return info,
5001 else => |err| return unexpectedKernError(err),
5002 }
5003 }
5004
5005 pub fn getIdentifierInfo(thread: MachThread) MachError!std.c.thread_identifier_info {
5006 var info: std.c.thread_identifier_info = undefined;
5007 var count = std.c.THREAD_IDENTIFIER_INFO_COUNT;
5008 switch (getKernError(std.c.thread_info(
5009 thread.port,
5010 std.c.THREAD_IDENTIFIER_INFO,
5011 @as(std.c.thread_info_t, @ptrCast(&info)),
5012 &count,
5013 ))) {
5014 .SUCCESS => return info,
5015 else => |err| return unexpectedKernError(err),
5016 }
5017 }
5018};
5019
5020pub fn machTaskForPid(pid: std.c.pid_t) MachError!MachTask {
5021 var port: std.c.mach_port_name_t = undefined;
5022 switch (getKernError(std.c.task_for_pid(std.c.mach_task_self(), pid, &port))) {
5023 .SUCCESS => {},
5024 .FAILURE => return error.PermissionDenied,
5025 else => |err| return unexpectedKernError(err),
5026 }
5027 return MachTask{ .port = port };
5028}
5029
5030pub fn machTaskForSelf() MachTask {
5031 return .{ .port = std.c.mach_task_self() };
5032}
5033
5034pub fn getKernError(err: std.c.kern_return_t) KernE {
5035 return @as(KernE, @enumFromInt(@as(u32, @truncate(@as(usize, @intCast(err))))));
5036}
5037
5038pub fn unexpectedKernError(err: KernE) std.posix.UnexpectedError {
5039 if (std.posix.unexpected_error_tracing) {
5040 std.debug.print("unexpected error: {d}\n", .{@intFromEnum(err)});
5041 std.debug.dumpCurrentStackTrace(null);
5042 }
5043 return error.Unexpected;
5044}
5045
5046/// Kernel return values
5047pub const KernE = enum(u32) {
5048 SUCCESS = 0,
5049 /// Specified address is not currently valid
5050 INVALID_ADDRESS = 1,
5051 /// Specified memory is valid, but does not permit the
5052 /// required forms of access.
5053 PROTECTION_FAILURE = 2,
5054 /// The address range specified is already in use, or
5055 /// no address range of the size specified could be
5056 /// found.
5057 NO_SPACE = 3,
5058 /// The function requested was not applicable to this
5059 /// type of argument, or an argument is invalid
5060 INVALID_ARGUMENT = 4,
5061 /// The function could not be performed. A catch-all.
5062 FAILURE = 5,
5063 /// A system resource could not be allocated to fulfill
5064 /// this request. This failure may not be permanent.
5065 RESOURCE_SHORTAGE = 6,
5066 /// The task in question does not hold receive rights
5067 /// for the port argument.
5068 NOT_RECEIVER = 7,
5069 /// Bogus access restriction.
5070 NO_ACCESS = 8,
5071 /// During a page fault, the target address refers to a
5072 /// memory object that has been destroyed. This
5073 /// failure is permanent.
5074 MEMORY_FAILURE = 9,
5075 /// During a page fault, the memory object indicated
5076 /// that the data could not be returned. This failure
5077 /// may be temporary; future attempts to access this
5078 /// same data may succeed, as defined by the memory
5079 /// object.
5080 MEMORY_ERROR = 10,
5081 /// The receive right is already a member of the portset.
5082 ALREADY_IN_SET = 11,
5083 /// The receive right is not a member of a port set.
5084 NOT_IN_SET = 12,
5085 /// The name already denotes a right in the task.
5086 NAME_EXISTS = 13,
5087 /// The operation was aborted. Ipc code will
5088 /// catch this and reflect it as a message error.
5089 ABORTED = 14,
5090 /// The name doesn't denote a right in the task.
5091 INVALID_NAME = 15,
5092 /// Target task isn't an active task.
5093 INVALID_TASK = 16,
5094 /// The name denotes a right, but not an appropriate right.
5095 INVALID_RIGHT = 17,
5096 /// A blatant range error.
5097 INVALID_VALUE = 18,
5098 /// Operation would overflow limit on user-references.
5099 UREFS_OVERFLOW = 19,
5100 /// The supplied (port) capability is improper.
5101 INVALID_CAPABILITY = 20,
5102 /// The task already has send or receive rights
5103 /// for the port under another name.
5104 RIGHT_EXISTS = 21,
5105 /// Target host isn't actually a host.
5106 INVALID_HOST = 22,
5107 /// An attempt was made to supply "precious" data
5108 /// for memory that is already present in a
5109 /// memory object.
5110 MEMORY_PRESENT = 23,
5111 /// A page was requested of a memory manager via
5112 /// memory_object_data_request for an object using
5113 /// a MEMORY_OBJECT_COPY_CALL strategy, with the
5114 /// VM_PROT_WANTS_COPY flag being used to specify
5115 /// that the page desired is for a copy of the
5116 /// object, and the memory manager has detected
5117 /// the page was pushed into a copy of the object
5118 /// while the kernel was walking the shadow chain
5119 /// from the copy to the object. This error code
5120 /// is delivered via memory_object_data_error
5121 /// and is handled by the kernel (it forces the
5122 /// kernel to restart the fault). It will not be
5123 /// seen by users.
5124 MEMORY_DATA_MOVED = 24,
5125 /// A strategic copy was attempted of an object
5126 /// upon which a quicker copy is now possible.
5127 /// The caller should retry the copy using
5128 /// vm_object_copy_quickly. This error code
5129 /// is seen only by the kernel.
5130 MEMORY_RESTART_COPY = 25,
5131 /// An argument applied to assert processor set privilege
5132 /// was not a processor set control port.
5133 INVALID_PROCESSOR_SET = 26,
5134 /// The specified scheduling attributes exceed the thread's
5135 /// limits.
5136 POLICY_LIMIT = 27,
5137 /// The specified scheduling policy is not currently
5138 /// enabled for the processor set.
5139 INVALID_POLICY = 28,
5140 /// The external memory manager failed to initialize the
5141 /// memory object.
5142 INVALID_OBJECT = 29,
5143 /// A thread is attempting to wait for an event for which
5144 /// there is already a waiting thread.
5145 ALREADY_WAITING = 30,
5146 /// An attempt was made to destroy the default processor
5147 /// set.
5148 DEFAULT_SET = 31,
5149 /// An attempt was made to fetch an exception port that is
5150 /// protected, or to abort a thread while processing a
5151 /// protected exception.
5152 EXCEPTION_PROTECTED = 32,
5153 /// A ledger was required but not supplied.
5154 INVALID_LEDGER = 33,
5155 /// The port was not a memory cache control port.
5156 INVALID_MEMORY_CONTROL = 34,
5157 /// An argument supplied to assert security privilege
5158 /// was not a host security port.
5159 INVALID_SECURITY = 35,
5160 /// thread_depress_abort was called on a thread which
5161 /// was not currently depressed.
5162 NOT_DEPRESSED = 36,
5163 /// Object has been terminated and is no longer available
5164 TERMINATED = 37,
5165 /// Lock set has been destroyed and is no longer available.
5166 LOCK_SET_DESTROYED = 38,
5167 /// The thread holding the lock terminated before releasing
5168 /// the lock
5169 LOCK_UNSTABLE = 39,
5170 /// The lock is already owned by another thread
5171 LOCK_OWNED = 40,
5172 /// The lock is already owned by the calling thread
5173 LOCK_OWNED_SELF = 41,
5174 /// Semaphore has been destroyed and is no longer available.
5175 SEMAPHORE_DESTROYED = 42,
5176 /// Return from RPC indicating the target server was
5177 /// terminated before it successfully replied
5178 RPC_SERVER_TERMINATED = 43,
5179 /// Terminate an orphaned activation.
5180 RPC_TERMINATE_ORPHAN = 44,
5181 /// Allow an orphaned activation to continue executing.
5182 RPC_CONTINUE_ORPHAN = 45,
5183 /// Empty thread activation (No thread linked to it)
5184 NOT_SUPPORTED = 46,
5185 /// Remote node down or inaccessible.
5186 NODE_DOWN = 47,
5187 /// A signalled thread was not actually waiting.
5188 NOT_WAITING = 48,
5189 /// Some thread-oriented operation (semaphore_wait) timed out
5190 OPERATION_TIMED_OUT = 49,
5191 /// During a page fault, indicates that the page was rejected
5192 /// as a result of a signature check.
5193 CODESIGN_ERROR = 50,
5194 /// The requested property cannot be changed at this time.
5195 POLICY_STATIC = 51,
5196 /// The provided buffer is of insufficient size for the requested data.
5197 INSUFFICIENT_BUFFER_SIZE = 52,
5198 /// Denied by security policy
5199 DENIED = 53,
5200 /// The KC on which the function is operating is missing
5201 MISSING_KC = 54,
5202 /// The KC on which the function is operating is invalid
5203 INVALID_KC = 55,
5204 /// A search or query operation did not return a result
5205 NOT_FOUND = 56,
5206 _,
5207};