1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/logic/MemoryUtils.cpp Fri May 14 16:14:59 2010 -0700
1.3 @@ -0,0 +1,554 @@
1.4 +/**
1.5 + * vim: set ts=4 sw=4 tw=99 noet :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + */
1.32 +
1.33 +#include "MemoryUtils.h"
1.34 +#ifdef PLATFORM_LINUX
1.35 +#include <fcntl.h>
1.36 +#include <link.h>
1.37 +#include <sys/mman.h>
1.38 +#endif
1.39 +#ifdef PLATFORM_APPLE
1.40 +#include <mach-o/dyld_images.h>
1.41 +#include <mach-o/loader.h>
1.42 +#include <mach-o/nlist.h>
1.43 +#endif
1.44 +
1.45 +MemoryUtils g_MemUtils;
1.46 +
1.47 +MemoryUtils::MemoryUtils()
1.48 +{
1.49 +#ifdef PLATFORM_APPLE
1.50 +
1.51 + /* Get pointer to struct that describes all loaded mach-o images in process */
1.52 + struct nlist list[2];
1.53 + memset(list, 0, sizeof(list));
1.54 + list[0].n_un.n_name = (char *)"_dyld_all_image_infos";
1.55 + nlist("/usr/lib/dyld", list);
1.56 + m_ImageList = (struct dyld_all_image_infos *)list[0].n_value;
1.57 +
1.58 +#endif
1.59 +}
1.60 +
1.61 +MemoryUtils::~MemoryUtils()
1.62 +{
1.63 +#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
1.64 + for (size_t i = 0; i < m_SymTables.size(); i++)
1.65 + {
1.66 + delete m_SymTables[i];
1.67 + }
1.68 + m_SymTables.clear();
1.69 +#endif
1.70 +}
1.71 +
1.72 +void MemoryUtils::OnSourceModAllInitialized()
1.73 +{
1.74 + sharesys->AddInterface(NULL, this);
1.75 +}
1.76 +
1.77 +void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t len)
1.78 +{
1.79 + DynLibInfo lib;
1.80 + bool found;
1.81 + char *ptr, *end;
1.82 +
1.83 + memset(&lib, 0, sizeof(DynLibInfo));
1.84 +
1.85 + if (!GetLibraryInfo(libPtr, lib))
1.86 + {
1.87 + return NULL;
1.88 + }
1.89 +
1.90 + ptr = reinterpret_cast<char *>(lib.baseAddress);
1.91 + end = ptr + lib.memorySize;
1.92 +
1.93 + while (ptr < end)
1.94 + {
1.95 + found = true;
1.96 + for (register size_t i = 0; i < len; i++)
1.97 + {
1.98 + if (pattern[i] != '\x2A' && pattern[i] != ptr[i])
1.99 + {
1.100 + found = false;
1.101 + break;
1.102 + }
1.103 + }
1.104 +
1.105 + if (found)
1.106 + return ptr;
1.107 +
1.108 + ptr++;
1.109 + }
1.110 +
1.111 + return NULL;
1.112 +}
1.113 +
1.114 +void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
1.115 +{
1.116 +#ifdef PLATFORM_WINDOWS
1.117 +
1.118 + return GetProcAddress((HMODULE)handle, symbol);
1.119 +
1.120 +#elif defined PLATFORM_LINUX
1.121 +
1.122 + struct link_map *dlmap;
1.123 + struct stat dlstat;
1.124 + int dlfile;
1.125 + uintptr_t map_base;
1.126 + Elf32_Ehdr *file_hdr;
1.127 + Elf32_Shdr *sections, *shstrtab_hdr, *symtab_hdr, *strtab_hdr;
1.128 + Elf32_Sym *symtab;
1.129 + const char *shstrtab, *strtab;
1.130 + uint16_t section_count;
1.131 + uint32_t symbol_count;
1.132 + LibSymbolTable *libtable;
1.133 + SymbolTable *table;
1.134 + Symbol *symbol_entry;
1.135 +
1.136 + dlmap = (struct link_map *)handle;
1.137 + symtab_hdr = NULL;
1.138 + strtab_hdr = NULL;
1.139 + table = NULL;
1.140 +
1.141 + /* See if we already have a symbol table for this library */
1.142 + for (size_t i = 0; i < m_SymTables.size(); i++)
1.143 + {
1.144 + libtable = m_SymTables[i];
1.145 + if (libtable->lib_base == dlmap->l_addr)
1.146 + {
1.147 + table = &libtable->table;
1.148 + break;
1.149 + }
1.150 + }
1.151 +
1.152 + /* If we don't have a symbol table for this library, then create one */
1.153 + if (table == NULL)
1.154 + {
1.155 + libtable = new LibSymbolTable();
1.156 + libtable->table.Initialize();
1.157 + libtable->lib_base = dlmap->l_addr;
1.158 + libtable->last_pos = 0;
1.159 + table = &libtable->table;
1.160 + m_SymTables.push_back(libtable);
1.161 + }
1.162 +
1.163 + /* See if the symbol is already cached in our table */
1.164 + symbol_entry = table->FindSymbol(symbol, strlen(symbol));
1.165 + if (symbol_entry != NULL)
1.166 + {
1.167 + return symbol_entry->address;
1.168 + }
1.169 +
1.170 + /* If symbol isn't in our table, then we have open the actual library */
1.171 + dlfile = open(dlmap->l_name, O_RDONLY);
1.172 + if (dlfile == -1 || fstat(dlfile, &dlstat) == -1)
1.173 + {
1.174 + close(dlfile);
1.175 + return NULL;
1.176 + }
1.177 +
1.178 + /* Map library file into memory */
1.179 + file_hdr = (Elf32_Ehdr *)mmap(NULL, dlstat.st_size, PROT_READ, MAP_PRIVATE, dlfile, 0);
1.180 + map_base = (uintptr_t)file_hdr;
1.181 + if (file_hdr == MAP_FAILED)
1.182 + {
1.183 + close(dlfile);
1.184 + return NULL;
1.185 + }
1.186 + close(dlfile);
1.187 +
1.188 + if (file_hdr->e_shoff == 0 || file_hdr->e_shstrndx == SHN_UNDEF)
1.189 + {
1.190 + munmap(file_hdr, dlstat.st_size);
1.191 + return NULL;
1.192 + }
1.193 +
1.194 + sections = (Elf32_Shdr *)(map_base + file_hdr->e_shoff);
1.195 + section_count = file_hdr->e_shnum;
1.196 + /* Get ELF section header string table */
1.197 + shstrtab_hdr = §ions[file_hdr->e_shstrndx];
1.198 + shstrtab = (const char *)(map_base + shstrtab_hdr->sh_offset);
1.199 +
1.200 + /* Iterate sections while looking for ELF symbol table and string table */
1.201 + for (uint16_t i = 0; i < section_count; i++)
1.202 + {
1.203 + Elf32_Shdr &hdr = sections[i];
1.204 + const char *section_name = shstrtab + hdr.sh_name;
1.205 +
1.206 + if (strcmp(section_name, ".symtab") == 0)
1.207 + {
1.208 + symtab_hdr = &hdr;
1.209 + }
1.210 + else if (strcmp(section_name, ".strtab") == 0)
1.211 + {
1.212 + strtab_hdr = &hdr;
1.213 + }
1.214 + }
1.215 +
1.216 + /* Uh oh, we don't have a symbol table or a string table */
1.217 + if (symtab_hdr == NULL || strtab_hdr == NULL)
1.218 + {
1.219 + munmap(file_hdr, dlstat.st_size);
1.220 + return NULL;
1.221 + }
1.222 +
1.223 + symtab = (Elf32_Sym *)(map_base + symtab_hdr->sh_offset);
1.224 + strtab = (const char *)(map_base + strtab_hdr->sh_offset);
1.225 + symbol_count = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
1.226 +
1.227 + /* Iterate symbol table starting from the position we were at last time */
1.228 + for (uint32_t i = libtable->last_pos; i < symbol_count; i++)
1.229 + {
1.230 + Elf32_Sym &sym = symtab[i];
1.231 + unsigned char sym_type = ELF32_ST_TYPE(sym.st_info);
1.232 + const char *sym_name = strtab + sym.st_name;
1.233 + Symbol *cur_sym;
1.234 +
1.235 + /* Skip symbols that are undefined or do not refer to functions or objects */
1.236 + if (sym.st_shndx == SHN_UNDEF || (sym_type != STT_FUNC && sym_type != STT_OBJECT))
1.237 + {
1.238 + continue;
1.239 + }
1.240 +
1.241 + /* Caching symbols as we go along */
1.242 + cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlmap->l_addr + sym.st_value));
1.243 + if (strcmp(symbol, sym_name) == 0)
1.244 + {
1.245 + symbol_entry = cur_sym;
1.246 + libtable->last_pos = ++i;
1.247 + break;
1.248 + }
1.249 + }
1.250 +
1.251 + munmap(file_hdr, dlstat.st_size);
1.252 + return symbol_entry ? symbol_entry->address : NULL;
1.253 +
1.254 +#elif defined PLATFORM_APPLE
1.255 +
1.256 + uintptr_t dlbase;
1.257 + uint32_t image_count;
1.258 + struct mach_header *file_hdr;
1.259 + struct load_command *loadcmds;
1.260 + struct symtab_command *symtab_hdr;
1.261 + struct nlist *symtab;
1.262 + const char *strtab;
1.263 + uint32_t loadcmd_count;
1.264 + uint32_t symbol_count;
1.265 + LibSymbolTable *libtable;
1.266 + SymbolTable *table;
1.267 + Symbol *symbol_entry;
1.268 +
1.269 + dlbase = 0;
1.270 + image_count = m_ImageList->infoArrayCount;
1.271 + symtab_hdr = NULL;
1.272 +
1.273 + /* Loop through mach-o images in process.
1.274 + * We can skip index 0 since that is just the executable.
1.275 + */
1.276 + for (uint32_t i = 1; i < image_count; i++)
1.277 + {
1.278 + const struct dyld_image_info &info = m_ImageList->infoArray[i];
1.279 +
1.280 + /* "Load" each one until we get a matching handle */
1.281 + void *h = dlopen(info.imageFilePath, RTLD_NOLOAD);
1.282 + if (h == handle)
1.283 + {
1.284 + dlbase = (uintptr_t)info.imageLoadAddress;
1.285 + dlclose(h);
1.286 + break;
1.287 + }
1.288 +
1.289 + dlclose(h);
1.290 + }
1.291 +
1.292 + if (!dlbase)
1.293 + {
1.294 + /* Uh oh, we couldn't find a matching handle */
1.295 + return NULL;
1.296 + }
1.297 +
1.298 + /* See if we already have a symbol table for this library */
1.299 + for (size_t i = 0; i < m_SymTables.size(); i++)
1.300 + {
1.301 + libtable = m_SymTables[i];
1.302 + if (libtable->lib_base == dlbase)
1.303 + {
1.304 + table = &libtable->table;
1.305 + break;
1.306 + }
1.307 + }
1.308 +
1.309 + /* If we don't have a symbol table for this library, then create one */
1.310 + if (table == NULL)
1.311 + {
1.312 + libtable = new LibSymbolTable();
1.313 + libtable->table.Initialize();
1.314 + libtable->lib_base = dlbase;
1.315 + libtable->last_pos = 0;
1.316 + table = &libtable->table;
1.317 + m_SymTables.push_back(libtable);
1.318 + }
1.319 +
1.320 + /* See if the symbol is already cached in our table */
1.321 + symbol_entry = table->FindSymbol(symbol, strlen(symbol));
1.322 + if (symbol_entry != NULL)
1.323 + {
1.324 + return symbol_entry->address;
1.325 + }
1.326 +
1.327 + /* If symbol isn't in our table, then we have to locate it in memory */
1.328 +
1.329 + file_hdr = (struct mach_header *)dlbase;
1.330 + loadcmds = (struct load_command *)(dlbase + sizeof(struct mach_header));
1.331 + loadcmd_count = file_hdr->ncmds;
1.332 +
1.333 + /* Loop through load commands until we find the one for the symbol table */
1.334 + for (uint32_t i = 0; i < loadcmd_count; i++)
1.335 + {
1.336 + if (loadcmds->cmd == LC_SYMTAB)
1.337 + {
1.338 + symtab_hdr = (struct symtab_command *)loadcmds;
1.339 + break;
1.340 + }
1.341 +
1.342 + /* Load commands are not of a fixed size which is why we add the size */
1.343 + loadcmds = (struct load_command *)((uintptr_t)loadcmds + loadcmds->cmdsize);
1.344 + }
1.345 +
1.346 + if (!symtab_hdr || !symtab_hdr->symoff || !symtab_hdr->stroff)
1.347 + {
1.348 + /* Uh oh, no symbol table */
1.349 + return NULL;
1.350 + }
1.351 +
1.352 + symtab = (struct nlist *)(dlbase + symtab_hdr->symoff);
1.353 + strtab = (const char *)(dlbase + symtab_hdr->stroff);
1.354 + symbol_count = symtab_hdr->nsyms;
1.355 +
1.356 + /* Iterate symbol table starting from the position we were at last time */
1.357 + for (uint32_t i = libtable->last_pos; i < symbol_count; i++)
1.358 + {
1.359 + struct nlist &sym = symtab[i];
1.360 + /* Ignore the prepended underscore on all symbols, so +1 here */
1.361 + const char *sym_name = strtab + sym.n_un.n_strx + 1;
1.362 + Symbol *cur_sym;
1.363 +
1.364 + /* Skip symbols that are undefined */
1.365 + if (sym.n_sect == NO_SECT)
1.366 + {
1.367 + continue;
1.368 + }
1.369 +
1.370 + /* Caching symbols as we go along */
1.371 + cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlbase + sym.n_value));
1.372 + if (strcmp(symbol, sym_name) == 0)
1.373 + {
1.374 + symbol_entry = cur_sym;
1.375 + libtable->last_pos = ++i;
1.376 + break;
1.377 + }
1.378 + }
1.379 +
1.380 + return symbol_entry ? symbol_entry->address : NULL;
1.381 +
1.382 +#endif
1.383 +}
1.384 +
1.385 +bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
1.386 +{
1.387 + uintptr_t baseAddr;
1.388 +
1.389 + if (libPtr == NULL)
1.390 + {
1.391 + return false;
1.392 + }
1.393 +
1.394 +#ifdef PLATFORM_WINDOWS
1.395 +
1.396 + MEMORY_BASIC_INFORMATION info;
1.397 + IMAGE_DOS_HEADER *dos;
1.398 + IMAGE_NT_HEADERS *pe;
1.399 + IMAGE_FILE_HEADER *file;
1.400 + IMAGE_OPTIONAL_HEADER *opt;
1.401 +
1.402 + if (!VirtualQuery(libPtr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
1.403 + {
1.404 + return false;
1.405 + }
1.406 +
1.407 + baseAddr = reinterpret_cast<uintptr_t>(info.AllocationBase);
1.408 +
1.409 + /* All this is for our insane sanity checks :o */
1.410 + dos = reinterpret_cast<IMAGE_DOS_HEADER *>(baseAddr);
1.411 + pe = reinterpret_cast<IMAGE_NT_HEADERS *>(baseAddr + dos->e_lfanew);
1.412 + file = &pe->FileHeader;
1.413 + opt = &pe->OptionalHeader;
1.414 +
1.415 + /* Check PE magic and signature */
1.416 + if (dos->e_magic != IMAGE_DOS_SIGNATURE || pe->Signature != IMAGE_NT_SIGNATURE || opt->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
1.417 + {
1.418 + return false;
1.419 + }
1.420 +
1.421 + /* Check architecture, which is 32-bit/x86 right now
1.422 + * Should change this for 64-bit if Valve gets their act together
1.423 + */
1.424 + if (file->Machine != IMAGE_FILE_MACHINE_I386)
1.425 + {
1.426 + return false;
1.427 + }
1.428 +
1.429 + /* For our purposes, this must be a dynamic library */
1.430 + if ((file->Characteristics & IMAGE_FILE_DLL) == 0)
1.431 + {
1.432 + return false;
1.433 + }
1.434 +
1.435 + /* Finally, we can do this */
1.436 + lib.memorySize = opt->SizeOfImage;
1.437 +
1.438 +#elif defined PLATFORM_LINUX
1.439 +
1.440 + Dl_info info;
1.441 + Elf32_Ehdr *file;
1.442 + Elf32_Phdr *phdr;
1.443 + uint16_t phdrCount;
1.444 +
1.445 + if (!dladdr(libPtr, &info))
1.446 + {
1.447 + return false;
1.448 + }
1.449 +
1.450 + if (!info.dli_fbase || !info.dli_fname)
1.451 + {
1.452 + return false;
1.453 + }
1.454 +
1.455 + /* This is for our insane sanity checks :o */
1.456 + baseAddr = reinterpret_cast<uintptr_t>(info.dli_fbase);
1.457 + file = reinterpret_cast<Elf32_Ehdr *>(baseAddr);
1.458 +
1.459 + /* Check ELF magic */
1.460 + if (memcmp(ELFMAG, file->e_ident, SELFMAG) != 0)
1.461 + {
1.462 + return false;
1.463 + }
1.464 +
1.465 + /* Check ELF version */
1.466 + if (file->e_ident[EI_VERSION] != EV_CURRENT)
1.467 + {
1.468 + return false;
1.469 + }
1.470 +
1.471 + /* Check ELF architecture, which is 32-bit/x86 right now
1.472 + * Should change this for 64-bit if Valve gets their act together
1.473 + */
1.474 + if (file->e_ident[EI_CLASS] != ELFCLASS32 || file->e_machine != EM_386 || file->e_ident[EI_DATA] != ELFDATA2LSB)
1.475 + {
1.476 + return false;
1.477 + }
1.478 +
1.479 + /* For our purposes, this must be a dynamic library/shared object */
1.480 + if (file->e_type != ET_DYN)
1.481 + {
1.482 + return false;
1.483 + }
1.484 +
1.485 + phdrCount = file->e_phnum;
1.486 + phdr = reinterpret_cast<Elf32_Phdr *>(baseAddr + file->e_phoff);
1.487 +
1.488 + /* Add up the memory sizes of segments marked as PT_LOAD as those are the only ones that should be in memory */
1.489 + for (uint16_t i = 0; i < phdrCount; i++)
1.490 + {
1.491 + Elf32_Phdr &hdr = phdr[i];
1.492 +
1.493 + if (hdr.p_type == PT_LOAD)
1.494 + {
1.495 + lib.memorySize += hdr.p_memsz;
1.496 + }
1.497 + }
1.498 +
1.499 +#elif defined PLATFORM_APPLE
1.500 +
1.501 + Dl_info info;
1.502 + struct mach_header *file;
1.503 + struct segment_command *seg;
1.504 + uint32_t cmd_count;
1.505 +
1.506 + if (!dladdr(libPtr, &info))
1.507 + {
1.508 + return false;
1.509 + }
1.510 +
1.511 + if (!info.dli_fbase || !info.dli_fname)
1.512 + {
1.513 + return false;
1.514 + }
1.515 +
1.516 + /* This is for our insane sanity checks :o */
1.517 + baseAddr = (uintptr_t)info.dli_fbase;
1.518 + file = (struct mach_header *)baseAddr;
1.519 +
1.520 + /* Check Mach-O magic */
1.521 + if (file->magic != MH_MAGIC)
1.522 + {
1.523 + return false;
1.524 + }
1.525 +
1.526 + /* Check architecture (32-bit/x86) */
1.527 + if (file->cputype != CPU_TYPE_I386 || file->cpusubtype != CPU_SUBTYPE_I386_ALL)
1.528 + {
1.529 + return false;
1.530 + }
1.531 +
1.532 + /* For our purposes, this must be a dynamic library */
1.533 + if (file->filetype != MH_DYLIB)
1.534 + {
1.535 + return false;
1.536 + }
1.537 +
1.538 + cmd_count = file->ncmds;
1.539 + seg = (struct segment_command *)(baseAddr + sizeof(struct mach_header));
1.540 +
1.541 + /* Add up memory sizes of mapped segments */
1.542 + for (uint32_t i = 0; i < cmd_count; i++)
1.543 + {
1.544 + if (seg->cmd == LC_SEGMENT)
1.545 + {
1.546 + lib.memorySize += seg->vmsize;
1.547 + }
1.548 +
1.549 + seg = (struct segment_command *)((uintptr_t)seg + seg->cmdsize);
1.550 + }
1.551 +
1.552 +#endif
1.553 +
1.554 + lib.baseAddress = reinterpret_cast<void *>(baseAddr);
1.555 +
1.556 + return true;
1.557 +}