1/*
2 *  Copyright 2004 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#include "webrtc/base/latebindingsymboltable.h"
12
13#if defined(WEBRTC_POSIX)
14#include <dlfcn.h>
15#endif
16
17#include "webrtc/base/logging.h"
18
19namespace rtc {
20
21#if defined(WEBRTC_POSIX)
22static const DllHandle kInvalidDllHandle = NULL;
23#else
24#error Not implemented
25#endif
26
27static const char *GetDllError() {
28#if defined(WEBRTC_POSIX)
29  const char *err = dlerror();
30  if (err) {
31    return err;
32  } else {
33    return "No error";
34  }
35#else
36#error Not implemented
37#endif
38}
39
40static bool LoadSymbol(DllHandle handle,
41                       const char *symbol_name,
42                       void **symbol) {
43#if defined(WEBRTC_POSIX)
44  *symbol = dlsym(handle, symbol_name);
45  const char *err = dlerror();
46  if (err) {
47    LOG(LS_ERROR) << "Error loading symbol " << symbol_name << ": " << err;
48    return false;
49  } else if (!*symbol) {
50    // ELF allows for symbols to be NULL, but that should never happen for our
51    // usage.
52    LOG(LS_ERROR) << "Symbol " << symbol_name << " is NULL";
53    return false;
54  }
55  return true;
56#else
57#error Not implemented
58#endif
59}
60
61LateBindingSymbolTable::LateBindingSymbolTable(const TableInfo *info,
62    void **table)
63    : info_(info),
64      table_(table),
65      handle_(kInvalidDllHandle),
66      undefined_symbols_(false) {
67  ClearSymbols();
68}
69
70LateBindingSymbolTable::~LateBindingSymbolTable() {
71  Unload();
72}
73
74bool LateBindingSymbolTable::IsLoaded() const {
75  return handle_ != kInvalidDllHandle;
76}
77
78bool LateBindingSymbolTable::Load() {
79  ASSERT(info_->dll_name != NULL);
80  return LoadFromPath(info_->dll_name);
81}
82
83bool LateBindingSymbolTable::LoadFromPath(const char *dll_path) {
84  if (IsLoaded()) {
85    return true;
86  }
87  if (undefined_symbols_) {
88    // We do not attempt to load again because repeated attempts are not
89    // likely to succeed and DLL loading is costly.
90    LOG(LS_ERROR) << "We know there are undefined symbols";
91    return false;
92  }
93
94#if defined(WEBRTC_POSIX)
95  handle_ = dlopen(dll_path,
96                   // RTLD_NOW front-loads symbol resolution so that errors are
97                   // caught early instead of causing a process abort later.
98                   // RTLD_LOCAL prevents other modules from automatically
99                   // seeing symbol definitions in the newly-loaded tree. This
100                   // is necessary for same-named symbols in different ABI
101                   // versions of the same library to not explode.
102                   RTLD_NOW|RTLD_LOCAL
103#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) && defined(RTLD_DEEPBIND)
104                   // RTLD_DEEPBIND makes symbol dependencies in the
105                   // newly-loaded tree prefer to resolve to definitions within
106                   // that tree (the default on OS X). This is necessary for
107                   // same-named symbols in different ABI versions of the same
108                   // library to not explode.
109                   |RTLD_DEEPBIND
110#endif
111                   );  // NOLINT
112#else
113#error Not implemented
114#endif
115
116  if (handle_ == kInvalidDllHandle) {
117    LOG(LS_WARNING) << "Can't load " << dll_path << ": "
118                    << GetDllError();
119    return false;
120  }
121#if defined(WEBRTC_POSIX)
122  // Clear any old errors.
123  dlerror();
124#endif
125  for (int i = 0; i < info_->num_symbols; ++i) {
126    if (!LoadSymbol(handle_, info_->symbol_names[i], &table_[i])) {
127      undefined_symbols_ = true;
128      Unload();
129      return false;
130    }
131  }
132  return true;
133}
134
135void LateBindingSymbolTable::Unload() {
136  if (!IsLoaded()) {
137    return;
138  }
139
140#if defined(WEBRTC_POSIX)
141  if (dlclose(handle_) != 0) {
142    LOG(LS_ERROR) << GetDllError();
143  }
144#else
145#error Not implemented
146#endif
147
148  handle_ = kInvalidDllHandle;
149  ClearSymbols();
150}
151
152void LateBindingSymbolTable::ClearSymbols() {
153  memset(table_, 0, sizeof(void *) * info_->num_symbols);
154}
155
156}  // namespace rtc
157