aboutsummaryrefslogtreecommitdiff
path: root/src/abg-symtab-reader.h
blob: 948fc2703891713ffc62a5de4cceb4c0389adbf4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2020 Google, Inc.
//
// Author: Matthias Maennich

/// @file
///
/// This contains the declarations for the symtab reader.

#ifndef __ABG_SYMTAB_READER_H__
#define __ABG_SYMTAB_READER_H__

#include <gelf.h>

#include <functional>
#include <iterator>
#include <memory>
#include <unordered_map>
#include <vector>

#include "abg-cxx-compat.h"  // for abg_compat::optional
#include "abg-ir.h"

namespace abigail
{
namespace symtab_reader
{

/// The symtab filter is the object passed to the symtab object in order to
/// iterate over the symbols in the symtab while applying filters.
///
/// The general idea is that it consists of a set of optionally enforced flags,
/// such as 'functions' or 'variables'. If not set, those are not filtered for,
/// neither inclusive nor exclusive. If set they are all ANDed together.
class symtab_filter
{
public:
  // Default constructor disabling all features.
  symtab_filter() {}

  bool
  matches(const elf_symbol& symbol) const;

  /// Enable or disable function filtering
  ///
  /// @param new_value whether to filter for functions
  void
  set_functions(bool new_value = true)
  {functions_ = new_value;};

  /// Enable or disable variable filtering
  ///
  /// @param new_value whether to filter for variables
  void
  set_variables(bool new_value = true)
  {variables_ = new_value;};

  /// Enable or disable public symbol filtering
  ///
  /// @param new_value whether to filter for public symbols
  void
  set_public_symbols(bool new_value = true)
  {public_symbols_ = new_value;};

  /// Enable or disable undefined symbol filtering
  ///
  /// @param new_value whether to filter for undefined symbols
  void
  set_undefined_symbols(bool new_value = true)
  {undefined_symbols_ = new_value;};

  /// Enable or disable kernel symbol filtering
  ///
  /// @param new_value whether to filter for kernel symbols
  void
  set_kernel_symbols(bool new_value = true)
  {kernel_symbols_ = new_value;};

private:
  // The symbol is a function (FUNC)
  abg_compat::optional<bool> functions_;

  // The symbol is a variables (OBJECT)
  abg_compat::optional<bool> variables_;

  // The symbol is publicly accessible (global/weak with default/protected
  // visibility)
  abg_compat::optional<bool> public_symbols_;

  // The symbols is not defined (declared)
  abg_compat::optional<bool> undefined_symbols_;

  // The symbol is listed in the ksymtab (for Linux Kernel binaries).
  abg_compat::optional<bool> kernel_symbols_;
};

/// Base iterator for our custom iterator based on whatever the const_iterator
/// is for a vector of symbols.
/// As of writing this, std::vector<elf_symbol_sptr>::const_iterator.
typedef elf_symbols::const_iterator base_iterator;

/// An iterator to walk a vector of elf_symbols filtered by symtab_filter.
///
/// The implementation inherits all properties from the vector's
/// const_iterator, but intercepts where necessary to allow effective
/// filtering. This makes it a STL compatible iterator for general purpose
/// usage.
class symtab_iterator : public base_iterator
{
public:
  typedef base_iterator::value_type	 value_type;
  typedef base_iterator::reference	 reference;
  typedef base_iterator::pointer	 pointer;
  typedef base_iterator::difference_type difference_type;
  typedef std::forward_iterator_tag	 iterator_category;

  /// Construct the iterator based on a pair of underlying iterators and a
  /// symtab_filter object. Immediately fast forward to the next element that
  /// matches the criteria (if any).
  ///
  /// @param begin the underlying begin iterator
  ///
  /// @param begin the underlying end iterator
  ///
  /// @param filter the symtab_filter to apply
  symtab_iterator(base_iterator	       begin,
		  base_iterator	       end,
		  const symtab_filter& filter = symtab_filter())
    : base_iterator(begin), end_(end), filter_(filter)
  {skip_to_next();}

  /// Pre-increment operator to advance to the next matching element.
  ///
  /// @return itself after incrementing
  symtab_iterator&
  operator++()
  {
    base_iterator::operator++();
    skip_to_next();
    return *this;
  }

  /// Post-increment operator to advance to the next matching element.
  ///
  /// @return a copy of the iterator before incrementing
  symtab_iterator
  operator++(int)
  {
    symtab_iterator result(*this);
    ++(*this);
    return result;
  }

private:
  /// The end of the underlying iterator.
  const base_iterator end_;

  /// The symtab_filter used to determine when to advance.
  const symtab_filter& filter_;

  /// Skip to the next element that matches the filter criteria (if any). Hold
  /// off when reaching the end of the underlying iterator.
  void
  skip_to_next()
  {
    while (*this != end_ && !filter_.matches(***this))
      ++(*this);
  }
};

/// Convenience declaration of a unique_ptr<symtab>
class symtab;
typedef std::unique_ptr<symtab> symtab_ptr;

/// symtab is the actual data container of the symtab_reader implementation.
///
/// The symtab is instantiated either via an Elf handle (from binary) or from a
/// set of existing symbol maps (usually when instantiated from XML). It will
/// then discover the symtab, possibly the ksymtab (for Linux Kernel binaries)
/// and setup the data containers and lookup maps for later perusal.
///
/// The symtab is supposed to be used in a const context as all information is
/// already computed at construction time. Symbols are stored sorted to allow
/// deterministic reading of the entries.
///
/// An example use of the symtab class is
///
/// const auto symtab    = symtab::load(elf_handle, env);
/// symtab_filter filter = symtab->make_filter();
/// filter.set_public_symbols();
/// filter.set_functions();
///
/// for (const auto& symbol : filtered_symtab(*symtab, filter))
///   {
///     std::cout << symbol->get_name() << "\n";
///   }
///
/// This uses the filtered_symtab proxy object to capture the filter.
class symtab
{
public:
  typedef std::function<bool(const elf_symbol_sptr&)> symbol_predicate;

  /// Indicate whether any (kernel) symbols have been seen at construction.
  ///
  /// @return true if there are symbols detected earlier.
  bool
  has_symbols() const
  {return is_kernel_binary_ ? has_ksymtab_entries_ : !symbols_.empty();}

  symtab_filter
  make_filter() const;

  /// The (only) iterator type we offer is a const_iterator implemented by the
  /// symtab_iterator.
  typedef symtab_iterator const_iterator;

  /// Obtain an iterator to the beginning of the symtab according to the filter
  /// criteria. Whenever this iterator advances, it skips elements that do not
  /// match the filter criteria.
  ///
  /// @param filter the symtab_filter to match symbols against
  ///
  /// @return a filtering const_iterator of the underlying type
  const_iterator
  begin(const symtab_filter& filter) const
  {return symtab_iterator(symbols_.begin(), symbols_.end(), filter);}

  /// Obtain an iterator to the end of the symtab.
  ///
  /// @return an end iterator
  const_iterator
  end() const
  {return symtab_iterator(symbols_.end(), symbols_.end());}

  const elf_symbols&
  lookup_symbol(const std::string& name) const;

  const elf_symbol_sptr&
  lookup_symbol(GElf_Addr symbol_addr) const;

  static symtab_ptr
  load(Elf*		elf_handle,
       ir::environment* env,
       symbol_predicate is_suppressed = NULL);

  static symtab_ptr
  load(string_elf_symbols_map_sptr function_symbol_map,
       string_elf_symbols_map_sptr variables_symbol_map);

  void
  update_main_symbol(GElf_Addr addr, const std::string& name);

private:
  /// Default constructor. Private to enforce creation by factory methods.
  symtab();

  /// The vector of symbols we discovered.
  elf_symbols symbols_;

  /// Whether this is a Linux Kernel binary
  bool is_kernel_binary_;

  /// Whether this kernel_binary has ksymtab entries
  ///
  /// A kernel module might not have a ksymtab if it does not export any
  /// symbols. In order to quickly decide whether the symbol table is empty, we
  /// remember whether we ever saw ksymtab entries.
  bool has_ksymtab_entries_;

  /// Lookup map name->symbol(s)
  typedef std::unordered_map<std::string, std::vector<elf_symbol_sptr>>
		       name_symbol_map_type;
  name_symbol_map_type name_symbol_map_;

  /// Lookup map addr->symbol
  typedef std::unordered_map<GElf_Addr, elf_symbol_sptr> addr_symbol_map_type;
  addr_symbol_map_type addr_symbol_map_;

  /// Lookup map function entry address -> symbol
  addr_symbol_map_type entry_addr_symbol_map_;

  bool
  load_(Elf* elf_handle, ir::environment* env, symbol_predicate is_suppressed);

  bool
  load_(string_elf_symbols_map_sptr function_symbol_map,
       string_elf_symbols_map_sptr variables_symbol_map);

  GElf_Addr
  setup_symbol_lookup_tables(Elf* elf_handle,
			     GElf_Sym* elf_symbol,
			     const elf_symbol_sptr& symbol_sptr);

  void
  update_function_entry_address_symbol_map(Elf*	     elf_handle,
					   GElf_Sym* native_symbol,
					   const elf_symbol_sptr& symbol_sptr);

  void
  add_alternative_address_lookups(Elf* elf_handle);
};

/// Helper class to allow range-for loops on symtabs for C++11 and later code.
/// It serves as a proxy for the symtab iterator and provides a begin() method
/// without arguments, as required for range-for loops (and possibly other
/// iterator based transformations).
///
/// Example usage:
///
///   for (const auto& symbol : filtered_symtab(tab, filter))
///     {
///       std::cout << symbol->get_name() << "\n";
///     }
///
class filtered_symtab
{
  const symtab&	      tab_;
  const symtab_filter filter_;

public:
  /// Construct the proxy object keeping references to the underlying symtab
  /// and the filter object.
  filtered_symtab(const symtab& tab, const symtab_filter& filter)
    : tab_(tab), filter_(filter)
  {}

  /// Pass through symtab.begin(), but also pass on the filter.
  symtab::const_iterator
  begin() const
  {return tab_.begin(filter_);}

  /// Pass through symtab.end().
  symtab::const_iterator
  end() const
  {return tab_.end();}
};

} // end namespace symtab_reader
} // end namespace abigail

#endif // __ABG_SYMTAB_READER_H__