1/*
2 * Copyright 2014-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <folly/experimental/symbolizer/ElfCache.h>
18
19#include <link.h>
20
21/*
22 * This is declared in `link.h' on Linux platforms, but apparently not on the
23 * Mac version of the file. It's harmless to declare again, in any case.
24 *
25 * Note that declaring it with `extern "C"` results in linkage conflicts.
26 */
27extern struct r_debug _r_debug;
28
29namespace folly {
30namespace symbolizer {
31
32size_t countLoadedElfFiles() {
33 // _r_debug synchronization is... lacking to say the least. It's
34 // meant as an aid for debuggers and synchronization is done by
35 // calling dl_debug_state() which debuggers are supposed to
36 // intercept by setting a breakpoint on.
37
38 // Can't really do that here, so we apply the hope-and-pray strategy.
39 if (_r_debug.r_version != 1 || _r_debug.r_state != r_debug::RT_CONSISTENT) {
40 // computo ergo sum
41 return 1;
42 }
43
44 // r_map -> head of a linked list of 'link_map_t' entries,
45 // one per ELF 'binary' in the process address space.
46 size_t count = 0;
47 for (auto lmap = _r_debug.r_map; lmap != nullptr; lmap = lmap->l_next) {
48 ++count;
49 }
50 return count;
51}
52
53SignalSafeElfCache::SignalSafeElfCache(size_t capacity) {
54 map_.reserve(capacity);
55 slots_.reserve(capacity);
56
57 // Preallocate
58 for (size_t i = 0; i < capacity; ++i) {
59 slots_.push_back(std::make_shared<ElfFile>());
60 }
61}
62
63std::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) {
64 if (p.size() > Path::kMaxSize) {
65 return nullptr;
66 }
67
68 scratchpad_.assign(p);
69 auto pos = map_.find(scratchpad_);
70 if (pos != map_.end()) {
71 return slots_[pos->second];
72 }
73
74 size_t n = map_.size();
75 if (n >= slots_.size()) {
76 DCHECK_EQ(map_.size(), slots_.size());
77 return nullptr;
78 }
79
80 auto& f = slots_[n];
81
82 const char* msg = "";
83 int r = f->openAndFollow(scratchpad_.data(), true, &msg);
84 if (r != ElfFile::kSuccess) {
85 return nullptr;
86 }
87
88 map_[scratchpad_] = n;
89 return f;
90}
91
92ElfCache::ElfCache(size_t capacity) : capacity_(capacity) {}
93
94std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) {
95 std::lock_guard<std::mutex> lock(mutex_);
96
97 auto pos = files_.find(p);
98 if (pos != files_.end()) {
99 // Found, move to back (MRU)
100 auto& entry = pos->second;
101 lruList_.erase(lruList_.iterator_to(*entry));
102 lruList_.push_back(*entry);
103 return filePtr(entry);
104 }
105
106 auto entry = std::make_shared<Entry>();
107 entry->path = p.str();
108 auto& path = entry->path;
109
110 // No negative caching
111 const char* msg = "";
112 int r = entry->file.openAndFollow(path.c_str(), true, &msg);
113 if (r != ElfFile::kSuccess) {
114 return nullptr;
115 }
116
117 if (files_.size() == capacity_) {
118 auto& e = lruList_.front();
119 lruList_.pop_front();
120 files_.erase(e.path);
121 }
122
123 files_.emplace(entry->path, entry);
124 lruList_.push_back(*entry);
125
126 return filePtr(entry);
127}
128
129std::shared_ptr<ElfFile> ElfCache::filePtr(const std::shared_ptr<Entry>& e) {
130 // share ownership
131 return std::shared_ptr<ElfFile>(e, &e->file);
132}
133} // namespace symbolizer
134} // namespace folly
135