blob: 6f895259814b8dedb80fbdb73bbad2e6bb399265 [file] [log] [blame]
Yabin Cuiec12ed92015-06-08 10:38:10 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
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 "dso.h"
18
Yabin Cuib3783552015-06-11 11:15:42 -070019#include <stdlib.h>
Yabin Cuicc2e59e2015-08-21 14:23:43 -070020#include <string.h>
Yabin Cuic8485602015-08-20 15:04:39 -070021
Yabin Cuicc2e59e2015-08-21 14:23:43 -070022#include <algorithm>
Yabin Cuic8485602015-08-20 15:04:39 -070023#include <limits>
Yabin Cuicc2e59e2015-08-21 14:23:43 -070024#include <vector>
Yabin Cuic8485602015-08-20 15:04:39 -070025
Yabin Cuib3783552015-06-11 11:15:42 -070026#include <base/logging.h>
Yabin Cuic8485602015-08-20 15:04:39 -070027
Yabin Cuiec12ed92015-06-08 10:38:10 -070028#include "environment.h"
29#include "read_elf.h"
Yabin Cuib3783552015-06-11 11:15:42 -070030#include "utils.h"
Yabin Cuiec12ed92015-06-08 10:38:10 -070031
Yabin Cuicc2e59e2015-08-21 14:23:43 -070032static OneTimeFreeAllocator symbol_name_allocator;
33
34Symbol::Symbol(const std::string& name, uint64_t addr, uint64_t len)
35 : addr(addr),
36 len(len),
37 name_(symbol_name_allocator.AllocateString(name)),
38 demangled_name_(nullptr) {
Yabin Cuib10a8fb2015-08-18 16:32:18 -070039}
40
Yabin Cuicc2e59e2015-08-21 14:23:43 -070041const char* Symbol::DemangledName() const {
42 if (demangled_name_ == nullptr) {
43 const std::string s = Dso::Demangle(name_);
44 if (s == name_) {
45 demangled_name_ = name_;
46 } else {
47 demangled_name_ = symbol_name_allocator.AllocateString(s);
48 }
49 }
50 return demangled_name_;
Yabin Cuiec12ed92015-06-08 10:38:10 -070051}
52
Yabin Cuic8485602015-08-20 15:04:39 -070053bool Dso::demangle_ = true;
54std::string Dso::symfs_dir_;
55std::string Dso::vmlinux_;
56std::unordered_map<std::string, BuildId> Dso::build_id_map_;
Yabin Cuicc2e59e2015-08-21 14:23:43 -070057size_t Dso::dso_count_;
Yabin Cuiba50c4b2015-07-21 11:24:48 -070058
Yabin Cuic8485602015-08-20 15:04:39 -070059void Dso::SetDemangle(bool demangle) {
Yabin Cui39d3cae2015-07-13 16:23:13 -070060 demangle_ = demangle;
61}
Yabin Cuib3783552015-06-11 11:15:42 -070062
Yabin Cuib10a8fb2015-08-18 16:32:18 -070063extern "C" char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status);
64
Yabin Cuic8485602015-08-20 15:04:39 -070065std::string Dso::Demangle(const std::string& name) {
Yabin Cuib10a8fb2015-08-18 16:32:18 -070066 if (!demangle_) {
67 return name;
68 }
69 int status;
70 bool is_linker_symbol = (name.find(linker_prefix) == 0);
71 const char* mangled_str = name.c_str();
72 if (is_linker_symbol) {
73 mangled_str += linker_prefix.size();
74 }
75 std::string result = name;
76 char* demangled_name = __cxa_demangle(mangled_str, nullptr, nullptr, &status);
77 if (status == 0) {
78 if (is_linker_symbol) {
79 result = std::string("[linker]") + demangled_name;
80 } else {
81 result = demangled_name;
82 }
83 free(demangled_name);
84 } else if (is_linker_symbol) {
85 result = std::string("[linker]") + mangled_str;
86 }
87 return result;
88}
89
Yabin Cuic8485602015-08-20 15:04:39 -070090bool Dso::SetSymFsDir(const std::string& symfs_dir) {
91 std::string dirname = symfs_dir;
92 if (!dirname.empty()) {
93 if (dirname.back() != '/') {
94 dirname.push_back('/');
95 }
96 std::vector<std::string> files;
97 std::vector<std::string> subdirs;
98 GetEntriesInDir(symfs_dir, &files, &subdirs);
99 if (files.empty() && subdirs.empty()) {
100 LOG(ERROR) << "Invalid symfs_dir '" << symfs_dir << "'";
Yabin Cuiba50c4b2015-07-21 11:24:48 -0700101 return false;
Yabin Cuic8485602015-08-20 15:04:39 -0700102 }
Yabin Cuiba50c4b2015-07-21 11:24:48 -0700103 }
Yabin Cuic8485602015-08-20 15:04:39 -0700104 symfs_dir_ = dirname;
105 return true;
106}
107
108void Dso::SetVmlinux(const std::string& vmlinux) {
109 vmlinux_ = vmlinux;
110}
111
112void Dso::SetBuildIds(const std::vector<std::pair<std::string, BuildId>>& build_ids) {
113 std::unordered_map<std::string, BuildId> map;
114 for (auto& pair : build_ids) {
115 LOG(DEBUG) << "build_id_map: " << pair.first << ", " << pair.second.ToString();
116 map.insert(pair);
117 }
118 build_id_map_ = std::move(map);
119}
120
121BuildId Dso::GetExpectedBuildId(const std::string& filename) {
122 auto it = build_id_map_.find(filename);
123 if (it != build_id_map_.end()) {
124 return it->second;
125 }
126 return BuildId();
127}
128
129std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path) {
130 std::string path = dso_path;
131 if (dso_type == DSO_KERNEL) {
132 path = "[kernel.kallsyms]";
133 }
134 return std::unique_ptr<Dso>(new Dso(dso_type, path));
135}
136
Yabin Cui547c60e2015-10-12 16:56:05 -0700137Dso::Dso(DsoType type, const std::string& path)
138 : type_(type), path_(path), min_vaddr_(std::numeric_limits<uint64_t>::max()), is_loaded_(false) {
Yabin Cuicc2e59e2015-08-21 14:23:43 -0700139 dso_count_++;
Yabin Cuic8485602015-08-20 15:04:39 -0700140}
141
Yabin Cuicc2e59e2015-08-21 14:23:43 -0700142Dso::~Dso() {
143 if (--dso_count_ == 0) {
144 symbol_name_allocator.Clear();
145 }
146}
147
148struct SymbolComparator {
149 bool operator()(const Symbol& symbol1, const Symbol& symbol2) {
150 return symbol1.addr < symbol2.addr;
151 }
152};
153
Yabin Cui3c8c2132015-08-13 20:30:20 -0700154std::string Dso::GetAccessiblePath() const {
155 return symfs_dir_ + path_;
156}
157
Yabin Cui547c60e2015-10-12 16:56:05 -0700158const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) {
Yabin Cuic8485602015-08-20 15:04:39 -0700159 if (!is_loaded_) {
160 is_loaded_ = true;
161 if (!Load()) {
162 LOG(DEBUG) << "failed to load dso: " << path_;
163 return nullptr;
164 }
165 }
Yabin Cuic8485602015-08-20 15:04:39 -0700166
Yabin Cui547c60e2015-10-12 16:56:05 -0700167 auto it = std::upper_bound(symbols_.begin(), symbols_.end(), Symbol("", vaddr_in_dso, 0),
Yabin Cuicc2e59e2015-08-21 14:23:43 -0700168 SymbolComparator());
Yabin Cuic8485602015-08-20 15:04:39 -0700169 if (it != symbols_.begin()) {
170 --it;
Yabin Cui547c60e2015-10-12 16:56:05 -0700171 if (it->addr <= vaddr_in_dso && it->addr + it->len > vaddr_in_dso) {
Yabin Cuicc2e59e2015-08-21 14:23:43 -0700172 return &*it;
Yabin Cuic8485602015-08-20 15:04:39 -0700173 }
174 }
175 return nullptr;
176}
177
Yabin Cui547c60e2015-10-12 16:56:05 -0700178uint64_t Dso::MinVirtualAddress() {
179 if (min_vaddr_ == std::numeric_limits<uint64_t>::max()) {
180 min_vaddr_ = 0;
181 if (type_ == DSO_ELF_FILE) {
182 BuildId build_id = GetExpectedBuildId(GetAccessiblePath());
183
184 uint64_t addr;
185 if (ReadMinExecutableVirtualAddressFromElfFile(GetAccessiblePath(), build_id, &addr)) {
186 min_vaddr_ = addr;
187 }
188 }
189 }
190 return min_vaddr_;
191}
192
Yabin Cuic8485602015-08-20 15:04:39 -0700193bool Dso::Load() {
194 bool result = false;
195 switch (type_) {
196 case DSO_KERNEL:
197 result = LoadKernel();
198 break;
199 case DSO_KERNEL_MODULE:
200 result = LoadKernelModule();
201 break;
202 case DSO_ELF_FILE:
203 result = LoadElfFile();
204 break;
205 }
206 if (result) {
Yabin Cuicc2e59e2015-08-21 14:23:43 -0700207 std::sort(symbols_.begin(), symbols_.end(), SymbolComparator());
Yabin Cuic8485602015-08-20 15:04:39 -0700208 FixupSymbolLength();
209 }
210 return result;
Yabin Cuiba50c4b2015-07-21 11:24:48 -0700211}
212
Yabin Cuiec12ed92015-06-08 10:38:10 -0700213static bool IsKernelFunctionSymbol(const KernelSymbol& symbol) {
214 return (symbol.type == 'T' || symbol.type == 't' || symbol.type == 'W' || symbol.type == 'w');
215}
216
Yabin Cuic8485602015-08-20 15:04:39 -0700217bool Dso::KernelSymbolCallback(const KernelSymbol& kernel_symbol, Dso* dso) {
Yabin Cuiec12ed92015-06-08 10:38:10 -0700218 if (IsKernelFunctionSymbol(kernel_symbol)) {
Yabin Cuic8485602015-08-20 15:04:39 -0700219 dso->InsertSymbol(Symbol(kernel_symbol.name, kernel_symbol.addr, 0));
Yabin Cuiec12ed92015-06-08 10:38:10 -0700220 }
221 return false;
222}
223
Yabin Cuic8485602015-08-20 15:04:39 -0700224void Dso::VmlinuxSymbolCallback(const ElfFileSymbol& elf_symbol, Dso* dso) {
Yabin Cui39d3cae2015-07-13 16:23:13 -0700225 if (elf_symbol.is_func) {
Yabin Cuic8485602015-08-20 15:04:39 -0700226 dso->InsertSymbol(Symbol(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len));
Yabin Cui39d3cae2015-07-13 16:23:13 -0700227 }
228}
229
Yabin Cuic8485602015-08-20 15:04:39 -0700230bool Dso::LoadKernel() {
Yabin Cui638c5582015-07-01 16:16:57 -0700231 BuildId build_id = GetExpectedBuildId(DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID);
Yabin Cui39d3cae2015-07-13 16:23:13 -0700232 if (!vmlinux_.empty()) {
233 ParseSymbolsFromElfFile(vmlinux_, build_id,
Yabin Cuic8485602015-08-20 15:04:39 -0700234 std::bind(VmlinuxSymbolCallback, std::placeholders::_1, this));
Yabin Cui39d3cae2015-07-13 16:23:13 -0700235 } else {
Yabin Cui04c70a62015-08-04 14:48:39 -0700236 if (!build_id.IsEmpty()) {
237 BuildId real_build_id;
238 GetKernelBuildId(&real_build_id);
239 bool match = (build_id == real_build_id);
240 LOG(DEBUG) << "check kernel build id (" << (match ? "match" : "mismatch") << "): expected "
241 << build_id.ToString() << ", real " << real_build_id.ToString();
242 if (!match) {
243 return false;
244 }
Yabin Cui39d3cae2015-07-13 16:23:13 -0700245 }
Yabin Cui8a52e972015-10-01 11:32:44 -0700246
Yabin Cui04c70a62015-08-04 14:48:39 -0700247 ProcessKernelSymbols("/proc/kallsyms",
Yabin Cuic8485602015-08-20 15:04:39 -0700248 std::bind(&KernelSymbolCallback, std::placeholders::_1, this));
Yabin Cui8a52e972015-10-01 11:32:44 -0700249 bool allZero = true;
250 for (auto& symbol : symbols_) {
251 if (symbol.addr != 0) {
252 allZero = false;
253 break;
254 }
255 }
256 if (allZero) {
257 LOG(WARNING) << "Symbol addresses in /proc/kallsyms are all zero. Check "
258 "/proc/sys/kernel/kptr_restrict if possible.";
259 symbols_.clear();
260 return false;
261 }
Yabin Cui638c5582015-07-01 16:16:57 -0700262 }
Yabin Cui04c70a62015-08-04 14:48:39 -0700263 return true;
Yabin Cuiec12ed92015-06-08 10:38:10 -0700264}
265
Yabin Cuic8485602015-08-20 15:04:39 -0700266void Dso::ElfFileSymbolCallback(const ElfFileSymbol& elf_symbol, Dso* dso,
Yabin Cuiec12ed92015-06-08 10:38:10 -0700267 bool (*filter)(const ElfFileSymbol&)) {
268 if (filter(elf_symbol)) {
Yabin Cui547c60e2015-10-12 16:56:05 -0700269 dso->InsertSymbol(Symbol(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len));
Yabin Cuiec12ed92015-06-08 10:38:10 -0700270 }
271}
272
273static bool SymbolFilterForKernelModule(const ElfFileSymbol& elf_symbol) {
274 // TODO: Parse symbol outside of .text section.
275 return (elf_symbol.is_func && elf_symbol.is_in_text_section);
276}
277
Yabin Cuic8485602015-08-20 15:04:39 -0700278bool Dso::LoadKernelModule() {
279 BuildId build_id = GetExpectedBuildId(path_);
Yabin Cuiba50c4b2015-07-21 11:24:48 -0700280 ParseSymbolsFromElfFile(
Yabin Cuic8485602015-08-20 15:04:39 -0700281 symfs_dir_ + path_, build_id,
282 std::bind(ElfFileSymbolCallback, std::placeholders::_1, this, SymbolFilterForKernelModule));
Yabin Cui04c70a62015-08-04 14:48:39 -0700283 return true;
Yabin Cuiec12ed92015-06-08 10:38:10 -0700284}
285
286static bool SymbolFilterForDso(const ElfFileSymbol& elf_symbol) {
Yabin Cuib3783552015-06-11 11:15:42 -0700287 return elf_symbol.is_func || (elf_symbol.is_label && elf_symbol.is_in_text_section);
288}
289
Yabin Cuic8485602015-08-20 15:04:39 -0700290bool Dso::LoadElfFile() {
Yabin Cui547c60e2015-10-12 16:56:05 -0700291 bool loaded = false;
292 BuildId build_id = GetExpectedBuildId(GetAccessiblePath());
293
294 if (symfs_dir_.empty()) {
295 // Linux host can store debug shared libraries in /usr/lib/debug.
296 loaded = ParseSymbolsFromElfFile(
297 "/usr/lib/debug" + path_, build_id,
298 std::bind(ElfFileSymbolCallback, std::placeholders::_1, this, SymbolFilterForDso));
299 }
300 if (!loaded) {
301 loaded = ParseSymbolsFromElfFile(
302 GetAccessiblePath(), build_id,
303 std::bind(ElfFileSymbolCallback, std::placeholders::_1, this, SymbolFilterForDso));
304 }
305 return loaded;
Yabin Cuiec12ed92015-06-08 10:38:10 -0700306}
Yabin Cui638c5582015-07-01 16:16:57 -0700307
Yabin Cuic8485602015-08-20 15:04:39 -0700308void Dso::InsertSymbol(const Symbol& symbol) {
Yabin Cuicc2e59e2015-08-21 14:23:43 -0700309 symbols_.push_back(symbol);
Yabin Cuic8485602015-08-20 15:04:39 -0700310}
311
312void Dso::FixupSymbolLength() {
313 Symbol* prev_symbol = nullptr;
314 for (auto& symbol : symbols_) {
315 if (prev_symbol != nullptr && prev_symbol->len == 0) {
Yabin Cuicc2e59e2015-08-21 14:23:43 -0700316 prev_symbol->len = symbol.addr - prev_symbol->addr;
Yabin Cuic8485602015-08-20 15:04:39 -0700317 }
Yabin Cuicc2e59e2015-08-21 14:23:43 -0700318 prev_symbol = &symbol;
Yabin Cui638c5582015-07-01 16:16:57 -0700319 }
Yabin Cuic8485602015-08-20 15:04:39 -0700320 if (prev_symbol != nullptr && prev_symbol->len == 0) {
321 prev_symbol->len = std::numeric_limits<unsigned long long>::max() - prev_symbol->addr;
322 }
Yabin Cui638c5582015-07-01 16:16:57 -0700323}