core/MemoryUtils.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Tue Mar 09 18:41:25 2010 -0500 (2010-03-09)
changeset 2932 77cd75e433b4
parent 2863 d740d9cdf53a
child 2992 affc67b319f4
permissions -rw-r--r--
Fixed inaccurate description for sm_unban (bug 4245, r=dvander)
     1 /**
     2  * vim: set ts=4 sw=4 tw=99 noet :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2009 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  */
    29 
    30 #include "MemoryUtils.h"
    31 #include "ShareSys.h"
    32 #ifdef PLATFORM_LINUX
    33 #include <fcntl.h>
    34 #include <link.h>
    35 #include <sys/mman.h>
    36 #endif
    37 
    38 MemoryUtils g_MemUtils;
    39 
    40 #if 0
    41 MemoryUtils::MemoryUtils()
    42 {
    43 #ifdef PLATFORM_WINDOWS
    44 
    45 	SYSTEM_INFO info;
    46 	GetSystemInfo(&info);
    47 
    48 	m_PageSize = info.dwPageSize;
    49 
    50 #elif defined PLATFORM_POSIX
    51 
    52 	m_PageSize = sysconf(_SC_PAGE_SIZE);
    53 
    54 #endif
    55 }
    56 #endif
    57 
    58 MemoryUtils::~MemoryUtils()
    59 {
    60 #ifdef PLATFORM_LINUX
    61 	for (size_t i = 0; i < m_SymTables.size(); i++)
    62 	{
    63 		delete m_SymTables[i];
    64 	}
    65 	m_SymTables.clear();
    66 #endif
    67 }
    68 
    69 void MemoryUtils::OnSourceModAllInitialized()
    70 {
    71 	g_ShareSys.AddInterface(NULL, this);
    72 }
    73 
    74 void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t len)
    75 {
    76 	DynLibInfo lib;
    77 	bool found;
    78 	char *ptr, *end;
    79 
    80 	memset(&lib, 0, sizeof(DynLibInfo));
    81 
    82 	if (!GetLibraryInfo(libPtr, lib))
    83 	{
    84 		return NULL;
    85 	}
    86 
    87 	ptr = reinterpret_cast<char *>(lib.baseAddress);
    88 	end = ptr + lib.memorySize;
    89 
    90 	while (ptr < end)
    91 	{
    92 		found = true;
    93 		for (register size_t i = 0; i < len; i++)
    94 		{
    95 			if (pattern[i] != '\x2A' && pattern[i] != ptr[i])
    96 			{
    97 				found = false;
    98 				break;
    99 			}
   100 		}
   101 
   102 		if (found)
   103 			return ptr;
   104 
   105 		ptr++;
   106 	}
   107 
   108 	return NULL;
   109 }
   110 
   111 void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
   112 {
   113 #ifdef PLATFORM_WINDOWS
   114 
   115 	return GetProcAddress((HMODULE)handle, symbol);
   116 
   117 #elif defined PLATFORM_LINUX
   118 
   119 	struct link_map *dlmap;
   120 	struct stat dlstat;
   121 	int dlfile;
   122 	uintptr_t map_base;
   123 	Elf32_Ehdr *file_hdr;
   124 	Elf32_Shdr *sections, *shstrtab_hdr, *symtab_hdr, *strtab_hdr;
   125 	Elf32_Sym *symtab;
   126 	const char *shstrtab, *strtab;
   127 	uint16_t section_count;
   128 	uint32_t symbol_count;
   129 	LibSymbolTable *libtable;
   130 	SymbolTable *table;
   131 	Symbol *symbol_entry;
   132 
   133 	dlmap = (struct link_map *)handle;
   134 	symtab_hdr = NULL;
   135 	strtab_hdr = NULL;
   136 	table = NULL;
   137 	
   138 	/* See if we already have a symbol table for this library */
   139 	for (size_t i = 0; i < m_SymTables.size(); i++)
   140 	{
   141 		libtable = m_SymTables[i];
   142 		if (libtable->lib_base == dlmap->l_addr)
   143 		{
   144 			table = &libtable->table;
   145 			break;
   146 		}
   147 	}
   148 
   149 	/* If we don't have a symbol table for this library, then create one */
   150 	if (table == NULL)
   151 	{
   152 		libtable = new LibSymbolTable();
   153 		libtable->table.Initialize();
   154 		libtable->lib_base = dlmap->l_addr;
   155 		libtable->last_pos = 0;
   156 		table = &libtable->table;
   157 		m_SymTables.push_back(libtable);
   158 	}
   159 
   160 	/* See if the symbol is already cached in our table */
   161 	symbol_entry = table->FindSymbol(symbol, strlen(symbol));
   162 	if (symbol_entry != NULL)
   163 	{
   164 		return symbol_entry->address;
   165 	}
   166 
   167 	/* If symbol isn't in our table, then we have open the actual library */
   168 	dlfile = open(dlmap->l_name, O_RDONLY);
   169 	if (dlfile == -1 || fstat(dlfile, &dlstat) == -1)
   170 	{
   171 		close(dlfile);
   172 		return NULL;
   173 	}
   174 
   175 	/* Map library file into memory */
   176 	file_hdr = (Elf32_Ehdr *)mmap(NULL, dlstat.st_size, PROT_READ, MAP_PRIVATE, dlfile, 0);
   177 	map_base = (uintptr_t)file_hdr;
   178 	if (file_hdr == MAP_FAILED)
   179 	{
   180 		close(dlfile);
   181 		return NULL;
   182 	}
   183 	close(dlfile);
   184 
   185 	if (file_hdr->e_shoff == 0 || file_hdr->e_shstrndx == SHN_UNDEF)
   186 	{
   187 		munmap(file_hdr, dlstat.st_size);
   188 		return NULL;
   189 	}
   190 
   191 	sections = (Elf32_Shdr *)(map_base + file_hdr->e_shoff);
   192 	section_count = file_hdr->e_shnum;
   193 	/* Get ELF section header string table */
   194 	shstrtab_hdr = &sections[file_hdr->e_shstrndx];
   195 	shstrtab = (const char *)(map_base + shstrtab_hdr->sh_offset);
   196 
   197 	/* Iterate sections while looking for ELF symbol table and string table */
   198 	for (uint16_t i = 0; i < section_count; i++)
   199 	{
   200 		Elf32_Shdr &hdr = sections[i];
   201 		const char *section_name = shstrtab + hdr.sh_name;
   202 
   203 		if (strcmp(section_name, ".symtab") == 0)
   204 		{
   205 			symtab_hdr = &hdr;
   206 		}
   207 		else if (strcmp(section_name, ".strtab") == 0)
   208 		{
   209 			strtab_hdr = &hdr;
   210 		}
   211 	}
   212 
   213 	/* Uh oh, we don't have a symbol table or a string table */
   214 	if (symtab_hdr == NULL || strtab_hdr == NULL)
   215 	{
   216 		munmap(file_hdr, dlstat.st_size);
   217 		return NULL;
   218 	}
   219 
   220 	symtab = (Elf32_Sym *)(map_base + symtab_hdr->sh_offset);
   221 	strtab = (const char *)(map_base + strtab_hdr->sh_offset);
   222 	symbol_count = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
   223 
   224 	/* Iterate symbol table starting from the position we were at last time */
   225 	for (uint32_t i = libtable->last_pos; i < symbol_count; i++)
   226 	{
   227 		Elf32_Sym &sym = symtab[i];
   228 		unsigned char sym_type = ELF32_ST_TYPE(sym.st_info);
   229 		const char *sym_name = strtab + sym.st_name;
   230 		Symbol *cur_sym;
   231 
   232 		/* Skip symbols that are undefined or do not refer to functions or objects */
   233 		if (sym.st_shndx == SHN_UNDEF || (sym_type != STT_FUNC && sym_type != STT_OBJECT))
   234 		{
   235 			continue;
   236 		}
   237 
   238 		/* Caching symbols as we go along */
   239 		cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlmap->l_addr + sym.st_value));
   240 		if (strcmp(symbol, sym_name) == 0)
   241 		{
   242 			symbol_entry = cur_sym;
   243 			libtable->last_pos = ++i;
   244 			break;
   245 		}
   246 	}
   247 
   248 	munmap(file_hdr, dlstat.st_size);
   249 	return symbol_entry ? symbol_entry->address : NULL;
   250 
   251 #endif
   252 }
   253 
   254 bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
   255 {
   256 	unsigned long baseAddr;
   257 
   258 	if (libPtr == NULL)
   259 	{
   260 		return false;
   261 	}
   262 
   263 #ifdef PLATFORM_WINDOWS
   264 
   265 	MEMORY_BASIC_INFORMATION info;
   266 	IMAGE_DOS_HEADER *dos;
   267 	IMAGE_NT_HEADERS *pe;
   268 	IMAGE_FILE_HEADER *file;
   269 	IMAGE_OPTIONAL_HEADER *opt;
   270 
   271 	if (!VirtualQuery(libPtr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
   272 	{
   273 		return false;
   274 	}
   275 
   276 	baseAddr = reinterpret_cast<unsigned long>(info.AllocationBase);
   277 
   278 	/* All this is for our insane sanity checks :o */
   279 	dos = reinterpret_cast<IMAGE_DOS_HEADER *>(baseAddr);
   280 	pe = reinterpret_cast<IMAGE_NT_HEADERS *>(baseAddr + dos->e_lfanew);
   281 	file = &pe->FileHeader;
   282 	opt = &pe->OptionalHeader;
   283 
   284 	/* Check PE magic and signature */
   285 	if (dos->e_magic != IMAGE_DOS_SIGNATURE || pe->Signature != IMAGE_NT_SIGNATURE || opt->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
   286 	{
   287 		return false;
   288 	}
   289 
   290 	/* Check architecture, which is 32-bit/x86 right now
   291 	 * Should change this for 64-bit if Valve gets their act together
   292 	 */
   293 	if (file->Machine != IMAGE_FILE_MACHINE_I386)
   294 	{
   295 		return false;
   296 	}
   297 
   298 	/* For our purposes, this must be a dynamic library */
   299 	if ((file->Characteristics & IMAGE_FILE_DLL) == 0)
   300 	{
   301 		return false;
   302 	}
   303 
   304 	/* Finally, we can do this */
   305 	lib.memorySize = opt->SizeOfImage;
   306 
   307 #elif defined PLATFORM_LINUX
   308 
   309 	Dl_info info;
   310 	Elf32_Ehdr *file;
   311 	Elf32_Phdr *phdr;
   312 	uint16_t phdrCount;
   313 
   314 	if (!dladdr(libPtr, &info))
   315 	{
   316 		return false;
   317 	}
   318 
   319 	if (!info.dli_fbase || !info.dli_fname)
   320 	{
   321 		return false;
   322 	}
   323 
   324 	/* This is for our insane sanity checks :o */
   325 	baseAddr = reinterpret_cast<unsigned long>(info.dli_fbase);
   326 	file = reinterpret_cast<Elf32_Ehdr *>(baseAddr);
   327 
   328 	/* Check ELF magic */
   329 	if (memcmp(ELFMAG, file->e_ident, SELFMAG) != 0)
   330 	{
   331 		return false;
   332 	}
   333 
   334 	/* Check ELF version */
   335 	if (file->e_ident[EI_VERSION] != EV_CURRENT)
   336 	{
   337 		return false;
   338 	}
   339 
   340 	/* Check ELF architecture, which is 32-bit/x86 right now
   341 	 * Should change this for 64-bit if Valve gets their act together
   342 	 */
   343 	if (file->e_ident[EI_CLASS] != ELFCLASS32 || file->e_machine != EM_386 || file->e_ident[EI_DATA] != ELFDATA2LSB)
   344 	{
   345 		return false;
   346 	}
   347 
   348 	/* For our purposes, this must be a dynamic library/shared object */
   349 	if (file->e_type != ET_DYN)
   350 	{
   351 		return false;
   352 	}
   353 
   354 	phdrCount = file->e_phnum;
   355 	phdr = reinterpret_cast<Elf32_Phdr *>(baseAddr + file->e_phoff);
   356 	
   357 	/* Add up the memory sizes of segments marked as PT_LOAD as those are the only ones that should be in memory */
   358 	for (uint16_t i = 0; i < phdrCount; i++)
   359 	{
   360 		Elf32_Phdr &hdr = phdr[i];
   361 
   362 		if (hdr.p_type == PT_LOAD)
   363 		{
   364 			lib.memorySize += hdr.p_memsz;
   365 		}
   366 	}
   367 
   368 #endif
   369 
   370 	lib.baseAddress = reinterpret_cast<void *>(baseAddr);
   371 
   372 	return true;
   373 }