1#!/usr/bin/env recon 2 3local cjson = require 'cjson' 4local lpeg = require 'lpeg' 5lpeg.locale(lpeg) 6 7-- Only certain regions of the master file should be parsed. 8-- The convention is that any `#if/#else/#endif` clauses are parsed, assuming the condition is true/defined, except for `COMPAT_GETFSSTAT`. 9 10local region_state = { valid = true, } 11local function in_valid_region(line) 12 -- Only C preprocessor directives can affect the region's validity. 13 if line:sub(1, 1) ~= '#' then 14 return region_state.valid 15 end 16 17 -- This is the only macro definition that is assumed to be undefined. 18 local assume_defined = not line:match('COMPAT_GETFSSTAT') 19 if line:match('^#if') then 20 region_state.valid = assume_defined 21 elseif line:match('^#else') then 22 region_state.valid = not region_state.valid 23 elseif line:match('^#endif') then 24 region_state.valid = true 25 end 26end 27 28-- Parse a syscall declaration line from `bsd/kern/syscalls.master` into a table with `name`, `number`, `arguments`, and `old` keys. 29 30-- Primitive tokens. 31local space = lpeg.S(' \t')^0 32local identifier = (lpeg.alnum + lpeg.P('_'))^1 33local numeric = lpeg.digit^1 / tonumber 34 35-- Matching the function name of the syscall declaration. 36local function_ptn = lpeg.Cg(identifier^1, 'name') * lpeg.P('(') 37local nosys_ptn = lpeg.P('e')^-1 * lpeg.P('nosys(') 38 39-- Matching an argument list. 40local arg = lpeg.C((1 - lpeg.S(',)'))^1) 41local args_ptn = lpeg.Ct(arg * (lpeg.P(',') * space * arg)^0) 42 43-- Matching a normal C-style declaration of the syscall. 44local decl_ptn = (1 - function_ptn)^1 * (function_ptn - nosys_ptn) * 45 lpeg.Cg(args_ptn, 'arguments') 46 47-- Matching an old breadcrumb, with empty arguments table. 48local old_ptn = lpeg.P('old') * space * lpeg.Cg(identifier^1, 'name') * 49 lpeg.Cg(lpeg.Cc(true), 'old') * lpeg.Cg(lpeg.Cc({}), 'arguments') 50local old_decl_ptn = (1 - old_ptn)^1 * old_ptn 51 52local syscall_ptn = lpeg.Ct(lpeg.Cg(numeric, 'number') * 53 (decl_ptn + old_decl_ptn)) 54 55local bsd_syscalls = {} 56for line in io.stdin:lines() do 57 if in_valid_region(line) then 58 bsd_syscalls[#bsd_syscalls + 1] = syscall_ptn:match(line) 59 end 60end 61 62local syscalls = { 63 bsd_syscalls = bsd_syscalls, 64 mach_syscalls = { 65 -- Duplicate the names from `mach_trap_table` here. 66 { number = 10, name = 'mach_vm_allocate', 67 arguments = { 68 'mach_port_name_t target', 69 'mach_vm_address_t *address', 70 'mach_vm_size_t size', 71 'int flags', 72 }, 73 }, 74 { number = 11, name = 'mach_vm_purgable_control', 75 arguments = { 76 'mach_port_name_t target', 77 'mach_vm_offset_t address', 78 'vm_purgable_t control', 79 'int *state', 80 }, 81 }, 82 { number = 12, name = 'mach_vm_deallocate', 83 arguments = { 84 'mach_port_name_t target', 85 'mach_vm_address_t address', 86 'mach_vm_size_t size', 87 }, 88 }, 89 { number = 13, name = 'task_dyld_process_info_notify_get', 90 arguments = { 91 'mach_port_name_array_t names_addr', 92 'natural_t *names_count_addr', 93 }, 94 }, 95 { number = 14, name = 'mach_vm_protect', 96 arguments = { 97 'mach_port_name_t task', 98 'mach_vm_address_t address', 99 'mach_vm_size_t size', 100 'boolean_t set_maximum', 101 'vm_prot_t new_protection', 102 } 103 }, 104 { number = 15, name = 'mach_vm_map', 105 arguments = { 106 'mach_port_name_t target', 107 'mach_vm_address_t *address', 108 'mach_vm_size_t size', 109 'mach_vm_offset_t mask', 110 'int flags', 111 'mem_entry_name_port_t object', 112 'memory_object_offset_t offset', 113 'boolean_t copy', 114 'vm_prot_t cur_protection', 115 'vm_prot_t max_protection', 116 'vm_inherit_t inheritance', 117 }, 118 }, 119 { number = 16, name = 'mach_port_allocate', 120 arguments = { 121 'mach_port_name_t target', 122 'mach_port_right_t right', 123 'mach_port_name_t *name', 124 }, 125 }, 126 127 { number = 18, name = 'mach_port_deallocate', 128 arguments = { 129 'mach_port_name_t target', 130 'mach_port_name_t name', 131 }, 132 }, 133 { number = 19, name = 'mach_port_mod_refs', 134 arguments = { 135 'mach_port_name_t target', 136 'mach_port_name_t name', 137 'mach_port_right_t right', 138 'mach_port_delta_t delta', 139 }, 140 }, 141 { number = 20, name = 'mach_port_move_member', 142 arguments = { 143 'mach_port_name_t target', 144 'mach_port_name_t member', 145 'mach_port_name_t after', 146 }, 147 }, 148 { number = 21, name = 'mach_port_insert_right', 149 arguments = { 150 'mach_port_name_t target', 151 'mach_port_name_t name', 152 'mach_port_name_t poly', 153 'mach_msg_type_name_t polyPoly', 154 }, 155 }, 156 { number = 22, name = 'mach_port_insert_member', 157 arguments = { 158 'mach_port_name_t target', 159 'mach_port_name_t name', 160 'mach_port_name_t pset', 161 }, 162 }, 163 { number = 23, name = 'mach_port_extract_member', 164 arguments = { 165 'mach_port_name_t target', 166 'mach_port_name_t name', 167 'mach_port_name_t pset', 168 }, 169 }, 170 { number = 24, name = 'mach_port_construct', 171 arguments = { 172 'mach_port_name_t target', 173 'mach_port_options_t *options', 174 'uint64_t context', 175 'mach_port_name_t *name', 176 }, 177 }, 178 { number = 25, name = 'mach_port_destruct', 179 arguments = { 180 'mach_port_name_t target', 181 'mach_port_name_t name', 182 'mach_port_delta_t srdelta', 183 'uint64_t guard', 184 }, 185 }, 186 { number = 26, name = 'mach_reply_port', 187 arguments = { 'void' }, 188 }, 189 { number = 27, name = 'thread_self', 190 arguments = { 'void' }, 191 }, 192 { number = 28, name = 'task_self', 193 arguments = { 'void' }, 194 }, 195 { number = 29, name = 'host_self', 196 arguments = { 'void' }, 197 }, 198 199 { number = 31, name = 'mach_msg', 200 arguments = { 201 'mach_msg_header_t *msg', 202 'mach_msg_option_t option', 203 'mach_msg_size_t send_size', 204 'mach_msg_size_t rcv_size', 205 'mach_port_name_t rcv_name', 206 'mach_msg_timeout_t timeout', 207 'mach_port_name_t notify', 208 }, 209 }, 210 { number = 32, name = 'mach_msg_overwrite', 211 arguments = { 212 'mach_msg_header_t *msg', 213 'mach_msg_option_t option', 214 'mach_msg_size_t send_size', 215 'mach_msg_size_t rcv_size', 216 'mach_port_name_t rcv_name', 217 'mach_msg_timeout_t timeout', 218 'mach_port_name_t notify', 219 'mach_msg_header_t *rcv_msg', 220 'mach_msg_size_t rcv_limit', 221 }, 222 }, 223 { number = 33, name = 'semaphore_signal', 224 arguments = { 225 'mach_port_name_t signal_name', 226 }, 227 }, 228 { number = 34, name = 'semaphore_signal_all', 229 arguments = { 230 'mach_port_name_t signal_name', 231 }, 232 }, 233 { number = 35, name = 'semaphore_signal_thread', 234 arguments = { 235 'mach_port_name_t signal_name', 236 'mach_port_name_t thread_name', 237 }, 238 }, 239 { number = 36, name = 'semaphore_wait', 240 arguments = { 241 'mach_port_name_t wait_name', 242 }, 243 }, 244 { number = 37, name = 'semaphore_wait_signal', 245 arguments = { 246 'mach_port_name_t wait_name', 247 'mach_port_name_t signal_name', 248 }, 249 }, 250 { number = 38, name = 'semaphore_timedwait', 251 arguments = { 252 'mach_port_name_t wait_name', 253 'unsigned int sec', 254 'clock_res_t nsec', 255 }, 256 }, 257 { number = 39, name = 'semaphore_timedwait_signal', 258 arguments = { 259 'mach_port_name_t wait_name', 260 'mach_port_name_t signal_name', 261 'unsigned int sec', 262 'clock_res_t nsec', 263 }, 264 }, 265 { number = 40, name = 'mach_port_get_attributes', 266 arguments = { 267 'mach_port_name_t target', 268 'mach_port_name_t name', 269 'mach_port_flavor_t flavor', 270 'mach_port_info_t port_info_out', 271 'mach_msg_type_number_t *port_info_outCnt', 272 }, 273 }, 274 { number = 41, name = 'mach_port_guard', 275 arguments = { 276 'mach_port_name_t target', 277 'mach_port_name_t name', 278 'uint64_t guard', 279 'boolean_t strict', 280 }, 281 }, 282 { number = 42, name = 'mach_port_unguard', 283 arguments = { 284 'mach_port_name_t target', 285 'mach_port_name_t name', 286 'uint64_t guard', 287 }, 288 }, 289 { number = 43, name = 'mach_generate_activity_id', 290 arguments = { 291 'mach_port_name_t target', 292 'int count', 293 'uint64_t *activity_id', 294 }, 295 }, 296 { number = 44, name = 'task_name_for_pid', 297 arguments = { 298 'mach_port_name_t target_tport', 299 'int pid', 300 'mach_port_name_t *tn', 301 }, 302 }, 303 { number = 45, name = 'task_for_pid', 304 arguments = { 305 'mach_port_name_t target_tport', 306 'int pid', 307 'mach_port_name_t *t', 308 }, 309 }, 310 { number = 46, name = 'pid_for_task', 311 arguments = { 312 'mach_port_name_t t', 313 'int *x', 314 }, 315 }, 316 { number = 47, name = 'mach_msg2', 317 arguments = { 318 'void *data', 319 'mach_msg_option64_t option64', 320 'mach_msg_header_t header', 321 'mach_msg_size_t send_size', 322 'mach_msg_size_t rcv_size', 323 'mach_port_t rcv_name', 324 'uint64_t timeout', 325 'uint32_t priority', 326 }, 327 }, 328 { number = 48, name = 'macx_swapon', 329 arguments = { 330 'uint64_t filename', 331 'int flags', 332 'int size', 333 'int priority', 334 }, 335 }, 336 { number = 49, name = 'macx_swapoff', 337 arguments = { 338 'uint64_t filename', 339 'int flags', 340 }, 341 }, 342 { number = 50, name = 'thread_get_special_reply_port', 343 arguments = { 'void' }, 344 }, 345 { number = 51, name = 'macx_triggers', 346 arguments = { 347 'int hi_water', 348 'int low_water', 349 'int flags', 350 'mach_port_t alert_port', 351 }, 352 }, 353 { number = 52, name = 'macx_backing_store_suspend', 354 arguments = { 355 'boolean_t suspend', 356 }, 357 }, 358 { number = 53, name = 'macx_backing_store_recovery', 359 arguments = { 360 'int pid', 361 }, 362 }, 363 364 { number = 58, name = 'pfz_exit', 365 arguments = { 'void' }, 366 }, 367 { number = 59, name = 'swtch_pri', 368 arguments = { 369 'int pri', 370 }, 371 }, 372 { number = 60, name = 'swtch', 373 arguments = { 'void' }, 374 }, 375 { number = 61, name = 'thread_switch', 376 arguments = { 377 'mach_port_name_t thread_name', 378 'int option', 379 'mach_msg_timeout_t option_time', 380 }, 381 }, 382 { number = 62, name = 'clock_sleep', 383 arguments = { 384 'mach_port_name_t clock_name', 385 'sleep_type_t sleep_type', 386 'int sleep_sec', 387 'int sleep_nsec', 388 'mach_timespec_t *wakeup_time', 389 }, 390 }, 391 392 { number = 70, name = 'host_create_mach_voucher', 393 arguments = { 394 'mach_port_name_t host', 395 'mach_voucher_attr_raw_recipe_array_t recipes', 396 'int recipes_size', 397 'mach_port_name_t *voucher', 398 }, 399 }, 400 401 { number = 72, name = 'mach_voucher_extract_attr_recipe', 402 arguments = { 403 'mach_port_name_t voucher_name', 404 'mach_voucher_attr_key_t key', 405 'mach_voucher_attr_raw_recipe_t recipe', 406 'mach_msg_type_number_t *recipe_size', 407 }, 408 }, 409 410 { number = 76, name = 'mach_port_type', 411 arguments = { 412 'ipc_space_t task', 413 'mach_port_name_t name', 414 'mach_port_type_t *ptype', 415 }, 416 }, 417 { number = 77, name = 'mach_port_request_notification', 418 arguments = { 419 'ipc_space_t task', 420 'mach_port_name_t name', 421 'mach_msg_id_t msgid', 422 'mach_port_mscount_t sync', 423 'mach_port_name_t notify', 424 'mach_msg_type_name_t notifyPoly', 425 'mach_port_name_t *previous', 426 }, 427 }, 428 { number = 88, name = 'exclaves_ctl', 429 arguments = { 430 'mach_port_name_t name', 431 'uint32_t operation_and_flags', 432 'uint64_t identifier', 433 'mach_vm_address_t buffer', 434 'mach_vm_size_t size', 435 'mach_vm_size_t size2', 436 'mach_vm_size_t offset', 437 }, 438 }, 439 440 { number = 89, name = 'mach_timebase_info', 441 arguments = { 442 'mach_timebase_info_t info', 443 }, 444 }, 445 { number = 90, name = 'mach_wait_until', 446 arguments = { 447 'uint64_t deadline', 448 }, 449 }, 450 { number = 91, name = 'mk_timer_create', 451 arguments = { 'void' }, 452 }, 453 { number = 92, name = 'mk_timer_destroy', 454 arguments = { 455 'mach_port_name_t name', 456 }, 457 }, 458 { number = 93, name = 'mk_timer_arm', 459 arguments = { 460 'mach_port_name_t name', 461 'uint64_t expire_time', 462 }, 463 }, 464 { number = 94, name = 'mk_timer_cancel', 465 arguments = { 466 'mach_port_name_t name', 467 'uint64_t *result_time', 468 }, 469 }, 470 { number = 95, name = 'mk_timer_arm_leeway', 471 arguments = { 472 'mach_port_name_t name', 473 'uint64_t mk_timer_flags', 474 'uint64_t mk_timer_expire_time', 475 'uint64_t mk_timer_leeway', 476 }, 477 }, 478 { number = 96, name = 'debug_control_port_for_pid', 479 arguments = { 480 'mach_port_name_t target_tport', 481 'int pid', 482 'mach_port_name_t *t', 483 }, 484 }, 485 486 { number = 100, name = 'iokit_user_client', 487 arguments = { 488 'void *userClientRef', 489 'uint32_t index', 490 'void *p1', 491 'void *p2', 492 'void *p3', 493 'void *p4', 494 'void *p5', 495 'void *p6', 496 }, 497 }, 498 }, 499} 500 501-- Basic sanity checking that the same number isn't claimed by two syscalls. 502for type, entries in pairs(syscalls) do 503 local numbers_seen = {} 504 for _, call in ipairs(entries) do 505 if numbers_seen[call.number] then 506 io.stderr:write(('error: %s: saw %d twice: %s and %s\n'):format(type, 507 call.number, call.name, numbers_seen[call.number])) 508 os.exit(1) 509 end 510 numbers_seen[call.number] = call.name 511 end 512end 513 514print(cjson.encode(syscalls)) 515