| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 1 | /* |
| 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 Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 19 | #include <stdlib.h> |
| 20 | #include <base/logging.h> |
| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 21 | #include "environment.h" |
| 22 | #include "read_elf.h" |
| Yabin Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 23 | #include "utils.h" |
| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 24 | |
| 25 | bool SymbolComparator::operator()(const std::unique_ptr<SymbolEntry>& symbol1, |
| 26 | const std::unique_ptr<SymbolEntry>& symbol2) { |
| 27 | return symbol1->addr < symbol2->addr; |
| 28 | } |
| 29 | |
| 30 | const SymbolEntry* DsoEntry::FindSymbol(uint64_t offset_in_dso) { |
| 31 | std::unique_ptr<SymbolEntry> symbol(new SymbolEntry{ |
| Yabin Cui | 9fd3cc1 | 2015-06-25 17:42:23 -0700 | [diff] [blame] | 32 | "", // name |
| 33 | offset_in_dso, // addr |
| 34 | 0, // len |
| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 35 | }); |
| 36 | |
| 37 | auto it = symbols.upper_bound(symbol); |
| 38 | if (it != symbols.begin()) { |
| 39 | --it; |
| 40 | if ((*it)->addr <= offset_in_dso && (*it)->addr + (*it)->len > offset_in_dso) { |
| 41 | return (*it).get(); |
| 42 | } |
| 43 | } |
| 44 | return nullptr; |
| 45 | } |
| 46 | |
| Yabin Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 47 | bool DsoFactory::demangle = true; |
| 48 | |
| 49 | void DsoFactory::SetDemangle(bool demangle) { |
| 50 | DsoFactory::demangle = demangle; |
| 51 | } |
| 52 | |
| 53 | std::string DsoFactory::symfs_dir; |
| 54 | |
| 55 | bool DsoFactory::SetSymFsDir(const std::string& symfs_dir) { |
| 56 | std::string dirname = symfs_dir; |
| Yabin Cui | 638c558 | 2015-07-01 16:16:57 -0700 | [diff] [blame^] | 57 | if (!dirname.empty()) { |
| 58 | if (dirname.back() != '/') { |
| 59 | dirname.push_back('/'); |
| 60 | } |
| 61 | std::vector<std::string> files; |
| 62 | std::vector<std::string> subdirs; |
| 63 | GetEntriesInDir(symfs_dir, &files, &subdirs); |
| 64 | if (files.empty() && subdirs.empty()) { |
| 65 | LOG(ERROR) << "Invalid symfs_dir '" << symfs_dir << "'"; |
| 66 | return false; |
| 67 | } |
| Yabin Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 68 | } |
| 69 | DsoFactory::symfs_dir = dirname; |
| 70 | return true; |
| 71 | } |
| 72 | |
| Yabin Cui | 638c558 | 2015-07-01 16:16:57 -0700 | [diff] [blame^] | 73 | std::unordered_map<std::string, BuildId> DsoFactory::build_id_map; |
| 74 | |
| 75 | void DsoFactory::SetBuildIds(const std::vector<std::pair<std::string, BuildId>>& build_ids) { |
| 76 | std::unordered_map<std::string, BuildId> map; |
| 77 | for (auto& pair : build_ids) { |
| 78 | LOG(DEBUG) << "build_id_map: " << pair.first << ", " << pair.second.ToString(); |
| 79 | map.insert(pair); |
| 80 | } |
| 81 | build_id_map = std::move(map); |
| 82 | } |
| 83 | |
| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 84 | static bool IsKernelFunctionSymbol(const KernelSymbol& symbol) { |
| 85 | return (symbol.type == 'T' || symbol.type == 't' || symbol.type == 'W' || symbol.type == 'w'); |
| 86 | } |
| 87 | |
| 88 | static bool KernelSymbolCallback(const KernelSymbol& kernel_symbol, DsoEntry* dso) { |
| 89 | if (IsKernelFunctionSymbol(kernel_symbol)) { |
| 90 | SymbolEntry* symbol = new SymbolEntry{ |
| Yabin Cui | 9fd3cc1 | 2015-06-25 17:42:23 -0700 | [diff] [blame] | 91 | kernel_symbol.name, // name |
| 92 | kernel_symbol.addr, // addr |
| 93 | 0, // len |
| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 94 | }; |
| 95 | dso->symbols.insert(std::unique_ptr<SymbolEntry>(symbol)); |
| 96 | } |
| 97 | return false; |
| 98 | } |
| 99 | |
| Yabin Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 100 | static void FixupSymbolLength(DsoEntry* dso) { |
| 101 | SymbolEntry* prev_symbol = nullptr; |
| 102 | for (auto& symbol : dso->symbols) { |
| 103 | if (prev_symbol != nullptr && prev_symbol->len == 0) { |
| 104 | prev_symbol->len = symbol->addr - prev_symbol->addr; |
| 105 | } |
| 106 | prev_symbol = symbol.get(); |
| 107 | } |
| 108 | if (prev_symbol != nullptr && prev_symbol->len == 0) { |
| 109 | prev_symbol->len = ULLONG_MAX - prev_symbol->addr; |
| 110 | } |
| 111 | } |
| 112 | |
| Yabin Cui | 9fd3cc1 | 2015-06-25 17:42:23 -0700 | [diff] [blame] | 113 | // TODO: Fix the way to get kernel symbols. See b/22179177. |
| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 114 | std::unique_ptr<DsoEntry> DsoFactory::LoadKernel() { |
| 115 | std::unique_ptr<DsoEntry> dso(new DsoEntry); |
| 116 | dso->path = "[kernel.kallsyms]"; |
| Yabin Cui | 638c558 | 2015-07-01 16:16:57 -0700 | [diff] [blame^] | 117 | BuildId build_id = GetExpectedBuildId(DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID); |
| 118 | BuildId real_build_id; |
| 119 | GetKernelBuildId(&real_build_id); |
| 120 | bool match = (build_id == real_build_id); |
| 121 | LOG(DEBUG) << "check kernel build id (" << (match ? "match" : "mismatch") << "): expected " |
| 122 | << build_id.ToString() << ", real " << real_build_id.ToString(); |
| 123 | if (match) { |
| 124 | ProcessKernelSymbols("/proc/kallsyms", |
| 125 | std::bind(&KernelSymbolCallback, std::placeholders::_1, dso.get())); |
| 126 | } |
| Yabin Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 127 | FixupSymbolLength(dso.get()); |
| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 128 | return dso; |
| 129 | } |
| 130 | |
| 131 | static void ParseSymbolCallback(const ElfFileSymbol& elf_symbol, DsoEntry* dso, |
| 132 | bool (*filter)(const ElfFileSymbol&)) { |
| 133 | if (filter(elf_symbol)) { |
| 134 | SymbolEntry* symbol = new SymbolEntry{ |
| Yabin Cui | 9fd3cc1 | 2015-06-25 17:42:23 -0700 | [diff] [blame] | 135 | elf_symbol.name, // name |
| 136 | elf_symbol.start_in_file, // addr |
| 137 | elf_symbol.len, // len |
| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 138 | }; |
| 139 | dso->symbols.insert(std::unique_ptr<SymbolEntry>(symbol)); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | static bool SymbolFilterForKernelModule(const ElfFileSymbol& elf_symbol) { |
| 144 | // TODO: Parse symbol outside of .text section. |
| 145 | return (elf_symbol.is_func && elf_symbol.is_in_text_section); |
| 146 | } |
| 147 | |
| 148 | std::unique_ptr<DsoEntry> DsoFactory::LoadKernelModule(const std::string& dso_path) { |
| 149 | std::unique_ptr<DsoEntry> dso(new DsoEntry); |
| 150 | dso->path = dso_path; |
| Yabin Cui | 638c558 | 2015-07-01 16:16:57 -0700 | [diff] [blame^] | 151 | BuildId build_id = GetExpectedBuildId(dso_path); |
| 152 | ParseSymbolsFromElfFile(symfs_dir + dso_path, build_id, |
| 153 | std::bind(ParseSymbolCallback, std::placeholders::_1, dso.get(), |
| 154 | SymbolFilterForKernelModule)); |
| Yabin Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 155 | FixupSymbolLength(dso.get()); |
| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 156 | return dso; |
| 157 | } |
| 158 | |
| 159 | static bool SymbolFilterForDso(const ElfFileSymbol& elf_symbol) { |
| Yabin Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 160 | return elf_symbol.is_func || (elf_symbol.is_label && elf_symbol.is_in_text_section); |
| 161 | } |
| 162 | |
| 163 | extern "C" char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status); |
| 164 | |
| 165 | static void DemangleInPlace(std::string* name) { |
| 166 | int status; |
| Yabin Cui | 2c17e0d | 2015-06-11 14:57:21 -0700 | [diff] [blame] | 167 | bool is_linker_symbol = (name->find(linker_prefix) == 0); |
| 168 | const char* mangled_str = name->c_str(); |
| 169 | if (is_linker_symbol) { |
| 170 | mangled_str += linker_prefix.size(); |
| 171 | } |
| 172 | char* demangled_name = __cxa_demangle(mangled_str, nullptr, nullptr, &status); |
| Yabin Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 173 | if (status == 0) { |
| Yabin Cui | 2c17e0d | 2015-06-11 14:57:21 -0700 | [diff] [blame] | 174 | if (is_linker_symbol) { |
| 175 | *name = std::string("[linker]") + demangled_name; |
| 176 | } else { |
| 177 | *name = demangled_name; |
| 178 | } |
| Yabin Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 179 | free(demangled_name); |
| Yabin Cui | 2c17e0d | 2015-06-11 14:57:21 -0700 | [diff] [blame] | 180 | } else if (is_linker_symbol) { |
| 181 | std::string temp = std::string("[linker]") + mangled_str; |
| 182 | *name = std::move(temp); |
| Yabin Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 183 | } |
| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 184 | } |
| 185 | |
| 186 | std::unique_ptr<DsoEntry> DsoFactory::LoadDso(const std::string& dso_path) { |
| 187 | std::unique_ptr<DsoEntry> dso(new DsoEntry); |
| 188 | dso->path = dso_path; |
| Yabin Cui | 638c558 | 2015-07-01 16:16:57 -0700 | [diff] [blame^] | 189 | BuildId build_id = GetExpectedBuildId(dso_path); |
| 190 | ParseSymbolsFromElfFile( |
| 191 | symfs_dir + dso_path, build_id, |
| 192 | std::bind(ParseSymbolCallback, std::placeholders::_1, dso.get(), SymbolFilterForDso)); |
| Yabin Cui | b378355 | 2015-06-11 11:15:42 -0700 | [diff] [blame] | 193 | if (demangle) { |
| 194 | for (auto& symbol : dso->symbols) { |
| 195 | DemangleInPlace(&symbol->name); |
| 196 | } |
| 197 | } |
| 198 | FixupSymbolLength(dso.get()); |
| Yabin Cui | ec12ed9 | 2015-06-08 10:38:10 -0700 | [diff] [blame] | 199 | return dso; |
| 200 | } |
| Yabin Cui | 638c558 | 2015-07-01 16:16:57 -0700 | [diff] [blame^] | 201 | |
| 202 | BuildId DsoFactory::GetExpectedBuildId(const std::string& filename) { |
| 203 | auto it = build_id_map.find(filename); |
| 204 | if (it != build_id_map.end()) { |
| 205 | return it->second; |
| 206 | } |
| 207 | return BuildId(); |
| 208 | } |