lazy_dylib_loader.c [plain text]
#include <stddef.h>
#include <string.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <stdio.h>
#ifndef LC_LAZY_LOAD_DYLIB
#define LC_LAZY_LOAD_DYLIB 0x20
#endif
#ifndef S_LAZY_DYLIB_SYMBOL_POINTERS
#define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10
#endif
#ifndef LC_LOAD_UPWARD_DYLIB
#define LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD)
#endif
#if __LP64__
#define LC_SEGMENT_COMMAND LC_SEGMENT_64
#define LC_ROUTINES_COMMAND LC_ROUTINES_64
typedef struct mach_header_64 macho_header;
typedef struct section_64 macho_section;
typedef struct nlist_64 macho_nlist;
typedef struct segment_command_64 macho_segment_command;
#else
#define LC_SEGMENT_COMMAND LC_SEGMENT
#define LC_ROUTINES_COMMAND LC_ROUTINES
typedef struct mach_header macho_header;
typedef struct section macho_section;
typedef struct nlist macho_nlist;
typedef struct segment_command macho_segment_command;
#endif
extern const macho_header __dso_handle;
int dyld_lazy_dylib_proxy() __attribute__((weak,visibility("hidden")));
int dyld_lazy_dylib_proxy()
{
return 0;
}
const char* dyld_lazy_dylib_path_fix(const char*) __attribute__((weak,visibility("hidden")));
const char* dyld_lazy_dylib_path_fix(const char* path)
{
return path;
}
static void* getHandleForLazyOrdinal(const macho_header* mh, void* handles[], uint8_t ordinal)
{
const uint32_t cmd_count = mh->ncmds;
const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header));
const struct load_command* cmd = cmds;
uint8_t loadDylibCount = 0;
uint8_t loadLazyDylibCount = 0;
uint32_t i;
for (i = 0; i < cmd_count; ++i) {
switch ( cmd->cmd ) {
case LC_LOAD_DYLIB:
case LC_LOAD_WEAK_DYLIB:
case LC_LOAD_UPWARD_DYLIB:
++loadDylibCount;
break;
case LC_LAZY_LOAD_DYLIB:
++loadDylibCount;
if ( loadDylibCount == ordinal ) {
if ( handles[loadLazyDylibCount] == NULL ) {
const struct dylib_command* dylib = (struct dylib_command*)cmd;
const char* path = (char*)cmd + dylib->dylib.name.offset;
const char* fixedPath = dyld_lazy_dylib_path_fix(path);
handles[loadLazyDylibCount] = dlopen(fixedPath, RTLD_LAZY);
}
return handles[loadLazyDylibCount];
}
++loadLazyDylibCount;
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
return NULL;
}
void* lazy_load_dylib(uintptr_t* lazyPointer) __attribute__((visibility("hidden")));
void* lazy_load_dylib(uintptr_t* lazyPointer)
{
static const macho_header* mh = NULL;
static const macho_nlist* symbolTable = NULL;
static const char* stringTable = NULL;
static const uint8_t* linkEditBase = NULL;
static const uint32_t* indirectSymbolTable = NULL;
static intptr_t slide = 0;
static void* minHandles[8];
static void** handles;
uint32_t i;
if ( mh == NULL ) {
const macho_header* tmh = &__dso_handle;
const uint32_t cmd_count = tmh->ncmds;
const struct load_command* const cmds = (struct load_command*)((char*)tmh + sizeof(macho_header));
const struct load_command* cmd = cmds;
for (i = 0; i < cmd_count; ++i) {
if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
const macho_segment_command* seg = (macho_segment_command*)cmd;
if ( strcmp(seg->segname,"__TEXT") == 0 )
slide = (uintptr_t)tmh - seg->vmaddr;
else if ( strcmp(seg->segname,"__LINKEDIT") == 0 )
linkEditBase = (uint8_t*)(seg->vmaddr + slide - seg->fileoff);
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
uint32_t lazyDylibCount = 0;
cmd = cmds;
for (i = 0; i < cmd_count; ++i) {
switch ( cmd->cmd ) {
case LC_SYMTAB:
{
const struct symtab_command* symtab = (struct symtab_command*)cmd;
stringTable = (const char*)&linkEditBase[symtab->stroff];
symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]);
}
break;
case LC_DYSYMTAB:
{
const struct dysymtab_command* dsymtab = (struct dysymtab_command*)cmd;
indirectSymbolTable = (uint32_t*)(&linkEditBase[dsymtab->indirectsymoff]);
}
break;
case LC_LAZY_LOAD_DYLIB:
++lazyDylibCount;
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
if ( lazyDylibCount < 8 )
handles = minHandles;
else
handles = calloc(lazyDylibCount, sizeof(void*));
mh = tmh;
}
void* result = &dyld_lazy_dylib_proxy;
const uint32_t cmd_count = mh->ncmds;
const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header));
const struct load_command* cmd = cmds;
for (i = 0; i < cmd_count; ++i) {
if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
const macho_segment_command* seg = (macho_segment_command*)cmd;
const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command));
const macho_section* const sectionsEnd = §ionsStart[seg->nsects];
const macho_section* sect;
for (sect=sectionsStart; sect < sectionsEnd; ++sect) {
const uint8_t type = sect->flags & SECTION_TYPE;
if ( type == S_LAZY_DYLIB_SYMBOL_POINTERS ) { const uint32_t pointerCount = sect->size / sizeof(uintptr_t);
uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + slide);
if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) {
const uint32_t indirectTableOffset = sect->reserved1;
const uint32_t lazyIndex = lazyPointer - symbolPointers;
uint32_t symbolIndex = indirectSymbolTable[indirectTableOffset + lazyIndex];
if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) {
const char* symbolName = &stringTable[symbolTable[symbolIndex].n_un.n_strx];
uint8_t ordinal = GET_LIBRARY_ORDINAL(symbolTable[symbolIndex].n_desc);
void* handle = getHandleForLazyOrdinal(mh, handles, ordinal);
if ( handle != NULL ) {
void* addr = dlsym(handle, &symbolName[1]);
if ( addr != NULL )
result = addr;
*lazyPointer = (uintptr_t)result;
return result;
}
}
}
}
}
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
*lazyPointer = (uintptr_t)result;
return result;
}