Skip to content

Improve compatibility with iOS / macOS 27 shared caches#8260

Open
bdash wants to merge 4 commits into
devfrom
test_dsc_27
Open

Improve compatibility with iOS / macOS 27 shared caches#8260
bdash wants to merge 4 commits into
devfrom
test_dsc_27

Conversation

@bdash

@bdash bdash commented Jun 11, 2026

Copy link
Copy Markdown
Contributor
  • objc_msgSend$stub functions no longer appear in the __objc_stubs section of their dylib. Instead they're coalesced across multiple dylibs and appear in a stub island region of the shared cache. This means that AnalyzeStubFunction can no longer determine the type of stub it is processing purely based on the containing section name. It now considers the target of the call to determine the type of the stub.
  • objc_msgSend and friends now have definitions in multiple dylibs throughout the shared cache (/usr/lib/objc/libobjcMsgSendN.dylib). This means that loading the target of objc_msgSend calls within objc_msgSend$stub functions is not sufficient to make selector definitions visible to analysis. Instead, we explicitly load /usr/lib/libobjc.A.dylib whenever we process a stub function that references libobjcMsgSendN.dylib.
  • Add support for method type strings that are pointers relative to selector base address.
  • Introduce a new activity to rename objc_msgSend$stub stub functions. They no longer have symbol information since they live in stub island regions outside of images. This also helps with regular stripped Mach-O binaries.

bdash added 3 commits June 11, 2026 14:55
These shared caches contain symbols pointing into address ranges that
are no longer mapped, such as `objc_msgSend$stub` functions that are now
merged into stub island regions.
…shared caches

* `objc_msgSend$stub` functions no longer appear in the `__objc_stubs`
  section of their dylib. Instead they're coalesced across multiple
  dylibs and appear in a stub island region of the shared cache. This
  means that `AnalyzeStubFunction` can no longer determine the type of
  stub it is processing purely based on the containing section name. It
  now considers the target of the call to determine the type of the
  stub.

* `objc_msgSend` and friends now have definitions in multiple dylibs
  throughout the shared cache (`/usr/lib/objc/libobjcMsgSendN.dylib`).
  This means that loading the target of `objc_msgSend` calls within
  `objc_msgSend$stub` functions is not sufficient to make selector
  definitions visible to analysis. Instead, we explicitly load
  `/usr/lib/libobjc.A.dylib` whenever we process a stub function that
  references `libobjcMsgSendN.dylib`.
…e to selector base address

These show up in iOS 27 shared caches.
@bdash bdash requested a review from emesare June 11, 2026 22:22
This helps for stripped binaries, and in cases such as the macOS 27
shared cache where the symbols are no longer accruate for stub functions
since they are coalesced into stub island regions outside of any dylib.
return Ok(());
}

let func = ac.function();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to update AnalysisContext::function to be an Option<Ref<Function>>, since it can be executed in the context of the binary view for a module level workflow.

This isn't an issue with your PR I just noticed it when reviewing.

let Some(insn) = block.iter().last() else {
return Ok(());
};
let LowLevelILInstructionKind::TailCallSsa(call_op) = insn.kind() else {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this also suppose to capture jumps as well? https://github.com/Vector35/binaryninja-api/pull/8261/changes#diff-153013bd9df2d3608be397874180b01d8e12bf3415cfe2cf26f001ab287d4120R47 I see this and it seems like it should? Not sure.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code you link to deals with cross-image stub functions into an image that is not yet loaded. In that case the call will often be represented as a jump. This case is a little different.

objc_msgSend$stub functions always end with a call to objc_msgSend. AnalyzeStubFunction in SharedCacheWorkflow.cpp automatically loads libobjc (and/or the new /usr/lib/objc/libobjcMsgSendN.dylib) when it processes one of these stub functions, so any jump instruction that may have existed will have been converted to a tailcall.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants