// Copyright 2018 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/profiler/module_cache.h" #include #include #include #include "base/debug/alias.h" #include "base/process/process_handle.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/win/pe_image.h" #include "base/win/scoped_handle.h" #include "base/win/win_util.h" namespace base { namespace { // Gets the unique build ID and the corresponding debug path for a module. // Windows build IDs are created by a concatenation of a GUID and AGE fields // found in the headers of a module. The GUID is stored in the first 16 bytes // and the AGE is stored in the last 4 bytes. Returns the empty string if the // function fails to get the build ID. The debug path (pdb file) can be found // in the PE file and is the build time path where the debug file was produced. // // Example: // dumpbin chrome.exe /headers | find "Format:" // ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ... // // The resulting buildID string of this instance of chrome.exe is // "16B2A4281DED442E9A36FCE8CBD2972610". // // Note that the AGE field is encoded in decimal, not hex. void GetDebugInfoForModule(HMODULE module_handle, std::string* build_id, FilePath* pdb_name) { GUID guid; DWORD age; LPCSTR pdb_file = nullptr; size_t pdb_file_length = 0; if (!win::PEImage(module_handle) .GetDebugId(&guid, &age, &pdb_file, &pdb_file_length)) { return; } FilePath::StringType pdb_filename; if (!UTF8ToWide(pdb_file, pdb_file_length, &pdb_filename)) return; *pdb_name = FilePath(std::move(pdb_filename)).BaseName(); auto buffer = win::WStringFromGUID(guid); RemoveChars(buffer, L"{}-", &buffer); buffer.append(NumberToWString(age)); *build_id = WideToUTF8(buffer); } // Returns true if the address is in the address space accessible to // applications and DLLs, as reported by ::GetSystemInfo. bool IsValidUserSpaceAddress(uintptr_t address) { static LPVOID max_app_addr = 0; static LPVOID min_app_addr = 0; if (!max_app_addr) { SYSTEM_INFO sys_info; ::GetSystemInfo(&sys_info); max_app_addr = sys_info.lpMaximumApplicationAddress; min_app_addr = sys_info.lpMinimumApplicationAddress; } return reinterpret_cast(address) >= min_app_addr && reinterpret_cast(address) <= max_app_addr; } // Traits class to adapt GenericScopedHandle for HMODULES. class ModuleHandleTraits : public win::HandleTraits { public: using Handle = HMODULE; ModuleHandleTraits() = delete; ModuleHandleTraits(const ModuleHandleTraits&) = delete; ModuleHandleTraits& operator=(const ModuleHandleTraits&) = delete; static bool CloseHandle(HMODULE handle) { return ::FreeLibrary(handle) != 0; } static bool IsHandleValid(HMODULE handle) { return handle != nullptr; } static HMODULE NullHandle() { return nullptr; } }; // HMODULE is not really a handle, and has reference count semantics, so the // standard VerifierTraits does not apply. using ScopedModuleHandle = win::GenericScopedHandle; class WindowsModule : public ModuleCache::Module { public: WindowsModule(ScopedModuleHandle module_handle, const MODULEINFO module_info, const std::string& id, const FilePath& debug_basename) : module_handle_(std::move(module_handle)), module_info_(module_info), id_(id), debug_basename_(debug_basename) {} WindowsModule(const WindowsModule&) = delete; WindowsModule& operator=(const WindowsModule&) = delete; // ModuleCache::Module uintptr_t GetBaseAddress() const override { return reinterpret_cast(module_info_.lpBaseOfDll); } std::string GetId() const override { return id_; } FilePath GetDebugBasename() const override { return debug_basename_; } size_t GetSize() const override { return module_info_.SizeOfImage; } bool IsNative() const override { return true; } private: ScopedModuleHandle module_handle_; const MODULEINFO module_info_; std::string id_; FilePath debug_basename_; }; ScopedModuleHandle GetModuleHandleForAddress(uintptr_t address) { // Record the address in crash dumps to help understand the source of // GetModuleHandleEx crashes on Windows 11 observed in // https://crbug.com/1297776. debug::Alias(&address); if (!IsValidUserSpaceAddress(address)) return ScopedModuleHandle(nullptr); HMODULE module_handle = nullptr; // GetModuleHandleEx() increments the module reference count, which is then // managed and ultimately decremented by ScopedModuleHandle. if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(address), &module_handle)) { const DWORD error = ::GetLastError(); DCHECK_EQ(ERROR_MOD_NOT_FOUND, static_cast(error)); } return ScopedModuleHandle(module_handle); } std::unique_ptr CreateModuleForHandle( ScopedModuleHandle module_handle) { FilePath pdb_name; std::string build_id; GetDebugInfoForModule(module_handle.get(), &build_id, &pdb_name); MODULEINFO module_info; if (!::GetModuleInformation(GetCurrentProcessHandle(), module_handle.get(), &module_info, sizeof(module_info))) { return nullptr; } return std::make_unique(std::move(module_handle), module_info, build_id, pdb_name); } } // namespace // static std::unique_ptr ModuleCache::CreateModuleForAddress( uintptr_t address) { ScopedModuleHandle module_handle = GetModuleHandleForAddress(address); if (!module_handle.is_valid()) return nullptr; return CreateModuleForHandle(std::move(module_handle)); } } // namespace base