1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/profiler/module_cache.h"
6
7 #include <objbase.h>
8 #include <psapi.h>
9
10 #include <string>
11
12 #include "base/debug/alias.h"
13 #include "base/process/process_handle.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/win/pe_image.h"
18 #include "base/win/scoped_handle.h"
19 #include "base/win/win_util.h"
20
21 namespace base {
22
23 namespace {
24
25 // Gets the unique build ID and the corresponding debug path for a module.
26 // Windows build IDs are created by a concatenation of a GUID and AGE fields
27 // found in the headers of a module. The GUID is stored in the first 16 bytes
28 // and the AGE is stored in the last 4 bytes. Returns the empty string if the
29 // function fails to get the build ID. The debug path (pdb file) can be found
30 // in the PE file and is the build time path where the debug file was produced.
31 //
32 // Example:
33 // dumpbin chrome.exe /headers | find "Format:"
34 // ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ...
35 //
36 // The resulting buildID string of this instance of chrome.exe is
37 // "16B2A4281DED442E9A36FCE8CBD2972610".
38 //
39 // Note that the AGE field is encoded in decimal, not hex.
GetDebugInfoForModule(HMODULE module_handle,std::string * build_id,FilePath * pdb_name)40 void GetDebugInfoForModule(HMODULE module_handle,
41 std::string* build_id,
42 FilePath* pdb_name) {
43 GUID guid;
44 DWORD age;
45 LPCSTR pdb_file = nullptr;
46 size_t pdb_file_length = 0;
47 if (!win::PEImage(module_handle)
48 .GetDebugId(&guid, &age, &pdb_file, &pdb_file_length)) {
49 return;
50 }
51
52 FilePath::StringType pdb_filename;
53 if (!UTF8ToWide(pdb_file, pdb_file_length, &pdb_filename))
54 return;
55 *pdb_name = FilePath(std::move(pdb_filename)).BaseName();
56
57 auto buffer = win::WStringFromGUID(guid);
58 RemoveChars(buffer, L"{}-", &buffer);
59 buffer.append(NumberToWString(age));
60 *build_id = WideToUTF8(buffer);
61 }
62
63 // Returns true if the address is in the address space accessible to
64 // applications and DLLs, as reported by ::GetSystemInfo.
IsValidUserSpaceAddress(uintptr_t address)65 bool IsValidUserSpaceAddress(uintptr_t address) {
66 static LPVOID max_app_addr = 0;
67 static LPVOID min_app_addr = 0;
68 if (!max_app_addr) {
69 SYSTEM_INFO sys_info;
70 ::GetSystemInfo(&sys_info);
71 max_app_addr = sys_info.lpMaximumApplicationAddress;
72 min_app_addr = sys_info.lpMinimumApplicationAddress;
73 }
74 return reinterpret_cast<LPVOID>(address) >= min_app_addr &&
75 reinterpret_cast<LPVOID>(address) <= max_app_addr;
76 }
77
78 // Traits class to adapt GenericScopedHandle for HMODULES.
79 class ModuleHandleTraits : public win::HandleTraits {
80 public:
81 using Handle = HMODULE;
82
83 ModuleHandleTraits() = delete;
84 ModuleHandleTraits(const ModuleHandleTraits&) = delete;
85 ModuleHandleTraits& operator=(const ModuleHandleTraits&) = delete;
86
CloseHandle(HMODULE handle)87 static bool CloseHandle(HMODULE handle) { return ::FreeLibrary(handle) != 0; }
IsHandleValid(HMODULE handle)88 static bool IsHandleValid(HMODULE handle) { return handle != nullptr; }
NullHandle()89 static HMODULE NullHandle() { return nullptr; }
90 };
91
92 // HMODULE is not really a handle, and has reference count semantics, so the
93 // standard VerifierTraits does not apply.
94 using ScopedModuleHandle =
95 win::GenericScopedHandle<ModuleHandleTraits, win::DummyVerifierTraits>;
96
97 class WindowsModule : public ModuleCache::Module {
98 public:
WindowsModule(ScopedModuleHandle module_handle,const MODULEINFO module_info,const std::string & id,const FilePath & debug_basename)99 WindowsModule(ScopedModuleHandle module_handle,
100 const MODULEINFO module_info,
101 const std::string& id,
102 const FilePath& debug_basename)
103 : module_handle_(std::move(module_handle)),
104 module_info_(module_info),
105 id_(id),
106 debug_basename_(debug_basename) {}
107
108 WindowsModule(const WindowsModule&) = delete;
109 WindowsModule& operator=(const WindowsModule&) = delete;
110
111 // ModuleCache::Module
GetBaseAddress() const112 uintptr_t GetBaseAddress() const override {
113 return reinterpret_cast<uintptr_t>(module_info_.lpBaseOfDll);
114 }
115
GetId() const116 std::string GetId() const override { return id_; }
GetDebugBasename() const117 FilePath GetDebugBasename() const override { return debug_basename_; }
GetSize() const118 size_t GetSize() const override { return module_info_.SizeOfImage; }
IsNative() const119 bool IsNative() const override { return true; }
120
121 private:
122 ScopedModuleHandle module_handle_;
123 const MODULEINFO module_info_;
124 std::string id_;
125 FilePath debug_basename_;
126 };
127
GetModuleHandleForAddress(uintptr_t address)128 ScopedModuleHandle GetModuleHandleForAddress(uintptr_t address) {
129 // Record the address in crash dumps to help understand the source of
130 // GetModuleHandleEx crashes on Windows 11 observed in
131 // https://crbug.com/1297776.
132 debug::Alias(&address);
133 if (!IsValidUserSpaceAddress(address))
134 return ScopedModuleHandle(nullptr);
135
136 HMODULE module_handle = nullptr;
137
138 // GetModuleHandleEx() increments the module reference count, which is then
139 // managed and ultimately decremented by ScopedModuleHandle.
140 if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
141 reinterpret_cast<LPCTSTR>(address),
142 &module_handle)) {
143 const DWORD error = ::GetLastError();
144 DCHECK_EQ(ERROR_MOD_NOT_FOUND, static_cast<int>(error));
145 }
146 return ScopedModuleHandle(module_handle);
147 }
148
CreateModuleForHandle(ScopedModuleHandle module_handle)149 std::unique_ptr<ModuleCache::Module> CreateModuleForHandle(
150 ScopedModuleHandle module_handle) {
151 FilePath pdb_name;
152 std::string build_id;
153 GetDebugInfoForModule(module_handle.get(), &build_id, &pdb_name);
154
155 MODULEINFO module_info;
156 if (!::GetModuleInformation(GetCurrentProcessHandle(), module_handle.get(),
157 &module_info, sizeof(module_info))) {
158 return nullptr;
159 }
160
161 return std::make_unique<WindowsModule>(std::move(module_handle), module_info,
162 build_id, pdb_name);
163 }
164
165 } // namespace
166
167 // static
CreateModuleForAddress(uintptr_t address)168 std::unique_ptr<const ModuleCache::Module> ModuleCache::CreateModuleForAddress(
169 uintptr_t address) {
170 ScopedModuleHandle module_handle = GetModuleHandleForAddress(address);
171 if (!module_handle.is_valid())
172 return nullptr;
173 return CreateModuleForHandle(std::move(module_handle));
174 }
175
176 } // namespace base
177