1 /*
2  *  Copyright (c) 2010 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #ifndef AUDIO_DEVICE_LATEBINDINGSYMBOLTABLE_LINUX_H_
12 #define AUDIO_DEVICE_LATEBINDINGSYMBOLTABLE_LINUX_H_
13 
14 #include <assert.h>
15 #include <stddef.h>  // for NULL
16 #include <string.h>
17 
18 #include "rtc_base/constructor_magic.h"
19 
20 // This file provides macros for creating "symbol table" classes to simplify the
21 // dynamic loading of symbols from DLLs. Currently the implementation only
22 // supports Linux and pure C symbols.
23 // See talk/sound/pulseaudiosymboltable.(h|cc) for an example.
24 
25 namespace webrtc {
26 namespace adm_linux {
27 
28 #ifdef WEBRTC_LINUX
29 typedef void* DllHandle;
30 
31 const DllHandle kInvalidDllHandle = NULL;
32 #else
33 #error Not implemented
34 #endif
35 
36 // These are helpers for use only by the class below.
37 DllHandle InternalLoadDll(const char dll_name[]);
38 
39 void InternalUnloadDll(DllHandle handle);
40 
41 bool InternalLoadSymbols(DllHandle handle,
42                          int num_symbols,
43                          const char* const symbol_names[],
44                          void* symbols[]);
45 
46 template <int SYMBOL_TABLE_SIZE,
47           const char kDllName[],
48           const char* const kSymbolNames[]>
49 class LateBindingSymbolTable {
50  public:
LateBindingSymbolTable()51   LateBindingSymbolTable()
52       : handle_(kInvalidDllHandle), undefined_symbols_(false) {
53     memset(symbols_, 0, sizeof(symbols_));
54   }
55 
~LateBindingSymbolTable()56   ~LateBindingSymbolTable() { Unload(); }
57 
NumSymbols()58   static int NumSymbols() { return SYMBOL_TABLE_SIZE; }
59 
60   // We do not use this, but we offer it for theoretical convenience.
GetSymbolName(int index)61   static const char* GetSymbolName(int index) {
62     assert(index < NumSymbols());
63     return kSymbolNames[index];
64   }
65 
IsLoaded()66   bool IsLoaded() const { return handle_ != kInvalidDllHandle; }
67 
68   // Loads the DLL and the symbol table. Returns true iff the DLL and symbol
69   // table loaded successfully.
Load()70   bool Load() {
71     if (IsLoaded()) {
72       return true;
73     }
74     if (undefined_symbols_) {
75       // We do not attempt to load again because repeated attempts are not
76       // likely to succeed and DLL loading is costly.
77       return false;
78     }
79     handle_ = InternalLoadDll(kDllName);
80     if (!IsLoaded()) {
81       return false;
82     }
83     if (!InternalLoadSymbols(handle_, NumSymbols(), kSymbolNames, symbols_)) {
84       undefined_symbols_ = true;
85       Unload();
86       return false;
87     }
88     return true;
89   }
90 
Unload()91   void Unload() {
92     if (!IsLoaded()) {
93       return;
94     }
95     InternalUnloadDll(handle_);
96     handle_ = kInvalidDllHandle;
97     memset(symbols_, 0, sizeof(symbols_));
98   }
99 
100   // Retrieves the given symbol. NOTE: Recommended to use LATESYM_GET below
101   // instead of this.
GetSymbol(int index)102   void* GetSymbol(int index) const {
103     assert(IsLoaded());
104     assert(index < NumSymbols());
105     return symbols_[index];
106   }
107 
108  private:
109   DllHandle handle_;
110   bool undefined_symbols_;
111   void* symbols_[SYMBOL_TABLE_SIZE];
112 
113   RTC_DISALLOW_COPY_AND_ASSIGN(LateBindingSymbolTable);
114 };
115 
116 // This macro must be invoked in a header to declare a symbol table class.
117 #define LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(ClassName) enum {
118 // This macro must be invoked in the header declaration once for each symbol
119 // (recommended to use an X-Macro to avoid duplication).
120 // This macro defines an enum with names built from the symbols, which
121 // essentially creates a hash table in the compiler from symbol names to their
122 // indices in the symbol table class.
123 #define LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(ClassName, sym) \
124   ClassName##_SYMBOL_TABLE_INDEX_##sym,
125 
126 // This macro completes the header declaration.
127 #define LATE_BINDING_SYMBOL_TABLE_DECLARE_END(ClassName)       \
128   ClassName##_SYMBOL_TABLE_SIZE                                \
129   }                                                            \
130   ;                                                            \
131                                                                \
132   extern const char ClassName##_kDllName[];                    \
133   extern const char* const                                     \
134       ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE]; \
135                                                                \
136   typedef ::webrtc::adm_linux::LateBindingSymbolTable<         \
137       ClassName##_SYMBOL_TABLE_SIZE, ClassName##_kDllName,     \
138       ClassName##_kSymbolNames>                                \
139       ClassName;
140 
141 // This macro must be invoked in a .cc file to define a previously-declared
142 // symbol table class.
143 #define LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(ClassName, dllName) \
144   const char ClassName##_kDllName[] = dllName;                     \
145   const char* const ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE] = {
146 // This macro must be invoked in the .cc definition once for each symbol
147 // (recommended to use an X-Macro to avoid duplication).
148 // This would have to use the mangled name if we were to ever support C++
149 // symbols.
150 #define LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(ClassName, sym) #sym,
151 
152 #define LATE_BINDING_SYMBOL_TABLE_DEFINE_END(ClassName) \
153   }                                                     \
154   ;
155 
156 // Index of a given symbol in the given symbol table class.
157 #define LATESYM_INDEXOF(ClassName, sym) (ClassName##_SYMBOL_TABLE_INDEX_##sym)
158 
159 // Returns a reference to the given late-binded symbol, with the correct type.
160 #define LATESYM_GET(ClassName, inst, sym) \
161   (*reinterpret_cast<__typeof__(&sym)>(   \
162       (inst)->GetSymbol(LATESYM_INDEXOF(ClassName, sym))))
163 
164 }  // namespace adm_linux
165 }  // namespace webrtc
166 
167 #endif  // ADM_LATEBINDINGSYMBOLTABLE_LINUX_H_
168