diff -r 94e008ed3db9 -r 53dfd73380c3 core/logic/MemoryUtils.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/logic/MemoryUtils.cpp Fri May 14 16:14:59 2010 -0700
@@ -0,0 +1,554 @@
+/**
+ * vim: set ts=4 sw=4 tw=99 noet :
+ * =============================================================================
+ * SourceMod
+ * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ */
+
+#include "MemoryUtils.h"
+#ifdef PLATFORM_LINUX
+#include
+#include
+#include
+#endif
+#ifdef PLATFORM_APPLE
+#include
+#include
+#include
+#endif
+
+MemoryUtils g_MemUtils;
+
+MemoryUtils::MemoryUtils()
+{
+#ifdef PLATFORM_APPLE
+
+ /* 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
+}
+
+MemoryUtils::~MemoryUtils()
+{
+#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
+ for (size_t i = 0; i < m_SymTables.size(); i++)
+ {
+ delete m_SymTables[i];
+ }
+ m_SymTables.clear();
+#endif
+}
+
+void MemoryUtils::OnSourceModAllInitialized()
+{
+ sharesys->AddInterface(NULL, this);
+}
+
+void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t len)
+{
+ DynLibInfo lib;
+ bool found;
+ char *ptr, *end;
+
+ memset(&lib, 0, sizeof(DynLibInfo));
+
+ if (!GetLibraryInfo(libPtr, lib))
+ {
+ return NULL;
+ }
+
+ ptr = reinterpret_cast(lib.baseAddress);
+ end = ptr + lib.memorySize;
+
+ while (ptr < end)
+ {
+ found = true;
+ for (register size_t i = 0; i < len; i++)
+ {
+ if (pattern[i] != '\x2A' && pattern[i] != ptr[i])
+ {
+ found = false;
+ break;
+ }
+ }
+
+ if (found)
+ return ptr;
+
+ ptr++;
+ }
+
+ return NULL;
+}
+
+void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
+{
+#ifdef PLATFORM_WINDOWS
+
+ return GetProcAddress((HMODULE)handle, symbol);
+
+#elif defined PLATFORM_LINUX
+
+ struct link_map *dlmap;
+ struct stat dlstat;
+ int dlfile;
+ uintptr_t map_base;
+ Elf32_Ehdr *file_hdr;
+ Elf32_Shdr *sections, *shstrtab_hdr, *symtab_hdr, *strtab_hdr;
+ Elf32_Sym *symtab;
+ const char *shstrtab, *strtab;
+ uint16_t section_count;
+ uint32_t symbol_count;
+ LibSymbolTable *libtable;
+ SymbolTable *table;
+ Symbol *symbol_entry;
+
+ dlmap = (struct link_map *)handle;
+ symtab_hdr = NULL;
+ strtab_hdr = NULL;
+ table = 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 == dlmap->l_addr)
+ {
+ 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 = dlmap->l_addr;
+ 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 open the actual library */
+ dlfile = open(dlmap->l_name, O_RDONLY);
+ if (dlfile == -1 || fstat(dlfile, &dlstat) == -1)
+ {
+ close(dlfile);
+ return NULL;
+ }
+
+ /* Map library file into memory */
+ file_hdr = (Elf32_Ehdr *)mmap(NULL, dlstat.st_size, PROT_READ, MAP_PRIVATE, dlfile, 0);
+ map_base = (uintptr_t)file_hdr;
+ if (file_hdr == MAP_FAILED)
+ {
+ close(dlfile);
+ return NULL;
+ }
+ close(dlfile);
+
+ if (file_hdr->e_shoff == 0 || file_hdr->e_shstrndx == SHN_UNDEF)
+ {
+ munmap(file_hdr, dlstat.st_size);
+ return NULL;
+ }
+
+ sections = (Elf32_Shdr *)(map_base + file_hdr->e_shoff);
+ section_count = file_hdr->e_shnum;
+ /* Get ELF section header string table */
+ shstrtab_hdr = §ions[file_hdr->e_shstrndx];
+ shstrtab = (const char *)(map_base + shstrtab_hdr->sh_offset);
+
+ /* Iterate sections while looking for ELF symbol table and string table */
+ for (uint16_t i = 0; i < section_count; i++)
+ {
+ Elf32_Shdr &hdr = sections[i];
+ const char *section_name = shstrtab + hdr.sh_name;
+
+ if (strcmp(section_name, ".symtab") == 0)
+ {
+ symtab_hdr = &hdr;
+ }
+ else if (strcmp(section_name, ".strtab") == 0)
+ {
+ strtab_hdr = &hdr;
+ }
+ }
+
+ /* Uh oh, we don't have a symbol table or a string table */
+ if (symtab_hdr == NULL || strtab_hdr == NULL)
+ {
+ munmap(file_hdr, dlstat.st_size);
+ return NULL;
+ }
+
+ symtab = (Elf32_Sym *)(map_base + symtab_hdr->sh_offset);
+ strtab = (const char *)(map_base + strtab_hdr->sh_offset);
+ symbol_count = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
+
+ /* Iterate symbol table starting from the position we were at last time */
+ for (uint32_t i = libtable->last_pos; i < symbol_count; i++)
+ {
+ Elf32_Sym &sym = symtab[i];
+ unsigned char sym_type = ELF32_ST_TYPE(sym.st_info);
+ const char *sym_name = strtab + sym.st_name;
+ Symbol *cur_sym;
+
+ /* Skip symbols that are undefined or do not refer to functions or objects */
+ if (sym.st_shndx == SHN_UNDEF || (sym_type != STT_FUNC && sym_type != STT_OBJECT))
+ {
+ continue;
+ }
+
+ /* Caching symbols as we go along */
+ cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlmap->l_addr + sym.st_value));
+ if (strcmp(symbol, sym_name) == 0)
+ {
+ symbol_entry = cur_sym;
+ libtable->last_pos = ++i;
+ break;
+ }
+ }
+
+ 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)
+{
+ uintptr_t baseAddr;
+
+ if (libPtr == NULL)
+ {
+ return false;
+ }
+
+#ifdef PLATFORM_WINDOWS
+
+ MEMORY_BASIC_INFORMATION info;
+ IMAGE_DOS_HEADER *dos;
+ IMAGE_NT_HEADERS *pe;
+ IMAGE_FILE_HEADER *file;
+ IMAGE_OPTIONAL_HEADER *opt;
+
+ if (!VirtualQuery(libPtr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
+ {
+ return false;
+ }
+
+ baseAddr = reinterpret_cast(info.AllocationBase);
+
+ /* All this is for our insane sanity checks :o */
+ dos = reinterpret_cast(baseAddr);
+ pe = reinterpret_cast(baseAddr + dos->e_lfanew);
+ file = &pe->FileHeader;
+ opt = &pe->OptionalHeader;
+
+ /* Check PE magic and signature */
+ if (dos->e_magic != IMAGE_DOS_SIGNATURE || pe->Signature != IMAGE_NT_SIGNATURE || opt->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
+ {
+ return false;
+ }
+
+ /* Check architecture, which is 32-bit/x86 right now
+ * Should change this for 64-bit if Valve gets their act together
+ */
+ if (file->Machine != IMAGE_FILE_MACHINE_I386)
+ {
+ return false;
+ }
+
+ /* For our purposes, this must be a dynamic library */
+ if ((file->Characteristics & IMAGE_FILE_DLL) == 0)
+ {
+ return false;
+ }
+
+ /* Finally, we can do this */
+ lib.memorySize = opt->SizeOfImage;
+
+#elif defined PLATFORM_LINUX
+
+ Dl_info info;
+ Elf32_Ehdr *file;
+ Elf32_Phdr *phdr;
+ uint16_t phdrCount;
+
+ if (!dladdr(libPtr, &info))
+ {
+ return false;
+ }
+
+ if (!info.dli_fbase || !info.dli_fname)
+ {
+ return false;
+ }
+
+ /* This is for our insane sanity checks :o */
+ baseAddr = reinterpret_cast(info.dli_fbase);
+ file = reinterpret_cast(baseAddr);
+
+ /* Check ELF magic */
+ if (memcmp(ELFMAG, file->e_ident, SELFMAG) != 0)
+ {
+ return false;
+ }
+
+ /* Check ELF version */
+ if (file->e_ident[EI_VERSION] != EV_CURRENT)
+ {
+ return false;
+ }
+
+ /* Check ELF architecture, which is 32-bit/x86 right now
+ * Should change this for 64-bit if Valve gets their act together
+ */
+ if (file->e_ident[EI_CLASS] != ELFCLASS32 || file->e_machine != EM_386 || file->e_ident[EI_DATA] != ELFDATA2LSB)
+ {
+ return false;
+ }
+
+ /* For our purposes, this must be a dynamic library/shared object */
+ if (file->e_type != ET_DYN)
+ {
+ return false;
+ }
+
+ phdrCount = file->e_phnum;
+ phdr = reinterpret_cast(baseAddr + file->e_phoff);
+
+ /* Add up the memory sizes of segments marked as PT_LOAD as those are the only ones that should be in memory */
+ for (uint16_t i = 0; i < phdrCount; i++)
+ {
+ Elf32_Phdr &hdr = phdr[i];
+
+ if (hdr.p_type == PT_LOAD)
+ {
+ 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
+
+ lib.baseAddress = reinterpret_cast(baseAddr);
+
+ return true;
+}