diff --git a/core/GameConfigs.cpp b/core/GameConfigs.cpp index 86bd1c18..d8569066 100644 --- a/core/GameConfigs.cpp +++ b/core/GameConfigs.cpp @@ -1,8 +1,8 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -25,8 +25,6 @@ * this exception to all derivative works. AlliedModders LLC defines further * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * or . - * - * Version: $Id$ */ #include @@ -506,7 +504,11 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states) void *handle = dlopen(info.dli_fname, RTLD_NOW); if (handle) { +#if SOURCE_ENGINE == SE_LEFT4DEAD2 + final_addr = g_MemUtils.ResolveSymbol(handle, &s_TempSig.sig[1]); +#else final_addr = dlsym(handle, &s_TempSig.sig[1]); +#endif dlclose(handle); } else { g_Logger.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")", diff --git a/core/MemoryUtils.cpp b/core/MemoryUtils.cpp index 6af80718..9cacaf35 100644 --- a/core/MemoryUtils.cpp +++ b/core/MemoryUtils.cpp @@ -1,8 +1,8 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -25,15 +25,14 @@ * this exception to all derivative works. AlliedModders LLC defines further * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * or . - * - * Version: $Id$ */ #include "MemoryUtils.h" #include "ShareSys.h" #ifdef PLATFORM_LINUX -#include -#include +#include +#include +#include #endif MemoryUtils g_MemUtils; @@ -56,6 +55,17 @@ MemoryUtils::MemoryUtils() } #endif +MemoryUtils::~MemoryUtils() +{ +#ifdef PLATFORM_LINUX + for (size_t i = 0; i < m_SymTables.size(); i++) + { + delete m_SymTables[i]; + } + m_SymTables.clear(); +#endif +} + void MemoryUtils::OnSourceModAllInitialized() { g_ShareSys.AddInterface(NULL, this); @@ -98,6 +108,149 @@ void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t l 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; + +#endif +} + bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib) { unsigned long baseAddr; diff --git a/core/MemoryUtils.h b/core/MemoryUtils.h index 26713fc0..4b5d8467 100644 --- a/core/MemoryUtils.h +++ b/core/MemoryUtils.h @@ -1,8 +1,8 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * Copyright (C) 2004-2000 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -25,8 +25,6 @@ * this exception to all derivative works. AlliedModders LLC defines further * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * or . - * - * Version: $Id$ */ #ifndef _INCLUDE_SOURCEMOD_MEMORYUTILS_H_ @@ -34,6 +32,12 @@ #include #include "sm_globals.h" +#ifdef PLATFORM_LINUX +#include +#include "sm_symtable.h" + +using namespace SourceHook; +#endif using namespace SourceMod; @@ -43,16 +47,34 @@ struct DynLibInfo size_t memorySize; }; +#ifdef PLATFORM_LINUX +typedef uint32_t Elf32_Addr; + +struct LibSymbolTable +{ + SymbolTable table; + Elf32_Addr lib_base; + uint32_t last_pos; +}; +#endif + class MemoryUtils : public IMemoryUtils, public SMGlobalClass { +public: + ~MemoryUtils(); public: // SMGlobalClass void OnSourceModAllInitialized(); public: // IMemoryUtils void *FindPattern(const void *libPtr, const char *pattern, size_t len); + void *ResolveSymbol(void *handle, const char *symbol); public: bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib); +#ifdef PLATFORM_LINUX +private: + CVector m_SymTables; +#endif }; extern MemoryUtils g_MemUtils; diff --git a/core/msvc9/sourcemod_mm.vcproj b/core/msvc9/sourcemod_mm.vcproj index 0c128507..792744ac 100644 --- a/core/msvc9/sourcemod_mm.vcproj +++ b/core/msvc9/sourcemod_mm.vcproj @@ -60,6 +60,7 @@ + + diff --git a/core/sm_symtable.h b/core/sm_symtable.h new file mode 100644 index 00000000..1e2e9c40 --- /dev/null +++ b/core/sm_symtable.h @@ -0,0 +1,231 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2009 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 . + */ + +#ifndef _INCLUDE_SOURCEMOD_CORE_SYMBOLTABLE_H_ +#define _INCLUDE_SOURCEMOD_CORE_SYMBOLTABLE_H_ + +#include +#include +#include + +#define KESTRING_TABLE_START_SIZE 65536 + +struct Symbol +{ + size_t length; + uint32_t hash; + void *address; + Symbol *tbl_next; + + inline char *buffer() + { + return reinterpret_cast(this + 1); + } +}; + +class SymbolTable +{ +public: + ~SymbolTable() + { + for (uint32_t i = 0; i < nbuckets; i++) + { + Symbol *sym = buckets[i]; + while (sym != NULL) + { + Symbol *next = sym->tbl_next; + free(sym); + sym = next; + } + } + free(buckets); + } + + bool Initialize() + { + buckets = (Symbol **)malloc(sizeof(Symbol *) * KESTRING_TABLE_START_SIZE); + if (buckets == NULL) + { + return false; + } + memset(buckets, 0, sizeof(Symbol *) * KESTRING_TABLE_START_SIZE); + + nbuckets = KESTRING_TABLE_START_SIZE; + nused = 0; + bucketmask = KESTRING_TABLE_START_SIZE - 1; + return true; + } + + static inline uint32_t HashString(const char *data, size_t len) + { + #undef get16bits + #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) + #define get16bits(d) (*((const uint16_t *) (d))) + #endif + #if !defined (get16bits) + #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) + #endif + uint32_t hash = len, tmp; + int rem; + + if (len <= 0 || data == NULL) + { + return 0; + } + + rem = len & 3; + len >>= 2; + + /* Main loop */ + for (;len > 0; len--) { + hash += get16bits (data); + tmp = (get16bits (data+2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + data += 2 * sizeof (uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) { + case 3: hash += get16bits (data); + hash ^= hash << 16; + hash ^= data[sizeof (uint16_t)] << 18; + hash += hash >> 11; + break; + case 2: hash += get16bits (data); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += *data; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; + + #undef get16bits + } + + Symbol **FindSymbolBucket(const char *str, size_t len, uint32_t hash) + { + uint32_t bucket = hash & bucketmask; + Symbol **pkvs = &buckets[bucket]; + + Symbol *kvs = *pkvs; + while (kvs != NULL) + { + if (len == kvs->length && memcmp(str, kvs->buffer(), len * sizeof(char)) == 0) + { + return pkvs; + } + pkvs = &kvs->tbl_next; + kvs = *pkvs; + } + + return pkvs; + } + + void ResizeSymbolTable() + { + uint32_t xnbuckets = nbuckets * 2; + Symbol **xbuckets = (Symbol **)malloc(sizeof(Symbol *) * xnbuckets); + if (xbuckets == NULL) + { + return; + } + memset(xbuckets, 0, sizeof(Symbol *) * xnbuckets); + uint32_t xbucketmask = xnbuckets - 1; + for (uint32_t i = 0; i < nbuckets; i++) + { + Symbol *sym = buckets[i]; + while (sym != NULL) + { + Symbol *next = sym->tbl_next; + uint32_t bucket = sym->hash & xbucketmask; + sym->tbl_next = xbuckets[bucket]; + xbuckets[bucket] = sym; + sym = next; + } + } + free(buckets); + buckets = xbuckets; + nbuckets = xnbuckets; + bucketmask = xbucketmask; + } + + Symbol *FindSymbol(const char *str, size_t len) + { + uint32_t hash = HashString(str, len); + Symbol **pkvs = FindSymbolBucket(str, len, hash); + return *pkvs; + } + + Symbol *InternSymbol(const char* str, size_t len, void *address) + { + uint32_t hash = HashString(str, len); + Symbol **pkvs = FindSymbolBucket(str, len, hash); + if (*pkvs != NULL) + { + return *pkvs; + } + + Symbol *kvs = (Symbol *)malloc(sizeof(Symbol) + sizeof(char) * (len + 1)); + kvs->length = len; + kvs->hash = hash; + kvs->address = address; + kvs->tbl_next = NULL; + memcpy(kvs + 1, str, sizeof(char) * (len + 1)); + *pkvs = kvs; + nused++; + + if (nused > nbuckets && nbuckets <= INT_MAX / 2) + { + ResizeSymbolTable(); + } + + return kvs; + } +private: + uint32_t nbuckets; + uint32_t nused; + uint32_t bucketmask; + Symbol **buckets; +}; + +#endif //_INCLUDE_SOURCEMOD_CORE_SYMBOLTABLE_H_ diff --git a/extensions/sdktools/vcaller.cpp b/extensions/sdktools/vcaller.cpp index 1da76029..547d7856 100644 --- a/extensions/sdktools/vcaller.cpp +++ b/extensions/sdktools/vcaller.cpp @@ -1,8 +1,8 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= * SourceMod SDKTools Extension - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -25,8 +25,6 @@ * this exception to all derivative works. AlliedModders LLC defines further * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * or . - * - * Version: $Id$ */ #include "extension.h" @@ -131,7 +129,11 @@ static cell_t PrepSDKCall_SetSignature(IPluginContext *pContext, const cell_t *p { return 0; } +#if SOURCE_ENGINE == SE_LEFT4DEAD2 + s_call_addr = memutils->ResolveSymbol(handle, &sig[1]); +#else s_call_addr = dlsym(handle, &sig[1]); +#endif dlclose(handle); return (s_call_addr != NULL) ? 1 : 0; diff --git a/public/IMemoryUtils.h b/public/IMemoryUtils.h index 3334bf88..592bc7ed 100644 --- a/public/IMemoryUtils.h +++ b/public/IMemoryUtils.h @@ -1,8 +1,8 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -25,8 +25,6 @@ * this exception to all derivative works. AlliedModders LLC defines further * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * or . - * - * Version: $Id$ */ #ifndef _INCLUDE_SOURCEMOD_INTERFACE_BINARYUTILS_H_ @@ -35,7 +33,7 @@ #include #define SMINTERFACE_MEMORYUTILS_NAME "IMemoryUtils" -#define SMINTERFACE_MEMORYUTILS_VERSION 1 +#define SMINTERFACE_MEMORYUTILS_VERSION 2 /** * @file IMemoryUtils.h @@ -65,6 +63,19 @@ namespace SourceMod * @return Pointer to pattern found in memory, NULL if not found. */ virtual void *FindPattern(const void *libPtr, const char *pattern, size_t len) =0; + + /** + * @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. + * + * @param handle Operating system specific handle that points to dynamic library. + * This comes from dlopen() on Linux or LoadLibrary() on Windows. + * @param symbol Symbol name. + * @return Symbol pointer, or NULL if not found. + */ + virtual void *ResolveSymbol(void *handle, const char *symbol) =0; }; }