Added IMemoryUtils::ResolveSymbol() for resolving symbols hidden via GCC's -fvisibility=hidden (bug 4091, r=dvander,pred).

Right now, this is only used on L4D2 where it is needed.
This commit is contained in:
Scott Ehlert 2009-11-06 16:24:40 -06:00
parent 5bf17fc5ae
commit dc0ea197cd
7 changed files with 463 additions and 23 deletions

View File

@ -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 <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include <string.h>
@ -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\")",

View File

@ -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 <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include "MemoryUtils.h"
#include "ShareSys.h"
#ifdef PLATFORM_LINUX
#include <string.h>
#include <elf.h>
#include <fcntl.h>
#include <link.h>
#include <sys/mman.h>
#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 = &sections[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;

View File

@ -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 <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_MEMORYUTILS_H_
@ -34,6 +32,12 @@
#include <IMemoryUtils.h>
#include "sm_globals.h"
#ifdef PLATFORM_LINUX
#include <sh_vector.h>
#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<LibSymbolTable *> m_SymTables;
#endif
};
extern MemoryUtils g_MemUtils;

View File

@ -60,6 +60,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -224,6 +225,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -308,6 +310,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -472,6 +475,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -556,6 +560,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -638,6 +643,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -719,6 +725,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -803,6 +810,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -885,6 +893,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -966,6 +975,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -1050,6 +1060,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -1214,6 +1225,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -1298,6 +1310,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -1548,6 +1561,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -1712,6 +1726,7 @@
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="BINARY_NAME=\&quot;$(TargetFileName)\&quot;"
AdditionalIncludeDirectories="..\..\public"
/>
<Tool
Name="VCPreLinkEventTool"
@ -2263,6 +2278,10 @@
RelativePath="..\sm_stringutil.h"
>
</File>
<File
RelativePath="..\sm_symtable.h"
>
</File>
<File
RelativePath="..\sm_trie.h"
>

231
core/sm_symtable.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
* 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 <http://www.sourcemod.net/license.php>.
*/
#ifndef _INCLUDE_SOURCEMOD_CORE_SYMBOLTABLE_H_
#define _INCLUDE_SOURCEMOD_CORE_SYMBOLTABLE_H_
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#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<char *>(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_

View File

@ -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 <http://www.sourcemod.net/license.php>.
*
* 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;

View File

@ -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 <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_INTERFACE_BINARYUTILS_H_
@ -35,7 +33,7 @@
#include <IShareSys.h>
#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;
};
}