generate-rpc-wrappers.py
1 #!/usr/bin/env python3 2 3 import os 4 import sys 5 from collections import OrderedDict 6 import textwrap 7 from datetime import datetime 8 9 XNU_TRAP_CALL = 1 << 0 10 XNU_TRAP_NOPREFIX = 1 << 1 11 XNU_TRAP_NOSUFFIX = 1 << 2 12 XNU_TRAP_NOSUFFIX_ARGS = 1 << 3 13 XNU_TRAP_BSD = 1 << 4 14 XNU_TRAP_NO_DTAPE_DEF = 1 << 5 15 XNU_BSD_TRAP_CALL = XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | XNU_TRAP_NOSUFFIX | XNU_TRAP_NOSUFFIX_ARGS | XNU_TRAP_BSD 16 UNMANAGED_CALL = 1 << 6 17 ALLOW_INTERRUPTIONS = 1 << 7 18 PUSH_UNKNOWN_REPLIES = 1 << 8 19 20 # NOTE: in Python 3.7+, we can rely on dictionaries having their items in insertion order. 21 # unfortunately, we can't expect everyone building Darling to have Python 3.7+ installed. 22 calls = [ 23 # 24 # FORMAT: 25 # tuple with either 3 or 4 members: 26 # 1. call name: the name of the remote procedure 27 # 2. call parameters: the set of parameters callers are expected to provide arguments for. 28 # 3. return parameters: the set of parameters the procedure is expected to return values for. 29 # 4. flags: an optional set of flags that modify how this call is processed and wrapped. 30 # if the set of flags (4) is omitted, it defaults to 0. 31 # 32 # PARAMETERS: 33 # each parameter (both for calls and returns) is a tuple with either 2 or 3 members: 34 # 1. parameter name: the name of the parameter (duh) 35 # 2. public type: the type used in the public RPC wrappers 36 # 3. (optional) private type: the type used internally for serialization and for the server implementation. 37 # if the private type (3) is omitted, it is the same as the public type. 38 # 39 # TYPES: 40 # the types that can be used are normal C types. however, to be more architecture-agnostic, 41 # it is recommended to use `stdint.h` types whenever possible (e.g. `int32_t` instead of `int`, 42 # `uint64_t` instead of `unsigned long`, etc.). 43 # 44 # it is VERY IMPORTANT that pointer types ALWAYS have a distinct, fixed-size integral private type 45 # that is wide enough to accommodate pointers for all architectures. a good choice is `uint64_t`; 46 # NOT `uintptr_t`, as its size varies according to the architecture. 47 # 48 # SPECIAL TYPES: 49 # one special type that is supported is `@fd`. this type indicates that the parameter specifies a file descriptor. 50 # it will be treated as an `int` type-wise, but the RPC wrappers will perform some additional work on it 51 # to serialize it across the connection. this works bi-directionally (i.e. both the client and server can send and receive FDs). 52 # the resulting descriptor received on the other end (in either client or server) will behave like a `dup()`ed descriptor. 53 # 54 # FLAGS: 55 # XNU_TRAP_CALL 56 # this indicates that the given call is actually an XNU trap. this enables more advanced wrappers to be generated for that call 57 # and avoid unnecessary boilerplate code on the server side. 58 # XNU_TRAP_NOPREFIX 59 # this indicates that the XNU trap does not use the `_kernelrpc_` prefix on the server side. 60 # this affects both the function and argument structure names. 61 # XNU_TRAP_NOSUFFIX 62 # this indicates that the XNU trap does not use the `_trap` suffix on the server side. 63 # this affects both the function and argument structure names. 64 # XNU_TRAP_NOSUFFIX_ARGS 65 # this indicates the arguments structure for this call on the server side does not use the `_trap` suffix. 66 # this does not affect the name of the function on the server side. 67 # XNU_TRAP_BSD 68 # this indicates the XNU trap is for a BSD syscall. BSD syscalls have 2 return codes: one for failure and one for success. 69 # this flag informs the RPC wrapper generator about this so it can handle it appropriately. 70 # XNU_TRAP_NO_DTAPE_DEF 71 # by default, the RPC wrapper generator code will generate duct-tape wrappers for XNU traps that automatically call the 72 # XNU handler function for the trap. this flag tells it not to do that; this means you must define the duct-tape handler yourself. 73 # UNMANAGED_CALL 74 # this indicates that the given call may be called from an unmanaged process; that is, a process that the server does not manage or 75 # have direct access to (e.g. it cannot access its memory). this is mainly useful for calls that inspect the state of the container. 76 # ALLOW_INTERRUPTIONS 77 # by default, calls are performed atomically with signals disabled on the calling thread; this way, the call is either fully performed 78 # or fully unperformed. this flag indicates that the call should be allowed to be interrupted by signals. most calls should be performed 79 # without interruptions, but calls that may wait (usually for long periods of time) should be performed with interruptions allowed. 80 # 81 # do note that this means callers may see the value of `dserver_rpc_hooks_get_interrupt_status()` on return and must handle it appropriately. 82 # this status is only returned when the RPC send operation is interrupted; when the RPC receive operation is interrupted, it is simply retried. 83 # thus, even when interruptions are allowed, callers should still see a consistent RPC state. 84 # PUSH_UNKNOWN_REPLIES 85 # the vast majority of calls should fail (spectacularly) when they receive an unexpected/unknown reply from the server. 86 # 99% of the time, this is indicative of a critical RPC error. some calls (currently only one: interrupt_enter), however, need to gracefully handle 87 # mixed up replies because of race conditions. 88 # 89 # for example, when a signal is received, interrupt_enter is called. sometimes, signals arrive while we're waiting for a reply from the server 90 # for another call. when the server receives interrupt_enter, it interrupts the current call and any reply it generates is deferred to be delivered 91 # once the server receives interrupt_exit. however, there is still a race condition here: if the server already had the reply to the interrupted 92 # call queued for delivery when the signal was received, the client will send interrupt_enter and immediately receive the reply to the interrupted 93 # call. without handling this gracefully (by saving the reply for later), RPC communication becomes desynchronized and the program crashes. 94 # 95 # note that this flag should only be used in very special circumstances (interrupt_enter currently being the only such one). 96 # not only can it mask legitimate RPC communication errors, but it also requires significantly more stack space to handle such calls, 97 # as the wrapper must create a buffer large enough to store any possible reply (including any potential descriptors). 98 # 99 # the way this works is that calls with this flag allocate enough space in the reply buffer to hold all possible replies; 100 # if they receive an unexpected reply, they push it back to the server. the server then holds on to the reply 101 # and re-sends it when appropriate (e.g. for interrupt_enter, that's after interrupt_exit is called). 102 # 103 # TODO: we should probably add a class for these calls (so it's more readable). 104 # we could even create a DSL (à-la-MIG), but that's probably overkill since 105 # we only use our RPC for darlingserver calls. 106 # 107 108 ('checkin', [ 109 ('is_fork', 'bool'), 110 ('stack_hint', 'void*', 'uint64_t'), 111 ('lifetime_listener_pipe', '@fd') 112 ], []), 113 114 ('checkout', [ 115 ('exec_listener_pipe', '@fd'), 116 ('executing_macho', 'bool'), 117 ], []), 118 119 ('vchroot_path', [ 120 ('buffer', 'char*', 'uint64_t'), 121 ('buffer_size', 'uint64_t'), 122 ], [ 123 ('length', 'uint64_t'), 124 ]), 125 126 ('kprintf', [ 127 ('string', 'const char*', 'uint64_t'), 128 ('string_length', 'uint64_t'), 129 ], []), 130 131 ('started_suspended', [], [ 132 ('suspended', 'bool'), 133 ]), 134 135 ('get_tracer', [], [ 136 ('tracer', 'int32_t'), 137 ]), 138 139 ('uidgid', [ 140 ('new_uid', 'int32_t'), 141 ('new_gid', 'int32_t'), 142 ], [ 143 ('old_uid', 'int32_t'), 144 ('old_gid', 'int32_t'), 145 ]), 146 147 ('set_thread_handles', [ 148 ('pthread_handle', 'uint64_t'), 149 ('dispatch_qaddr', 'uint64_t'), 150 ], []), 151 152 ('vchroot', [ 153 ('directory_fd', '@fd'), 154 ], []), 155 156 ('mldr_path', [ 157 ('buffer', 'char*', 'uint64_t'), 158 ('buffer_size', 'uint64_t'), 159 ], [ 160 ('length', 'uint64_t'), 161 ]), 162 163 ('fork_wait_for_child', [], [], ALLOW_INTERRUPTIONS), 164 165 ('sigprocess', [ 166 ('bsd_signal_number', 'int32_t'), 167 ('linux_signal_number', 'int32_t'), 168 ('sender_pid', 'int32_t'), 169 ('code', 'int32_t'), 170 ('signal_address', 'uint64_t'), 171 172 # these are in/out pointers 173 ('thread_state', 'uint64_t'), 174 ('float_state', 'uint64_t'), 175 ], [ 176 ('new_bsd_signal_number', 'int32_t'), 177 ]), 178 179 ('task_is_64_bit', [ 180 ('id', 'int32_t'), 181 ], [ 182 ('is_64_bit', 'bool'), 183 ]), 184 185 ('interrupt_enter', [], [], PUSH_UNKNOWN_REPLIES), 186 187 ('interrupt_exit', [], []), 188 189 ('console_open', [], [ 190 ('console', '@fd'), 191 ]), 192 193 ('set_dyld_info', [ 194 ('address', 'uint64_t'), 195 ('length', 'uint64_t'), 196 ], []), 197 198 ('stop_after_exec', [], []), 199 200 ('set_tracer', [ 201 ('target', 'int32_t'), 202 ('tracer', 'int32_t'), 203 ], []), 204 205 ('tid_for_thread', [ 206 ('thread', 'uint32_t'), 207 ], [ 208 ('tid', 'int32_t'), 209 ]), 210 211 ('ptrace_sigexc', [ 212 ('target', 'int32_t'), 213 ('enabled', 'bool'), 214 ], []), 215 216 ('ptrace_thupdate', [ 217 ('target', 'int32_t'), 218 ('signum', 'int32_t'), 219 ], []), 220 221 ('thread_suspended', [ 222 # these are in/out pointers 223 ('thread_state', 'uint64_t'), 224 ('float_state', 'uint64_t'), 225 ], []), 226 227 ('s2c_perform', [], []), 228 229 ('set_executable_path', [ 230 ('buffer', 'const char*', 'uint64_t'), 231 ('buffer_size', 'uint64_t') 232 ], []), 233 234 ('get_executable_path', [ 235 ('pid', 'int32_t'), 236 ('buffer', 'char*', 'uint64_t'), 237 ('buffer_size', 'uint64_t') 238 ], [ 239 ('length', 'uint64_t'), 240 ]), 241 242 ('groups', [ 243 ('new_groups', 'const uint32_t*', 'uint64_t'), 244 ('new_group_count', 'uint64_t'), 245 ('old_groups', 'uint32_t*', 'uint64_t'), 246 ('old_group_space', 'uint64_t'), 247 ], [ 248 ('old_group_count', 'uint64_t'), 249 ]), 250 251 # 252 # kqueue channels 253 # 254 255 ('kqchan_mach_port_open', [ 256 ('port_name', 'uint32_t'), 257 ('receive_buffer', 'void*', 'uint64_t'), 258 ('receive_buffer_size', 'uint64_t'), 259 ('saved_filter_flags', 'uint64_t'), 260 ], [ 261 ('socket', '@fd'), 262 ]), 263 264 ('kqchan_proc_open', [ 265 ('pid', 'int32_t'), 266 ('flags', 'uint32_t'), 267 ], [ 268 ('socket', '@fd'), 269 ]), 270 271 # 272 # pthread cancelation 273 # 274 275 ('pthread_kill', [ 276 ('thread_port', 'uint32_t'), 277 ('signal', 'int32_t'), 278 ], []), 279 280 ('pthread_canceled', [ 281 ('action', 'int32_t'), 282 ], []), 283 284 ('pthread_markcancel', [ 285 ('thread_port', 'uint32_t'), 286 ], []), 287 288 # 289 # Mach IPC traps 290 # 291 292 ('task_self_trap', [], [ 293 ('port_name', 'uint32_t'), 294 ]), 295 296 ('host_self_trap', [], [ 297 ('port_name', 'uint32_t'), 298 ]), 299 300 ('thread_self_trap', [], [ 301 ('port_name', 'uint32_t'), 302 ]), 303 304 ('mach_reply_port', [], [ 305 ('port_name', 'uint32_t'), 306 ]), 307 308 ('thread_get_special_reply_port', [], [ 309 ('port_name', 'uint32_t'), 310 ]), 311 312 ('mach_msg_overwrite', [ 313 ('msg', 'void*', 'uint64_t'), 314 ('option', 'int32_t'), 315 ('send_size', 'uint32_t'), 316 ('rcv_size', 'uint32_t'), 317 ('rcv_name', 'uint32_t'), 318 ('timeout', 'uint32_t'), 319 ('priority', 'uint32_t'), 320 ('rcv_msg', 'void*', 'uint64_t'), 321 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | ALLOW_INTERRUPTIONS), 322 323 ('mach_port_deallocate', [ 324 ('target', 'uint32_t'), 325 ('name', 'uint32_t'), 326 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 327 328 ('mach_port_allocate', [ 329 ('target', 'uint32_t'), 330 ('right', 'int32_t'), 331 332 # this would be better as a return parameter, 333 # but due to the way darlingserver handles Mach IPC calls, 334 # we need a pointer into the calling process's memory 335 ('name', 'uint32_t*', 'uint64_t'), 336 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 337 338 ('mach_port_mod_refs', [ 339 ('target', 'uint32_t'), 340 ('name', 'uint32_t'), 341 ('right', 'int32_t'), 342 ('delta', 'int32_t'), 343 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 344 345 ('mach_port_move_member', [ 346 ('target', 'uint32_t'), 347 ('member', 'uint32_t'), 348 ('after', 'uint32_t'), 349 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 350 351 ('mach_port_insert_right', [ 352 ('target', 'uint32_t'), 353 ('name', 'uint32_t'), 354 ('poly', 'uint32_t'), 355 ('polyPoly', 'int32_t'), 356 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 357 358 ('mach_port_insert_member', [ 359 ('target', 'uint32_t'), 360 ('name', 'uint32_t'), 361 ('pset', 'uint32_t'), 362 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 363 364 ('mach_port_extract_member', [ 365 ('target', 'uint32_t'), 366 ('name', 'uint32_t'), 367 ('pset', 'uint32_t'), 368 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 369 370 ('mach_port_construct', [ 371 ('target', 'uint32_t'), 372 ('options', 'void*', 'uint64_t'), 373 ('context', 'uint64_t'), 374 ('name', 'uint32_t*', 'uint64_t'), 375 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 376 377 ('mach_port_destruct', [ 378 ('target', 'uint32_t'), 379 ('name', 'uint32_t'), 380 ('srdelta', 'int32_t'), 381 ('guard', 'uint64_t'), 382 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 383 384 ('mach_port_guard', [ 385 ('target', 'uint32_t'), 386 ('name', 'uint32_t'), 387 ('guard', 'uint64_t'), 388 ('strict', 'bool'), 389 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 390 391 ('mach_port_unguard', [ 392 ('target', 'uint32_t'), 393 ('name', 'uint32_t'), 394 ('guard', 'uint64_t'), 395 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 396 397 ('mach_port_request_notification', [ 398 ('target', 'uint32_t'), 399 ('name', 'uint32_t'), 400 ('msgid', 'int32_t'), 401 ('sync', 'uint32_t'), 402 ('notify', 'uint32_t'), 403 ('notifyPoly', 'uint32_t'), 404 ('previous', 'uint32_t*', 'uint64_t'), 405 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 406 407 ('mach_port_get_attributes', [ 408 ('target', 'uint32_t'), 409 ('name', 'uint32_t'), 410 ('flavor', 'int32_t'), 411 ('info', 'void*', 'uint64_t'), 412 ('count', 'uint32_t*', 'uint64_t'), 413 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 414 415 ('mach_port_type', [ 416 ('target', 'uint32_t'), 417 ('name', 'uint32_t'), 418 ('ptype', 'uint32_t*', 'uint64_t'), 419 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 420 421 ('task_for_pid', [ 422 ('target_tport', 'uint32_t'), 423 ('pid', 'int32_t'), 424 ('t', 'uint32_t*', 'uint64_t'), 425 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | XNU_TRAP_NOSUFFIX), 426 427 ('task_name_for_pid', [ 428 ('target_tport', 'uint32_t'), 429 ('pid', 'int32_t'), 430 ('t', 'uint32_t*', 'uint64_t'), 431 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | XNU_TRAP_NOSUFFIX), 432 433 ('pid_for_task', [ 434 ('t', 'uint32_t'), 435 ('pid', 'int32_t*', 'uint64_t'), 436 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | XNU_TRAP_NOSUFFIX), 437 438 # 439 # Mach VM traps 440 # 441 442 ('mach_vm_allocate', [ 443 ('target', 'uint32_t'), 444 ('addr', 'uint64_t*', 'uint64_t'), 445 ('size', 'uint64_t'), 446 ('flags', 'int32_t'), 447 ], [], XNU_TRAP_CALL), 448 449 ('mach_vm_deallocate', [ 450 ('target', 'uint32_t'), 451 ('address', 'uint64_t'), 452 ('size', 'uint64_t'), 453 ], [], XNU_TRAP_CALL | XNU_TRAP_NOSUFFIX_ARGS), 454 455 # 456 # Mach semaphore traps 457 # 458 459 ('semaphore_signal', [ 460 ('signal_name', 'uint32_t'), 461 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX), 462 463 ('semaphore_signal_all', [ 464 ('signal_name', 'uint32_t'), 465 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX), 466 467 ('semaphore_wait', [ 468 ('wait_name', 'uint32_t'), 469 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | ALLOW_INTERRUPTIONS), 470 471 ('semaphore_wait_signal', [ 472 ('wait_name', 'uint32_t'), 473 ('signal_name', 'uint32_t'), 474 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | ALLOW_INTERRUPTIONS), 475 476 ('semaphore_timedwait', [ 477 ('wait_name', 'uint32_t'), 478 ('sec', 'uint32_t'), 479 ('nsec', 'uint32_t'), 480 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | ALLOW_INTERRUPTIONS), 481 482 ('semaphore_timedwait_signal', [ 483 ('wait_name', 'uint32_t'), 484 ('signal_name', 'uint32_t'), 485 ('sec', 'uint32_t'), 486 ('nsec', 'uint32_t'), 487 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX | ALLOW_INTERRUPTIONS), 488 489 # 490 # mk_timer traps 491 # 492 493 ('mk_timer_create', [], [ 494 ('port_name', 'uint32_t'), 495 ]), 496 497 ('mk_timer_destroy', [ 498 ('name', 'uint32_t'), 499 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX), 500 501 ('mk_timer_arm', [ 502 ('name', 'uint32_t'), 503 ('expire_time', 'uint64_t'), 504 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX), 505 506 ('mk_timer_cancel', [ 507 ('name', 'uint32_t'), 508 ('result_time', 'uint64_t*', 'uint64_t'), 509 ], [], XNU_TRAP_CALL | XNU_TRAP_NOPREFIX), 510 511 # 512 # psynch calls 513 # 514 515 ('psynch_cvbroad', [ 516 ('cv', 'uint64_t'), 517 ('cvlsgen', 'uint64_t'), 518 ('cvudgen', 'uint64_t'), 519 ('flags', 'uint32_t'), 520 ('mutex', 'uint64_t'), 521 ('mugen', 'uint64_t'), 522 ('tid', 'uint64_t'), 523 ], [ 524 ('retval', 'uint32_t'), 525 ], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF), 526 527 ('psynch_cvclrprepost', [ 528 ('cv', 'uint64_t'), 529 ('cvgen', 'uint32_t'), 530 ('cvugen', 'uint32_t'), 531 ('cvsgen', 'uint32_t'), 532 ('prepocnt', 'uint32_t'), 533 ('preposeq', 'uint32_t'), 534 ('flags', 'uint32_t'), 535 ], [ 536 ('retval', 'uint32_t'), 537 ], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF), 538 539 ('psynch_cvsignal', [ 540 ('cv', 'uint64_t'), 541 ('cvlsgen', 'uint64_t'), 542 ('cvugen', 'uint32_t'), 543 ('threadport', 'int32_t'), 544 ('mutex', 'uint64_t'), 545 ('mugen', 'uint64_t'), 546 ('tid', 'uint64_t'), 547 ('flags', 'uint32_t'), 548 ], [ 549 ('retval', 'uint32_t'), 550 ], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF), 551 552 ('psynch_cvwait', [ 553 ('cv', 'uint64_t'), 554 ('cvlsgen', 'uint64_t'), 555 ('cvugen', 'uint32_t'), 556 ('mutex', 'uint64_t'), 557 ('mugen', 'uint64_t'), 558 ('flags', 'uint32_t'), 559 ('sec', 'int64_t'), 560 ('nsec', 'uint32_t'), 561 ], [ 562 ('retval', 'uint32_t'), 563 ], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF | ALLOW_INTERRUPTIONS), 564 565 ('psynch_mutexdrop', [ 566 ('mutex', 'uint64_t'), 567 ('mgen', 'uint32_t'), 568 ('ugen', 'uint32_t'), 569 ('tid', 'uint64_t'), 570 ('flags', 'uint32_t'), 571 ], [ 572 ('retval', 'uint32_t'), 573 ], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF), 574 575 ('psynch_mutexwait', [ 576 ('mutex', 'uint64_t'), 577 ('mgen', 'uint32_t'), 578 ('ugen', 'uint32_t'), 579 ('tid', 'uint64_t'), 580 ('flags', 'uint32_t'), 581 ], [ 582 ('retval', 'uint32_t'), 583 ], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF | ALLOW_INTERRUPTIONS), 584 585 ('psynch_rw_rdlock', [ 586 ('rwlock', 'uint64_t'), 587 ('lgenval', 'uint32_t'), 588 ('ugenval', 'uint32_t'), 589 ('rw_wc', 'uint32_t'), 590 ('flags', 'int32_t'), 591 ], [ 592 ('retval', 'uint32_t'), 593 ], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF | ALLOW_INTERRUPTIONS), 594 595 ('psynch_rw_unlock', [ 596 ('rwlock', 'uint64_t'), 597 ('lgenval', 'uint32_t'), 598 ('ugenval', 'uint32_t'), 599 ('rw_wc', 'uint32_t'), 600 ('flags', 'int32_t'), 601 ], [ 602 ('retval', 'uint32_t'), 603 ], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF), 604 605 ('psynch_rw_wrlock', [ 606 ('rwlock', 'uint64_t'), 607 ('lgenval', 'uint32_t'), 608 ('ugenval', 'uint32_t'), 609 ('rw_wc', 'uint32_t'), 610 ('flags', 'int32_t'), 611 ], [ 612 ('retval', 'uint32_t'), 613 ], XNU_BSD_TRAP_CALL | XNU_TRAP_NO_DTAPE_DEF | ALLOW_INTERRUPTIONS), 614 615 # 616 # debug calls 617 # 618 619 ('debug_list_processes', [], [ 620 ('process_count', 'uint64_t'), 621 ('fd', '@fd'), 622 ], UNMANAGED_CALL), 623 624 ('debug_list_ports', [ 625 ('process', 'uint32_t'), 626 ], [ 627 ('port_count', 'uint64_t'), 628 ('fd', '@fd'), 629 ], UNMANAGED_CALL), 630 631 ('debug_list_members', [ 632 ('process', 'uint32_t'), 633 ('portset', 'uint32_t'), 634 ], [ 635 ('port_count', 'uint64_t'), 636 ('fd', '@fd'), 637 ], UNMANAGED_CALL), 638 639 ('debug_list_messages', [ 640 ('process', 'uint32_t'), 641 ('port', 'uint32_t'), 642 ], [ 643 ('message_count', 'uint64_t'), 644 ('fd', '@fd'), 645 ], UNMANAGED_CALL), 646 ] 647 648 ALLOWED_PRIVATE_TYPES = [ 649 'bool', 650 'int8_t', 651 'uint8_t', 652 'int16_t', 653 'uint16_t', 654 'int32_t', 655 'uint32_t', 656 'int64_t', 657 'uint64_t', 658 ] 659 660 def parse_type(param_tuple, is_public): 661 type_str = param_tuple[1].strip() 662 663 if type_str == '@fd': 664 type_str = 'int' if is_public else 'int32_t' 665 else: 666 if not is_public and len(param_tuple) > 2: 667 type_str = param_tuple[2].strip() 668 669 if not is_public and type_str not in ALLOWED_PRIVATE_TYPES: 670 raise ValueError('Invalid private type: ' + type_str) 671 672 return type_str 673 674 # we have to specify alignment for structures members greater than 4 bytes wide because on 32-bit architectures, 675 # these are 4-byte aligned, but on 64-bit architectures, these are 8-byte aligned. however, we want the same structure 676 # definitions across architectures, so we specify 8-byte alignment for 8-byte types. 677 678 def alignment_for_type(type): 679 if type == 'int64_t' or type == 'uint64_t': 680 return 8 681 else: 682 return 0 683 684 def alignment_str_for_type(type): 685 alignment = alignment_for_type(type) 686 if alignment > 0: 687 return ' __attribute__((aligned(' + str(alignment) + ')))' 688 else: 689 return '' 690 691 def is_fd(param_tuple): 692 return param_tuple[1] == '@fd' 693 694 if len(sys.argv) < 5: 695 sys.exit("Usage: " + sys.argv[0] + " <public-header-path> <internal-header-path> <library-source-path> <library-import>") 696 697 os.makedirs(os.path.dirname(sys.argv[1]), exist_ok=True) 698 os.makedirs(os.path.dirname(sys.argv[2]), exist_ok=True) 699 os.makedirs(os.path.dirname(sys.argv[3]), exist_ok=True) 700 701 def to_camel_case(snake_str): 702 components = snake_str.split('_') 703 return ''.join(x.title() for x in components) 704 705 public_header = open(sys.argv[1], "w") 706 internal_header = open(sys.argv[2], "w") 707 library_source = open(sys.argv[3], "w") 708 library_import = sys.argv[4] 709 710 license_header = """\ 711 // This file has been auto-generated by generate-rpc-wrappers.py for use with darlingserver 712 713 /** 714 * This file is part of Darling. 715 * 716 * Copyright (C) {} Darling developers 717 * 718 * Darling is free software: you can redistribute it and/or modify 719 * it under the terms of the GNU General Public License as published by 720 * the Free Software Foundation, either version 3 of the License, or 721 * (at your option) any later version. 722 * 723 * Darling is distributed in the hope that it will be useful, 724 * but WITHOUT ANY WARRANTY; without even the implied warranty of 725 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 726 * GNU General Public License for more details. 727 * 728 * You should have received a copy of the GNU General Public License 729 * along with Darling. If not, see <http://www.gnu.org/licenses/>. 730 */ 731 732 """.format(datetime.now().year) 733 734 public_header.write(license_header) 735 library_source.write(license_header) 736 internal_header.write(license_header) 737 738 public_header.write("""\ 739 #ifndef _DARLINGSERVER_API_H_ 740 #define _DARLINGSERVER_API_H_ 741 742 #include <sys/types.h> 743 #include <stdint.h> 744 #include <stdbool.h> 745 746 #ifdef __cplusplus 747 extern "C" { 748 #endif 749 750 """) 751 752 public_header.write("#define DSERVER_CALL_UNMANAGED_FLAG 0x80000000U\n\n") 753 public_header.write("enum dserver_callnum {\n") 754 # "52ccall" -> "s2c call" 755 public_header.write("\tdserver_callnum_s2c = 0x52cca11,\n") 756 public_header.write("\tdserver_callnum_push_reply = 0xbadca11,\n") 757 public_header.write("\tdserver_callnum_invalid = 0,\n") 758 idx = 1 759 for call in calls: 760 call_name = call[0] 761 call_parameters = call[1] 762 reply_parameters = call[2] 763 flags = call[3] if len(call) >= 4 else 0 764 765 debug_flag = "" 766 767 if (flags & UNMANAGED_CALL) != 0: 768 debug_flag = "DSERVER_CALL_UNMANAGED_FLAG | " 769 770 public_header.write("\tdserver_callnum_" + call_name + " = " + debug_flag + str(idx) + "U,\n") 771 772 idx += 1 773 public_header.write("};\n") 774 775 public_header.write("""\ 776 777 typedef enum dserver_callnum dserver_callnum_t; 778 779 #ifndef DSERVER_RPC_HOOKS_ARCHITECTURE 780 #define DSERVER_RPC_HOOKS_ARCHITECTURE 1 781 enum dserver_rpc_architecture { 782 dserver_rpc_architecture_invalid, 783 dserver_rpc_architecture_i386, 784 dserver_rpc_architecture_x86_64, 785 dserver_rpc_architecture_arm32, 786 dserver_rpc_architecture_arm64, 787 }; 788 789 typedef enum dserver_rpc_architecture dserver_rpc_architecture_t; 790 #endif 791 792 typedef struct dserver_rpc_callhdr { 793 dserver_callnum_t number; 794 pid_t pid; 795 pid_t tid; 796 dserver_rpc_architecture_t architecture; 797 } dserver_rpc_callhdr_t; 798 799 typedef struct dserver_rpc_replyhdr { 800 dserver_callnum_t number; 801 int code; 802 } dserver_rpc_replyhdr_t; 803 804 typedef struct dserver_rpc_call_push_reply { 805 dserver_rpc_callhdr_t header; 806 uint64_t reply; 807 uint64_t reply_size; 808 } dserver_rpc_call_push_reply_t; 809 810 // 811 // Debug calls 812 // 813 814 typedef struct dserver_debug_process { 815 uint32_t pid; 816 uint64_t port_count; 817 } dserver_debug_process_t; 818 819 typedef struct dserver_debug_port { 820 uint32_t port_name; 821 uint32_t rights; 822 uint64_t refs; 823 uint64_t messages; 824 } dserver_debug_port_t; 825 826 typedef struct dserver_debug_message { 827 uint32_t sender; 828 uint64_t size; 829 } dserver_debug_message_t; 830 831 """) 832 833 library_source.write("""\ 834 #include {} 835 #include <darlingserver/rpc-supplement.h> 836 837 #if !defined(dserver_rpc_hooks_msghdr_t) || !defined(dserver_rpc_hooks_iovec_t) || !defined(dserver_rpc_hooks_cmsghdr_t) || !defined(DSERVER_RPC_HOOKS_CMSG_SPACE) || !defined(DSERVER_RPC_HOOKS_CMSG_FIRSTHDR) || !defined(DSERVER_RPC_HOOKS_SOL_SOCKET) || !defined(DSERVER_RPC_HOOKS_SCM_RIGHTS) || !defined(DSERVER_RPC_HOOKS_CMSG_LEN) || !defined(DSERVER_RPC_HOOKS_CMSG_DATA) || !defined(DSERVER_RPC_HOOKS_ATTRIBUTE) || !defined(dserver_rpc_hooks_atomic_save_t) 838 #error Missing definitions 839 #endif 840 841 #ifndef dserver_rpc_hooks_get_pid 842 DSERVER_RPC_HOOKS_ATTRIBUTE pid_t dserver_rpc_hooks_get_pid(void); 843 #endif 844 845 #ifndef dserver_rpc_hooks_get_tid 846 DSERVER_RPC_HOOKS_ATTRIBUTE pid_t dserver_rpc_hooks_get_tid(void); 847 #endif 848 849 #ifndef dserver_rpc_hooks_get_architecture 850 DSERVER_RPC_HOOKS_ATTRIBUTE dserver_rpc_architecture_t dserver_rpc_hooks_get_architecture(void); 851 #endif 852 853 #ifndef dserver_rpc_hooks_get_server_address 854 DSERVER_RPC_HOOKS_ATTRIBUTE void* dserver_rpc_hooks_get_server_address(void); 855 #endif 856 857 #ifndef dserver_rpc_hooks_get_server_address_length 858 DSERVER_RPC_HOOKS_ATTRIBUTE size_t dserver_rpc_hooks_get_server_address_length(void); 859 #endif 860 861 #ifndef dserver_rpc_hooks_memcpy 862 DSERVER_RPC_HOOKS_ATTRIBUTE void* dserver_rpc_hooks_memcpy(void* destination, const void* source, size_t length); 863 #endif 864 865 #ifndef dserver_rpc_hooks_send_message 866 DSERVER_RPC_HOOKS_ATTRIBUTE long int dserver_rpc_hooks_send_message(int socket, const dserver_rpc_hooks_msghdr_t* message); 867 #endif 868 869 #ifndef dserver_rpc_hooks_receive_message 870 DSERVER_RPC_HOOKS_ATTRIBUTE long int dserver_rpc_hooks_receive_message(int socket, dserver_rpc_hooks_msghdr_t* out_message); 871 #endif 872 873 #ifndef dserver_rpc_hooks_get_bad_message_status 874 DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_bad_message_status(void); 875 #endif 876 877 #ifndef dserver_rpc_hooks_get_communication_error_status 878 DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_communication_error_status(void); 879 #endif 880 881 #ifndef dserver_rpc_hooks_get_broken_pipe_status 882 DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_broken_pipe_status(void); 883 #endif 884 885 #ifndef dserver_rpc_hooks_close_fd 886 DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_close_fd(int fd); 887 #endif 888 889 #ifndef dserver_rpc_hooks_get_socket 890 DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_socket(void); 891 #endif 892 893 #ifndef dserver_rpc_hooks_printf 894 DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_printf(const char* format, ...); 895 #endif 896 897 #ifndef dserver_rpc_hooks_atomic_begin 898 DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_atomic_begin(dserver_rpc_hooks_atomic_save_t* atomic_save); 899 #endif 900 901 #ifndef dserver_rpc_hooks_atomic_end 902 DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_atomic_end(dserver_rpc_hooks_atomic_save_t* atomic_save); 903 #endif 904 905 #ifndef dserver_rpc_hooks_get_interrupt_status 906 DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_interrupt_status(void); 907 #endif 908 909 #ifndef dserver_rpc_hooks_push_reply 910 DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_push_reply(int socket, const dserver_rpc_hooks_msghdr_t* message, size_t size); 911 #endif 912 913 """.format(library_import)) 914 915 internal_header.write("#define DSERVER_VALID_CALLNUM_CASES \\\n") 916 for call in calls: 917 call_name = call[0] 918 call_parameters = call[1] 919 reply_parameters = call[2] 920 921 internal_header.write("\tcase dserver_callnum_" + call_name + ": \\\n") 922 internal_header.write("\n") 923 924 internal_header.write("#define DSERVER_CONSTRUCT_CASES \\\n") 925 for call in calls: 926 call_name = call[0] 927 call_parameters = call[1] 928 reply_parameters = call[2] 929 camel_name = to_camel_case(call_name) 930 931 internal_header.write("\tCALL_CASE(" + call_name + ", " + camel_name + "); \\\n") 932 internal_header.write("\n") 933 934 internal_header.write("#define DSERVER_ENUM_VALUES \\\n") 935 for call in calls: 936 call_name = call[0] 937 call_parameters = call[1] 938 reply_parameters = call[2] 939 camel_name = to_camel_case(call_name) 940 941 internal_header.write("\t" + camel_name + " = dserver_callnum_" + call_name + ", \\\n") 942 internal_header.write("\n") 943 944 internal_header.write("#define DSERVER_CLASS_DECLS \\\n") 945 for call in calls: 946 call_name = call[0] 947 call_parameters = call[1] 948 reply_parameters = call[2] 949 camel_name = to_camel_case(call_name) 950 951 internal_header.write("\tclass " + camel_name + "; \\\n") 952 internal_header.write("\n") 953 954 internal_header.write("#define DSERVER_CLASS_DEFS \\\n") 955 for call in calls: 956 call_name = call[0] 957 call_parameters = call[1] 958 reply_parameters = call[2] 959 flags = call[3] if len(call) >= 4 else 0 960 camel_name = to_camel_case(call_name) 961 fd_count_in_reply = 0 962 963 internal_header.write(textwrap.indent(textwrap.dedent("""\ 964 class Call::{1}: public Call, public std::enable_shared_from_this<Call::{1}> {{ \\ 965 friend class Call; \\ 966 private: \\ 967 {2} 968 public: \\ 969 {1}(std::shared_ptr<Thread> thread, dserver_rpc_call_{0}_t* data, Message&& requestMessage): \\ 970 Call(thread, requestMessage.address(), reinterpret_cast<dserver_rpc_callhdr_t*>(data)){3} \\ 971 {4} 972 {{ \\ 973 """), '\t').format( 974 call_name, 975 camel_name, 976 ("dserver_call_" + call_name + "_t _body; \\") if len(call_parameters) > 0 else "\\", 977 "," if len(call_parameters) > 0 else "", 978 "_body(data->body) \\" if len(call_parameters) > 0 else "\\" 979 ) 980 ) 981 982 for param in call_parameters: 983 param_name = param[0] 984 985 if not is_fd(param): 986 continue 987 988 internal_header.write("\t\t\t_body." + param_name + " = requestMessage.extractDescriptorAtIndex(_body." + param_name + "); \\\n") 989 internal_header.write("\t\t}; \\\n") 990 991 internal_header.write("\t\t~" + camel_name + "() { \\\n") 992 for param in call_parameters: 993 param_name = param[0] 994 995 if not is_fd(param): 996 continue 997 998 internal_header.write("\t\t\tif (_body." + param_name + " != -1) { \\\n") 999 internal_header.write("\t\t\t\tclose(_body." + param_name + "); \\\n") 1000 internal_header.write("\t\t} \\\n") 1001 1002 internal_header.write(textwrap.indent(textwrap.dedent("""\ 1003 }}; \\ 1004 virtual Call::Number number() const override {{ \\ 1005 return Call::Number::{0}; \\ 1006 }}; \\ 1007 virtual void processCall() override; \\ 1008 private: \\ 1009 """), '\t').format(camel_name)) 1010 1011 internal_header.write("\t\tvoid _sendReply(int resultCode") 1012 for param in reply_parameters: 1013 param_name = param[0] 1014 1015 if is_fd(param): 1016 fd_count_in_reply += 1 1017 1018 internal_header.write(", " + parse_type(param, False) + " " + param_name) 1019 internal_header.write(") { \\\n") 1020 1021 internal_header.write("\t\t\trpcReplyLog.debug() << \"Replying to call #\" << dserver_callnum_" + call_name + " << \" (dserver_callnum_" + call_name + ") from PID \" << _header.pid << \", TID \" << _header.tid << \" with result code \" << resultCode ") 1022 1023 for param in reply_parameters: 1024 param_name = param[0] 1025 1026 internal_header.write("<< \", " + param_name + "=\" << " + param_name + " ") 1027 1028 internal_header.write("<< rpcReplyLog.endLog; \\\n") 1029 1030 internal_header.write(textwrap.indent(textwrap.dedent("""\ 1031 Message reply(sizeof(dserver_rpc_reply_{0}_t), 0); \\ 1032 int fdIndex = 0; \\ 1033 reply.setAddress(_replyAddress); \\ 1034 auto replyStruct = reinterpret_cast<dserver_rpc_reply_{0}_t*>(reply.data().data()); \\ 1035 replyStruct->header.number = dserver_callnum_{0}; \\ 1036 replyStruct->header.code = resultCode; \\ 1037 """), '\t\t\t').format(call_name)) 1038 1039 fd_index = 0 1040 for param in reply_parameters: 1041 param_name = param[0] 1042 val = param_name 1043 1044 if is_fd(param): 1045 val = "((" + param_name + " >= 0) ? (fdIndex++) : (-1))" 1046 internal_header.write("\t\t\tif (" + param_name + " >= 0) { \\\n") 1047 internal_header.write("\t\t\t\treply.pushDescriptor(" + param_name + "); \\\n") 1048 internal_header.write("\t\t\t} \\\n") 1049 1050 internal_header.write("\t\t\treplyStruct->body." + param_name + " = " + val + "; \\\n") 1051 internal_header.write("\t\t\tif (auto thread = _thread.lock()) { \\\n") 1052 internal_header.write("\t\t\t\tthread->pushCallReply(shared_from_this(), std::move(reply)); \\\n") 1053 internal_header.write("\t\t\t} else { \\\n") 1054 internal_header.write("\t\t\t\tCall::sendReply(std::move(reply)); \\\n") 1055 internal_header.write("\t\t\t} \\\n") 1056 internal_header.write("\t\t}; \\\n") 1057 1058 if len(reply_parameters) == 0: 1059 internal_header.write("\tpublic: \\\n") 1060 internal_header.write("\t\tvoid sendBasicReply(int resultCode) override { \\\n") 1061 internal_header.write("\t\t\t_sendReply(resultCode); \\\n") 1062 internal_header.write("\t\t}; \\\n") 1063 1064 if (flags & XNU_TRAP_CALL) != 0: 1065 internal_header.write("\t\tbool isXNUTrap() const override { \\\n") 1066 internal_header.write("\t\t\treturn true; \\\n") 1067 internal_header.write("\t\t}; \\\n") 1068 1069 if (flags & XNU_TRAP_BSD) != 0: 1070 internal_header.write("\t\tbool isBSDTrap() const override { \\\n") 1071 internal_header.write("\t\t\treturn true; \\\n") 1072 internal_header.write("\t\t}; \\\n") 1073 internal_header.write("\t\tvoid sendBSDReply(int resultCode, uint32_t returnValue) override { \\\n") 1074 internal_header.write("\t\t\t_sendReply(resultCode, returnValue); \\\n") 1075 internal_header.write("\t\t}; \\\n") 1076 1077 internal_header.write("\t}; \\\n") 1078 internal_header.write("\n") 1079 1080 internal_header.write("#define DSERVER_CLASS_SOURCE_DEFS \\\n") 1081 for call in calls: 1082 call_name = call[0] 1083 call_parameters = call[1] 1084 reply_parameters = call[2] 1085 flags = call[3] if len(call) >= 4 else 0 1086 camel_name = to_camel_case(call_name) 1087 1088 if (flags & XNU_TRAP_CALL) == 0: 1089 continue 1090 1091 # XNU traps return values by writing directly to the calling process's memory at the addresses given as regular call parameters 1092 if (flags & XNU_TRAP_BSD) != 0: 1093 if len(reply_parameters) != 1: 1094 raise RuntimeError("Call marked as a BSD trap does not have exactly 1 reply parameter") 1095 elif len(reply_parameters) > 0: 1096 raise RuntimeError("Call marked as an XNU trap has reply parameters") 1097 1098 internal_header.write("\tvoid DarlingServer::Call::{0}::processCall() {{ \\\n".format(camel_name)) 1099 1100 if (flags & XNU_TRAP_BSD) != 0: 1101 internal_header.write("\t\tuint32_t* retvalPointer = nullptr; \\\n") 1102 internal_header.write("\t\t{ \\\n") 1103 internal_header.write("\t\t\tif (auto thread = _thread.lock()) { \\\n") 1104 internal_header.write("\t\t\t\tretvalPointer = thread->bsdReturnValuePointer(); \\\n") 1105 internal_header.write("\t\t\t} \\\n") 1106 internal_header.write("\t\t}; \\\n") 1107 1108 internal_header.write("\t\tThread::syscallReturn(dtape_{0}(".format(call_name)) 1109 1110 is_first = True 1111 for param in call_parameters: 1112 param_name = param[0] 1113 1114 if is_first: 1115 is_first = False 1116 else: 1117 internal_header.write(", ") 1118 1119 internal_header.write("_body.{0}".format(param_name)) 1120 1121 if (flags & XNU_TRAP_BSD) != 0: 1122 if not is_first: 1123 internal_header.write(", ") 1124 internal_header.write("retvalPointer") 1125 1126 internal_header.write(")); \\\n") 1127 internal_header.write("\t}; \\\n") 1128 internal_header.write("\n") 1129 1130 internal_header.write("#define DSERVER_DTAPE_DECLS \\\n") 1131 for call in calls: 1132 call_name = call[0] 1133 call_parameters = call[1] 1134 flags = call[3] if len(call) >= 4 else 0 1135 camel_name = to_camel_case(call_name) 1136 1137 if (flags & XNU_TRAP_CALL) == 0: 1138 continue 1139 1140 internal_header.write("\tint dtape_{0}(".format(call_name)) 1141 1142 is_first = True 1143 for param in call_parameters: 1144 param_name = param[0] 1145 1146 if is_first: 1147 is_first = False 1148 else: 1149 internal_header.write(", ") 1150 1151 internal_header.write("{0} {1}".format(parse_type(param, False), param_name)) 1152 1153 if (flags & XNU_TRAP_BSD) != 0: 1154 if not is_first: 1155 internal_header.write(", ") 1156 internal_header.write("uint32_t* retval") 1157 1158 internal_header.write("); \\\n") 1159 internal_header.write("\n") 1160 1161 internal_header.write("#define DSERVER_DTAPE_DEFS \\\n") 1162 for call in calls: 1163 call_name = call[0] 1164 call_parameters = call[1] 1165 flags = call[3] if len(call) >= 4 else 0 1166 camel_name = to_camel_case(call_name) 1167 1168 if (flags & XNU_TRAP_CALL) == 0 or (flags & XNU_TRAP_NO_DTAPE_DEF) != 0: 1169 continue 1170 1171 trap_name = call_name 1172 if (flags & XNU_TRAP_NOPREFIX) == 0: 1173 trap_name = "_kernelrpc_" + trap_name 1174 if (flags & XNU_TRAP_NOSUFFIX) == 0: 1175 trap_name = trap_name + "_trap" 1176 1177 trap_args_name = call_name 1178 if (flags & XNU_TRAP_NOPREFIX) == 0: 1179 trap_args_name = "_kernelrpc_" + trap_args_name 1180 if (flags & (XNU_TRAP_NOSUFFIX | XNU_TRAP_NOSUFFIX_ARGS)) == 0: 1181 trap_args_name = trap_args_name + "_trap" 1182 1183 internal_header.write("\tint dtape_{0}(".format(call_name)) 1184 1185 is_first = True 1186 for param in call_parameters: 1187 param_name = param[0] 1188 1189 if is_first: 1190 is_first = False 1191 else: 1192 internal_header.write(", ") 1193 1194 internal_header.write("{0} {1}".format(parse_type(param, False), param_name)) 1195 1196 internal_header.write(") { \\\n") 1197 internal_header.write("\t\tstruct {0}_args args = {{ \\\n".format(trap_args_name)) 1198 1199 for param in call_parameters: 1200 param_name = param[0] 1201 internal_header.write("\t\t\t.{0} = {0}, \\\n".format(param_name)) 1202 1203 internal_header.write("\t\t}; \\\n") 1204 internal_header.write("\t\treturn {0}(&args); \\\n".format(trap_name)) 1205 internal_header.write("\t}; \\\n") 1206 internal_header.write("\n") 1207 1208 public_header.write("__attribute__((always_inline)) static const char* dserver_callnum_to_string(dserver_callnum_t callnum) {\n") 1209 public_header.write("\tswitch (callnum) {\n") 1210 public_header.write("\t\tcase dserver_callnum_s2c: return \"dserver_callnum_s2c\";\n") 1211 public_header.write("\t\tcase dserver_callnum_push_reply: return \"dserver_callnum_push_reply\";\n") 1212 for call in calls: 1213 call_name = call[0] 1214 public_header.write("\t\tcase dserver_callnum_" + call_name + ": return \"dserver_callnum_" + call_name + "\";\n") 1215 public_header.write("\t\tdefault: return (const char*)0;\n") 1216 public_header.write("\t}\n") 1217 public_header.write("};\n\n") 1218 1219 max_call_fd_count = 0 1220 max_reply_fd_count = 0 1221 for call in calls: 1222 call_name = call[0] 1223 call_parameters = call[1] 1224 reply_parameters = call[2] 1225 fd_count_in_call = 0 1226 fd_count_in_reply = 0 1227 flags = call[3] if len(call) >= 4 else 0 1228 for param in call_parameters: 1229 if is_fd(param): 1230 fd_count_in_call += 1 1231 for param in reply_parameters: 1232 if is_fd(param): 1233 fd_count_in_reply += 1 1234 if fd_count_in_call > max_call_fd_count: 1235 max_call_fd_count = fd_count_in_call 1236 if fd_count_in_reply > max_reply_fd_count: 1237 max_reply_fd_count = fd_count_in_reply 1238 1239 for call in calls: 1240 call_name = call[0] 1241 call_parameters = call[1] 1242 reply_parameters = call[2] 1243 fd_count_in_call = 0 1244 fd_count_in_reply = 0 1245 flags = call[3] if len(call) >= 4 else 0 1246 1247 # define the RPC call body structure 1248 if len(call_parameters) > 0: 1249 public_header.write("typedef struct dserver_call_" + call_name + " dserver_call_" + call_name + "_t;\n") 1250 public_header.write("struct dserver_call_" + call_name + " {\n") 1251 for param in call_parameters: 1252 param_name = param[0] 1253 1254 if is_fd(param): 1255 fd_count_in_call += 1 1256 1257 parsed_type = parse_type(param, False) 1258 public_header.write("\t" + parsed_type + " " + param_name + alignment_str_for_type(parsed_type) + ";\n") 1259 public_header.write("};\n") 1260 1261 # define the RPC call structure 1262 public_header.write(textwrap.dedent("""\ 1263 typedef struct dserver_rpc_call_{0} dserver_rpc_call_{0}_t; 1264 struct dserver_rpc_call_{0} {{ 1265 dserver_rpc_callhdr_t header; 1266 """).format(call_name)) 1267 if len(call_parameters) > 0: 1268 public_header.write("\tdserver_call_" + call_name + "_t body;\n") 1269 public_header.write("};\n") 1270 1271 # define the RPC reply body structure 1272 if len(reply_parameters) > 0: 1273 public_header.write("typedef struct dserver_reply_" + call_name + " dserver_reply_" + call_name + "_t;\n") 1274 public_header.write("struct dserver_reply_" + call_name + " {\n") 1275 for param in reply_parameters: 1276 param_name = param[0] 1277 1278 if is_fd(param): 1279 fd_count_in_reply += 1 1280 1281 parsed_type = parse_type(param, False) 1282 public_header.write("\t" + parsed_type + " " + param_name + alignment_str_for_type(parsed_type) + ";\n") 1283 public_header.write("};\n") 1284 1285 # define the RPC reply structure 1286 public_header.write(textwrap.dedent("""\ 1287 typedef struct dserver_rpc_reply_{0} dserver_rpc_reply_{0}_t; 1288 struct dserver_rpc_reply_{0} {{ 1289 dserver_rpc_replyhdr_t header; 1290 """).format(call_name)) 1291 if len(reply_parameters) > 0: 1292 public_header.write("\tdserver_reply_" + call_name + "_t body;\n") 1293 public_header.write("};\n") 1294 1295 tmp = "int dserver_rpc_explicit_" + call_name + "(int server_socket" 1296 public_header.write(tmp) 1297 library_source.write(tmp) 1298 for param in call_parameters: 1299 param_name = param[0] 1300 1301 tmp = ", " 1302 tmp += parse_type(param, True) + " " + param_name 1303 public_header.write(tmp) 1304 library_source.write(tmp) 1305 1306 for param in reply_parameters: 1307 param_name = param[0] 1308 1309 tmp = ", " 1310 tmp += parse_type(param, True) + "* out_" + param_name 1311 public_header.write(tmp) 1312 library_source.write(tmp) 1313 public_header.write(");\n\n") 1314 library_source.write(") {\n") 1315 1316 # define the RPC call wrapper function 1317 library_source.write(textwrap.indent(textwrap.dedent("""\ 1318 dserver_rpc_call_{0}_t call = {{ 1319 .header = {{ 1320 .architecture = dserver_rpc_hooks_get_architecture(), 1321 .pid = dserver_rpc_hooks_get_pid(), 1322 .tid = dserver_rpc_hooks_get_tid(), 1323 .number = dserver_callnum_{0}, 1324 }}, 1325 """), '\t').format(call_name)) 1326 1327 if len(call_parameters) > 0: 1328 library_source.write("\t\t.body = {\n") 1329 fd_index = 0 1330 for param in call_parameters: 1331 param_name = param[0] 1332 val = param_name 1333 1334 if is_fd(param): 1335 val = "(" + param_name + " < 0) ? -1 : " + str(fd_index) 1336 fd_index += 1 1337 1338 # On recent versions of clang, the compiler will throw out an error if there is 1339 # a mismatch with certain integer types. 1340 if len(param) > 2: 1341 val = "(" + param[2] + ")" + val 1342 1343 library_source.write("\t\t\t." + param_name + " = " + val + ",\n") 1344 library_source.write("\t\t},\n") 1345 1346 library_source.write("\t};\n") 1347 library_source.write("\tunion {\n") 1348 library_source.write("\t\tdserver_rpc_reply_" + call_name + "_t reply;\n") 1349 library_source.write("\t\tdserver_s2c_call_t s2c;\n") 1350 1351 # make room for any potetial replies if we need to handle unexpected replies 1352 if (flags & PUSH_UNKNOWN_REPLIES) != 0: 1353 for other_call in calls: 1354 other_call_name = other_call[0] 1355 other_call_parameters = other_call[1] 1356 other_reply_parameters = other_call[2] 1357 other_fd_count_in_reply = 0 1358 other_flags = other_call[3] if len(other_call) >= 4 else 0 1359 library_source.write("\t\tdserver_rpc_reply_" + other_call_name + "_t potential_reply_" + other_call_name + ";\n") 1360 1361 library_source.write("\t} reply_msg;\n") 1362 1363 # we always allocate space for at least one FD, since S2C calls might send a descriptor 1364 library_source.write("\tint fds[" + str(max(1, fd_count_in_call, fd_count_in_reply, max_reply_fd_count if (flags & PUSH_UNKNOWN_REPLIES) != 0 else 0)) + "];\n") 1365 library_source.write("\tint valid_fd_count;\n") 1366 library_source.write("\tchar controlbuf[DSERVER_RPC_HOOKS_CMSG_SPACE(sizeof(fds))];\n") 1367 1368 if fd_count_in_call > 0: 1369 library_source.write("\tvalid_fd_count = 0;\n") 1370 for param in call_parameters: 1371 param_name = param[0] 1372 1373 if not is_fd(param): 1374 continue 1375 1376 library_source.write("\tif (" + param_name + " >= 0) {\n") 1377 library_source.write("\t\tfds[valid_fd_count++] = " + param_name + ";\n") 1378 library_source.write("\t}\n") 1379 1380 library_source.write(textwrap.indent(textwrap.dedent("""\ 1381 dserver_rpc_hooks_iovec_t call_data = { 1382 .iov_base = &call, 1383 .iov_len = sizeof(call), 1384 }; 1385 dserver_rpc_hooks_msghdr_t callmsg = { 1386 .msg_name = dserver_rpc_hooks_get_server_address(), 1387 .msg_namelen = dserver_rpc_hooks_get_server_address_length(), 1388 .msg_iov = &call_data, 1389 .msg_iovlen = 1, 1390 """), '\t')) 1391 1392 if fd_count_in_call == 0: 1393 library_source.write("\t\t.msg_control = NULL,\n") 1394 library_source.write("\t\t.msg_controllen = 0,\n") 1395 else: 1396 library_source.write("\t\t.msg_control = (valid_fd_count > 0) ? controlbuf : NULL,\n") 1397 library_source.write("\t\t.msg_controllen = (valid_fd_count > 0) ? sizeof(controlbuf) : 0,\n") 1398 1399 library_source.write("\t};\n") 1400 1401 if fd_count_in_call > 0: 1402 library_source.write(textwrap.indent(textwrap.dedent("""\ 1403 if (valid_fd_count > 0) { 1404 dserver_rpc_hooks_cmsghdr_t* call_cmsg = DSERVER_RPC_HOOKS_CMSG_FIRSTHDR(&callmsg); 1405 call_cmsg->cmsg_level = DSERVER_RPC_HOOKS_SOL_SOCKET; 1406 call_cmsg->cmsg_type = DSERVER_RPC_HOOKS_SCM_RIGHTS; 1407 call_cmsg->cmsg_len = DSERVER_RPC_HOOKS_CMSG_LEN(sizeof(int) * valid_fd_count); 1408 dserver_rpc_hooks_memcpy(DSERVER_RPC_HOOKS_CMSG_DATA(call_cmsg), fds, sizeof(int) * valid_fd_count); 1409 } 1410 """), '\t')) 1411 1412 library_source.write(textwrap.indent(textwrap.dedent("""\ 1413 dserver_rpc_hooks_iovec_t reply_data = { 1414 .iov_base = &reply_msg, 1415 .iov_len = sizeof(reply_msg), 1416 }; 1417 dserver_rpc_hooks_msghdr_t replymsg = { 1418 .msg_name = NULL, 1419 .msg_namelen = 0, 1420 .msg_iov = &reply_data, 1421 .msg_iovlen = 1, 1422 .msg_control = controlbuf, 1423 .msg_controllen = sizeof(controlbuf), 1424 """), '\t')) 1425 1426 library_source.write("\t};\n\n") 1427 1428 if (flags & ALLOW_INTERRUPTIONS) == 0: 1429 library_source.write("\tdserver_rpc_hooks_atomic_save_t atomic_save;\n") 1430 library_source.write("\tdserver_rpc_hooks_atomic_begin(&atomic_save);\n\n") 1431 1432 library_source.write("\tlong int long_status;\n\n") 1433 1434 library_source.write("retry_send:\n") 1435 library_source.write("\tlong_status = dserver_rpc_hooks_send_message(server_socket, &callmsg);\n\n") 1436 1437 library_source.write("\tif (long_status == dserver_rpc_hooks_get_interrupt_status()) {\n") 1438 if (flags & ALLOW_INTERRUPTIONS) == 0: 1439 library_source.write("\t\tgoto retry_send;\n") 1440 else: 1441 library_source.write("\t\treturn (int)long_status;\n") 1442 library_source.write("\t}\n\n") 1443 1444 library_source.write("\tif (long_status < 0) {\n") 1445 if (flags & ALLOW_INTERRUPTIONS) == 0: 1446 library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n") 1447 library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD SEND STATUS: %ld ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, long_status);\n") 1448 library_source.write("\t\treturn (int)long_status;\n") 1449 library_source.write("\t}\n\n") 1450 1451 library_source.write("\tif (long_status != sizeof(call)) {\n") 1452 if (flags & ALLOW_INTERRUPTIONS) == 0: 1453 library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n") 1454 library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD SEND LENGTH: %ld (expected %zu) ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, long_status, sizeof(call));\n") 1455 library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n") 1456 library_source.write("\t}\n\n") 1457 1458 library_source.write("retry_receive:\n") 1459 library_source.write("\tlong_status = dserver_rpc_hooks_receive_message(server_socket, &replymsg);\n\n") 1460 1461 library_source.write("\tif (long_status == dserver_rpc_hooks_get_interrupt_status()) {\n") 1462 library_source.write("\t\tgoto retry_receive;\n") 1463 library_source.write("\t}\n\n") 1464 1465 library_source.write("\tif (long_status < 0) {\n") 1466 if (flags & ALLOW_INTERRUPTIONS) == 0: 1467 library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n") 1468 library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD RECEIVE STATUS: %ld ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, long_status);\n") 1469 library_source.write("\t\treturn (int)long_status;\n") 1470 library_source.write("\t}\n\n") 1471 1472 library_source.write("\tif (long_status < sizeof(dserver_rpc_replyhdr_t)) {\n") 1473 if (flags & ALLOW_INTERRUPTIONS) == 0: 1474 library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n") 1475 library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD RECEIVE MESSAGE: length=%ld (expected %zu) ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, long_status, sizeof(reply_msg.reply));\n") 1476 library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n") 1477 library_source.write("\t}\n\n") 1478 1479 library_source.write("\tif (reply_msg.reply.header.number != dserver_callnum_" + call_name + ") {\n") 1480 if (flags & PUSH_UNKNOWN_REPLIES) != 0: 1481 library_source.write("\t\tdserver_rpc_hooks_push_reply(server_socket, &replymsg, long_status);\n") 1482 library_source.write("\t\tgoto retry_receive;\n") 1483 else: 1484 if (flags & ALLOW_INTERRUPTIONS) == 0: 1485 library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n") 1486 library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD RECEIVE MESSAGE: number=%d (expected %d), code=%d, length=%ld (expected %zu) ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, reply_msg.reply.header.number, dserver_callnum_" + call_name + ", reply_msg.reply.header.code, long_status, sizeof(reply_msg.reply));\n") 1487 library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n") 1488 library_source.write("\t}\n\n") 1489 1490 library_source.write("\tif (long_status != sizeof(reply_msg.reply)) {\n") 1491 if (flags & ALLOW_INTERRUPTIONS) == 0: 1492 library_source.write("\t\tdserver_rpc_hooks_atomic_end(&atomic_save);\n") 1493 library_source.write("\t\tdserver_rpc_hooks_printf(\"*** %d:%d: %s: BAD RECEIVE MESSAGE: number=%d (expected %d), code=%d, length=%ld (expected %zu) ***\\n\", dserver_rpc_hooks_get_pid(), dserver_rpc_hooks_get_tid(), __func__, reply_msg.reply.header.number, dserver_callnum_" + call_name + ", reply_msg.reply.header.code, long_status, sizeof(reply_msg.reply));\n") 1494 library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n") 1495 library_source.write("\t}\n\n") 1496 1497 if (flags & ALLOW_INTERRUPTIONS) == 0: 1498 library_source.write("\tdserver_rpc_hooks_atomic_end(&atomic_save);\n\n") 1499 1500 if fd_count_in_reply != 0: 1501 library_source.write("\tvalid_fd_count = 0;\n") 1502 for param in reply_parameters: 1503 param_name = param[0] 1504 1505 if not is_fd(param): 1506 continue 1507 1508 library_source.write("\tif (reply_msg.reply.body." + param_name + " >= 0) {\n") 1509 library_source.write("\t\t++valid_fd_count;\n") 1510 library_source.write("\t}\n") 1511 1512 library_source.write(textwrap.indent(textwrap.dedent("""\ 1513 if (valid_fd_count > 0) { 1514 dserver_rpc_hooks_cmsghdr_t* reply_cmsg = DSERVER_RPC_HOOKS_CMSG_FIRSTHDR(&replymsg); 1515 if (!reply_cmsg || reply_cmsg->cmsg_level != DSERVER_RPC_HOOKS_SOL_SOCKET || reply_cmsg->cmsg_type != DSERVER_RPC_HOOKS_SCM_RIGHTS || reply_cmsg->cmsg_len != DSERVER_RPC_HOOKS_CMSG_LEN(sizeof(int) * valid_fd_count)) { 1516 return dserver_rpc_hooks_get_bad_message_status(); 1517 } 1518 dserver_rpc_hooks_memcpy(fds, DSERVER_RPC_HOOKS_CMSG_DATA(reply_cmsg), sizeof(int) * valid_fd_count); 1519 } 1520 """), '\t')) 1521 1522 for param in reply_parameters: 1523 param_name = param[0] 1524 1525 if is_fd(param): 1526 library_source.write("\tif (out_" + param_name + ") {\n") 1527 library_source.write("\t\t*out_" + param_name + " = (reply_msg.reply.body." + param_name + " >= 0) ? fds[reply_msg.reply.body." + param_name + "] : -1;\n") 1528 library_source.write("\t} else if (reply_msg.reply.body." + param_name + " >= 0) {\n") 1529 library_source.write("\t\tdserver_rpc_hooks_close_fd(fds[reply_msg.reply.body." + param_name + "]);\n") 1530 library_source.write("\t}\n") 1531 else: 1532 library_source.write("\tif (out_" + param_name + ") {\n") 1533 library_source.write("\t\t*out_" + param_name + " = reply_msg.reply.body." + param_name + ";\n") 1534 library_source.write("\t}\n") 1535 1536 library_source.write("\treturn reply_msg.reply.header.code;\n") 1537 1538 library_source.write("};\n\n") 1539 1540 # declare the RPC call wrapper function 1541 # (and output the prototype part of the function definition) 1542 tmp = "int dserver_rpc_" + call_name + "(" 1543 public_header.write(tmp) 1544 library_source.write(tmp) 1545 is_first = True 1546 for param in call_parameters: 1547 param_name = param[0] 1548 1549 if is_first: 1550 is_first = False 1551 tmp = "" 1552 else: 1553 tmp = ", " 1554 tmp += parse_type(param, True) + " " + param_name 1555 public_header.write(tmp) 1556 library_source.write(tmp) 1557 1558 for param in reply_parameters: 1559 param_name = param[0] 1560 1561 if is_first: 1562 is_first = False 1563 tmp = "" 1564 else: 1565 tmp = ", " 1566 tmp += parse_type(param, True) + "* out_" + param_name 1567 public_header.write(tmp) 1568 library_source.write(tmp) 1569 public_header.write(");\n\n") 1570 library_source.write(") {\n") 1571 1572 library_source.write("\tint server_socket = dserver_rpc_hooks_get_socket();\n") 1573 library_source.write("\tif (server_socket < 0) {\n") 1574 library_source.write("\t\treturn dserver_rpc_hooks_get_broken_pipe_status();\n") 1575 library_source.write("\t}\n\n") 1576 1577 library_source.write("\treturn dserver_rpc_explicit_" + call_name + "(server_socket") 1578 1579 for param in call_parameters: 1580 param_name = param[0] 1581 1582 tmp = ", " 1583 tmp += param_name 1584 library_source.write(tmp) 1585 1586 for param in reply_parameters: 1587 param_name = param[0] 1588 1589 tmp = ", " 1590 tmp += "out_" + param_name 1591 library_source.write(tmp) 1592 1593 library_source.write(");\n") 1594 1595 library_source.write("};\n\n") 1596 1597 public_header.write("// we don't care about multiple evaluation here\n") 1598 public_header.write("#define dserver_rpc_helper_max(a, b) (((b) > (a)) ? (b) : (a))\n\n") 1599 1600 curr_call_len_str = "0" 1601 curr_reply_len_str = "0" 1602 for call in calls: 1603 call_name = call[0] 1604 call_parameters = call[1] 1605 reply_parameters = call[2] 1606 flags = call[3] if len(call) >= 4 else 0 1607 curr_call_len_str = "(dserver_rpc_helper_max(sizeof(dserver_rpc_call_" + call_name + "_t), " + curr_call_len_str + "))" 1608 curr_reply_len_str = "(dserver_rpc_helper_max(sizeof(dserver_rpc_reply_" + call_name + "_t), " + curr_reply_len_str + "))" 1609 1610 public_header.write("#define DSERVER_RPC_CALL_MAX_LENGTH " + curr_call_len_str + "\n") 1611 public_header.write("#define DSERVER_RPC_REPLY_MAX_LENGTH " + curr_reply_len_str + "\n") 1612 public_header.write("#define DSERVER_RPC_CALL_MAX_FD_COUNT " + str(max_call_fd_count) + "\n") 1613 public_header.write("#define DSERVER_RPC_REPLY_MAX_FD_COUNT " + str(max_reply_fd_count) + "\n\n") 1614 1615 public_header.write("""\ 1616 #ifdef __cplusplus 1617 }; 1618 #endif 1619 1620 #endif // _DARLINGSERVER_API_H_ 1621 """) 1622 1623 public_header.close() 1624 internal_header.close() 1625 library_source.close()