Home | History | Annotate | Download | only in base
      1 /*
      2  * libjingle
      3  * Copyright 2004--2010, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #ifndef TALK_BASE_LATEBINDINGSYMBOLTABLE_H_
     29 #define TALK_BASE_LATEBINDINGSYMBOLTABLE_H_
     30 
     31 #include <stddef.h>  // for NULL
     32 #include <string.h>
     33 
     34 #include "talk/base/common.h"
     35 #include "talk/base/logging.h"
     36 
     37 // This file provides macros for creating "symbol table" classes to simplify the
     38 // dynamic loading of symbols from DLLs. Currently the implementation only
     39 // supports Linux and pure C symbols.
     40 // See talk/sound/pulseaudiosymboltable.(h|cc) for an example.
     41 
     42 namespace talk_base {
     43 
     44 #ifdef LINUX
     45 typedef void *DllHandle;
     46 
     47 const DllHandle kInvalidDllHandle = NULL;
     48 #else
     49 #error Not implemented
     50 #endif
     51 
     52 // These are helpers for use only by the class below.
     53 DllHandle InternalLoadDll(const char dll_name[]);
     54 
     55 void InternalUnloadDll(DllHandle handle);
     56 
     57 bool InternalLoadSymbols(DllHandle handle,
     58                          int num_symbols,
     59                          const char *const symbol_names[],
     60                          void *symbols[]);
     61 
     62 template <int SYMBOL_TABLE_SIZE,
     63           const char kDllName[],
     64           const char *const kSymbolNames[]>
     65 class LateBindingSymbolTable {
     66  public:
     67   LateBindingSymbolTable()
     68       : handle_(kInvalidDllHandle),
     69         undefined_symbols_(false) {
     70     memset(symbols_, 0, sizeof(symbols_));
     71   }
     72 
     73   ~LateBindingSymbolTable() {
     74     Unload();
     75   }
     76 
     77   static int NumSymbols() {
     78     return SYMBOL_TABLE_SIZE;
     79   }
     80 
     81   // We do not use this, but we offer it for theoretical convenience.
     82   static const char *GetSymbolName(int index) {
     83     ASSERT(index < NumSymbols());
     84     return kSymbolNames[index];
     85   }
     86 
     87   bool IsLoaded() const {
     88     return handle_ != kInvalidDllHandle;
     89   }
     90 
     91   // Loads the DLL and the symbol table. Returns true iff the DLL and symbol
     92   // table loaded successfully.
     93   bool Load() {
     94     if (IsLoaded()) {
     95       return true;
     96     }
     97     if (undefined_symbols_) {
     98       // We do not attempt to load again because repeated attempts are not
     99       // likely to succeed and DLL loading is costly.
    100       LOG(LS_ERROR) << "We know there are undefined symbols";
    101       return false;
    102     }
    103     handle_ = InternalLoadDll(kDllName);
    104     if (!IsLoaded()) {
    105       return false;
    106     }
    107     if (!InternalLoadSymbols(handle_, NumSymbols(), kSymbolNames, symbols_)) {
    108       undefined_symbols_ = true;
    109       Unload();
    110       return false;
    111     }
    112     return true;
    113   }
    114 
    115   void Unload() {
    116     if (!IsLoaded()) {
    117       return;
    118     }
    119     InternalUnloadDll(handle_);
    120     handle_ = kInvalidDllHandle;
    121     memset(symbols_, 0, sizeof(symbols_));
    122   }
    123 
    124   // Retrieves the given symbol. NOTE: Recommended to use LATESYM_GET below
    125   // instead of this.
    126   void *GetSymbol(int index) const {
    127     ASSERT(IsLoaded());
    128     ASSERT(index < NumSymbols());
    129     return symbols_[index];
    130   }
    131 
    132  private:
    133   DllHandle handle_;
    134   bool undefined_symbols_;
    135   void *symbols_[SYMBOL_TABLE_SIZE];
    136 
    137   DISALLOW_COPY_AND_ASSIGN(LateBindingSymbolTable);
    138 };
    139 
    140 // This macro must be invoked in a header to declare a symbol table class.
    141 #define LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(ClassName) \
    142 enum {
    143 
    144 // This macro must be invoked in the header declaration once for each symbol
    145 // (recommended to use an X-Macro to avoid duplication).
    146 // This macro defines an enum with names built from the symbols, which
    147 // essentially creates a hash table in the compiler from symbol names to their
    148 // indices in the symbol table class.
    149 #define LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(ClassName, sym) \
    150   ClassName##_SYMBOL_TABLE_INDEX_##sym,
    151 
    152 // This macro completes the header declaration.
    153 #define LATE_BINDING_SYMBOL_TABLE_DECLARE_END(ClassName) \
    154   ClassName##_SYMBOL_TABLE_SIZE \
    155 }; \
    156 \
    157 extern const char ClassName##_kDllName[]; \
    158 extern const char *const \
    159     ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE]; \
    160 \
    161 typedef ::talk_base::LateBindingSymbolTable<ClassName##_SYMBOL_TABLE_SIZE, \
    162                                             ClassName##_kDllName, \
    163                                             ClassName##_kSymbolNames> \
    164     ClassName;
    165 
    166 // This macro must be invoked in a .cc file to define a previously-declared
    167 // symbol table class.
    168 #define LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(ClassName, dllName) \
    169 const char ClassName##_kDllName[] = dllName; \
    170 const char *const ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE] = {
    171 
    172 // This macro must be invoked in the .cc definition once for each symbol
    173 // (recommended to use an X-Macro to avoid duplication).
    174 // This would have to use the mangled name if we were to ever support C++
    175 // symbols.
    176 #define LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(ClassName, sym) \
    177   #sym,
    178 
    179 #define LATE_BINDING_SYMBOL_TABLE_DEFINE_END(ClassName) \
    180 };
    181 
    182 // Index of a given symbol in the given symbol table class.
    183 #define LATESYM_INDEXOF(ClassName, sym) \
    184   (ClassName##_SYMBOL_TABLE_INDEX_##sym)
    185 
    186 // Returns a reference to the given late-binded symbol, with the correct type.
    187 #define LATESYM_GET(ClassName, inst, sym) \
    188   (*reinterpret_cast<typeof(&sym)>( \
    189       (inst)->GetSymbol(LATESYM_INDEXOF(ClassName, sym))))
    190 
    191 }  // namespace talk_base
    192 
    193 #endif  // TALK_BASE_LATEBINDINGSYMBOLTABLE_H_
    194