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/constructormagic.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),
53         undefined_symbols_(false) {
54     memset(symbols_, 0, sizeof(symbols_));
55   }
56 
~LateBindingSymbolTable()57   ~LateBindingSymbolTable() {
58     Unload();
59   }
60 
NumSymbols()61   static int NumSymbols() {
62     return SYMBOL_TABLE_SIZE;
63   }
64 
65   // We do not use this, but we offer it for theoretical convenience.
GetSymbolName(int index)66   static const char *GetSymbolName(int index) {
67     assert(index < NumSymbols());
68     return kSymbolNames[index];
69   }
70 
IsLoaded()71   bool IsLoaded() const {
72     return handle_ != kInvalidDllHandle;
73   }
74 
75   // Loads the DLL and the symbol table. Returns true iff the DLL and symbol
76   // table loaded successfully.
Load()77   bool Load() {
78     if (IsLoaded()) {
79       return true;
80     }
81     if (undefined_symbols_) {
82       // We do not attempt to load again because repeated attempts are not
83       // likely to succeed and DLL loading is costly.
84       return false;
85     }
86     handle_ = InternalLoadDll(kDllName);
87     if (!IsLoaded()) {
88       return false;
89     }
90     if (!InternalLoadSymbols(handle_, NumSymbols(), kSymbolNames, symbols_)) {
91       undefined_symbols_ = true;
92       Unload();
93       return false;
94     }
95     return true;
96   }
97 
Unload()98   void Unload() {
99     if (!IsLoaded()) {
100       return;
101     }
102     InternalUnloadDll(handle_);
103     handle_ = kInvalidDllHandle;
104     memset(symbols_, 0, sizeof(symbols_));
105   }
106 
107   // Retrieves the given symbol. NOTE: Recommended to use LATESYM_GET below
108   // instead of this.
GetSymbol(int index)109   void *GetSymbol(int index) const {
110     assert(IsLoaded());
111     assert(index < NumSymbols());
112     return symbols_[index];
113   }
114 
115  private:
116   DllHandle handle_;
117   bool undefined_symbols_;
118   void *symbols_[SYMBOL_TABLE_SIZE];
119 
120   RTC_DISALLOW_COPY_AND_ASSIGN(LateBindingSymbolTable);
121 };
122 
123 // This macro must be invoked in a header to declare a symbol table class.
124 #define LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(ClassName) \
125 enum {
126 
127 // This macro must be invoked in the header declaration once for each symbol
128 // (recommended to use an X-Macro to avoid duplication).
129 // This macro defines an enum with names built from the symbols, which
130 // essentially creates a hash table in the compiler from symbol names to their
131 // indices in the symbol table class.
132 #define LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(ClassName, sym) \
133   ClassName##_SYMBOL_TABLE_INDEX_##sym,
134 
135 // This macro completes the header declaration.
136 #define LATE_BINDING_SYMBOL_TABLE_DECLARE_END(ClassName)       \
137   ClassName##_SYMBOL_TABLE_SIZE                                \
138   }                                                            \
139   ;                                                            \
140                                                                \
141   extern const char ClassName##_kDllName[];                    \
142   extern const char* const                                     \
143       ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE]; \
144                                                                \
145   typedef ::webrtc::adm_linux::LateBindingSymbolTable<         \
146       ClassName##_SYMBOL_TABLE_SIZE, ClassName##_kDllName,     \
147       ClassName##_kSymbolNames>                                \
148       ClassName;
149 
150 // This macro must be invoked in a .cc file to define a previously-declared
151 // symbol table class.
152 #define LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(ClassName, dllName) \
153 const char ClassName##_kDllName[] = dllName; \
154 const char *const ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE] = {
155 
156 // This macro must be invoked in the .cc definition once for each symbol
157 // (recommended to use an X-Macro to avoid duplication).
158 // This would have to use the mangled name if we were to ever support C++
159 // symbols.
160 #define LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(ClassName, sym) \
161   #sym,
162 
163 #define LATE_BINDING_SYMBOL_TABLE_DEFINE_END(ClassName) \
164 };
165 
166 // Index of a given symbol in the given symbol table class.
167 #define LATESYM_INDEXOF(ClassName, sym) \
168   (ClassName##_SYMBOL_TABLE_INDEX_##sym)
169 
170 // Returns a reference to the given late-binded symbol, with the correct type.
171 #define LATESYM_GET(ClassName, inst, sym) \
172   (*reinterpret_cast<typeof(&sym)>( \
173       (inst)->GetSymbol(LATESYM_INDEXOF(ClassName, sym))))
174 
175 }  // namespace adm_linux
176 }  // namespace webrtc
177 
178 #endif  // ADM_LATEBINDINGSYMBOLTABLE_LINUX_H_
179