Detecting the iOS device hardware architecture (32-bit/64-bit)

Posted: October 9th, 2013 | Author: | Filed under: iOS, Programming | Tags: , , , , , | 2 Comments »

In a previous post I explained how to detect if an app runs in a 32-bit or 64-bit iOS Simulator. It was not explaining how to detect if an iOS app runs on a 32-bit or 64-bit iOS device. This post aims at giving a generic method that can detect all cases:

  • 32-bit application running in a 32-bit iOS Simulator
  • 32-bit application running in a 64-bit iOS Simulator
  • 64-bit application running in a 64-bit iOS Simulator
  • 32-bit application running in a 32-bit iOS device
  • 32-bit application running in a 64-bit iOS device
  • 64-bit application running in a 64-bit iOS device

Below is the method is64bitHardware. It returns YES if the hardware is a 64-bit hardware and works on a real iOS device and in an iOS Simulator.
When running in an iOS Simulator, it uses the function is64bitSimulator() from my previous post http://blog.timac.org/?p=886.
When running on a real iOS device, it asks the kernel for the host info.


#include <mach/mach.h>

+ (BOOL) is64bitHardware
{
#if __LP64__
	// The app has been compiled for 64-bit intel and runs as 64-bit intel
	return YES;
#endif
	
	// Use some static variables to avoid performing the tasks several times.
	static BOOL sHardwareChecked = NO;
	static BOOL sIs64bitHardware = NO;
	
	if(!sHardwareChecked)
	{
		sHardwareChecked = YES;
	
#if TARGET_IPHONE_SIMULATOR
		// The app was compiled as 32-bit for the iOS Simulator.
		// We check if the Simulator is a 32-bit or 64-bit simulator using the function is64bitSimulator()
		// See http://blog.timac.org/?p=886
		sIs64bitHardware = is64bitSimulator();
#else
		// The app runs on a real iOS device: ask the kernel for the host info.
		struct host_basic_info host_basic_info;
		unsigned int count;
		kern_return_t returnValue = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)(&host_basic_info), &count);
		if(returnValue != KERN_SUCCESS)
		{
			sIs64bitHardware = NO;
		}
		
		sIs64bitHardware = (host_basic_info.cpu_type == CPU_TYPE_ARM64);

#endif // TARGET_IPHONE_SIMULATOR
	}

	return sIs64bitHardware;
}



Detecting if an app runs in a 32-bit or 64-bit iOS Simulator

Posted: October 1st, 2013 | Author: | Filed under: iOS, Programming | Tags: , , , , | No Comments »

With Xcode 5, it is now possible to compile an application for armv7 and/or arm64.
You can compile an application as 32-bit and/or as 64-bit and you can run this application in a 32-bit or 64-bit iOS Simulator:

In fact there are 3 different cases:

  • 32-bit application running in a 32-bit iOS Simulator
  • 32-bit application running in a 64-bit iOS Simulator
  • 64-bit application running in a 64-bit iOS Simulator

It is possible to distinguish these 3 different cases but this is not as easy as I would expected.

The case of a 64-bit application running in a 64-bit iOS Simulator is simple to solve by just using the define __LP64__: A 64-bit application can only run in a 64-bit iOS Simulator.

Distinguishing the 2 other cases is more difficult and a runtime check is needed. Something to note is that the ‘iOS Simulator’ process is running as 64-bit even in the case of a 32-bit iOS Simulator.

I noticed however that a process called ‘SimulatorBridge’ is used:

  • When launching an 64-bit iOS Simulator, the process ‘SimulatorBridge’ runs as 64-bit
  • When launching an 32-bit iOS Simulator, the process ‘SimulatorBridge’ runs as 32-bit

Running a 32-bit iOS Simulator

Below is a function to know if the iOS Simulator is a 32-bit Simulator or a 64-bit Simulator. The function “is64bitSimulator()” returns true if the iOS Simulator is a 64-bit iOS Simulator:

#include <sys/sysctl.h>

#if TARGET_IPHONE_SIMULATOR

bool is64bitSimulator()
{
	bool is64bitSimulator = false;
	
	/* Setting up the mib (Management Information Base) which is an array of integers where each
	 * integer specifies how the data will be gathered.  Here we are setting the MIB
	 * block to lookup the information on all the BSD processes on the system.  Also note that
	 * every regular application has a recognized BSD process accociated with it.  We pass
	 * CTL_KERN, KERN_PROC, KERN_PROC_ALL to sysctl as the MIB to get back a BSD structure with
	 * all BSD process information for all processes in it (including BSD process names)
	 */
	int mib[6] = {0,0,0,0,0,0};
	mib[0] = CTL_KERN;
    mib[1] = KERN_PROC;
    mib[2] = KERN_PROC_ALL;
	
	long numberOfRunningProcesses = 0;
	struct kinfo_proc* BSDProcessInformationStructure = NULL;
	size_t sizeOfBufferRequired = 0;
	
	/* Here we have a loop set up where we keep calling sysctl until we finally get an unrecoverable error
	 * (and we return) or we finally get a succesful result.  Note with how dynamic the process list can
	 * be you can expect to have a failure here and there since the process list can change between
	 * getting the size of buffer required and the actually filling that buffer.
	 */
	BOOL successfullyGotProcessInformation = NO;
	int error = 0;
	
	while (successfullyGotProcessInformation == NO)
	{
		/* Now that we have the MIB for looking up process information we will pass it to sysctl to get the 
		 * information we want on BSD processes.  However, before we do this we must know the size of the buffer to 
		 * allocate to accomidate the return value.  We can get the size of the data to allocate also using the 
		 * sysctl command.  In this case we call sysctl with the proper arguments but specify no return buffer 
		 * specified (null buffer).  This is a special case which causes sysctl to return the size of buffer required.
		 *
		 * First Argument: The MIB which is really just an array of integers.  Each integer is a constant
		 *     representing what information to gather from the system.  Check out the man page to know what
		 *     constants sysctl will work with.  Here of course we pass our MIB block which was passed to us.
		 * Second Argument: The number of constants in the MIB (array of integers).  In this case there are three.
		 * Third Argument: The output buffer where the return value from sysctl will be stored.  In this case
		 *     we don't want anything return yet since we don't yet know the size of buffer needed.  Thus we will
		 *     pass null for the buffer to begin with.
		 * Forth Argument: The size of the output buffer required.  Since the buffer itself is null we can just
		 *     get the buffer size needed back from this call.
		 * Fifth Argument: The new value we want the system data to have.  Here we don't want to set any system
		 *     information we only want to gather it.  Thus, we pass null as the buffer so sysctl knows that 
		 *     we have no desire to set the value.
		 * Sixth Argument: The length of the buffer containing new information (argument five).  In this case
		 *     argument five was null since we didn't want to set the system value.  Thus, the size of the buffer
		 *     is zero or NULL.
		 * Return Value: a return value indicating success or failure.  Actually, sysctl will either return
		 *     zero on no error and -1 on error.  The errno UNIX variable will be set on error.
		 */ 
		error = sysctl(mib, 3, NULL, &sizeOfBufferRequired, NULL, 0);
		if (error) 
			return NULL;
		
		/* Now we successful obtained the size of the buffer required for the sysctl call.  This is stored in the 
		 * SizeOfBufferRequired variable.  We will malloc a buffer of that size to hold the sysctl result.
		 */
		BSDProcessInformationStructure = (struct kinfo_proc*) malloc(sizeOfBufferRequired);
		if (BSDProcessInformationStructure == NULL)
			return NULL;
		
		/* Now we have the buffer of the correct size to hold the result we can now call sysctl
		 * and get the process information.  
		 *
		 * First Argument: The MIB for gathering information on running BSD processes.  The MIB is really 
		 *     just an array of integers.  Each integer is a constant representing what information to 
		 *     gather from the system.  Check out the man page to know what constants sysctl will work with.  
		 * Second Argument: The number of constants in the MIB (array of integers).  In this case there are three.
		 * Third Argument: The output buffer where the return value from sysctl will be stored.  This is the buffer
		 *     which we allocated specifically for this purpose.  
		 * Forth Argument: The size of the output buffer (argument three).  In this case its the size of the 
		 *     buffer we already allocated.  
		 * Fifth Argument: The buffer containing the value to set the system value to.  In this case we don't
		 *     want to set any system information we only want to gather it.  Thus, we pass null as the buffer
		 *     so sysctl knows that we have no desire to set the value.
		 * Sixth Argument: The length of the buffer containing new information (argument five).  In this case
		 *     argument five was null since we didn't want to set the system value.  Thus, the size of the buffer
		 *     is zero or NULL.
		 * Return Value: a return value indicating success or failure.  Actually, sysctl will either return 
		 *     zero on no error and -1 on error.  The errno UNIX variable will be set on error.
		 */
		error = sysctl(mib, 3, BSDProcessInformationStructure, &sizeOfBufferRequired, NULL, 0);
		if (error == 0)
        {
			//Here we successfully got the process information.  Thus set the variable to end this sysctl calling loop
            successfullyGotProcessInformation = YES;
        }
        else 
        {
			/* failed getting process information we will try again next time around the loop.  Note this is caused
			 * by the fact the process list changed between getting the size of the buffer and actually filling
			 * the buffer (something which will happen from time to time since the process list is dynamic).
			 * Anyways, the attempted sysctl call failed.  We will now begin again by freeing up the allocated 
			 * buffer and starting again at the beginning of the loop.
			 */
            free(BSDProcessInformationStructure); 
        }
	} //end while loop
	
	
	/* Now that we have the BSD structure describing the running processes we will parse it for the desired
     * process name.  First we will the number of running processes.  We can determine
     * the number of processes running because there is a kinfo_proc structure for each process.
     */
	numberOfRunningProcesses = sizeOfBufferRequired / sizeof(struct kinfo_proc);
	for (int i = 0; i < numberOfRunningProcesses; i++)
    {
		//Getting name of process we are examining
        const char *name = BSDProcessInformationStructure[i].kp_proc.p_comm;
		
		if(strcmp(name, "SimulatorBridge") == 0)
		{
			int p_flag = BSDProcessInformationStructure[i].kp_proc.p_flag;
			is64bitSimulator = (p_flag & P_LP64) == P_LP64;
			break;
		}
    }
	
	free(BSDProcessInformationStructure);
	return is64bitSimulator;
}

#endif // TARGET_IPHONE_SIMULATOR


Determining the running kernel mode on 10.6

Posted: March 6th, 2010 | Author: | Filed under: macOS, Programming | Tags: , , , , , , , | 1 Comment »

It might be useful in some cases to know if the MacOS kernel is running in the 32-bit or 64-bit (K64) mode. This is useful for example if you write an application like ‘System Profiler’ that displays the details of the currently running system:

'System Software Overview' in the 'System Profiler' app

Obviously this post only applies to intel machines running on Snow Leopard (see my previous post ‘Intel 64-bit summary’). To simplify the code, I assume that you compiled your application with the 10.6 SDK.

In the IOKit framework on 10.6 the function OSKextGetRunningKernelArchitecture is defined as followed in the OSKext.h header:


/*!
 * @function OSKextGetRunningKernelArchitecture
 * @abstract Returns the architecture of the running kernel.
 *
 * @result
 * The architecture of the running kernel.
 * The caller does not own the returned pointer and should not free it.
 *
 * @discussion
 * This function consults the kernel task and returns a pointer to the
 * NXArchInfo struct representing its architecture.
 * The running kernel's architecture does not necessarily match that
 * of user space tasks (for example, a 32-bit user space task could be
 * running with a 64-bit kernel, or vice-versa).
 */
CF_EXPORT const NXArchInfo *
OSKextGetRunningKernelArchitecture(void)
                AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER;

This function is the one I use to determine the current running kernel mode:

#import <Foundation/Foundation.h>
#import <mach-o/arch.h>

// OSKextGetRunningKernelArchitecture is not available in
// the 10.6 SDK headers, but exported since 10.6.
extern const NXArchInfo *OSKextGetRunningKernelArchitecture(void);

static BOOL Is64BitKernel()
{
	BOOL isK64 = NO;
	
	const NXArchInfo *archInfo = OSKextGetRunningKernelArchitecture();
	if (archInfo != NULL)
	{
		isK64 = ((archInfo->cputype & CPU_ARCH_ABI64) != 0);
	}
	
    return isK64;
}

int main (int argc, const char * argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	
	fprintf(stderr, "Is 64-bit kernel: %d\n", Is64BitKernel());
   
    [pool drain];
    return 0;
}

And don’t forget to link against the IOKit framework.

Also note that it is quite easy to make the previous code compiling with the 10.5 (or earlier) SDK by dynamically getting the address of the OSKextGetRunningKernelArchitecture function and checking if the address is not NULL.


‘Open in 32-bit mode’ – ‘Open using Rosetta’

Posted: January 30th, 2010 | Author: | Filed under: macOS, Programming | Tags: , , , , , , , , | No Comments »

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. 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.

'Open in 32-bit mode' checkbox in the Finder

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):

The same application but different checkbox selected in the Finder

Here is what you will see if you open now the file ‘~/Library/Preferences/com.apple.LaunchServices.plist’ with Property List Editor:

The content of the file com.apple.LaunchServices.plist

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;
}