Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "PlatformMacros.h" |
michael@0 | 7 | #include "nsAutoPtr.h" |
michael@0 | 8 | |
michael@0 | 9 | #if !defined(SPS_OS_windows) |
michael@0 | 10 | # include "common/module.h" |
michael@0 | 11 | # include "processor/cfi_frame_info.h" |
michael@0 | 12 | #endif |
michael@0 | 13 | #include "google_breakpad/processor/code_module.h" |
michael@0 | 14 | #include "google_breakpad/processor/code_modules.h" |
michael@0 | 15 | #include "google_breakpad/processor/stack_frame.h" |
michael@0 | 16 | #include "common/logging.h" |
michael@0 | 17 | |
michael@0 | 18 | #if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \ |
michael@0 | 19 | || defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android) |
michael@0 | 20 | # include "common/linux/dump_symbols.h" |
michael@0 | 21 | #elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) |
michael@0 | 22 | # include "shim_mac_dump_syms.h" |
michael@0 | 23 | #elif defined(SPS_OS_windows) |
michael@0 | 24 | /* This is all stubbed out anyway, so don't do anything. */ |
michael@0 | 25 | #else |
michael@0 | 26 | # error "Unknown platform" |
michael@0 | 27 | #endif |
michael@0 | 28 | |
michael@0 | 29 | #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) |
michael@0 | 30 | # include "mozilla/Types.h" |
michael@0 | 31 | # include "ElfLoader.h" |
michael@0 | 32 | # include <dlfcn.h> |
michael@0 | 33 | # include <sys/mman.h> |
michael@0 | 34 | # include "nsString.h" |
michael@0 | 35 | # include "nsDirectoryServiceUtils.h" |
michael@0 | 36 | # include "nsDirectoryServiceDefs.h" |
michael@0 | 37 | # include <sys/stat.h> |
michael@0 | 38 | # include <fcntl.h> |
michael@0 | 39 | #endif |
michael@0 | 40 | |
michael@0 | 41 | #include "local_debug_info_symbolizer.h" |
michael@0 | 42 | |
michael@0 | 43 | namespace google_breakpad { |
michael@0 | 44 | |
michael@0 | 45 | LocalDebugInfoSymbolizer::~LocalDebugInfoSymbolizer() { |
michael@0 | 46 | # if !defined(SPS_OS_windows) |
michael@0 | 47 | for (SymbolMap::iterator it = symbols_.begin(); |
michael@0 | 48 | it != symbols_.end(); |
michael@0 | 49 | ++it) { |
michael@0 | 50 | delete it->second; |
michael@0 | 51 | } |
michael@0 | 52 | # endif |
michael@0 | 53 | } |
michael@0 | 54 | |
michael@0 | 55 | #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) |
michael@0 | 56 | |
michael@0 | 57 | // Find out where the installation's lib directory is, since we'll |
michael@0 | 58 | // have to look in there to get hold of libmozglue.so. Returned |
michael@0 | 59 | // C string is heap allocated and the caller must deallocate it. |
michael@0 | 60 | static char* get_installation_lib_dir ( void ) |
michael@0 | 61 | { |
michael@0 | 62 | nsCOMPtr<nsIProperties> |
michael@0 | 63 | directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); |
michael@0 | 64 | if (!directoryService) return NULL; |
michael@0 | 65 | nsCOMPtr<nsIFile> greDir; |
michael@0 | 66 | nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), |
michael@0 | 67 | getter_AddRefs(greDir)); |
michael@0 | 68 | if (NS_FAILED(rv)) return NULL; |
michael@0 | 69 | nsCString path; |
michael@0 | 70 | rv = greDir->GetNativePath(path); |
michael@0 | 71 | if (NS_FAILED(rv)) return NULL; |
michael@0 | 72 | return strdup(path.get()); |
michael@0 | 73 | } |
michael@0 | 74 | |
michael@0 | 75 | // Read symbol data from a file on Android. OBJ_FILENAME has |
michael@0 | 76 | // three possible cases: |
michael@0 | 77 | // |
michael@0 | 78 | // (1) /foo/bar/xyzzy/blah.apk!/libwurble.so |
michael@0 | 79 | // We hand it as-is to faulty.lib and let it fish the relevant |
michael@0 | 80 | // bits out of the APK. |
michael@0 | 81 | // |
michael@0 | 82 | // (2) libmozglue.so |
michael@0 | 83 | // This is part of the Fennec installation, but is not in the |
michael@0 | 84 | // APK. Instead we have to figure out the installation path |
michael@0 | 85 | // and look for it there. Because of faulty.lib limitations, |
michael@0 | 86 | // we have to use regular open/mmap instead of faulty.lib. |
michael@0 | 87 | // |
michael@0 | 88 | // (3) libanythingelse.so |
michael@0 | 89 | // faulty.lib assumes this is a system library, and prepends |
michael@0 | 90 | // "/system/lib/" to the path. So as in (1), we can give it |
michael@0 | 91 | // as-is to faulty.lib. |
michael@0 | 92 | // |
michael@0 | 93 | // Hence only (2) requires special-casing here. |
michael@0 | 94 | // |
michael@0 | 95 | static bool ReadSymbolData_ANDROID(const string& obj_filename, |
michael@0 | 96 | const std::vector<string>& debug_dirs, |
michael@0 | 97 | SymbolData symbol_data, |
michael@0 | 98 | Module** module) |
michael@0 | 99 | { |
michael@0 | 100 | string obj_file_to_use = obj_filename; |
michael@0 | 101 | |
michael@0 | 102 | // Do (2) in the comment above. |
michael@0 | 103 | if (obj_file_to_use == "libmozglue.so") { |
michael@0 | 104 | char* libdir = get_installation_lib_dir(); |
michael@0 | 105 | if (libdir) { |
michael@0 | 106 | obj_file_to_use = string(libdir) + "/lib/" + obj_file_to_use; |
michael@0 | 107 | free(libdir); |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | // Use regular open/mmap here because of faulty.lib limitations |
michael@0 | 111 | int fd = open(obj_file_to_use.c_str(), O_RDONLY); |
michael@0 | 112 | if (fd == -1) { |
michael@0 | 113 | BPLOG(INFO) << "ReadSymbolData_APK: Failed to open \'" |
michael@0 | 114 | << obj_file_to_use << "\'"; |
michael@0 | 115 | return false; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | struct stat st; |
michael@0 | 119 | if (fstat(fd, &st) != 0) { |
michael@0 | 120 | close(fd); |
michael@0 | 121 | BPLOG(INFO) << "ReadSymbolData_APK: Failed to fstat \'" |
michael@0 | 122 | << obj_file_to_use << "\'"; |
michael@0 | 123 | return false; |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | void* image = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0); |
michael@0 | 127 | if (image == MAP_FAILED) { |
michael@0 | 128 | close(fd); |
michael@0 | 129 | BPLOG(INFO) << "ReadSymbolData_APK: Failed to mmap \'" |
michael@0 | 130 | << obj_file_to_use << "\'"; |
michael@0 | 131 | return false; |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | bool ok = ReadSymbolDataInternal((const uint8_t*)image, |
michael@0 | 135 | obj_file_to_use, debug_dirs, |
michael@0 | 136 | symbol_data, module); |
michael@0 | 137 | munmap(image, st.st_size); |
michael@0 | 138 | close(fd); |
michael@0 | 139 | return ok; |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | // Regardless of whether the file is inside an APK or not, we ask |
michael@0 | 143 | // faulty.lib to map it, then call ReadSymbolDataInternal, then |
michael@0 | 144 | // unmap and dlclose it. |
michael@0 | 145 | void* hdl = dlopen(obj_file_to_use.c_str(), RTLD_GLOBAL | RTLD_LAZY); |
michael@0 | 146 | if (!hdl) { |
michael@0 | 147 | BPLOG(INFO) << "ReadSymbolData_APK: Failed to get handle for ELF file \'" |
michael@0 | 148 | << obj_file_to_use << "\'"; |
michael@0 | 149 | return false; |
michael@0 | 150 | } |
michael@0 | 151 | |
michael@0 | 152 | size_t sz = __dl_get_mappable_length(hdl); |
michael@0 | 153 | if (sz == 0) { |
michael@0 | 154 | dlclose(hdl); |
michael@0 | 155 | BPLOG(INFO) << "ReadSymbolData_APK: Unable to get size for ELF file \'" |
michael@0 | 156 | << obj_file_to_use << "\'"; |
michael@0 | 157 | return false; |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | void* image = __dl_mmap(hdl, NULL, sz, 0); |
michael@0 | 161 | if (image == MAP_FAILED) { |
michael@0 | 162 | dlclose(hdl); |
michael@0 | 163 | BPLOG(INFO) << "ReadSymbolData_APK: Failed to mmap ELF file \'" |
michael@0 | 164 | << obj_file_to_use << "\'"; |
michael@0 | 165 | return false; |
michael@0 | 166 | } |
michael@0 | 167 | |
michael@0 | 168 | bool ok = ReadSymbolDataInternal((const uint8_t*)image, |
michael@0 | 169 | obj_file_to_use, debug_dirs, |
michael@0 | 170 | symbol_data, module); |
michael@0 | 171 | __dl_munmap(hdl, image, sz); |
michael@0 | 172 | dlclose(hdl); |
michael@0 | 173 | |
michael@0 | 174 | return ok; |
michael@0 | 175 | } |
michael@0 | 176 | #endif /* defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) */ |
michael@0 | 177 | |
michael@0 | 178 | |
michael@0 | 179 | StackFrameSymbolizer::SymbolizerResult |
michael@0 | 180 | LocalDebugInfoSymbolizer::FillSourceLineInfo(const CodeModules* modules, |
michael@0 | 181 | const SystemInfo* system_info, |
michael@0 | 182 | StackFrame* frame) { |
michael@0 | 183 | if (!modules) { |
michael@0 | 184 | return kError; |
michael@0 | 185 | } |
michael@0 | 186 | const CodeModule* module = modules->GetModuleForAddress(frame->instruction); |
michael@0 | 187 | if (!module) { |
michael@0 | 188 | return kError; |
michael@0 | 189 | } |
michael@0 | 190 | frame->module = module; |
michael@0 | 191 | |
michael@0 | 192 | # if !defined(SPS_OS_windows) |
michael@0 | 193 | Module* debug_info_module = NULL; |
michael@0 | 194 | SymbolMap::const_iterator it = symbols_.find(module->code_file()); |
michael@0 | 195 | if (it == symbols_.end()) { |
michael@0 | 196 | if (no_symbol_modules_.find(module->code_file()) != |
michael@0 | 197 | no_symbol_modules_.end()) { |
michael@0 | 198 | return kNoError; |
michael@0 | 199 | } |
michael@0 | 200 | |
michael@0 | 201 | bool ok = false; |
michael@0 | 202 | # if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) |
michael@0 | 203 | ok = ReadSymbolData_ANDROID(module->code_file(), debug_dirs_, |
michael@0 | 204 | ONLY_CFI, &debug_info_module); |
michael@0 | 205 | # elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) |
michael@0 | 206 | ok = ReadSymbolData_DARWIN(module->code_file(), debug_dirs_, |
michael@0 | 207 | ONLY_CFI, &debug_info_module); |
michael@0 | 208 | # else |
michael@0 | 209 | ok = ReadSymbolData(module->code_file(), debug_dirs_, |
michael@0 | 210 | ONLY_CFI, &debug_info_module); |
michael@0 | 211 | # endif |
michael@0 | 212 | |
michael@0 | 213 | if (!ok) { |
michael@0 | 214 | if (debug_info_module) |
michael@0 | 215 | delete debug_info_module; |
michael@0 | 216 | no_symbol_modules_.insert(module->code_file()); |
michael@0 | 217 | return kNoError; |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | symbols_[module->code_file()] = debug_info_module; |
michael@0 | 221 | } else { |
michael@0 | 222 | debug_info_module = it->second; |
michael@0 | 223 | } |
michael@0 | 224 | |
michael@0 | 225 | u_int64_t address = frame->instruction - frame->module->base_address(); |
michael@0 | 226 | Module::Function* function = |
michael@0 | 227 | debug_info_module->FindFunctionByAddress(address); |
michael@0 | 228 | if (function) { |
michael@0 | 229 | frame->function_name = function->name; |
michael@0 | 230 | //TODO: line info: function->lines |
michael@0 | 231 | } else { |
michael@0 | 232 | Module::Extern* ex = debug_info_module->FindExternByAddress(address); |
michael@0 | 233 | if (ex) { |
michael@0 | 234 | frame->function_name = ex->name; |
michael@0 | 235 | } |
michael@0 | 236 | } |
michael@0 | 237 | # endif /* !defined(SPS_OS_windows) */ |
michael@0 | 238 | return kNoError; |
michael@0 | 239 | } |
michael@0 | 240 | |
michael@0 | 241 | |
michael@0 | 242 | WindowsFrameInfo* LocalDebugInfoSymbolizer::FindWindowsFrameInfo( |
michael@0 | 243 | const StackFrame* frame) { |
michael@0 | 244 | // Not currently implemented, would require PDBSourceLineWriter to |
michael@0 | 245 | // implement an API to return symbol data. |
michael@0 | 246 | return NULL; |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | #if !defined(SPS_OS_windows) |
michael@0 | 250 | // Taken wholesale from source_line_resolver_base.cc |
michael@0 | 251 | bool ParseCFIRuleSet(const string& rule_set, CFIFrameInfo* frame_info) { |
michael@0 | 252 | CFIFrameInfoParseHandler handler(frame_info); |
michael@0 | 253 | CFIRuleParser parser(&handler); |
michael@0 | 254 | return parser.Parse(rule_set); |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | static void ConvertCFI(const UniqueString* name, const Module::Expr& rule, |
michael@0 | 258 | CFIFrameInfo* frame_info) { |
michael@0 | 259 | if (name == ustr__ZDcfa()) frame_info->SetCFARule(rule); |
michael@0 | 260 | else if (name == ustr__ZDra()) frame_info->SetRARule(rule); |
michael@0 | 261 | else frame_info->SetRegisterRule(name, rule); |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | |
michael@0 | 265 | static void ConvertCFI(const Module::RuleMap& rule_map, |
michael@0 | 266 | CFIFrameInfo* frame_info) { |
michael@0 | 267 | for (Module::RuleMap::const_iterator it = rule_map.begin(); |
michael@0 | 268 | it != rule_map.end(); ++it) { |
michael@0 | 269 | ConvertCFI(it->first, it->second, frame_info); |
michael@0 | 270 | } |
michael@0 | 271 | } |
michael@0 | 272 | #endif |
michael@0 | 273 | |
michael@0 | 274 | CFIFrameInfo* LocalDebugInfoSymbolizer::FindCFIFrameInfo( |
michael@0 | 275 | const StackFrame* frame) { |
michael@0 | 276 | #if defined(SPS_OS_windows) |
michael@0 | 277 | return NULL; |
michael@0 | 278 | #else |
michael@0 | 279 | if (!frame || !frame->module) return NULL; |
michael@0 | 280 | |
michael@0 | 281 | SymbolMap::const_iterator it = symbols_.find(frame->module->code_file()); |
michael@0 | 282 | if (it == symbols_.end()) return NULL; |
michael@0 | 283 | |
michael@0 | 284 | Module* module = it->second; |
michael@0 | 285 | u_int64_t address = frame->instruction - frame->module->base_address(); |
michael@0 | 286 | Module::StackFrameEntry* entry = |
michael@0 | 287 | module->FindStackFrameEntryByAddress(address); |
michael@0 | 288 | if (!entry) |
michael@0 | 289 | return NULL; |
michael@0 | 290 | |
michael@0 | 291 | //TODO: can we cache this data per-address? does that make sense? |
michael@0 | 292 | // TODO: Maybe this should use google_breakpad::scoped_ptr, since we're in |
michael@0 | 293 | // "namespace google_breakpad". Not using scoped_ptr currently, because its |
michael@0 | 294 | // header triggers build warnings -- see bug 855010. |
michael@0 | 295 | nsAutoPtr<CFIFrameInfo> rules(new CFIFrameInfo()); |
michael@0 | 296 | ConvertCFI(entry->initial_rules, rules); |
michael@0 | 297 | for (Module::RuleChangeMap::const_iterator delta_it = |
michael@0 | 298 | entry->rule_changes.begin(); |
michael@0 | 299 | delta_it != entry->rule_changes.end() && delta_it->first < address; |
michael@0 | 300 | ++delta_it) { |
michael@0 | 301 | ConvertCFI(delta_it->second, rules); |
michael@0 | 302 | } |
michael@0 | 303 | return rules.forget(); |
michael@0 | 304 | #endif /* defined(SPS_OS_windows) */ |
michael@0 | 305 | } |
michael@0 | 306 | |
michael@0 | 307 | } // namespace google_breakpad |