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