Full Code of sbingner/substitute for AI

master 211873b3c184
115 files
751.3 KB
225.1k tokens
Showing preview only (787K chars total). The displayed content is truncated. Use the JSON API for full output.
Repository: sbingner/substitute
Branch: master
Commit: 211873b3c184
Files: 115
Total size: 751.3 KB

Directory structure:
gitextract_f5flmijb/

├── .gitignore
├── LICENSE.txt
├── README.md
├── configure
├── darwin-bootstrap/
│   ├── DEBIAN/
│   │   ├── control
│   │   ├── extrainst_
│   │   └── postrm
│   ├── bundle-loader.c
│   ├── com.ex.substituted.plist
│   ├── ib-log.h
│   ├── inject-into-launchd.c
│   ├── posixspawn-hook.c
│   ├── safemode-ui-hook.m
│   ├── safemode-ui-hook.plist
│   ├── safety-dance/
│   │   ├── AutoGrid.h
│   │   ├── AutoGrid.m
│   │   ├── Info.plist
│   │   └── main.m
│   ├── substituted-plist-loader.h
│   ├── substituted.m
│   └── unrestrict.c
├── doc/
│   └── installed-README.txt
├── ent.plist
├── generated/
│   ├── darwin-inject-asm.S
│   ├── generic-dis-arm.inc.h
│   ├── generic-dis-arm64.inc.h
│   ├── generic-dis-thumb.inc.h
│   ├── generic-dis-thumb2.inc.h
│   └── manual-mach.inc.h
├── lib/
│   ├── arm/
│   │   ├── arch-dis.h
│   │   ├── arch-transform-dis.inc.h
│   │   ├── assemble.h
│   │   ├── dis-arm.inc.h
│   │   ├── dis-main.inc.h
│   │   ├── dis-thumb.inc.h
│   │   ├── dis-thumb2.inc.h
│   │   ├── jump-patch.h
│   │   └── misc.h
│   ├── arm64/
│   │   ├── arch-dis.h
│   │   ├── arch-transform-dis.inc.h
│   │   ├── assemble.h
│   │   ├── dis-main.inc.h
│   │   ├── jump-patch.h
│   │   └── misc.h
│   ├── cbit/
│   │   ├── cqueue.c
│   │   ├── cqueue.h
│   │   ├── htab.h
│   │   ├── misc.h
│   │   ├── vec.c
│   │   └── vec.h
│   ├── darwin/
│   │   ├── execmem.c
│   │   ├── find-syms.c
│   │   ├── inject-asm-raw.c
│   │   ├── inject-asm-raw.order
│   │   ├── inject.c
│   │   ├── interpose.c
│   │   ├── mach-decls.h
│   │   ├── manual-syscall.h
│   │   ├── objc-asm.S
│   │   ├── objc.c
│   │   ├── objc.h
│   │   ├── read.c
│   │   ├── read.h
│   │   ├── substrate-compat.c
│   │   └── xxpc.h
│   ├── dis.h
│   ├── execmem.h
│   ├── hook-functions.c
│   ├── jump-dis.c
│   ├── jump-dis.h
│   ├── ptrauth_helpers.h
│   ├── strerror.c
│   ├── substitute-internal.h
│   ├── substitute.h
│   ├── transform-dis.c
│   ├── transform-dis.h
│   └── x86/
│       ├── arch-dis.h
│       ├── arch-transform-dis.inc.h
│       ├── dis-main.inc.h
│       ├── jump-patch.h
│       └── misc.h
├── script/
│   ├── gen-deb.sh
│   ├── gen-inject-asm.sh
│   ├── gen-manual-mach.py
│   ├── mconfig.py
│   └── test-transform-dis.sh
├── substrate/
│   ├── lgpl-3.0.tar.xz
│   └── substrate.h
├── substrate-shim.c
├── test/
│   ├── injected-test-dylib.c
│   ├── insns-arm.S
│   ├── insns-libz-arm.S
│   ├── lol.c
│   ├── test-dis.c
│   ├── test-execmem.c
│   ├── test-find-syms.c
│   ├── test-hook-functions.c
│   ├── test-htab.c
│   ├── test-imp-forwarding.m
│   ├── test-inject.c
│   ├── test-interpose.c
│   ├── test-jump-dis.c
│   ├── test-objc-hook.m
│   ├── test-pc-patch.c
│   ├── test-posixspawn-hook.c
│   ├── test-substrate.cpp
│   ├── test-td-simple.c
│   ├── test-transform-dis.c
│   ├── test-vec.cpp
│   ├── transform-dis-cases-arm.S
│   ├── transform-dis-cases-arm64.S
│   ├── transform-dis-cases-i386.S
│   ├── transform-dis-cases-x86_64.S
│   └── transform-dis-cases.h
└── vendor/
    └── dyld_cache_format.h

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.swp
out
Makefile
config.log
config.status


================================================
FILE: LICENSE.txt
================================================
Some files in this repository contain their own licensing info in a header:
substrate.h, which is based on an older version of CydiaSubstrate, is under
LGPLv3, and substitute.h and the generated files are in the public domain.  (So
if you want to take advantage of the more lax permission below, you can't use
the Substrate compatibility layer; also, the Debian package includes the
obligatory copy of the (L)GPLv3.)

For all other files:

Copyright Nicholas Allegra (comex)

This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option) any
later version.

** You may optionally consider the license amended with the following special
exception: When statically linking, it is not necessary to include materials
specifically for relinking with a modified version of the library (i.e.
subsection 6a of the LGPLv2.1, or subsection 4d0 of version 3.0).

This library is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
details.

You should not have received a copy of the GNU Lesser General Public License
along with this library, as it can easily be found on the World Wide Web.  If,
due to a temporal anomaly, you are trapped in some time period after 1999 (the
release date of the LGPL v2.1) but before 1991 (the debut of the Web), you may
get a copy by writing to the Free Software Foundation, Inc., 51 Franklin
Street, Fifth Floor, Boston, MA  02110-1301  USA.  For all other users, not
having to distribute all 5,000 words of the thing may be considered another
optional license exception.


================================================
FILE: README.md
================================================
(lib)substitute
---------------

Substitute is a system for modifying code at runtime by substituting custom
implementations for arbitrary functions and Objective-C methods.  It is also a
Free Software substitute for [Cydia Substrate](http://www.cydiasubstrate.com).
It currently has full support for iOS and limited support for OS X; in the
(hopefully near) future I will port it more widely.

License: LGPLv2.1+ with optional extra permissiveness; see LICENSE.txt

Current status
==============

Alpha.  Currently only build tested on Mac, targeting Mac and iOS.

To compile for iOS:

    ./configure --xcode-sdk=iphoneos --xcode-archs=arm64 && make -j8 && ./script/gen-deb.sh

You may want to turn off IB_VERBOSE in darwin-bootstrap/ib-log.h, which
currently spams a lot of files to /tmp and spams the syslog.  I will turn it
off by default soon.

To compile for Mac (does not support bootstrapping/bundle loading/etc., only
direct usage as a library):

    ./configure && make -j8

In other situations, `./configure --help` should be informative.  I'm using a
build system I wrote from scratch, intended to be extensible for many use cases
rather than project specific, and therefore somewhat complex; it's currently
rather rough around the edges.  Please let me know about any problems with it.

Known issues (will be fixed soon):
    - launchd will sometimes crash when injecting Substrate while Substitute is
      already loaded.  [not sure whether this is still an issue]
    - White-on-white status bar (I think) in SafetyDance.
    - Each dylib is >100kb due mostly to zero padding (and fat binaries).  This
      is easily fixed by adding FS compression, which I need to do in the deb.

How to use on iOS:

    Extensions should be placed in /Library/Substitute/DynamicLibraries, with
    the same layout as Substrate.  If you want to quickly test whether an
    existing Substrate extension works with Substitute, you can run

    install_name_tool -change \
      /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate \
      /usr/lib/libsubstitute.0.dylib \
      extension.dylib

    and move it to the new directory.

Substitute compared to Substrate
================================
* `+` Free software, so you can actually use it somewhere other than iOS or
      Android, e.g. by bundling whatever parts of it you need with your app.
      See below for more on this.
* `+` More sophisticated, partially automatically generated disassemblers,
      which handle a larger portion of the space of possible PC-relative
      instructions that might be found in a patch target function - though I'm
      not sure how likely this is to help in practice.\*
* `+` Identifies if a function is too short to patch.
* `+` An extra disassembly step goes through the rest of the function to
      optimistically identify jumps back to the patched region, which are
      possible in rare cases; these can't currently be fixed up, but an
      appropriate error code is returned.
* `+` API returns error codes.
* `+` Some more functionality - interposing...
* `+` cross-platform support will be high priority soon(tm)
* `?` C, not C++
* `-` not yet stable
* `-` bigger binary size (because of the disassemblers)
* `-` Android will never be supported

\* Both libraries work by overwriting a few instructions at the beginning of
such a function; to allow the substitute function to call back into the
original, they first copy those instructions elsewhere, and append a jump to
the unmodified instructions after the patch site constituting the rest of the
function.  However, some instructions depend on the current PC and will break
if run from an unexpected location, so both libraries disassemble the moved
instructions and fix up the most common such sequences.  The benefit to this
library's approach is mostly on 32-bit ARM/Thumb; ARM64 only has a few
instructions that use PC, and Substrate already borrows a full x86 disassembler
library.

Todo list (approx. priority order)
==================================
- iOS: safe mode
- iOS: ensure re-patching launchd (for upgrades) works
- iOS: install without reboot
- On-the-fly hooking and unhooking support:
    - support for optimistically trying to unhook, in the hope that no further modifications were made to that memory
    - some API to load/unload from all existing processes
- Linux



================================================
FILE: configure
================================================
#!/usr/bin/env python
import sys, os, glob
sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'script'))
import mconfig
settings = mconfig.settings_root
settings.package_unix_name.value = 'substitute'

c = settings.host_machine().c_tools()
c.cc.required()
c.dsymutil.required()
settings.host_machine().will_need_darwin_target_conditionals()

settings.add_setting_option('imaon2', '--with-imaon2', 'path to imaon2 (optional)', '')
settings.add_setting_option('gen_dia', '--enable-recompile-dia', 'generate darwin-inject-asm.S', False, bool=True)
settings.add_setting_option('enable_tests', '--enable-tests', 'tests!', False, bool=True)
settings.add_setting_option('enable_ios_bootstrap', '--enable-ios-bootstrap', 'default: true if targeting iOS',
    lambda: settings.host_machine().is_ios(),
    bool=True)
settings.add_setting_option('install_name', '--install-name', 'LC_ID_DYLIB',
    lambda: ['/usr/local/lib/libsubstitute.0.dylib', '/usr/lib/libsubstitute.0.dylib'][settings.host_machine().is_ios()])

ldid_tool = mconfig.CLITool('ldid', ['ldid'], 'LDID', settings.host_machine(), settings.host_machine().toolchains())
ldid_tool.optional_nocheck()

asm_archs = [
    ('x86_64', []),
    ('i386', []),
    ('arm', ['-marm']),
    ('arm64', []), # XXX
]

asm_cflags = ['-Os', '-fno-stack-protector', '-isysroot', '/dev/null', '-fPIC']
asm_ldflags = ['-nostartfiles', '-nodefaultlibs']
machs = []
for name, cflags in asm_archs:
    mach = mconfig.Machine('asm-' + name, settings, 'for cross-compiling the inject baton', name + '-apple-darwin10')
    machs.append(mach)
    cc = mach.c_tools().cc
    cc.optional()
    def f():
        if settings.gen_dia:
            cc.argv()
            mach_settings = settings[mach.name]
            mach_settings.cflags = cflags + asm_cflags + mach_settings.cflags
            mach_settings.ldflags = asm_ldflags + mach_settings.ldflags
    mconfig.post_parse_args_will_need.append(f)

def wrong_ios():
    if settings.enable_ios_bootstrap and not settings.host_machine().is_ios():
        raise mconfig.DependencyNotFoundException("iOS bootstrap requires iOS, but the target doesn't seem to be iOS - if you're compiling without Xcode, please specify --target=armv7-apple-darwin10 or something like that")
mconfig.post_parse_args_will_need.append(wrong_ios)


mconfig.parse_args()
####################################
mconfig.mark_safe_to_clean('(src)/generated/darwin-inject-asm.S', settings)

if settings.enable_ios_bootstrap:
    mconfig.log('Will build iOS bootstrap.\n')

if settings.enable_werror:
    for mach in machs + [settings.host_machine()]:
        settings[mach.name].cflags = ['-Wno-error'] + settings[mach.name].cflags

# XXX this is a mess and wrong
flags = ['-O3']
if settings.host_machine().is_ios():
    flags.append('-miphoneos-version-min=11.0')
if settings.host_machine().is_tvos():
    flags.append('-mappletvos-version-min=10.0')
for i in ('cflags', 'ldflags'):
    settings.host[i] = flags + settings.host[i]
settings.host.cflags = ['-fvisibility=hidden', '-g'] + settings.host.cflags
settings.host.ldflags = ['-dead_strip'] + settings.host.ldflags

# todo make overridable?
cc_argv = c.cc.argv()
if 'armv7' in cc_argv or 'arm64' in cc_argv or 'arm64e' in cc_argv:
    settings.modify_link = lambda env: (
        env['cmds'].append(ldid_tool.argv() + ['-S', env['outs'][0]]),
    )

settings.host.debug_info = True
settings.c_includes = ['(src)/lib', '(src)/substrate', '(src)/vendor']

emitter = settings.emitter

def cb(fn):
    if fn.endswith('/objc.c'):
        return settings.specialize(obj_ldflag_sets=[('-lobjc',)])
    return settings
# Note: the order of darwin-inject-asm.o is significant.  Per man page, ld is
# guaranteed to link objects in order, which is necessary because
# darwin-inject-asm.S does not itself ensure there is at least 0x4000 bytes of
# executable stuff after inject_page_start (so that arm can remap into arm64).
# By putting it at the beginning, we can just reuse the space for the rest of
# the library rather than having to pad with zeroes.
# (This only matters on 32-bit ARM, and the text segment is currently 0xa000
# bytes there, more than enough.)

mconfig.build_and_link_c_objs(
    emitter,
    settings.host_machine(),
    settings.specialize(override_ldflags=['-install_name', settings.install_name] + settings.host.ldflags),
    'dylib',
    '(out)/libsubstitute.dylib',
    [
        '(src)/generated/darwin-inject-asm.S',
        '(src)/lib/darwin/find-syms.c',
        '(src)/lib/darwin/inject.c',
        '(src)/lib/darwin/interpose.c',
        '(src)/lib/darwin/objc-asm.S',
        '(src)/lib/darwin/objc.c',
        '(src)/lib/darwin/read.c',
        '(src)/lib/darwin/substrate-compat.c',
        '(src)/lib/darwin/execmem.c',
        '(src)/lib/cbit/vec.c',
        '(src)/lib/jump-dis.c',
        '(src)/lib/transform-dis.c',
        '(src)/lib/hook-functions.c',
        '(src)/lib/strerror.c',
    ],
    settings_cb=cb
)
#settings.test = 'foo baz'
emitter.add_command(settings, ['(out)/libsubstitute.0.dylib'], [], ['ln -nfs libsubstitute.dylib (out)/libsubstitute.0.dylib'])

def o_to_bin(exe):
    bin = os.path.splitext(exe)[0] + '.bin'
    emitter.add_command(settings, [bin], [exe], ['segedit -extract __TEXT __text (outs[0]) (ins[0])'])
    return bin

if settings.gen_dia:
    args = []
    for (name, _cflags), mach in zip(asm_archs, machs):
        exe = '(out)/inject-asm-raw-%s' % (name,)
        bin = exe + '.bin'
        mconfig.build_and_link_c_objs(emitter, mach,
            settings.specialize(
                override_obj_fn='(out)/inject-asm-raw-%s.o' % (name,),
                override_ldflags=['-Wl,-order_file,(src)/lib/darwin/inject-asm-raw.order'] + settings[mach.name].ldflags,
            ),
            'dylib',
            exe,
            ['(src)/lib/darwin/inject-asm-raw.c'])
        bin = o_to_bin(exe)
        args.extend([name, bin])
    emitter.add_command(settings, ['(src)/generated/darwin-inject-asm.S'], ['(src)/script/gen-inject-asm.sh'] + args[1::2], [['(src)/script/gen-inject-asm.sh', '(outs[0])'] + args])

if settings.enable_tests:
    # just for quick testing
    for ofile, sfile, cflags, mach in [
        ('insns-arm.o', 'insns-arm.S', [], machs[2]),
        ('insns-thumb2.o', 'insns-arm.S', ['-DTHUMB2'], machs[2]),
        ('insns-libz-arm.o', 'insns-libz-arm.S', [], machs[2]),
        ('insns-libz-thumb2.o', 'insns-libz-arm.S', ['-DTHUMB2'], machs[2]),
        ('transform-dis-cases-arm64.o', 'transform-dis-cases-arm64.S', [], machs[3]),
        ('transform-dis-cases-i386.o', 'transform-dis-cases-i386.S', [], machs[1]),
        ('transform-dis-cases-x86_64.o', 'transform-dis-cases-x86_64.S', [], machs[0]),
        ('transform-dis-cases-arm.o', 'transform-dis-cases-arm.S', [], machs[2]),
        ('transform-dis-cases-thumb.o', 'transform-dis-cases-arm.S', ['-DTHUMB'], machs[2]),
    ]:
        mconfig.build_c_objs(emitter, mach, settings.specialize(
            override_obj_fn='(out)/'+ofile,
            override_cflags=cflags+settings.host.cflags
        ), ['(src)/test/'+sfile])
        o_to_bin('(out)/'+ofile)

    tests = [
        ('find-syms', ['-std=c89']),
        ('find-syms-cpp', 'find-syms', ['-x', 'c++', '-std=c++98'], {'cpp': True}),
        ('substrate', ['-x', 'c++', '-std=c++98'], {'cpp': True}),
        ('imp-forwarding', [], ['-framework', 'Foundation', '-lobjc'], {'extra_objs': ['(out)/lib/darwin/objc-asm.o']}),
        ('objc-hook', [], ['-framework', 'Foundation']),
        ('interpose',),
        ('inject', {'extra_objs': ['(out)/lib/darwin/inject.o', '(out)/lib/darwin/read.o', '(out)/generated/darwin-inject-asm.o']}),
        ('pc-patch', {'extra_objs': ['(out)/lib/darwin/execmem.o']}),
        ('execmem', [], ['-segprot', '__TEST', 'rwx', 'rx'], {'extra_objs': ['(out)/lib/darwin/execmem.o']}),
        ('hook-functions', [], ['-segprot', '__TEST', 'rwx', 'rx']),
        ('posixspawn-hook',),
        ('htab',),
        ('vec', {'cpp': True, 'extra_objs': ['(out)/lib/cbit/vec.o']}),
        ('vec-c', 'vec', ['-x', 'c'], {'extra_objs': ['(out)/lib/cbit/vec.o']}),
    ]

    for arch, hdr, xdis, target in [
        ('arm', 'arm/dis-arm.inc.h', 'dis_arm', 'arm'),
        ('thumb', 'arm/dis-thumb.inc.h', 'dis_thumb', 'arm'),
        ('thumb2', 'arm/dis-thumb2.inc.h', 'dis_thumb2', 'arm'),
        ('arm64', 'arm64/dis-main.inc.h', 'dis', 'arm64'),
        ('i386', 'x86/dis-main.inc.h', 'dis', 'i386'),
        ('x86_64', 'x86/dis-main.inc.h', 'dis', 'x86_64')
    ]:
        tests.append(('td-simple-'+arch, 'td-simple', ['-DHDR="%s"' % (hdr,), '-Dxdis='+xdis, '-DFORCE_TARGET_'+target]))
        tests.append(('jump-dis-'+arch, 'jump-dis', ['-O0', '-DFORCE_TARGET_'+target], {'extra_objs': ['(out)/lib/cbit/vec.o']}))
        tests.append(('transform-dis-'+arch, 'transform-dis', ['-O0', '-DFORCE_TARGET_'+target], {'extra_objs': ['(out)/lib/cbit/vec.o']}))

    for tup in tests:
        tup = list(tup)
        ibase = obase = tup.pop(0)
        cflags = ldflags = []
        options = {}
        if tup and isinstance(tup[0], str): ibase = tup.pop(0)
        if tup and isinstance(tup[0], (list, tuple)): cflags = tup.pop(0)
        if tup and isinstance(tup[0], (list, tuple)): ldflags = tup.pop(0)
        if tup: options, = tup
        o = '(out)/test-'+obase
        cfile = glob.glob(settings.src+'/test/test-'+ibase+'.*')[0]
        mconfig.build_and_link_c_objs(emitter, settings.host_machine(), settings.specialize(
            override_cflags=cflags+settings.host.cflags,
            override_ldflags=ldflags+settings.host.ldflags,
            override_obj_fn=o+'.o',
            override_is_cxx=options.get('cxx', False),
        ), 'exec', o, [cfile], objs=options.get('extra_objs', [])+['(out)/libsubstitute.dylib'])

    mconfig.build_and_link_c_objs(emitter, settings.host_machine(), settings, 'dylib', '(out)/injected-test-dylib.dylib', ['(src)/test/injected-test-dylib.c'])

if settings.enable_ios_bootstrap:
    mconfig.build_and_link_c_objs(emitter, settings.host_machine(),
        settings.specialize(
            override_cflags=['-fobjc-arc', '-Wno-unused-parameter']+settings.host.cflags,
            override_ldflags=['-framework', 'UIKit', '-framework', 'Foundation']+settings.host.ldflags,
        ),
        'exec',
        '(out)/SafetyDance.app/SafetyDance',
        [
            '(src)/darwin-bootstrap/safety-dance/AutoGrid.m',
            '(src)/darwin-bootstrap/safety-dance/main.m',
        ]
    )
    emitter.add_command(settings, ['(out)/SafetyDance.app/Info.plist'], ['(src)/darwin-bootstrap/safety-dance/Info.plist'], ['plutil -convert binary1 -o (outs[0]) (ins[0])'])
    for out in ['Default.png', 'Default@2x.png']:
        emitter.add_command(settings, ['(out)/SafetyDance.app/'+out], ['(src)/darwin-bootstrap/safety-dance/white.png'], ['cp (ins[0]) (outs[0])'])
    emitter.add_command(settings, ['safety-dance'], list(filter(lambda out: '/safety-dance/' in out, emitter.all_outs)), [], phony=True)

    ls = ['(out)/libsubstitute.dylib']
    for ty, out, ins, objs, ldf, cf in [
        ('dylib', '(out)/posixspawn-hook.dylib', ['(src)/darwin-bootstrap/posixspawn-hook.c'], ls, ['-lbsm'], []),
        ('dylib', '(out)/bundle-loader.dylib', ['(src)/darwin-bootstrap/bundle-loader.c'], [], [], []),
        ('exec', '(out)/unrestrict', ['(src)/darwin-bootstrap/unrestrict.c'], ls, [], []),
        ('exec', '(out)/inject-into-launchd', ['(src)/darwin-bootstrap/inject-into-launchd.c'], ls, ['-framework', 'IOKit', '-framework', 'CoreFoundation'], []),
        ('exec', '(out)/substituted', ['(src)/darwin-bootstrap/substituted.m'], ls, ['-lbsm', '-framework', 'Foundation', '-framework', 'CoreFoundation'], ['-fobjc-arc']),
        ('dylib', '(out)/safemode-ui-hook.dylib', ['(src)/darwin-bootstrap/safemode-ui-hook.m'], ls, ['-framework', 'Foundation', '-framework', 'UIKit'], ['-fobjc-arc']),
    ]:
        mconfig.build_and_link_c_objs(emitter, settings.host_machine(), settings.specialize(override_ldflags=ldf+settings.host.ldflags, override_cflags=cf+settings.host.cflags), ty, out, ins, objs=objs)

emitter.add_command(settings, ['all'], list(emitter.all_outs), [], phony=True)
emitter.set_default_rule('all')

mconfig.finish_and_emit()


================================================
FILE: darwin-bootstrap/DEBIAN/control
================================================
Package: com.ex.substitute
Architecture: iphoneos-arm
Name: Substitute
Depiction: http://jslinux.org
Author: comex <comexk+da@gmail.com>
Priority: optional
Version: {VERSION}
Description: Substrate substitute for code substitution
Maintainer: comex <comexk+dm@gmail.com>
Section: System
Depends: firmware (>= 8.0)



================================================
FILE: darwin-bootstrap/DEBIAN/extrainst_
================================================
#!/bin/bash

# for now, always reboot on install/uninstall

# <- http://iphonedevwiki.net/index.php/Packaging#Telling_Cydia_to_respring.2Freboot_etc._after_install
function finish() {
	f="${1}"
	[[ -z "${f}" || -z "${CYDIA}" ]] && return
	cydia=(${CYDIA})
	[[ ${cydia[1]} -eq 1 ]] || return
	echo "finish:${f}" >&${cydia[0]}
}

finish reboot


================================================
FILE: darwin-bootstrap/DEBIAN/postrm
================================================
#!/bin/bash

# for now, always reboot on install/uninstall

# <- http://iphonedevwiki.net/index.php/Packaging#Telling_Cydia_to_respring.2Freboot_etc._after_install
function finish() {
	f="${1}"
	[[ -z "${f}" || -z "${CYDIA}" ]] && return
	cydia=(${CYDIA})
	[[ ${cydia[1]} -eq 1 ]] || return
	echo "finish:${f}" >&${cydia[0]}
}

if [[ "$1" == "remove" ]]; then
	finish reboot
fi


================================================
FILE: darwin-bootstrap/bundle-loader.c
================================================
#define IB_LOG_NAME "bundle-loader"
#define IB_LOG_TO_SYSLOG
#include "ib-log.h"
#include "darwin/mach-decls.h"
#include "darwin/xxpc.h"
#include "substitute-internal.h"
#include <dlfcn.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mach/mig.h>
#include <mach-o/loader.h>
#include <objc/runtime.h>
#include <CoreFoundation/CoreFoundation.h>
#include <syslog.h>
#include <pthread.h>
#include <sys/time.h>
extern char ***_NSGetArgv(void);

static struct {
    bool initialized;
    typeof(CFBundleGetBundleWithIdentifier) *CFBundleGetBundleWithIdentifier;
    typeof(CFStringCreateWithCStringNoCopy) *CFStringCreateWithCStringNoCopy;
    typeof(CFRelease) *CFRelease;
    typeof(kCFAllocatorNull) *kCFAllocatorNull;
} cf_funcs;

static struct {
    bool initialized;
    typeof(objc_getClass) *objc_getClass;
} objc_funcs;

static xxpc_connection_t substituted_conn;

static pthread_mutex_t hello_reply_mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t hello_reply_cond = PTHREAD_COND_INITIALIZER;
static xxpc_object_t hello_reply;
static bool hello_reply_ready;

#define GET(funcs, handle, name) (funcs)->name = dlsym(handle, #name)

static void *dlopen_noload(const char *path) {
    void *h = dlopen(path, RTLD_LAZY | RTLD_NOLOAD);
    if (!h)
        return NULL;
    dlclose(h);
    return dlopen(path, RTLD_LAZY);
}

static bool cf_has_bundle(const char *name) {
    if (!cf_funcs.initialized) {
        const char *cf =
            "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
        void *handle = dlopen_noload(cf);
        if (IB_VERBOSE)
            ib_log("CF handle: %p", handle);
        if (handle) {
            GET(&cf_funcs, handle, CFBundleGetBundleWithIdentifier);
            GET(&cf_funcs, handle, CFStringCreateWithCStringNoCopy);
            GET(&cf_funcs, handle, CFRelease);
            GET(&cf_funcs, handle, kCFAllocatorNull);
        }
        cf_funcs.initialized = true;
    }
    if (!cf_funcs.CFBundleGetBundleWithIdentifier)
        return false;
    CFStringRef str = cf_funcs.CFStringCreateWithCStringNoCopy(
        NULL, name, kCFStringEncodingUTF8, *cf_funcs.kCFAllocatorNull);
    if (!str)
        return false;
    bool ret = !!cf_funcs.CFBundleGetBundleWithIdentifier(str);
    cf_funcs.CFRelease(str);
    return ret;
}

static bool objc_has_class(const char *name) {
    if (!objc_funcs.initialized) {
        void *handle = dlopen_noload("/usr/lib/libobjc.A.dylib");
        if (IB_VERBOSE)
            ib_log("objc handle: %p", handle);
        if (handle)
            GET(&objc_funcs, handle, objc_getClass);
        objc_funcs.initialized = true;
    }
    if (!objc_funcs.objc_getClass)
        return false;
    return !!objc_funcs.objc_getClass(name);
}

static void use_dylib(const char *name) {
    if (IB_VERBOSE)
        ib_log("loading dylib %s", name);
    dlopen(name, RTLD_LAZY);
}

enum bundle_test_result {
    BUNDLE_TEST_RESULT_INVALID,
    BUNDLE_TEST_RESULT_FAIL,
    BUNDLE_TEST_RESULT_PASS,
    BUNDLE_TEST_RESULT_EMPTY,
};

static enum bundle_test_result do_bundle_test_type(
    xxpc_object_t info, const char *key, bool (*test)(const char *)) {
    xxpc_object_t values = xxpc_dictionary_get_value(info, key);
    if (!values)
        return BUNDLE_TEST_RESULT_EMPTY;
    if (xxpc_get_type(values) != XXPC_TYPE_ARRAY)
        return BUNDLE_TEST_RESULT_INVALID;
    size_t count = xxpc_array_get_count(values);
    if (count == 0)
        return BUNDLE_TEST_RESULT_EMPTY;
    for (size_t i = 0; i < count; i++) {
        const char *value = xxpc_array_get_string(values, i);
        if (!value)
            return BUNDLE_TEST_RESULT_INVALID;
        if (test(value))
            return BUNDLE_TEST_RESULT_PASS;
    }
    return BUNDLE_TEST_RESULT_FAIL;
}

/* return false if the info was invalid */
static bool check_bundle_with_info(xxpc_object_t info) {
    bool any = xxpc_dictionary_get_bool(info, "any");
    enum bundle_test_result btr =
        do_bundle_test_type(info, "bundles", cf_has_bundle);
    if (btr == BUNDLE_TEST_RESULT_INVALID)
        return false;
    if (!any && btr == BUNDLE_TEST_RESULT_FAIL)
        goto no_load;
    if (any && btr == BUNDLE_TEST_RESULT_PASS)
        goto do_load;
    btr = do_bundle_test_type(info, "classes", objc_has_class);
    if (btr == BUNDLE_TEST_RESULT_INVALID)
        return false;
    if (btr == BUNDLE_TEST_RESULT_FAIL)
        goto no_load;
    else
        goto do_load;

no_load:
    return true;
do_load:;
    const char *name = xxpc_dictionary_get_string(info, "dylib");
    if (!name)
        return false;
    use_dylib(name);
    return true;
}

static void notify_added_removed(const struct mach_header *mh32, bool is_add) {
    char id_dylib_buf[32];
    const char *id_dylib;

    const mach_header_x *mh = (void *) mh32;
    uint32_t ncmds = mh->ncmds;
    const struct load_command *lc = (void *) (mh + 1);
    for (uint32_t i = 0; i < ncmds; i++) {
        if (lc->cmd == LC_ID_DYLIB) {
            const struct dylib_command *dc = (void *) lc;
            id_dylib = (const char *) dc + dc->dylib.name.offset;
            goto ok;
        }
        lc = (void *) lc + lc->cmdsize;
    }
    /* no name? */
    sprintf(id_dylib_buf, "unknown.%p", mh32);
    id_dylib = id_dylib_buf;

ok:;
    xxpc_object_t message = xxpc_dictionary_create(NULL, NULL, 0);
    xxpc_dictionary_set_string(message, "type", "add-remove");
    xxpc_dictionary_set_bool(message, "is-add", is_add);
    xxpc_dictionary_set_string(message, "id-dylib", id_dylib);
    xxpc_connection_send_message(substituted_conn, message);
    xxpc_release(message);
}

static void add_image_cb(const struct mach_header *mh, intptr_t vmaddr_slide) {
    notify_added_removed(mh, true);

}

static void remove_image_cb(const struct mach_header *mh, intptr_t vmaddr_slide) {
    notify_added_removed(mh, false);
}

static bool handle_hello_reply(xxpc_object_t dict) {
    if (IB_VERBOSE) {
        char *desc = xxpc_copy_description(dict);
        ib_log("hello_reply: %s", desc);
        free(desc);
    }
    bool notify = xxpc_dictionary_get_bool(dict, "notify-me-of-add-remove");
    if (notify) {
        _dyld_register_func_for_add_image(add_image_cb);
        _dyld_register_func_for_remove_image(remove_image_cb);

        for (uint32_t i = 0, count = _dyld_image_count(); i < count; i++) {
            const struct mach_header *mh32 = _dyld_get_image_header(i);
            if (mh32)
                notify_added_removed(mh32, true);
        }
    }
    xxpc_object_t bundles = xxpc_dictionary_get_value(dict, "bundles");
    if (!bundles || xxpc_get_type(bundles) != XXPC_TYPE_ARRAY)
        return false;
    for (size_t i = 0, count = xxpc_array_get_count(bundles);
         i < count; i++) {
        if (!check_bundle_with_info(xxpc_array_get_value(bundles, i)))
            return false;
    }
    return true;
}

static void signal_hello_reply(xxpc_object_t object) {
    if (hello_reply_ready)
        return;
    pthread_mutex_lock(&hello_reply_mtx);
    hello_reply = object;
    hello_reply_ready = true;
    pthread_cond_signal(&hello_reply_cond);
    pthread_mutex_unlock(&hello_reply_mtx);
}

static void handle_xxpc_object(xxpc_object_t object, bool is_reply) {
    const char *msg;
    xxpc_type_t ty = xxpc_get_type(object);
    if (ty == XXPC_TYPE_DICTIONARY) {
        if (is_reply) {
            signal_hello_reply(xxpc_retain(object));
            return;
        }
        msg = "received extraneous message from substituted";
    } else if (ty == XXPC_TYPE_ERROR) {
        signal_hello_reply(NULL);
        msg = "XPC error communicating with substituted";
    } else {
        msg = "unknown object received from XPC";
    }
    char *desc = xxpc_copy_description(object);
    ib_log("%s: %s", msg, desc);
    free(desc);
}

/* this is DYLD_INSERT_LIBRARIES'd, not injected. */
__attribute__((constructor))
static void init() {
    xxpc_connection_t conn = xxpc_connection_create_mach_service(
        "com.ex.substituted", NULL, 0);
    /* it's not supposed to return null, but just in case */
    if (!conn) {
        ib_log("xxpc_connection_create_mach_service returned null");
        goto bad;
    }

    substituted_conn = conn;

    xxpc_connection_set_event_handler(conn, ^(xxpc_object_t object) {
        handle_xxpc_object(object, false);
    });
    xxpc_connection_resume(conn);

    const char *argv0 = (*_NSGetArgv())[0];
    if (!argv0)
        argv0 = "???";

    xxpc_object_t message = xxpc_dictionary_create(NULL, NULL, 0);
    xxpc_dictionary_set_string(message, "type", "hello");
    xxpc_dictionary_set_int64(message, "proto-version", 1);
    xxpc_dictionary_set_string(message, "argv0", argv0);
    xxpc_connection_send_message_with_reply(conn, message, NULL,
                                           ^(xxpc_object_t reply) {
        handle_xxpc_object(reply, true);
    });
    xxpc_release(message);

    /* Timing out *always* means a bug (or the user manually unloaded
     * substituted).  Therefore, a high timeout is actually a good thing,
     * because it makes it clear (especially in testing) that something's wrong
     * rather than more subtly slowing things down.
     * We use mach_absolute_time + pthread_cond_timedwait_relative_np instead
     * of regular pthread_cond_timedwait in order to avoid issues if the system
     * time changes. */
    uint64_t then = mach_absolute_time() + 10 * NSEC_PER_SEC;
    pthread_mutex_lock(&hello_reply_mtx);
    while (!hello_reply_ready) {
        uint64_t now = mach_absolute_time();
        uint64_t remaining = now >= then ? 0 : then - now;
        struct timespec remaining_ts = {.tv_sec = remaining / NSEC_PER_SEC,
                                        .tv_nsec = remaining % NSEC_PER_SEC};
        int err = pthread_cond_timedwait_relative_np(&hello_reply_cond,
                                                     &hello_reply_mtx,
                                                     &remaining_ts);
        if (err != 0) {
            if (err == ETIMEDOUT)
                ib_log("ACK - didn't receive a reply from substituted in time!");
            else
                ib_log("pthread_cond_timedwait failed: %s", strerror(err));
            pthread_mutex_unlock(&hello_reply_mtx);
            goto bad;
        }
    }
    pthread_mutex_unlock(&hello_reply_mtx);

    if (hello_reply == NULL) {
        /* thread notified us of XPC error */
        goto bad;
    }

    if (!handle_hello_reply(hello_reply)) {
        char *desc = xxpc_copy_description(hello_reply);
        ib_log("received invalid message from substituted: %s", desc);
        free(desc);
        xxpc_release(hello_reply);
        goto bad;
    }
    xxpc_release(hello_reply);

    return;
bad:
    ib_log("giving up on loading bundles for this process...");
}


================================================
FILE: darwin-bootstrap/com.ex.substituted.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>ProcessType</key>
	<string>Interactive</string>
	<key>MachServices</key>
	<dict>
		<key>com.ex.substituted</key>
		<true/>
	</dict>
	<key>Label</key>
	<string>com.ex.substituted</string>
	<key>Program</key>
	<string>/Library/Substitute/Helpers/substituted</string>
	<key>EnvironmentVariables</key>
	<dict>
		<key>_MSSafeMode</key>
		<string>1</string>
	</dict>
</dict>
</plist>



================================================
FILE: darwin-bootstrap/ib-log.h
================================================
#pragma once
#include <dispatch/dispatch.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#ifdef IB_LOG_TO_SYSLOG
#include <syslog.h>
#define ib_log(fmt, args...) syslog(LOG_ERR, IB_LOG_NAME ": " fmt, ##args)
#else
static FILE *logfp;
static void open_logfp_if_necessary() {
    /* syslog() doesn't seem to work from launchd... */
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
        char filename[128];
        sprintf(filename, "/tmp/substitute-" IB_LOG_NAME "-log.%ld",
                (long) getpid());
        logfp = fopen(filename, "w");
        if (!logfp) {
            /* Ack... */
            logfp = stderr;
        }
    });
}
#define ib_log(fmt, args...) do { \
    open_logfp_if_necessary(); \
    fprintf(logfp, fmt "\n", ##args); \
    fflush(logfp); \
} while(0)
#endif

static inline void ib_log_hex(const void *buf, size_t size) {
    const uint8_t *up = buf;
    char *hex = malloc(2 * size + 1), *p = hex;
    for (size_t i = 0; i < size; i++) {
        sprintf(p, "%02x", up[i]);
        p += 2;
    }
    ib_log("%s", hex);
    free(hex);
}

#define IB_VERBOSE 1


================================================
FILE: darwin-bootstrap/inject-into-launchd.c
================================================
/* This is an iOS executable, placed in /etc/rc.d, that injects
 * posixspawn-hook.dylib into launchd (pid 1). */

#define IB_LOG_NAME "iil"
#include "ib-log.h"
#include "substitute.h"
#include "substitute-internal.h"
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <stdbool.h>
#include <stdint.h>
#include <syslog.h>
#include <CoreFoundation/CoreFoundation.h>

void *IOHIDEventCreateKeyboardEvent(CFAllocatorRef, uint64_t, uint32_t, uint32_t, bool, uint32_t);
void *IOHIDEventSystemCreate(CFAllocatorRef);
void *IOHIDEventSystemCopyEvent(void *, uint32_t, void *, uint32_t);

CFIndex IOHIDEventGetIntegerValue(void *, uint32_t);
enum {
    kIOHIDEventTypeKeyboard = 3,
    kIOHIDEventFieldKeyboardDown = 3 << 16 | 2,
};

static bool button_pressed(void *event_system, uint32_t usage_page, uint32_t usage) {
    /* This magic comes straight from Substrate... I don't really understand
     * what it's doing.  In particular, where is the equivalent kernel
     * implementation on OS X?  Does it not exist?  But I guess Substrate is
     * emulating backboardd. */
    void *dummy = IOHIDEventCreateKeyboardEvent(NULL, mach_absolute_time(),
                                                usage_page, usage,
                                                0, 0);
    if (!dummy) {
        ib_log("couldn't create dummy HID event");
        return false;
    }
    void *event = IOHIDEventSystemCopyEvent(event_system,
                                            kIOHIDEventTypeKeyboard,
                                            dummy, 0);
    if (!event)
        return false;
    CFIndex ival = IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
    return ival;
}

int main(UNUSED int argc, char **argv) {
    pid_t pid = argv[1] ? atoi(argv[1]) : 1; /* for testing */

    void *event_system = IOHIDEventSystemCreate(NULL);
    if (!event_system) {
        ib_log("couldn't create HID event system");
    } else {

            /* consumer page -> Volume Increment */
        if (button_pressed(event_system, 0x0c, 0xe9) ||
            /* telephony page -> Flash */
            button_pressed(event_system, 0x0b, 0x21)) {
            ib_log("disabling due to button press");
            return 0;
        }
    }
    mach_port_t port = 0;
    kern_return_t kr = mach_port_allocate(mach_task_self(),
                                          MACH_PORT_RIGHT_RECEIVE,
                                          &port);
    if (kr) {
        ib_log("mach_port_allocate: %x", kr);
        return 0;
    }
    const char *lib = "/Library/Substitute/Helpers/posixspawn-hook.dylib";
    struct shuttle shuttle = {
        .type = SUBSTITUTE_SHUTTLE_MACH_PORT,
        .u.mach.right_type = MACH_MSG_TYPE_MAKE_SEND,
        .u.mach.port = port
    };
    char *error;
    int ret = substitute_dlopen_in_pid(pid, lib, 0, &shuttle, 1, &error);
    if (ret) {
        ib_log("substitute_dlopen_in_pid: %s/%s",
               substitute_strerror(ret), error);
        return 0;
    }
    /* wait for it to finish */
    static struct {
        mach_msg_header_t hdr;
        mach_msg_trailer_t huh;
    } msg;
    kr = mach_msg_overwrite(NULL, MACH_RCV_MSG, 0, sizeof(msg), port,
                            MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
                            &msg.hdr, 0);
    if (kr)
        ib_log("mach_msg_overwrite: %x", kr);
}


================================================
FILE: darwin-bootstrap/posixspawn-hook.c
================================================
/* This library is loaded into launchd, and from there into xpcproxy, which
 * launchd uses as an intermediary to exec its processes; its main purpose is
 * to ensure that bundle-loader.dylib is specified in DYLD_INSERT_LIBRARIES
 * when launching such processes.  In the interests of not making ssh really
 * weird (and because it's what Substrate does), this is separate from
 * bundle-loader itself, so any processes that do their own spawning won't get
 * the environment override (though D_I_L will be inherited if the environment
 * isn't reset).
 *
 * It also handles the sandbox override for substituted.
 *
 * Note: Because bundle-loader synchronously contacts substituted, it must not
 * be loaded into any synchronous stuff launchd runs before starting jobs
 * proper.  Therefore, it's only inserted if the spawning process is xpcproxy
 * (rather than launchd directly).  I don't think iOS 7 does this yet, so this
 * needs to be fixed there.
 */

#define IB_LOG_NAME "posixspawn-hook"
#include "ib-log.h"
#include "substitute.h"
#include "substitute-internal.h"
#include "darwin/xxpc.h"
#include "cbit/htab.h"
#include <mach/mach.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <spawn.h>
#include <sys/wait.h>
#include <syslog.h>
#include <malloc/malloc.h>
#include <errno.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <libkern/OSByteOrder.h>

#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <spawn.h>
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/sysctl.h>
#include <dlfcn.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>

#define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_AFTER_DELAY 4
struct __attribute__((__packed__)) JAILBREAKD_ENTITLE_PID_AND_SIGCONT {
    uint8_t Command;
    int32_t PID;
};

void calljailbreakd(pid_t PID){
#define BUFSIZE 1024
    
    int sockfd, portno, n;
    int serverlen;
    struct sockaddr_in serveraddr;
    struct hostent *server;
    char *hostname;
    char buf[BUFSIZE];
    
    hostname = "127.0.0.1";
    portno = 5;
    
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
        printf("ERROR opening socket\n");
    
    /* gethostbyname: get the server's DNS entry */
    server = gethostbyname(hostname);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host as %s\n", hostname);
        exit(0);
    }
    
    /* build the server's Internet address */
    bzero((char *) &serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    bcopy((char *)server->h_addr,
          (char *)&serveraddr.sin_addr.s_addr, server->h_length);
    serveraddr.sin_port = htons(portno);
    
    /* get a message from the user */
    bzero(buf, BUFSIZE);
    
    struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT entitlePacket;
    entitlePacket.Command = JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_AFTER_DELAY;
    entitlePacket.PID = PID;
    
    memcpy(buf, &entitlePacket, sizeof(struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT));
    
    serverlen = sizeof(serveraddr);
    n = sendto(sockfd, buf, sizeof(struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT), 0, (const struct sockaddr *)&serveraddr, serverlen);
    if (n < 0)
        printf("Error in sendto\n");
}


#define _pid_hash(pidp) (*(pidp))
#define _pid_eq(pid1p, pid2p) (*(pid1p) == *(pid2p))
#define _pid_null(pidp) (!*(pidp))
DECL_STATIC_HTAB_KEY(pid_t, pid_t, _pid_hash, _pid_eq, _pid_null, 0);
DECL_HTAB(pid_str, pid_t, char *);

extern char ***_NSGetEnviron(void);

struct au_tid;
extern void audit_token_to_au32(audit_token_t, uid_t *, uid_t *, gid_t *,
                                uid_t *, gid_t *, pid_t *, pid_t *,
                                struct au_tid *);

static typeof(posix_spawn) *old_posix_spawn, *old_posix_spawnp,
                           hook_posix_spawn, hook_posix_spawnp;
static typeof(wait4) *old_wait4, hook_wait4;
static typeof(waitpid) *old_waitpid, hook_waitpid;
static int (*old_sandbox_check)(pid_t, const char *, int type, ...);
static int (*old_xpc_pipe_try_receive)(mach_port_t, xxpc_object_t *,
                                       mach_port_t *, void *, size_t, int);

static bool g_is_launchd;
static pthread_mutex_t g_state_lock = PTHREAD_MUTEX_INITIALIZER;
/* covering... */
static xxpc_object_t g_bundleid_to_fate;
static HTAB_STORAGE(pid_str) g_pid_to_bundleid =
    HTAB_STORAGE_INIT_STATIC(&g_pid_to_bundleid, pid_str);

static bool advance(char **strp, const char *template) {
    size_t len = strlen(template);
    if (!strncmp(*strp, template, len)) {
        *strp += len;
        return true;
    }
    return false;
}

static bool spawn_unrestrict(pid_t pid, bool should_resume, bool is_exec) {
    const char *prog = "/Library/Substitute/Helpers/unrestrict";
    char pid_s[32];
    sprintf(pid_s, "%ld", (long) pid);
    const char *should_resume_s = should_resume ? "1" : "0";
    const char *is_exec_s = is_exec ? "1" : "0";
    const char *argv[] = {prog, pid_s, should_resume_s, is_exec_s, NULL};
    pid_t prog_pid;
    char *env[] = {"_MSSafeMode=1", NULL};
    if (old_posix_spawn(&prog_pid, prog, NULL, NULL, (char **) argv, env)) {
        ib_log("posixspawn-hook: couldn't start unrestrict - oh well...");
        return false;
    }
    if (IB_VERBOSE)
        ib_log("unrestrict pid: %d; should_resume=%d is_exec=%d",
               prog_pid, should_resume, is_exec);
    int xstat;
    /* reap intermediate to avoid zombie - if it doesn't work, not a big deal */
    if (waitpid(prog_pid, &xstat, 0) == -1)
        ib_log("posixspawn-hook: couldn't waitpid");
    if (IB_VERBOSE)
        ib_log("unrestrict xstat=%x", xstat);
    return true;
}

static bool looks_restricted(const char *filename) {
    int fd = open(filename, O_RDONLY);
    if (fd == -1) {
        ib_log("open '%s': %s", filename, strerror(errno));
        return false;
    }
    bool ret = false;
    uint32_t offset = 0;
    union {
        uint32_t magic;
        struct {
            struct fat_header fh;
            struct fat_arch fa1;
        };
        struct mach_header mh;
    } u;
    if (read(fd, &u, sizeof(u)) != sizeof(u)) {
        ib_log("read header for '%s': %s", filename, strerror(errno));
        goto end;
    }
    if (ntohl(u.magic) == FAT_MAGIC) {
        /* Fat binary - to avoid needing to replicate grade_binary in the
         * kernel, we assume all architectures have the same restrict-ness. */
         if (u.fh.nfat_arch == 0)
            goto end;
        offset = ntohl(u.fa1.offset);
        if (pread(fd, &u, sizeof(u), offset) != sizeof(u)) {
            ib_log("read header (inside fat) for '%s': %s",
                   filename, strerror(errno));
            goto end;
        }
    }
    bool swap, is64;
    switch (u.magic) {
    case MH_MAGIC:
        swap = false;
        is64 = false;
        break;
    case MH_MAGIC_64:
        swap = false;
        is64 = true;
        break;
    case MH_CIGAM:
        swap = true;
        is64 = false;
        break;
    case MH_CIGAM_64:
        swap = true;
        is64 = true;
        break;
    default:
        ib_log("bad mach-o magic for '%s'", filename);
        goto end;
    }
    uint32_t sizeofcmds = u.mh.sizeofcmds;
    if (swap)
        sizeofcmds = OSSwapInt32(sizeofcmds);
    offset += is64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header);
    char *cmds_buf = malloc(sizeofcmds);
    ssize_t actual = pread(fd, cmds_buf, sizeofcmds, offset);
    if (actual < 0 || (uint32_t) actual != sizeofcmds) {
        ib_log("read load cmds for '%s': %s", filename, strerror(errno));
        free(cmds_buf);
        goto end;
    }
    /* overestimation is fine here */
    const char sectname[] = "__restrict";
    ret = !!memmem(cmds_buf, sizeofcmds, sectname, sizeof(sectname));
    free(cmds_buf);
end:
    close(fd);
    return ret;
}

static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
                                    pid_t *restrict pidp, const char *restrict path,
                                    const posix_spawn_file_actions_t *file_actions,
                                    const posix_spawnattr_t *restrict attrp,
                                    char *const argv[restrict],
                                    char *const envp[restrict]) {
    char *new = NULL;
    char **new_envp = NULL;
    char *const *envp_to_use = envp;
    char *const *my_envp = envp ? envp : *_NSGetEnviron();
    posix_spawnattr_t my_attr = NULL;

    short flags = 0;
    if (attrp && posix_spawnattr_getflags(attrp, &flags))
        goto crap;

    if (IB_VERBOSE) {
        ib_log("hook_posix_spawn_generic: path=%s%s%s (ld=%d)",
               path,
               (flags & POSIX_SPAWN_SETEXEC) ? " (exec)" : "",
               (flags & POSIX_SPAWN_START_SUSPENDED) ? " (suspend)" : "",
               g_is_launchd);
        for (char *const *ap = argv; *ap; ap++)
            ib_log("   %s", *ap);
    }


    static const char bl_dylib[] =
        "/Library/Substitute/Helpers/bundle-loader.dylib";
    static const char psh_dylib[] =
        "/Library/Substitute/Helpers/posixspawn-hook.dylib";

    const char *bundleid = NULL;

    /* which dylib should we add, if any? */
    const char *dylib_to_add;
    if (g_is_launchd) {
        if (strcmp(path, "/usr/libexec/xpcproxy"))
            goto skip;
        if (argv[0] && argv[1])
            bundleid = argv[1];
        dylib_to_add = psh_dylib;
    } else {
        /* - substituted obviously doesn't want to have bundle_loader run in it
         *   and try to contact substituted.  I have _MSSafeMode=1 in the plist
         *   so that Substrate also leaves it alone, and that's also checked in
         *   this routine, so the strcmp is just a backup.
         * - I am not sure why notifyd is an issue.  Some libc functions
         *   (localtime) synchronously contact it, which launchd could be
         *   calling, but I haven't caught it in the act.  XXX I'd like to be
         *   completely sure that notifyd and nothing else is a problem.
         * - sshd is here because one of its routines tries to close all file
         *   descriptors after a certain number - fine in a sane system, but
         *   here, if a file descriptor opened with guarded_open_np is closed
         *   with close, it crashes the process (and I don't see any way to
         *   cheat and disable the guard without actually knowing it).
         *   bundle-loader uses xpc, which uses dispatch, which uses
         *   guarded_open_np for its descriptors.  I could try to hook
         *   guarded_open_np for dispatch instead, but that doesn't help if an
         *   actual loaded bundle uses it from some other library, and I don't
         *   want to completely disable this bug detection measure for all
         *   processes.  Just excluding it from hooking is easier, and doing so
         *   provides a tiny bit of extra safety anyway, because ssh can
         *   sometimes be used as a last resort if hooking is screwing
         *   something up.
         * note: sshd is started with the wrapper, with argv[0] != path
         */
        if (!strcmp(path, "/Library/Substitute/Helpers/substituted") ||
            !strcmp(path, "/usr/sbin/notifyd") ||
            !strcmp(xbasename(argv[0] ?: ""), "sshd"))
            goto skip;
        dylib_to_add = bl_dylib;
    }

    if (access(dylib_to_add, R_OK)) {
        /* Substitute must have been uninstalled or something.  In the future
         * we'll be able to actually unload from launchd, but this is still
         * useful as a safety measure. */
        goto skip;
    }

    if (attrp) {
        posix_spawnattr_t attr = *attrp;
        size_t size = malloc_size(attr);
        my_attr = malloc(size);
        if (!my_attr)
            goto crap;
        memcpy(my_attr, attr, size);
    } else {
        if (posix_spawnattr_init(&my_attr))
            goto crap;
    }
    /* This mirrors Substrate's logic with safe mode.  I don't really
     * understand the point of the difference between its 'safe' (which removes
     * Substrate from DYLD_INSERT_LIBRARIES) and 'quit' (which just skips
     * directly to the original spawn), but I guess I'll just do the same for
     * maximum safety... */
    bool safe_mode = false;
    const char *orig_dyld_insert = "";
    size_t env_count = 0;
    for (char *const *ep = my_envp; *ep; ep++) {
        env_count++;
        char *env = *ep;
        if (advance(&env, "_MSSafeMode=") ||
            advance(&env, "_SubstituteSafeMode=")) {
            if (IB_VERBOSE)
                ib_log("got safe mode env: %s", *ep);
            if (!strcmp(env, "0") || !strcmp(env, "NO"))
                continue;
            else if (!strcmp(env, "1") || !strcmp(env, "YES"))
                safe_mode = true;
            else
                goto skip;
        } else if (advance(&env, "DYLD_INSERT_LIBRARIES=")) {
            orig_dyld_insert = env;
        }
    }
    new = malloc(sizeof("DYLD_INSERT_LIBRARIES=") - 1 +
                 sizeof(psh_dylib) /* not - 1, because : */ +
                 strlen(orig_dyld_insert) + 1);
    char *newp_orig = stpcpy(new, "DYLD_INSERT_LIBRARIES=");
    char *newp = newp_orig;
    const char *p = orig_dyld_insert;
    while (*p) { /* W.N.H. */
        const char *next = strchr(p, ':') ?: (p + strlen(p));
        /* append if it isn't one of ours */
        bool is_substitute =
            (next - p == sizeof(bl_dylib) - 1 &&
             !memcmp(p, bl_dylib, sizeof(bl_dylib) - 1)) ||
            (next - p == sizeof(psh_dylib) - 1 &&
             !memcmp(p, psh_dylib, sizeof(psh_dylib) - 1));
        if (!is_substitute) {
            if (newp != newp_orig)
                *newp++ = ':';
            memcpy(newp, p, next - p);
            newp += next - p;
            *newp = '\0';
        }
        if (!*next)
            break;
        p = next + 1;
    }
    /* append ours if necessary */
    if (!safe_mode) {
        if (newp != newp_orig)
            *newp++ = ':';
        newp = stpcpy(newp, dylib_to_add);
    }
    if (IB_VERBOSE)
        ib_log("using %s", new);
    /* no libraries? then just get rid of it */
    if (newp == newp_orig) {
        free(new);
        new = NULL;
    }
    new_envp = malloc(sizeof(char *) * (env_count + 2));
    envp_to_use = new_envp;
    char **outp = new_envp;
    for (size_t idx = 0; idx < env_count; idx++) {
        char *env = my_envp[idx];
        /* remove *all* D_I_L, including duplicates */
        if (!advance(&env, "DYLD_INSERT_LIBRARIES="))
            *outp++ = env;
    }
    if (new)
        *outp++ = new;
    *outp++ = NULL;

    if (safe_mode)
        goto skip;


    /* Deal with the dumb __restrict section.  A complication is that this
     * could actually be an exec. */
    bool need_unrestrict = looks_restricted(path);

    /* TODO skip this if Substrate is doing it anyway */
    bool was_suspended;
    if (need_unrestrict) {
        was_suspended = flags & POSIX_SPAWN_START_SUSPENDED;
        flags |= POSIX_SPAWN_START_SUSPENDED;
        if (posix_spawnattr_setflags(&my_attr, flags))
            goto crap;
        if (flags & POSIX_SPAWN_SETEXEC) {
            /* make the marker fd; hope you weren't using that */
            if (dup2(2, 255) != 255) {
                ib_log("dup2 failure - %s", strerror(errno));
                goto skip;
            }
            if (fcntl(255, F_SETFD, FD_CLOEXEC))
                goto crap;
            if (!spawn_unrestrict(getpid(), !was_suspended, true))
                goto skip;
        }
    }
    if (IB_VERBOSE)
        ib_log("**");
    if (!g_is_launchd) {
        ib_log("non-launchd, calling jailbreakd on ourselves");
        calljailbreakd(getpid());
    }
    int ret = old(pidp, path, file_actions, &my_attr, argv, envp_to_use);
    if (IB_VERBOSE)
        ib_log("ret=%d pid=%ld", ret, (long) *pidp);

    if (ret)
        goto cleanup;
    /* Since it returned, obviously it was not SETEXEC, so we need to
     * unrestrict it ourself. */
    pid_t pid = *pidp;
    if (need_unrestrict)
        spawn_unrestrict(pid, !was_suspended, false);

    if (bundleid) {
        pthread_mutex_lock(&g_state_lock);
        bool new_entry;
        char **old_bundleid = htab_setp_pid_str(&g_pid_to_bundleid.h, &pid, &new_entry);
        if (!new_entry)
            free(*old_bundleid);
        *old_bundleid = strdup(bundleid);
        pthread_mutex_unlock(&g_state_lock);
    }

    //calljailbreakd(pid);

    goto cleanup;
crap:
    ib_log("posixspawn-hook: weird error - OOM?  skipping our stuff");
skip:
    ret = old(pidp, path, file_actions, attrp, argv, envp);
cleanup:
    free(new_envp);
    free(new);
    free(my_attr);
    return ret;
}

static int hook_posix_spawn(pid_t *restrict pid, const char *restrict path,
                            const posix_spawn_file_actions_t *file_actions,
                            const posix_spawnattr_t *restrict attrp,
                            char *const argv[restrict],
                            char *const envp[restrict]) {
    return hook_posix_spawn_generic(old_posix_spawn, pid, path, file_actions,
                                    attrp, argv, envp);
}

static int hook_posix_spawnp(pid_t *restrict pid, const char *restrict path,
                             const posix_spawn_file_actions_t *file_actions,
                             const posix_spawnattr_t *restrict attrp,
                             char *const argv[restrict],
                             char *const envp[restrict]) {
    return hook_posix_spawn_generic(old_posix_spawnp, pid, path, file_actions,
                                    attrp, argv, envp);
}

static void after_wait_generic(pid_t pid, int stat) {
    if (pid == -1)
        return;
    pthread_mutex_lock(&g_state_lock);
    struct htab_bucket_pid_str *bucket =
        htab_getbucket_pid_str(&g_pid_to_bundleid.h, &pid);
    if (!bucket) {
        /* probably spawned some other way / not a task */
        if (IB_VERBOSE)
            ib_log("reaped unknown pid %d", pid);
        goto end;
    }
    char *bundleid = bucket->value;
    xxpc_dictionary_set_int64(g_bundleid_to_fate, bundleid, stat);
    free(bundleid);
    htab_removeat_pid_str(&g_pid_to_bundleid.h, bucket);
end:
    pthread_mutex_unlock(&g_state_lock);
}

static pid_t hook_wait4(pid_t pid, int *stat_loc, int options,
                        struct rusage *rusage) {
    pid_t ret = old_wait4(pid, stat_loc, options, rusage);
    after_wait_generic(ret, *stat_loc);
    return ret;
}

static pid_t hook_waitpid(pid_t pid, int *stat_loc, int options) {
    pid_t ret = old_waitpid(pid, stat_loc, options);
    after_wait_generic(ret, *stat_loc);
    return ret;
}

static int hook_xpc_pipe_try_receive(mach_port_t port_set, xxpc_object_t *requestp,
                                     mach_port_t *recvp, void *mig_demux,
                                     size_t msg_size, int dunno_ignored) {
    int res = old_xpc_pipe_try_receive(port_set, requestp, recvp, mig_demux,
                                       msg_size, dunno_ignored);
    if (res)
        return res;
    xxpc_object_t request = *requestp;
    if (!request || /* just to be sure */
        xxpc_get_type(request) != XXPC_TYPE_DICTIONARY)
        return res;
    /* is it for us? - usage of "in"/"out" is to satisfy the public vproc API */
    xxpc_object_t in = xxpc_dictionary_get_value(request, "in");
    if (!in || xxpc_get_type(in) != XXPC_TYPE_DICTIONARY)
        return res;
    const char *name = xxpc_dictionary_get_string(in,
        "com.ex.substitute.hook-operation");
    if (!name)
        return res;
    /* is it from someone untrustworthy? */
    audit_token_t at;
    xxpc_dictionary_get_audit_token(request, &at);
    uid_t euid;
    pid_t pid;
    audit_token_to_au32(at, NULL, &euid, NULL, NULL, NULL, &pid, NULL, NULL);
    if (euid != 0) {
        ib_log("Attempt to perform hook-operation by pid %d with euid %d",
               euid, pid);
        return res;
    }
    xxpc_object_t reply = NULL;
    if (!strcmp(name, "bundleid-to-fate")) {
        const char *bundleid = xxpc_dictionary_get_string(in, "bundleid");
        if (!bundleid)
            goto invalid;
        reply = xxpc_dictionary_create_reply(request);
        xxpc_object_t out = xxpc_dictionary_create(NULL, NULL, 0);
        pthread_mutex_lock(&g_state_lock);
        xxpc_object_t fate = xxpc_dictionary_get_value(g_bundleid_to_fate,
                                                       bundleid);
        if (fate) {
            if (IB_VERBOSE) {
                char *desc = xxpc_copy_description(fate);
                ib_log("your (%s) fate is %s", bundleid, desc);
                free(desc);
            }
            xxpc_dictionary_set_value(out, "fate", fate);
        } else {
            if (IB_VERBOSE)
                ib_log("your (%s) fate is unavailable", bundleid);
        }
        pthread_mutex_unlock(&g_state_lock);
        xxpc_dictionary_set_value(reply, "out", out);
        xxpc_release(out);
    } else {
        ib_log("unknown hook-operation '%s'", name);
        return res;
    }

    if (reply) {
        int reply_res = xxpc_pipe_routine_reply(reply);
        if (reply_res) {
            ib_log("xxpc_pipe_routine_reply: %d", reply_res);
            return res;
        }
        xxpc_release(reply);
    }
    xxpc_release(request);
    *requestp = NULL;
    return 0;

invalid:
    ib_log("invalid hook-operation='%s' message", name);
    return res;
}

static int hook_sandbox_check(pid_t pid, const char *op, int type, ...) {
    /* Can't easily determine the number of arguments, so just assume there's
     * less than 5 pointers' worth. */
    va_list ap;
    va_start(ap, type);
    long blah[5];
    for (int i = 0; i < 5; i++)
        blah[i] = va_arg(ap, long);
    va_end(ap);
    if (!strcmp(op, "mach-lookup")) {
        const char *name = (void *) blah[0];
        if (!strcmp(name, "com.ex.substituted")) {
            /* always allow */
            return 0;
        }
    }
    return old_sandbox_check(pid, op, type,
                             blah[0], blah[1], blah[2], blah[3], blah[4]);
}

EXPORT
void substitute_init(struct shuttle *shuttle, size_t nshuttle) {
    /* Just tell them we're done */
    if (nshuttle != 1) {
        ib_log("nshuttle = %zd?", nshuttle);
        return;
    }
    mach_port_t notify_port = shuttle[0].u.mach.port;
    mach_msg_header_t done_hdr;
    done_hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, 0);
    done_hdr.msgh_size = sizeof(done_hdr);
    done_hdr.msgh_remote_port = notify_port;
    done_hdr.msgh_local_port = 0;
    done_hdr.msgh_voucher_port = 0;
    done_hdr.msgh_id = 42;
    kern_return_t kr = mach_msg_send(&done_hdr);
    if (kr)
        ib_log("posixspawn-hook: mach_msg_send failed: kr=%x", kr);
    /* MOVE deallocated the port */
}

__attribute__((constructor))
static void init() {
    /* Note: I'm using interposing to minimize the chance of conflict with
     * Substrate.  This shouldn't actually be necessary, because MSHookProcess,
     * at least as of the old version I'm looking at the source code of, blocks
     * until the thread it remotely creates exits, and that thread does
     * pthread_join on the 'real' pthread it creates to do the dlopen (unlike
     * the equivalent in Substitute - the difference is to decrease dependence
     * on pthread internals).  substitute_dlopen_in_pid does not, but that's
     * what the notify port is for.  Meanwhile, the jailbreak I have installed
     * properly runs rc.d sequentially, so the injection tools won't do their
     * thing at the same time.  But just in case any of that doesn't hold up...
     *
     * (it also decreases the amount of library code necessary to load from
     * disk...)
     */
    g_bundleid_to_fate = xxpc_dictionary_create(NULL, NULL, 0);

    const char *image0 = _dyld_get_image_name(0);
    g_is_launchd = !!strstr(image0, "launchd");
    struct substitute_image *im = substitute_open_image(image0);
    if (!im) {
        ib_log("posixspawn-hook: substitute_open_image failed");
        goto end;
    }

    static const struct substitute_import_hook hooks[] = {
        {"_posix_spawn", hook_posix_spawn, &old_posix_spawn},
        {"_posix_spawnp", hook_posix_spawnp, &old_posix_spawnp},
        {"_sandbox_check", hook_sandbox_check, &old_sandbox_check},
        {"_waitpid", hook_waitpid, &old_waitpid},
        {"_wait4", hook_wait4, &old_wait4},
        {"_xpc_pipe_try_receive", hook_xpc_pipe_try_receive,
         &old_xpc_pipe_try_receive},
    };

    int err = substitute_interpose_imports(im, hooks, sizeof(hooks)/sizeof(*hooks),
                                           NULL, 0);
    if (err) {
        ib_log("posixspawn-hook: substitute_interpose_imports failed: %s",
               substitute_strerror(err));
        goto end;
    }

end:
    if (im)
        substitute_close_image(im);
}


================================================
FILE: darwin-bootstrap/safemode-ui-hook.m
================================================
#include "substitute.h"
#include <objc/runtime.h>
#include <notify.h>
#include <dispatch/dispatch.h>
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#include <mach-o/dyld.h>

static Class SpringBoard, SBApplicationController;
static bool g_did_activate_safetydance;

@interface _SBApplicationController
- (id)applicationWithBundleIdentifier:(NSString *)identifier;
+ (instancetype)sharedInstanceIfExists;
@end

@interface _SpringBoard
- (void)relaunchSpringBoard;
@end

@interface _SBApplication
- (void)setFlag:(int64_t)flag forActivationSetting:(unsigned)setting;
- (BOOL)launchApplicationWithIdentifier:(NSString *)iden
        suspended:(BOOL)suspended;
- (BOOL)isRunning;
- (void)activate;
@end

static bool detect_substrate_safe_mode() {
    /* Defer to Substrate's safe mode.  On my device, this doesn't seem to be
     * strictly necessary, because for whatever reason SafetyDance doesn't get
     * launched when MobileSafety is loaded, but let's not rely on that
     * quirk... */
    for (uint32_t i = 0, count = _dyld_image_count();
         i < count; i++) {
        const char *name = _dyld_get_image_name(i);
        if (name && strstr(name, "MobileSafety"))
            return true;
    }
    return false;
}

void (*old_applicationDidFinishLaunching)(id, SEL, id);
static void my_applicationDidFinishLaunching(id self, SEL sel, id app) {
    old_applicationDidFinishLaunching(self, sel, app);
    if (detect_substrate_safe_mode()) {
        NSLog(@"Deferring to Substrate's safe mode.");
        return;
    }
    id controller = [SBApplicationController sharedInstanceIfExists];
    if (!controller) {
        NSLog(@"substitute safe mode: sharedInstanceIfExists => nil!");
        return;
    }
    NSString *bundle_id = @"com.ex.SafetyDance";
    id sbapp = [controller applicationWithBundleIdentifier:bundle_id];
    if (!sbapp) {
        NSLog(@"substitute safe mode: no app with bundle ID '%@' - installation messed up?",
              bundle_id);
        return;
    }
    [sbapp setFlag:1 forActivationSetting:1]; /* noAnimate */
    /* [sbapp setFlag:1 forActivationSetting:5]; */ /* seo */
    [self launchApplicationWithIdentifier:bundle_id suspended:NO];
    g_did_activate_safetydance = true;
}

BOOL (*old_handleDoubleHeightStatusBarTap)(id, SEL, int64_t);
static BOOL my_handleDoubleHeightStatusBarTap(id self, SEL sel, int64_t number) {
    if (g_did_activate_safetydance && number == 202) {
        NSString *bundle_id = @"com.ex.SafetyDance";
        id controller = [SBApplicationController sharedInstanceIfExists];
        id sbapp = [controller applicationWithBundleIdentifier:bundle_id];
        if ([sbapp isRunning]) {
            NSLog(@"activate!");
            [sbapp setFlag:1 forActivationSetting:20]; /* fromBanner */
            [self launchApplicationWithIdentifier:bundle_id suspended:NO];
            return YES;
        }
    }
    return old_handleDoubleHeightStatusBarTap(self, sel, number);
}

__attribute__((constructor))
static void init() {
    #define GET(clsname) \
        clsname = objc_getClass(#clsname); \
        if (!clsname) { \
            NSLog(@"substitute safe mode failed to find %s", #clsname); \
            return; \
        }

    GET(SpringBoard);
    GET(SBApplicationController);

    int notify_token;
    uint32_t notify_status = notify_register_dispatch(
        "com.ex.substitute.safemode-restart-springboard-plz",
        &notify_token, dispatch_get_main_queue(), ^(int tok) {
            id sb = [UIApplication sharedApplication];
            [sb relaunchSpringBoard];
        }
    );

    #define HOOK(cls, sel, selvar) do { \
        int ret = substitute_hook_objc_message(cls, @selector(sel), \
                                               my_##selvar, \
                                               &old_##selvar, NULL); \
        if (ret) { \
            NSLog(@"substitute safe mode '%s' hook failed: %d", #sel, ret); \
            return; \
        } \
    } while(0)

    HOOK(SpringBoard, applicationDidFinishLaunching:,
         applicationDidFinishLaunching);
    HOOK(SpringBoard, handleDoubleHeightStatusBarTap:,
         handleDoubleHeightStatusBarTap);
}


================================================
FILE: darwin-bootstrap/safemode-ui-hook.plist
================================================
<plist>
<dict>
    <key>Filter</key>
    <dict>
        <key>Executables</key>
        <array>
            <string>/System/Library/CoreServices/SpringBoard.app/SpringBoard</string>
        </array>
        <key>SafeMode</key>
        <true/>
    </dict>
</dict>
</plist>


================================================
FILE: darwin-bootstrap/safety-dance/AutoGrid.h
================================================
//
//  AutoGrid.h
//  SafetyDance
//
//  Created by Nicholas Allegra on 1/26/15.
//  Copyright (c) 2015 Nicholas Allegra. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface AutoGrid : UIView {
    NSArray *views;
    UIScrollView *scrollView;
}
- (void)setViews:(NSArray *)views;

@end


================================================
FILE: darwin-bootstrap/safety-dance/AutoGrid.m
================================================
//
//  AutoGrid.m
//  SafetyDance
//
//  Created by Nicholas Allegra on 1/26/15.
//  Copyright (c) 2015 Nicholas Allegra. All rights reserved.
//

#import "AutoGrid.h"

@implementation AutoGrid
- (void)setViews:(NSArray *)_views {
    views = _views;
    [scrollView removeFromSuperview];
    scrollView = [[UIScrollView alloc] init];
    [self addSubview:scrollView];
    for (UIView *view in views)
        [scrollView addSubview:view];
    [self setNeedsLayout];
}

- (void)layoutSubviews {
    scrollView.frame = self.bounds;
    CGFloat paddingX = 22, paddingY = 10;
    NSUInteger nviews = [views count];
    CGSize *sizes = malloc(sizeof(*sizes) * nviews);

    for (NSUInteger i = 0; i < nviews; i++)
        sizes[i] = [[views objectAtIndex:i] intrinsicContentSize];

    CGFloat availableWidth = self.bounds.size.width;
    /* try to lay out using an increasing number of columns */
    NSUInteger cols;
    CGSize contentSize;
    CGFloat *colWidths = NULL;
    for (cols = 1; ; cols++) {
        free(colWidths);
        colWidths = malloc(sizeof(*colWidths) * cols);
        for (NSUInteger col = 0; col < cols; col++)
            colWidths[col] = 0;
        CGFloat tentativeHeight = 0;
        CGFloat tentativeWidth = 0;
        for (NSUInteger row = 0; row < nviews / cols; row++) {
            CGFloat totalWidth = 0;
            CGFloat maxHeight = 0;
            for (NSUInteger col = 0; col < cols; col++) {
                NSUInteger i = row * cols + col;
                if (i >= nviews)
                    goto done1;
                CGSize size = sizes[i];
                if (size.width > colWidths[col])
                    colWidths[col] = size.width;
                if (col != 0)
                    totalWidth += paddingX;
                totalWidth += size.width;
                if (size.height > maxHeight)
                    maxHeight = size.height;
            }
            if (totalWidth > tentativeWidth)
                tentativeWidth = totalWidth;
            tentativeHeight += maxHeight + paddingY;
        }
    done1:
        if (cols > 1 && tentativeWidth > availableWidth) {
            cols--;
            break;
        }
        contentSize = CGSizeMake(tentativeWidth, tentativeHeight);
        /* NSLog(@"%f", contentSize.height); */
        if (contentSize.width == 0)
            break;

    }
    scrollView.contentSize = contentSize;
    CGFloat y = 0;
    for (NSUInteger row = 0; ; row++) {
        CGFloat x = 0;
        CGFloat maxHeight = 0;
        for (NSUInteger col = 0; col < cols; col++) {
            NSUInteger i = row * cols + col;
            if (i >= nviews)
                goto done2;
            CGSize size = sizes[i];
            UIView *view = [views objectAtIndex:i];
            if (col != 0)
                x += paddingX;
            view.frame = CGRectMake(x, y, size.width, size.height);
            x += colWidths[col];
            if (size.height > maxHeight)
                maxHeight = size.height;
        }
        y += maxHeight + paddingY;
    }
done2:
    free(sizes);
    free(colWidths);
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

@end


================================================
FILE: darwin-bootstrap/safety-dance/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>SafetyDance</string>
	<key>CFBundleIdentifier</key>
	<string>com.ex.SafetyDance</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>SafetyDance</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>LSRequiresIPhoneOS</key>
	<false/>
	<key>UIDeviceFamily</key>
	<integer>2</integer>
	<key>UILaunchImages</key>
	<array>
		<dict>
			<key>UILaunchImageMinimumOSVersion</key>
			<string>7.0</string>
			<key>UILaunchImageName</key>
			<string>Default</string>
			<key>UILaunchImageOrientation</key>
			<string>Portrait</string>
			<key>UILaunchImageSize</key>
			<string>{320, 480}</string>
		</dict>
		<dict>
			<key>UILaunchImageMinimumOSVersion</key>
			<string>7.0</string>
			<key>UILaunchImageName</key>
			<string>Default</string>
			<key>UILaunchImageOrientation</key>
			<string>Portrait</string>
			<key>UILaunchImageSize</key>
			<string>{320, 568}</string>
		</dict>
		<dict>
			<key>UILaunchImageMinimumOSVersion</key>
			<string>8.0</string>
			<key>UILaunchImageName</key>
			<string>Default</string>
			<key>UILaunchImageOrientation</key>
			<string>Portrait</string>
			<key>UILaunchImageSize</key>
			<string>{375, 667}</string>
		</dict>
		<dict>
			<key>UILaunchImageMinimumOSVersion</key>
			<string>8.0</string>
			<key>UILaunchImageName</key>
			<string>Default</string>
			<key>UILaunchImageOrientation</key>
			<string>Portrait</string>
			<key>UILaunchImageSize</key>
			<string>{414, 736}</string>
		</dict>
		<dict>
			<key>UILaunchImageMinimumOSVersion</key>
			<string>7.0</string>
			<key>UILaunchImageName</key>
			<string>Default</string>
			<key>UILaunchImageOrientation</key>
			<string>Portrait</string>
			<key>UILaunchImageSize</key>
			<string>{768, 1024}</string>
		</dict>
	</array>
	<key>UIStatusBarStyle</key>
	<string>UIStatusBarStyleLightContent</string>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>SBAppTags</key>
	<array>
		<string>hidden</string>
	</array>
	<key>UIBackgroundModes</key>
	<array>
		<string>continuous</string>
		<string>unboundedTaskCompletion</string>
	</array>
	<key>_UILaunchAlwaysFullScreen</key>
	<true/>
	<key>SBIsLaunchableDuringSetup</key>
	<true/>
	<key>BKRequiresExtendedLaunchInterval</key>
	<true/>
</dict>
</plist>


================================================
FILE: darwin-bootstrap/safety-dance/main.m
================================================
#include "darwin/xxpc.h"
#include "substitute-internal.h"
#import <UIKit/UIKit.h>
#import "AutoGrid.h"
#include <notify.h>

static NSArray *g_dylibs_to_show;

@interface UIApplication (Private)
- (void)terminateWithSuccess;
- (void)suspend;

- (void)setGlowAnimationEnabled:(BOOL)enabled forStyle:(int)style;
- (void)addStatusBarStyleOverrides:(int)overrides;
- (void)removeStatusBarStyleOverrides:(int)overrides;
- (void)setDoubleHeightStatusText:(NSString *)text forStyle:(int)style;
@end

@interface ViewController : UIViewController {
    AutoGrid *autoGrid;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self loadStuff];
    /*
    NSMutableArray *names = [NSMutableArray array];
    for (int i = 0; i < 200; i++)
        [names addObject:[NSString stringWithFormat:@"Some Dylib %d", i]];
    g_dylibs_to_show = names;
    */
    NSMutableArray *views = [NSMutableArray array];
    for (NSString *name in g_dylibs_to_show) {
        UILabel *label = [[UILabel alloc] init];
        label.text = name;
        [views addObject:label];
    }
    [autoGrid setViews:views];
}

- (void)exitReturnToNormal {
    notify_post("com.ex.substitute.safemode-restart-springboard-plz");
}

- (void)exitContinueSafe {
    /* mimic Setup.app, but we don't want to actually quit */
    UIApplication *app = [UIApplication sharedApplication];
    [app suspend];
}

#define EXPLANATION \
    @"SpringBoard seems to have crashed.  This might have been caused by Substitute jailbreak extension, or it could be unrelated.  Just to be safe, extensions in SpringBoard have been temporarily disabled.\n\nThe following extensions were running:"

static void hugging(UIView *view, UILayoutPriority pri) {
    [view setContentHuggingPriority:pri forAxis:UILayoutConstraintAxisHorizontal];
    [view setContentHuggingPriority:pri forAxis:UILayoutConstraintAxisVertical];
}
static void compression(UIView *view, UILayoutPriority pri) {
    [view setContentCompressionResistancePriority:pri forAxis:UILayoutConstraintAxisHorizontal];
    [view setContentCompressionResistancePriority:pri forAxis:UILayoutConstraintAxisVertical];
}

- (void)loadStuff {
    self.view.backgroundColor = [UIColor whiteColor];

    UILabel *top = [[UILabel alloc] init];
    top.translatesAutoresizingMaskIntoConstraints = NO;
    top.textAlignment = NSTextAlignmentCenter;
    hugging(top, 251);
    top.text = @"libsubstitute";
    top.font = [UIFont systemFontOfSize:23];
    [self.view addSubview:top];

    UILabel *big = [[UILabel alloc] init];
    big.translatesAutoresizingMaskIntoConstraints = NO;
    big.textAlignment = NSTextAlignmentCenter;
    hugging(big, 251);
    [big setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisHorizontal];
    [big setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisVertical];
    big.text = @"Safe Mode";
    big.font = [UIFont systemFontOfSize:32];
    [self.view addSubview:big];

    UILabel *explain = [[UILabel alloc] init];
    explain.translatesAutoresizingMaskIntoConstraints = NO;
    explain.textAlignment = NSTextAlignmentCenter;
    hugging(explain, 251);
    compression(explain, 999);
    explain.text = EXPLANATION;
    explain.font = [UIFont systemFontOfSize:14];
    explain.minimumScaleFactor = 0.5; /* test */
    explain.numberOfLines = 0;
    [self.view addSubview:explain];

    UIButton *returnButton = [UIButton buttonWithType:UIButtonTypeSystem];
    returnButton.translatesAutoresizingMaskIntoConstraints = NO;
    returnButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
    returnButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
    returnButton.titleLabel.font = [UIFont systemFontOfSize:17];
    [returnButton setTitle:@"Return to Normal" forState:UIControlStateNormal];
    [self.view addSubview:returnButton];
    [returnButton addTarget:self action:@selector(exitReturnToNormal)
                  forControlEvents:UIControlEventTouchUpInside];

    UIButton *continueButton = [UIButton buttonWithType:UIButtonTypeSystem];
    continueButton.translatesAutoresizingMaskIntoConstraints = NO;
    hugging(continueButton, 999);
    compression(continueButton, 300);
    continueButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
    continueButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
    continueButton.titleLabel.font = [UIFont systemFontOfSize:17];
    [continueButton setTitle:@"Continue in Safe Mode" forState:UIControlStateNormal];
    [self.view addSubview:continueButton];
    [continueButton addTarget:self action:@selector(exitContinueSafe)
                    forControlEvents:UIControlEventTouchUpInside];

    autoGrid = [[AutoGrid alloc] init];
    autoGrid.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:autoGrid];

    NSDictionary *viewsDictionary = @{
        @"top": top,
        @"big": big,
        @"explain": explain,
        @"returnButton": returnButton,
        @"continueButton": continueButton,
        @"grid": autoGrid,
#ifdef __IPHONE_11_0
        @"topGuide": self.view.safeAreaLayoutGuide.topAnchor,
        @"bottomGuide": self.view.safeAreaLayoutGuide.bottomAnchor,
#else
        @"topGuide": self.topLayoutGuide,
        @"bottomGuide": self.bottomLayoutGuide,
#endif
    };
    NSMutableArray *constraints = [[NSMutableArray alloc] init];
    [constraints addObjectsFromArray:
        [NSLayoutConstraint constraintsWithVisualFormat:
            @"V:[topGuide]-10-[top]-0@100-[big]-0@100-[explain]-18@200-[grid]-18-[continueButton]-8-[returnButton]-20@100-[bottomGuide]"
            options:NSLayoutFormatAlignAllCenterX metrics:nil views:viewsDictionary]];
    NSArray *additional = @[
        @"[explain(<=650)]",
        @"|->=10-[explain]->=10-|",
        @"|-20-[grid]-20-|",
    ];
    for (NSString *fmt in additional) {
        [constraints addObjectsFromArray:
            [NSLayoutConstraint constraintsWithVisualFormat:fmt options:0 metrics:nil views:viewsDictionary]];
    }
    [self.view addConstraints:constraints];
}


- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
        return UIInterfaceOrientationMaskAll;
    else if ([UIScreen mainScreen].bounds.size.width >= 414)
        return UIInterfaceOrientationMaskAllButUpsideDown;
    else
        return UIInterfaceOrientationMaskPortrait;
}

@end

@interface AppDelegate : UIResponder <UIApplicationDelegate> {
}

@property (strong, nonatomic) UIWindow *window;


@end

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    ViewController *viewController = [[ViewController alloc] init];
    self.window.rootViewController = viewController;
    [self.window makeKeyAndVisible];

    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    /* mimic Voice Memos... */
    NSLog(@"did enter background");
    [application setGlowAnimationEnabled:YES forStyle:202];
    [application setDoubleHeightStatusText:@"Safe Mode" forStyle:202];
    [application addStatusBarStyleOverrides:4];
    NSLog(@"(done)");
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    NSLog(@"did become active");
    [application setGlowAnimationEnabled:NO forStyle:202];
    [application removeStatusBarStyleOverrides:4];
    NSLog(@"(done)");
}

@end

static const char *test_and_transform_id_dylib(const char *id_dylib) {
    const char *base = xbasename(id_dylib);
    static const char dir1[] = "/Library/MobileSubstrate/DynamicLibraries/";
    static const char dir2[] = "/Library/Substitute/DynamicLibraries/";
    if (!strncmp(id_dylib, dir1, sizeof(dir1) - 1) ||
        !strncmp(id_dylib, dir2, sizeof(dir2) - 1))
        return base;
    char *fn = NULL, *fn2 = NULL;
    asprintf(&fn, "%s%s", dir1, base);
    asprintf(&fn2, "%s%s", dir2, base);
    bool found = !access(fn, F_OK) || !access(fn2, F_OK);
    free(fn);
    free(fn2);
    if (found)
        return base;
    return NULL;
}

static void do_bad() {
    NSLog(@"problem asking substituted for springboard-fatal-loaded-dylibs...");
    g_dylibs_to_show = @[@"<error>"];
}

static void startup() {
    xxpc_connection_t conn = xxpc_connection_create_mach_service(
        "com.ex.substituted", NULL, 0);
    if (!conn)
        return do_bad();
    xxpc_connection_set_event_handler(conn, ^(xxpc_object_t event) {
        NSLog(@"< %@", event);
    });
    xxpc_connection_resume(conn);
    xxpc_object_t request = xxpc_dictionary_create(NULL, NULL, 0);
    xxpc_dictionary_set_string(request, "type",
                               "springboard-fatal-loaded-dylibs");
    NSLog(@"asking substituted...");
    xxpc_object_t reply = xxpc_connection_send_message_with_reply_sync(
        conn, request);
    NSLog(@"done.");
    if (!reply || xxpc_get_type(reply) != XXPC_TYPE_DICTIONARY)
        return do_bad();
    NS_VALID_UNTIL_END_OF_SCOPE
    xxpc_object_t dylibs = xxpc_dictionary_get_value(reply, "dylibs");
    if (!dylibs) {
        g_dylibs_to_show = @[@"<unknown>"];
        return;
    }
    if (xxpc_get_type(dylibs) != XXPC_TYPE_ARRAY)
        return do_bad();
    NSMutableArray *ary = [NSMutableArray array];
    for (size_t i = 0, count = xxpc_array_get_count(dylibs);
         i < count; i++) {
        const char *dylib = xxpc_array_get_string(dylibs, i);
        if (!dylib)
            return do_bad();
        const char *dylib_to_show = test_and_transform_id_dylib(dylib);
        if (dylib_to_show)
            [ary addObject:[NSString stringWithCString:dylib_to_show
                                     encoding:NSUTF8StringEncoding]];
    }
    g_dylibs_to_show = ary;
}

int main(int argc, char *argv[]) {
    @autoreleasepool {
        startup();
        return UIApplicationMain(argc, argv, nil, @"AppDelegate");
    }
}



================================================
FILE: darwin-bootstrap/substituted-plist-loader.h
================================================
void get_bundle_list(const char *exec_name,
                     const void **bundle_list, size_t *bundle_list_size);


================================================
FILE: darwin-bootstrap/substituted.m
================================================
#include "darwin/xxpc.h"
#include "substitute.h"
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <mach/vm_param.h>

void *vproc_swap_complex(void *vp, int key, xxpc_object_t inval,
                         __strong xxpc_object_t *outval);

/* This is a daemon contacted by all processes which can load extensions.  It
 * currently does the work of reading the plists in
 * /Library/Substitute/DynamicLibraries in order to avoid loading objc/CF
 * libraries into the target binary (unless actually required by loaded
 * libraries).  In the future it will help with hot loading. */

static enum {
    NO_SAFE,
    NEEDS_SAFE,
    REALLY_SAFE,
} g_springboard_needs_safe;

static NSSet *g_springboard_loaded_dylibs;
static NSSet *g_springboard_last_loaded_dylibs;

static bool load_state() {
    int fd = shm_open("com.ex.substituted.state", O_RDONLY);
    if (fd == -1)
        return false;
    struct stat st;
    if (fstat(fd, &st)) {
        NSLog(@"fstat error");
        return false;
    }
    size_t size = (size_t) st.st_size;
    /* note: can't have concurrent modification */
    void *buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (buf == MAP_FAILED) {
        NSLog(@"mmap error");
        return false;
    }
    NSData *nsd = [NSData dataWithBytesNoCopy:buf length:size];
    NSError *err = nil;
    id result = [NSPropertyListSerialization propertyListWithData:nsd
                                             options:NSPropertyListImmutable
                                             format:NULL error:&err];
    if (err)
        NSLog(@"propertyListWithData: %@", err);
    if (!result)
        return false;
    if (![result isKindOfClass:[NSSet class]]) {
        NSLog(@"loaded data is not NSSet - newer version?");
        return false;
    }
    for (id str in result) {
        if (![str isKindOfClass:[NSString class]]) {
            NSLog(@"loaded data member is not NSString");
            return false;
        }
    }
    g_springboard_last_loaded_dylibs = result;
    munmap(buf, size);
    close(fd);
    return true;
}

static bool save_state() {
    NSError *err = nil;
    NSData *data = [NSPropertyListSerialization
                    dataWithPropertyList:g_springboard_last_loaded_dylibs
                    format:NSPropertyListBinaryFormat_v1_0 options:0
                    error:&err];
    if (err)
        NSLog(@"dataWithPropertyList: %@", err);
    if (!data)
        return false;
    int fd = shm_open("com.ex.substituted.state", O_RDWR | O_CREAT | O_TRUNC,
                      0644);
    if (fd == -1) {
        NSLog(@"shm_open error (write)");
        return false;
    }
    size_t size = ([data length] + PAGE_MASK) & ~PAGE_MASK;
    if (ftruncate(fd, size)) {
        NSLog(@"ftruncate error (%zu)", size);
        return false;
    }

    void *buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (buf == MAP_FAILED) {
        NSLog(@"mmap error (write)");
        return false;
    }

    memcpy(buf, [data bytes], [data length]);
    munmap(buf, size);
    close(fd);
    return true;
}

extern kern_return_t bootstrap_look_up3(mach_port_t bp,
    const char *service_name, mach_port_t *sp, pid_t target_pid,
    const uuid_t instance_id, uint64_t flags);

static kern_return_t my_bootstrap_look_up3(mach_port_t bp,
    const char *service_name, mach_port_t *sp, pid_t target_pid,
    const uuid_t instance_id, uint64_t flags) {
    NSLog(@"Something in substituted tried to look up '%s', which could cause a deadlock.  This is a bug.",
          service_name);
    return KERN_FAILURE;
}
static const struct substitute_function_hook deadlock_warning_hook = {
    bootstrap_look_up3, my_bootstrap_look_up3, NULL
};

static void install_deadlock_warning() {
    int ret = substitute_hook_functions(&deadlock_warning_hook, 1, NULL, 0);
    if (ret) {
        NSLog(@"substitute_hook_functions(&deadlock_warning_hook, ..) failed: %d",
              ret);
    }
}

static double id_to_double(id o) {
    if ([o isKindOfClass:[NSString class]]) {
        NSScanner *scan = [NSScanner scannerWithString:o];
        double d;
        if (![scan scanDouble:&d] || !scan.atEnd)
            return NAN;
        return d;
    } else if ([o isKindOfClass:[NSNumber class]]) {
        return [o doubleValue];
    } else {
        return NAN;
    }
}

static xxpc_object_t nsstring_to_xpc(NSString *in) {
    return xxpc_string_create([in cStringUsingEncoding:NSUTF8StringEncoding]);
}

@interface PeerHandler : NSObject {
    xxpc_object_t _connection;
    NSString *_argv0;
    bool _is_springboard;
    NSMutableSet *_loaded_dylibs;
}

@end

enum convert_filters_ret TEST;

@implementation PeerHandler

enum convert_filters_ret {
    PROVISIONAL_PASS,
    FAIL,
    INVALID
};

- (enum convert_filters_ret)
    convertFiltersForBundleInfo:(NSDictionary *)plist_dict
    toXPCReply:(xxpc_object_t)out_info {

    for (NSString *key in [plist_dict allKeys]) {
        if (!([key isEqualToString:@"Filter"]))
            return INVALID;
    }

    NSDictionary *filter = [plist_dict objectForKey:@"Filter"];
    if (!filter)
        return PROVISIONAL_PASS;

    if (![filter isKindOfClass:[NSDictionary class]])
        return INVALID;

    for (NSString *key in [filter allKeys]) {
        if (!([key isEqualToString:@"CoreFoundationVersion"] ||
              [key isEqualToString:@"Classes"] ||
              [key isEqualToString:@"Bundles"] ||
              [key isEqualToString:@"Executables"] ||
              [key isEqualToString:@"Mode"] ||
              [key isEqualToString:@"SafeMode"])) {
            return INVALID;
        }
    }

    bool for_safe_mode = false;
    NSNumber *safe_mode_num = [filter objectForKey:@"SafeMode"];
    if (safe_mode_num) {
         if ([safe_mode_num isEqual:[NSNumber numberWithBool:true]])
            for_safe_mode = true;
         else if (![safe_mode_num isEqual:[NSNumber numberWithBool:false]])
            return INVALID;
    }
    /* in REALLY_SAFE mode, nothing gets loaded */
    if (for_safe_mode) {
        if (!_is_springboard || g_springboard_needs_safe != NEEDS_SAFE)
            return FAIL;
    } else {
        if (_is_springboard && g_springboard_needs_safe != NO_SAFE)
            return FAIL;
    }

    bool any = false;
    NSString *mode_str = [filter objectForKey:@"Mode"];
    if (mode_str) {
        any = [mode_str isEqual:@"Any"];
        if (!any && ![mode_str isEqual:@"All"])
            return INVALID;
    }

    xxpc_dictionary_set_bool(out_info, "any", any);

    /* First do the two we can test here. */

    NSArray *cfv = [filter objectForKey:@"CoreFoundationVersion"];
    if (cfv) {
        if (![cfv isKindOfClass:[NSArray class]] ||
            [cfv count] == 0 ||
            [cfv count] > 2)
            return INVALID;
        double version = kCFCoreFoundationVersionNumber;
        double minimum = id_to_double([cfv objectAtIndex:0]);
        if (minimum != minimum)
            return INVALID;
        if (version < minimum)
            return FAIL;
        id supremum_o = [cfv objectAtIndex:1];
        if (supremum_o) {
            double supremum = id_to_double(supremum_o);
            if (supremum != supremum)
                return INVALID;
            if (version >= supremum)
                return FAIL;
        }
    }

    NSArray *executables = [filter objectForKey:@"Executables"];
    if (executables) {
        if (![executables isKindOfClass:[NSArray class]])
            return INVALID;
        for (NSString *name in executables) {
            if (![name isKindOfClass:[NSString class]])
                return INVALID;
            if ([name isEqualToString:_argv0])
                goto ok;
        }
        if (any)
            return PROVISIONAL_PASS; /* without adding other conditions */
        else
            return FAIL;
    ok:;
    }

    /* Convert the rest to tests for bundle-loader. */

    struct {
        __unsafe_unretained NSString *key;
        const char *okey;
    } types[2] = {
        {@"Classes", "classes"},
        {@"Bundles", "bundles"},
    };

    for (int i = 0; i < 2; i++) {
        NSArray *things = [filter objectForKey:types[i].key];
        if (things) {
            if (![things isKindOfClass:[NSArray class]])
                return INVALID;
            xxpc_object_t out_things = xxpc_array_create(NULL, 0);
            for (NSString *name in things) {
                if (![name isKindOfClass:[NSString class]])
                    return INVALID;
                xxpc_array_append_value(out_things, nsstring_to_xpc(name));
            }
            xxpc_dictionary_set_value(out_info, types[i].okey, out_things);
        }
    }

    return PROVISIONAL_PASS;
}

- (void)updateSpringBoardNeedsSafeThen:(void (^)())then {
    xxpc_object_t inn = xxpc_dictionary_create(NULL, NULL, 0);
    xxpc_dictionary_set_string(inn, "com.ex.substitute.hook-operation",
                               "bundleid-to-fate");
    xxpc_dictionary_set_string(inn, "bundleid", "com.apple.SpringBoard");

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
                   ^{
        int to_set = REALLY_SAFE;
        xxpc_object_t out = NULL;
        vproc_swap_complex(NULL, 99999, inn, &out);
        if (!out) {
            NSLog(@"couldn't talk to launchd :( - assume worst case scenario");
            goto out;
        }
        __unsafe_unretained xxpc_type_t type = xxpc_get_type(out);
        if (xxpc_get_type(out) != XXPC_TYPE_DICTIONARY)
            goto bad_data;

        bool crashed;
        __unsafe_unretained xxpc_object_t fate =
            xxpc_dictionary_get_value(out, "fate");
        if (!fate) {
            /* no record yet */
            crashed = false;
        } else if (xxpc_get_type(fate) == XXPC_TYPE_INT64) {
            int stat = (int) xxpc_int64_get_value(fate);
            crashed = WIFSIGNALED(stat) && WTERMSIG(stat) != SIGTERM;
        } else {
            goto bad_data;
        }


        if (crashed) {
            if (g_springboard_needs_safe) {
                NSLog(@"SpringBoard crashed while in safe mode; using Really Safe Mode (no UI) next time :(");
                to_set = REALLY_SAFE;
            } else {
                NSLog(@"SpringBoard crashed; using safe mode next time.");
                to_set = NEEDS_SAFE;
            }
        } else {
            to_set = NO_SAFE;
        }
        goto out;

    bad_data:
        NSLog(@"bad data %@ from launchd!?", out);
        goto out;
    out:
        dispatch_async(dispatch_get_main_queue(), ^{
            g_springboard_needs_safe = to_set;
            then();
        });
    });
}

- (void)handleMessageHello:(NS_VALID_UNTIL_END_OF_SCOPE xxpc_object_t)request {
    NSString *sb_exe =
        @"/System/Library/CoreServices/SpringBoard.app/SpringBoard";

    if (_argv0 != NULL)
        goto bad;

    int64_t version = xxpc_dictionary_get_int64(request, "proto-version");
    if (version != 1) {
        /* in the future there will be a proper unloading mechanism, but here's
         * a bit of future proofing */
        NSLog(@"request received from wrong version of bundle-loader: %@", request);
        xxpc_connection_cancel(_connection);
    }

    const char *argv0 = xxpc_dictionary_get_string(request, "argv0");
    if (!argv0)
        goto bad;
    _argv0 = [NSString stringWithCString:argv0
                       encoding:NSUTF8StringEncoding];

    _is_springboard = [_argv0 isEqualToString:sb_exe];

    if (_is_springboard) {
        g_springboard_last_loaded_dylibs = g_springboard_loaded_dylibs;
        g_springboard_loaded_dylibs = _loaded_dylibs = [NSMutableSet set];
        save_state();

        [self updateSpringBoardNeedsSafeThen:^{
            [self handleMessageHelloRest:request];
        }];
    } else {
        [self handleMessageHelloRest:request];
    }
    return;

bad:
    [self handleBadMessage:request];
}

- (void)handleMessageHelloRest:(NS_VALID_UNTIL_END_OF_SCOPE
                                xxpc_object_t)request {

    xxpc_object_t bundles = xxpc_array_create(NULL, 0);

    NSError *err;
    NSString *base = @"/Library/Substitute/DynamicLibraries";
    NSArray *list = [[NSFileManager defaultManager]
                     contentsOfDirectoryAtPath:base
                     error:&err];

    for (NSString *dylib in list) {
        if (![[dylib pathExtension] isEqualToString:@"dylib"])
            continue;
        NSString *plist = [[dylib stringByDeletingPathExtension]
                           stringByAppendingPathExtension:@"plist"];
        NSString *full_plist = [base stringByAppendingPathComponent:plist];
        NSDictionary *plist_dict = [NSDictionary dictionaryWithContentsOfFile:
                                    full_plist];
        if (!plist_dict) {
            NSLog(@"missing, unreadable, or invalid plist '%@' for dylib '%@'; unlike Substrate, we require plists",
                  full_plist, dylib);
            continue;
        }

        xxpc_object_t info = xxpc_dictionary_create(NULL, NULL, 0);

        enum convert_filters_ret ret =
            [self convertFiltersForBundleInfo:plist_dict toXPCReply:info];
        if (ret == FAIL) {
            continue;
        } else if (ret == INVALID) {
            NSLog(@"bad data in plist '%@' for dylib '%@'", full_plist, dylib);
            continue;
        }

        NSString *dylib_path = [base stringByAppendingPathComponent:dylib];
        xxpc_dictionary_set_value(info, "dylib", nsstring_to_xpc(dylib_path));
        xxpc_array_append_value(bundles, info);
    }

    xxpc_object_t reply = xxpc_dictionary_create_reply(request);
    xxpc_dictionary_set_value(reply, "bundles", bundles);
    if (_is_springboard)
        xxpc_dictionary_set_bool(reply, "notify-me-of-add-remove", true);
    xxpc_connection_send_message(_connection, reply);
}

- (void)handleMessageAddRemove:(NS_VALID_UNTIL_END_OF_SCOPE
                                xxpc_object_t)request {
    bool is_add = xxpc_dictionary_get_bool(request, "is-add");
    const char *id_dylib = xxpc_dictionary_get_string(request, "id-dylib");
    if (!id_dylib || !_loaded_dylibs)
        return [self handleBadMessage:request];
    NSString *id_dylib_s = [NSString stringWithCString:id_dylib
                                     encoding:NSUTF8StringEncoding];
    if (is_add)
        [_loaded_dylibs addObject:id_dylib_s];
    else
        [_loaded_dylibs removeObject:id_dylib_s];
}

- (void)handleMessageSBFatalLoadedDylibs:(xxpc_object_t)request {
    /* This should probably be secured somehow... */
    NSLog(@"handleMessageSBFatalLoadedDylibs");
    NSSet *set = g_springboard_last_loaded_dylibs;
    xxpc_object_t reply = xxpc_dictionary_create_reply(request);
    if (set) {
        xxpc_object_t dylibs = xxpc_array_create(NULL, 0);
        xxpc_dictionary_set_value(reply, "dylibs", dylibs);
        for (NSString *dylib in set) {
            xxpc_array_set_string(dylibs, XXPC_ARRAY_APPEND,
                                  [dylib cStringUsingEncoding:
                                         NSUTF8StringEncoding]);

        }
    }
    xxpc_connection_send_message(_connection, reply);
}

- (void)handleBadMessage:(xxpc_object_t)request {
    NSLog(@"bad message received: %@", request);
    xxpc_connection_cancel(_connection);
}

- (void)handleMessage:(NS_VALID_UNTIL_END_OF_SCOPE xxpc_object_t)request {
    const char *type = xxpc_dictionary_get_string(request, "type");
    if (!type)
        goto bad;
    if (!strcmp(type, "hello"))
        return [self handleMessageHello:request];
    else if (!strcmp(type, "add-remove"))
        return [self handleMessageAddRemove:request];
    else if (!strcmp(type, "springboard-fatal-loaded-dylibs"))
        return [self handleMessageSBFatalLoadedDylibs:request];
    else
        goto bad;
bad:
    return [self handleBadMessage:request];
}

- (instancetype)initWithConnection:(xxpc_object_t)connection {
    _connection = connection;
    xxpc_connection_set_event_handler(connection, ^(xxpc_object_t event) {
        xxpc_type_t ty = xxpc_get_type(event);
        if (ty == XXPC_TYPE_DICTIONARY) {
            [self handleMessage:event];
        } else if (ty == XXPC_TYPE_ERROR) {
            if (event == XXPC_ERROR_CONNECTION_INVALID) {
                /* [self handleHangup]; */
            } else {
                NSLog(@"XPC error from connection: %@", event);
                xxpc_connection_cancel(connection);
            }
        } else {
            NSLog(@"unknown object received from XPC (peer): %@", event);
        }
    });
    xxpc_connection_resume(connection);
    return self;
}
@end

int main() {
    NSLog(@"hello from substituted");
    install_deadlock_warning();
    load_state();
    xxpc_connection_t listener = xxpc_connection_create_mach_service(
        "com.ex.substituted", dispatch_get_main_queue(),
        XXPC_CONNECTION_MACH_SERVICE_LISTENER);
    if (!listener) {
        NSLog(@"xxpc_connection_create_mach_service returned null");
        exit(1);
    }
    xxpc_connection_set_event_handler(listener, ^(xxpc_object_t object) {
        xxpc_type_t ty = xxpc_get_type(object);
        if (ty == XXPC_TYPE_CONNECTION) {
            (void) [[PeerHandler alloc] initWithConnection:object];
        } else if (ty == XXPC_TYPE_ERROR) {
            NSLog(@"XPC error in server: %@", object);
            exit(1);
        } else {
            NSLog(@"unknown object received from XPC: %@", object);
        }
    });
    xxpc_connection_resume(listener);
    [[NSRunLoop mainRunLoop] run];
}


================================================
FILE: darwin-bootstrap/unrestrict.c
================================================
/* This is an iOS executable spawned from posixspawn-hook.dylib, which accesses
 * a process and changes the name of any __RESTRICT,__restrict sections in the
 * main executable in memory.  Doing so prevents dyld from refusing to honor
 * DYLD_INSERT_LIBRARIES (which is a pretty dumb protection mechanism in the
 * first place, even on OS X).
 *
 * It exists as a separate executable because (a) such processes may be
 * launched with POSIX_SPAWN_SETEXEC, which makes posix_spawn act like exec and
 * replace the current process, and (b) if they're not, launchd/xpcproxy (into
 * which posixspawn-hook is injected) still can't task_for_pid the child
 * process itself, because they don't have the right entitlements.
*/

#define IB_LOG_NAME "unrestrict"
#include "ib-log.h"
#include "darwin/mach-decls.h"
#include "substitute.h"
#include "substitute-internal.h"
#include <stdlib.h>
#include <syslog.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <mach/mach.h>
#include <mach-o/loader.h>

#define PROC_PIDFDVNODEINFO 1
#define PROC_PIDFDVNODEINFO_SIZE 176
int proc_pidfdinfo(int, int, int, void *, int);

static bool unrestrict_macho_header(void *header, size_t size) {
    struct mach_header *mh = header;
    if (mh->magic != MH_MAGIC && mh->magic != MH_MAGIC_64) {
        ib_log("bad mach-o magic");
        return false;
    }
    bool did_modify = false;
    size_t off = mh->magic == MH_MAGIC_64 ? sizeof(struct mach_header_64)
                                          : sizeof(struct mach_header);
    for (uint32_t i = 0; i < mh->ncmds; i++) {
        if (off > size || size - off < sizeof(struct load_command))
            break; /* whatever */
        struct load_command *lc = header + off;
        if (lc->cmdsize > size - off)
            break;
        #define CASES(code...) \
            if (lc->cmd == LC_SEGMENT) { \
                typedef struct segment_command segment_command_y; \
                typedef struct section section_y; \
                code \
            } else if (lc->cmd == LC_SEGMENT_64) { \
                typedef struct segment_command_64 segment_command_y; \
                typedef struct section_64 section_y; \
                code \
            }
        CASES(
            segment_command_y *sc = (void *) lc;
            if (lc->cmdsize < sizeof(*sc) ||
                sc->nsects > (lc->cmdsize - sizeof(*sc)) / sizeof(struct section)) {
                ib_log("bad segment_command");
                return false;
            }
            if (!strncmp(sc->segname, "__RESTRICT", 16)) {
                section_y *sect = (void *) (sc + 1);
                for (uint32_t i = 0; i < sc->nsects; i++, sect++) {
                    if (!strncmp(sect->sectname, "__restrict", 16)) {
                        strcpy(sect->sectname, "\xf0\x9f\x92\xa9");
                        did_modify = true;
                    }
                }
            }
        )
        #undef CASES

        if (off + lc->cmdsize < off) {
            ib_log("overflowing lc->cmdsize");
            return false;
        }
        off += lc->cmdsize;
    }
    return did_modify;
}

static void unrestrict(task_t task) {
    vm_address_t header_addr = 0;
    kern_return_t kr;

    /* alrighty then, let's look at the damage.  find the first readable
     * segment */
setback:;
    mach_vm_address_t segm_addr = 0;
    mach_vm_size_t segm_size = 0;
    vm_region_basic_info_data_64_t info;
    mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
    mach_port_t object_name;
    while (1) {
        kr = mach_vm_region(task, &segm_addr, &segm_size, VM_REGION_BASIC_INFO_64,
                            (vm_region_info_t) &info, &info_count, &object_name);
        if (kr == KERN_INVALID_ADDRESS) {
            /* nothing! maybe it's not there *yet*? this actually is possible */
            usleep(10);
            goto setback;
        } else if (kr) {
            ib_log("mach_vm_region(%lx): %x", (long) segm_addr, kr);
            return;
        }
        if (info.protection)
            break;

        segm_addr++;
    }

    size_t toread = 0x4000;
    if (segm_size < toread)
        toread = segm_size;
    if ((kr = vm_allocate(mach_task_self(), &header_addr, toread,
                          VM_FLAGS_ANYWHERE))) {
        ib_log("vm_allocate(%zx): %x", toread, kr);
        return;
    }
    mach_vm_size_t actual = toread;
    kr = mach_vm_read_overwrite(task, segm_addr, toread, header_addr,
                                &actual);
    if (kr || actual != toread) {
        ib_log("mach_vm_read_overwrite: %x", kr);
        return;
    }

    bool did_modify = unrestrict_macho_header((void *) header_addr, toread);

    if (did_modify) {
        if ((kr = vm_protect(mach_task_self(), header_addr, toread,
                             FALSE, info.protection))) {
            ib_log("vm_protect(%lx=>%d): %x",
                   (long) header_addr, info.protection, kr);
            return;
        }
        vm_prot_t cur, max;
        if ((kr = mach_vm_remap(task, &segm_addr, toread, 0,
                                VM_FLAGS_OVERWRITE,
                                mach_task_self(), header_addr, FALSE,
                                &cur, &max, info.inheritance))) {
            ib_log("mach_vm_remap(%lx=>%lx size=%zx): %x",
                   (long) header_addr, (long) segm_addr, toread, kr);
            return;
        }
    }
}



int main(int argc, char **argv) {
    if (argc != 4) {
        ib_log("wrong number of args");
        return 1;
    }

    const char *pids = argv[1];
    char *end;
    long pid = strtol(pids, &end, 10);
    if (!pids[0] || *end) {
        ib_log("pid not an integer");
        return 1;
    }

    const char *should_resume = argv[2];
    if (strcmp(should_resume, "0") && strcmp(should_resume, "1")) {
        ib_log("should_resume not 0 or 1");
        return 1;
    }

    const char *is_exec = argv[3];
    if (strcmp(is_exec, "0") && strcmp(is_exec, "1")) {
        ib_log("is_exec not 0 or 1");
        return 1;
    }

    /* double fork to avoid zombies */
    int ret = fork();
    if (ret == -1) {
        ib_log("fork: %s", strerror(errno));
        return 1;
    } else if (ret) {
        return 0;
    }

    if (IB_VERBOSE) {
        ib_log("unrestricting %ld (sr=%s, ie=%s)", pid,
               should_resume, is_exec);
    }

    int rv = 1;

    int retries = 0;
    int wait_us = 50;
    mach_port_t task;
    while (1) {
        kern_return_t kr = task_for_pid(mach_task_self(), (pid_t) pid, &task);
        if (kr) {
            /* If we're still in the old task (specifically, exec_handle_sugid
             * has not been called yet), task_for_pid will fail because
             * xpcproxy changed uid since it started, and for some dumb reason
             * debugging any such processes is prohibited by
             * task_for_pid_posix_check. */
            if (retries++ == 20) {
                ib_log("TFP still failing (%d) after 20 retries", kr);
                goto fail;
            }
        } else {
            if (is_exec[0] != '1') {
                break;
            } else {
                /* The process might not have transitioned yet.  We set up a
                 * dummy fd 255 in the parent process which was marked CLOEXEC,
                 * so test if that still exists.  AFAICT, Substrate's
                 * equivalent to this is not actually correct.
                 * (I don't think the task_for_pid failure check is sufficient,
                 * as P_SUGID is only set if the credential actually changed.)
                 */
                char buf[PROC_PIDFDVNODEINFO_SIZE];
                /* A bug in proc_pidfdinfo makes it never return -1.  Yuck. */
                errno = 0;
                proc_pidfdinfo(pid, 255, PROC_PIDFDVNODEINFO, buf, sizeof(buf));
                if (errno == EBADF) {
                    break;
                } else if (errno) {
                    ib_log("proc_pidfdinfo: %s", strerror(errno));
                    goto fail;
                }

                if (retries++ == 20) {
                    ib_log("still in parent process after 20 retries");
                    goto fail;
                }
            }
        }
        /* ok, just retry */
        wait_us *= 2;
        if (wait_us > 200000)
            wait_us = 200000;
        while (usleep(wait_us))
            ;
    }

    if (IB_VERBOSE && retries > 0)
        ib_log("note: ready after %d retries", retries);

    unrestrict(task);

    rv = 0;
fail:
    if (should_resume[0] == '1') {
        if ((kill(pid, SIGCONT))) {
            ib_log("kill SIGCONT: %s", strerror(errno));
            return 1;
        }
    }

    return rv;
}


================================================
FILE: doc/installed-README.txt
================================================
todo


================================================
FILE: ent.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>platform-application</key>
	<true/>
	<key>get-task-allow</key>
	<true/>
	<key>com.apple.system-task-ports</key>
	<true/>
	<key>task_for_pid-allow</key>
	<true/>
	<key>run-unsigned-code</key>
	<true/>
</dict>
</plist>


================================================
FILE: generated/darwin-inject-asm.S
================================================
/* Generated by script/gen-inject-asm.sh.  The relevant source is in-tree (make
 * out/darwin-inject-asm.S), but this file has been checked in too, in case
 * your C compiler doesn't support all of these architectures.
 * This file contains code for 4 architectures in one text page; it's remapped
 * into the target process and the appropriate thunk executed.  Having ARM code
 * here on x86 and whatnot is currently pointless (and use of that code is
 * disabled in case any future Rosetta-like emulator breaks naive attempts to
 * inject into foreign-architecture processes), but we need two architectures
 * anyway, so the rest are included in case doing so is useful someday. */
.align 14
.private_extern _inject_page_start
_inject_page_start:
.align 2
.private_extern _inject_start_x86_64
_inject_start_x86_64:
.byte 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x30, 0x48, 0x8d, 0x45, 0xf4, 0x31, 0xc9, 0x89, 0xce, 0x48, 0x8d, 0x15, 0x99, 0x00, 0x00, 0x00, 0x48, 0x89, 0x7d, 0xf8, 0xc7, 0x45, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x7d, 0xf8, 0x48, 0x8b, 0x3f, 0x4c, 0x8b, 0x45, 0xf8, 0x48, 0x89, 0x7d, 0xe8, 0x48, 0x89, 0xc7, 0x4c, 0x89, 0xc1, 0x48, 0x8b, 0x45, 0xe8, 0xff, 0xd0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0x8b, 0x49, 0x08, 0x8b, 0x7d, 0xf4, 0x89, 0x45, 0xe4, 0xff, 0xd1, 0x31, 0xff, 0x89, 0xf9, 0x31, 0xd2, 0x48, 0x8b, 0x75, 0xf8, 0x48, 0x8b, 0x76, 0x30, 0x89, 0xf7, 0x89, 0x7d, 0xe0, 0x48, 0x89, 0xcf, 0x48, 0x89, 0xce, 0x8b, 0x4d, 0xe0, 0x89, 0x45, 0xdc, 0xe8, 0x1f, 0x00, 0x00, 0x00, 0xb9, 0xad, 0x0b, 0x00, 0x00, 0x89, 0xce, 0x89, 0x45, 0xd8, 0xb0, 0x00, 0xff, 0xd6, 0x48, 0x83, 0xc4, 0x30, 0x5d, 0xc3, 0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x69, 0x01, 0x00, 0x02, 0x49, 0x89, 0xca, 0x0f, 0x05, 0xc3, 0xb8, 0x24, 0x00, 0x00, 0x01, 0x49, 0x89, 0xca, 0x0f, 0x05, 0xc3, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x40, 0x31, 0xf6, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x8b, 0x7d, 0xf8, 0x48, 0x89, 0x7d, 0xf0, 0x48, 0x8b, 0x7d, 0xf0, 0x48, 0x8b, 0x7f, 0x10, 0x48, 0x8b, 0x45, 0xf0, 0x48, 0x8b, 0x40, 0x28, 0x48, 0x89, 0x7d, 0xd0, 0x48, 0x89, 0xc7, 0x48, 0x8b, 0x45, 0xd0, 0xff, 0xd0, 0x48, 0x89, 0x45, 0xe8, 0x48, 0x81, 0x7d, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x84, 0x48, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x35, 0x80, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x45, 0xf0, 0x48, 0x8b, 0x40, 0x18, 0x48, 0x8b, 0x7d, 0xe8, 0xff, 0xd0, 0x48, 0x89, 0x45, 0xe0, 0x48, 0x81, 0x7d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x84, 0x1c, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x45, 0xe0, 0x48, 0x8b, 0x4d, 0xf0, 0x48, 0x81, 0xc1, 0x40, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x72, 0x38, 0x48, 0x89, 0xcf, 0xff, 0xd0, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x45, 0xf0, 0x48, 0x8b, 0x40, 0x30, 0x89, 0xc1, 0x89, 0xcf, 0xe8, 0x4d, 0xff, 0xff, 0xff, 0xb9, 0x00, 0x20, 0x00, 0x00, 0x89, 0xce, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x81, 0xe2, 0x00, 0xf0, 0xff, 0xff, 0x48, 0x89, 0x55, 0xd8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x7d, 0xd8, 0x89, 0x45, 0xcc, 0xff, 0xd2, 0x48, 0x83, 0xc4, 0x40, 0x5d, 0xc3, 0x90, 0x73, 0x75, 0x62, 0x73, 0x74, 0x69, 0x74, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00
.align 2
.private_extern _inject_start_i386
_inject_start_i386:
.byte 0x55, 0x89, 0xe5, 0x57, 0x56, 0x83, 0xec, 0x30, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x58, 0x8d, 0x55, 0xf0, 0x31, 0xf6, 0x8d, 0x80, 0xf3, 0x00, 0x00, 0x00, 0x89, 0x4d, 0xf4, 0xc7, 0x45, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x4d, 0xf4, 0x8b, 0x09, 0x8b, 0x7d, 0xf4, 0x89, 0x14, 0x24, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0x89, 0x44, 0x24, 0x08, 0x89, 0x7c, 0x24, 0x0c, 0x89, 0x75, 0xec, 0xff, 0xd1, 0x8b, 0x4d, 0xf4, 0x8b, 0x49, 0x04, 0x8b, 0x55, 0xf0, 0x89, 0x14, 0x24, 0x89, 0x45, 0xe8, 0xff, 0xd1, 0x31, 0xc9, 0x8b, 0x55, 0xf4, 0x8b, 0x52, 0x18, 0xc7, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x08, 0x00, 0x00, 0x00, 0x00, 0x89, 0x54, 0x24, 0x0c, 0x89, 0x45, 0xe4, 0x89, 0x4d, 0xe0, 0xe8, 0x1e, 0x00, 0x00, 0x00, 0xb9, 0xad, 0x0b, 0x00, 0x00, 0x89, 0x45, 0xdc, 0xff, 0xd1, 0x83, 0xc4, 0x30, 0x5e, 0x5f, 0x5d, 0xc3, 0x66, 0x66, 0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x69, 0x01, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x81, 0xc2, 0x0b, 0x00, 0x00, 0x00, 0x89, 0xe1, 0x0f, 0x34, 0xc3, 0xb8, 0xdc, 0xff, 0xff, 0xff, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x81, 0xc2, 0x0b, 0x00, 0x00, 0x00, 0x89, 0xe1, 0x0f, 0x34, 0xc3, 0x55, 0x89, 0xe5, 0x81, 0xed, 0x00, 0x04, 0x00, 0x00, 0x8b, 0x54, 0x24, 0x08, 0x8b, 0x42, 0x10, 0x81, 0xe2, 0x00, 0xf0, 0xff, 0xff, 0x89, 0x55, 0x08, 0xc7, 0x45, 0x0c, 0x00, 0x20, 0x00, 0x00, 0x83, 0xc0, 0x03, 0xff, 0xe0, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x89, 0xe5, 0x56, 0x83, 0xec, 0x24, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x58, 0x8b, 0x4d, 0x08, 0x31, 0xd2, 0x89, 0x4d, 0xf8, 0x8b, 0x4d, 0xf8, 0x89, 0x4d, 0xf4, 0x8b, 0x4d, 0xf4, 0x8b, 0x49, 0x08, 0x8b, 0x75, 0xf4, 0x8b, 0x76, 0x14, 0x89, 0x34, 0x24, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0x89, 0x45, 0xe8, 0x89, 0x55, 0xe4, 0xff, 0xd1, 0x89, 0x45, 0xf0, 0x81, 0x7d, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x84, 0x4b, 0x00, 0x00, 0x00, 0x8b, 0x45, 0xe8, 0x8d, 0x88, 0xac, 0x00, 0x00, 0x00, 0x8b, 0x55, 0xf4, 0x8b, 0x52, 0x0c, 0x8b, 0x75, 0xf0, 0x89, 0x34, 0x24, 0x89, 0x4c, 0x24, 0x04, 0xff, 0xd2, 0x89, 0x45, 0xec, 0x81, 0x7d, 0xec, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x84, 0x1b, 0x00, 0x00, 0x00, 0x8b, 0x45, 0xec, 0x8b, 0x4d, 0xf4, 0x81, 0xc1, 0x20, 0x00, 0x00, 0x00, 0x8b, 0x55, 0xf4, 0x8b, 0x52, 0x1c, 0x89, 0x0c, 0x24, 0x89, 0x54, 0x24, 0x04, 0xff, 0xd0, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x45, 0xf4, 0x8b, 0x40, 0x18, 0x89, 0x04, 0x24, 0xe8, 0x13, 0xff, 0xff, 0xff, 0x8b, 0x4d, 0xf4, 0x89, 0x0c, 0x24, 0x89, 0x45, 0xe0, 0xe8, 0x1b, 0xff, 0xff, 0xff, 0x83, 0xc4, 0x24, 0x5e, 0x5d, 0xc3, 0x90, 0x73, 0x75, 0x62, 0x73, 0x74, 0x69, 0x74, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00
.align 2
.private_extern _inject_start_arm
_inject_start_arm:
.byte 0x80, 0xb5, 0x6f, 0x46, 0x87, 0xb0, 0x05, 0xa9, 0x00, 0x22, 0x40, 0xf2, 0x6f, 0x03, 0xc0, 0xf2, 0x00, 0x03, 0x7b, 0x44, 0x06, 0x90, 0x05, 0x92, 0x06, 0x98, 0x00, 0x68, 0xdd, 0xf8, 0x18, 0x90, 0x04, 0x90, 0x08, 0x46, 0x11, 0x46, 0x1a, 0x46, 0x4b, 0x46, 0xdd, 0xf8, 0x10, 0x90, 0xc8, 0x47, 0x06, 0x99, 0x49, 0x68, 0x05, 0x9a, 0x03, 0x90, 0x10, 0x46, 0x88, 0x47, 0x00, 0x21, 0x06, 0x9a, 0x93, 0x69, 0x02, 0x90, 0x08, 0x46, 0x01, 0x91, 0x01, 0x9a, 0x00, 0xf0, 0x07, 0xf8, 0x40, 0xf6, 0xad, 0x31, 0x00, 0x90, 0x88, 0x47, 0x07, 0xb0, 0x80, 0xbd, 0x00, 0xbf, 0xec, 0x46, 0x70, 0xb4, 0x9c, 0xe8, 0x70, 0x00, 0x40, 0xf2, 0x69, 0x1c, 0x80, 0xdf, 0x70, 0xbc, 0x70, 0x47, 0x00, 0xbf, 0xec, 0x46, 0x70, 0xb4, 0x9c, 0xe8, 0x70, 0x00, 0x6f, 0xf0, 0x23, 0x0c, 0x80, 0xdf, 0x70, 0xbc, 0x70, 0x47, 0x00, 0xbf, 0x80, 0xb5, 0x6f, 0x46, 0x89, 0xb0, 0x00, 0x21, 0x08, 0x90, 0x08, 0x98, 0x07, 0x90, 0x07, 0x98, 0x80, 0x68, 0x07, 0x9a, 0x52, 0x69, 0x03, 0x90, 0x10, 0x46, 0x03, 0x9a, 0x90, 0x47, 0x00, 0x21, 0x06, 0x90, 0x06, 0x98, 0x88, 0x42, 0x1b, 0xd0, 0x40, 0xf2, 0x58, 0x01, 0xc0, 0xf2, 0x00, 0x01, 0x79, 0x44, 0x07, 0x98, 0xc0, 0x68, 0x06, 0x9a, 0x02, 0x90, 0x10, 0x46, 0x02, 0x9a, 0x90, 0x47, 0x00, 0x21, 0x05, 0x90, 0x05, 0x98, 0x88, 0x42, 0x09, 0xd0, 0x05, 0x98, 0x07, 0x99, 0x20, 0x31, 0x07, 0x9a, 0xd2, 0x69, 0x01, 0x90, 0x08, 0x46, 0x11, 0x46, 0x01, 0x9a, 0x90, 0x47, 0xff, 0xe7, 0x07, 0x98, 0x80, 0x69, 0xff, 0xf7, 0xc2, 0xff, 0x42, 0xf2, 0x00, 0x01, 0x07, 0x9a, 0x4f, 0xf2, 0x00, 0x03, 0xcf, 0xf6, 0xff, 0x73, 0x1a, 0x40, 0x04, 0x92, 0x07, 0x9a, 0x12, 0x69, 0x04, 0x9b, 0x00, 0x90, 0x18, 0x46, 0x90, 0x47, 0x09, 0xb0, 0x80, 0xbd, 0x00, 0xbf, 0x73, 0x75, 0x62, 0x73, 0x74, 0x69, 0x74, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00
.align 2
.private_extern _inject_start_arm64
_inject_start_arm64:
.byte 0xf4, 0x4f, 0xbe, 0xa9, 0xfd, 0x7b, 0x01, 0xa9, 0xfd, 0x43, 0x00, 0x91, 0xff, 0x43, 0x00, 0xd1, 0xf3, 0x03, 0x00, 0xaa, 0xff, 0x0f, 0x00, 0xb9, 0x68, 0x02, 0x40, 0xf9, 0x42, 0x03, 0x00, 0x10, 0x1f, 0x20, 0x03, 0xd5, 0xe0, 0x33, 0x00, 0x91, 0x01, 0x00, 0x80, 0xd2, 0xe3, 0x03, 0x13, 0xaa, 0x00, 0x01, 0x3f, 0xd6, 0x68, 0x06, 0x40, 0xf9, 0xe0, 0x0f, 0x40, 0xb9, 0x00, 0x01, 0x3f, 0xd6, 0x63, 0x32, 0x40, 0xb9, 0x00, 0x00, 0x80, 0xd2, 0x01, 0x00, 0x80, 0xd2, 0x02, 0x00, 0x80, 0x52, 0x07, 0x00, 0x00, 0x94, 0xa8, 0x75, 0x81, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0xbf, 0x43, 0x00, 0xd1, 0xfd, 0x7b, 0x41, 0xa9, 0xf4, 0x4f, 0xc2, 0xa8, 0xc0, 0x03, 0x5f, 0xd6, 0x30, 0x2d, 0x80, 0xd2, 0x01, 0x10, 0x00, 0xd4, 0xc0, 0x03, 0x5f, 0xd6, 0x70, 0x04, 0x80, 0x92, 0x01, 0x10, 0x00, 0xd4, 0xc0, 0x03, 0x5f, 0xd6, 0xf4, 0x4f, 0xbe, 0xa9, 0xfd, 0x7b, 0x01, 0xa9, 0xfd, 0x43, 0x00, 0x91, 0xf3, 0x03, 0x00, 0xaa, 0x68, 0x0a, 0x40, 0xf9, 0x60, 0x16, 0x40, 0xf9, 0x01, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x40, 0x01, 0x00, 0xb4, 0x68, 0x0e, 0x40, 0xf9, 0x01, 0x02, 0x00, 0x10, 0x1f, 0x20, 0x03, 0xd5, 0x00, 0x01, 0x3f, 0xd6, 0xe8, 0x03, 0x00, 0xaa, 0x88, 0x00, 0x00, 0xb4, 0x60, 0x02, 0x01, 0x91, 0x61, 0x1e, 0x40, 0xf9, 0x00, 0x01, 0x3f, 0xd6, 0x60, 0x32, 0x40, 0xb9, 0xea, 0xff, 0xff, 0x97, 0x60, 0xc6, 0x72, 0x92, 0x62, 0x12, 0x40, 0xf9, 0xe1, 0x03, 0x11, 0x32, 0xfd, 0x7b, 0x41, 0xa9, 0xf4, 0x4f, 0xc2, 0xa8, 0x40, 0x00, 0x1f, 0xd6, 0x73, 0x75, 0x62, 0x73, 0x74, 0x69, 0x74, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00


================================================
FILE: generated/generic-dis-arm.inc.h
================================================
/* Generated code; do not edit!
   generated by tables/gen.js from imaon2 'f0e220720bbfb8f8e00e76af56806a28fc8739a2'
   https://github.com/comex/imaon2
   arguments: '--gen-hook-disassembler -n _arm --dis-pattern=P(XXX) out/out-ARM.json'
   (fair warning: at present the main (Rust) code in that repository is barely
    started, embarrassingly so; no need to look at it ;p)
   In case it's copyrightable in any way, consider the generated code in the
   public domain.
*/

/* GPR_Rn_unk_Rd_1_ADDri: ADDri */
/* GPR_Rn_GPR_Rm_unk_Rd_1_ADDrr: ADDrr */
/* GPR_Rn_so_reg_imm_shift_unk_Rd_1_ADDrsi: ADDrsi */
/* GPR_Rn_so_reg_reg_shift_unk_Rd_1_ADDrsr: ADDrsr */
/* adrlabel_label_unk_Rd_1_ADR: ADR */
/* bl_target_func_2_BL: BL, BL_pred */
/* GPR_func_3_BLX: BLX, BLX_pred, BXJ */
/* GPR_dst_B_2_BX: BX, BX_pred */
/* br_target_target_pred_p_B_1_Bcc: Bcc */
/* addr_offset_none_addr_unk_Rt_13_LDA: LDA, LDAB, LDAEX, LDAEXB, LDAEXD, LDAEXH, LDAH, LDREX, LDREXB, LDREXD, LDREXH, SWP, SWPB */
/* addrmode5_addr_8_LDC2L_OFFSET: LDC2L_OFFSET, LDC2_OFFSET, LDCL_OFFSET, LDC_OFFSET, VLDRD, VLDRS, VSTRD, VSTRS */
/* addr_offset_none_addr_4_LDC2L_OPTION: LDC2L_OPTION, LDC2_OPTION, LDCL_OPTION, LDC_OPTION */
/* addr_offset_none_addr_postidx_imm8s4_offset_4_LDC2L_POST: LDC2L_POST, LDC2_POST, LDCL_POST, LDC_POST */
/* addrmode5_pre_addr_4_LDC2L_PRE: LDC2L_PRE, LDC2_PRE, LDCL_PRE, LDC_PRE */
/* GPR_Rn_reglist_regs_16_LDMDA: LDMDA, LDMDA_UPD, LDMDB, LDMDB_UPD, LDMIA, LDMIA_UPD, LDMIB, LDMIB_UPD, sysLDMDA, sysLDMDA_UPD, sysLDMDB, sysLDMDB_UPD, sysLDMIA, sysLDMIA_UPD, sysLDMIB, sysLDMIB_UPD */
/* addr_offset_none_addr_am2offset_imm_offset_unk_Rt_4_LDRBT_POST_IMM: LDRBT_POST_IMM, LDRB_POST_IMM, LDRT_POST_IMM, LDR_POST_IMM */
/* addr_offset_none_addr_am2offset_reg_offset_unk_Rt_4_LDRBT_POST_REG: LDRBT_POST_REG, LDRB_POST_REG, LDRT_POST_REG, LDR_POST_REG */
/* addrmode_imm12_pre_addr_unk_Rt_2_LDRB_PRE_IMM: LDRB_PRE_IMM, LDR_PRE_IMM */
/* ldst_so_reg_addr_unk_Rt_2_LDRB_PRE_REG: LDRB_PRE_REG, LDR_PRE_REG */
/* addrmode_imm12_addr_unk_Rt_2_LDRBi12: LDRBi12, LDRi12 */
/* ldst_so_reg_shift_unk_Rt_2_LDRBrs: LDRBrs, LDRrs */
/* addrmode3_addr_unk_Rt_4_LDRD: LDRD, LDRH, LDRSB, LDRSH */
/* addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST: LDRD_POST, LDRH_POST, LDRSB_POST, LDRSH_POST */
/* addrmode3_pre_addr_unk_Rt_4_LDRD_PRE: LDRD_PRE, LDRH_PRE, LDRSB_PRE, LDRSH_PRE */
/* addr_offset_none_addr_postidx_imm8_offset_unk_Rt_3_LDRHTi: LDRHTi, LDRSBTi, LDRSHTi */
/* addr_offset_none_addr_postidx_reg_Rm_unk_Rt_3_LDRHTr: LDRHTr, LDRSBTr, LDRSHTr */
/* GPR_Rt_4_MCR: MCR, MCR2, VMOVDRR, VMOVSR */
/* GPRnopc_Rt_4_MCRR: MCRR, MCRR2, MRRC, MRRC2 */
/* unk_Rd_5_MOVTi16: MOVTi16, MOVi, MOVi16, MOVsi, MOVsr */
/* GPR_Rm_unk_Rd_1_MOVr: MOVr */
/* tcGPR_Rm_unk_Rd_1_MOVr_TC: MOVr_TC */
/* unk_Rt_13_MRC: MRC, MRC2, VMOVRRD, VMOVRRS, VMOVRS, VMRS, VMRS_FPEXC, VMRS_FPINST, VMRS_FPINST2, VMRS_FPSID, VMRS_MVFR0, VMRS_MVFR1, VMRS_MVFR2 */
/* addrmode5_addr_S_4_STC2L_OFFSET: STC2L_OFFSET, STC2_OFFSET, STCL_OFFSET, STC_OFFSET */
/* addr_offset_none_addr_S_4_STC2L_OPTION: STC2L_OPTION, STC2_OPTION, STCL_OPTION, STC_OPTION */
/* addr_offset_none_addr_postidx_imm8s4_offset_S_4_STC2L_POST: STC2L_POST, STC2_POST, STCL_POST, STC_POST */
/* addrmode5_pre_addr_S_4_STC2L_PRE: STC2L_PRE, STC2_PRE, STCL_PRE, STC_PRE */
/* GPR_Rt_addr_offset_none_addr_S_3_STL: STL, STLB, STLH */
/* GPR_Rt_addr_offset_none_addr_unk_Rd_S_6_STLEX: STLEX, STLEXB, STLEXH, STREX, STREXB, STREXH */
/* GPRPairOp_Rt_addr_offset_none_addr_unk_Rd_S_2_STLEXD: STLEXD, STREXD */
/* GPR_Rn_reglist_regs_S_16_STMDA: STMDA, STMDA_UPD, STMDB, STMDB_UPD, STMIA, STMIA_UPD, STMIB, STMIB_UPD, sysSTMDA, sysSTMDA_UPD, sysSTMDB, sysSTMDB_UPD, sysSTMIA, sysSTMIA_UPD, sysSTMIB, sysSTMIB_UPD */
/* GPR_Rt_addr_offset_none_addr_am2offset_imm_offset_S_4_STRBT_POST_IMM: STRBT_POST_IMM, STRB_POST_IMM, STRT_POST_IMM, STR_POST_IMM */
/* GPR_Rt_addr_offset_none_addr_am2offset_reg_offset_S_4_STRBT_POST_REG: STRBT_POST_REG, STRB_POST_REG, STRT_POST_REG, STR_POST_REG */
/* GPR_Rt_addrmode_imm12_pre_addr_S_2_STRB_PRE_IMM: STRB_PRE_IMM, STR_PRE_IMM */
/* GPR_Rt_ldst_so_reg_addr_S_2_STRB_PRE_REG: STRB_PRE_REG, STR_PRE_REG */
/* GPRnopc_Rt_addrmode_imm12_addr_S_1_STRBi12: STRBi12 */
/* GPRnopc_Rt_ldst_so_reg_shift_S_1_STRBrs: STRBrs */
/* GPR_Rt_addrmode3_addr_S_2_STRD: STRD, STRH */
/* GPR_Rt_addr_offset_none_addr_am3offset_offset_S_2_STRD_POST: STRD_POST, STRH_POST */
/* GPR_Rt_addrmode3_pre_addr_S_2_STRD_PRE: STRD_PRE, STRH_PRE */
/* GPR_Rt_addr_offset_none_addr_postidx_imm8_offset_S_1_STRHTi: STRHTi */
/* GPR_Rt_addr_offset_none_addr_postidx_reg_Rm_S_1_STRHTr: STRHTr */
/* GPR_Rt_addrmode_imm12_addr_S_1_STRi12: STRi12 */
/* GPR_Rt_ldst_so_reg_shift_S_1_STRrs: STRrs */
    switch ((op >> 20) & 0x1f) {
    case 0: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            switch ((op >> 5) & 0x1) {
            case 0: {
                if ((op & 0xf2000f0) == 0xd0) {
                    insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST:;
                    struct bitslice offset = {.nruns = 4, .runs = (struct bitslice_run[]) {{0,0,4}, {8,4,4}, {22,9,1}, {23,8,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                    return P(addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST)(ctx, offset, Rt, addr); /* 0x000000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xf3000b0) == 0xb0) {
                    insn_GPR_Rt_addr_offset_none_addr_am3offset_offset_S_2_STRD_POST:;
                    struct bitslice offset = {.nruns = 4, .runs = (struct bitslice_run[]) {{0,0,4}, {8,4,4}, {22,9,1}, {23,8,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                    return P(GPR_Rt_addr_offset_none_addr_am3offset_offset_S_2_STRD_POST)(ctx, offset, Rt, addr); /* 0x000000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            if ((op & 0xf3f0000) == 0x20f0000) {
                insn_adrlabel_label_unk_Rd_1_ADR:;
                struct bitslice label = {.nruns = 2, .runs = (struct bitslice_run[]) {{0,0,12}, {22,12,2}}};
                struct bitslice Rd = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                return P(adrlabel_label_unk_Rd_1_ADR)(ctx, label, Rd); /* 0x020f0000 | 0xf0c0ffff */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 2: {
            insn_GPR_Rt_addr_offset_none_addr_am2offset_imm_offset_S_4_STRBT_POST_IMM:;
            struct bitslice offset = {.nruns = 2, .runs = (struct bitslice_run[]) {{0,0,12}, {23,12,1}}};
            struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
            struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
            return P(GPR_Rt_addr_offset_none_addr_am2offset_imm_offset_S_4_STRBT_POST_IMM)(ctx, offset, Rt, addr); /* 0x04000000 | 0xf0efffff */
        }
        case 3: {
            if ((op & 0xf100010) == 0x6000000) {
                insn_GPR_Rt_addr_offset_none_addr_am2offset_reg_offset_S_4_STRBT_POST_REG:;
                struct bitslice offset = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,4}, {5,5,7}, {23,12,1}}};
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                return P(GPR_Rt_addr_offset_none_addr_am2offset_reg_offset_S_4_STRBT_POST_REG)(ctx, offset, Rt, addr); /* 0x06000000 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4: {
            insn_GPR_Rn_reglist_regs_S_16_STMDA:;
            struct bitslice regs = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,16}}};
            struct bitslice Rn = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
            return P(GPR_Rn_reglist_regs_S_16_STMDA)(ctx, regs, Rn); /* 0x08000000 | 0xf1efffff */
        }
        case 5: {
            insn_br_target_target_pred_p_B_1_Bcc:;
            struct bitslice target = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,24}}};
            struct bitslice p = {.nruns = 1, .runs = (struct bitslice_run[]) {{28,0,4}}};
            return P(br_target_target_pred_p_B_1_Bcc)(ctx, target, p); /* 0x0a000000 | 0xf0ffffff */
        }
        case 6:
            return P(unidentified)(ctx);
        case 7: {
            if ((op & 0xf100010) == 0xe000010) {
                insn_GPR_Rt_4_MCR:;
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                return P(GPR_Rt_4_MCR)(ctx, Rt); /* 0x0e000010 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 1: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0x1) {
            case 0: {
                if ((op & 0xf2000f0) == 0xd0) {
                    goto insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST; /* 0x000000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xf3000b0) == 0x1000b0) {
                    goto insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST; /* 0x001000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0: {
                insn_addr_offset_none_addr_am2offset_imm_offset_unk_Rt_4_LDRBT_POST_IMM:;
                struct bitslice offset = {.nruns = 2, .runs = (struct bitslice_run[]) {{0,0,12}, {23,12,1}}};
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                return P(addr_offset_none_addr_am2offset_imm_offset_unk_Rt_4_LDRBT_POST_IMM)(ctx, offset, Rt, addr); /* 0x04100000 | 0xf0efffff */
            }
            case 1: {
                if ((op & 0xf100010) == 0x6100000) {
                    insn_addr_offset_none_addr_am2offset_reg_offset_unk_Rt_4_LDRBT_POST_REG:;
                    struct bitslice offset = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,4}, {5,5,7}, {23,12,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                    return P(addr_offset_none_addr_am2offset_reg_offset_unk_Rt_4_LDRBT_POST_REG)(ctx, offset, Rt, addr); /* 0x06100000 | 0xf0efffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0: {
                insn_GPR_Rn_reglist_regs_16_LDMDA:;
                struct bitslice regs = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,16}}};
                struct bitslice Rn = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                return P(GPR_Rn_reglist_regs_16_LDMDA)(ctx, regs, Rn); /* 0x08100000 | 0xf1efffff */
            }
            case 1:
                goto insn_br_target_target_pred_p_B_1_Bcc; /* 0x0a000000 | 0xf0ffffff */
            }
        }
        case 3: {
            if ((op & 0xf100010) == 0xe100010) {
                insn_unk_Rt_13_MRC:;
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                return P(unk_Rt_13_MRC)(ctx, Rt); /* 0x0e100010 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 2:
    case 10: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            if ((op & 0xf700ff0) == 0x2000b0) {
                struct bitslice Rm = {.nruns = 2, .runs = (struct bitslice_run[]) {{0,0,4}, {23,4,1}}};
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                return P(GPR_Rt_addr_offset_none_addr_postidx_reg_Rm_S_1_STRHTr)(ctx, Rm, Rt, addr); /* 0x002000b0 | 0xf08ff00f */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 1:
            return P(unidentified)(ctx);
        case 2:
            goto insn_GPR_Rt_addr_offset_none_addr_am2offset_imm_offset_S_4_STRBT_POST_IMM; /* 0x04000000 | 0xf0efffff */
        case 3: {
            if ((op & 0xf100010) == 0x6000000) {
                goto insn_GPR_Rt_addr_offset_none_addr_am2offset_reg_offset_S_4_STRBT_POST_REG; /* 0x06000000 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4:
            goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
        case 5:
            goto insn_br_target_target_pred_p_B_1_Bcc; /* 0x0a000000 | 0xf0ffffff */
        case 6: {
            insn_addr_offset_none_addr_postidx_imm8s4_offset_S_4_STC2L_POST:;
            struct bitslice offset = {.nruns = 2, .runs = (struct bitslice_run[]) {{0,0,8}, {23,8,1}}};
            struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
            return P(addr_offset_none_addr_postidx_imm8s4_offset_S_4_STC2L_POST)(ctx, offset, addr); /* 0x0c200000 | 0xf0cfffff */
        }
        case 7: {
            if ((op & 0xf100010) == 0xe000010) {
                goto insn_GPR_Rt_4_MCR; /* 0x0e000010 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 3:
    case 11: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0x1) {
            case 0: {
                if ((op & 0xf700ff0) == 0x3000d0) {
                    insn_addr_offset_none_addr_postidx_reg_Rm_unk_Rt_3_LDRHTr:;
                    struct bitslice Rm = {.nruns = 2, .runs = (struct bitslice_run[]) {{0,0,4}, {23,4,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                    return P(addr_offset_none_addr_postidx_reg_Rm_unk_Rt_3_LDRHTr)(ctx, Rm, Rt, addr); /* 0x003000d0 | 0xf08ff00f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xf700fb0) == 0x3000b0) {
                    goto insn_addr_offset_none_addr_postidx_reg_Rm_unk_Rt_3_LDRHTr; /* 0x003000b0 | 0xf08ff04f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_addr_offset_none_addr_am2offset_imm_offset_unk_Rt_4_LDRBT_POST_IMM; /* 0x04100000 | 0xf0efffff */
            case 1: {
                if ((op & 0xf100010) == 0x6100000) {
                    goto insn_addr_offset_none_addr_am2offset_reg_offset_unk_Rt_4_LDRBT_POST_REG; /* 0x06100000 | 0xf0efffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rn_reglist_regs_16_LDMDA; /* 0x08100000 | 0xf1efffff */
            case 1:
                goto insn_br_target_target_pred_p_B_1_Bcc; /* 0x0a000000 | 0xf0ffffff */
            }
        }
        case 3: {
            switch ((op >> 25) & 0x1) {
            case 0: {
                insn_addr_offset_none_addr_postidx_imm8s4_offset_4_LDC2L_POST:;
                struct bitslice offset = {.nruns = 2, .runs = (struct bitslice_run[]) {{0,0,8}, {23,8,1}}};
                struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                return P(addr_offset_none_addr_postidx_imm8s4_offset_4_LDC2L_POST)(ctx, offset, addr); /* 0x0c300000 | 0xf0cfffff */
            }
            case 1: {
                if ((op & 0xf100010) == 0xe100010) {
                    goto insn_unk_Rt_13_MRC; /* 0x0e100010 | 0xf0efffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        }
    }
    case 4: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            switch ((op >> 5) & 0x1) {
            case 0: {
                if ((op & 0xf2000f0) == 0xd0) {
                    goto insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST; /* 0x000000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xf3000b0) == 0xb0) {
                    goto insn_GPR_Rt_addr_offset_none_addr_am3offset_offset_S_2_STRD_POST; /* 0x000000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            if ((op & 0xf3f0000) == 0x20f0000) {
                goto insn_adrlabel_label_unk_Rd_1_ADR; /* 0x020f0000 | 0xf0c0ffff */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 2:
            goto insn_GPR_Rt_addr_offset_none_addr_am2offset_imm_offset_S_4_STRBT_POST_IMM; /* 0x04000000 | 0xf0efffff */
        case 3: {
            if ((op & 0xf100010) == 0x6000000) {
                goto insn_GPR_Rt_addr_offset_none_addr_am2offset_reg_offset_S_4_STRBT_POST_REG; /* 0x06000000 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4:
            goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
        case 5:
            goto insn_br_target_target_pred_p_B_1_Bcc; /* 0x0a000000 | 0xf0ffffff */
        case 6: {
            if ((op & 0xff00fd0) == 0xc400b10) {
                goto insn_GPR_Rt_4_MCR; /* 0x0c400b10 | 0xf00ff02f */
            } else {
                insn_GPRnopc_Rt_4_MCRR:;
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                return P(GPRnopc_Rt_4_MCRR)(ctx, Rt); /* 0x0c400000 | 0xf00fffff */
            }
        }
        case 7: {
            if ((op & 0xf100010) == 0xe000010) {
                goto insn_GPR_Rt_4_MCR; /* 0x0e000010 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 5: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            switch ((op >> 5) & 0x1) {
            case 0: {
                if ((op & 0xf2000f0) == 0xd0) {
                    goto insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST; /* 0x000000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xf3000b0) == 0x1000b0) {
                    goto insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST; /* 0x001000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1:
            return P(unidentified)(ctx);
        case 2:
            goto insn_addr_offset_none_addr_am2offset_imm_offset_unk_Rt_4_LDRBT_POST_IMM; /* 0x04100000 | 0xf0efffff */
        case 3: {
            if ((op & 0xf100010) == 0x6100000) {
                goto insn_addr_offset_none_addr_am2offset_reg_offset_unk_Rt_4_LDRBT_POST_REG; /* 0x06100000 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4:
            goto insn_GPR_Rn_reglist_regs_16_LDMDA; /* 0x08100000 | 0xf1efffff */
        case 5:
            goto insn_br_target_target_pred_p_B_1_Bcc; /* 0x0a000000 | 0xf0ffffff */
        case 6: {
            if ((op & 0xff00ed0) == 0xc500a10) {
                goto insn_unk_Rt_13_MRC; /* 0x0c500a10 | 0xf00ff12f */
            } else {
                goto insn_GPRnopc_Rt_4_MCRR; /* 0x0c500000 | 0xf00fffff */
            }
        }
        case 7: {
            if ((op & 0xf100010) == 0xe100010) {
                goto insn_unk_Rt_13_MRC; /* 0x0e100010 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 6:
    case 14: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            if ((op & 0xf7000f0) == 0x6000b0) {
                struct bitslice offset = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,4}, {8,4,4}, {23,8,1}}};
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                return P(GPR_Rt_addr_offset_none_addr_postidx_imm8_offset_S_1_STRHTi)(ctx, offset, Rt, addr); /* 0x006000b0 | 0xf08fff0f */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 1:
            return P(unidentified)(ctx);
        case 2:
            goto insn_GPR_Rt_addr_offset_none_addr_am2offset_imm_offset_S_4_STRBT_POST_IMM; /* 0x04000000 | 0xf0efffff */
        case 3: {
            if ((op & 0xf100010) == 0x6000000) {
                goto insn_GPR_Rt_addr_offset_none_addr_am2offset_reg_offset_S_4_STRBT_POST_REG; /* 0x06000000 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4:
            goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
        case 5:
            goto insn_br_target_target_pred_p_B_1_Bcc; /* 0x0a000000 | 0xf0ffffff */
        case 6:
            goto insn_addr_offset_none_addr_postidx_imm8s4_offset_S_4_STC2L_POST; /* 0x0c200000 | 0xf0cfffff */
        case 7: {
            if ((op & 0xf100010) == 0xe000010) {
                goto insn_GPR_Rt_4_MCR; /* 0x0e000010 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 7:
    case 15: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0x1) {
            case 0: {
                if ((op & 0xf7000f0) == 0x7000d0) {
                    insn_addr_offset_none_addr_postidx_imm8_offset_unk_Rt_3_LDRHTi:;
                    struct bitslice offset = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,4}, {8,4,4}, {23,8,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                    return P(addr_offset_none_addr_postidx_imm8_offset_unk_Rt_3_LDRHTi)(ctx, offset, Rt, addr); /* 0x007000d0 | 0xf08fff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xf7000b0) == 0x7000b0) {
                    goto insn_addr_offset_none_addr_postidx_imm8_offset_unk_Rt_3_LDRHTi; /* 0x007000b0 | 0xf08fff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_addr_offset_none_addr_am2offset_imm_offset_unk_Rt_4_LDRBT_POST_IMM; /* 0x04100000 | 0xf0efffff */
            case 1: {
                if ((op & 0xf100010) == 0x6100000) {
                    goto insn_addr_offset_none_addr_am2offset_reg_offset_unk_Rt_4_LDRBT_POST_REG; /* 0x06100000 | 0xf0efffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rn_reglist_regs_16_LDMDA; /* 0x08100000 | 0xf1efffff */
            case 1:
                goto insn_br_target_target_pred_p_B_1_Bcc; /* 0x0a000000 | 0xf0ffffff */
            }
        }
        case 3: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_addr_offset_none_addr_postidx_imm8s4_offset_4_LDC2L_POST; /* 0x0c300000 | 0xf0cfffff */
            case 1: {
                if ((op & 0xf100010) == 0xe100010) {
                    goto insn_unk_Rt_13_MRC; /* 0x0e100010 | 0xf0efffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        }
    }
    case 8: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            switch ((op >> 4) & 0x3) {
            case 0: {
                if ((op & 0xfe00ff0) == 0x800000) {
                    insn_GPR_Rn_GPR_Rm_unk_Rd_1_ADDrr:;
                    struct bitslice Rm = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,4}}};
                    struct bitslice Rd = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    struct bitslice Rn = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                    return P(GPR_Rn_GPR_Rm_unk_Rd_1_ADDrr)(ctx, Rm, Rd, Rn); /* 0x00800000 | 0xf01ff00f */
                } else {
                    insn_GPR_Rn_so_reg_imm_shift_unk_Rd_1_ADDrsi:;
                    struct bitslice shift = {.nruns = 2, .runs = (struct bitslice_run[]) {{0,0,4}, {5,5,7}}};
                    struct bitslice Rd = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    struct bitslice Rn = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                    return P(GPR_Rn_so_reg_imm_shift_unk_Rd_1_ADDrsi)(ctx, shift, Rd, Rn); /* 0x00800000 | 0xf01fffcf */
                }
            }
            case 1: {
                switch ((op >> 7) & 0x1) {
                case 0: {
                    insn_GPR_Rn_so_reg_reg_shift_unk_Rd_1_ADDrsr:;
                    struct bitslice shift = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,4}, {5,5,2}, {8,8,4}}};
                    struct bitslice Rd = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    struct bitslice Rn = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                    return P(GPR_Rn_so_reg_reg_shift_unk_Rd_1_ADDrsr)(ctx, shift, Rd, Rn); /* 0x00800010 | 0xf01fff6f */
                }
                case 1: {
                    if ((op & 0xf2000f0) == 0xd0) {
                        goto insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST; /* 0x000000d0 | 0xf0dfff0f */
                    } else {
                        return P(unidentified)(ctx);
                    }
                }
                }
            }
            case 2:
                goto insn_GPR_Rn_so_reg_imm_shift_unk_Rd_1_ADDrsi; /* 0x00800000 | 0xf01fffef */
            case 3: {
                switch ((op >> 7) & 0x1) {
                case 0:
                    goto insn_GPR_Rn_so_reg_reg_shift_unk_Rd_1_ADDrsr; /* 0x00800010 | 0xf01fff6f */
                case 1:
                    goto insn_GPR_Rt_addr_offset_none_addr_am3offset_offset_S_2_STRD_POST; /* 0x000000b0 | 0xf0cfff4f */
                }
            }
            }
        }
        case 1: {
            switch ((op >> 16) & 0xf) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
            case 6:
            case 7:
            case 8:
            case 9:
            case 10:
            case 11:
            case 12:
            case 13:
            case 14: {
                insn_GPR_Rn_unk_Rd_1_ADDri:;
                struct bitslice Rd = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                struct bitslice Rn = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                return P(GPR_Rn_unk_Rd_1_ADDri)(ctx, Rd, Rn); /* 0x02800000 | 0xf01fffff */
            }
            case 15:
                goto insn_adrlabel_label_unk_Rd_1_ADR; /* 0x020f0000 | 0xf0c0ffff */
            }
        }
        case 2:
            goto insn_GPR_Rt_addr_offset_none_addr_am2offset_imm_offset_S_4_STRBT_POST_IMM; /* 0x04000000 | 0xf0efffff */
        case 3: {
            if ((op & 0xf100010) == 0x6000000) {
                goto insn_GPR_Rt_addr_offset_none_addr_am2offset_reg_offset_S_4_STRBT_POST_REG; /* 0x06000000 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4:
            goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
        case 5:
            goto insn_br_target_target_pred_p_B_1_Bcc; /* 0x0a000000 | 0xf0ffffff */
        case 6: {
            insn_addr_offset_none_addr_S_4_STC2L_OPTION:;
            struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
            return P(addr_offset_none_addr_S_4_STC2L_OPTION)(ctx, addr); /* 0x0c800000 | 0xf04fffff */
        }
        case 7: {
            if ((op & 0xf100010) == 0xe000010) {
                goto insn_GPR_Rt_4_MCR; /* 0x0e000010 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 9: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            switch ((op >> 4) & 0x3) {
            case 0: {
                if ((op & 0xfe00ff0) == 0x800000) {
                    goto insn_GPR_Rn_GPR_Rm_unk_Rd_1_ADDrr; /* 0x00800000 | 0xf01ff00f */
                } else {
                    goto insn_GPR_Rn_so_reg_imm_shift_unk_Rd_1_ADDrsi; /* 0x00800000 | 0xf01fffcf */
                }
            }
            case 1: {
                switch ((op >> 7) & 0x1) {
                case 0:
                    goto insn_GPR_Rn_so_reg_reg_shift_unk_Rd_1_ADDrsr; /* 0x00800010 | 0xf01fff6f */
                case 1: {
                    if ((op & 0xf2000f0) == 0xd0) {
                        goto insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST; /* 0x000000d0 | 0xf0dfff0f */
                    } else {
                        return P(unidentified)(ctx);
                    }
                }
                }
            }
            case 2:
                goto insn_GPR_Rn_so_reg_imm_shift_unk_Rd_1_ADDrsi; /* 0x00800000 | 0xf01fffef */
            case 3: {
                switch ((op >> 7) & 0x1) {
                case 0:
                    goto insn_GPR_Rn_so_reg_reg_shift_unk_Rd_1_ADDrsr; /* 0x00800010 | 0xf01fff6f */
                case 1:
                    goto insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST; /* 0x001000b0 | 0xf0cfff4f */
                }
            }
            }
        }
        case 1:
            goto insn_GPR_Rn_unk_Rd_1_ADDri; /* 0x02800000 | 0xf01fffff */
        case 2:
            goto insn_addr_offset_none_addr_am2offset_imm_offset_unk_Rt_4_LDRBT_POST_IMM; /* 0x04100000 | 0xf0efffff */
        case 3: {
            if ((op & 0xf100010) == 0x6100000) {
                goto insn_addr_offset_none_addr_am2offset_reg_offset_unk_Rt_4_LDRBT_POST_REG; /* 0x06100000 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4:
            goto insn_GPR_Rn_reglist_regs_16_LDMDA; /* 0x08100000 | 0xf1efffff */
        case 5:
            goto insn_br_target_target_pred_p_B_1_Bcc; /* 0x0a000000 | 0xf0ffffff */
        case 6: {
            insn_addr_offset_none_addr_4_LDC2L_OPTION:;
            struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
            return P(addr_offset_none_addr_4_LDC2L_OPTION)(ctx, addr); /* 0x0c900000 | 0xf04fffff */
        }
        case 7: {
            if ((op & 0xf100010) == 0xe100010) {
                goto insn_unk_Rt_13_MRC; /* 0x0e100010 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 12: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            switch ((op >> 5) & 0x1) {
            case 0: {
                if ((op & 0xf2000f0) == 0xd0) {
                    goto insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST; /* 0x000000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xf3000b0) == 0xb0) {
                    goto insn_GPR_Rt_addr_offset_none_addr_am3offset_offset_S_2_STRD_POST; /* 0x000000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            if ((op & 0xf3f0000) == 0x20f0000) {
                goto insn_adrlabel_label_unk_Rd_1_ADR; /* 0x020f0000 | 0xf0c0ffff */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 2:
            goto insn_GPR_Rt_addr_offset_none_addr_am2offset_imm_offset_S_4_STRBT_POST_IMM; /* 0x04000000 | 0xf0efffff */
        case 3: {
            if ((op & 0xf100010) == 0x6000000) {
                goto insn_GPR_Rt_addr_offset_none_addr_am2offset_reg_offset_S_4_STRBT_POST_REG; /* 0x06000000 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4:
            goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
        case 5:
            goto insn_br_target_target_pred_p_B_1_Bcc; /* 0x0a000000 | 0xf0ffffff */
        case 6:
            goto insn_addr_offset_none_addr_S_4_STC2L_OPTION; /* 0x0c800000 | 0xf04fffff */
        case 7: {
            if ((op & 0xf100010) == 0xe000010) {
                goto insn_GPR_Rt_4_MCR; /* 0x0e000010 | 0xf0efffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 13: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0x1) {
            case 0: {
                if ((op & 0xf2000f0) == 0xd0) {
                    goto insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST; /* 0x000000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xf3000b0) == 0x1000b0) {
                    goto insn_addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST; /* 0x001000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_addr_offset_none_addr_am2offset_imm_offset_unk_Rt_4_LDRBT_POST_IMM; /* 0x04100000 | 0xf0efffff */
            case 1: {
                if ((op & 0xf100010) == 0x6100000) {
                    goto insn_addr_offset_none_addr_am2offset_reg_offset_unk_Rt_4_LDRBT_POST_REG; /* 0x06100000 | 0xf0efffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rn_reglist_regs_16_LDMDA; /* 0x08100000 | 0xf1efffff */
            case 1:
                goto insn_br_target_target_pred_p_B_1_Bcc; /* 0x0a000000 | 0xf0ffffff */
            }
        }
        case 3: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_addr_offset_none_addr_4_LDC2L_OPTION; /* 0x0c900000 | 0xf04fffff */
            case 1: {
                if ((op & 0xf100010) == 0xe100010) {
                    goto insn_unk_Rt_13_MRC; /* 0x0e100010 | 0xf0efffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        }
    }
    case 16: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            switch ((op >> 5) & 0x3) {
            case 0: {
                if ((op & 0xfb00ff0) == 0x1000090) {
                    insn_addr_offset_none_addr_unk_Rt_13_LDA:;
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                    return P(addr_offset_none_addr_unk_Rt_13_LDA)(ctx, Rt, addr); /* 0x01000090 | 0xf04ff00f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1:
            case 3: {
                if ((op & 0xf3000b0) == 0x10000b0) {
                    insn_GPR_Rt_addrmode3_addr_S_2_STRD:;
                    struct bitslice addr = {.nruns = 5, .runs = (struct bitslice_run[]) {{0,0,4}, {8,4,4}, {16,9,4}, {22,13,1}, {23,8,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    return P(GPR_Rt_addrmode3_addr_S_2_STRD)(ctx, addr, Rt); /* 0x010000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 2: {
                if ((op & 0xf2000f0) == 0x10000d0) {
                    insn_addrmode3_addr_unk_Rt_4_LDRD:;
                    struct bitslice addr = {.nruns = 5, .runs = (struct bitslice_run[]) {{0,0,4}, {8,4,4}, {16,9,4}, {22,13,1}, {23,8,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    return P(addrmode3_addr_unk_Rt_4_LDRD)(ctx, addr, Rt); /* 0x010000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            insn_unk_Rd_5_MOVTi16:;
            struct bitslice Rd = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
            return P(unk_Rd_5_MOVTi16)(ctx, Rd); /* 0x03000000 | 0xf04fffff */
        }
        case 2: {
            insn_GPR_Rt_addrmode_imm12_addr_S_1_STRi12:;
            struct bitslice addr = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,12}, {16,13,4}, {23,12,1}}};
            struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
            return P(GPR_Rt_addrmode_imm12_addr_S_1_STRi12)(ctx, addr, Rt); /* 0x05000000 | 0xf08fffff */
        }
        case 3: {
            if ((op & 0xf700010) == 0x7000000) {
                insn_GPR_Rt_ldst_so_reg_shift_S_1_STRrs:;
                struct bitslice shift = {.nruns = 4, .runs = (struct bitslice_run[]) {{0,0,4}, {5,5,7}, {16,13,4}, {23,12,1}}};
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                return P(GPR_Rt_ldst_so_reg_shift_S_1_STRrs)(ctx, shift, Rt); /* 0x07000000 | 0xf08fffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4:
            goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
        case 5: {
            insn_bl_target_func_2_BL:;
            struct bitslice func = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,24}}};
            return P(bl_target_func_2_BL)(ctx, func); /* 0x0b000000 | 0xf0ffffff */
        }
        case 6: {
            switch ((op >> 9) & 0x7) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
            case 6:
            case 7: {
                insn_addrmode5_addr_S_4_STC2L_OFFSET:;
                struct bitslice addr = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,8}, {16,9,4}, {23,8,1}}};
                return P(addrmode5_addr_S_4_STC2L_OFFSET)(ctx, addr); /* 0x0d000000 | 0xf0cfffff */
            }
            case 5: {
                insn_addrmode5_addr_8_LDC2L_OFFSET:;
                struct bitslice addr = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,8}, {16,9,4}, {23,8,1}}};
                return P(addrmode5_addr_8_LDC2L_OFFSET)(ctx, addr); /* 0x0d000a00 | 0xf0cff1ff */
            }
            }
        }
        case 7:
            return P(unidentified)(ctx);
        }
    }
    case 17:
    case 21: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0x1) {
            case 0: {
                if ((op & 0xf2000f0) == 0x10000d0) {
                    goto insn_addrmode3_addr_unk_Rt_4_LDRD; /* 0x010000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xf3000b0) == 0x11000b0) {
                    goto insn_addrmode3_addr_unk_Rt_4_LDRD; /* 0x011000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0: {
                insn_addrmode_imm12_addr_unk_Rt_2_LDRBi12:;
                struct bitslice addr = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,12}, {16,13,4}, {23,12,1}}};
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                return P(addrmode_imm12_addr_unk_Rt_2_LDRBi12)(ctx, addr, Rt); /* 0x05100000 | 0xf0cfffff */
            }
            case 1: {
                if ((op & 0xf300010) == 0x7100000) {
                    insn_ldst_so_reg_shift_unk_Rt_2_LDRBrs:;
                    struct bitslice shift = {.nruns = 4, .runs = (struct bitslice_run[]) {{0,0,4}, {5,5,7}, {16,13,4}, {23,12,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    return P(ldst_so_reg_shift_unk_Rt_2_LDRBrs)(ctx, shift, Rt); /* 0x07100000 | 0xf0cfffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rn_reglist_regs_16_LDMDA; /* 0x08100000 | 0xf1efffff */
            case 1:
                goto insn_bl_target_func_2_BL; /* 0x0b000000 | 0xf0ffffff */
            }
        }
        case 3: {
            if ((op & 0xf300000) == 0xd100000) {
                goto insn_addrmode5_addr_8_LDC2L_OFFSET; /* 0x0d100000 | 0xf0cfffff */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 18: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0x7) {
            case 0: {
                if ((op & 0xffffff0) == 0x12fff10) {
                    struct bitslice dst = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,4}}};
                    return P(GPR_dst_B_2_BX)(ctx, dst); /* 0x012fff10 | 0xf000000f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xfffffe0) == 0x12fff20) {
                    struct bitslice func = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,4}}};
                    return P(GPR_func_3_BLX)(ctx, func); /* 0x012fff20 | 0xf000001f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 2:
            case 3:
            case 4:
                return P(unidentified)(ctx);
            case 5:
            case 7: {
                if ((op & 0xf3000b0) == 0x12000b0) {
                    insn_GPR_Rt_addrmode3_pre_addr_S_2_STRD_PRE:;
                    struct bitslice addr = {.nruns = 5, .runs = (struct bitslice_run[]) {{0,0,4}, {8,4,4}, {16,9,4}, {22,13,1}, {23,8,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    return P(GPR_Rt_addrmode3_pre_addr_S_2_STRD_PRE)(ctx, addr, Rt); /* 0x012000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 6: {
                if ((op & 0xf2000f0) == 0x12000d0) {
                    insn_addrmode3_pre_addr_unk_Rt_4_LDRD_PRE:;
                    struct bitslice addr = {.nruns = 5, .runs = (struct bitslice_run[]) {{0,0,4}, {8,4,4}, {16,9,4}, {22,13,1}, {23,8,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    return P(addrmode3_pre_addr_unk_Rt_4_LDRD_PRE)(ctx, addr, Rt); /* 0x012000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0: {
                insn_GPR_Rt_addrmode_imm12_pre_addr_S_2_STRB_PRE_IMM:;
                struct bitslice addr = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,12}, {16,13,4}, {23,12,1}}};
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                return P(GPR_Rt_addrmode_imm12_pre_addr_S_2_STRB_PRE_IMM)(ctx, addr, Rt); /* 0x05200000 | 0xf0cfffff */
            }
            case 1: {
                if ((op & 0xf300010) == 0x7200000) {
                    insn_GPR_Rt_ldst_so_reg_addr_S_2_STRB_PRE_REG:;
                    struct bitslice addr = {.nruns = 4, .runs = (struct bitslice_run[]) {{0,0,4}, {5,5,7}, {16,13,4}, {23,12,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    return P(GPR_Rt_ldst_so_reg_addr_S_2_STRB_PRE_REG)(ctx, addr, Rt); /* 0x07200000 | 0xf0cfffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
            case 1:
                goto insn_bl_target_func_2_BL; /* 0x0b000000 | 0xf0ffffff */
            }
        }
        case 3: {
            if ((op & 0xf300000) == 0xd200000) {
                insn_addrmode5_pre_addr_S_4_STC2L_PRE:;
                struct bitslice addr = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,8}, {16,9,4}, {23,8,1}}};
                return P(addrmode5_pre_addr_S_4_STC2L_PRE)(ctx, addr); /* 0x0d200000 | 0xf0cfffff */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 19:
    case 23: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0x1) {
            case 0: {
                if ((op & 0xf2000f0) == 0x12000d0) {
                    goto insn_addrmode3_pre_addr_unk_Rt_4_LDRD_PRE; /* 0x012000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xf3000b0) == 0x13000b0) {
                    goto insn_addrmode3_pre_addr_unk_Rt_4_LDRD_PRE; /* 0x013000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0: {
                insn_addrmode_imm12_pre_addr_unk_Rt_2_LDRB_PRE_IMM:;
                struct bitslice addr = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,12}, {16,13,4}, {23,12,1}}};
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                return P(addrmode_imm12_pre_addr_unk_Rt_2_LDRB_PRE_IMM)(ctx, addr, Rt); /* 0x05300000 | 0xf0cfffff */
            }
            case 1: {
                if ((op & 0xf300010) == 0x7300000) {
                    insn_ldst_so_reg_addr_unk_Rt_2_LDRB_PRE_REG:;
                    struct bitslice addr = {.nruns = 4, .runs = (struct bitslice_run[]) {{0,0,4}, {5,5,7}, {16,13,4}, {23,12,1}}};
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    return P(ldst_so_reg_addr_unk_Rt_2_LDRB_PRE_REG)(ctx, addr, Rt); /* 0x07300000 | 0xf0cfffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rn_reglist_regs_16_LDMDA; /* 0x08100000 | 0xf1efffff */
            case 1:
                goto insn_bl_target_func_2_BL; /* 0x0b000000 | 0xf0ffffff */
            }
        }
        case 3: {
            if ((op & 0xf300000) == 0xd300000) {
                insn_addrmode5_pre_addr_4_LDC2L_PRE:;
                struct bitslice addr = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,8}, {16,9,4}, {23,8,1}}};
                return P(addrmode5_pre_addr_4_LDC2L_PRE)(ctx, addr); /* 0x0d300000 | 0xf0cfffff */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 20: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            switch ((op >> 5) & 0x3) {
            case 0: {
                if ((op & 0xfb00ff0) == 0x1000090) {
                    goto insn_addr_offset_none_addr_unk_Rt_13_LDA; /* 0x01000090 | 0xf04ff00f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1:
            case 3: {
                if ((op & 0xf3000b0) == 0x10000b0) {
                    goto insn_GPR_Rt_addrmode3_addr_S_2_STRD; /* 0x010000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 2: {
                if ((op & 0xf2000f0) == 0x10000d0) {
                    goto insn_addrmode3_addr_unk_Rt_4_LDRD; /* 0x010000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1:
            goto insn_unk_Rd_5_MOVTi16; /* 0x03000000 | 0xf04fffff */
        case 2: {
            insn_GPRnopc_Rt_addrmode_imm12_addr_S_1_STRBi12:;
            struct bitslice addr = {.nruns = 3, .runs = (struct bitslice_run[]) {{0,0,12}, {16,13,4}, {23,12,1}}};
            struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
            return P(GPRnopc_Rt_addrmode_imm12_addr_S_1_STRBi12)(ctx, addr, Rt); /* 0x05400000 | 0xf08fffff */
        }
        case 3: {
            if ((op & 0xf700010) == 0x7400000) {
                insn_GPRnopc_Rt_ldst_so_reg_shift_S_1_STRBrs:;
                struct bitslice shift = {.nruns = 4, .runs = (struct bitslice_run[]) {{0,0,4}, {5,5,7}, {16,13,4}, {23,12,1}}};
                struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                return P(GPRnopc_Rt_ldst_so_reg_shift_S_1_STRBrs)(ctx, shift, Rt); /* 0x07400000 | 0xf08fffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4:
            goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
        case 5:
            goto insn_bl_target_func_2_BL; /* 0x0b000000 | 0xf0ffffff */
        case 6: {
            switch ((op >> 9) & 0x7) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
            case 6:
            case 7:
                goto insn_addrmode5_addr_S_4_STC2L_OFFSET; /* 0x0d000000 | 0xf0cfffff */
            case 5:
                goto insn_addrmode5_addr_8_LDC2L_OFFSET; /* 0x0d000a00 | 0xf0cff1ff */
            }
        }
        case 7:
            return P(unidentified)(ctx);
        }
    }
    case 22: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0x1) {
            case 0: {
                if ((op & 0xf2000f0) == 0x12000d0) {
                    goto insn_addrmode3_pre_addr_unk_Rt_4_LDRD_PRE; /* 0x012000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 1: {
                if ((op & 0xf3000b0) == 0x12000b0) {
                    goto insn_GPR_Rt_addrmode3_pre_addr_S_2_STRD_PRE; /* 0x012000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rt_addrmode_imm12_pre_addr_S_2_STRB_PRE_IMM; /* 0x05200000 | 0xf0cfffff */
            case 1: {
                if ((op & 0xf300010) == 0x7200000) {
                    goto insn_GPR_Rt_ldst_so_reg_addr_S_2_STRB_PRE_REG; /* 0x07200000 | 0xf0cfffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
            case 1:
                goto insn_bl_target_func_2_BL; /* 0x0b000000 | 0xf0ffffff */
            }
        }
        case 3: {
            if ((op & 0xf300000) == 0xd200000) {
                goto insn_addrmode5_pre_addr_S_4_STC2L_PRE; /* 0x0d200000 | 0xf0cfffff */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 24: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0x1f) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 8:
            case 9:
            case 10:
            case 11:
            case 12:
            case 16:
            case 17:
            case 18:
            case 19:
            case 24:
            case 25:
            case 26:
            case 27:
                return P(unidentified)(ctx);
            case 4: {
                if ((op & 0xfb0fff0) == 0x180fc90) {
                    insn_GPR_Rt_addr_offset_none_addr_S_3_STL:;
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,4}}};
                    struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                    return P(GPR_Rt_addr_offset_none_addr_S_3_STL)(ctx, Rt, addr); /* 0x0180fc90 | 0xf04f000f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 5:
            case 7:
            case 13:
            case 15:
            case 21:
            case 23:
            case 29:
            case 31: {
                if ((op & 0xf3000b0) == 0x10000b0) {
                    goto insn_GPR_Rt_addrmode3_addr_S_2_STRD; /* 0x010000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 6:
            case 14:
            case 22:
            case 30: {
                if ((op & 0xf2000f0) == 0x10000d0) {
                    goto insn_addrmode3_addr_unk_Rt_4_LDRD; /* 0x010000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 20:
            case 28: {
                if ((op & 0xfb00ef0) == 0x1800e90) {
                    insn_GPR_Rt_addr_offset_none_addr_unk_Rd_S_6_STLEX:;
                    struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,4}}};
                    struct bitslice Rd = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                    struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                    return P(GPR_Rt_addr_offset_none_addr_unk_Rd_S_6_STLEX)(ctx, Rt, Rd, addr); /* 0x01800e90 | 0xf04ff10f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rt_addrmode_imm12_addr_S_1_STRi12; /* 0x05000000 | 0xf08fffff */
            case 1: {
                if ((op & 0xf700010) == 0x7000000) {
                    goto insn_GPR_Rt_ldst_so_reg_shift_S_1_STRrs; /* 0x07000000 | 0xf08fffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
            case 1:
                goto insn_bl_target_func_2_BL; /* 0x0b000000 | 0xf0ffffff */
            }
        }
        case 3: {
            switch ((op >> 9) & 0x7) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
            case 6:
            case 7: {
                if ((op & 0xf300000) == 0xd000000) {
                    goto insn_addrmode5_addr_S_4_STC2L_OFFSET; /* 0x0d000000 | 0xf0cfffff */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 5: {
                if ((op & 0xf300e00) == 0xd000a00) {
                    goto insn_addrmode5_addr_8_LDC2L_OFFSET; /* 0x0d000a00 | 0xf0cff1ff */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        }
    }
    case 25:
    case 29: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0xf) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 8:
            case 9:
            case 10:
            case 11:
                return P(unidentified)(ctx);
            case 4: {
                if ((op & 0xfb00dff) == 0x1900c9f) {
                    goto insn_addr_offset_none_addr_unk_Rt_13_LDA; /* 0x01900c9f | 0xf04ff200 */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 5:
            case 7:
            case 13:
            case 15: {
                if ((op & 0xf3000b0) == 0x11000b0) {
                    goto insn_addrmode3_addr_unk_Rt_4_LDRD; /* 0x011000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 6:
            case 14: {
                if ((op & 0xf2000f0) == 0x10000d0) {
                    goto insn_addrmode3_addr_unk_Rt_4_LDRD; /* 0x010000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 12: {
                if ((op & 0xfb00fff) == 0x1900f9f) {
                    goto insn_addr_offset_none_addr_unk_Rt_13_LDA; /* 0x01900f9f | 0xf04ff000 */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_addrmode_imm12_addr_unk_Rt_2_LDRBi12; /* 0x05100000 | 0xf0cfffff */
            case 1: {
                if ((op & 0xf300010) == 0x7100000) {
                    goto insn_ldst_so_reg_shift_unk_Rt_2_LDRBrs; /* 0x07100000 | 0xf0cfffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rn_reglist_regs_16_LDMDA; /* 0x08100000 | 0xf1efffff */
            case 1:
                goto insn_bl_target_func_2_BL; /* 0x0b000000 | 0xf0ffffff */
            }
        }
        case 3: {
            if ((op & 0xf300000) == 0xd100000) {
                goto insn_addrmode5_addr_8_LDC2L_OFFSET; /* 0x0d100000 | 0xf0cfffff */
            } else {
                return P(unidentified)(ctx);
            }
        }
        }
    }
    case 26: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            switch ((op >> 4) & 0x3) {
            case 0: {
                switch ((op >> 16) & 0xf) {
                case 0: {
                    if ((op & 0xfef0ff0) == 0x1a00000) {
                        insn_GPR_Rm_unk_Rd_1_MOVr:;
                        struct bitslice Rm = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,4}}};
                        struct bitslice Rd = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                        return P(GPR_Rm_unk_Rd_1_MOVr)(ctx, Rm, Rd); /* 0x01a00000 | 0xf010f00f */
                    } else {
                        goto insn_unk_Rd_5_MOVTi16; /* 0x01a00000 | 0xf010ffcf */
                    }
                }
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                case 8:
                case 9:
                case 10:
                case 11:
                case 12:
                case 13:
                case 14:
                case 15: {
                    if ((op & 0xfe00ff0) == 0x1a00000) {
                        insn_tcGPR_Rm_unk_Rd_1_MOVr_TC:;
                        struct bitslice Rm = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,4}}};
                        struct bitslice Rd = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                        return P(tcGPR_Rm_unk_Rd_1_MOVr_TC)(ctx, Rm, Rd); /* 0x01a00000 | 0xf01ff00f */
                    } else {
                        return P(unidentified)(ctx);
                    }
                }
                }
            }
            case 1: {
                switch ((op >> 6) & 0x3) {
                case 0:
                case 1: {
                    if ((op & 0xfef0090) == 0x1a00010) {
                        goto insn_unk_Rd_5_MOVTi16; /* 0x01a00010 | 0xf010ff6f */
                    } else {
                        return P(unidentified)(ctx);
                    }
                }
                case 2: {
                    if ((op & 0xff00ef0) == 0x1a00e90) {
                        struct bitslice Rt = {.nruns = 1, .runs = (struct bitslice_run[]) {{0,0,4}}};
                        struct bitslice Rd = {.nruns = 1, .runs = (struct bitslice_run[]) {{12,0,4}}};
                        struct bitslice addr = {.nruns = 1, .runs = (struct bitslice_run[]) {{16,0,4}}};
                        return P(GPRPairOp_Rt_addr_offset_none_addr_unk_Rd_S_2_STLEXD)(ctx, Rt, Rd, addr); /* 0x01a00e90 | 0xf00ff10f */
                    } else {
                        return P(unidentified)(ctx);
                    }
                }
                case 3:
                    goto insn_addrmode3_pre_addr_unk_Rt_4_LDRD_PRE; /* 0x012000d0 | 0xf0dfff0f */
                }
            }
            case 2: {
                if ((op & 0xfef0010) == 0x1a00000) {
                    goto insn_unk_Rd_5_MOVTi16; /* 0x01a00000 | 0xf010ffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 3: {
                switch ((op >> 7) & 0x1) {
                case 0: {
                    if ((op & 0xfef0090) == 0x1a00010) {
                        goto insn_unk_Rd_5_MOVTi16; /* 0x01a00010 | 0xf010ff6f */
                    } else {
                        return P(unidentified)(ctx);
                    }
                }
                case 1:
                    goto insn_GPR_Rt_addrmode3_pre_addr_S_2_STRD_PRE; /* 0x012000b0 | 0xf0cfff4f */
                }
            }
            }
        }
        case 1: {
            if ((op & 0xfef0000) == 0x3a00000) {
                goto insn_unk_Rd_5_MOVTi16; /* 0x03a00000 | 0xf010ffff */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 2:
            goto insn_GPR_Rt_addrmode_imm12_pre_addr_S_2_STRB_PRE_IMM; /* 0x05200000 | 0xf0cfffff */
        case 3: {
            if ((op & 0xf300010) == 0x7200000) {
                goto insn_GPR_Rt_ldst_so_reg_addr_S_2_STRB_PRE_REG; /* 0x07200000 | 0xf0cfffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4:
            goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
        case 5:
            goto insn_bl_target_func_2_BL; /* 0x0b000000 | 0xf0ffffff */
        case 6:
            goto insn_addrmode5_pre_addr_S_4_STC2L_PRE; /* 0x0d200000 | 0xf0cfffff */
        case 7:
            return P(unidentified)(ctx);
        }
    }
    case 27: {
        switch ((op >> 25) & 0x7) {
        case 0: {
            switch ((op >> 4) & 0x3) {
            case 0: {
                switch ((op >> 16) & 0xf) {
                case 0: {
                    if ((op & 0xfef0ff0) == 0x1a00000) {
                        goto insn_GPR_Rm_unk_Rd_1_MOVr; /* 0x01a00000 | 0xf010f00f */
                    } else {
                        goto insn_unk_Rd_5_MOVTi16; /* 0x01a00000 | 0xf010ffcf */
                    }
                }
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                case 8:
                case 9:
                case 10:
                case 11:
                case 12:
                case 13:
                case 14:
                case 15: {
                    if ((op & 0xfe00ff0) == 0x1a00000) {
                        goto insn_tcGPR_Rm_unk_Rd_1_MOVr_TC; /* 0x01a00000 | 0xf01ff00f */
                    } else {
                        return P(unidentified)(ctx);
                    }
                }
                }
            }
            case 1: {
                switch ((op >> 6) & 0x3) {
                case 0:
                case 1: {
                    if ((op & 0xfef0090) == 0x1a00010) {
                        goto insn_unk_Rd_5_MOVTi16; /* 0x01a00010 | 0xf010ff6f */
                    } else {
                        return P(unidentified)(ctx);
                    }
                }
                case 2: {
                    if ((op & 0xfb00eff) == 0x1b00e9f) {
                        goto insn_addr_offset_none_addr_unk_Rt_13_LDA; /* 0x01b00e9f | 0xf04ff100 */
                    } else {
                        return P(unidentified)(ctx);
                    }
                }
                case 3:
                    goto insn_addrmode3_pre_addr_unk_Rt_4_LDRD_PRE; /* 0x012000d0 | 0xf0dfff0f */
                }
            }
            case 2: {
                if ((op & 0xfef0010) == 0x1a00000) {
                    goto insn_unk_Rd_5_MOVTi16; /* 0x01a00000 | 0xf010ffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 3: {
                switch ((op >> 7) & 0x1) {
                case 0: {
                    if ((op & 0xfef0090) == 0x1a00010) {
                        goto insn_unk_Rd_5_MOVTi16; /* 0x01a00010 | 0xf010ff6f */
                    } else {
                        return P(unidentified)(ctx);
                    }
                }
                case 1:
                    goto insn_addrmode3_pre_addr_unk_Rt_4_LDRD_PRE; /* 0x013000b0 | 0xf0cfff4f */
                }
            }
            }
        }
        case 1: {
            if ((op & 0xfef0000) == 0x3a00000) {
                goto insn_unk_Rd_5_MOVTi16; /* 0x03a00000 | 0xf010ffff */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 2:
            goto insn_addrmode_imm12_pre_addr_unk_Rt_2_LDRB_PRE_IMM; /* 0x05300000 | 0xf0cfffff */
        case 3: {
            if ((op & 0xf300010) == 0x7300000) {
                goto insn_ldst_so_reg_addr_unk_Rt_2_LDRB_PRE_REG; /* 0x07300000 | 0xf0cfffef */
            } else {
                return P(unidentified)(ctx);
            }
        }
        case 4:
            goto insn_GPR_Rn_reglist_regs_16_LDMDA; /* 0x08100000 | 0xf1efffff */
        case 5:
            goto insn_bl_target_func_2_BL; /* 0x0b000000 | 0xf0ffffff */
        case 6:
            goto insn_addrmode5_pre_addr_4_LDC2L_PRE; /* 0x0d300000 | 0xf0cfffff */
        case 7:
            return P(unidentified)(ctx);
        }
    }
    case 28: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0x1f) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 8:
            case 9:
            case 10:
            case 11:
            case 12:
            case 16:
            case 17:
            case 18:
            case 19:
            case 24:
            case 25:
            case 26:
            case 27:
                return P(unidentified)(ctx);
            case 4: {
                if ((op & 0xfb0fff0) == 0x180fc90) {
                    goto insn_GPR_Rt_addr_offset_none_addr_S_3_STL; /* 0x0180fc90 | 0xf04f000f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 5:
            case 7:
            case 13:
            case 15:
            case 21:
            case 23:
            case 29:
            case 31: {
                if ((op & 0xf3000b0) == 0x10000b0) {
                    goto insn_GPR_Rt_addrmode3_addr_S_2_STRD; /* 0x010000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 6:
            case 14:
            case 22:
            case 30: {
                if ((op & 0xf2000f0) == 0x10000d0) {
                    goto insn_addrmode3_addr_unk_Rt_4_LDRD; /* 0x010000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 20:
            case 28: {
                if ((op & 0xfb00ef0) == 0x1800e90) {
                    goto insn_GPR_Rt_addr_offset_none_addr_unk_Rd_S_6_STLEX; /* 0x01800e90 | 0xf04ff10f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPRnopc_Rt_addrmode_imm12_addr_S_1_STRBi12; /* 0x05400000 | 0xf08fffff */
            case 1: {
                if ((op & 0xf700010) == 0x7400000) {
                    goto insn_GPRnopc_Rt_ldst_so_reg_shift_S_1_STRBrs; /* 0x07400000 | 0xf08fffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
            case 1:
                goto insn_bl_target_func_2_BL; /* 0x0b000000 | 0xf0ffffff */
            }
        }
        case 3: {
            switch ((op >> 9) & 0x7) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
            case 6:
            case 7: {
                if ((op & 0xf300000) == 0xd000000) {
                    goto insn_addrmode5_addr_S_4_STC2L_OFFSET; /* 0x0d000000 | 0xf0cfffff */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 5: {
                if ((op & 0xf300e00) == 0xd000a00) {
                    goto insn_addrmode5_addr_8_LDC2L_OFFSET; /* 0x0d000a00 | 0xf0cff1ff */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        }
    }
    case 30: {
        switch ((op >> 26) & 0x3) {
        case 0: {
            switch ((op >> 5) & 0x1f) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 8:
            case 9:
            case 10:
            case 11:
            case 12:
            case 16:
            case 17:
            case 18:
            case 19:
            case 24:
            case 25:
            case 26:
            case 27:
                return P(unidentified)(ctx);
            case 4: {
                if ((op & 0xff0fff0) == 0x1e0fc90) {
                    goto insn_GPR_Rt_addr_offset_none_addr_S_3_STL; /* 0x01e0fc90 | 0xf00f000f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 5:
            case 7:
            case 13:
            case 15:
            case 21:
            case 23:
            case 29:
            case 31: {
                if ((op & 0xf3000b0) == 0x12000b0) {
                    goto insn_GPR_Rt_addrmode3_pre_addr_S_2_STRD_PRE; /* 0x012000b0 | 0xf0cfff4f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 6:
            case 14:
            case 22:
            case 30: {
                if ((op & 0xf2000f0) == 0x12000d0) {
                    goto insn_addrmode3_pre_addr_unk_Rt_4_LDRD_PRE; /* 0x012000d0 | 0xf0dfff0f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            case 20:
            case 28: {
                if ((op & 0xff00ef0) == 0x1e00e90) {
                    goto insn_GPR_Rt_addr_offset_none_addr_unk_Rd_S_6_STLEX; /* 0x01e00e90 | 0xf00ff10f */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 1: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rt_addrmode_imm12_pre_addr_S_2_STRB_PRE_IMM; /* 0x05200000 | 0xf0cfffff */
            case 1: {
                if ((op & 0xf300010) == 0x7200000) {
                    goto insn_GPR_Rt_ldst_so_reg_addr_S_2_STRB_PRE_REG; /* 0x07200000 | 0xf0cfffef */
                } else {
                    return P(unidentified)(ctx);
                }
            }
            }
        }
        case 2: {
            switch ((op >> 25) & 0x1) {
            case 0:
                goto insn_GPR_Rn_reglist_regs_S_16_STMDA; /* 0x08000000 | 0xf1efffff */
            case 1:
                goto insn_bl_target_func_2_BL; /* 0x0b000000 | 0xf0ffffff */
            }
        }
        case 3: {
            if ((op & 0xf300000) == 0xd200000) {
          
gitextract_f5flmijb/

├── .gitignore
├── LICENSE.txt
├── README.md
├── configure
├── darwin-bootstrap/
│   ├── DEBIAN/
│   │   ├── control
│   │   ├── extrainst_
│   │   └── postrm
│   ├── bundle-loader.c
│   ├── com.ex.substituted.plist
│   ├── ib-log.h
│   ├── inject-into-launchd.c
│   ├── posixspawn-hook.c
│   ├── safemode-ui-hook.m
│   ├── safemode-ui-hook.plist
│   ├── safety-dance/
│   │   ├── AutoGrid.h
│   │   ├── AutoGrid.m
│   │   ├── Info.plist
│   │   └── main.m
│   ├── substituted-plist-loader.h
│   ├── substituted.m
│   └── unrestrict.c
├── doc/
│   └── installed-README.txt
├── ent.plist
├── generated/
│   ├── darwin-inject-asm.S
│   ├── generic-dis-arm.inc.h
│   ├── generic-dis-arm64.inc.h
│   ├── generic-dis-thumb.inc.h
│   ├── generic-dis-thumb2.inc.h
│   └── manual-mach.inc.h
├── lib/
│   ├── arm/
│   │   ├── arch-dis.h
│   │   ├── arch-transform-dis.inc.h
│   │   ├── assemble.h
│   │   ├── dis-arm.inc.h
│   │   ├── dis-main.inc.h
│   │   ├── dis-thumb.inc.h
│   │   ├── dis-thumb2.inc.h
│   │   ├── jump-patch.h
│   │   └── misc.h
│   ├── arm64/
│   │   ├── arch-dis.h
│   │   ├── arch-transform-dis.inc.h
│   │   ├── assemble.h
│   │   ├── dis-main.inc.h
│   │   ├── jump-patch.h
│   │   └── misc.h
│   ├── cbit/
│   │   ├── cqueue.c
│   │   ├── cqueue.h
│   │   ├── htab.h
│   │   ├── misc.h
│   │   ├── vec.c
│   │   └── vec.h
│   ├── darwin/
│   │   ├── execmem.c
│   │   ├── find-syms.c
│   │   ├── inject-asm-raw.c
│   │   ├── inject-asm-raw.order
│   │   ├── inject.c
│   │   ├── interpose.c
│   │   ├── mach-decls.h
│   │   ├── manual-syscall.h
│   │   ├── objc-asm.S
│   │   ├── objc.c
│   │   ├── objc.h
│   │   ├── read.c
│   │   ├── read.h
│   │   ├── substrate-compat.c
│   │   └── xxpc.h
│   ├── dis.h
│   ├── execmem.h
│   ├── hook-functions.c
│   ├── jump-dis.c
│   ├── jump-dis.h
│   ├── ptrauth_helpers.h
│   ├── strerror.c
│   ├── substitute-internal.h
│   ├── substitute.h
│   ├── transform-dis.c
│   ├── transform-dis.h
│   └── x86/
│       ├── arch-dis.h
│       ├── arch-transform-dis.inc.h
│       ├── dis-main.inc.h
│       ├── jump-patch.h
│       └── misc.h
├── script/
│   ├── gen-deb.sh
│   ├── gen-inject-asm.sh
│   ├── gen-manual-mach.py
│   ├── mconfig.py
│   └── test-transform-dis.sh
├── substrate/
│   ├── lgpl-3.0.tar.xz
│   └── substrate.h
├── substrate-shim.c
├── test/
│   ├── injected-test-dylib.c
│   ├── insns-arm.S
│   ├── insns-libz-arm.S
│   ├── lol.c
│   ├── test-dis.c
│   ├── test-execmem.c
│   ├── test-find-syms.c
│   ├── test-hook-functions.c
│   ├── test-htab.c
│   ├── test-imp-forwarding.m
│   ├── test-inject.c
│   ├── test-interpose.c
│   ├── test-jump-dis.c
│   ├── test-objc-hook.m
│   ├── test-pc-patch.c
│   ├── test-posixspawn-hook.c
│   ├── test-substrate.cpp
│   ├── test-td-simple.c
│   ├── test-transform-dis.c
│   ├── test-vec.cpp
│   ├── transform-dis-cases-arm.S
│   ├── transform-dis-cases-arm64.S
│   ├── transform-dis-cases-i386.S
│   ├── transform-dis-cases-x86_64.S
│   └── transform-dis-cases.h
└── vendor/
    └── dyld_cache_format.h
Condensed preview — 114 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (801K chars).
[
  {
    "path": ".gitignore",
    "chars": 44,
    "preview": "*.swp\nout\nMakefile\nconfig.log\nconfig.status\n"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1829,
    "preview": "Some files in this repository contain their own licensing info in a header:\nsubstrate.h, which is based on an older vers..."
  },
  {
    "path": "README.md",
    "chars": 4361,
    "preview": "(lib)substitute\n---------------\n\nSubstitute is a system for modifying code at runtime by substituting custom\nimplementat..."
  },
  {
    "path": "configure",
    "chars": 12240,
    "preview": "#!/usr/bin/env python\nimport sys, os, glob\nsys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'script'))\nimport..."
  },
  {
    "path": "darwin-bootstrap/DEBIAN/control",
    "chars": 315,
    "preview": "Package: com.ex.substitute\nArchitecture: iphoneos-arm\nName: Substitute\nDepiction: http://jslinux.org\nAuthor: comex <come..."
  },
  {
    "path": "darwin-bootstrap/DEBIAN/extrainst_",
    "chars": 342,
    "preview": "#!/bin/bash\n\n# for now, always reboot on install/uninstall\n\n# <- http://iphonedevwiki.net/index.php/Packaging#Telling_Cy..."
  },
  {
    "path": "darwin-bootstrap/DEBIAN/postrm",
    "chars": 378,
    "preview": "#!/bin/bash\n\n# for now, always reboot on install/uninstall\n\n# <- http://iphonedevwiki.net/index.php/Packaging#Telling_Cy..."
  },
  {
    "path": "darwin-bootstrap/bundle-loader.c",
    "chars": 10808,
    "preview": "#define IB_LOG_NAME \"bundle-loader\"\n#define IB_LOG_TO_SYSLOG\n#include \"ib-log.h\"\n#include \"darwin/mach-decls.h\"\n#include..."
  },
  {
    "path": "darwin-bootstrap/com.ex.substituted.plist",
    "chars": 556,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P..."
  },
  {
    "path": "darwin-bootstrap/ib-log.h",
    "chars": 1117,
    "preview": "#pragma once\n#include <dispatch/dispatch.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n#ifdef IB_LOG_TO..."
  },
  {
    "path": "darwin-bootstrap/inject-into-launchd.c",
    "chars": 3365,
    "preview": "/* This is an iOS executable, placed in /etc/rc.d, that injects\n * posixspawn-hook.dylib into launchd (pid 1). */\n\n#defi..."
  },
  {
    "path": "darwin-bootstrap/posixspawn-hook.c",
    "chars": 25054,
    "preview": "/* This library is loaded into launchd, and from there into xpcproxy, which\n * launchd uses as an intermediary to exec i..."
  },
  {
    "path": "darwin-bootstrap/safemode-ui-hook.m",
    "chars": 4173,
    "preview": "#include \"substitute.h\"\n#include <objc/runtime.h>\n#include <notify.h>\n#include <dispatch/dispatch.h>\n#import <Foundation..."
  },
  {
    "path": "darwin-bootstrap/safemode-ui-hook.plist",
    "chars": 271,
    "preview": "<plist>\n<dict>\n    <key>Filter</key>\n    <dict>\n        <key>Executables</key>\n        <array>\n            <string>/Syst..."
  },
  {
    "path": "darwin-bootstrap/safety-dance/AutoGrid.h",
    "chars": 296,
    "preview": "//\n//  AutoGrid.h\n//  SafetyDance\n//\n//  Created by Nicholas Allegra on 1/26/15.\n//  Copyright (c) 2015 Nicholas Allegra..."
  },
  {
    "path": "darwin-bootstrap/safety-dance/AutoGrid.m",
    "chars": 3286,
    "preview": "//\n//  AutoGrid.m\n//  SafetyDance\n//\n//  Created by Nicholas Allegra on 1/26/15.\n//  Copyright (c) 2015 Nicholas Allegra..."
  },
  {
    "path": "darwin-bootstrap/safety-dance/Info.plist",
    "chars": 2937,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P..."
  },
  {
    "path": "darwin-bootstrap/safety-dance/main.m",
    "chars": 10053,
    "preview": "#include \"darwin/xxpc.h\"\n#include \"substitute-internal.h\"\n#import <UIKit/UIKit.h>\n#import \"AutoGrid.h\"\n#include <notify...."
  },
  {
    "path": "darwin-bootstrap/substituted-plist-loader.h",
    "chars": 118,
    "preview": "void get_bundle_list(const char *exec_name,\n                     const void **bundle_list, size_t *bundle_list_size);\n"
  },
  {
    "path": "darwin-bootstrap/substituted.m",
    "chars": 17648,
    "preview": "#include \"darwin/xxpc.h\"\n#include \"substitute.h\"\n#import <Foundation/Foundation.h>\n#import <CoreFoundation/CoreFoundatio..."
  },
  {
    "path": "darwin-bootstrap/unrestrict.c",
    "chars": 8746,
    "preview": "/* This is an iOS executable spawned from posixspawn-hook.dylib, which accesses\n * a process and changes the name of any..."
  },
  {
    "path": "doc/installed-README.txt",
    "chars": 5,
    "preview": "todo\n"
  },
  {
    "path": "ent.plist",
    "chars": 394,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P..."
  },
  {
    "path": "generated/darwin-inject-asm.S",
    "chars": 9381,
    "preview": "/* Generated by script/gen-inject-asm.sh.  The relevant source is in-tree (make\n * out/darwin-inject-asm.S), but this fi..."
  },
  {
    "path": "generated/generic-dis-arm.inc.h",
    "chars": 85707,
    "preview": "/* Generated code; do not edit!\n   generated by tables/gen.js from imaon2 'f0e220720bbfb8f8e00e76af56806a28fc8739a2'..."
  },
  {
    "path": "generated/generic-dis-arm64.inc.h",
    "chars": 9273,
    "preview": "/* Generated code; do not edit!\n   generated by tables/gen.js from imaon2 'f0e220720bbfb8f8e00e76af56806a28fc8739a2'..."
  },
  {
    "path": "generated/generic-dis-thumb.inc.h",
    "chars": 11501,
    "preview": "/* Generated code; do not edit!\n   generated by tables/gen.js from imaon2 'f0e220720bbfb8f8e00e76af56806a28fc8739a2'..."
  },
  {
    "path": "generated/generic-dis-thumb2.inc.h",
    "chars": 147963,
    "preview": "/* Generated code; do not edit!\n   generated by tables/gen.js from imaon2 'f0e220720bbfb8f8e00e76af56806a28fc8739a2'..."
  },
  {
    "path": "generated/manual-mach.inc.h",
    "chars": 24334,
    "preview": "typedef mach_port_t thread_read_t;\ntypedef mach_port_t vm_map_read_t;\n/* Routine thread_get_state */\nstatic\nkern_return_..."
  },
  {
    "path": "lib/arm/arch-dis.h",
    "chars": 2324,
    "preview": "#pragma once\n#define MIN_INSN_SIZE 2\n/* each input instruction might turn into:\n * - 2 bytes for Bcc, if in IT\n * then O..."
  },
  {
    "path": "lib/arm/arch-transform-dis.inc.h",
    "chars": 7880,
    "preview": "/* TODO fix BL incl MOV LR, PC */\n#include \"arm/assemble.h\"\n\nstatic struct assemble_ctx tdctx_to_actx(const struct trans..."
  },
  {
    "path": "lib/arm/assemble.h",
    "chars": 4205,
    "preview": "#pragma once\n#include \"dis.h\"\n\nstruct assemble_ctx {\n    void **codep;\n    void *code_base;\n    uint_tptr pc_of_code_bas..."
  },
  {
    "path": "lib/arm/dis-arm.inc.h",
    "chars": 10737,
    "preview": "#include \"dis.h\"\n\n/*\n    ARM\n           65 24-20\n    LDRSB: 10 xx1x1\n    LDRH:  01 xx1x1\n    LDRSH: 11 xx1x1\n    LDRD:..."
  },
  {
    "path": "lib/arm/dis-main.inc.h",
    "chars": 434,
    "preview": "#include \"dis-thumb.inc.h\"\n#include \"dis-thumb2.inc.h\"\n#include \"dis-arm.inc.h\"\n\nstatic INLINE void P(dis)(tdis_ctx ctx)..."
  },
  {
    "path": "lib/arm/dis-thumb.inc.h",
    "chars": 4420,
    "preview": "#include \"dis.h\"\nstatic INLINE void P(GPR_Rm_unk_Rdn_1_tADDhirr)(tdis_ctx ctx, struct bitslice Rdn, struct bitslice Rm)..."
  },
  {
    "path": "lib/arm/dis-thumb2.inc.h",
    "chars": 10180,
    "preview": "#include \"dis.h\"\n\nstatic inline unsigned flip16(unsigned op) {\n    return op >> 16 | op << 16;\n}\n\nstatic inline enum pcr..."
  },
  {
    "path": "lib/arm/jump-patch.h",
    "chars": 677,
    "preview": "#pragma once\n#include \"dis.h\"\n#include \"arm/assemble.h\"\n#define MAX_JUMP_PATCH_SIZE 8\n#define MAX_EXTENDED_PATCH_SIZE (M..."
  },
  {
    "path": "lib/arm/misc.h",
    "chars": 72,
    "preview": "#pragma once\n#define TARGET_POINTER_SIZE 4\n#define TARGET_DIS_SUPPORTED\n"
  },
  {
    "path": "lib/arm64/arch-dis.h",
    "chars": 1490,
    "preview": "#pragma once\n#define MIN_INSN_SIZE 4\n#define TD_MAX_REWRITTEN_SIZE (7 * 2 * 4) /* also conservative */\n#define ARCH_MAX_..."
  },
  {
    "path": "lib/arm64/arch-transform-dis.inc.h",
    "chars": 1896,
    "preview": "#include \"arm64/assemble.h\"\n\nstatic NOINLINE UNUSED\nvoid transform_dis_pcrel(struct transform_dis_ctx *ctx, uint_tptr dp..."
  },
  {
    "path": "lib/arm64/assemble.h",
    "chars": 2367,
    "preview": "#pragma once\n#include \"dis.h\"\n\nstatic inline int size_of_MOVi64(uint64_t val) {\n    int num_nybbles = val == 0 ? 1 : ((6..."
  },
  {
    "path": "lib/arm64/dis-main.inc.h",
    "chars": 3495,
    "preview": "static INLINE void P(adrlabel_label_unk_Xd_1_ADR)(tdis_ctx ctx, struct bitslice Xd, struct bitslice label) {\n    return..."
  },
  {
    "path": "lib/arm64/jump-patch.h",
    "chars": 952,
    "preview": "#pragma once\n#include \"arm64/assemble.h\"\n#define MAX_JUMP_PATCH_SIZE 20\n#define MAX_EXTENDED_PATCH_SIZE MAX_JUMP_PATCH_S..."
  },
  {
    "path": "lib/arm64/misc.h",
    "chars": 72,
    "preview": "#pragma once\n#define TARGET_POINTER_SIZE 8\n#define TARGET_DIS_SUPPORTED\n"
  },
  {
    "path": "lib/cbit/cqueue.c",
    "chars": 790,
    "preview": "#include \"circle.h\"\n\nvoid cqueue_realloc_internal(struct cqueue_internal *ci,\n                             size_t new_ca..."
  },
  {
    "path": "lib/cbit/cqueue.h",
    "chars": 1945,
    "preview": "#pragma once\n#include \"misc.h\"\n\nstruct cqueue_internal {\n    char *start, *end, *read_ptr, *write_ptr;\n};\n\n#ifdef __cplu..."
  },
  {
    "path": "lib/cbit/htab.h",
    "chars": 11559,
    "preview": "#pragma once\n#include \"misc.h\"\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n\nstruct..."
  },
  {
    "path": "lib/cbit/misc.h",
    "chars": 1268,
    "preview": "#pragma once\n\n#define UNUSED_STATIC_INLINE __attribute__((unused)) static inline\n\n#define LET_LOOP__(expr, ctr) \\\n    if..."
  },
  {
    "path": "lib/cbit/vec.c",
    "chars": 985,
    "preview": "#include \"vec.h\"\n#include <stdio.h>\n#include <stdlib.h>\n\nvoid vec_realloc_internal(struct vec_internal *vi, size_t new_c..."
  },
  {
    "path": "lib/cbit/vec.h",
    "chars": 4509,
    "preview": "#pragma once\n#include \"misc.h\"\n#include <stdlib.h>\n#include <string.h>\n\nstruct vec_internal {\n    size_t length;\n    siz..."
  },
  {
    "path": "lib/darwin/execmem.c",
    "chars": 32665,
    "preview": "/* define to avoid error that ucontext is \"deprecated\" (it's unavoidable with\n * sigaction!) */\n#define _XOPEN_SOURCE 70..."
  },
  {
    "path": "lib/darwin/find-syms.c",
    "chars": 22067,
    "preview": "#ifdef __APPLE__\n\n#include <stdbool.h>\n#include <ptrauth.h>\n#include <dlfcn.h>\n#include <pthread.h>\n#include <sys/mman.h..."
  },
  {
    "path": "lib/darwin/inject-asm-raw.c",
    "chars": 2761,
    "preview": "#define WANT_BSDTHREAD_TERMINATE\n#define WANT_SEMAPHORE_WAIT_TRAP\n#include \"darwin/manual-syscall.h\"\n\n#ifdef __arm64__\n#..."
  },
  {
    "path": "lib/darwin/inject-asm-raw.order",
    "chars": 7,
    "preview": "_entry\n"
  },
  {
    "path": "lib/darwin/inject.c",
    "chars": 26569,
    "preview": "#ifdef __APPLE__\n#include \"substitute.h\"\n#include \"substitute-internal.h\"\n#include \"darwin/read.h\"\n#include \"darwin/mach..."
  },
  {
    "path": "lib/darwin/interpose.c",
    "chars": 8240,
    "preview": "#ifdef __APPLE__\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"substitute.h\"\n#include \"substitute-internal.h\"\n#in..."
  },
  {
    "path": "lib/darwin/mach-decls.h",
    "chars": 1219,
    "preview": "#pragma once\n#include <stdint.h>\n#include <mach/mach.h>\n\nkern_return_t mach_vm_read_overwrite(vm_map_t, mach_vm_address_..."
  },
  {
    "path": "lib/darwin/manual-syscall.h",
    "chars": 1337,
    "preview": "#pragma once\n\n#define GEN_SYSCALL(name, num) \\\n    __asm__(\".private_extern _manual_\" #name \"\\n\" \\\n            \".pushsec..."
  },
  {
    "path": "lib/darwin/objc-asm.S",
    "chars": 1641,
    "preview": "#include \"objc.h\"\n.text\n.align _PAGE_SHIFT\n#ifdef __arm__\n.thumb_func _remap_start\n.thumb\n#endif\n.private_extern _remap_..."
  },
  {
    "path": "lib/darwin/objc.c",
    "chars": 8103,
    "preview": "#if defined(__APPLE__)\n#include \"substitute.h\"\n#include \"substitute-internal.h\"\n#include \"objc.h\"\n#include <stddef.h>\n#i..."
  },
  {
    "path": "lib/darwin/objc.h",
    "chars": 567,
    "preview": "#pragma once\n/* PAGE_SIZE is not actually a constant on iOS */\n#if defined(__arm64__)\n#define _PAGE_SHIFT 14\n#else\n#defi..."
  },
  {
    "path": "lib/darwin/read.c",
    "chars": 579,
    "preview": "#include \"darwin/read.h\"\nbool read_leb128(void **ptr, void *end, bool is_signed, uint64_t *out) {\n    uint64_t result =..."
  },
  {
    "path": "lib/darwin/read.h",
    "chars": 415,
    "preview": "#pragma once\n#include <string.h>\n#include <stdint.h>\n#include <stdbool.h>\n\nbool read_leb128(void **ptr, void *end, bool..."
  },
  {
    "path": "lib/darwin/substrate-compat.c",
    "chars": 3342,
    "preview": "#include \"substitute.h\"\n#include \"substitute-internal.h\"\n#include \"execmem.h\"\n#include \"ptrauth_helpers.h\"\n#include <os/..."
  },
  {
    "path": "lib/darwin/xxpc.h",
    "chars": 4109,
    "preview": "#pragma once\n/* distinct names to avoid incompatibility if <xpc/xpc.h> gets included somehow\n * on OS X. */\n#include <di..."
  },
  {
    "path": "lib/dis.h",
    "chars": 5075,
    "preview": "#pragma once\n\n#include \"substitute-internal.h\"\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdlib.h>\n\n#define IN..."
  },
  {
    "path": "lib/execmem.h",
    "chars": 1043,
    "preview": "#pragma once\n#include <sys/types.h>\n/* For allocating trampolines - this is just a mmap wrapper. */\nint execmem_alloc_un..."
  },
  {
    "path": "lib/hook-functions.c",
    "chars": 10897,
    "preview": "#include \"substitute-internal.h\"\n#ifdef TARGET_DIS_SUPPORTED\n#include \"substitute.h\"\n#include \"jump-dis.h\"\n#include \"tra..."
  },
  {
    "path": "lib/jump-dis.c",
    "chars": 5649,
    "preview": "#include \"cbit/vec.h\"\n#include \"substitute-internal.h\"\n#ifdef TARGET_DIS_SUPPORTED\n#define DIS_MAY_MODIFY 0\n#include \"di..."
  },
  {
    "path": "lib/jump-dis.h",
    "chars": 214,
    "preview": "#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n#include \"dis.h\"\n\nbool jump_dis_main(void *code_ptr, uintptr_t pc_..."
  },
  {
    "path": "lib/ptrauth_helpers.h",
    "chars": 805,
    "preview": "#ifndef PTRAUTH_HELPERS_H\n#define PTRAUTH_HELPERS_H\n// Helpers for PAC archs.\n\n// If the compiler understands __arm64e__..."
  },
  {
    "path": "lib/strerror.c",
    "chars": 1236,
    "preview": "#include \"substitute.h\"\n#include \"substitute-internal.h\"\n\nEXPORT\nconst char *substitute_strerror(int err) {\n    #define..."
  },
  {
    "path": "lib/substitute-internal.h",
    "chars": 3043,
    "preview": "#pragma once\n#include <Availability.h>\n#undef __TVOS_PROHIBITED\n#define __TVOS_PROHIBITED\n#undef __WATCHOS_PROHIBITED\n#d..."
  },
  {
    "path": "lib/substitute.h",
    "chars": 12840,
    "preview": "/*\n    libsubstitute - https://github.com/comex/substitute\n    This header file itself is in the public domain (or in an..."
  },
  {
    "path": "lib/transform-dis.c",
    "chars": 5355,
    "preview": "#include \"substitute-internal.h\"\n#ifdef TARGET_DIS_SUPPORTED\n#define DIS_MAY_MODIFY 1\n\n#include \"substitute.h\"\n#include..."
  },
  {
    "path": "lib/transform-dis.h",
    "chars": 505,
    "preview": "#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n#include \"dis.h\"\n\n#define TRANSFORM_DIS_BAN_CALLS 1\n\nint transform..."
  },
  {
    "path": "lib/x86/arch-dis.h",
    "chars": 441,
    "preview": "#pragma once\n#define MIN_INSN_SIZE 1\n/* min([18 * 3,\n *      4 + 18 + 15 + 18,\n *      6 + 12])\n * See transform_dis_* f..."
  },
  {
    "path": "lib/x86/arch-transform-dis.inc.h",
    "chars": 3885,
    "preview": "/* Pretty trivial, but in its own file to match the other architectures. */\n#include \"x86/jump-patch.h\"\n\nstatic inline v..."
  },
  {
    "path": "lib/x86/dis-main.inc.h",
    "chars": 11075,
    "preview": "/*\nrandom notes:\n\nREX: 0100wrxb\n\nprefixes REX opc ModR/M SIB displacement immediate\n\n1A/C: modrm stuff\ni64: 32 only\no64:..."
  },
  {
    "path": "lib/x86/jump-patch.h",
    "chars": 1149,
    "preview": "#pragma once\n#define MAX_JUMP_PATCH_SIZE 14\n#define MAX_EXTENDED_PATCH_SIZE (MAX_JUMP_PATCH_SIZE+14)\n#include \"dis.h\"\n\ns..."
  },
  {
    "path": "lib/x86/misc.h",
    "chars": 136,
    "preview": "#pragma once\n#ifdef TARGET_x86_64\n#define TARGET_POINTER_SIZE 8\n#else\n#define TARGET_POINTER_SIZE 4\n#endif\n#define TARGE..."
  },
  {
    "path": "script/gen-deb.sh",
    "chars": 1599,
    "preview": "#!/bin/bash\nset -e\ndebroot=out/debroot\nversion=\"$(git describe --always --dirty | sed 's/-/+/g')\"\nrm -rf $debroot\nmkdir..."
  },
  {
    "path": "script/gen-inject-asm.sh",
    "chars": 985,
    "preview": "#!/bin/sh\noutfile=\"$1\"\nshift\n(cat <<END\n/* Generated by script/gen-inject-asm.sh.  The relevant source is in-tree (make..."
  },
  {
    "path": "script/gen-manual-mach.py",
    "chars": 2407,
    "preview": "import subprocess, re\n# i wish there was a good simple tooling/ast library that could do this more cleanly\n# maybe libTo..."
  },
  {
    "path": "script/mconfig.py",
    "chars": 55084,
    "preview": "import re, argparse, sys, os, string, shlex, subprocess, glob, parser, hashlib, json, errno\nfrom collections import Orde..."
  },
  {
    "path": "script/test-transform-dis.sh",
    "chars": 256,
    "preview": "#!/bin/sh\nset -xe\nbarch=\"$1\"\nis_thumb=0\nif [ \"$1\" = \"thumb\" ]; then\n    barch=arm\n    is_thumb=1\nfi\nmake -j8 out/transfo..."
  },
  {
    "path": "substrate/substrate.h",
    "chars": 15687,
    "preview": "/* Cydia Substrate - Powerful Code Insertion Platform\n * Copyright (C) 2008-2011  Jay Freeman (saurik)\n*/\n\n/* Modified f..."
  },
  {
    "path": "substrate-shim.c",
    "chars": 2289,
    "preview": "#include <stdlib.h>\n#include <objc/runtime.h>\n#include \"lib/substitute.h\"\n\nextern void *SubGetImageByName(const char *fi..."
  },
  {
    "path": "test/injected-test-dylib.c",
    "chars": 790,
    "preview": "#include \"substitute-internal.h\"\n#include <stdio.h>\n#include <mach/mach.h>\n#include <assert.h>\n__attribute__((constructo..."
  },
  {
    "path": "test/insns-arm.S",
    "chars": 679,
    "preview": "#ifdef THUMB2\n.thumb\n.thumb_func\n.syntax unified\n#endif\n.global foo\nfoo:\nstrex r0, r1, [pc]\nmov r0, pc\n#ifndef THUMB2\nad..."
  },
  {
    "path": "test/insns-libz-arm.S",
    "chars": 7573,
    "preview": "#ifdef THUMB2\n.thumb\n.thumb_func\n.syntax unified\n#endif\n_deflateInit2_:\n@ BB#0:\n\tpush\t{r4, r5, r6, r7, lr}\n\tadd\tr7, sp,..."
  },
  {
    "path": "test/lol.c",
    "chars": 2387,
    "preview": "#include <stdint.h>\n#include <stdlib.h>\n#include <stdio.h>\n#define IF_BOTHER_WITH_MODIFY(...) __VA_ARGS__\n#include \"dis...."
  },
  {
    "path": "test/test-dis.c",
    "chars": 565,
    "preview": "#include <stdio.h>\n#include \"dis.h\"\nunsigned f(unsigned x) {\n    struct bitslice addr = {.nruns = 4, .runs = (struct bit..."
  },
  {
    "path": "test/test-execmem.c",
    "chars": 1202,
    "preview": "#include \"substitute-internal.h\"\n#include \"execmem.h\"\n#include <stdio.h>\n#include <search.h> /* for the victim */\n#inclu..."
  },
  {
    "path": "test/test-find-syms.c",
    "chars": 502,
    "preview": "#include <substitute.h>\n#include <stdio.h>\n#include <assert.h>\n#include <dlfcn.h>\n\nint main() {\n\tconst char *foundation..."
  },
  {
    "path": "test/test-hook-functions.c",
    "chars": 2032,
    "preview": "#include \"substitute.h\"\n#include \"substitute-internal.h\"\n#include <stdio.h>\n#include <search.h>\n#include <unistd.h>\n#inc..."
  },
  {
    "path": "test/test-htab.c",
    "chars": 2856,
    "preview": "#include \"cbit/htab.h\"\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\nstruct teststr {..."
  },
  {
    "path": "test/test-imp-forwarding.m",
    "chars": 1718,
    "preview": "#include \"../lib/darwin/objc.c\"\n#include <objc/runtime.h>\n#include <stdio.h>\n#include <assert.h>\n#import <Foundation/Fou..."
  },
  {
    "path": "test/test-inject.c",
    "chars": 1275,
    "preview": "#include \"substitute.h\"\n#include \"substitute-internal.h\"\n\n#include <unistd.h>\n#include <stdio.h>\n#include <stdlib.h>\n#in..."
  },
  {
    "path": "test/test-interpose.c",
    "chars": 826,
    "preview": "#include \"substitute.h\"\n\n#include <unistd.h>\n#include <stdio.h>\n#include <mach-o/dyld.h>\n#include <assert.h>\n\nstatic voi..."
  },
  {
    "path": "test/test-jump-dis.c",
    "chars": 536,
    "preview": "#define JUMP_DIS_VERBOSE\n#include <stdio.h>\n#include \"jump-dis.c\"\n#include <stdlib.h>\nint main(UNUSED int argc, char **a..."
  },
  {
    "path": "test/test-objc-hook.m",
    "chars": 1117,
    "preview": "#include \"substitute.h\"\n#import <Foundation/Foundation.h>\n\n@interface Base : NSObject {\n}\n- (void)foo:(NSString *)str;\n-..."
  },
  {
    "path": "test/test-pc-patch.c",
    "chars": 1413,
    "preview": "#include \"substitute-internal.h\"\n#include \"execmem.h\"\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#includ..."
  },
  {
    "path": "test/test-posixspawn-hook.c",
    "chars": 275,
    "preview": "#include <spawn.h>\n#include <stdlib.h>\n#include <stdio.h>\nint main(__attribute__((unused)) int argc, char **argv) {..."
  },
  {
    "path": "test/test-substrate.cpp",
    "chars": 767,
    "preview": "#include <substrate.h>\n#include <stdio.h>\n#include <assert.h>\n#include <dlfcn.h>\n#include <time.h>\n\nint main() {\n\tconst..."
  },
  {
    "path": "test/test-td-simple.c",
    "chars": 3017,
    "preview": "#include <stdint.h>\n#include <stdlib.h>\n#include <stdio.h>\n#define IF_BOTHER_WITH_MODIFY(...) __VA_ARGS__\n#include \"dis...."
  },
  {
    "path": "test/test-transform-dis.c",
    "chars": 5798,
    "preview": "#include <stdio.h>\n#define TRANSFORM_DIS_VERBOSE 1\n#include \"transform-dis.c\"\n#include <stdlib.h>\n#include <string.h>\n#i..."
  },
  {
    "path": "test/test-vec.cpp",
    "chars": 612,
    "preview": "#include \"cbit/vec.h\"\n#include <stdio.h>\nDECL_VEC(const char *, ccp);\nDECL_VEC(int, int);\n\nint main() {\n    VEC_STORAGE(..."
  },
  {
    "path": "test/transform-dis-cases-arm.S",
    "chars": 320,
    "preview": "#include \"transform-dis-cases.h\"\n#ifdef THUMB\n.thumb\n#endif\n\n\n#ifndef THUMB\nGIVEN blne 0f; nop; nop; 0:\nEXPECT beq 1f; m..."
  },
  {
    "path": "test/transform-dis-cases-arm64.S",
    "chars": 472,
    "preview": "#include \"transform-dis-cases.h\"\n\n/* yay clang, no semicolons allowed */\n\nGIVEN\n    blr x5\nEXPECT\n    blr x5\n\nGIVEN..."
  },
  {
    "path": "test/transform-dis-cases-i386.S",
    "chars": 725,
    "preview": "#include \"transform-dis-cases.h\"\n\nGIVEN call 0f; 0: pop %edx\n/* XXX the extra push isn't necessary in 32-bit mode */\nEXP..."
  },
  {
    "path": "test/transform-dis-cases-x86_64.S",
    "chars": 480,
    "preview": "#define GIVEN .ascii \"GIVEN\";\n#define EXPECT .ascii \"EXPECT\";\n#define EXPECT_ERR .ascii \"EXPECT_ERR\";\n\nGIVEN nopl (%rip)..."
  },
  {
    "path": "test/transform-dis-cases.h",
    "chars": 115,
    "preview": "#pragma once\n#define GIVEN .ascii \"GIVEN\";\n#define EXPECT .ascii \"EXPECT\";\n#define EXPECT_ERR .ascii \"EXPECT_ERR\";\n"
  },
  {
    "path": "vendor/dyld_cache_format.h",
    "chars": 7231,
    "preview": "/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- \n *\n * Copyright (c) 2006-2009 Apple Inc. All rights reserved.\n *..."
  }
]

About this extraction

This page contains the full source code of the sbingner/substitute GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 115 files (751.3 KB), approximately 225.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!