diff --git a/core/MemoryUtils.cpp b/core/MemoryUtils.cpp index 9cacaf359..89471c014 100644 --- a/core/MemoryUtils.cpp +++ b/core/MemoryUtils.cpp @@ -2,7 +2,7 @@ * vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= * SourceMod - * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. + * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -34,30 +34,31 @@ #include #include #endif +#ifdef PLATFORM_APPLE +#include +#include +#include +#endif MemoryUtils g_MemUtils; -#if 0 MemoryUtils::MemoryUtils() { -#ifdef PLATFORM_WINDOWS +#ifdef PLATFORM_APPLE - SYSTEM_INFO info; - GetSystemInfo(&info); - - m_PageSize = info.dwPageSize; - -#elif defined PLATFORM_POSIX - - m_PageSize = sysconf(_SC_PAGE_SIZE); + /* Get pointer to struct that describes all loaded mach-o images in process */ + struct nlist list[2]; + memset(list, 0, sizeof(list)); + list[0].n_un.n_name = (char *)"_dyld_all_image_infos"; + nlist("/usr/lib/dyld", list); + m_ImageList = (struct dyld_all_image_infos *)list[0].n_value; #endif } -#endif MemoryUtils::~MemoryUtils() { -#ifdef PLATFORM_LINUX +#if defined PLATFORM_LINUX || defined PLATFORM_APPLE for (size_t i = 0; i < m_SymTables.size(); i++) { delete m_SymTables[i]; @@ -113,7 +114,7 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol) #ifdef PLATFORM_WINDOWS return GetProcAddress((HMODULE)handle, symbol); - + #elif defined PLATFORM_LINUX struct link_map *dlmap; @@ -248,12 +249,140 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol) munmap(file_hdr, dlstat.st_size); return symbol_entry ? symbol_entry->address : NULL; +#elif defined PLATFORM_APPLE + + uintptr_t dlbase; + uint32_t image_count; + struct mach_header *file_hdr; + struct load_command *loadcmds; + struct symtab_command *symtab_hdr; + struct nlist *symtab; + const char *strtab; + uint32_t loadcmd_count; + uint32_t symbol_count; + LibSymbolTable *libtable; + SymbolTable *table; + Symbol *symbol_entry; + + dlbase = 0; + image_count = m_ImageList->infoArrayCount; + symtab_hdr = NULL; + + /* Loop through mach-o images in process. + * We can skip index 0 since that is just the executable. + */ + for (uint32_t i = 1; i < image_count; i++) + { + const struct dyld_image_info &info = m_ImageList->infoArray[i]; + + /* "Load" each one until we get a matching handle */ + void *h = dlopen(info.imageFilePath, RTLD_NOLOAD); + if (h == handle) + { + dlbase = (uintptr_t)info.imageLoadAddress; + dlclose(h); + break; + } + + dlclose(h); + } + + if (!dlbase) + { + /* Uh oh, we couldn't find a matching handle */ + return NULL; + } + + /* See if we already have a symbol table for this library */ + for (size_t i = 0; i < m_SymTables.size(); i++) + { + libtable = m_SymTables[i]; + if (libtable->lib_base == dlbase) + { + table = &libtable->table; + break; + } + } + + /* If we don't have a symbol table for this library, then create one */ + if (table == NULL) + { + libtable = new LibSymbolTable(); + libtable->table.Initialize(); + libtable->lib_base = dlbase; + libtable->last_pos = 0; + table = &libtable->table; + m_SymTables.push_back(libtable); + } + + /* See if the symbol is already cached in our table */ + symbol_entry = table->FindSymbol(symbol, strlen(symbol)); + if (symbol_entry != NULL) + { + return symbol_entry->address; + } + + /* If symbol isn't in our table, then we have to locate it in memory */ + + file_hdr = (struct mach_header *)dlbase; + loadcmds = (struct load_command *)(dlbase + sizeof(struct mach_header)); + loadcmd_count = file_hdr->ncmds; + + /* Loop through load commands until we find the one for the symbol table */ + for (uint32_t i = 0; i < loadcmd_count; i++) + { + if (loadcmds->cmd == LC_SYMTAB) + { + symtab_hdr = (struct symtab_command *)loadcmds; + break; + } + + /* Load commands are not of a fixed size which is why we add the size */ + loadcmds = (struct load_command *)((uintptr_t)loadcmds + loadcmds->cmdsize); + } + + if (!symtab_hdr || !symtab_hdr->symoff || !symtab_hdr->stroff) + { + /* Uh oh, no symbol table */ + return NULL; + } + + symtab = (struct nlist *)(dlbase + symtab_hdr->symoff); + strtab = (const char *)(dlbase + symtab_hdr->stroff); + symbol_count = symtab_hdr->nsyms; + + /* Iterate symbol table starting from the position we were at last time */ + for (uint32_t i = libtable->last_pos; i < symbol_count; i++) + { + struct nlist &sym = symtab[i]; + /* Ignore the prepended underscore on all symbols, so +1 here */ + const char *sym_name = strtab + sym.n_un.n_strx + 1; + Symbol *cur_sym; + + /* Skip symbols that are undefined */ + if (sym.n_sect == NO_SECT) + { + continue; + } + + /* Caching symbols as we go along */ + cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlbase + sym.n_value)); + if (strcmp(symbol, sym_name) == 0) + { + symbol_entry = cur_sym; + libtable->last_pos = ++i; + break; + } + } + + return symbol_entry ? symbol_entry->address : NULL; + #endif } bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib) { - unsigned long baseAddr; + uintptr_t baseAddr; if (libPtr == NULL) { @@ -273,7 +402,7 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib) return false; } - baseAddr = reinterpret_cast(info.AllocationBase); + baseAddr = reinterpret_cast(info.AllocationBase); /* All this is for our insane sanity checks :o */ dos = reinterpret_cast(baseAddr); @@ -322,7 +451,7 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib) } /* This is for our insane sanity checks :o */ - baseAddr = reinterpret_cast(info.dli_fbase); + baseAddr = reinterpret_cast(info.dli_fbase); file = reinterpret_cast(baseAddr); /* Check ELF magic */ @@ -364,6 +493,59 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib) lib.memorySize += hdr.p_memsz; } } + +#elif defined PLATFORM_APPLE + + Dl_info info; + struct mach_header *file; + struct segment_command *seg; + uint32_t cmd_count; + + if (!dladdr(libPtr, &info)) + { + return false; + } + + if (!info.dli_fbase || !info.dli_fname) + { + return false; + } + + /* This is for our insane sanity checks :o */ + baseAddr = (uintptr_t)info.dli_fbase; + file = (struct mach_header *)baseAddr; + + /* Check Mach-O magic */ + if (file->magic != MH_MAGIC) + { + return false; + } + + /* Check architecture (32-bit/x86) */ + if (file->cputype != CPU_TYPE_I386 || file->cpusubtype != CPU_SUBTYPE_I386_ALL) + { + return false; + } + + /* For our purposes, this must be a dynamic library */ + if (file->filetype != MH_DYLIB) + { + return false; + } + + cmd_count = file->ncmds; + seg = (struct segment_command *)(baseAddr + sizeof(struct mach_header)); + + /* Add up memory sizes of mapped segments */ + for (uint32_t i = 0; i < cmd_count; i++) + { + if (seg->cmd == LC_SEGMENT) + { + lib.memorySize += seg->vmsize; + } + + seg = (struct segment_command *)((uintptr_t)seg + seg->cmdsize); + } #endif diff --git a/core/MemoryUtils.h b/core/MemoryUtils.h index 4b5d8467f..8615e904a 100644 --- a/core/MemoryUtils.h +++ b/core/MemoryUtils.h @@ -2,7 +2,7 @@ * vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= * SourceMod - * Copyright (C) 2004-2000 AlliedModders LLC. All rights reserved. + * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -32,7 +32,7 @@ #include #include "sm_globals.h" -#ifdef PLATFORM_LINUX +#if defined PLATFORM_LINUX || defined PLATFORM_APPLE #include #include "sm_symtable.h" @@ -47,13 +47,11 @@ struct DynLibInfo size_t memorySize; }; -#ifdef PLATFORM_LINUX -typedef uint32_t Elf32_Addr; - +#if defined PLATFORM_LINUX || defined PLATFORM_APPLE struct LibSymbolTable { SymbolTable table; - Elf32_Addr lib_base; + uintptr_t lib_base; uint32_t last_pos; }; #endif @@ -63,6 +61,7 @@ class MemoryUtils : public SMGlobalClass { public: + MemoryUtils(); ~MemoryUtils(); public: // SMGlobalClass void OnSourceModAllInitialized(); @@ -71,9 +70,12 @@ public: // IMemoryUtils void *ResolveSymbol(void *handle, const char *symbol); public: bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib); -#ifdef PLATFORM_LINUX +#if defined PLATFORM_LINUX || defined PLATFORM_APPLE private: CVector m_SymTables; +#ifdef PLATFORM_APPLE + struct dyld_all_image_infos *m_ImageList; +#endif #endif }; diff --git a/public/IMemoryUtils.h b/public/IMemoryUtils.h index 592bc7ed1..f0d4ee551 100644 --- a/public/IMemoryUtils.h +++ b/public/IMemoryUtils.h @@ -67,11 +67,14 @@ namespace SourceMod /** * @brief Retrieves a symbol pointer from a dynamic library. * - * Note: On Linux, this function is able to resolve symbols that are hidden via GCC's - * -fvisibility=hidden option. + * Note: On Linux and Mac OS X, this function is able to resolve symbols that are hidden + * via GCC's -fvisibility=hidden option. + * + * Note: On Mac OS X, the symbol name should be passed without the prepended underscore, + * as this is how dlsym() would expect it. * * @param handle Operating system specific handle that points to dynamic library. - * This comes from dlopen() on Linux or LoadLibrary() on Windows. + * This comes from dlopen() on Linux/OS X or LoadLibrary() on Windows. * @param symbol Symbol name. * @return Symbol pointer, or NULL if not found. */