A crash report is two address spaces glued together. Frames in LabSession belong to my Mach-O and symbolicate from my dSYM. Frames in UIKitCore belong to the dyld shared cache and will never appear in nm LabSession. Mixing those up is how people “extract the cache” when they only needed atos and Xcode’s system symbols. This lab is a self-signed simulator build. cryptid=1 stops the lab. I do not dump, decrypt, or ship a dyld shared cache.

Mach-O load commands
Figure 1. LC_UUID is the key that matches the dSYM to the crashing image. cryptid=1 stops the lab.

Same binary gate as always

 1$ file LabSession
 2LabSession: Mach-O 64-bit executable arm64
 3
 4$ otool -l LabSession | egrep 'cmd LC_UUID|uuid |cryptid|LC_CODE|segname __TEXT'
 5      cmd LC_SEGMENT_64
 6  segname __TEXT
 7      cmd LC_ENCRYPTION_INFO_64
 8  cryptid 0
 9      cmd LC_UUID
10         uuid A1B2C3D4-E5F6-7890-ABCD-EF1234567890
11      cmd LC_CODE_SIGNATURE

cryptid 0. If it is 1, I stop; FairPlay-encrypted __TEXT is not a crash I can symbolicate from this file.

1$ dwarfdump -u LabSession
2UUID: A1B2C3D4-E5F6-7890-ABCD-EF1234567890 (arm64) LabSession
3
4$ dwarfdump -u LabSession.app.dSYM
5UUID: A1B2C3D4-E5F6-7890-ABCD-EF1234567890 (arm64) LabSession

The UUID in LC_UUID, the UUID dwarfdump -u prints on the binary, and the UUID on the dSYM must match. A rebuilt binary with a new UUID will not symbolicate an old .crash even if the source line numbers look close.

1$ class-dump LabSession | sed -n '/LabSession/,+14p'
2@interface LabSession : NSObject
3- (void)startWithToken:(id)token;
4- (void)insecureCopy:(id)token;     ; lab crash
5- (void)tapCrash:(id)sender;        ; UIButton action
6@end

Why UIKit symbols are not in the app

1$ nm LabSession | egrep 'UIApplication|UIControl|objc_msgSend|insecureCopy'
20000000100001e10 t -[LabSession insecureCopy:]
30000000100001f80 t -[LabSession tapCrash:]
4                 U _objc_msgSend
5$ nm LabSession | grep UIKit
6$ otool -L LabSession | grep -i uikit
7    /System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0)

U _objc_msgSend is an undefined symbol. UIKit.framework is a load command, not a copy of UIKit’s __TEXT inside my file. On iOS / the simulator, UIKit’s pages come from the dyld shared cache (one giant mapped image, many frameworks). That is why a crash report shows:

12  UIKitCore  0x000000018b12c4a8 -[UIApplication sendAction:to:from:forEvent:] + 96

and atos -o LabSession on that address is meaningless. The address is not in my Mach-O.

I do not extract the device DSC with third-party dumpers to resolve that frame. Xcode already has DeviceSupport / iOS SDK symbols. symbolicatecrash uses those. Pulling a cache off a phone to “have UIKit locally” is out of scope here and is how people wander into decryption / DRM tooling I will not document.

Crash report excerpt (paths redacted)

Lab: simulator, Apple Silicon, I tapped the UIButton that calls tapCrash:insecureCopy: with a 36-character stand-in token.

 1Incident Identifier: [REDACTED]
 2Hardware Model:      Mac[REDACTED]
 3Process:             LabSession [412]
 4Path:                /Users/[REDACTED]/Library/Developer/CoreSimulator/Devices/[REDACTED]/data/Containers/Bundle/Application/[REDACTED]/LabSession.app/LabSession
 5Identifier:          com.lab.session
 6Version:             1.0 (1)
 7Code Type:           ARM-64 (Native)
 8Parent Process:      launchd_sim [REDACTED]
 9Date/Time:           2024-08-19 15:02:11.180 +0800
10OS Version:          iPhoneSimulator 17.x [REDACTED]
11Exception Type:      EXC_BAD_ACCESS (SIGSEGV)
12Exception Subtype:   KERN_PROTECTION_FAILURE at 0x[REDACTED]
13Termination Reason:  SIGNAL 11 Segmentation fault: 11
14
15Thread 0 Crashed:
160  libsystem_platform.dylib  0x00000001890afc2c _platform_memmove + 204
171  LabSession                0x0000000104a81e38 -[LabSession insecureCopy:] + 0x28
182  LabSession                0x0000000104a81f98 -[LabSession tapCrash:] + 0x18
193  UIKitCore                 0x000000018b12c4a8 -[UIApplication sendAction:to:from:forEvent:] + 96
204  UIKitCore                 0x000000018b12c5f0 -[UIControl sendAction:to:forEvent:] + 128
215  UIKitCore                 0x000000018b12c8a4 -[UIControl _sendActionsForEvents:withEvent:] + 352
22
23Binary Images:
240x104a80000 - 0x104a87fff LabSession arm64  <a1b2c3d4e5f67890abcdef1234567890> /Users/[REDACTED]/Library/Developer/CoreSimulator/Devices/[REDACTED]/data/Containers/Bundle/Application/[REDACTED]/LabSession.app/LabSession
250x180000000 - 0x18fffffff dyld shared cache arm64  <[REDACTED]>

Frame 1–2 are mine. Frames 3–5 are the shared cache. Binary Images gives me the load address 0x104a80000 and the UUID a1b2c3d4… that must match dwarfdump -u.

atos on the lab frames

File-unslid __TEXT vmaddr is 0x100000000 (otool -l). Load address in the report is 0x104a80000. Slide = 0x4a80000.

1$ xcrun atos -o LabSession.app.dSYM/Contents/Resources/DWARF/LabSession \
2      -arch arm64 -l 0x104a80000 \
3      0x104a81e38 0x104a81f98
4-[LabSession insecureCopy:] (in LabSession) (LabSession.m:41)
5-[LabSession tapCrash:] (in LabSession) (LabSession.m:49)

Same addresses through lldb after I reproduce under the debugger (no report needed):

1(lldb) image list LabSession
2[  0] A1B2C3D4-E5F6-7890-ABCD-EF1234567890 0x0000000104a80000 LabSession
3(lldb) image lookup -v -a 0x104a81e38
4      Address: LabSession[0x0000000100001e38] (LabSession.__TEXT.__text + 0x238)
5      Summary: LabSession`-[LabSession insecureCopy:] + 40 at LabSession.m:41
6(lldb) # UIKit address from the report — not in this image:
7(lldb) image lookup -a 0x18b12c4a8
8      Address: UIKitCore[0x000000018b12c4a8]
9      Summary: UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 96

image lookup on the UIKit address works in a live simulator process because dyld already mapped the cache. It does not work against the on-disk LabSession file. That is the whole lesson.

For a .crash file I do not have a live process for:

1$ export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
2$ xcrun symbolicatecrash LabSession.crash > LabSession.crash.symbolicated

symbolicatecrash matches UUID → dSYM (Spotlight / dwarfdump) for my frames, and UUID → Xcode iOS SDK symbols for UIKit. I do not pass a hand-extracted DSC.

ARM64 at the crashing IMP

 1; otool -tV LabSession   file VA, slide 0
 2; -[LabSession insecureCopy:]
 30000000100001e10  pacibsp
 40000000100001e14  stp    x29, x30, [sp, #-0x30]!
 50000000100001e18  mov    x29, sp
 60000000100001e1c  stp    x20, x19, [sp, #0x10]
 70000000100001e20  sub    sp, sp, #0x10         ; char buf[16]
 80000000100001e24  mov    x19, x2               ; NSString * token
 90000000100001e28  mov    x0, x19
100000000100001e2c  bl     0x1000024a0           ; -[NSString UTF8String]
110000000100001e30  add    x8, x29, #0x18        ; &buf
120000000100001e34  mov    x1, x0
130000000100001e38  bl     0x1000024c4           ; _memcpy   ← crash pc + 0x28 from start

Report said insecureCopy: + 0x28. 0x100001e10 + 0x28 = 0x100001e38, which is the bl memcpy. That is the line atos named LabSession.m:41.

AArch64 frame
Figure 2. Frame at insecureCopy: 16-byte local under memcpy. lr saved at [fp+8]; UIKit is the caller, not in this image.
objc_msgSend dispatch
Figure 3. tapCrash: is an ObjC action. UIKit sendAction: is in the shared cache; the IMP it lands on is in LabSession.

Sanitized reproduction

I paste a 36-character stand-in lab_ + 'A'*32. I log length, not the token.

1(lldb) breakpoint set -n '-[LabSession insecureCopy:]'
2(lldb) po [$x2 length]
336
4(lldb) memory read -c 8 $x2
5; skip — NSString object header is not the bytes. UTF8:
6(lldb) p (char *)[(NSString *)$x2 UTF8String]
7(char *) $1 = 0x0000000283bb4a00 "lab_AAAA"...
8(lldb) # remaining 28 bytes not copied into the note

ASAN rebuild of the same file:

1==412==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x...
2WRITE of size 37 at ... thread T0
3    #0 memcpy
4    #1 -[LabSession insecureCopy:] LabSession.m:41
5    #2 -[LabSession tapCrash:] LabSession.m:49
6    #3 -[UIApplication sendAction:to:from:forEvent:]
7Shadow bytes around the buggy address:
8  00 00 00 00[f1]f1 f1 f1 00 00[f3]f3

Source of the lab bug:

1- (void)insecureCopy:(NSString *)token {
2    char buf[16];
3    const char *u = token.UTF8String;
4    memcpy(buf, u, strlen(u) + 1);   // no bound
5    _scratch = buf[0];
6}

Repro is: 36-byte UTF-8, 16-byte buffer, memcpy size 37, pc insecureCopy:+0x28, UUID-matched atos line LabSession.m:41, UIKit frames left to symbolicatecrash. Not a DSC dump, not FairPlay, not a jailbreak.

Closing

dwarfdump -uLC_UUID ↔ crash Binary Images is the join key. atos -l <load address> resolves my frames from my dSYM. UIKit lives in the dyld shared cache; nm LabSession will never grow those symbols. Xcode’s symbolicatecrash is the supported path for system frames. Extracting the cache off a device is not.

Commands appendix

1otool -l LabSession | egrep 'cryptid|LC_UUID|uuid '
2dwarfdump -u LabSession
3dwarfdump -u LabSession.app.dSYM
4nm LabSession | egrep 'insecureCopy|UIApplication|objc_msgSend'
5class-dump LabSession
6xcrun atos -o LabSession.app.dSYM/Contents/Resources/DWARF/LabSession -arch arm64 -l 0x104a80000 0x104a81e38
7xcrun symbolicatecrash LabSession.crash
8xcrun lldb ./LabSession