/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- PlatformWindows.cpp -----------------------------------------------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | |
9 | | #include "PlatformWindows.h" |
10 | | |
11 | | #include <cstdio> |
12 | | #include <optional> |
13 | | #if defined(_WIN32) |
14 | | #include "lldb/Host/windows/windows.h" |
15 | | #include <winsock2.h> |
16 | | #endif |
17 | | |
18 | | #include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" |
19 | | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
20 | | #include "lldb/Breakpoint/BreakpointLocation.h" |
21 | | #include "lldb/Breakpoint/BreakpointSite.h" |
22 | | #include "lldb/Core/Debugger.h" |
23 | | #include "lldb/Core/Module.h" |
24 | | #include "lldb/Core/PluginManager.h" |
25 | | #include "lldb/Expression/DiagnosticManager.h" |
26 | | #include "lldb/Expression/FunctionCaller.h" |
27 | | #include "lldb/Expression/UserExpression.h" |
28 | | #include "lldb/Expression/UtilityFunction.h" |
29 | | #include "lldb/Host/HostInfo.h" |
30 | | #include "lldb/Target/DynamicLoader.h" |
31 | | #include "lldb/Target/Process.h" |
32 | | #include "lldb/Utility/Status.h" |
33 | | |
34 | | #include "llvm/ADT/ScopeExit.h" |
35 | | #include "llvm/Support/ConvertUTF.h" |
36 | | |
37 | | using namespace lldb; |
38 | | using namespace lldb_private; |
39 | | |
40 | | LLDB_PLUGIN_DEFINE(PlatformWindows) |
41 | | |
42 | | static uint32_t g_initialize_count = 0; |
43 | | |
44 | | PlatformSP PlatformWindows::CreateInstance(bool force, |
45 | 204 | const lldb_private::ArchSpec *arch) { |
46 | | // The only time we create an instance is when we are creating a remote |
47 | | // windows platform |
48 | 204 | const bool is_host = false; |
49 | | |
50 | 204 | bool create = force; |
51 | 204 | if (!create && arch198 && arch->IsValid()198 ) { |
52 | 198 | const llvm::Triple &triple = arch->GetTriple(); |
53 | 198 | switch (triple.getVendor()) { |
54 | 90 | case llvm::Triple::PC: |
55 | 90 | create = true; |
56 | 90 | break; |
57 | | |
58 | 101 | case llvm::Triple::UnknownVendor: |
59 | 101 | create = !arch->TripleVendorWasSpecified(); |
60 | 101 | break; |
61 | | |
62 | 7 | default: |
63 | 7 | break; |
64 | 198 | } |
65 | | |
66 | 198 | if (create) { |
67 | 113 | switch (triple.getOS()) { |
68 | 90 | case llvm::Triple::Win32: |
69 | 90 | break; |
70 | | |
71 | 11 | case llvm::Triple::UnknownOS: |
72 | 11 | create = arch->TripleOSWasSpecified(); |
73 | 11 | break; |
74 | | |
75 | 12 | default: |
76 | 12 | create = false; |
77 | 12 | break; |
78 | 113 | } |
79 | 113 | } |
80 | 198 | } |
81 | 204 | if (create) |
82 | 96 | return PlatformSP(new PlatformWindows(is_host)); |
83 | 108 | return PlatformSP(); |
84 | 204 | } |
85 | | |
86 | 3.92k | llvm::StringRef PlatformWindows::GetPluginDescriptionStatic(bool is_host) { |
87 | 3.92k | return is_host ? "Local Windows user platform plug-in."0 |
88 | 3.92k | : "Remote Windows user platform plug-in."; |
89 | 3.92k | } |
90 | | |
91 | 3.92k | void PlatformWindows::Initialize() { |
92 | 3.92k | Platform::Initialize(); |
93 | | |
94 | 3.92k | if (g_initialize_count++ == 0) { |
95 | | #if defined(_WIN32) |
96 | | // Force a host flag to true for the default platform object. |
97 | | PlatformSP default_platform_sp(new PlatformWindows(true)); |
98 | | default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); |
99 | | Platform::SetHostPlatform(default_platform_sp); |
100 | | #endif |
101 | 3.92k | PluginManager::RegisterPlugin( |
102 | 3.92k | PlatformWindows::GetPluginNameStatic(false), |
103 | 3.92k | PlatformWindows::GetPluginDescriptionStatic(false), |
104 | 3.92k | PlatformWindows::CreateInstance); |
105 | 3.92k | } |
106 | 3.92k | } |
107 | | |
108 | 3.92k | void PlatformWindows::Terminate() { |
109 | 3.92k | if (g_initialize_count > 0) { |
110 | 3.92k | if (--g_initialize_count == 0) { |
111 | 3.92k | PluginManager::UnregisterPlugin(PlatformWindows::CreateInstance); |
112 | 3.92k | } |
113 | 3.92k | } |
114 | | |
115 | 3.92k | Platform::Terminate(); |
116 | 3.92k | } |
117 | | |
118 | | /// Default Constructor |
119 | 96 | PlatformWindows::PlatformWindows(bool is_host) : RemoteAwarePlatform(is_host) { |
120 | 288 | const auto &AddArch = [&](const ArchSpec &spec) { |
121 | 288 | if (llvm::any_of(m_supported_architectures, [spec](const ArchSpec &rhs) { |
122 | 192 | return spec.IsExactMatch(rhs); |
123 | 192 | })) |
124 | 96 | return; |
125 | 192 | if (spec.IsValid()) |
126 | 192 | m_supported_architectures.push_back(spec); |
127 | 192 | }; |
128 | 96 | AddArch(HostInfo::GetArchitecture(HostInfo::eArchKindDefault)); |
129 | 96 | AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind32)); |
130 | 96 | AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind64)); |
131 | 96 | } |
132 | | |
133 | 0 | Status PlatformWindows::ConnectRemote(Args &args) { |
134 | 0 | Status error; |
135 | 0 | if (IsHost()) { |
136 | 0 | error.SetErrorStringWithFormatv( |
137 | 0 | "can't connect to the host platform '{0}', always connected", |
138 | 0 | GetPluginName()); |
139 | 0 | } else { |
140 | 0 | if (!m_remote_platform_sp) |
141 | 0 | m_remote_platform_sp = |
142 | 0 | platform_gdb_server::PlatformRemoteGDBServer::CreateInstance( |
143 | 0 | /*force=*/true, nullptr); |
144 | |
|
145 | 0 | if (m_remote_platform_sp) { |
146 | 0 | if (error.Success()) { |
147 | 0 | if (m_remote_platform_sp) { |
148 | 0 | error = m_remote_platform_sp->ConnectRemote(args); |
149 | 0 | } else { |
150 | 0 | error.SetErrorString( |
151 | 0 | "\"platform connect\" takes a single argument: <connect-url>"); |
152 | 0 | } |
153 | 0 | } |
154 | 0 | } else |
155 | 0 | error.SetErrorString("failed to create a 'remote-gdb-server' platform"); |
156 | |
|
157 | 0 | if (error.Fail()) |
158 | 0 | m_remote_platform_sp.reset(); |
159 | 0 | } |
160 | |
|
161 | 0 | return error; |
162 | 0 | } |
163 | | |
164 | | uint32_t PlatformWindows::DoLoadImage(Process *process, |
165 | | const FileSpec &remote_file, |
166 | | const std::vector<std::string> *paths, |
167 | 0 | Status &error, FileSpec *loaded_image) { |
168 | 0 | DiagnosticManager diagnostics; |
169 | |
|
170 | 0 | if (loaded_image) |
171 | 0 | loaded_image->Clear(); |
172 | |
|
173 | 0 | ThreadSP thread = process->GetThreadList().GetExpressionExecutionThread(); |
174 | 0 | if (!thread) { |
175 | 0 | error.SetErrorString("LoadLibrary error: no thread available to invoke LoadLibrary"); |
176 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
177 | 0 | } |
178 | | |
179 | 0 | ExecutionContext context; |
180 | 0 | thread->CalculateExecutionContext(context); |
181 | |
|
182 | 0 | Status status; |
183 | 0 | UtilityFunction *loader = |
184 | 0 | process->GetLoadImageUtilityFunction(this, [&]() -> std::unique_ptr<UtilityFunction> { |
185 | 0 | return MakeLoadImageUtilityFunction(context, status); |
186 | 0 | }); |
187 | 0 | if (loader == nullptr) |
188 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
189 | | |
190 | 0 | FunctionCaller *invocation = loader->GetFunctionCaller(); |
191 | 0 | if (!invocation) { |
192 | 0 | error.SetErrorString("LoadLibrary error: could not get function caller"); |
193 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
194 | 0 | } |
195 | | |
196 | | /* Convert name */ |
197 | 0 | llvm::SmallVector<llvm::UTF16, 261> name; |
198 | 0 | if (!llvm::convertUTF8ToUTF16String(remote_file.GetPath(), name)) { |
199 | 0 | error.SetErrorString("LoadLibrary error: could not convert path to UCS2"); |
200 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
201 | 0 | } |
202 | 0 | name.emplace_back(L'\0'); |
203 | | |
204 | | /* Inject name paramter into inferior */ |
205 | 0 | lldb::addr_t injected_name = |
206 | 0 | process->AllocateMemory(name.size() * sizeof(llvm::UTF16), |
207 | 0 | ePermissionsReadable | ePermissionsWritable, |
208 | 0 | status); |
209 | 0 | if (injected_name == LLDB_INVALID_ADDRESS) { |
210 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: unable to allocate memory for name: %s", |
211 | 0 | status.AsCString()); |
212 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
213 | 0 | } |
214 | | |
215 | 0 | auto name_cleanup = llvm::make_scope_exit([process, injected_name]() { |
216 | 0 | process->DeallocateMemory(injected_name); |
217 | 0 | }); |
218 | |
|
219 | 0 | process->WriteMemory(injected_name, name.data(), |
220 | 0 | name.size() * sizeof(llvm::UTF16), status); |
221 | 0 | if (status.Fail()) { |
222 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: unable to write name: %s", |
223 | 0 | status.AsCString()); |
224 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
225 | 0 | } |
226 | | |
227 | | /* Inject paths parameter into inferior */ |
228 | 0 | lldb::addr_t injected_paths{0x0}; |
229 | 0 | std::optional<llvm::detail::scope_exit<std::function<void()>>> paths_cleanup; |
230 | 0 | if (paths) { |
231 | 0 | llvm::SmallVector<llvm::UTF16, 261> search_paths; |
232 | |
|
233 | 0 | for (const auto &path : *paths) { |
234 | 0 | if (path.empty()) |
235 | 0 | continue; |
236 | | |
237 | 0 | llvm::SmallVector<llvm::UTF16, 261> buffer; |
238 | 0 | if (!llvm::convertUTF8ToUTF16String(path, buffer)) |
239 | 0 | continue; |
240 | | |
241 | 0 | search_paths.append(std::begin(buffer), std::end(buffer)); |
242 | 0 | search_paths.emplace_back(L'\0'); |
243 | 0 | } |
244 | 0 | search_paths.emplace_back(L'\0'); |
245 | |
|
246 | 0 | injected_paths = |
247 | 0 | process->AllocateMemory(search_paths.size() * sizeof(llvm::UTF16), |
248 | 0 | ePermissionsReadable | ePermissionsWritable, |
249 | 0 | status); |
250 | 0 | if (injected_paths == LLDB_INVALID_ADDRESS) { |
251 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: unable to allocate memory for paths: %s", |
252 | 0 | status.AsCString()); |
253 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
254 | 0 | } |
255 | | |
256 | 0 | paths_cleanup.emplace([process, injected_paths]() { |
257 | 0 | process->DeallocateMemory(injected_paths); |
258 | 0 | }); |
259 | |
|
260 | 0 | process->WriteMemory(injected_paths, search_paths.data(), |
261 | 0 | search_paths.size() * sizeof(llvm::UTF16), status); |
262 | 0 | if (status.Fail()) { |
263 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: unable to write paths: %s", |
264 | 0 | status.AsCString()); |
265 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
266 | 0 | } |
267 | 0 | } |
268 | | |
269 | | /* Inject wszModulePath into inferior */ |
270 | | // FIXME(compnerd) should do something better for the length? |
271 | | // GetModuleFileNameA is likely limited to PATH_MAX rather than the NT path |
272 | | // limit. |
273 | 0 | unsigned injected_length = 261; |
274 | |
|
275 | 0 | lldb::addr_t injected_module_path = |
276 | 0 | process->AllocateMemory(injected_length + 1, |
277 | 0 | ePermissionsReadable | ePermissionsWritable, |
278 | 0 | status); |
279 | 0 | if (injected_module_path == LLDB_INVALID_ADDRESS) { |
280 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: unable to allocate memory for module location: %s", |
281 | 0 | status.AsCString()); |
282 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
283 | 0 | } |
284 | | |
285 | 0 | auto injected_module_path_cleanup = |
286 | 0 | llvm::make_scope_exit([process, injected_module_path]() { |
287 | 0 | process->DeallocateMemory(injected_module_path); |
288 | 0 | }); |
289 | | |
290 | | /* Inject __lldb_LoadLibraryResult into inferior */ |
291 | 0 | const uint32_t word_size = process->GetAddressByteSize(); |
292 | 0 | lldb::addr_t injected_result = |
293 | 0 | process->AllocateMemory(3 * word_size, |
294 | 0 | ePermissionsReadable | ePermissionsWritable, |
295 | 0 | status); |
296 | 0 | if (status.Fail()) { |
297 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: could not allocate memory for result: %s", |
298 | 0 | status.AsCString()); |
299 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
300 | 0 | } |
301 | | |
302 | 0 | auto result_cleanup = llvm::make_scope_exit([process, injected_result]() { |
303 | 0 | process->DeallocateMemory(injected_result); |
304 | 0 | }); |
305 | |
|
306 | 0 | process->WritePointerToMemory(injected_result + word_size, |
307 | 0 | injected_module_path, status); |
308 | 0 | if (status.Fail()) { |
309 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: could not initialize result: %s", |
310 | 0 | status.AsCString()); |
311 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
312 | 0 | } |
313 | | |
314 | | // XXX(compnerd) should we use the compiler to get the sizeof(unsigned)? |
315 | 0 | process->WriteScalarToMemory(injected_result + 2 * word_size, |
316 | 0 | Scalar{injected_length}, sizeof(unsigned), |
317 | 0 | status); |
318 | 0 | if (status.Fail()) { |
319 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: could not initialize result: %s", |
320 | 0 | status.AsCString()); |
321 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
322 | 0 | } |
323 | | |
324 | | /* Setup Formal Parameters */ |
325 | 0 | ValueList parameters = invocation->GetArgumentValues(); |
326 | 0 | parameters.GetValueAtIndex(0)->GetScalar() = injected_name; |
327 | 0 | parameters.GetValueAtIndex(1)->GetScalar() = injected_paths; |
328 | 0 | parameters.GetValueAtIndex(2)->GetScalar() = injected_result; |
329 | |
|
330 | 0 | lldb::addr_t injected_parameters = LLDB_INVALID_ADDRESS; |
331 | 0 | diagnostics.Clear(); |
332 | 0 | if (!invocation->WriteFunctionArguments(context, injected_parameters, |
333 | 0 | parameters, diagnostics)) { |
334 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: unable to write function parameters: %s", |
335 | 0 | diagnostics.GetString().c_str()); |
336 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
337 | 0 | } |
338 | | |
339 | 0 | auto parameter_cleanup = |
340 | 0 | llvm::make_scope_exit([invocation, &context, injected_parameters]() { |
341 | 0 | invocation->DeallocateFunctionResults(context, injected_parameters); |
342 | 0 | }); |
343 | |
|
344 | 0 | TypeSystemClangSP scratch_ts_sp = |
345 | 0 | ScratchTypeSystemClang::GetForTarget(process->GetTarget()); |
346 | 0 | if (!scratch_ts_sp) { |
347 | 0 | error.SetErrorString("LoadLibrary error: unable to get (clang) type system"); |
348 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
349 | 0 | } |
350 | | |
351 | | /* Setup Return Type */ |
352 | 0 | CompilerType VoidPtrTy = |
353 | 0 | scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType(); |
354 | |
|
355 | 0 | Value value; |
356 | 0 | value.SetCompilerType(VoidPtrTy); |
357 | | |
358 | | /* Invoke expression */ |
359 | 0 | EvaluateExpressionOptions options; |
360 | 0 | options.SetExecutionPolicy(eExecutionPolicyAlways); |
361 | 0 | options.SetLanguage(eLanguageTypeC_plus_plus); |
362 | 0 | options.SetIgnoreBreakpoints(true); |
363 | 0 | options.SetUnwindOnError(true); |
364 | | // LoadLibraryEx{A,W}/FreeLibrary cannot raise exceptions which we can handle. |
365 | | // They may potentially throw SEH exceptions which we do not know how to |
366 | | // handle currently. |
367 | 0 | options.SetTrapExceptions(false); |
368 | 0 | options.SetTimeout(process->GetUtilityExpressionTimeout()); |
369 | 0 | options.SetIsForUtilityExpr(true); |
370 | |
|
371 | 0 | ExpressionResults result = |
372 | 0 | invocation->ExecuteFunction(context, &injected_parameters, options, |
373 | 0 | diagnostics, value); |
374 | 0 | if (result != eExpressionCompleted) { |
375 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: failed to execute LoadLibrary helper: %s", |
376 | 0 | diagnostics.GetString().c_str()); |
377 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
378 | 0 | } |
379 | | |
380 | | /* Read result */ |
381 | 0 | lldb::addr_t token = process->ReadPointerFromMemory(injected_result, status); |
382 | 0 | if (status.Fail()) { |
383 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: could not read the result: %s", |
384 | 0 | status.AsCString()); |
385 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
386 | 0 | } |
387 | | |
388 | 0 | if (!token) { |
389 | | // XXX(compnerd) should we use the compiler to get the sizeof(unsigned)? |
390 | 0 | uint64_t error_code = |
391 | 0 | process->ReadUnsignedIntegerFromMemory(injected_result + 2 * word_size + sizeof(unsigned), |
392 | 0 | word_size, 0, status); |
393 | 0 | if (status.Fail()) { |
394 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: could not read error status: %s", |
395 | 0 | status.AsCString()); |
396 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
397 | 0 | } |
398 | | |
399 | 0 | error.SetErrorStringWithFormat("LoadLibrary Error: %" PRIu64, error_code); |
400 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
401 | 0 | } |
402 | | |
403 | 0 | std::string module_path; |
404 | 0 | process->ReadCStringFromMemory(injected_module_path, module_path, status); |
405 | 0 | if (status.Fail()) { |
406 | 0 | error.SetErrorStringWithFormat("LoadLibrary error: could not read module path: %s", |
407 | 0 | status.AsCString()); |
408 | 0 | return LLDB_INVALID_IMAGE_TOKEN; |
409 | 0 | } |
410 | | |
411 | 0 | if (loaded_image) |
412 | 0 | loaded_image->SetFile(module_path, llvm::sys::path::Style::native); |
413 | 0 | return process->AddImageToken(token); |
414 | 0 | } |
415 | | |
416 | 0 | Status PlatformWindows::UnloadImage(Process *process, uint32_t image_token) { |
417 | 0 | const addr_t address = process->GetImagePtrFromToken(image_token); |
418 | 0 | if (address == LLDB_INVALID_IMAGE_TOKEN) |
419 | 0 | return Status("invalid image token"); |
420 | | |
421 | 0 | StreamString expression; |
422 | 0 | expression.Printf("FreeLibrary((HMODULE)0x%" PRIx64 ")", address); |
423 | |
|
424 | 0 | ValueObjectSP value; |
425 | 0 | Status result = |
426 | 0 | EvaluateLoaderExpression(process, expression.GetData(), value); |
427 | 0 | if (result.Fail()) |
428 | 0 | return result; |
429 | | |
430 | 0 | if (value->GetError().Fail()) |
431 | 0 | return value->GetError(); |
432 | | |
433 | 0 | Scalar scalar; |
434 | 0 | if (value->ResolveValue(scalar)) { |
435 | 0 | if (scalar.UInt(1)) |
436 | 0 | return Status("expression failed: \"%s\"", expression.GetData()); |
437 | 0 | process->ResetImageToken(image_token); |
438 | 0 | } |
439 | | |
440 | 0 | return Status(); |
441 | 0 | } |
442 | | |
443 | 0 | Status PlatformWindows::DisconnectRemote() { |
444 | 0 | Status error; |
445 | |
|
446 | 0 | if (IsHost()) { |
447 | 0 | error.SetErrorStringWithFormatv( |
448 | 0 | "can't disconnect from the host platform '{0}', always connected", |
449 | 0 | GetPluginName()); |
450 | 0 | } else { |
451 | 0 | if (m_remote_platform_sp) |
452 | 0 | error = m_remote_platform_sp->DisconnectRemote(); |
453 | 0 | else |
454 | 0 | error.SetErrorString("the platform is not currently connected"); |
455 | 0 | } |
456 | 0 | return error; |
457 | 0 | } |
458 | | |
459 | | ProcessSP PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info, |
460 | | Debugger &debugger, Target &target, |
461 | 0 | Status &error) { |
462 | | // Windows has special considerations that must be followed when launching or |
463 | | // attaching to a process. The key requirement is that when launching or |
464 | | // attaching to a process, you must do it from the same the thread that will |
465 | | // go into a permanent loop which will then receive debug events from the |
466 | | // process. In particular, this means we can't use any of LLDB's generic |
467 | | // mechanisms to do it for us, because it doesn't have the special knowledge |
468 | | // required for setting up the background thread or passing the right flags. |
469 | | // |
470 | | // Another problem is that LLDB's standard model for debugging a process |
471 | | // is to first launch it, have it stop at the entry point, and then attach to |
472 | | // it. In Windows this doesn't quite work, you have to specify as an |
473 | | // argument to CreateProcess() that you're going to debug the process. So we |
474 | | // override DebugProcess here to handle this. Launch operations go directly |
475 | | // to the process plugin, and attach operations almost go directly to the |
476 | | // process plugin (but we hijack the events first). In essence, we |
477 | | // encapsulate all the logic of Launching and Attaching in the process |
478 | | // plugin, and PlatformWindows::DebugProcess is just a pass-through to get to |
479 | | // the process plugin. |
480 | |
|
481 | 0 | if (IsRemote()) { |
482 | 0 | if (m_remote_platform_sp) |
483 | 0 | return m_remote_platform_sp->DebugProcess(launch_info, debugger, target, |
484 | 0 | error); |
485 | 0 | else |
486 | 0 | error.SetErrorString("the platform is not currently connected"); |
487 | 0 | } |
488 | | |
489 | 0 | if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { |
490 | | // This is a process attach. Don't need to launch anything. |
491 | 0 | ProcessAttachInfo attach_info(launch_info); |
492 | 0 | return Attach(attach_info, debugger, &target, error); |
493 | 0 | } |
494 | | |
495 | 0 | ProcessSP process_sp = |
496 | 0 | target.CreateProcess(launch_info.GetListener(), |
497 | 0 | launch_info.GetProcessPluginName(), nullptr, false); |
498 | |
|
499 | 0 | process_sp->HijackProcessEvents(launch_info.GetHijackListener()); |
500 | | |
501 | | // We need to launch and attach to the process. |
502 | 0 | launch_info.GetFlags().Set(eLaunchFlagDebug); |
503 | 0 | if (process_sp) |
504 | 0 | error = process_sp->Launch(launch_info); |
505 | |
|
506 | 0 | return process_sp; |
507 | 0 | } |
508 | | |
509 | | lldb::ProcessSP PlatformWindows::Attach(ProcessAttachInfo &attach_info, |
510 | | Debugger &debugger, Target *target, |
511 | 0 | Status &error) { |
512 | 0 | error.Clear(); |
513 | 0 | lldb::ProcessSP process_sp; |
514 | 0 | if (!IsHost()) { |
515 | 0 | if (m_remote_platform_sp) |
516 | 0 | process_sp = |
517 | 0 | m_remote_platform_sp->Attach(attach_info, debugger, target, error); |
518 | 0 | else |
519 | 0 | error.SetErrorString("the platform is not currently connected"); |
520 | 0 | return process_sp; |
521 | 0 | } |
522 | | |
523 | 0 | if (target == nullptr) { |
524 | 0 | TargetSP new_target_sp; |
525 | 0 | FileSpec emptyFileSpec; |
526 | 0 | ArchSpec emptyArchSpec; |
527 | |
|
528 | 0 | error = debugger.GetTargetList().CreateTarget( |
529 | 0 | debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp); |
530 | 0 | target = new_target_sp.get(); |
531 | 0 | } |
532 | |
|
533 | 0 | if (!target || error.Fail()) |
534 | 0 | return process_sp; |
535 | | |
536 | 0 | process_sp = |
537 | 0 | target->CreateProcess(attach_info.GetListenerForProcess(debugger), |
538 | 0 | attach_info.GetProcessPluginName(), nullptr, false); |
539 | |
|
540 | 0 | process_sp->HijackProcessEvents(attach_info.GetHijackListener()); |
541 | 0 | if (process_sp) |
542 | 0 | error = process_sp->Attach(attach_info); |
543 | |
|
544 | 0 | return process_sp; |
545 | 0 | } |
546 | | |
547 | 0 | void PlatformWindows::GetStatus(Stream &strm) { |
548 | 0 | Platform::GetStatus(strm); |
549 | |
|
550 | | #ifdef _WIN32 |
551 | | llvm::VersionTuple version = HostInfo::GetOSVersion(); |
552 | | strm << " Host: Windows " << version.getAsString() << '\n'; |
553 | | #endif |
554 | 0 | } |
555 | | |
556 | 0 | bool PlatformWindows::CanDebugProcess() { return true; } |
557 | | |
558 | 0 | ConstString PlatformWindows::GetFullNameForDylib(ConstString basename) { |
559 | 0 | if (basename.IsEmpty()) |
560 | 0 | return basename; |
561 | | |
562 | 0 | StreamString stream; |
563 | 0 | stream.Printf("%s.dll", basename.GetCString()); |
564 | 0 | return ConstString(stream.GetString()); |
565 | 0 | } |
566 | | |
567 | | size_t |
568 | | PlatformWindows::GetSoftwareBreakpointTrapOpcode(Target &target, |
569 | 0 | BreakpointSite *bp_site) { |
570 | 0 | ArchSpec arch = target.GetArchitecture(); |
571 | 0 | assert(arch.IsValid()); |
572 | 0 | const uint8_t *trap_opcode = nullptr; |
573 | 0 | size_t trap_opcode_size = 0; |
574 | |
|
575 | 0 | switch (arch.GetMachine()) { |
576 | 0 | case llvm::Triple::aarch64: { |
577 | 0 | static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x3e, 0xd4}; // brk #0xf000 |
578 | 0 | trap_opcode = g_aarch64_opcode; |
579 | 0 | trap_opcode_size = sizeof(g_aarch64_opcode); |
580 | |
|
581 | 0 | if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) |
582 | 0 | return trap_opcode_size; |
583 | 0 | return 0; |
584 | 0 | } break; |
585 | | |
586 | 0 | case llvm::Triple::arm: |
587 | 0 | case llvm::Triple::thumb: { |
588 | 0 | static const uint8_t g_thumb_opcode[] = {0xfe, 0xde}; // udf #0xfe |
589 | 0 | trap_opcode = g_thumb_opcode; |
590 | 0 | trap_opcode_size = sizeof(g_thumb_opcode); |
591 | |
|
592 | 0 | if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) |
593 | 0 | return trap_opcode_size; |
594 | 0 | return 0; |
595 | 0 | } break; |
596 | | |
597 | 0 | default: |
598 | 0 | return Platform::GetSoftwareBreakpointTrapOpcode(target, bp_site); |
599 | 0 | } |
600 | 0 | } |
601 | | |
602 | | std::unique_ptr<UtilityFunction> |
603 | | PlatformWindows::MakeLoadImageUtilityFunction(ExecutionContext &context, |
604 | 0 | Status &status) { |
605 | | // FIXME(compnerd) `-fdeclspec` is not passed to the clang instance? |
606 | 0 | static constexpr const char kLoaderDecls[] = R"( |
607 | 0 | extern "C" { |
608 | 0 | // errhandlingapi.h |
609 | 0 |
|
610 | 0 | // `LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS` |
611 | 0 | // |
612 | 0 | // Directories in the standard search path are not searched. This value cannot |
613 | 0 | // be combined with `LOAD_WITH_ALTERED_SEARCH_PATH`. |
614 | 0 | // |
615 | 0 | // This value represents the recommended maximum number of directories an |
616 | 0 | // application should include in its DLL search path. |
617 | 0 | #define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000 |
618 | 0 |
|
619 | 0 | // WINBASEAPI DWORD WINAPI GetLastError(VOID); |
620 | 0 | /* __declspec(dllimport) */ uint32_t __stdcall GetLastError(); |
621 | 0 |
|
622 | 0 | // libloaderapi.h |
623 | 0 |
|
624 | 0 | // WINBASEAPI DLL_DIRECTORY_COOKIE WINAPI AddDllDirectory(LPCWSTR); |
625 | 0 | /* __declspec(dllimport) */ void * __stdcall AddDllDirectory(const wchar_t *); |
626 | 0 |
|
627 | 0 | // WINBASEAPI BOOL WINAPI FreeModule(HMODULE); |
628 | 0 | /* __declspec(dllimport) */ int __stdcall FreeModule(void *hLibModule); |
629 | 0 |
|
630 | 0 | // WINBASEAPI DWORD WINAPI GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize); |
631 | 0 | /* __declspec(dllimport) */ uint32_t GetModuleFileNameA(void *, char *, uint32_t); |
632 | 0 |
|
633 | 0 | // WINBASEAPI HMODULE WINAPI LoadLibraryExW(LPCWSTR, HANDLE, DWORD); |
634 | 0 | /* __declspec(dllimport) */ void * __stdcall LoadLibraryExW(const wchar_t *, void *, uint32_t); |
635 | 0 |
|
636 | 0 | // corecrt_wstring.h |
637 | 0 |
|
638 | 0 | // _ACRTIMP size_t __cdecl wcslen(wchar_t const *_String); |
639 | 0 | /* __declspec(dllimport) */ size_t __cdecl wcslen(const wchar_t *); |
640 | 0 |
|
641 | 0 | // lldb specific code |
642 | 0 |
|
643 | 0 | struct __lldb_LoadLibraryResult { |
644 | 0 | void *ImageBase; |
645 | 0 | char *ModulePath; |
646 | 0 | unsigned Length; |
647 | 0 | unsigned ErrorCode; |
648 | 0 | }; |
649 | 0 |
|
650 | 0 | _Static_assert(sizeof(struct __lldb_LoadLibraryResult) <= 3 * sizeof(void *), |
651 | 0 | "__lldb_LoadLibraryResult size mismatch"); |
652 | 0 |
|
653 | 0 | void * __lldb_LoadLibraryHelper(const wchar_t *name, const wchar_t *paths, |
654 | 0 | __lldb_LoadLibraryResult *result) { |
655 | 0 | for (const wchar_t *path = paths; path && *path; ) { |
656 | 0 | (void)AddDllDirectory(path); |
657 | 0 | path += wcslen(path) + 1; |
658 | 0 | } |
659 | 0 |
|
660 | 0 | result->ImageBase = LoadLibraryExW(name, nullptr, |
661 | 0 | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); |
662 | 0 | if (result->ImageBase == nullptr) |
663 | 0 | result->ErrorCode = GetLastError(); |
664 | 0 | else |
665 | 0 | result->Length = GetModuleFileNameA(result->ImageBase, result->ModulePath, |
666 | 0 | result->Length); |
667 | 0 |
|
668 | 0 | return result->ImageBase; |
669 | 0 | } |
670 | 0 | } |
671 | 0 | )"; |
672 | |
|
673 | 0 | static constexpr const char kName[] = "__lldb_LoadLibraryHelper"; |
674 | |
|
675 | 0 | ProcessSP process = context.GetProcessSP(); |
676 | 0 | Target &target = process->GetTarget(); |
677 | |
|
678 | 0 | auto function = target.CreateUtilityFunction(std::string{kLoaderDecls}, kName, |
679 | 0 | eLanguageTypeC_plus_plus, |
680 | 0 | context); |
681 | 0 | if (!function) { |
682 | 0 | std::string error = llvm::toString(function.takeError()); |
683 | 0 | status.SetErrorStringWithFormat("LoadLibrary error: could not create utility function: %s", |
684 | 0 | error.c_str()); |
685 | 0 | return nullptr; |
686 | 0 | } |
687 | | |
688 | 0 | TypeSystemClangSP scratch_ts_sp = |
689 | 0 | ScratchTypeSystemClang::GetForTarget(target); |
690 | 0 | if (!scratch_ts_sp) |
691 | 0 | return nullptr; |
692 | | |
693 | 0 | CompilerType VoidPtrTy = |
694 | 0 | scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType(); |
695 | 0 | CompilerType WCharPtrTy = |
696 | 0 | scratch_ts_sp->GetBasicType(eBasicTypeWChar).GetPointerType(); |
697 | |
|
698 | 0 | ValueList parameters; |
699 | |
|
700 | 0 | Value value; |
701 | 0 | value.SetValueType(Value::ValueType::Scalar); |
702 | |
|
703 | 0 | value.SetCompilerType(WCharPtrTy); |
704 | 0 | parameters.PushValue(value); // name |
705 | 0 | parameters.PushValue(value); // paths |
706 | |
|
707 | 0 | value.SetCompilerType(VoidPtrTy); |
708 | 0 | parameters.PushValue(value); // result |
709 | |
|
710 | 0 | Status error; |
711 | 0 | std::unique_ptr<UtilityFunction> utility{std::move(*function)}; |
712 | 0 | utility->MakeFunctionCaller(VoidPtrTy, parameters, context.GetThreadSP(), |
713 | 0 | error); |
714 | 0 | if (error.Fail()) { |
715 | 0 | status.SetErrorStringWithFormat("LoadLibrary error: could not create function caller: %s", |
716 | 0 | error.AsCString()); |
717 | 0 | return nullptr; |
718 | 0 | } |
719 | | |
720 | 0 | if (!utility->GetFunctionCaller()) { |
721 | 0 | status.SetErrorString("LoadLibrary error: could not get function caller"); |
722 | 0 | return nullptr; |
723 | 0 | } |
724 | | |
725 | 0 | return utility; |
726 | 0 | } |
727 | | |
728 | | Status PlatformWindows::EvaluateLoaderExpression(Process *process, |
729 | | const char *expression, |
730 | 0 | ValueObjectSP &value) { |
731 | | // FIXME(compnerd) `-fdeclspec` is not passed to the clang instance? |
732 | 0 | static constexpr const char kLoaderDecls[] = R"( |
733 | 0 | extern "C" { |
734 | 0 | // libloaderapi.h |
735 | 0 |
|
736 | 0 | // WINBASEAPI DLL_DIRECTORY_COOKIE WINAPI AddDllDirectory(LPCWSTR); |
737 | 0 | /* __declspec(dllimport) */ void * __stdcall AddDllDirectory(const wchar_t *); |
738 | 0 |
|
739 | 0 | // WINBASEAPI BOOL WINAPI FreeModule(HMODULE); |
740 | 0 | /* __declspec(dllimport) */ int __stdcall FreeModule(void *); |
741 | 0 |
|
742 | 0 | // WINBASEAPI DWORD WINAPI GetModuleFileNameA(HMODULE, LPSTR, DWORD); |
743 | 0 | /* __declspec(dllimport) */ uint32_t GetModuleFileNameA(void *, char *, uint32_t); |
744 | 0 |
|
745 | 0 | // WINBASEAPI HMODULE WINAPI LoadLibraryExW(LPCWSTR, HANDLE, DWORD); |
746 | 0 | /* __declspec(dllimport) */ void * __stdcall LoadLibraryExW(const wchar_t *, void *, uint32_t); |
747 | 0 | } |
748 | 0 | )"; |
749 | |
|
750 | 0 | if (DynamicLoader *loader = process->GetDynamicLoader()) { |
751 | 0 | Status result = loader->CanLoadImage(); |
752 | 0 | if (result.Fail()) |
753 | 0 | return result; |
754 | 0 | } |
755 | | |
756 | 0 | ThreadSP thread = process->GetThreadList().GetExpressionExecutionThread(); |
757 | 0 | if (!thread) |
758 | 0 | return Status("selected thread is invalid"); |
759 | | |
760 | 0 | StackFrameSP frame = thread->GetStackFrameAtIndex(0); |
761 | 0 | if (!frame) |
762 | 0 | return Status("frame 0 is invalid"); |
763 | | |
764 | 0 | ExecutionContext context; |
765 | 0 | frame->CalculateExecutionContext(context); |
766 | |
|
767 | 0 | EvaluateExpressionOptions options; |
768 | 0 | options.SetUnwindOnError(true); |
769 | 0 | options.SetIgnoreBreakpoints(true); |
770 | 0 | options.SetExecutionPolicy(eExecutionPolicyAlways); |
771 | 0 | options.SetLanguage(eLanguageTypeC_plus_plus); |
772 | | // LoadLibraryEx{A,W}/FreeLibrary cannot raise exceptions which we can handle. |
773 | | // They may potentially throw SEH exceptions which we do not know how to |
774 | | // handle currently. |
775 | 0 | options.SetTrapExceptions(false); |
776 | 0 | options.SetTimeout(process->GetUtilityExpressionTimeout()); |
777 | |
|
778 | 0 | Status error; |
779 | 0 | ExpressionResults result = UserExpression::Evaluate( |
780 | 0 | context, options, expression, kLoaderDecls, value, error); |
781 | 0 | if (result != eExpressionCompleted) |
782 | 0 | return error; |
783 | | |
784 | 0 | if (value->GetError().Fail()) |
785 | 0 | return value->GetError(); |
786 | | |
787 | 0 | return Status(); |
788 | 0 | } |