Skip to content

Commit 1f509da

Browse files
committed
[DSC] Handle changes to how objc_msgSend is called in iOS / macOS 27 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`.
1 parent 5bf826b commit 1f509da

1 file changed

Lines changed: 40 additions & 6 deletions

File tree

view/sharedcache/workflow/SharedCacheWorkflow.cpp

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,17 @@ void IdentifyStub(BinaryView& view, const SharedCacheController& controller, uin
9999
// Ref<Type> selectedType = demangledType;
100100
Ref<Type> selectedType = nullptr;
101101
if (const auto image = controller.GetImageContaining(symbolAddr))
102-
if (auto typeLib = TypeLibraryFromName(view, image->name))
102+
{
103+
// The objc_msgSend trampolines live in their own libobjcMsgSend* dylibs which have no type
104+
// library. Look in libobjc.A.dylib instead.
105+
std::string typeLibName = image->name;
106+
if (typeLibName.rfind("/usr/lib/objc/libobjcMsgSend", 0) == 0)
107+
typeLibName = "/usr/lib/libobjc.A.dylib";
108+
109+
if (auto typeLib = TypeLibraryFromName(view, typeLibName))
103110
if (Ref<Type> libraryType = view.ImportTypeLibraryObject(typeLib, {symbol->name}); libraryType)
104111
selectedType = libraryType;
112+
}
105113

106114
if (selectedType != nullptr)
107115
targetFunc->ApplyAutoDiscoveredType(selectedType);
@@ -112,7 +120,18 @@ void IdentifyStub(BinaryView& view, const SharedCacheController& controller, uin
112120
view.DefineAutoSymbol(bnSymbol);
113121
}
114122

115-
void AnalyzeStubFunction(Ref<Function> func, Ref<MediumLevelILFunction> mlil, SharedCacheController& controller, bool loadImage)
123+
// Controls which images AnalyzeStubFunction may auto-load to resolve a stub's jump target.
124+
enum class StubImageLoading
125+
{
126+
// Do not auto-load any target image.
127+
None,
128+
// Only the dedicated objc_msgSend libraries introduced in macOS 27.
129+
ObjCMsgSendOnly,
130+
// Any directly referenced image.
131+
AnyReferenced,
132+
};
133+
134+
void AnalyzeStubFunction(Ref<Function> func, Ref<MediumLevelILFunction> mlil, SharedCacheController& controller, StubImageLoading imageLoading)
116135
{
117136
// 1. Identify the load target and load the region, resolving the load to a const pointer.
118137
// 2. We _should_ have a proper call now to the appropriate external function (external to the current image)
@@ -138,15 +157,28 @@ void AnalyzeStubFunction(Ref<Function> func, Ref<MediumLevelILFunction> mlil, Sh
138157
const auto image = controller.GetImageContaining(imageAddr);
139158
if (!image.has_value() || controller.IsImageLoaded(*image))
140159
return false;
160+
161+
const bool isLibobjcMsgSend = image->name.rfind("/usr/lib/objc/libobjcMsgSend", 0) == 0;
162+
if (imageLoading == StubImageLoading::ObjCMsgSendOnly && !isLibobjcMsgSend)
163+
return false;
164+
165+
if (isLibobjcMsgSend)
166+
{
167+
// The selectors referenced by `objc_msgSend` stubs still live in libobjc.A.dylib.
168+
// Load it too so they are resolved to strings rather than `sel_` symbols.
169+
auto libobjc = controller.GetImageWithName("/usr/lib/libobjc.A.dylib");
170+
if (libobjc && !controller.IsImageLoaded(*libobjc))
171+
controller.ApplyImage(*view, *libobjc);
172+
}
173+
141174
return controller.ApplyImage(*view, *image);
142175
};
143176

144177
auto loadTarget = [&](uint64_t targetAddr) {
145178
// Skip if already loaded.
146179
if (view->IsValidOffset(targetAddr))
147180
return false;
148-
// If the stub function is allowed to load images (for inlining)
149-
if (loadImage && loadTargetImage(targetAddr))
181+
if (imageLoading != StubImageLoading::None && loadTargetImage(targetAddr))
150182
return true;
151183
return loadStubIslandRegion(targetAddr);
152184
};
@@ -358,10 +390,12 @@ void AnalyzeFunction(Ref<AnalysisContext> ctx)
358390
AnalyzeStandardFunction(func, mlilSsa, *controller);
359391
break;
360392
case StubFunction:
361-
AnalyzeStubFunction(func, mlilSsa, *controller, false);
393+
AnalyzeStubFunction(func, mlilSsa, *controller,
394+
workflowState->autoLoadObjCStubRequirements ? StubImageLoading::ObjCMsgSendOnly : StubImageLoading::None);
362395
break;
363396
case ObjCStubFunction:
364-
AnalyzeStubFunction(func, mlilSsa, *controller, workflowState->autoLoadObjCStubRequirements);
397+
AnalyzeStubFunction(func, mlilSsa, *controller,
398+
workflowState->autoLoadObjCStubRequirements ? StubImageLoading::AnyReferenced : StubImageLoading::None);
365399
break;
366400
}
367401
}

0 commit comments

Comments
 (0)