How to determine binary image architecture at runtime?
Asked Answered
W

3

6

Crash log contains "Binary Images" section with information about architecture (armv6/armv7) and identifier of all loaded modules. How to determine this information at runtime? (at least, just for application executable)
NSBundle has method executableArchitectures, but how to determine which architecture is running?

Waldrup answered 6/4, 2011 at 13:34 Comment(0)
M
19

Alright time for the long answer. The mach headers of the dyld images in the application contain the information you are looking for. I have added an example that I only tested to work and nothing else so I would not recommend pasting it directly into production code. What it does it get all of the mach headers for all of the currently loaded dyld images and prints an output very similar to the Binary Images section of the crash log. The methods I call are not thread safe. The one thing I am missing is the end address to the binary image because I did not bother looking up how to find that.

Main.m

#import <UIKit/UIKit.h>

#include <string.h>
#import <mach-o/loader.h>
#import <mach-o/dyld.h>
#import <mach-o/arch.h>

void printImage(const struct mach_header *header)
{
    uint8_t *header_ptr = (uint8_t*)header;
    typedef struct load_command load_command;

    const NXArchInfo *info = NXGetArchInfoFromCpuType(header->cputype, header->cpusubtype);

    //Print the architecture ex. armv7
    printf("%s ", info->name);

    header_ptr += sizeof(struct mach_header);
    load_command *command = (load_command*)header_ptr;

    for(int i = 0; i < header->ncmds > 0; ++i)
    {
        if(command->cmd == LC_UUID)
        {
            struct uuid_command ucmd = *(struct uuid_command*)header_ptr;

            CFUUIDRef cuuid = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, *((CFUUIDBytes*)ucmd.uuid));
            CFStringRef suuid = CFUUIDCreateString(kCFAllocatorDefault, cuuid);
            CFStringEncoding encoding = CFStringGetFastestEncoding(suuid);

            //Print UUID
            printf("<%s> ", CFStringGetCStringPtr(suuid, encoding));

            CFRelease(cuuid);
            CFRelease(suuid);

            break;
        }

        header_ptr += command->cmdsize;
        command = (load_command*)header_ptr;
    }
}

void printBinaryImages()
{
    printf("Binary Images:\n");
    //Get count of all currently loaded DYLD
    uint32_t count = _dyld_image_count();

    for(uint32_t i = 0; i < count; i++)
    {
        //Name of image (includes full path)
        const char *dyld = _dyld_get_image_name(i);

        //Get name of file
        int slength = strlen(dyld);

        int j;
        for(j = slength - 1; j>= 0; --j)
            if(dyld[j] == '/') break;

        //strndup only available in iOS 4.3
        char *name = strndup(dyld + ++j, slength - j);
        printf("%s ", name);
        free(name);

        const struct mach_header *header = _dyld_get_image_header(i);
        //print address range
        printf("0x%X - ??? ", (uint32_t)header);

        printImage(header);

        //print file path
        printf("%s\n",  dyld);
    }
    printf("\n");
}

int main(int argc, char *argv[])
{        
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    printBinaryImages();
    [pool release];
    return retVal;
}

Example output:

Binary Images:
TestBed 0x1000 - ??? i386 <E96D079C-E035-389D-AA12-71E968C76BFE> /Users/username/Library/Application Support/iPhone Simulator/4.3/Applications/6F64D9F8-9179-4E21-AE32-4D4604BE77E5/TestBed.app/TestBed
UIKit 0x8000 - ??? i386 <72030911-362F-3E47-BAF3-ACD2CB6F88C0> /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk/System/Library/Frameworks/UIKit.framework/UIKit
Foundation 0x772000 - ??? i386 <EB718CBD-1D57-3D31-898D-7CFA9C172A46> /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk/System/Library/Frameworks/Foundation.framework/Foundation
CoreGraphics 0xA10000 - ??? i386 <D168A716-71F2-337A-AE0B-9DCF51AE9181> /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics
libSystem.dylib 0xCAA000 - ??? i386 <8DF0AFCD-FFA5-3049-88E2-7410F8398749> /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk/usr/lib/libSystem.dylib
...
Mushy answered 8/4, 2011 at 13:2 Comment(5)
Thank you for the answer! Since I needed CPU architecture just for application executable, I used dladdr(this_function) and then Dl_info.dli_fbase as a pointer to struct mach_header.Waldrup
@Joe, do you happen to know whether obtaining bundle uuid from _mh_execute_header (almost the same way) would work in Appstore binary? It works both in developer and ad-hoc builds, but I'd like to be sure.Skyward
@Skyward I am not sure where that function is located but if it is part of a private framework then it will not be allowed.Mushy
@Mushy mh_execute_header is not a function, it's a mach_header of the app image. My bundle was successfully validated using Organizer->Archives->Validate, so I think it's not forbidden. But I want to be sure that _dyld_get... functions work and mach_headers contain uuid info in a bundle compiled for appstore, just like in development/adhoc builds.Skyward
@Skyward I do not believe they are private. They are part of the /usr/include/ directory for the SDK and should be ok. Since I have not submitted an app using these API's I can not 100% guarantee it will be approved.Mushy
M
2

For a quick answer about architecture alone since you are building your application you can check some preprocessor defines to determine the current architecture that your application is built for. Make sure you check for the highest version of arm available first because each newer version defines all older versions.

#if __arm__
#import <arm/arch.h>

#ifdef __ARM_ARCH_6K__
//This is armv6
#endif //__ARM_ARCH_6K__
#endif //__arm__
Mushy answered 7/4, 2011 at 14:8 Comment(0)
S
0

We can use sysctl, sysctlbyname system call to get or set system information.

Sample code:

#import <sys/sysctl.h>
#import <mach/machine.h>

int32_t value = 0;
size_t length = sizeof(value);
sysctlbyname("hw.cputype", &value, &length, NULL, 0);

if (value == CPU_TYPE_ARM64) {
    // arm64
}
else if (value == CPU_TYPE_ARM) {
    // armv7/armv7s
}
else if (value == CPU_TYPE_X86) {
    // simulator
}

I just list most common arch at 2016. Look for "hw.cpusubtype" to get more detial, like CPU_SUBTYPE_ARM_V6 CPU_SUBTYPE_ARM_V7 CPU_SUBTYPE_ARM_V7S

Sabol answered 20/4, 2016 at 3:45 Comment(1)
This will return CPU type of the device, when the question is about architecture the binary module was compiled for. So, the @Joe's answer was correct.Waldrup

© 2022 - 2024 — McMap. All rights reserved.