For the complete documentation index, see llms.txt. This page is also available as Markdown.

Dyld Shared Cache

This page walks through how IDA 9.4 lets you analyse a Dyld Shared Cache: opening one, navigating its contents from the DSC Index, loading the bits you actually care about, and locating things across the cache without having to load everything first.

Apple's other cache, the kernelcache, works the same way in IDA; see Apple Kernelcache.

Opening a DSC

When IDA loads a Dyld Shared Cache, the loader's only job is to map the cache header into the database and prepare the internal data structures needed to navigate the cache. Every image, branch mapping, GOT and gap is left for the user to load on demand, through the workflow described below.

DSC open dialog
The initial DSC open dialog

The DSC Index

The DSC Index is the entry point to everything else. It is a tree showing every region of the cache (images, branch islands, branch mappings, GOTs, gaps) along with their sizes, addresses, and whether they're currently loaded.

DSC Index widget, all regions collapsed
The DSC Index widget, top-level kinds visible

If you ever close the panel, reopen it through the Dyld Shared Cache (DSC) index action under View > Open subviews.

Region kinds

  • Images — the actual dylibs and frameworks. By far the most interesting set of regions in the cache, and typically what reversers will look at first.

  • Branch islands — trampolines for long branches. They are an artefact of older caches; recent caches don't use them, so you will likely never encounter any (the screenshot above shows none for that very reason).

  • Branch mappings — __stubs shared between subcaches. The modern equivalent of branch islands: just trampolines, vastly uninteresting to a reverser. IDA may need to auto-load some of them to satisfy an image's dependencies, so don't be surprised to see branch mappings get loaded without you explicitly asking.

  • GOTs — global offset tables (iOS 16+ on arm64). Mostly pointers and jumps, mostly uninteresting; like branch mappings, may be auto-loaded as needed.

  • Gaps — regions the DSC loader couldn't classify when it scanned the cache.

Once expanded, every region carries its address, size, and loaded state:

DSC Index widget, expanded with addresses and sizes
The DSC Index with /usr/lib/system expanded; libxpc.dylib loaded

On-demand loading

The whole workflow is built on the assumption that any address may or may not be backed by loaded bytes at any given moment. Cross-references to unmapped addresses do not break, decompilation does not abort, and the user is free to load more whenever they want. Several entry points trigger a load:

  • the DSC Index context menu (described in Loading images below);

  • in-listing prompts — when navigating the disassembly, unmapped targets are decorated with a ⇗ marker. Double-clicking, or pressing Enter on, such a span prompts to load the target image.

In-listing arrow (disassembly)
The "⇗" marker in disassembly, on a pointer targeting an unmapped library

The same arrow appears in the decompiler — here on a thunk whose only purpose is to call a routine in another (still-unmapped) image:

In-listing arrow (decompiler)
The "↗" marker in the decompiler, on a thunk to an unmapped library

The purpose, throughout, is to give the user a hook (a double-click, an Enter key) and have IDA offer to load whatever needs to be loaded, seamlessly.

Loading images

The DSC Index context menu offers three actions to load regions:

  • Selected modules — load whatever is selected, as-is.

  • Selection with dependencies... — load the selection plus its dependencies, with a dialog to control depth and per-dependency inclusion.

  • From external Mach-O... — match the load commands of an off-cache Mach-O against this DSC and load the matching dependencies. See the action page for the full walkthrough.

In addition to the in-listing prompts mentioned in On-demand loading, the normal navigation actions of the disassembly and the decompiler may invite the user to load further images as needed.

Load with dependencies dialog
The dependencies picker dialog

Double-clicking and reload prompts

Double-clicking a region in the DSC Index jumps to its start address; if the region is not yet loaded, a confirmation dialog asks whether to load it first.

If you ask to load something that is already loaded, IDA prompts with <region> is already loaded. Do you want to load it again? (default: No).

The Functions list

Images loaded from one cache share an address space, so without grouping their functions would be a single flat heap. They are not: each image's functions go into a folder of their own, named after the image and nested under its install path, so /usr/lib/system/libxpc.dylib sits under usr -> lib -> system.

The Functions list, grouped by image
Three loaded images, each with a folder of its own; the functions below the folders belong to no image

The folder name is a configuration option; set it to group images differently, or to the empty string for a flat list.

Locating important information

Depending on the user's workflow, you may need to look up a symbol or a string literal coming from a log, a stack trace, an external tool. IDA provides three locator actions for that, all under the DSC Index context menu, all working across the entire cache without loading anything first:

  • Symbol — substring search across the cache's local symbol table and per-image export tables.

  • String — substring search across the cache's bytes, scoped to images or full files.

  • Region by address — given an address, highlight the matching region in the index.

The two substring locators work the same way: choosing the action first opens a query prompt (with a history of past queries and a collapsible Options group); confirming opens the DSC symbols / DSC strings results panel. The panel keeps the same query field and Options, so further searches can be refined in place — press Enter or click the Search button. The two prompts and panels look near-identical; the DSC strings prompt and panel below illustrate both. See each action page for the result tree's specifics.

DSC strings query prompt
The query prompt, with the Options group expanded
DSC strings panel
The DSC strings panel after a search

Configuration

cfg/dscu.cfg holds the shared-cache options. Each is documented in the file itself, at more length than here; this is what there is to look for.

Option
What it does

SEGNAME_FORMAT

How an image's segments are named. Default "%b:%S.%s", e.g. libsystem_c:__TEXT.__text.

FUNC_FOLDER_FORMAT

How the Functions list folder of an image is named. Default "%M", one folder per path component; empty means no folders.

DSCU_MIN_UNKNOWN_REGION_SIZE

Smallest unclassified region worth tracking. Default 0x1000: a cache has thousands of tiny uncovered gaps between sections, and tracking them all is not useful.

DSCU_LOAD_ZEROSIZED_SEGS

Load segments whose load command claims a size of zero, by loading the mapping that contains them. Default YES.

Both format options are built from the same pieces — %m the install path, %M the same as a path, %b its last component without the extension, %c the subcache file, %S and %s the Mach-O segment and section. A piece that does not exist takes its neighbouring punctuation with it. cfg/kcu.cfg spells the two the same way for the kernelcache.

DSCU_USE_SEG_PREFIXES is retired: SEGNAME_FORMAT says that and more. A config that still sets it keeps composing the names it composed before.

Some behaviour is driven by environment variables rather than the config file, for scripted and batch loads: IDA_DYLD_CACHE_IMAGE, IDA_DYLD_CACHE_DEPTH and IDA_DYLD_CACHE_ALL choose what is loaded, and IDA_DYLD_SHARED_CACHE_SLIDE gives the cache's slide. They are listed under environment variables.

Scripting (dscu_svc_t API)

The entire DSC workflow is driven by a public C++ service, dscu_svc_t, exposed through the <dscu.h> SDK header and the ida_dscu Python module. The same calls the UI makes are available to scripts and plugins: enumerate regions, query symbols, find strings, load images.

A worked IDAPython example shipping with IDA — dscu_query.py (under python/examples/disassembler/) — touches the main highlights: cache layout, image lookup, dependency walk, symbol search, string search.

Extracting an image as a standalone Mach-O

A dylib inside the cache is not a file you can hand to another tool: the builder splits its segments across the container, merges everybody's __LINKEDIT into one blob, and shares a single string pool between all of them. IDA ships an IDAPython example that reassembles one — see Extracting cache images, which covers shared caches and kernelcaches alike.

See also

Last updated

Was this helpful?