一般学习Mach-O 文件的同学,总是有一些特殊的需求驱动的,我也不例外。下面是一个简单的demo 来学习Symbol Table ,定位需要找的符号,最终达到定位符号或者函数的地址。
#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || NS_BUILD_32_LIKE_64
#define mach_header_t mach_header_64
#define segment_command_t segment_command_64
#define LC_SEGMENT_t LC_SEGMENT_64
#define section_t section_64
#define nlist_t nlist_64
#define KArchitectureOffsetValue 0
#else
#define mach_header_t mach_header
#define segment_command_t segment_command
#define LC_SEGMENT_t LC_SEGMENT
#define section_t section
#define KArchitectureOffsetValue 1
#define nlist_t nlist
#endif
#define kInvalidImageIndex -1
#import "ViewController.h"
#import <mach-o/ldsyms.h>
#import <mach-o/loader.h>
#import <mach-o/dyld.h>
#import <mach-o/arch.h>
#include <sys/types.h>
#include <mach/machine.h>
#include <mach-o/nlist.h>
#include <dlfcn.h>
@interface ViewController ()
@property (nonatomic,assign) const struct mach_header_t *machHeader;
@property (nonatomic,assign) intptr_t slide;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSInteger imageIndex = [self getImageIndexWithFrameworkName:@"UIKit"];
if (imageIndex != kInvalidImageIndex)
{
_slide = _dyld_get_image_vmaddr_slide((uint32_t)imageIndex);
_machHeader = (const struct mach_header_t *)_dyld_get_image_header((uint32_t)imageIndex);
[self printAllSymbolWithMachHeader:_machHeader withSlide:_slide];
}
}
- (NSInteger)getImageIndexWithFrameworkName:(NSString *)frameworkName
{
NSAssert(frameworkName.length, @"Framework can not be nil");
NSInteger index = kInvalidImageIndex;
if (frameworkName.length)
{
const char* targetName = [frameworkName UTF8String];
size_t targetNameLength = strlen(targetName);
uint32_t count = _dyld_image_count();
for(uint32_t dyldIndex = 0; dyldIndex < count; dyldIndex++)
{
const char *dyld = _dyld_get_image_name(dyldIndex);
int length = (int)strlen(dyld);
int charIndex = length - 1;
for(; charIndex>= 0; --charIndex)
{
if(dyld[charIndex] == '/')
{
break;
}
}
charIndex++;
char *name = strndup(dyld + charIndex, length - charIndex);
if (NULL != name)
{
if (strncmp(targetName, name, targetNameLength) == 0)
{
index = dyldIndex;
free(name);
break;
}
free(name);
}
}
}
return index;
}
-(void)printAllSymbolWithMachHeader:(const struct mach_header_t *)machHeader withSlide:(intptr_t)slide
{
struct symtab_command *symCommand = NULL;
struct segment_command_t *linkedit = NULL;
struct segment_command_t *textSegment = NULL;
const uint8_t* linkEditBase = NULL;
const char *strtab = NULL;
struct nlist_t *symTab = NULL;
struct nlist_t *nl = NULL;
textSegment = find_segment(machHeader, SEG_TEXT);
if (!textSegment) {
NSLog(@"找不到__TEXT\n");
return;
}
linkedit = find_segment(machHeader, SEG_LINKEDIT);
if (!linkedit)
{
NSLog(@"找不到__LINKEDIT\n");
return;
}else
{
linkEditBase = (uint8_t *)(slide + linkedit->vmaddr - linkedit->fileoff);
}
symCommand = (struct symtab_command *)find_load_command(machHeader, LC_SYMTAB);
if (!symCommand)
{
NSLog(@"找不到Symbol\n");
return;
}
char *symbolName;
strtab = (const char *)(linkEditBase + symCommand->stroff);
symTab = (struct nlist_t *)(linkEditBase + symCommand->symoff);
nl = (struct nlist_t *)symTab;
for (uint64_t i = 0;i < symCommand->nsyms;i++)
{
if (nl->n_un.n_strx != 0) {
symbolName = (char *)strtab + nl->n_un.n_strx;
///< https://blog.sentry.io/2017/04/11/ios-symbolication-troubles.html
if (strcmp(symbolName, "<redacted>") != 0) {
printf("Symbol :%s \n",symbolName);
}
}
nl = (struct nlist_t *)((uint64_t)nl + sizeof(struct nlist_t));
}
}
struct segment_command_t *find_segment(const struct mach_header_t *mh, const char *segname)
{
struct load_command *lc;
struct segment_command_t *seg, *foundseg = NULL;
lc = (struct load_command *)((uint64_t)mh + sizeof(struct mach_header_t));
while ((uint64_t)lc < (uint64_t)mh + (uint64_t)mh->sizeofcmds) {
if (lc->cmd == LC_SEGMENT_t) {
seg = (struct segment_command_t *)lc;
if (strcmp(seg->segname, segname) == 0) {
foundseg = seg;
break;
}
}
lc = (struct load_command *)((uint64_t)lc + (uint64_t)lc->cmdsize);
}
return foundseg;
}
struct load_command *find_load_command(const struct mach_header_t *mh, uint32_t cmd)
{
struct load_command *lc = NULL;
struct load_command *foundlc = NULL;
lc = (struct load_command *)((uint64_t)mh + sizeof(struct mach_header_t));
while ((uint64_t)lc < (uint64_t)mh + (uint64_t)mh->sizeofcmds)
{
if (lc->cmd == cmd) {
foundlc = (struct load_command *)lc;
break;
}
lc = (struct load_command *)((uint64_t)lc + (uint64_t)lc->cmdsize);
}
return foundlc;
}
if (strcmp(symbolName, "<redacted>") != 0)
{
printf("Symbol :%s \n",symbolName);
}
这里过滤了<redacted> 符号,为什么?具体可以参考The Troubles With iOS Symbolication 。