Nat! bio photo

Nat!

Senior Mull.

Twitter RSS

Github

Betcha can't debug that #2. Solution Part 1

If you look at the project, there seems to be offhand nothing problematic to it.

otool -L says (as expected):

FrameworkRosettaFuckup/build/Debug Native/FrameworkRosettaFuckup.app/Contents/MacOS/FrameworkRosettaFuckup:

/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 751.29.0)
@executable_path/../Frameworks/main.framework/Versions/A/main (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 550.29.0)

The code contained in main.framework, again, is just this:

@implementation NSString( URLFix)

- (NSString *) standardizedURLPath
{
   printf( "%s\n", __PRETTY_FUNCTION__);
   return( nil);
}

@end

Checking the Xcode documentation reveals nothing for any standardizedURLPath method.

But a class-dump of Foundation shows, that this is a method defined on NSString! As class-dump /System/Library/Frameworks/Foundation.framework/Foundation | egrep -B 1 -A5 standardizedURLPath gives for the 10.6.4 Foundation Framework/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 751.29.0):

@interface NSString (NSURLPathUtilities)
- (id)standardizedURLPath;
- (id)stringByRemovingPercentEscapes;
- (id)stringByAddingPercentEscapes;
- (id)urlPathRelativeToPath:(id)arg1;
@end

And it has been defined on category, something which will be of great concern later on,

The way to debug this is using a combination of dyld and Objective-C runtime environment flags (see dyld(1) or export OBJC_HELP=YES for details). Technically OBJC_PRINT_REPLACED_METHODS suffices, but then there is less to talk about.

export DYLD_PRINT_LIBRARIES=YES
export OBJC_PRINT_IMAGES=YES
export OBJC_PRINT_CLASS_SETUP=YES
export OBJC_PRINT_REPLACED_METHODS=YES

Here comes the output, thoughtfully filtered for easier reading with egrep 'NSString|standardizedURLPath|dyld:|IMAGES'. It's actually better to start the executable in the shell, because the debugger will start bash and arch beforehand, cluttering the output.

//
// dyld loads our stuff
//

dyld: loaded: /Volumes/Users/nat/Downloads/FrameworkRosettaFuckup/build/Debug Native/FrameworkRosettaFuckup.app/Contents/MacOS/FrameworkRosettaFuckup
dyld: loaded: /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
dyld: loaded: /Volumes/Users/nat/Downloads/FrameworkRosettaFuckup/build/Debug Native/main.framework/Versions/A/main
dyld: loaded: /usr/lib/libSystem.B.dylib
dyld: loaded: /usr/lib/libobjc.A.dylib

//
// up to this point dyld just loaded what it was told to by the linker, now it needs to load dependent
// frameworks and library to get things going
//
dyld: loaded: /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
dyld: loaded: /usr/lib/libauto.dylib
dyld: loaded: /usr/lib/libicucore.A.dylib
dyld: loaded: /usr/lib/libxml2.2.dylib
// hmm couldn't that be loaded lazily ? I don't do xml
dyld: loaded: /usr/lib/libz.1.dylib
// i don't do compression either
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CFNetwork.framework/Versions/A/CFNetwork
// networking, not really
dyld: loaded: /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration
dyld: loaded: /System/Library/Frameworks/Security.framework/Versions/A/Security
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices
dyld: loaded: /usr/lib/libstdc++.6.dylib
dyld: loaded: /usr/lib/system/libmathCommon.A.dylib
dyld: loaded: /usr/lib/libsqlite3.dylib
// huh ???
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices
dyld: loaded: /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
dyld: loaded: /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration
// O RLY ?
dyld: loaded: /usr/lib/libbsm.0.dylib
dyld: loaded: /System/Library/Frameworks/NetFS.framework/Versions/A/NetFS
dyld: loaded: /usr/lib/system/libkxld.dylib
dyld: loaded: /usr/lib/libxslt.1.dylib
// moar XML ...

//
// dyld loaded a whole lot of superfluous crap and some stuff I actually care about
// dyld hands things over to the objective-c runtime, (how it does this, we will see later)
//
objc[364]: IMAGES: processing 30 newly-mapped images...
objc[364]: IMAGES: loading image for /usr/lib/libobjc.A.dylib (preoptimized) (supports GC)
objc[364]: IMAGES: loading image for /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (preoptimized) (supports GC)
objc[364]: IMAGES: loading image for /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata (preoptimized) (supports GC)
objc[364]: IMAGES: loading image for /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (preoptimized) (supports GC)
objc[364]: IMAGES: loading image for /Volumes/Users/nat/Downloads/FrameworkRosettaFuckup/build/Debug Native/main.framework/Versions/A/main
objc[364]: IMAGES: loading image for /Volumes/Users/nat/Downloads/FrameworkRosettaFuckup/build/Debug Native/FrameworkRosettaFuckup.app/Contents/MacOS/FrameworkRosettaFuckup

//
// it is interesting to note, that except for libobjc.A.dylib,
// the frameworks containing ObjC code are "loaded"
// in reverse order
// of dependency. More on this later
//
objc[364]: CONNECT: attaching category 'NSString (NSUserDefaults_NSURLExtras)'
objc[364]: CONNECT: attaching category 'NSString (NSUnpublishedEOF)'
objc[364]: CONNECT: attaching category 'NSString (NSURLPathUtilities)'
objc[364]: CONNECT: attaching category 'NSString (NSURLUtilities)'
objc[364]: CONNECT: attaching category 'NSString (NSScriptingComparisonMethods)'
objc[364]: CONNECT: attaching category 'NSString (NSURLExtras)'
objc[364]: CONNECT: attaching category 'NSString (NSURLExtrasInternal)'
objc[364]: CONNECT: attaching category 'NSString (NSStringOtherEncodings)'
objc[364]: CONNECT: attaching category 'NSString (NSExtendedStringPropertyListParsing)'
objc[364]: CONNECT: attaching category 'NSString (NSComparisonMethods)'
objc[364]: CONNECT: attaching category 'NSString (NSScriptSuiteDebugging)'
objc[364]: CONNECT: attaching category 'NSString (NSScriptAppleEventConversion)'
objc[364]: REPLACED: -[NSObject isNSString__] by category NSKindOfAdditions (IMP was 0x90718df0 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation), now 0x94579bee (/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation))
objc[364]: CONNECT: attaching category 'NSString (NSKindOfAdditions)'
objc[364]: CONNECT: attaching category 'NSString (NSStringPortCoding)'
objc[364]: CONNECT: attaching category 'NSString (NSPathUtilities)'
objc[364]: CONNECT: attaching category 'NSString (NSSearchMethods)'
objc[364]: CONNECT: attaching category 'NSString (_NSMetadataQueryExtensions)'
objc[364]: CONNECT: attaching category 'NSString (NSDistantString)'
objc[364]: CONNECT: attaching category 'NSString (NSDecimalExtension)'
objc[364]: CONNECT: attaching category 'NSString (NSCFAdditions)'
objc[364]: CONNECT: attaching category 'NSString (URLFix)'
objc[364]: REPLACED: -[NSString standardizedURLPath] by category URLFix (IMP was 0x946eb3f1 (/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation), now 0x5f18 (/Volumes/Users/nat/Downloads/FrameworkRosettaFuckup/build/Debug Native/main.framework/Versions/A/main))
// and that's what we are looking for.
// -[NSString standardizedURLPath] is overwriting the method defined in Foundation.framework.
// So stuff should work as expected
objc[364]: CONNECT: class 'NSString' now connected
objc[364]: CONNECT: class 'NSStringPredicateOperator' now connected
-[NSString(URLFix) standardizedURLPath]
// and it does, as there is no magic in computing
// just lack of information sometimes.

Lets see how Rosetta does it. otool sees no difference in the frameworks linked:

FrameworkRosettaFuckup/build/Debug PPC/FrameworkRosettaFuckup.app/Contents/MacOS/FrameworkRosettaFuckup:

/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 751.29.0)
@executable_path/../Frameworks/main.framework/Versions/A/main (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 550.29.0)

And a run in the shell with aforementioned environment variables set, gives this output:

dyld: loaded: /Volumes/Users/nat/Downloads/FrameworkRosettaFuckup/build/Debug PPC/FrameworkRosettaFuckup.app/Contents/MacOS/FrameworkRosettaFuckup
dyld: loaded: /usr/libexec/oah/Shims/Interposers.dylib
// this is Rosetta interposing and translating the PPC code for us. Does it trip up something ??

dyld: loaded: /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
dyld: loaded: /Volumes/Users/nat/Downloads/FrameworkRosettaFuckup/build/Debug PPC/FrameworkRosettaFuckup.app/Contents/MacOS/../Frameworks/main.framework/Versions/A/main
dyld: loaded: /usr/lib/libSystem.B.dylib
dyld: loaded: /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
dyld: loaded: /usr/lib/libauto.dylib
dyld: loaded: /usr/lib/libicucore.A.dylib
dyld: loaded: /usr/lib/libobjc.A.dylib
dyld: loaded: /usr/lib/libxml2.2.dylib
dyld: loaded: /usr/lib/libz.1.dylib
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CFNetwork.framework/Versions/A/CFNetwork
dyld: loaded: /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration
dyld: loaded: /System/Library/Frameworks/Security.framework/Versions/A/Security
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices
dyld: loaded: /usr/lib/libstdc++.6.dylib
dyld: loaded: /usr/lib/system/libmathCommon.A.dylib
dyld: loaded: /usr/lib/libsqlite3.dylib
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices
dyld: loaded: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices
dyld: loaded: /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
dyld: loaded: /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration
dyld: loaded: /usr/lib/libbsm.0.dylib
dyld: loaded: /System/Library/Frameworks/NetFS.framework/Versions/A/NetFS
dyld: loaded: /usr/lib/system/libkxld.dylib
dyld: loaded: /usr/lib/libxslt.1.dylib
//
// nothing else different there
//
objc[439]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
objc[439]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods
objc[439]: IMAGES: processing 31 newly-mapped images...
objc[439]: IMAGES: loading image for /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata (supports GC)
objc[439]: IMAGES: loading image for /usr/lib/libobjc.A.dylib (supports GC)
objc[439]: IMAGES: loading image for /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (supports GC)
objc[439]: IMAGES: loading image for /Volumes/Users/nat/Downloads/FrameworkRosettaFuckup/build/Debug PPC/FrameworkRosettaFuckup.app/Contents/MacOS/../Frameworks/main.framework/Versions/A/main
objc[439]: IMAGES: loading image for /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (supports GC)
objc[439]: IMAGES: loading image for /Volumes/Users/nat/Downloads/FrameworkRosettaFuckup/build/Debug PPC/FrameworkRosettaFuckup.app/Contents/MacOS/FrameworkRosettaFuckup

// the order of images though is very much different than it was before
// which is significant

objc[439]: CONNECT: attaching category 'NSString (URLFix)'
// Our category gets loaded
objc[439]: CONNECT: attaching category 'NSString (NSUserDefaults_NSURLExtras)'
objc[439]: CONNECT: attaching category 'NSString (NSUnpublishedEOF)'
objc[439]: CONNECT: attaching category 'NSString (NSURLPathUtilities)'
objc[439]: REPLACED: -[NSString standardizedURLPath] by category NSURLPathUtilities (IMP was 0xb7f44 (/Volumes/Users/nat/Downloads/FrameworkRosettaFuckup/build/Debug PPC/FrameworkRosettaFuckup.app/Contents/MacOS/../Frameworks/main.framework/Versions/A/main), now 0x95582d2c (/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation))
//
// Now Foundation overwrote our implementation !
//
objc[439]: CONNECT: attaching category 'NSString (NSURLUtilities)'
objc[439]: CONNECT: attaching category 'NSString (NSScriptingComparisonMethods)'
objc[439]: CONNECT: attaching category 'NSString (NSURLExtras)'
objc[439]: CONNECT: attaching category 'NSString (NSURLExtrasInternal)'
objc[439]: CONNECT: attaching category 'NSString (NSStringOtherEncodings)'
objc[439]: CONNECT: attaching category 'NSString (NSExtendedStringPropertyListParsing)'
objc[439]: CONNECT: attaching category 'NSString (NSComparisonMethods)'
objc[439]: CONNECT: attaching category 'NSString (NSScriptSuiteDebugging)'
objc[439]: CONNECT: attaching category 'NSString (NSScriptAppleEventConversion)'
objc[439]: REPLACED: -[NSObject isNSString__] by category NSKindOfAdditions (IMP was 0x96cce1f8 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation), now 0x9552801c (/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation))
objc[439]: CONNECT: attaching category 'NSString (NSKindOfAdditions)'
objc[439]: CONNECT: attaching category 'NSString (NSStringPortCoding)'
objc[439]: CONNECT: attaching category 'NSString (NSPathUtilities)'
objc[439]: CONNECT: attaching category 'NSString (NSSearchMethods)'
objc[439]: CONNECT: attaching category 'NSString (_NSMetadataQueryExtensions)'
objc[439]: CONNECT: attaching category 'NSString (NSDistantString)'
objc[439]: CONNECT: attaching category 'NSString (NSDecimalExtension)'
objc[439]: CONNECT: attaching category 'NSString (NSCFAdditions)'
objc[439]: CONNECT: class 'NSString' now connected
objc[439]: CONNECT: class 'NSStringPredicateOperator' now connected

So this was the solution, that could have gotten you that luxury sedan, if you could have debugged that. Next up, lets dig a little deeper into the objc and dyld source.


2 Comments

A photo of 3309

From: 3309

From The Objective-C Programming Language - How you Use Categories:

[…] A framework-defined method you try to override may itself have been implemented in a category, and so which implementation takes precedence is not defined.

But obviously, you wouldn't expect that rosetta enforces this undefined behavior!

Anyway, that was a great post. Looking forward to part 2.

A photo of Nat!

From: Nat!

Thx for the kudos. Part II is in the making, but debugging other peoples code (xnu, csu, objc and dyld) is not that much fun and it takes a while. ;)

Nat!

Post a comment

All comments are held for moderation; basic HTML formatting accepted.

Name:
E-mail: (not published)
Website: