As you know, it is possible to force an Universal Binary (ppc, i386, x86_64) to run in 32-bit mode (i386) even if the machine could run x86_64 binaries.
- Easily preview Mermaid diagrams
- Live update when editing in your preferred editor
- Capture screenshots with customizable margins
- Create PNG from the Terminal
- Free download on the Mac App Store
And you can even force the application to run in Rosetta (ppc). To select in which mode the application should run, simply display the info of the application in the Finder and you should see 2 checkboxes:
- Open in 32-bit mode
- Open using Rosetta
As far as I know, the first checkbox should always be visible on an Intel machine as long as the application binary contains the x86_64 and i386 architecture.
The Rosetta checkbox will only be visible on an Intel machine if the application binary contains the ppc architecture. Note also that the developer can prevent the checkbox to appear in the Finder by setting the key ‘LSRequiresNativeExecution’ to true in the ‘Info.plist’ file.
You may wonder where these flags are stored on the machine. You might have noticed that if you set one flag on an application, trash the application and the preferences, and reinstall it, the flag will still be set. So the flag is definitively not stored in the application package or in the application preferences.
In fact the flags are stored in the file ‘com.apple.LaunchServices.plist’ in the user Preferences folder (~/Library/Preferences/com.apple.LaunchServices.plist). That means that several users on the same machine could have different flags set for the same applications.
Before looking at this file, let’s imagine you have an application called ‘MyApp’ which was compiled for the ppc, i386 and x86_64 architectures. In the Finder you can duplicate this application 2 times. For one instance, check the ‘run in 32-bit mode’, force another instance to run in Rosetta, and leave the original instance unchanged (run as x86_64):
Here is what you will see if you open now the file ‘~/Library/Preferences/com.apple.LaunchServices.plist’ with Property List Editor:
It’s fairly easy to understand how the flags are stored. The file contains a dictionary ‘LSArchitecturesForX86_64’. The keys of this dictionary are the applicationIDs (‘org.timac.MyApp’ for example). For each applicationID, there is an array containing the information for the different instances of the application. The information of each instance is a pair (path of the application, architecture of the application):
- The first item in the array is an alias to the first instance of application.
- The second item is the architecture of the first instance of the application.
- The third item is an alias to the second instance of application.
- The fourth is the architecture of the second instance of the application.
- …
Following is the source code for a small application that will read the content of the file ‘com.apple.LaunchServices.plist’ and will print the paths of each application whose flags (‘Open in 32-bit mode’ or ‘Open using Rosetta’) were changed in the Finder. It will also display the architecture selected in the Finder for each of the application. This application will output something like:
applicationID: org.timac.MyApp
Path: /Applications/MyApp.app
will run as: x86_64
Path: /Applications/MyApp 32-bit.app
will run as: i386
Path: /Applications/MyApp Rosetta.app
will run as: ppc
The source code:
#import <Foundation/Foundation.h>
// This function takes as parameter the data of the aliases
// stored in the com.apple.LaunchServices.plist file.
// It returns the resolved path as string.
static NSString *getResolvedAliasPath(NSData* inData)
{
NSString *outPath = nil;
if(inData != nil)
{
const void *theDataPtr = [inData bytes];
NSUInteger theDataLength = [inData length];
if(theDataPtr != nil && theDataLength > 0)
{
// Create an AliasHandle from the NSData
AliasHandle theAliasHandle;
theAliasHandle = (AliasHandle)NewHandle(theDataLength);
bcopy(theDataPtr, *theAliasHandle, theDataLength);
FSRef theRef;
Boolean wChang;
OSStatus err = noErr;
err = FSResolveAlias(NULL, theAliasHandle, &theRef, &wChang);
if(err == noErr)
{
// The path was resolved.
char path[1024];
err = FSRefMakePath(&theRef, (UInt8*)path, sizeof(path));
if(err == noErr)
outPath = [NSString stringWithUTF8String:path];
}
else
{
// If we can't resolve the alias (file not found),
// we can still return the path.
CFStringRef tmpPath = NULL;
err = FSCopyAliasInfo(theAliasHandle, NULL, NULL,
&tmpPath, NULL, NULL);
if(err == noErr && tmpPath != NULL)
outPath = [(NSString*)tmpPath autorelease];
}
DisposeHandle((Handle)theAliasHandle);
}
}
return outPath;
}
// This function prints the architecture for each application
// in the com.apple.LaunchServices.plist file
static void dumpLSArchitecturesForX86_64()
{
// The path of the com.apple.LaunchServices.plist file.
NSString *prefsPath =
@"~/Library/Preferences/com.apple.LaunchServices.plist";
prefsPath = [prefsPath stringByExpandingTildeInPath];
NSDictionary *mainDict =
[NSDictionary dictionaryWithContentsOfFile:prefsPath];
if(mainDict != nil)
{
// We are only interested by the
// "LSArchitecturesForX86_64" dictionary.
NSDictionary *architectureDict =
[mainDict objectForKey:@"LSArchitecturesForX86_64"];
// Get the list of applications.
// The array is ordered by applicationID.
NSArray *applicationIDArray = [architectureDict allKeys];
if(applicationIDArray != nil)
{
// For each applicationID
NSUInteger i = 0;
for(i = 0 ; i < [applicationIDArray count] ; i++)
{
NSString *applicationID =
[applicationIDArray objectAtIndex:i];
NSArray *appArray =
[architectureDict objectForKey:applicationID];
NSLog(@"-------------------------");
NSLog(@"applicationID: %@", applicationID);
// For each instance of the application,
// there is a pair (Alias, architecture).
// The alias is stored as a NSData
// and the architecture as a NSString.
NSUInteger j = 0;
for(j = 0 ; j < [appArray count] / 2 ; j++)
{
NSLog(@"\n");
// Just for safety
if(j * 2 + 1 < [appArray count])
{
NSData *aliasData =
[appArray objectAtIndex:j * 2];
NSString *theArch =
[appArray objectAtIndex:j * 2 + 1];
if(aliasData != nil && theArch != nil)
{
// Get the path of the application
NSString *resolvedPath =
getResolvedAliasPath(aliasData);
NSLog(@"\t Path: %@", resolvedPath);
NSLog(@"\t will run as: %@", theArch);
}
}
}
}
}
}
}
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
dumpLSArchitecturesForX86_64();
[pool drain];
return 0;
}