/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "PlatformMacros.h" #include "nsAutoPtr.h" #if !defined(SPS_OS_windows) # include "common/module.h" # include "processor/cfi_frame_info.h" #endif #include "google_breakpad/processor/code_module.h" #include "google_breakpad/processor/code_modules.h" #include "google_breakpad/processor/stack_frame.h" #include "common/logging.h" #if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \ || defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android) # include "common/linux/dump_symbols.h" #elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) # include "shim_mac_dump_syms.h" #elif defined(SPS_OS_windows) /* This is all stubbed out anyway, so don't do anything. */ #else # error "Unknown platform" #endif #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) # include "mozilla/Types.h" # include "ElfLoader.h" # include # include # include "nsString.h" # include "nsDirectoryServiceUtils.h" # include "nsDirectoryServiceDefs.h" # include # include #endif #include "local_debug_info_symbolizer.h" namespace google_breakpad { LocalDebugInfoSymbolizer::~LocalDebugInfoSymbolizer() { # if !defined(SPS_OS_windows) for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) { delete it->second; } # endif } #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) // Find out where the installation's lib directory is, since we'll // have to look in there to get hold of libmozglue.so. Returned // C string is heap allocated and the caller must deallocate it. static char* get_installation_lib_dir ( void ) { nsCOMPtr directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); if (!directoryService) return NULL; nsCOMPtr greDir; nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir)); if (NS_FAILED(rv)) return NULL; nsCString path; rv = greDir->GetNativePath(path); if (NS_FAILED(rv)) return NULL; return strdup(path.get()); } // Read symbol data from a file on Android. OBJ_FILENAME has // three possible cases: // // (1) /foo/bar/xyzzy/blah.apk!/libwurble.so // We hand it as-is to faulty.lib and let it fish the relevant // bits out of the APK. // // (2) libmozglue.so // This is part of the Fennec installation, but is not in the // APK. Instead we have to figure out the installation path // and look for it there. Because of faulty.lib limitations, // we have to use regular open/mmap instead of faulty.lib. // // (3) libanythingelse.so // faulty.lib assumes this is a system library, and prepends // "/system/lib/" to the path. So as in (1), we can give it // as-is to faulty.lib. // // Hence only (2) requires special-casing here. // static bool ReadSymbolData_ANDROID(const string& obj_filename, const std::vector& debug_dirs, SymbolData symbol_data, Module** module) { string obj_file_to_use = obj_filename; // Do (2) in the comment above. if (obj_file_to_use == "libmozglue.so") { char* libdir = get_installation_lib_dir(); if (libdir) { obj_file_to_use = string(libdir) + "/lib/" + obj_file_to_use; free(libdir); } // Use regular open/mmap here because of faulty.lib limitations int fd = open(obj_file_to_use.c_str(), O_RDONLY); if (fd == -1) { BPLOG(INFO) << "ReadSymbolData_APK: Failed to open \'" << obj_file_to_use << "\'"; return false; } struct stat st; if (fstat(fd, &st) != 0) { close(fd); BPLOG(INFO) << "ReadSymbolData_APK: Failed to fstat \'" << obj_file_to_use << "\'"; return false; } void* image = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (image == MAP_FAILED) { close(fd); BPLOG(INFO) << "ReadSymbolData_APK: Failed to mmap \'" << obj_file_to_use << "\'"; return false; } bool ok = ReadSymbolDataInternal((const uint8_t*)image, obj_file_to_use, debug_dirs, symbol_data, module); munmap(image, st.st_size); close(fd); return ok; } // Regardless of whether the file is inside an APK or not, we ask // faulty.lib to map it, then call ReadSymbolDataInternal, then // unmap and dlclose it. void* hdl = dlopen(obj_file_to_use.c_str(), RTLD_GLOBAL | RTLD_LAZY); if (!hdl) { BPLOG(INFO) << "ReadSymbolData_APK: Failed to get handle for ELF file \'" << obj_file_to_use << "\'"; return false; } size_t sz = __dl_get_mappable_length(hdl); if (sz == 0) { dlclose(hdl); BPLOG(INFO) << "ReadSymbolData_APK: Unable to get size for ELF file \'" << obj_file_to_use << "\'"; return false; } void* image = __dl_mmap(hdl, NULL, sz, 0); if (image == MAP_FAILED) { dlclose(hdl); BPLOG(INFO) << "ReadSymbolData_APK: Failed to mmap ELF file \'" << obj_file_to_use << "\'"; return false; } bool ok = ReadSymbolDataInternal((const uint8_t*)image, obj_file_to_use, debug_dirs, symbol_data, module); __dl_munmap(hdl, image, sz); dlclose(hdl); return ok; } #endif /* defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) */ StackFrameSymbolizer::SymbolizerResult LocalDebugInfoSymbolizer::FillSourceLineInfo(const CodeModules* modules, const SystemInfo* system_info, StackFrame* frame) { if (!modules) { return kError; } const CodeModule* module = modules->GetModuleForAddress(frame->instruction); if (!module) { return kError; } frame->module = module; # if !defined(SPS_OS_windows) Module* debug_info_module = NULL; SymbolMap::const_iterator it = symbols_.find(module->code_file()); if (it == symbols_.end()) { if (no_symbol_modules_.find(module->code_file()) != no_symbol_modules_.end()) { return kNoError; } bool ok = false; # if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) ok = ReadSymbolData_ANDROID(module->code_file(), debug_dirs_, ONLY_CFI, &debug_info_module); # elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) ok = ReadSymbolData_DARWIN(module->code_file(), debug_dirs_, ONLY_CFI, &debug_info_module); # else ok = ReadSymbolData(module->code_file(), debug_dirs_, ONLY_CFI, &debug_info_module); # endif if (!ok) { if (debug_info_module) delete debug_info_module; no_symbol_modules_.insert(module->code_file()); return kNoError; } symbols_[module->code_file()] = debug_info_module; } else { debug_info_module = it->second; } uint64_t address = frame->instruction - frame->module->base_address(); Module::Function* function = debug_info_module->FindFunctionByAddress(address); if (function) { frame->function_name = function->name; //TODO: line info: function->lines } else { Module::Extern* ex = debug_info_module->FindExternByAddress(address); if (ex) { frame->function_name = ex->name; } } # endif /* !defined(SPS_OS_windows) */ return kNoError; } WindowsFrameInfo* LocalDebugInfoSymbolizer::FindWindowsFrameInfo( const StackFrame* frame) { // Not currently implemented, would require PDBSourceLineWriter to // implement an API to return symbol data. return NULL; } #if !defined(SPS_OS_windows) // Taken wholesale from source_line_resolver_base.cc bool ParseCFIRuleSet(const string& rule_set, CFIFrameInfo* frame_info) { CFIFrameInfoParseHandler handler(frame_info); CFIRuleParser parser(&handler); return parser.Parse(rule_set); } static void ConvertCFI(const UniqueString* name, const Module::Expr& rule, CFIFrameInfo* frame_info) { if (name == ustr__ZDcfa()) frame_info->SetCFARule(rule); else if (name == ustr__ZDra()) frame_info->SetRARule(rule); else frame_info->SetRegisterRule(name, rule); } static void ConvertCFI(const Module::RuleMap& rule_map, CFIFrameInfo* frame_info) { for (Module::RuleMap::const_iterator it = rule_map.begin(); it != rule_map.end(); ++it) { ConvertCFI(it->first, it->second, frame_info); } } #endif CFIFrameInfo* LocalDebugInfoSymbolizer::FindCFIFrameInfo( const StackFrame* frame) { #if defined(SPS_OS_windows) return NULL; #else if (!frame || !frame->module) return NULL; SymbolMap::const_iterator it = symbols_.find(frame->module->code_file()); if (it == symbols_.end()) return NULL; Module* module = it->second; uint64_t address = frame->instruction - frame->module->base_address(); Module::StackFrameEntry* entry = module->FindStackFrameEntryByAddress(address); if (!entry) return NULL; //TODO: can we cache this data per-address? does that make sense? // TODO: Maybe this should use google_breakpad::scoped_ptr, since we're in // "namespace google_breakpad". Not using scoped_ptr currently, because its // header triggers build warnings -- see bug 855010. nsAutoPtr rules(new CFIFrameInfo()); ConvertCFI(entry->initial_rules, rules); for (Module::RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin(); delta_it != entry->rule_changes.end() && delta_it->first < address; ++delta_it) { ConvertCFI(delta_it->second, rules); } return rules.forget(); #endif /* defined(SPS_OS_windows) */ } } // namespace google_breakpad