如何在运行时获取构建 UUID 和镜像基地址
Posted
技术标签:
【中文标题】如何在运行时获取构建 UUID 和镜像基地址【英文标题】:How to get the build UUID in runtime and the image base address 【发布时间】:2013-11-07 23:07:49 【问题描述】:是否可以获取构建 UUID,您可以在 dSYM 生成的文件和 ios 中的图像基地址中检查该 UUID。
低级的东西不太好,谁能指教?
【问题讨论】:
【参考方案1】:这是一个类似于 Kerni 的答案的解决方案,但适用于任何平台(iOS 设备 + 模拟器和 OS X)和任何架构(32 位 + 64 位)。它还返回 NSUUID
而不是 NSString
。
#import <mach-o/dyld.h>
#import <mach-o/loader.h>
static NSUUID *ExecutableUUID(void)
const struct mach_header *executableHeader = NULL;
for (uint32_t i = 0; i < _dyld_image_count(); i++)
const struct mach_header *header = _dyld_get_image_header(i);
if (header->filetype == MH_EXECUTE)
executableHeader = header;
break;
if (!executableHeader)
return nil;
BOOL is64bit = executableHeader->magic == MH_MAGIC_64 || executableHeader->magic == MH_CIGAM_64;
uintptr_t cursor = (uintptr_t)executableHeader + (is64bit ? sizeof(struct mach_header_64) : sizeof(struct mach_header));
const struct segment_command *segmentCommand = NULL;
for (uint32_t i = 0; i < executableHeader->ncmds; i++, cursor += segmentCommand->cmdsize)
segmentCommand = (struct segment_command *)cursor;
if (segmentCommand->cmd == LC_UUID)
const struct uuid_command *uuidCommand = (const struct uuid_command *)segmentCommand;
return [[NSUUID alloc] initWithUUIDBytes:uuidCommand->uuid];
return nil;
【讨论】:
【参考方案2】:你自己的建议太复杂了。
检查 HockeySDK 中的这段代码,只需几行代码即可:https://github.com/bitstadium/HockeySDK-iOS/blob/develop/Classes/BITHockeyBaseManager.m#L136
- (NSString *)executableUUID
// This now requires the testing of this feature to be done on an actual device, since it returns always empty strings on the simulator
#if !TARGET_IPHONE_SIMULATOR
const uint8_t *command = (const uint8_t *)(&_mh_execute_header + 1);
for (uint32_t idx = 0; idx < _mh_execute_header.ncmds; ++idx)
const struct load_command *load_command = (const struct load_command *)command;
if (load_command->cmd == LC_UUID)
const struct uuid_command *uuid_command = (const struct uuid_command *)command;
const uint8_t *uuid = uuid_command->uuid;
return [[NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
uuid[0], uuid[1], uuid[2], uuid[3],
uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11],
uuid[12], uuid[13], uuid[14], uuid[15]]
lowercaseString];
else
command += load_command->cmdsize;
#endif
return @"";
【讨论】:
谢谢,试试看。【参考方案3】:这可以通过我从一些同事那里找到的课程来实现,他们在合理的实验室中找到并根据他们的需要进行了重构。
这里可以获取构建UUID、镜像基地址和应用名称。
/**
* @internal
*
* Async-safe binary image list element.
*/
typedef struct bin_image
/** The binary image's header address. */
uintptr_t header;
/** The binary image's name/path. */
char *name;
/** The previous image in the list, or NULL */
struct bin_image *prev;
/** The next image in the list, or NULL. */
struct bin_image *next;
bin_image_t;
/**
* @internal
*
* Async-safe binary image list. May be used to iterate over the binary images currently
* available in-process.
*/
typedef struct bs_image_list
/** The lock used by writers. No lock is required for readers. */
OSSpinLock write_lock;
/** The head of the list, or NULL if the list is empty. Must only be used to iterate or delete entries. */
bin_image_t *head;
/** The tail of the list, or NULL if the list is empty. Must only be used to append new entries. */
bin_image_t *tail;
/** The list reference count. No nodes will be deallocated while the count is greater than 0. If the count
* reaches 0, all nodes in the free list will be deallocated. */
int32_t refcount;
/** The node free list. */
bin_image_t *free;
bs_image_list_t;
/**
* @internal
*
* Shared dyld image list.
*/
static bs_image_list_t shared_image_list = 0 ;
/**
* @internal
*
* Maintains a linked list of binary images with support for async-safe iteration. Writing may occur concurrently with
* async-safe reading, but is not async-safe.
*
* Atomic compare and swap is used to ensure a consistent view of the list for readers. To simplify implementation, a
* write mutex is held for all updates; the implementation is not designed for efficiency in the face of contention
* between readers and writers, and it's assumed that no contention should realistically occur.
* @
*/
/**
* Initialize a new binary image list and issue a memory barrier
*
* @param list The list structure to be initialized.
*
* @warning This method is not async safe.
*/
static void image_list_init (bs_image_list_t *list)
memset(list, 0, sizeof(*list));
list->write_lock = OS_SPINLOCK_INIT;
/**
* Free any binary image list resources.
*
* @warning This method is not async safe.
*/
static void image_list_free (bs_image_list_t *list)
bin_image_t *next = list->head;
while (next != NULL)
/* Save the current pointer and fetch the next pointer. */
bin_image_t *cur = next;
next = cur->next;
/* Deallocate the current item. */
if (cur->name != NULL)
free(cur->name);
free(cur);
/**
* Append a new binary image record to @a list.
*
* @param list The list to which the image record should be appended.
* @param header The image's header address.
* @param name The image's name.
*
* @warning This method is not async safe.
*/
static void image_list_append (bs_image_list_t *list, uintptr_t header, const char *name)
/* Initialize the new entry. */
bin_image_t *new = calloc(1, sizeof(bin_image_t));
new->header = header;
new->name = strdup(name);
/* Update the image record and issue a memory barrier to ensure a consistent view. */
OSMemoryBarrier();
/* Lock the list from other writers. */
OSSpinLockLock(&list->write_lock);
/* If this is the first entry, initialize the list. */
if (list->tail == NULL)
/* Update the list tail. This need not be done atomically, as tail is never accessed by a lockless reader. */
list->tail = new;
/* Atomically update the list head; this will be iterated upon by lockless readers. */
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, new, (void **) (&list->head)))
/* Should never occur */
NSLog(@"An async image head was set with tail == NULL despite holding lock.");
/* Otherwise, append to the end of the list */
else
/* Atomically slot the new record into place; this may be iterated on by a lockless reader. */
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, new, (void **) (&list->tail->next)))
NSLog(@"Failed to append to image list despite holding lock");
/* Update the prev and tail pointers. This is never accessed without a lock, so no additional barrier
* is required here. */
new->prev = list->tail;
list->tail = new;
OSSpinLockUnlock(&list->write_lock);
/**
* Remove a binary image record from @a list.
*
* @param header The header address of the record to be removed. The first record matching this address will be removed.
*
* @warning This method is not async safe.
*/
static void image_list_remove (bs_image_list_t *list, uintptr_t header)
/* Lock the list from other writers. */
OSSpinLockLock(&list->write_lock);
/* Find the record. */
bin_image_t *item = list->head;
while (item != NULL)
if (item->header == header)
break;
item = item->next;
/* If not found, nothing to do */
if (item == NULL)
OSSpinLockUnlock(&list->write_lock);
return;
/*
* Atomically make the item unreachable by readers.
*
* This serves as a synchronization point -- after the CAS, the item is no longer reachable via the list.
*/
if (item == list->head)
if (!OSAtomicCompareAndSwapPtrBarrier(item, item->next, (void **) &list->head))
NSLog(@"Failed to remove image list head despite holding lock");
else
/* There MUST be a non-NULL prev pointer, as this is not HEAD. */
if (!OSAtomicCompareAndSwapPtrBarrier(item, item->next, (void **) &item->prev->next))
NSLog(@"Failed to remove image list item despite holding lock");
/* Now that the item is unreachable, update the prev/tail pointers. These are never accessed without a lock,
* and need not be updated atomically. */
if (item->next != NULL)
/* Item is not the tail (otherwise next would be NULL), so simply update the next item's prev pointer. */
item->next->prev = item->prev;
else
/* Item is the tail (next is NULL). Simply update the tail record. */
list->tail = item->prev;
/* If a reader is active, simply spin until inactive. */
while (list->refcount > 0)
if (item->name != NULL)
free(item->name);
free(item);
OSSpinLockUnlock(&list->write_lock);
/**
* Retain or release the list for reading. This method is async-safe.
*
* This must be issued prior to attempting to iterate the list, and must called again once reads have completed.
*
* @param list The list to be be retained or released for reading.
* @param enable If true, the list will be retained. If false, released.
*/
static void image_list_set_reading (bs_image_list_t *list, bool enable)
if (enable)
/* Increment and issue a barrier. Once issued, no items will be deallocated while a reference is held. */
OSAtomicIncrement32Barrier(&list->refcount);
else
/* Increment and issue a barrier. Once issued, items may again be deallocated. */
OSAtomicDecrement32Barrier(&list->refcount);
/**
* Return the next image record. This method is async-safe. If no additional images are available, will return NULL;
*
* @param list The list to be iterated.
* @param current The current image record, or NULL to start iteration.
*/
static bin_image_t *image_list_next (bs_image_list_t *list, bin_image_t *current)
if (current != NULL)
return current->next;
return list->head;
/**
* @internal
* dyld image add notification callback.
*/
static void image_add_callback (const struct mach_header *mh, intptr_t vmaddr_slide)
Dl_info info;
/* Look up the image info */
if (dladdr(mh, &info) == 0)
NSLog(@"%s: dladdr(%p, ...) failed", __FUNCTION__, mh);
return;
/* Register the image */
image_list_append(&shared_image_list, (uintptr_t) mh, info.dli_fname);
/**
* @internal
*
* Write a binary image frame
*
* @param file Output file
* @param name binary image path (or name).
* @param image_base Mach-O image base.
*/
static void process_binary_image (const char *name, const void *header,
struct uuid_command *out_uuid, uintptr_t *out_baseaddr)
uint32_t ncmds;
const struct mach_header *header32 = (const struct mach_header *) header;
const struct mach_header_64 *header64 = (const struct mach_header_64 *) header;
struct load_command *cmd;
/* Check for 32-bit/64-bit header and extract required values */
switch (header32->magic)
/* 32-bit */
case MH_MAGIC:
case MH_CIGAM:
ncmds = header32->ncmds;
cmd = (struct load_command *) (header32 + 1);
break;
/* 64-bit */
case MH_MAGIC_64:
case MH_CIGAM_64:
ncmds = header64->ncmds;
cmd = (struct load_command *) (header64 + 1);
break;
default:
NSLog(@"Invalid Mach-O header magic value: %x", header32->magic);
return;
/* Compute the image size and search for a UUID */
struct uuid_command *uuid = NULL;
for (uint32_t i = 0; cmd != NULL && i < ncmds; i++)
/* DWARF dSYM UUID */
if (cmd->cmd == LC_UUID && cmd->cmdsize == sizeof(struct uuid_command))
uuid = (struct uuid_command *) cmd;
cmd = (struct load_command *) ((uint8_t *) cmd + cmd->cmdsize);
/* Base address */
uintptr_t base_addr;
base_addr = (uintptr_t) header;
*out_baseaddr = base_addr;
if(out_uuid && uuid)
memcpy(out_uuid, uuid, sizeof(struct uuid_command));
+ (void)registerCallback
_dyld_register_func_for_add_image(image_add_callback);
+ (NSArray *)loadedImages
NSMutableArray *array = [NSMutableArray array];
int i;
struct uuid_command uuid = 0 ;
uintptr_t baseaddr;
char uuidstr[64] = 0 ;
image_list_set_reading(&shared_image_list, true);
bin_image_t *image = NULL;
while ((image = image_list_next(&shared_image_list, image)) != NULL)
process_binary_image(image->name, (const void *) (image->header), &uuid, &baseaddr);
for(i=0; i<16; i++)
sprintf(&uuidstr[2*i], "%02x", uuid.uuid[i]);
[array addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSString stringWithCString:image->name encoding:NSASCIIStringEncoding], [NSString stringWithCString:uuidstr encoding:NSASCIIStringEncoding], [NSNumber numberWithUnsignedLong:(long unsigned) baseaddr], nil]
forKeys:[NSArray arrayWithObjects:@"name", @"uuid", @"baseaddr", nil]]];
return [NSArray arrayWithArray:array];
你必须在某个地方调用registerCallbacks
,这样它才能正确返回结果。
问候。
【讨论】:
以上是关于如何在运行时获取构建 UUID 和镜像基地址的主要内容,如果未能解决你的问题,请参考以下文章
在 React 中提交表单时如何获取要显示的名称和 uuid
JS怎么能实现获取设备的UUID,比如手机访问这个地址可以获取它的UUID。