======================================================================== OAT containers: what's actually AOT-compiled to native code, and where ======================================================================== :doc:`app-inventory` catalogues the 124 preinstalled APKs but explicitly does not open their compiled Java. This page does: it looks inside the **308** ``.odex`` files under ``oat//`` (each with a matching ``.vdex`` sitting right there) plus 60 more ``boot-*.vdex`` files that live directly in ``system/framework/`` rather than under any ``oat/`` subdirectory — 368 ``.vdex`` files total — across ``system_a``, ``vendor_a``, ``product_a`` and ``system_ext_a``, establishes what container format they actually are on this device, and disassembles the handful of places that hold genuine AArch64 machine code rather than interpreted DEX bytecode. Key finding: almost none of it is native code. Of 308 ``.odex`` files, only **28 (9.1%)** contain any AOT-compiled machine code at all — the rest are ELF containers whose "executable" region is literally absent, because ART's build-time compiler filter for this image is ``verify`` for the overwhelming majority of apps (including every first-party Boox/Onyx app checked). The exceptions are System Server and a cluster of Mainline APEX system services, which are ``speed``/``speed-profile`` compiled and do hold megabytes of real compiled code — plus one vendor outlier, ``TrustZoneAccessService``, whose *only* compiled code is six JNI trampolines bridging into Qualcomm's Mink/QSEE IPC layer. Container format, confirmed against raw bytes =============================================== An ``.odex`` under ``oat//`` is a real ``ET_DYN`` ELF shared object. Confirmed directly on this dump (not assumed from AOSP documentation): .. code-block:: text $ xxd artifacts/system_a/system/app/knote2-release/oat/arm64/knote2-release.odex | head -1 00000000: 7f45 4c46 0201 0103 0000 0000 0000 0000 .ELF............ 00000010: 0300 b700 0100 0000 0000 0000 0000 0000 ................ ``e_ident`` = ``7f 45 4c 46 02 01 01 03`` (ELF64, little-endian, ``EI_OSABI`` = 3/Linux); ``e_type`` = 3 (``ET_DYN``); ``e_machine`` = ``0x00b7`` = 183 = ``EM_AARCH64``. Every ``oat/arm64/*.odex`` on this device (216 of the 308) is this format. The other 92 — the ``oat/arm/`` 32-bit compatibility copies — are the ELF32/``EM_ARM`` (40) equivalent of the same container; confirmed by extending the same parser to 32-bit ``Elf32_Phdr``/``Elf32_Sym`` layout (see `Provenance`_). Section layout (``services.odex``, the ``system_server`` OAT file, as a representative example — ``llvm-objdump -h``): .. code-block:: text Idx Name Size VMA Type 1 .note.gnu.build-id 00000024 00000000000003c0 DATA 2 .rodata 00163000 0000000000001000 DATA 3 .text 003c9968 0000000000164000 TEXT 4 .data.img.rel.ro 000031cc 000000000052e000 DATA 5 .bss 0000cd30 0000000000532000 BSS 6 .dex 00048990 000000000053f000 BSS 7 .dynstr / .dynsym / .hash / .dynamic (standard dynamic-linking sections) and its dynamic symbol table (no ``.symtab`` — these are stripped, but the dynsym table is intact and readable with ``llvm-objdump -T`` without needing to hand-walk ``PT_DYNAMIC``): .. code-block:: text 0000000000001000 g DO .rodata 0000000000163000 oatdata 0000000000164000 g DO .text 0000000000000000 oatexec 000000000052d964 g DO .text 0000000000000004 oatlastword 000000000052e000 g DO .data.img.rel.ro 00000000000031cc oatdataimgrelro 0000000000532000 g DO .bss 0000000000007ce8 oatbss 000000000053f000 g DO .dex 0000000000000000 oatdex 000000000058798c g DO .dex 0000000000000004 oatdexlastword ``oatdata`` marks the start of ``.rodata``: the OAT header itself, the key/value metadata store, and per-dex-file tables. ``oatexec``/``oatlastword`` bound the actual compiled-native-code region — note ``oatexec``'s own *symbol size* is always reported as 0 by the linker (it's a boundary marker, not a sized object); the real size of the executable region has to be computed as ``oatlastword.value + oatlastword.size - oatexec.value``. For ``services.odex`` that is ``0x52d964 + 4 - 0x164000 = 0x3c9968`` bytes (3,971,432 bytes) — matching the ``.text`` section size exactly. **When an OAT file has zero compiled methods, the ``oatexec``/``oatlastword`` symbols are absent from the dynsym table entirely** (not present-with-zero-size) — confirmed on every "verify"-filter file checked (see next section). ``oatdex`` marks the embedded/quickened DEX blob (``.dex`` section, mapped ``SHT_NOBITS`` in some files — see the ``system_server`` table above where it's typed ``BSS``, meaning the on-disk ``.odex`` doesn't even carry those bytes; they come from the paired ``.vdex`` at load time via ``mmap``). OAT header magic, confirmed in every file checked: .. code-block:: text 00001000: 6f61 740a 3234 3400 ... oat\n244\0 ... ``oat\n`` (4 bytes) + a NUL-terminated ASCII version string. Every file in this dump reports version **244** except four vendor-partition apps (``IWlanService``, ``TimeService``, ``TrustZoneAccessService``, ``pasrservice`` — all under ``artifacts/vendor_a/app/``), which report version **183**. This tracks the same Android-version split documented in :doc:`onyx-platform` and :doc:`security`: the ``vendor`` partition is frozen on an older Android/ART release than ``system``, and these four vendor apps were compiled by that older toolchain's ``dex2oat``. We did not locate or verify an authoritative OAT-version-to-AOSP-tag mapping for 183 vs. 244 — both numbers are read directly from the header bytes, not looked up. Following the fixed header fields is a flat key/value store of NUL-terminated ``key\0value\0`` pairs (``apex-versions``, ``bootclasspath``, ``classpath-dir``, ``compiler-filter``, ``debuggable``, ...). We did not reverse-engineer the exact fixed-width ``OatHeader`` struct that precedes this store (checksum/instruction-set/trampoline-offset fields) — that layout is known to have changed repeatedly across AOSP ``art/runtime/oat.h`` revisions and pinning it for version 244 specifically was out of scope here. What we did instead, robustly and without needing the struct layout, is a direct byte search for the ``compiler-filter\0`` key and read its value — see ``oat_scan.py`` in `Provenance`_. The compiler-filter reality ============================= This is the load-bearing finding. ``compiler-filter`` is an ART build-time setting that controls how much of an APK's DEX bytecode ``dex2oat`` actually turns into native machine code at build time, versus leaving to be interpreted (or quickened — bytecode rewritten in place with faster virtual-dispatch opcodes, still not native) at runtime. Scanning all 308 ``.odex`` files (script: ``oat_scan.py``, see `Provenance`_): .. code-block:: text compiler-filter distribution (308 files): verify 277 (89.9%) -- zero compiled methods, no oatexec symbol at all speed 24 ( 7.8%) -- full AOT compilation, large oatexec quicken 4 ( 1.3%) -- only JNI-native-method trampolines compiled speed-profile 3 ( 1.0%) -- profile-guided partial AOT compilation oatexec symbol present: 28 / 308 (9.1%) ``verify`` means ``dex2oat`` did class/method **verification only** — no compilation whatsoever, not even the small JNI entry stubs. Every single one of the 277 ``verify``-filter files we checked has *no* ``oatexec`` symbol in its dynamic symbol table (confirmed both by ``llvm-objdump -T`` spot checks and by the bulk scanner): the ``.text`` section, if a section-header table is even present, is size 0. This includes **every first-party Onyx/Boox app in the dump** that was checked: ``knote2-release`` (Notes), ``kreader2-release`` (Reader), ``kcb-release``, ``ksync-release``, ``kmail-release``, ``floatingbutton-release``, ``dict-release``, ``ai-assistant-release``, ``OnyxLatinIME-release``, and ``SystemUI`` itself. None of these apps have a single AOT-compiled method on this device image — everything in them runs interpreted or quickened-interpreted at runtime, compiled on-device later (if at all) by the JIT/profile-guided background ``dex2oat`` pass that Android normally runs after enough real-world usage, which isn't captured in a factory image dump. ``speed`` / ``speed-profile`` (27 files total) is where the real compiled code lives, and it is concentrated almost entirely in ``system`` framework and Mainline APEX service jars — i.e., the platform's own core services, not any app: .. code-block:: text oatexec size filter file 9,352,200 speed apex@com.android.wifi@...@service-wifi.jar@classes.odex 3,971,432 speed-profile system/framework/oat/arm64/services.odex (system_server) 3,676,216 speed system/app/KeyChain/oat/arm64/KeyChain.odex 2,873,976 speed apex@com.android.tethering@...@service-connectivity.jar@classes.odex 1,747,192 speed apex@com.android.appsearch@...@service-appsearch.jar@classes.odex 1,692,168 speed apex@com.android.uwb@...@service-uwb.jar@classes.odex 1,232,920 speed apex@com.android.permission@...@service-permission.jar@classes.odex 684,792 speed priv-app/SettingsProvider/oat/arm64/SettingsProvider.odex ... (19 more apex service-*.jar entries, 9.2 KB - 918 KB each) 36 speed priv-app/InputDevices/oat/arm64/InputDevices.odex (Full 28-row table with exact byte counts: ``/tmp/oat_scan_all.csv`` from the ``--find`` run described in `Provenance`_ — not checked into the repo since it's a full re-derivable dump, but the command to reproduce it is given there.) Note the long tail down to ``InputDevices.odex`` at a mere 36 bytes — a single trivial compiled method (see below) sitting alongside gigabytes of interpreted-only DEX. ``KeyChain.odex`` is the one exception among **apps** (not framework/APEX services) with full ``speed`` compilation — plausible given it's a `boot-critical`, security-sensitive system app, which (per general, unverified-for-this-device knowledge of Android's compilation policy) tends to be prioritized for AOT compilation. Disassembly: three real ``oatexec`` regions ============================================== All addresses below are file-relative (this ELF's ``PT_LOAD`` segments are a 1:1 file-offset-equals-vaddr mapping, verified with ``elfdis.py --segs`` on each file — no relocation/rebasing needed). Disassembled with ``elfdis.py`` (capstone 5.0.7, AArch64) from ``artifacts/re_static/``. **One artifact worth flagging up front**: a raw linear disassembly starting *exactly* at the ``oatexec`` symbol's address routinely decodes a few garbage/``udf`` instructions before it locks onto real code. This isn't a bug in the tooling — it's a small non-instruction header (ART's per-method ``OatQuickMethodHeader``: a packed, variable-length-encoded record of vmap-table offset, CFI offset and frame info that precedes every compiled method's entry point) sitting in the byte stream. capstone's linear disassembler has no way to know to skip it, so it either decodes it as nonsense (``udf #0`` happens to be the AArch64 encoding for four zero bytes) or, worse, desyncs and stops entirely at the first byte sequence with no valid encoding. We did not reverse the exact ``OatQuickMethodHeader`` byte layout for OAT version 244; the practical workaround used throughout this section was to nudge the start address forward by 0x10–0x20 bytes past a run of zeros/garbage until capstone locks onto a recognizable AArch64 function prologue. 1. ``services.odex`` (``system_server``, ``speed-profile``) — first compiled method --------------------------------------------------------------------------------------- Raw bytes at ``oatexec`` (``0x164000``): .. code-block:: text 00164000: 0000 0000 0000 0000 0000 0000 4ff4 0e00 ................ 12 zero bytes (the tail of the method header) then non-instruction bytes, then at ``0x164010`` a completely ordinary ART "quick"-ABI method prologue: .. code-block:: text 0x164010 sub sp, sp, #0xb0 0x164014 stp x19, x20, [sp, #0x50] 0x164018 stp x21, x22, [sp, #0x60] 0x16401c stp x23, x24, [sp, #0x70] 0x164020 stp x25, x26, [sp, #0x80] 0x164024 stp x27, x28, [sp, #0x90] 0x164028 stp x29, x30, [sp, #0xa0] 0x16402c stp d8, d9, [sp, #0x10] 0x164030 stp d10, d11, [sp, #0x20] 0x164034 stp d12, d13, [sp, #0x30] 0x164038 stp d14, d15, [sp, #0x40] 0x16403c str x0, [sp] ... 0x16404c mov x16, sp 0x164050 str x16, [x19, #0xa8] 0x164054 ldxr w16, [x19] 0x164058 mov w17, #0x5c000000 0x16405c cbnz w16, #0x164110 0x164060 stlxr w16, w17, [x19] 0x164064 cbnz w16, #0x164054 ... 0x164098 mov x0, x22 0x16409c ldr x30, [x25, #0x10] 0x1640a0 blr x30 0x1640a4 ldaxr w16, [x19] 0x1640a8 mov w17, #0x5c000000 0x1640ac cmp w16, w17 0x1640b0 b.ne #0x16411c 0x1640b4 stxr w16, wzr, [x19] 0x1640b8 cbnz w16, #0x1640a4 ... 0x164104 ldr w20, [x19, #0x20] 0x164108 add sp, sp, #0xb0 0x16410c ret Register ``x19`` is ART's reserved "Thread self" pointer in the AAPCS64 "quick" calling convention (never used as a general-purpose scratch register by ``dex2oat``-generated code). The body is an ``ldxr``/``stlxr`` compare-and-swap loop against ``[x19]`` (the ``Thread::tls32_.state_and_flags`` word) toggling between a runnable value and ``0x5c000000`` before and after an indirect call through ``x30`` loaded from ``[x25, #0x10]`` — i.e. this is a thread-state transition to a "suspended/native" state, call out, then transition back. That is the textbook shape of a **compiled JNI entry stub** (the same pattern ART emits for ``GenericJNI``/``@FastNative`` native-method trampolines — see the ``TrustZoneAccessService`` example below for an unambiguous, name-attributed instance of the identical pattern). We cannot name *which* System Server method this specific instance belongs to without parsing the OAT method-offset table (not done — see `Honest limits`_), but the instruction-level identity of the pattern itself is not in doubt. 2. ``KeyChain.odex`` (``speed``) — GC write-barrier ("card mark") idiom ---------------------------------------------------------------------------- At ``oatexec + 0x20000`` (file offset ``0x109010``), a run of 5 back-to-back occurrences of the same 12-instruction idiom, each following a different object's field store: .. code-block:: text 0x109040 adrp x1, #0x472000 0x109044 ldr w1, [x1, #0xc8c] 0x109048 cbz w1, #0x109174 0x10904c mov x23, x0 0x109050 mov x0, x1 0x109054 ldr x30, [x19, #0x1d8] 0x109058 blr x30 0x10905c str w24, [x0, #8] 0x109060 ldr x16, [x19, #0x90] 0x109064 lsr w17, w0, #0xa 0x109068 strb w16, [x16, x17] 0x10906c dmb ishst Two recognizable ART codegen idioms stacked here: - ``adrp x1,#0x472000`` / ``ldr w1,[x1,#0xc8c]`` / ``cbz w1,`` is the standard "resolve-if-not-already-resolved" guard ART emits before touching a ``Class``/``String``/static-field target: the pointer lives in the ``.data.img.rel.ro`` boot-image-relocation section (this device's ``.data.img.rel.ro`` for ``KeyChain.odex`` spans ``0x46b000``–``0x46d384``, comfortably containing ``0x472000``... actually this particular target falls just past that window, meaning it resolves into the *boot image's* own relocatable data rather than this OAT file's private table — consistent with referencing a boot-classpath class/string, not an app-private one); on a cache miss it calls a runtime helper through ``[x19, #0x1d8]`` (a ``QuickEntryPoints`` slot in the ``Thread`` struct) to resolve it the slow way. - ``str w24,[x0,#8]`` (a plain object-reference field store) immediately followed by ``ldr x16,[x19,#0x90]`` (load the GC card-table base pointer, cached in ``Thread``), ``lsr w17,w0,#0xa`` (divide the stored-to address by 1024 to get a card index) and ``strb w16,[x16,x17]`` (mark that card dirty), then a ``dmb ishst`` store-store barrier. This is ART's mandatory **write barrier**: every compiled store of a heap-reference-typed field has to dirty the corresponding card in the concurrent-GC's card table, and the compiler always emits it inline rather than calling out. This exact five-times-in-a-row shape (5 consecutive field-store + card-mark blocks) is consistent with a compiled constructor or a `readFields`-style deserialization method initializing several reference fields in sequence, though which one specifically was not determined (same OAT-method-table caveat as above). 3. ``service-wifi`` apex jar (``speed``) — a single trivial compiled method -------------------------------------------------------------------------------- At the very start of ``oatexec`` (``0x213010``, past the same header-garbage gap seen in example 1): .. code-block:: text 0x213010 str w2, [x1, #0x10] 0x213014 cbz w2, #0x213024 0x213018 ldr x16, [x19, #0x90] 0x21301c lsr w17, w1, #0xa 0x213020 strb w16, [x16, x17] 0x213024 dmb ishst 0x213028 ret The same card-mark idiom as example 2, but with the compiler's null-check optimization visible: ``cbz w2, #0x213024`` skips the card-mark entirely when the value just stored is ``null`` (a null reference can never point at a young/uncollected object, so marking the card would be wasted work) — a 7-instruction compiled setter, and (per the byte-range check against ``oatlastword``) one of only a literal handful of compiled methods in this 12 MB APEX jar's OAT file relative to its multi-megabyte DEX payload. 4. ``TrustZoneAccessService.odex`` (vendor, ``quicken``) — six JNI stubs, name-attributed ============================================================================================= This is the one file in the whole scan where we could tie compiled code to named Java methods with confidence, because the paired ``.apk`` is small enough to extract its ``classes.dex`` directly and read the method table. ``artifacts/vendor_a/app/TrustZoneAccessService/TrustZoneAccessService.odex`` reports ``compiler-filter=quicken`` (not ``speed``) yet still has a nonzero ``oatexec`` — 1,752 bytes, from ``0x2000`` to ``0x26d4``. Under ``quicken``, ``dex2oat`` compiles **no regular Java-bytecode methods** but does still compile a small, fixed-shape entry trampoline for every ``native``-declared Java method, because there is no other way to call into JNI without one. Pulling ``classes.dex`` straight out of the APK (``7z e ... classes.dex``) and grepping its string pool confirms the class and its six native methods: .. code-block:: text Lcom/qualcomm/qti/qms/service/trustzoneaccess/TZAccessService; nativeGetEnvHandle nativeGetOpenerHandle nativeInit nativeInvoke nativeIsNull nativeRelease — six methods, and the disassembly of ``oatexec`` is **exactly six** back-to-back trampolines of near-identical shape (at ``0x2000``, ``0x21a0``, ``0x22a0``, ``0x2390``, ``0x2480``, ``0x25e0`` — each preceded by the same kind of header-garbage gap as example 1, each ending in a ``ret``/slow-path pair). The count matching exactly is strong circumstantial evidence for "these six stubs are these six methods, in some order"; we did **not** establish the exact stub-to-name mapping (that needs the OAT method-offset table, not done here), but the structural finding — this file's *entire* compiled-code footprint is JNI glue for a Mink/TrustZone IPC class, nothing else — stands on its own. One representative stub (``nativeInit``, most likely, given it takes the most arguments of the six — 6 registers pushed into the JNI local-ref frame vs. 1–5 for the others): .. code-block:: text 0x2010 sub sp, sp, #0xd0 0x2014 stp x19, x20, [sp, #0x70] ... ; full quick-ABI register save 0x203c str x0, [sp] ; this/jclass 0x2040 str w1, [sp, #0xd8] ; spill JNIEnv-visible args 0x2044 stur x2, [sp, #0xdc] 0x2048 str w3, [sp, #0xe4] 0x204c str w4, [sp, #0xe8] 0x2050 str w5, [sp, #0xec] 0x2054 str w6, [sp, #0xf0] 0x2058 str w7, [sp, #0xf4] 0x205c mov x16, #6 ; JNI local-ref-table "6 refs" marker 0x2060 str w16, [sp, #0x10] 0x2064 ldr x16, [x19, #0x128] ; Thread::tlsPtr_.jni_env / top handle scope 0x2068 str x16, [sp, #8] 0x206c add x16, sp, #8 0x2070 str x16, [x19, #0x128] ; push new HandleScope/local-ref frame ... ; per-arg: null stays null, else &stack-slot 0x20b0 mov x0, x19 0x20b4 ldr x16, [x0, #0x338] ; QuickEntryPoints: JNI method-start helper 0x20b8 blr x16 ... 0x2120 ldr x0, [x19, #0xd8] ; ArtMethod* / down-call target 0x2124 ldr x16, [sp, #0x10] 0x2128 ldr x16, [x16, #0x18] ; dlsym'd native fn pointer 0x212c blr x16 ; call into libnative-api.so 0x2130 ldr w0, [sp, #0x3c] 0x2134 mov x1, x19 0x2138 ldr x16, [x1, #0x350] ; QuickEntryPoints: JNI method-end helper 0x213c blr x16 0x2144 ldr x16, [x19, #0xa8] ; check pending suspend/exception 0x2148 cbnz x16, #0x2180 ; slow path -> brk #0 (deopt/exception trap) 0x214c ldp x19, x20, [sp, #0x70] ... ; full register restore 0x217c ret This is the ART "GenericJNI" argument-marshalling/handle-scope pattern: push a JNI local-reference-table frame recorded in ``Thread`` (``[x19, #0x128]``), replace each non-null reference argument with a pointer to its own stack slot (a lightweight indirect reference so the native side can't outlive the frame), call the JNI method-entry ``QuickEntryPoint`` (``[x19,#0x338]``), ``blr`` the actual native function pointer, call the JNI method-exit ``QuickEntryPoint`` (``[x19,#0x350]``/``[x19,#0x368]`` — two different exit helpers appear across the six stubs, unconfirmed whether this is return-type dependent (reference-returning vs. primitive-returning)), then check for a pending suspend request/exception before restoring registers. The dex string pool also contains the literal string ``native-api`` positioned immediately before the six ``nativeXxx`` method-name strings — consistent with being the argument to ``System.loadLibrary("native-api")``. ``libnative-api.so`` does exist on this device, at ``artifacts/vendor_a/lib64/libnative-api.so`` (and a 32-bit copy in ``lib/``) — confirming the target of these trampolines' final ``blr``, though that library's own internals were not examined here (out of scope — this page is about the OAT container, not the Mink native implementation). The dex string pool additionally references ``com.qualcomm.qti.qms.api.mink. {CMinkObject,IMinkObject,JMinkObject}`` and ``com.qualcomm.qti.qms.api.minksocket.IMinkSocketFd`` — Qualcomm's "Mink" IDL IPC framework, which is how privileged Android userspace processes invoke QSEE trustlets on this SoC family. This is the userspace-JNI edge of the same QSEECOM bridge :doc:`security` documents from the HAL/daemon side (``qseecomd`` / ``libQSEEComAPI.so`` / ``vendor.qti.hardware.qseecom@1.0``); ``TZAccessService`` is a second, separate (Mink-based rather than QSEECOM-ioctl-based) path into the same secure world, not previously catalogued on this page's sibling doc. The ``.vdex`` container (lower priority, not fully resolved) ================================================================ ``.vdex`` files hold the original/quickened DEX bytecode plus a small quickening-info table — never native code. Header, confirmed on ``services.vdex`` (297,360 bytes): .. code-block:: text 00000000: 7664 6578 3032 3700 0400 0000 0000 0000 vdex027......... 00000010: 3c00 0000 0c00 0000 0100 0000 0000 0000 <............... 00000020: 0000 0000 0200 0000 4800 0000 3b49 0200 ........H...;I.. Magic is ``vdex027\0`` (8 bytes) followed by several 32-bit fields we did **not** map to named ``VdexFile::VdexFileHeader`` struct members with confidence (same reasoning as the OAT header: the layout has shifted across AOSP versions and we had no source tree to check it against for "027" specifically). Structural confirmation that this is a DEX-bytecode container rather than something else: a plain byte search for ``Landroid``/``Ljava`` class-descriptor prefixes finds real hits starting around file offset ``0x101** (e.g. ``Landroid/view/accessibility/ AccessibilityRecord;`` at ``0x101x4``), and immediately before that region is a table of steadily-increasing 4-byte little-endian offsets — the shape of a DEX ``string_ids`` table pointing forward into a string-data section. Unexplained: a plain search for the literal bytes ``dex\n`` (the standalone-``.dex`` file magic + version marker, e.g. ``dex\n035\0``) returns **zero** matches anywhere in ``services.vdex``, despite the file demonstrably containing real DEX structural data (string tables, type descriptors). Either this vdex format variant elides the per-embedded-dex magic/checksum/signature header (storing dex payloads as a shared, header-less blob addressed purely by the vdex header's own offset/size fields) or the embedded dex bodies are processed in some way not identified here. The vdex section table was not fully parsed to resolve this. Honest limits ================ - No OAT method-offset table (the per-``OatDexFile``/per-class/per-method structures inside ``oatdata`` that map a DEX ``method_id`` to a code offset) was parsed. Every disassembly excerpt above is address-only — real, verified machine code at a real, verified file offset — but *method name attribution* was only possible for ``TrustZoneAccessService`` (by the 6-methods/6-stubs count match against its APK's DEX, not by parsing OAT metadata), and even there the specific stub-to-method order is not established. A full ``OatQuickMethodHeader`` / ``OatMethodOffsets`` parser would be needed for exact per-method boundaries. - The fixed-width portion of ``OatHeader`` preceding the key/value store (checksum, instruction-set-features bitmap, trampoline offsets, etc.) was not mapped field-by-field; only the leading magic/version and the key/value store (found by direct string search, not by walking the struct) were used. - The ``.vdex`` per-dex-file section layout was not resolved (see above). - ``libnative-api.so`` (the JNI target of the ``TrustZoneAccessService`` stubs) was located but not analyzed — out of scope for an OAT-container page. - 92 of the 308 ``.odex`` files are the ``oat/arm/`` 32-bit copies; the bulk scan covers them (ELF32 support was added to ``oat_scan.py``), but no 32-bit file's ``oatexec`` was individually hand-disassembled — spot checks of a few (``org.apache.http.legacy.odex``, ``com.android.location. provider.odex``) via the compiler-filter/size columns alone show the same filter/size pattern as their arm64 siblings, so no separate 32-bit disassembly examples are included here. Provenance ============ :Source: All 308 ``.odex`` files (each with a matching ``.vdex``) under ``artifacts/{system_a,vendor_a,product_a,system_ext_a}/**/oat/**/``, plus the 60 ``boot-*.vdex`` files directly under ``system_a/system/framework/`` (368 ``.vdex`` total). Specific files quoted above: ``artifacts/system_a/system/framework/oat/arm64/services.odex``, ``artifacts/system_a/system/app/KeyChain/oat/arm64/KeyChain.odex``, ``artifacts/system_a/system/framework/oat/arm64/apex@com.android.wifi@ javalib@service-wifi.jar@classes.odex``, ``artifacts/vendor_a/app/TrustZoneAccessService/oat/arm64/ TrustZoneAccessService.odex`` (+ its sibling ``.apk`` and ``.vdex`` in the same directory), ``artifacts/system_a/system/app/knote2-release/oat/ arm64/knote2-release.odex`` and the other Onyx-app odex files named in `The compiler-filter reality`_, and ``artifacts/system_a/system/framework/oat/arm64/services.vdex``. :Method: - Container identification: raw ``xxd``/Python ``struct`` reads of ``e_ident``/``e_machine``/``e_type`` and the ``oat\n`` magic; cross- checked against ``llvm-objdump -h``/``-T`` (pixi LLVM 23.1.1) which independently confirms the section table and dynamic symbol table. - Bulk survey of all 308 files: ``artifacts/re_static/oat_scan.py`` (written for this task, saved for reuse) — walks ``PT_DYNAMIC`` -> ``DT_SYMTAB``/``DT_STRTAB``/``DT_HASH`` by hand (no reliance on a section-header table, works on stripped ELFs) to recover ``oatdata``/``oatexec``/``oatlastword`` and computes real ``oatexec`` size from symbol values; separately byte-searches the first 32 KiB for the ``compiler-filter\0`` key. Handles both ELF64/``EM_AARCH64`` ( ``oat/arm64/``) and ELF32/``EM_ARM`` (``oat/arm/``). Reproduce with: ``python3 artifacts/re_static/oat_scan.py --csv --find artifacts > /tmp/oat_scan_all.csv`` (takes a few seconds; no external dependencies beyond the standard library). - Disassembly: ``artifacts/re_static/elfdis.py`` (pre-existing tool, reused as-is) with system ``/usr/bin/python3`` + capstone 5.0.7 (the pixi python lacks capstone, so the system interpreter was used instead). - ``TrustZoneAccessService`` method-name attribution: ``7z e TrustZoneAccessService.apk classes.dex``, then ``strings classes.dex`` grepped for the class's method-name string-pool entries (no DEX decompiler used — this was a raw string-pool read, not a jadx-style decompile). - No code was executed; no OAT/DEX/VDEX file was mutated. All figures (counts, sizes, offsets) are re-derivable from the commands given above against the artifacts as they sit in this checkout. :Cross-refs: :doc:`app-inventory` (the app catalogue this page opens up), :doc:`onyx-platform` (the Android-version split between ``system`` and ``vendor`` that explains the OAT-version-183-vs-244 divide), :doc:`security` (the QSEECOM/Mink bridge to TrustZone that ``TrustZoneAccessService`` is a second instance of), :doc:`platform-boot` (``OnyxOtaService``, decompiled separately by DEX source rather than by this page's OAT-container approach — its own ``.odex``/``.vdex`` AOT copy was not separately examined there, and is covered by this page's bulk scan: ``compiler-filter=verify``, no compiled code, like the other Onyx apps above).