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