Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于ios下dlopen函数的探究 #25

Open
ohroy opened this issue Apr 16, 2019 · 3 comments
Open

关于ios下dlopen函数的探究 #25

ohroy opened this issue Apr 16, 2019 · 3 comments

Comments

@ohroy
Copy link
Owner

ohroy commented Apr 16, 2019

内容摘要

近日我在开发一个小工具的时候,用到了一个加载动态库的函数dlopen,基于我的windows平台的开发背景,我想当然的认为其返回值是当前moduleload address,即模块基地址。然后经验证后发现,其和windows下的LoadLibrary还是有本质的差别。那么,它返回的究竟是一个什么东西呢?又该如何通过这样的返回值,来顺利获取到我们需要的load address甚至其它更重要的信息呢?带着这样的疑惑,我与几位资深经验的开发者对这个问题进行了深入的探究。本文是探究思路和研究过程的记录。在读文本文后,你应当会对此方面了解有所加深,并且上述疑惑应当能够释疑。

前知储备

本文面向有一定技术水准的读者,所以不再赘述模块基地址等相关定义。但相对地,下文出现的一些术语或者俗语,在此做一个简短的说明,如果需要进一步了解,请参考附录或者自行检索相关资料。

  • aslr(Address space layout randomization),加载地址随机化,通俗来讲,就是同一个模块在每个进程每次被加载后的地址(可能)不一致。
  • load address,模块加载地址,即模块基地址,也等同于header address
  • patch 补丁,这里名次动用做打补丁

为什么需要load address

在通常开发情境下,一般地代码片段如下:

int main(int argc, char **argv) {  
        void *handle;  
        bool (*fuck)(girl);  
        char *error;  
      
        handle = dlopen ("libfuck.dylib", RTLD_LAZY);  
        if (!handle) {  
            fprintf (stderr, "%s ", dlerror());  
            exit(1);  
        }  
      
        fuck = dlsym(handle, "fuck");  
        if ((error = dlerror()) != NULL)  {  
            fprintf (stderr, "%s ", error);  
            exit(1);  
        }
        girl* xx= make_girl_byname("xx");
        fuck(girl);
        dlclose(handle);  
        return 0;  
    }  

但特殊情况下,我们无法通过dlsym来完成符号的寻找。如该模块根本没有导出符号,或者我们想要显式的调用某个未导出符号的函数,或者我们需要patchheader中的某个字段等情形时,我们就需要知道该模块的load address

如何获取load address

由于aslr的原因,现行通用的解决方案是通过遍历模块来获取。典型的代码片段如下:

int32_t nModNums= _dyld_image_count();
const char *pszModName = NULL;
intptr_t pModSlide = 0;
const struct mach_header* pModHeader = NULL;
    
for (uint32_t i = 0; i < nModNums; i++)
{
	
	pszModName = _dyld_get_image_name(i);
	if(!strcmp(pszModName,"/path/to/xx.dylib")){
    	pModSlide  = _dyld_get_image_vmaddr_slide(i);
    	pModHeader = _dyld_get_image_header(i);
    	NSLog(@"[FLWB]:vm_slide:%lx,load addr:%p",pModSlide,pModHeader);
	}

}

这样,我们就通过对比名字的方式,来获取到了header address
然而我们思考一下,这样做真的没有问题吗???
我认为,起码有两三个问题。

  1. 遍历导致的性能损失。
  2. 此处至少需要调用三个api来完成操作,而此三个api有较明显的特征,非常容易被hook针对。
  3. 也是最重要的,此处使用的几个api是线程安全的。这里是一般情况下我们不会注意到的,实际上,这个是一个常见的安全漏洞。而文档上也对此有说明
/*
 * The following functions allow you to iterate through all loaded images.  
 * This is not a thread safe operation.  Another thread can add or remove
 * an image during the iteration.  
 *
 * Many uses of these routines can be replace by a call to dladdr() which 
 * will return the mach_header and name of an image, given an address in 
 * the image. dladdr() is thread safe.
 */
extern uint32_t                    _dyld_image_count(void)                              __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0);
extern const struct mach_header*   _dyld_get_image_header(uint32_t image_index)         __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0);
extern intptr_t                    _dyld_get_image_vmaddr_slide(uint32_t image_index)   __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0);
extern const char*                 _dyld_get_image_name(uint32_t image_index)           __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0);

大意是要警惕在遍历时其它进程的添加或者修改操作。那么。显然地,假使我们在遍历的过程中,其它线程删除了一个模块,那么将导致我们的索引移位,得到一个完全不匹配的结果,同时也有可能造成内存访问错误。

那么,要想同时解决以上三个问题,(我认为)最优秀的解决应当是通过dlopen返回的结果来获取。从推理上来讲,dlsym既然能通过这个参数来获取到符号信息,而符号表是需要通过header来计算的。换言之,通过这个dlopen的返回值一定可以获取到header。搜索了一番,网上对此没有任何资料。
但理论上行得通的事情,实际上应该是可以的。于是我开始了探寻。

探寻过程

有了上文的资料,显然,dlopen的返回值必定是一个结构。我们由此来做发散思维。与此同时,我与几位大佬交流后,他们也都给出了非常重要的建议。

头文件及注释

这个是一个最简单的方案,我们去看一下dlopen的头文件声明,其位于/usr/include/dlfcn.h

extern int dlclose(void * __handle);
extern char * dlerror(void);
extern void * dlopen(const char * __path, int __mode);
extern void * dlsym(void * __handle, const char * __symbol);

然而这里面定义它为void* 类型,且变量名字为handle
我们幻想中此处应为一个结构体指针的美梦破碎了。

借用linux头文件

此方案由咸🐟提出。

On Linux, dlopen doesn't return the address where the ELF binary was loaded. It returns struct link_map instead, which has .l_addr member. So you'll want something like:

struct link_map *lm = (struct link_map*) dlopen(0, RTLD_NOW);
printf("%p\n", lm->l_addr);

However, despite what comment in /usr/include/link.h says, .l_addr is actually not a load address either. Instead, it's the difference between where ELF image was linked to load, and where it was actually loaded.

看到这份资料的时候我还是比较兴奋的,因为这无疑证明了我们的推断是正确的。然而我一番寻找,发现在macos下根本没有link.h这个头文件。一番搜索没有答案之下,我们安装windows subsystem然后顺利找到这个头文件。
其中关键部分如下:

struct link_map
  {
    /* These first few members are part of the protocol with the debugger.
       This is the same format used in SVR4.  */

    ElfW(Addr) l_addr;		/* Difference between the address in the ELF
				   file and the addresses in memory.  */
    char *l_name;		/* Absolute file name object was found in.  */
    ElfW(Dyn) *l_ld;		/* Dynamic section of the shared object.  */
    struct link_map *l_next, *l_prev; /* Chain of loaded objects.  */
  };

这里我们简单的复制此关键部分,并修改为

struct link_map
  {
    /* These first few members are part of the protocol with the debugger.
       This is the same format used in SVR4.  */

    void l_addr;		/* Difference between the address in the ELF
				   file and the addresses in memory.  */
    char *l_name;		/* Absolute file name object was found in.  */
    void*l_ld;		/* Dynamic section of the shared object.  */
    struct link_map *l_next, *l_prev; /* Chain of loaded objects.  */
  };

我们的代码如下:

	void* xxHandle=dlopen("xx.dylib",RTLD_LAZY);
	NSLog(@"[FLWB]:xxHandle:%p",xxHandle);
	struct link_map *lm = (struct link_map*)xxHandle;
	NSLog(@"[FLWB]:%s = %p",lm->l_name, lm->l_addr);

运行之后神奇的事情发生了,模块名字竟然正确的输出了!虽然基地址返回的明显不对。但至少证明了它确实是有组织的。但可能因为苹果做了修改导致了不同。
我们继续尝试,发现除了名字奇迹般的正确输出以外,其它的无一相符。
这里提一句题外话,我们显然可以看到linux下的设计还是比较精巧的,它使用了链表来存在模块信息,方便插入和遍历。这一点上和windows是一致的。在此情形下,我们需要隐藏模块的话,就需要修改相邻的两个链表左右节点,也就是所谓的“断链”。有兴趣的话,可以自行研究。

借用anroid头文件

linux的头文件无法适用于ios,是否是因为版本的问题呢,android是否可行?一番搜索之下我们找到来安卓的对应头文件

struct soinfo {
 public:
  char name[SOINFO_NAME_LEN];
  const Elf32_Phdr* phdr;
  size_t phnum;
  Elf32_Addr entry;
  Elf32_Addr base;
  unsigned size;

  uint32_t unused1;  // DO NOT USE, maintained for compatibility.

  Elf32_Dyn* dynamic;

  uint32_t unused2; // DO NOT USE, maintained for compatibility
  uint32_t unused3; // DO NOT USE, maintained for compatibility

  soinfo* next;
  unsigned flags;

  const char* strtab;
  Elf32_Sym* symtab;

  size_t nbucket;
  size_t nchain;
  unsigned* bucket;
  unsigned* chain;

  unsigned* plt_got;

  Elf32_Rel* plt_rel;
  size_t plt_rel_count;

  Elf32_Rel* rel;
  size_t rel_count;

  linker_function_t* preinit_array;
  size_t preinit_array_count;

  linker_function_t* init_array;
  size_t init_array_count;
  linker_function_t* fini_array;
  size_t fini_array_count;

  linker_function_t init_func;
  linker_function_t fini_func;

#if defined(ANDROID_ARM_LINKER)
  // ARM EABI section used for stack unwinding.
  unsigned* ARM_exidx;
  size_t ARM_exidx_count;
#elif defined(ANDROID_MIPS_LINKER)
  unsigned mips_symtabno;
  unsigned mips_local_gotno;
  unsigned mips_gotsym;
#endif

  size_t ref_count;
  link_map_t link_map;

  bool constructors_called;

  // When you read a virtual address from the ELF file, add this
  // value to get the corresponding address in the process' address space.
  Elf32_Addr load_bias;

  bool has_text_relocations;
  bool has_DT_SYMBOLIC;

  void CallConstructors();
  void CallDestructors();
  void CallPreInitConstructors();

 private:
  void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
  void CallFunction(const char* function_name, linker_function_t function);
};

然而一番尝试后同样无果,这彻底说明了,至少这部分apple是有自己的实现。

特殊符号定位法

注意,这里的特殊符号不是指的标点符号,而是特殊的symbol
此方案同样由咸🐟提出,并由Zhang扩充。
原理是通过macho里导出的特殊符号来直接获取。具体的符号如

/*
 * The value of the link editor defined symbol _MH_DYLIB_SYM is the address
 * of the mach header in a Mach-O dylib file type.  It does not appear in
 * any file type other than a MH_DYLIB file type.  The type of the symbol is
 * an N_SECT symbol even thought the header is not part of any section.  This
 * symbol is private to the code in the library it is a part of.
 */
#define _MH_DYLIB_SYM	"__mh_dylib_header"
#define MH_DYLIB_SYM	"_mh_dylib_header"

我们修改代码如下:

	void* xxHandle=dlopen("fuck.dylib",RTLD_LAZY);
	void* fuck1 = dlsym(xxHandle, "__mh_dylib_header");  
    void* fuck2 = dlsym(xxHandle, "_mh_dylib_header"); 

结果均获取不到这两个符号,推测为我这边测试环境的问题,或者在链接的过程中被去掉了。

特定符号偏移修正法

此方案由柳下惠提出,原理是通过查找给定的某个符号,然后减去对应的偏移来获取。这种是理论上肯定能行得通的解决方案,但实际上如若是遇到某个模块根本没有导出符号的话,此方案也是束手无策。此处有jmpzws基于此而来的优化方案。即是查找dyld_stub_binder这个特定的符号,align然后减去0x4000。
但同样地,我这边也是无法获取到这个符号。

回调获取法

此方案由Zhang提出,原理是先通过_dyld_register_func_for_add_image来注册一个回调,然后再dlopen即可。因为此回调回调用void (*func)(const mach_header *mh, intptr_t vmaddr_slide)这样的callback,在参数里面刚好就有mach_header,以此来获取模块基地址。此方案从理论上来讲,是完全可行的。但由于操作过于复杂,且(我)没有找到对应的取消注册钩子的办法导致使用代价过大,所以没有深入探寻。

暴力偏移法

多番尝试无果后,现行简单粗暴的方法。即dump出疑似结构的handle的内存,观察load address的位置,然后计算偏移,往后通过偏移来获取。
这种思路类似于游戏外挂的思路,即不管缘由,不考虑理论,先行实现。
于是我们修改代码如下

	void* xxHandle=dlopen("xx.dylib",RTLD_LAZY);
	NSLog(@"[FLWB]:xxHandle:%p",xxHandle);
	NSData *dataData = [NSData dataWithBytes:xxHandle length:0x80];
	NSLog(@"[FLWB]:%p = %@",xxHandle, dataData);

果然,我们在第0x58个成员的位置找到了疑似load address的位置。但这在实际使用上会有很大的问题。其中最为麻烦的是32位和64位的问题,由于类型长度可能不一致,所以在32位下未必是0x58/2,而且在执行时要定义宏或者手动判断系统位数,显然是一个比较繁琐的事情。
但这进一步说明了我们的思路的正确的,一定存在这样的一个结构体。正是由于种种方案给定我们信心,我们才能坚定的走下去。
所以我们下一步就是要找到这个头文件,如果找不到,那我们就需要猜测,然后自行做一个这样的头文件。(反正windows下都是这么做的。)

源码追溯法

正在此时,突然想起来jmpzws曾发的一个截图。

ImageLoader* image = (ImageLoader*)(((uintptr_t)handle) & (-4));	// clear mode bits

那么,这个ImageLoader可不就是我们苦苦寻找的结构体吗?于是我们找到对应的dyld的源码。
我们一番寻找后,发现其原来是个类,头文件如下

/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
 *
 * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


#ifndef __IMAGELOADER__
#define __IMAGELOADER__

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <mach/mach_time.h> // struct mach_timebase_info
#include <mach/mach_init.h> // struct mach_thread_self
#include <mach/shared_region.h>
#include <mach-o/loader.h> 
#include <mach-o/nlist.h> 
#include <stdint.h>
#include <stdlib.h>
#include <TargetConditionals.h>
#include <vector>
#include <new>
#include <uuid/uuid.h>

#if __arm__
 #include <mach/vm_page_size.h>
#endif

#if __x86_64__ || __i386__
	#include <CrashReporterClient.h>
#else
	// work around until iOS has CrashReporterClient.h
	#define CRSetCrashLogMessage(x)
	#define CRSetCrashLogMessage2(x)
#endif

#ifndef SHARED_REGION_BASE_ARM64
	#define SHARED_REGION_BASE_ARM64 0x7FFF80000000LL
#endif

#ifndef SHARED_REGION_SIZE_ARM64
	#define SHARED_REGION_SIZE_ARM64 0x10000000LL
#endif


#define LOG_BINDINGS 0

#include "mach-o/dyld_images.h"
#include "mach-o/dyld_priv.h"
#include "DyldSharedCache.h"

#if __i386__
	#define SHARED_REGION_BASE SHARED_REGION_BASE_I386
	#define SHARED_REGION_SIZE SHARED_REGION_SIZE_I386
#elif __x86_64__
	#define SHARED_REGION_BASE SHARED_REGION_BASE_X86_64
	#define SHARED_REGION_SIZE SHARED_REGION_SIZE_X86_64
#elif __arm__
	#define SHARED_REGION_BASE SHARED_REGION_BASE_ARM
	#define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM
#elif __arm64__
	#define SHARED_REGION_BASE SHARED_REGION_BASE_ARM64
	#define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM64
#endif

#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
	#define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10
#endif
#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT
	#define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08
#endif

#ifndef LC_MAIN
	#define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */
	struct entry_point_command {
		uint32_t  cmd;	/* LC_MAIN only used in MH_EXECUTE filetypes */
		uint32_t  cmdsize;	/* 24 */
		uint64_t  entryoff;	/* file (__TEXT) offset of main() */
		uint64_t  stacksize;/* if not zero, initial stack size */
	};
#endif

#if __IPHONE_OS_VERSION_MIN_REQUIRED          
	#define SPLIT_SEG_SHARED_REGION_SUPPORT 0
	#define SPLIT_SEG_DYLIB_SUPPORT			0
	#define PREBOUND_IMAGE_SUPPORT			__arm__
	#define TEXT_RELOC_SUPPORT				__i386__
	#define SUPPORT_OLD_CRT_INITIALIZATION	0
	#define SUPPORT_LC_DYLD_ENVIRONMENT		1
	#define SUPPORT_VERSIONED_PATHS			1
	#define SUPPORT_CLASSIC_MACHO			__arm__
	#define SUPPORT_ZERO_COST_EXCEPTIONS	(!__USING_SJLJ_EXCEPTIONS__)
	#define INITIAL_IMAGE_COUNT				150
	#define SUPPORT_ACCELERATE_TABLES		!TARGET_IPHONE_SIMULATOR
	#define SUPPORT_ROOT_PATH				TARGET_IPHONE_SIMULATOR
	#define USES_CHAINED_BINDS				(__arm64e__)
#else
	#define SPLIT_SEG_SHARED_REGION_SUPPORT 0
	#define SPLIT_SEG_DYLIB_SUPPORT			__i386__
	#define PREBOUND_IMAGE_SUPPORT			__i386__
	#define TEXT_RELOC_SUPPORT				__i386__
	#define SUPPORT_OLD_CRT_INITIALIZATION	__i386__
	#define SUPPORT_LC_DYLD_ENVIRONMENT		(__i386__ || __x86_64__)
	#define SUPPORT_VERSIONED_PATHS			1
	#define SUPPORT_CLASSIC_MACHO			1
	#define SUPPORT_ZERO_COST_EXCEPTIONS	1
	#define INITIAL_IMAGE_COUNT				200
	#define SUPPORT_ACCELERATE_TABLES		0
	#define SUPPORT_ROOT_PATH				1
	#define USES_CHAINED_BINDS				0
#endif

#define MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE (32*1024)

#define MH_HAS_OBJC			0x40000000


// <rdar://problem/13590567> optimize away dyld's initializers
#define VECTOR_NEVER_DESTRUCTED(type) \
	namespace std { \
		template <> \
		__vector_base<type, std::allocator<type> >::~__vector_base() { } \
	}
#define VECTOR_NEVER_DESTRUCTED_EXTERN(type) \
       namespace std { \
               template <> \
               __vector_base<type, std::allocator<type> >::~__vector_base(); \
       }
#define VECTOR_NEVER_DESTRUCTED_IMPL(type) \
       namespace std { \
               template <> \
               __vector_base<type, std::allocator<type> >::~__vector_base() { } \
       }

// utilities
namespace dyld {
	extern __attribute__((noreturn)) void throwf(const char* format, ...)  __attribute__((format(printf, 1, 2)));
	extern void log(const char* format, ...)  __attribute__((format(printf, 1, 2)));
	extern void warn(const char* format, ...)  __attribute__((format(printf, 1, 2)));
	extern const char* mkstringf(const char* format, ...)  __attribute__((format(printf, 1, 2)));
#if LOG_BINDINGS
	extern void logBindings(const char* format, ...)  __attribute__((format(printf, 1, 2)));
#endif
}
extern "C" 	int   vm_alloc(vm_address_t* addr, vm_size_t size, uint32_t flags);
extern "C" 	void* xmmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset);


#if __LP64__
	struct macho_header				: public mach_header_64  {};
	struct macho_nlist				: public nlist_64  {};	
#else
	struct macho_header				: public mach_header  {};
	struct macho_nlist				: public nlist  {};	
#endif


#if __arm64__
	#define dyld_page_trunc(__addr)     (__addr & (-16384))
	#define dyld_page_round(__addr)     ((__addr + 16383) & (-16384))
	#define dyld_page_size              16384
#elif __arm__
	#define dyld_page_trunc(__addr)     trunc_page_kernel(__addr)
	#define dyld_page_round(__addr)     round_page_kernel(__addr)
	#define dyld_page_size              vm_kernel_page_size
#else
	#define dyld_page_trunc(__addr)     (__addr & (-4096))
	#define dyld_page_round(__addr)     ((__addr + 4095) & (-4096))
	#define dyld_page_size              4096
#endif



struct ProgramVars
{
	const void*		mh;
	int*			NXArgcPtr;
	const char***	NXArgvPtr;
	const char***	environPtr;
	const char**	__prognamePtr;
};



//
// ImageLoader is an abstract base class.  To support loading a particular executable
// file format, you make a concrete subclass of ImageLoader.
//
// For each executable file (dynamic shared object) in use, an ImageLoader is instantiated.
//
// The ImageLoader base class does the work of linking together images, but it knows nothing
// about any particular file format.
//
//
class ImageLoader {
public:

	typedef uint32_t DefinitionFlags;
	static const DefinitionFlags kNoDefinitionOptions = 0;
	static const DefinitionFlags kWeakDefinition = 1;
	
	typedef uint32_t ReferenceFlags;
	static const ReferenceFlags kNoReferenceOptions = 0;
	static const ReferenceFlags kWeakReference = 1;
	static const ReferenceFlags kTentativeDefinition = 2;
	
	enum PrebindMode { kUseAllPrebinding, kUseSplitSegPrebinding, kUseAllButAppPredbinding, kUseNoPrebinding };
	enum BindingOptions { kBindingNone, kBindingLazyPointers, kBindingNeverSetLazyPointers };
	enum SharedRegionMode { kUseSharedRegion, kUsePrivateSharedRegion, kDontUseSharedRegion, kSharedRegionIsSharedCache };
	
	struct Symbol;  // abstact symbol

	struct MappedRegion {
		uintptr_t	address;
		size_t		size;
	};

	struct RPathChain {
		RPathChain(const RPathChain* n, std::vector<const char*>* p) : next(n), paths(p) {};
		const RPathChain*			next;
		std::vector<const char*>*	paths;
	};

	struct DOFInfo {
		void*				dof;
		const mach_header*	imageHeader;
		const char*			imageShortName;
	};

	struct DynamicReference {
		ImageLoader* from;
		ImageLoader* to;
	};
	
	struct InitializerTimingList
	{
		uintptr_t	count;
		struct {
			const char*		shortName;
			uint64_t		initTime;
		}			images[1];

		void addTime(const char* name, uint64_t time);
	};

	typedef void (^CoalesceNotifier)(const Symbol* implSym, const ImageLoader* implIn, const mach_header* implMh);
	
	struct LinkContext {
		ImageLoader*	(*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths, bool enforceIOSMac, unsigned& cacheIndex);
		void			(*terminationRecorder)(ImageLoader* image);
		bool			(*flatExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image);
		bool			(*coalescedExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image, CoalesceNotifier);
		unsigned int	(*getCoalescedImages)(ImageLoader* images[], unsigned imageIndex[]);
		void			(*undefinedHandler)(const char* name);
		MappedRegion*	(*getAllMappedRegions)(MappedRegion*);
		void *			(*bindingHandler)(const char *, const char *, void *);
		void			(*notifySingle)(dyld_image_states, const ImageLoader* image, InitializerTimingList*);
		void			(*notifyBatch)(dyld_image_states state, bool preflightOnly);
		void			(*removeImage)(ImageLoader* image);
		void			(*registerDOFs)(const std::vector<DOFInfo>& dofs);
		void			(*clearAllDepths)();
		void			(*printAllDepths)();
		unsigned int	(*imageCount)();
		void			(*setNewProgramVars)(const ProgramVars&);
		bool			(*inSharedCache)(const char* path);
		void			(*setErrorStrings)(unsigned errorCode, const char* errorClientOfDylibPath,
										const char* errorTargetDylibPath, const char* errorSymbol);
		ImageLoader*	(*findImageContainingAddress)(const void* addr);
		void			(*addDynamicReference)(ImageLoader* from, ImageLoader* to);
#if SUPPORT_ACCELERATE_TABLES
		void			(*notifySingleFromCache)(dyld_image_states, const mach_header* mh, const char* path);
		dyld_image_state_change_handler (*getPreInitNotifyHandler)(unsigned index);
		dyld_image_state_change_handler (*getBoundBatchHandler)(unsigned index);
#endif
		
#if SUPPORT_OLD_CRT_INITIALIZATION
		void			(*setRunInitialzersOldWay)();
#endif
		BindingOptions	bindingOptions;
		int				argc;
		const char**	argv;
		const char**	envp;
		const char**	apple;
		const char*		progname;
		ProgramVars		programVars;
		ImageLoader*	mainExecutable;
		const char* const * imageSuffix;
#if SUPPORT_ROOT_PATH
		const char**	rootPaths;
#endif
		const DyldSharedCache*  dyldCache;
		const dyld_interpose_tuple*	dynamicInterposeArray;
		size_t			dynamicInterposeCount;
		PrebindMode		prebindUsage;
		SharedRegionMode sharedRegionMode;
		bool			dyldLoadedAtSameAddressNeededBySharedCache;
		bool			strictMachORequired;
		bool			allowAtPaths;
		bool			allowEnvVarsPrint;
		bool			allowEnvVarsPath;
		bool			allowEnvVarsSharedCache;
		bool			allowClassicFallbackPaths;
		bool			allowInsertFailures;
		bool			mainExecutableCodeSigned;
		bool			preFetchDisabled;
		bool			prebinding;
		bool			bindFlat;
		bool			linkingMainExecutable;
		bool			startedInitializingMainExecutable;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
		bool			marzipan;
#endif
		bool			verboseOpts;
		bool			verboseEnv;
		bool			verboseLoading;
		bool			verboseMapping;
		bool			verboseRebase;
		bool			verboseBind;
		bool			verboseWeakBind;
		bool			verboseInit;
		bool			verboseDOF;
		bool			verbosePrebinding;
		bool			verboseCoreSymbolication;
		bool			verboseWarnings;
		bool			verboseRPaths;
		bool			verboseInterposing;
		bool			verboseCodeSignatures;
	};
	
	struct CoalIterator
	{
		ImageLoader*	image;
		const char*		symbolName;
		unsigned int	loadOrder;
		bool			weakSymbol;
		bool			symbolMatches;
		bool			done;
		// the following are private to the ImageLoader subclass
		uintptr_t		curIndex;
		uintptr_t		endIndex;
		uintptr_t		address;
		uintptr_t		type;
		uintptr_t		addend;
		uintptr_t		imageIndex;
	};
	
	virtual	void			initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned imageIndex) = 0;
	virtual	bool			incrementCoalIterator(CoalIterator&) = 0;
	virtual	uintptr_t		getAddressCoalIterator(CoalIterator&, const LinkContext& context) = 0;
	virtual	void			updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context) = 0;
	
	struct UninitedUpwards
	{
		uintptr_t	 count;
		ImageLoader* images[1];
	};

	
										// constructor is protected, but anyone can delete an image
	virtual								~ImageLoader();
	
										// link() takes a newly instantiated ImageLoader and does all 
										// fixups needed to make it usable by the process
	void								link(const LinkContext& context, bool forceLazysBound, bool preflight, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath);
	
										// runInitializers() is normally called in link() but the main executable must 
										// run crt code before initializers
	void								runInitializers(const LinkContext& context, InitializerTimingList& timingInfo);
	
										// called after link() forces all lazy pointers to be bound
	void								bindAllLazyPointers(const LinkContext& context, bool recursive);
	
										// used by dyld to see if a requested library is already loaded (might be symlink)
	bool								statMatch(const struct stat& stat_buf) const;

										// get short name of this image
	const char*							getShortName() const;

										// returns leaf name
	static const char*					shortName(const char* fullName);

										// get path used to load this image, not necessarily the "real" path
	const char*							getPath() const { return fPath; }

	uint32_t							getPathHash() const { return fPathHash; }

										// get the "real" path for this image (e.g. no @rpath)
	const char*							getRealPath() const;

										// get path this image is intended to be placed on disk or NULL if no preferred install location
	virtual const char*					getInstallPath() const = 0;

										// image was loaded with NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME and all clients are looking for install path
	bool								matchInstallPath() const;
	void								setMatchInstallPath(bool);
	
										// mark that this image's exported symbols should be ignored when linking other images (e.g. RTLD_LOCAL)
	void								setHideExports(bool hide = true);
	
										// check if this image's exported symbols should be ignored when linking other images 
	bool								hasHiddenExports() const;
	
										// checks if this image is already linked into the process
	bool								isLinked() const;
	
										// even if image is deleted, leave segments mapped in
	void								setLeaveMapped();
	
										// even if image is deleted, leave segments mapped in
	bool								leaveMapped() { return fLeaveMapped; }

										// image resides in dyld shared cache
	virtual bool						inSharedCache() const { return false; };

										// checks if the specifed address is within one of this image's segments
	virtual bool						containsAddress(const void* addr) const;

										// checks if the specifed symbol is within this image's symbol table
	virtual bool						containsSymbol(const void* addr) const = 0;

										// checks if the specifed address range overlaps any of this image's segments
	virtual bool						overlapsWithAddressRange(const void* start, const void* end) const;

										// adds to list of ranges of memory mapped in
	void								getMappedRegions(MappedRegion*& region) const;

										// st_mtime from stat() on file
	time_t								lastModified() const;

										// only valid for main executables, returns a pointer its entry point from LC_MAIN
	virtual void*						getEntryFromLC_MAIN() const = 0;
	
										// only valid for main executables, returns a pointer its main from LC_UNIXTHREAD
	virtual void*						getEntryFromLC_UNIXTHREAD() const = 0;
	
										// dyld API's require each image to have an associated mach_header
	virtual const struct mach_header*   machHeader() const = 0;
	
										// dyld API's require each image to have a slide (actual load address minus preferred load address)
	virtual uintptr_t					getSlide() const = 0;
	
										// last address mapped by image
	virtual const void*					getEnd() const = 0;
	
										// image has exports that participate in runtime coalescing
	virtual bool						hasCoalescedExports() const = 0;

										// search symbol table of definitions in this image for requested name
	virtual bool						findExportedSymbolAddress(const LinkContext& context, const char* symbolName,
																const ImageLoader* requestorImage, int requestorOrdinalOfDef,
																bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const;

										// search symbol table of definitions in this image for requested name
	virtual const Symbol*				findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const = 0;
	
										// search symbol table of definitions in this image for requested name
	virtual const Symbol*				findExportedSymbol(const char* name, bool searchReExports, const ImageLoader** foundIn) const {
											return findExportedSymbol(name, searchReExports, this->getPath(), foundIn);
										}
	
										// gets address of implementation (code) of the specified exported symbol
	virtual uintptr_t					getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, 
													const ImageLoader* requestor=NULL, bool runResolver=false, const char* symbolName=NULL) const = 0;
	
										// gets attributes of the specified exported symbol
	virtual DefinitionFlags				getExportedSymbolInfo(const Symbol* sym) const = 0;
	
										// gets name of the specified exported symbol
	virtual const char*					getExportedSymbolName(const Symbol* sym) const = 0;
	
										// gets how many symbols are exported by this image
	virtual uint32_t					getExportedSymbolCount() const = 0;
			
										// gets the i'th exported symbol
	virtual const Symbol*				getIndexedExportedSymbol(uint32_t index) const = 0;
			
										// find exported symbol as if imported by this image
										// used by RTLD_NEXT
	virtual const Symbol*				findExportedSymbolInDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const;
	
										// find exported symbol as if imported by this image
										// used by RTLD_SELF
	virtual const Symbol*				findExportedSymbolInImageOrDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const;
	
										// gets how many symbols are imported by this image
	virtual uint32_t					getImportedSymbolCount() const = 0;
	
										// gets the i'th imported symbol
	virtual const Symbol*				getIndexedImportedSymbol(uint32_t index) const = 0;
			
										// gets attributes of the specified imported symbol
	virtual ReferenceFlags				getImportedSymbolInfo(const Symbol* sym) const = 0;
			
										// gets name of the specified imported symbol
	virtual const char*					getImportedSymbolName(const Symbol* sym) const = 0;
			
										// find the closest symbol before addr
	virtual const char*					findClosestSymbol(const void* addr, const void** closestAddr) const = 0;
	
										// for use with accelerator tables
	virtual const char*					getIndexedPath(unsigned) const { return getPath(); }
	virtual const char*					getIndexedShortName(unsigned) const { return getShortName(); }

										// checks if this image is a bundle and can be loaded but not linked
	virtual bool						isBundle() const = 0;
	
										// checks if this image is a dylib 
	virtual bool						isDylib() const = 0;
	
										// checks if this image is a main executable 
	virtual bool						isExecutable() const = 0;
	
										// checks if this image is a main executable 
	virtual bool						isPositionIndependentExecutable() const = 0;
	
										// only for main executable
	virtual bool						forceFlat() const = 0;
	
										// called at runtime when a lazily bound function is first called
	virtual uintptr_t					doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0;
	
										// called at runtime when a fast lazily bound function is first called
	virtual uintptr_t					doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context,
															void (*lock)(), void (*unlock)()) = 0;

										// calls termination routines (e.g. C++ static destructors for image)
	virtual void						doTermination(const LinkContext& context) = 0;
					
										// return if this image has initialization routines
	virtual bool						needsInitialization() = 0;
			
										// return if this image has specified section and set start and length
	virtual bool						getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) = 0;

										// fills in info about __eh_frame and __unwind_info sections
	virtual void						getUnwindInfo(dyld_unwind_sections* info) = 0;

										// given a pointer into an image, find which segment and section it is in
	virtual const struct macho_section* findSection(const void* imageInterior) const = 0;

										// given a pointer into an image, find which segment and section it is in
	virtual bool						findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) = 0;
	
										// the image supports being prebound
	virtual bool						isPrebindable() const = 0;
	
										// the image is prebindable and its prebinding is valid
	virtual bool						usablePrebinding(const LinkContext& context) const = 0;
	
										// add all RPATH paths this image contains
	virtual	void						getRPaths(const LinkContext& context, std::vector<const char*>&) const = 0;
	
										// image has or uses weak definitions that need runtime coalescing
	virtual bool						participatesInCoalescing() const = 0;
		
										// if image has a UUID, copy into parameter and return true
	virtual	bool						getUUID(uuid_t) const = 0;

										// dynamic interpose values onto this image
	virtual void						dynamicInterpose(const LinkContext& context) = 0;

										// record interposing for any late binding
	void								addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count);
		
	virtual const char*					libPath(unsigned int) const = 0;

										// Image has objc sections, so information objc about when it comes and goes
	virtual	bool						notifyObjC() const { return false; }

	virtual bool						overridesCachedDylib(uint32_t& num) const { return false; }
	virtual void						setOverridesCachedDylib(uint32_t num) { }


//
// A segment is a chunk of an executable file that is mapped into memory.  
//
	virtual unsigned int				segmentCount() const = 0;
	virtual const char*					segName(unsigned int) const = 0;
	virtual uintptr_t					segSize(unsigned int) const = 0;
	virtual uintptr_t					segFileSize(unsigned int) const = 0;
	virtual bool						segHasTrailingZeroFill(unsigned int) = 0;
	virtual uintptr_t					segFileOffset(unsigned int) const = 0;
	virtual bool						segReadable(unsigned int) const = 0;
	virtual bool						segWriteable(unsigned int) const = 0;
	virtual bool						segExecutable(unsigned int) const = 0;
	virtual bool						segUnaccessible(unsigned int) const = 0;
	virtual bool						segHasPreferredLoadAddress(unsigned int) const = 0;
	virtual uintptr_t					segPreferredLoadAddress(unsigned int) const = 0;
	virtual uintptr_t					segActualLoadAddress(unsigned int) const = 0;
	virtual uintptr_t					segActualEndAddress(unsigned int) const = 0;

	
										// info from LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS
	virtual uint32_t					sdkVersion() const = 0;
	virtual uint32_t					minOSVersion() const = 0;
	
										// if the image contains interposing functions, register them
	virtual void						registerInterposing(const LinkContext& context) = 0;

	virtual bool						usesChainedFixups() const { return false; }


										// when resolving symbols look in subImage if symbol can't be found
	void								reExport(ImageLoader* subImage);

	virtual void						recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload);
	virtual void						recursiveBindWithAccounting(const LinkContext& context, bool forceLazysBound, bool neverUnload);
	void								weakBind(const LinkContext& context);

	void								applyInterposing(const LinkContext& context);

	dyld_image_states					getState() { return (dyld_image_states)fState; }

	ino_t								getInode() const { return fInode; }
    dev_t                               getDevice() const { return fDevice; }

										// used to sort images bottom-up
	int									compare(const ImageLoader* right) const;
	
	void								incrementDlopenReferenceCount() { ++fDlopenReferenceCount; }

	bool								decrementDlopenReferenceCount();
	
	void								printReferenceCounts();

	uint32_t							dlopenCount() const { return fDlopenReferenceCount; }

	void								setCanUnload() { fNeverUnload = false; fLeaveMapped = false; }

	bool								neverUnload() const { return fNeverUnload; }

	void								setNeverUnload() { fNeverUnload = true; fLeaveMapped = true; }
	void								setNeverUnloadRecursive();
	
	bool								isReferencedDownward() { return fIsReferencedDownward; }

	virtual uintptr_t					resolveWeak(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver,
													const ImageLoader** foundIn) { return 0; } 

										// triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast
	static void							printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo);
	static void							printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo);

										// used with DYLD_IMAGE_SUFFIX
	static void							addSuffix(const char* path, const char* suffix, char* result);
	
	static uint32_t						hash(const char*);
	
	static const uint8_t*				trieWalk(const uint8_t* start, const uint8_t* end, const char* stringToFind);

										// used instead of directly deleting image
	static void							deleteImage(ImageLoader*);

	static bool							haveInterposingTuples() { return !fgInterposingTuples.empty(); }
	static void							clearInterposingTuples() { fgInterposingTuples.clear(); }

	static void							applyInterposingToDyldCache(const LinkContext& context);

			bool						dependsOn(ImageLoader* image);
			
 			void						setPath(const char* path);
			void						setPaths(const char* path, const char* realPath);
			void						setPathUnowned(const char* path);
						
			void						clearDepth() { fDepth = 0; }
			int							getDepth() { return fDepth; }
			
			void						setBeingRemoved() { fBeingRemoved = true; }
			bool						isBeingRemoved() const { return fBeingRemoved; }
			
			void						markNotUsed() { fMarkedInUse = false; }
			void						markedUsedRecursive(const std::vector<DynamicReference>&);
			bool						isMarkedInUse() const	{ return fMarkedInUse; }
		
			void						setAddFuncNotified() { fAddFuncNotified = true; }
			bool						addFuncNotified() const { return fAddFuncNotified; }
	
	struct InterposeTuple { 
		uintptr_t		replacement; 
		ImageLoader*	neverImage;			// don't apply replacement to this image
		ImageLoader*	onlyImage;			// only apply replacement to this image
		uintptr_t		replacee; 
	};

	static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end);
	static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end);

	void			vmAccountingSetSuspended(const LinkContext& context, bool suspend);

protected:			
	// abstract base class so all constructors protected
					ImageLoader(const char* path, unsigned int libCount); 
					ImageLoader(const ImageLoader&);
	void			operator=(const ImageLoader&);
	void			operator delete(void* image) throw() { ::free(image); }
	

	struct LibraryInfo {
		uint32_t		checksum;
		uint32_t		minVersion;
		uint32_t		maxVersion;
	};

	struct DependentLibrary {
		ImageLoader*	image;
		uint32_t		required : 1,
						checksumMatches : 1,
						isReExported : 1,
						isSubFramework : 1;
	};
	
	struct DependentLibraryInfo {
		const char*			name;
		LibraryInfo			info;
		bool				required;
		bool				reExported;
		bool				upward;
	};


	typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[], const ProgramVars* vars);
	typedef void (*Terminator)(void);
	


	unsigned int			libraryCount() const { return fLibraryCount; }
	virtual ImageLoader*	libImage(unsigned int) const = 0;
	virtual bool			libReExported(unsigned int) const = 0;
	virtual bool			libIsUpward(unsigned int) const = 0;
	virtual void			setLibImage(unsigned int, ImageLoader*, bool, bool) = 0;

						// To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized.
						// These methods do the above, exactly once, and it the right order
	virtual void		recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath);
	virtual unsigned 	recursiveUpdateDepth(unsigned int maxDepth);
	virtual void		recursiveRebase(const LinkContext& context);
	virtual void		recursiveApplyInterposing(const LinkContext& context);
	virtual void		recursiveGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs);
	virtual void		recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
												ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&);

								// fill in information about dependent libraries (array length is fLibraryCount)
	virtual void				doGetDependentLibraries(DependentLibraryInfo libs[]) = 0;
	
								// called on images that are libraries, returns info about itself
	virtual LibraryInfo			doGetLibraryInfo(const LibraryInfo& requestorInfo) = 0;
	
								// do any fix ups in this image that depend only on the load address of the image
	virtual void				doRebase(const LinkContext& context) = 0;
	
								// do any symbolic fix ups in this image
	virtual void				doBind(const LinkContext& context, bool forceLazysBound) = 0;
	
								// called later via API to force all lazy pointer to be bound
	virtual void				doBindJustLazies(const LinkContext& context) = 0;
	
								// if image has any dtrace DOF sections, append them to list to be registered
	virtual void				doGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs) = 0;
	
								// do interpose
	virtual void				doInterpose(const LinkContext& context) = 0;

								// run any initialization routines in this image
	virtual bool				doInitialization(const LinkContext& context) = 0;
	
								// return if this image has termination routines
	virtual bool				needsTermination() = 0;
	
								// support for runtimes in which segments don't have to maintain their relative positions
	virtual bool				segmentsMustSlideTogether() const = 0;	
	
								// built with PIC code and can load at any address
	virtual bool				segmentsCanSlide() const = 0;		

								// set how much all segments slide
	virtual void				setSlide(intptr_t slide) = 0;		

								// returns if all dependent libraries checksum's were as expected and none slide
			bool				allDependentLibrariesAsWhenPreBound() const;

								// in mach-o a child tells it parent to re-export, instead of the other way around...
	virtual	bool				isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const = 0;

								// in mach-o a parent library knows name of sub libraries it re-exports..
	virtual	bool				hasSubLibrary(const LinkContext& context, const ImageLoader* child) const  = 0;

	virtual bool				weakSymbolsBound(unsigned index) { return fWeakSymbolsBound; }
	virtual void				setWeakSymbolsBound(unsigned index) { fWeakSymbolsBound = true; }

								// set fState to dyld_image_state_memory_mapped
	void						setMapped(const LinkContext& context);
		
	void						setFileInfo(dev_t device, ino_t inode, time_t modDate);

	void						setDepth(uint16_t depth) { fDepth = depth; }

	static uintptr_t			interposedAddress(const LinkContext& context, uintptr_t address, const ImageLoader* notInImage, const ImageLoader* onlyInImage=NULL);
	
	static uintptr_t			fgNextPIEDylibAddress;
	static uint32_t				fgImagesWithUsedPrebinding;
	static uint32_t				fgImagesUsedFromSharedCache;
	static uint32_t				fgImagesHasWeakDefinitions;
	static uint32_t				fgImagesRequiringCoalescing;
	static uint32_t				fgTotalRebaseFixups;
	static uint32_t				fgTotalBindFixups;
	static uint32_t				fgTotalBindSymbolsResolved;
	static uint32_t				fgTotalBindImageSearches;
	static uint32_t				fgTotalLazyBindFixups;
	static uint32_t				fgTotalPossibleLazyBindFixups;
	static uint32_t				fgTotalSegmentsMapped;
	static uint32_t				fgSymbolTrieSearchs;
	static uint64_t				fgTotalBytesMapped;
	static uint64_t				fgTotalBytesPreFetched;
	static uint64_t				fgTotalLoadLibrariesTime;
public:
	static uint64_t				fgTotalObjCSetupTime;
	static uint64_t				fgTotalDebuggerPausedTime;
	static uint64_t				fgTotalRebindCacheTime;
	static uint64_t				fgTotalRebaseTime;
	static uint64_t				fgTotalBindTime;
	static uint64_t				fgTotalWeakBindTime;
	static uint64_t				fgTotalDOF;
	static uint64_t				fgTotalInitTime;

protected:
	static std::vector<InterposeTuple>	fgInterposingTuples;
	
	const char*					fPath;
	const char*					fRealPath;
	dev_t						fDevice;
	ino_t						fInode;
	time_t						fLastModified;
	uint32_t					fPathHash;
	uint32_t					fDlopenReferenceCount;	// count of how many dlopens have been done on this image

	struct recursive_lock {
						recursive_lock(mach_port_t t) : thread(t), count(0) {}
		mach_port_t		thread;
		int				count;
	};
	void						recursiveSpinLock(recursive_lock&);
	void						recursiveSpinUnLock();

private:
	const ImageLoader::Symbol*	findExportedSymbolInDependentImagesExcept(const char* name, const ImageLoader** dsiStart, 
										const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const;

	void						processInitializers(const LinkContext& context, mach_port_t this_thread,
													InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& ups);


	recursive_lock*				fInitializerRecursiveLock;
	uint16_t					fDepth;
	uint16_t					fLoadOrder;
	uint32_t					fState : 8,
								fLibraryCount : 10,
								fAllLibraryChecksumsAndLoadAddressesMatch : 1,
								fLeaveMapped : 1,		// when unloaded, leave image mapped in cause some other code may have pointers into it
								fNeverUnload : 1,		// image was statically loaded by main executable
								fHideSymbols : 1,		// ignore this image's exported symbols when linking other images
								fMatchByInstallName : 1,// look at image's install-path not its load path
								fInterposed : 1,
								fRegisteredDOF : 1,
								fAllLazyPointersBound : 1,
								fMarkedInUse : 1,
								fBeingRemoved : 1,
								fAddFuncNotified : 1,
								fPathOwnedByImage : 1,
								fIsReferencedDownward : 1,
								fWeakSymbolsBound : 1;

	static uint16_t				fgLoadOrdinal;
};


VECTOR_NEVER_DESTRUCTED_EXTERN(ImageLoader::InterposeTuple);


#endif

如此之长的头文件,我们去掉方法和静态成员,整理以后,只有以下成员

    const char*					fPath;
	const char*					fRealPath;
	dev_t						fDevice;
	ino_t						fInode;
	time_t						fLastModified;
	uint32_t					fPathHash;
	uint32_t					fDlopenReferenceCount;	// count of how many dlopens have been done on this image
	recursive_lock*				fInitializerRecursiveLock;
	uint16_t					fDepth;
	uint16_t					fLoadOrder;
	uint32_t					fState : 8,
								fLibraryCount : 10,
								fAllLibraryChecksumsAndLoadAddressesMatch : 1,
								fLeaveMapped : 1,		// when unloaded, leave image mapped in cause some other code may have pointers into it
								fNeverUnload : 1,		// image was statically loaded by main executable
								fHideSymbols : 1,		// ignore this image's exported symbols when linking other images
								fMatchByInstallName : 1,// look at image's install-path not its load path
								fInterposed : 1,
								fRegisteredDOF : 1,
								fAllLazyPointersBound : 1,
								fMarkedInUse : 1,
								fBeingRemoved : 1,
								fAddFuncNotified : 1,
								fPathOwnedByImage : 1,
								fIsReferencedDownward : 1,
								fWeakSymbolsBound : 1;

根据我们上次dump的结果来看,然后是不够的,至少这里面根本没有我们需要的load address
那么,这是什么原因呢?
由于c++继承的特性,在c++里,类指针的第一个成员是虚表地址,往后依次是父类的成员(包括私有成员)和自己类成员。显然这里真正的类型应是它的某个子类。
结合我们上面的dump结果和apple的源码,一番尝试后,我们可以总结出它的结构如下

typedef struct {
    void*                       vTable;
    const char*					fPath;
	const char*					fRealPath;
	dev_t						fDevice;
	ino_t						fInode;
	time_t						fLastModified;
	uint32_t					fPathHash;
	uint32_t					fDlopenReferenceCount;	// count of how many dlopens have been done on this image
	void*				fInitializerRecursiveLock;
	uint16_t					fDepth;
	uint16_t					fLoadOrder;
	uint32_t					fState : 8,
								fLibraryCount : 10,
								fAllLibraryChecksumsAndLoadAddressesMatch : 1,
								fLeaveMapped : 1,		// when unloaded, leave image mapped in cause some other code may have pointers into it
								fNeverUnload : 1,		// image was statically loaded by main executable
								fHideSymbols : 1,		// ignore this image's exported symbols when linking other images
								fMatchByInstallName : 1,// look at image's install-path not its load path
								fInterposed : 1,
								fRegisteredDOF : 1,
								fAllLazyPointersBound : 1,
								fMarkedInUse : 1,
								fBeingRemoved : 1,
								fAddFuncNotified : 1,
								fPathOwnedByImage : 1,
								fIsReferencedDownward : 1,
								fWeakSymbolsBound : 1;
	uint64_t								fCoveredCodeLength;
	const uint8_t*							fMachOData;
	const uint8_t*							fLinkEditBase; // add any internal "offset" to this to get mapped address
	uintptr_t								fSlide;
	uint32_t								fEHFrameSectionOffset;
	uint32_t								fUnwindInfoSectionOffset;
	uint32_t								fDylibIDOffset;
	uint32_t								fSegmentsCount : 8,
											fIsSplitSeg : 1,
											fInSharedCache : 1,
#if TEXT_RELOC_SUPPORT
											fTextSegmentRebases : 1, 
											fTextSegmentBinds : 1, 
#endif
#if __i386__
											fReadOnlyImportSegment : 1,
#endif
											fHasSubLibraries : 1,
											fHasSubUmbrella : 1,
											fInUmbrella : 1,
											fHasDOFSections : 1,
											fHasDashInit : 1,
											fHasInitializers : 1,
											fHasTerminators : 1,
											fNotifyObjC : 1,
											fRetainForObjC : 1,
											fRegisteredAsRequiresCoalescing : 1, 	// <rdar://problem/7886402> Loading MH_DYLIB_STUB causing coalescable miscount
											fOverrideOfCacheImageNum : 12;

											
	static uint32_t					fgSymbolTableBinarySearchs;
} MachoImage;

由此,我们可以顺利的通过以下代码来获取到他基地址。

	MachoImage* image=(MachoImage*)dlopen("xx.dylib",RTLD_LAZY);
	NSLog(@"[FLWB]:xxHandle:%p",image->fLinkEditBase);

当然fMachOData也是可以的,但考虑到后面的注释

add any internal "offset" to this to get mapped address

我们还是优先使用fLinkEditBase
此外,我们也可以通过此头文件来获取其它未公开的成员。
到此为止,我们不但达到了我们获取基地址的需求,还能够额外的获取甚至关键的信息。

免责声明

本文所公开的技术细节仅做技术交流,任何权利责任人需自行承担其用途产生的后果。另外,转载本文需注明来源及作者信息。

鸣谢

排名不分先后

资料文献

  1. https://stackoverflow.com/questions/19451791/get-loaded-address-of-a-elf-binary-dlopen-is-not-working-as-expected
  2. 从dlsym()源码看android 动态链接过程
  3. dyld source
@since92x
Copy link

1

@shagfu
Copy link

shagfu commented Jul 2, 2019

某些系统下发现获取出来的不对,fCoveredCodeLength 是 fMachOData的地址,这是为什么

@caihongxiaoyang
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants