1 | //===-- PlatformDarwin.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 "PlatformDarwin.h" |
10 | |
11 | #include <cstring> |
12 | |
13 | #include <algorithm> |
14 | #include <memory> |
15 | #include <mutex> |
16 | #include <optional> |
17 | |
18 | #include "lldb/Breakpoint/BreakpointLocation.h" |
19 | #include "lldb/Breakpoint/BreakpointSite.h" |
20 | #include "lldb/Core/Debugger.h" |
21 | #include "lldb/Core/Module.h" |
22 | #include "lldb/Core/ModuleSpec.h" |
23 | #include "lldb/Core/PluginManager.h" |
24 | #include "lldb/Core/Section.h" |
25 | #include "lldb/Host/Host.h" |
26 | #include "lldb/Host/HostInfo.h" |
27 | #include "lldb/Host/XML.h" |
28 | #include "lldb/Interpreter/CommandInterpreter.h" |
29 | #include "lldb/Interpreter/OptionValueProperties.h" |
30 | #include "lldb/Interpreter/OptionValueString.h" |
31 | #include "lldb/Interpreter/Options.h" |
32 | #include "lldb/Symbol/ObjectFile.h" |
33 | #include "lldb/Symbol/SymbolFile.h" |
34 | #include "lldb/Symbol/SymbolVendor.h" |
35 | #include "lldb/Target/Platform.h" |
36 | #include "lldb/Target/Process.h" |
37 | #include "lldb/Target/Target.h" |
38 | #include "lldb/Utility/LLDBLog.h" |
39 | #include "lldb/Utility/Log.h" |
40 | #include "lldb/Utility/ProcessInfo.h" |
41 | #include "lldb/Utility/Status.h" |
42 | #include "lldb/Utility/Timer.h" |
43 | #include "llvm/ADT/STLExtras.h" |
44 | #include "llvm/Support/Error.h" |
45 | #include "llvm/Support/FileSystem.h" |
46 | #include "llvm/Support/Threading.h" |
47 | #include "llvm/Support/VersionTuple.h" |
48 | |
49 | #if defined(__APPLE__) |
50 | #include <TargetConditionals.h> |
51 | #endif |
52 | |
53 | using namespace lldb; |
54 | using namespace lldb_private; |
55 | |
56 | static Status ExceptionMaskValidator(const char *string, void *unused) { |
57 | Status error; |
58 | llvm::StringRef str_ref(string); |
59 | llvm::SmallVector<llvm::StringRef> candidates; |
60 | str_ref.split(A&: candidates, Separator: '|'); |
61 | for (auto candidate : candidates) { |
62 | if (!(candidate == "EXC_BAD_ACCESS" |
63 | || candidate == "EXC_BAD_INSTRUCTION" |
64 | || candidate == "EXC_ARITHMETIC" |
65 | || candidate == "EXC_RESOURCE" |
66 | || candidate == "EXC_GUARD" |
67 | || candidate == "EXC_SYSCALL" )) { |
68 | error.SetErrorStringWithFormat("invalid exception type: '%s'" , |
69 | candidate.str().c_str()); |
70 | return error; |
71 | } |
72 | } |
73 | return {}; |
74 | } |
75 | |
76 | /// Destructor. |
77 | /// |
78 | /// The destructor is virtual since this class is designed to be |
79 | /// inherited from by the plug-in instance. |
80 | PlatformDarwin::~PlatformDarwin() = default; |
81 | |
82 | // Static Variables |
83 | static uint32_t g_initialize_count = 0; |
84 | |
85 | void PlatformDarwin::Initialize() { |
86 | Platform::Initialize(); |
87 | |
88 | if (g_initialize_count++ == 0) { |
89 | PluginManager::RegisterPlugin(name: PlatformDarwin::GetPluginNameStatic(), |
90 | description: PlatformDarwin::GetDescriptionStatic(), |
91 | create_callback: PlatformDarwin::CreateInstance, |
92 | debugger_init_callback: PlatformDarwin::DebuggerInitialize); |
93 | } |
94 | } |
95 | |
96 | void PlatformDarwin::Terminate() { |
97 | if (g_initialize_count > 0) { |
98 | if (--g_initialize_count == 0) { |
99 | PluginManager::UnregisterPlugin(create_callback: PlatformDarwin::CreateInstance); |
100 | } |
101 | } |
102 | |
103 | Platform::Terminate(); |
104 | } |
105 | |
106 | llvm::StringRef PlatformDarwin::GetDescriptionStatic() { |
107 | return "Darwin platform plug-in." ; |
108 | } |
109 | |
110 | PlatformSP PlatformDarwin::CreateInstance(bool force, const ArchSpec *arch) { |
111 | // We only create subclasses of the PlatformDarwin plugin. |
112 | return PlatformSP(); |
113 | } |
114 | |
115 | #define LLDB_PROPERTIES_platformdarwin |
116 | #include "PlatformMacOSXProperties.inc" |
117 | |
118 | #define LLDB_PROPERTIES_platformdarwin |
119 | enum { |
120 | #include "PlatformMacOSXPropertiesEnum.inc" |
121 | }; |
122 | |
123 | class PlatformDarwinProperties : public Properties { |
124 | public: |
125 | static llvm::StringRef GetSettingName() { |
126 | static constexpr llvm::StringLiteral g_setting_name("darwin" ); |
127 | return g_setting_name; |
128 | } |
129 | |
130 | PlatformDarwinProperties() : Properties() { |
131 | m_collection_sp = std::make_shared<OptionValueProperties>(args: GetSettingName()); |
132 | m_collection_sp->Initialize(setting_definitions: g_platformdarwin_properties); |
133 | } |
134 | |
135 | ~PlatformDarwinProperties() override = default; |
136 | |
137 | const char *GetIgnoredExceptions() const { |
138 | const uint32_t idx = ePropertyIgnoredExceptions; |
139 | const OptionValueString *option_value = |
140 | m_collection_sp->GetPropertyAtIndexAsOptionValueString(idx); |
141 | assert(option_value); |
142 | return option_value->GetCurrentValue(); |
143 | } |
144 | |
145 | OptionValueString *GetIgnoredExceptionValue() { |
146 | const uint32_t idx = ePropertyIgnoredExceptions; |
147 | OptionValueString *option_value = |
148 | m_collection_sp->GetPropertyAtIndexAsOptionValueString(idx); |
149 | assert(option_value); |
150 | return option_value; |
151 | } |
152 | }; |
153 | |
154 | static PlatformDarwinProperties &GetGlobalProperties() { |
155 | static PlatformDarwinProperties g_settings; |
156 | return g_settings; |
157 | } |
158 | |
159 | void PlatformDarwin::DebuggerInitialize( |
160 | lldb_private::Debugger &debugger) { |
161 | if (!PluginManager::GetSettingForPlatformPlugin( |
162 | debugger, setting_name: PlatformDarwinProperties::GetSettingName())) { |
163 | const bool is_global_setting = false; |
164 | PluginManager::CreateSettingForPlatformPlugin( |
165 | debugger, properties_sp: GetGlobalProperties().GetValueProperties(), |
166 | description: "Properties for the Darwin platform plug-in." , is_global_property: is_global_setting); |
167 | OptionValueString *value = GetGlobalProperties().GetIgnoredExceptionValue(); |
168 | value->SetValidator(validator: ExceptionMaskValidator); |
169 | } |
170 | } |
171 | |
172 | Args |
173 | PlatformDarwin::GetExtraStartupCommands() { |
174 | std::string ignored_exceptions |
175 | = GetGlobalProperties().GetIgnoredExceptions(); |
176 | if (ignored_exceptions.empty()) |
177 | return {}; |
178 | Args ret_args; |
179 | std::string packet = "QSetIgnoredExceptions:" ; |
180 | packet.append(str: ignored_exceptions); |
181 | ret_args.AppendArgument(arg_str: packet); |
182 | return ret_args; |
183 | } |
184 | |
185 | lldb_private::Status |
186 | PlatformDarwin::PutFile(const lldb_private::FileSpec &source, |
187 | const lldb_private::FileSpec &destination, uint32_t uid, |
188 | uint32_t gid) { |
189 | // Unconditionally unlink the destination. If it is an executable, |
190 | // simply opening it and truncating its contents would invalidate |
191 | // its cached code signature. |
192 | Unlink(file_spec: destination); |
193 | return PlatformPOSIX::PutFile(source, destination, uid, gid); |
194 | } |
195 | |
196 | FileSpecList PlatformDarwin::LocateExecutableScriptingResources( |
197 | Target *target, Module &module, Stream &feedback_stream) { |
198 | FileSpecList file_list; |
199 | if (target && |
200 | target->GetDebugger().GetScriptLanguage() == eScriptLanguagePython) { |
201 | // NB some extensions might be meaningful and should not be stripped - |
202 | // "this.binary.file" |
203 | // should not lose ".file" but GetFileNameStrippingExtension() will do |
204 | // precisely that. Ideally, we should have a per-platform list of |
205 | // extensions (".exe", ".app", ".dSYM", ".framework") which should be |
206 | // stripped while leaving "this.binary.file" as-is. |
207 | |
208 | FileSpec module_spec = module.GetFileSpec(); |
209 | |
210 | if (module_spec) { |
211 | if (SymbolFile *symfile = module.GetSymbolFile()) { |
212 | ObjectFile *objfile = symfile->GetObjectFile(); |
213 | if (objfile) { |
214 | FileSpec symfile_spec(objfile->GetFileSpec()); |
215 | if (symfile_spec && |
216 | llvm::StringRef(symfile_spec.GetPath()) |
217 | .contains_insensitive(Other: ".dSYM/Contents/Resources/DWARF" ) && |
218 | FileSystem::Instance().Exists(file_spec: symfile_spec)) { |
219 | while (module_spec.GetFilename()) { |
220 | std::string module_basename( |
221 | module_spec.GetFilename().GetCString()); |
222 | std::string original_module_basename(module_basename); |
223 | |
224 | bool was_keyword = false; |
225 | |
226 | // FIXME: for Python, we cannot allow certain characters in |
227 | // module |
228 | // filenames we import. Theoretically, different scripting |
229 | // languages may have different sets of forbidden tokens in |
230 | // filenames, and that should be dealt with by each |
231 | // ScriptInterpreter. For now, we just replace dots with |
232 | // underscores, but if we ever support anything other than |
233 | // Python we will need to rework this |
234 | std::replace(first: module_basename.begin(), last: module_basename.end(), old_value: '.', |
235 | new_value: '_'); |
236 | std::replace(first: module_basename.begin(), last: module_basename.end(), old_value: ' ', |
237 | new_value: '_'); |
238 | std::replace(first: module_basename.begin(), last: module_basename.end(), old_value: '-', |
239 | new_value: '_'); |
240 | ScriptInterpreter *script_interpreter = |
241 | target->GetDebugger().GetScriptInterpreter(); |
242 | if (script_interpreter && |
243 | script_interpreter->IsReservedWord(word: module_basename.c_str())) { |
244 | module_basename.insert(p: module_basename.begin(), c: '_'); |
245 | was_keyword = true; |
246 | } |
247 | |
248 | StreamString path_string; |
249 | StreamString original_path_string; |
250 | // for OSX we are going to be in |
251 | // .dSYM/Contents/Resources/DWARF/<basename> let us go to |
252 | // .dSYM/Contents/Resources/Python/<basename>.py and see if the |
253 | // file exists |
254 | path_string.Printf(format: "%s/../Python/%s.py" , |
255 | symfile_spec.GetDirectory().GetCString(), |
256 | module_basename.c_str()); |
257 | original_path_string.Printf( |
258 | format: "%s/../Python/%s.py" , |
259 | symfile_spec.GetDirectory().GetCString(), |
260 | original_module_basename.c_str()); |
261 | FileSpec script_fspec(path_string.GetString()); |
262 | FileSystem::Instance().Resolve(file_spec&: script_fspec); |
263 | FileSpec orig_script_fspec(original_path_string.GetString()); |
264 | FileSystem::Instance().Resolve(file_spec&: orig_script_fspec); |
265 | |
266 | // if we did some replacements of reserved characters, and a |
267 | // file with the untampered name exists, then warn the user |
268 | // that the file as-is shall not be loaded |
269 | if (module_basename != original_module_basename && |
270 | FileSystem::Instance().Exists(file_spec: orig_script_fspec)) { |
271 | const char *reason_for_complaint = |
272 | was_keyword ? "conflicts with a keyword" |
273 | : "contains reserved characters" ; |
274 | if (FileSystem::Instance().Exists(file_spec: script_fspec)) |
275 | feedback_stream.Printf( |
276 | format: "warning: the symbol file '%s' contains a debug " |
277 | "script. However, its name" |
278 | " '%s' %s and as such cannot be loaded. LLDB will" |
279 | " load '%s' instead. Consider removing the file with " |
280 | "the malformed name to" |
281 | " eliminate this warning.\n" , |
282 | symfile_spec.GetPath().c_str(), |
283 | original_path_string.GetData(), reason_for_complaint, |
284 | path_string.GetData()); |
285 | else |
286 | feedback_stream.Printf( |
287 | format: "warning: the symbol file '%s' contains a debug " |
288 | "script. However, its name" |
289 | " %s and as such cannot be loaded. If you intend" |
290 | " to have this script loaded, please rename '%s' to " |
291 | "'%s' and retry.\n" , |
292 | symfile_spec.GetPath().c_str(), reason_for_complaint, |
293 | original_path_string.GetData(), path_string.GetData()); |
294 | } |
295 | |
296 | if (FileSystem::Instance().Exists(file_spec: script_fspec)) { |
297 | file_list.Append(file: script_fspec); |
298 | break; |
299 | } |
300 | |
301 | // If we didn't find the python file, then keep stripping the |
302 | // extensions and try again |
303 | ConstString filename_no_extension( |
304 | module_spec.GetFileNameStrippingExtension()); |
305 | if (module_spec.GetFilename() == filename_no_extension) |
306 | break; |
307 | |
308 | module_spec.SetFilename(filename_no_extension); |
309 | } |
310 | } |
311 | } |
312 | } |
313 | } |
314 | } |
315 | return file_list; |
316 | } |
317 | |
318 | Status PlatformDarwin::ResolveSymbolFile(Target &target, |
319 | const ModuleSpec &sym_spec, |
320 | FileSpec &sym_file) { |
321 | sym_file = sym_spec.GetSymbolFileSpec(); |
322 | if (FileSystem::Instance().IsDirectory(file_spec: sym_file)) { |
323 | sym_file = PluginManager::FindSymbolFileInBundle( |
324 | dsym_bundle_fspec: sym_file, uuid: sym_spec.GetUUIDPtr(), arch: sym_spec.GetArchitecturePtr()); |
325 | } |
326 | return {}; |
327 | } |
328 | |
329 | Status PlatformDarwin::GetSharedModule( |
330 | const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, |
331 | const FileSpecList *module_search_paths_ptr, |
332 | llvm::SmallVectorImpl<ModuleSP> *old_modules, bool *did_create_ptr) { |
333 | Status error; |
334 | module_sp.reset(); |
335 | |
336 | if (IsRemote()) { |
337 | // If we have a remote platform always, let it try and locate the shared |
338 | // module first. |
339 | if (m_remote_platform_sp) { |
340 | error = m_remote_platform_sp->GetSharedModule( |
341 | module_spec, process, module_sp, module_search_paths_ptr, old_modules, |
342 | did_create_ptr); |
343 | } |
344 | } |
345 | |
346 | if (!module_sp) { |
347 | // Fall back to the local platform and find the file locally |
348 | error = Platform::GetSharedModule(module_spec, process, module_sp, |
349 | module_search_paths_ptr, old_modules, |
350 | did_create_ptr); |
351 | |
352 | const FileSpec &platform_file = module_spec.GetFileSpec(); |
353 | if (!module_sp && module_search_paths_ptr && platform_file) { |
354 | // We can try to pull off part of the file path up to the bundle |
355 | // directory level and try any module search paths... |
356 | FileSpec bundle_directory; |
357 | if (Host::GetBundleDirectory(file: platform_file, bundle_directory)) { |
358 | if (platform_file == bundle_directory) { |
359 | ModuleSpec new_module_spec(module_spec); |
360 | new_module_spec.GetFileSpec() = bundle_directory; |
361 | if (Host::ResolveExecutableInBundle(file&: new_module_spec.GetFileSpec())) { |
362 | Status new_error(Platform::GetSharedModule( |
363 | module_spec: new_module_spec, process, module_sp, module_search_paths_ptr: nullptr, old_modules, |
364 | did_create_ptr)); |
365 | |
366 | if (module_sp) |
367 | return new_error; |
368 | } |
369 | } else { |
370 | char platform_path[PATH_MAX]; |
371 | char bundle_dir[PATH_MAX]; |
372 | platform_file.GetPath(path: platform_path, max_path_length: sizeof(platform_path)); |
373 | const size_t bundle_directory_len = |
374 | bundle_directory.GetPath(path: bundle_dir, max_path_length: sizeof(bundle_dir)); |
375 | char new_path[PATH_MAX]; |
376 | size_t num_module_search_paths = module_search_paths_ptr->GetSize(); |
377 | for (size_t i = 0; i < num_module_search_paths; ++i) { |
378 | const size_t search_path_len = |
379 | module_search_paths_ptr->GetFileSpecAtIndex(idx: i).GetPath( |
380 | path: new_path, max_path_length: sizeof(new_path)); |
381 | if (search_path_len < sizeof(new_path)) { |
382 | snprintf(s: new_path + search_path_len, |
383 | maxlen: sizeof(new_path) - search_path_len, format: "/%s" , |
384 | platform_path + bundle_directory_len); |
385 | FileSpec new_file_spec(new_path); |
386 | if (FileSystem::Instance().Exists(file_spec: new_file_spec)) { |
387 | ModuleSpec new_module_spec(module_spec); |
388 | new_module_spec.GetFileSpec() = new_file_spec; |
389 | Status new_error(Platform::GetSharedModule( |
390 | module_spec: new_module_spec, process, module_sp, module_search_paths_ptr: nullptr, old_modules, |
391 | did_create_ptr)); |
392 | |
393 | if (module_sp) { |
394 | module_sp->SetPlatformFileSpec(new_file_spec); |
395 | return new_error; |
396 | } |
397 | } |
398 | } |
399 | } |
400 | } |
401 | } |
402 | } |
403 | } |
404 | if (module_sp) |
405 | module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); |
406 | return error; |
407 | } |
408 | |
409 | size_t |
410 | PlatformDarwin::GetSoftwareBreakpointTrapOpcode(Target &target, |
411 | BreakpointSite *bp_site) { |
412 | const uint8_t *trap_opcode = nullptr; |
413 | uint32_t trap_opcode_size = 0; |
414 | bool bp_is_thumb = false; |
415 | |
416 | llvm::Triple::ArchType machine = target.GetArchitecture().GetMachine(); |
417 | switch (machine) { |
418 | case llvm::Triple::aarch64_32: |
419 | case llvm::Triple::aarch64: { |
420 | // 'brk #0' or 0xd4200000 in BE byte order |
421 | static const uint8_t g_arm64_breakpoint_opcode[] = {0x00, 0x00, 0x20, 0xD4}; |
422 | trap_opcode = g_arm64_breakpoint_opcode; |
423 | trap_opcode_size = sizeof(g_arm64_breakpoint_opcode); |
424 | } break; |
425 | |
426 | case llvm::Triple::thumb: |
427 | bp_is_thumb = true; |
428 | [[fallthrough]]; |
429 | case llvm::Triple::arm: { |
430 | static const uint8_t g_arm_breakpoint_opcode[] = {0xFE, 0xDE, 0xFF, 0xE7}; |
431 | static const uint8_t g_thumb_breakpooint_opcode[] = {0xFE, 0xDE}; |
432 | |
433 | // Auto detect arm/thumb if it wasn't explicitly specified |
434 | if (!bp_is_thumb) { |
435 | lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetConstituentAtIndex(idx: 0)); |
436 | if (bp_loc_sp) |
437 | bp_is_thumb = bp_loc_sp->GetAddress().GetAddressClass() == |
438 | AddressClass::eCodeAlternateISA; |
439 | } |
440 | if (bp_is_thumb) { |
441 | trap_opcode = g_thumb_breakpooint_opcode; |
442 | trap_opcode_size = sizeof(g_thumb_breakpooint_opcode); |
443 | break; |
444 | } |
445 | trap_opcode = g_arm_breakpoint_opcode; |
446 | trap_opcode_size = sizeof(g_arm_breakpoint_opcode); |
447 | } break; |
448 | |
449 | case llvm::Triple::ppc: |
450 | case llvm::Triple::ppc64: { |
451 | static const uint8_t g_ppc_breakpoint_opcode[] = {0x7F, 0xC0, 0x00, 0x08}; |
452 | trap_opcode = g_ppc_breakpoint_opcode; |
453 | trap_opcode_size = sizeof(g_ppc_breakpoint_opcode); |
454 | } break; |
455 | |
456 | default: |
457 | return Platform::GetSoftwareBreakpointTrapOpcode(target, bp_site); |
458 | } |
459 | |
460 | if (trap_opcode && trap_opcode_size) { |
461 | if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) |
462 | return trap_opcode_size; |
463 | } |
464 | return 0; |
465 | } |
466 | |
467 | bool PlatformDarwin::ModuleIsExcludedForUnconstrainedSearches( |
468 | lldb_private::Target &target, const lldb::ModuleSP &module_sp) { |
469 | if (!module_sp) |
470 | return false; |
471 | |
472 | ObjectFile *obj_file = module_sp->GetObjectFile(); |
473 | if (!obj_file) |
474 | return false; |
475 | |
476 | ObjectFile::Type obj_type = obj_file->GetType(); |
477 | return obj_type == ObjectFile::eTypeDynamicLinker; |
478 | } |
479 | |
480 | void PlatformDarwin::x86GetSupportedArchitectures( |
481 | std::vector<ArchSpec> &archs) { |
482 | ArchSpec host_arch = HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKindDefault); |
483 | archs.push_back(x: host_arch); |
484 | |
485 | if (host_arch.GetCore() == ArchSpec::eCore_x86_64_x86_64h) { |
486 | archs.push_back(x: ArchSpec("x86_64-apple-macosx" )); |
487 | archs.push_back(x: HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKind32)); |
488 | } else { |
489 | ArchSpec host_arch64 = HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKind64); |
490 | if (host_arch.IsExactMatch(rhs: host_arch64)) |
491 | archs.push_back(x: HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKind32)); |
492 | } |
493 | } |
494 | |
495 | static llvm::ArrayRef<const char *> GetCompatibleArchs(ArchSpec::Core core) { |
496 | switch (core) { |
497 | default: |
498 | [[fallthrough]]; |
499 | case ArchSpec::eCore_arm_arm64e: { |
500 | static const char *g_arm64e_compatible_archs[] = { |
501 | "arm64e" , "arm64" , "armv7" , "armv7f" , "armv7k" , "armv7s" , |
502 | "armv7m" , "armv7em" , "armv6m" , "armv6" , "armv5" , "armv4" , |
503 | "arm" , "thumbv7" , "thumbv7f" , "thumbv7k" , "thumbv7s" , "thumbv7m" , |
504 | "thumbv7em" , "thumbv6m" , "thumbv6" , "thumbv5" , "thumbv4t" , "thumb" , |
505 | }; |
506 | return {g_arm64e_compatible_archs}; |
507 | } |
508 | case ArchSpec::eCore_arm_arm64: { |
509 | static const char *g_arm64_compatible_archs[] = { |
510 | "arm64" , "armv7" , "armv7f" , "armv7k" , "armv7s" , "armv7m" , |
511 | "armv7em" , "armv6m" , "armv6" , "armv5" , "armv4" , "arm" , |
512 | "thumbv7" , "thumbv7f" , "thumbv7k" , "thumbv7s" , "thumbv7m" , "thumbv7em" , |
513 | "thumbv6m" , "thumbv6" , "thumbv5" , "thumbv4t" , "thumb" , |
514 | }; |
515 | return {g_arm64_compatible_archs}; |
516 | } |
517 | case ArchSpec::eCore_arm_armv7: { |
518 | static const char *g_armv7_compatible_archs[] = { |
519 | "armv7" , "armv6m" , "armv6" , "armv5" , "armv4" , "arm" , |
520 | "thumbv7" , "thumbv6m" , "thumbv6" , "thumbv5" , "thumbv4t" , "thumb" , |
521 | }; |
522 | return {g_armv7_compatible_archs}; |
523 | } |
524 | case ArchSpec::eCore_arm_armv7f: { |
525 | static const char *g_armv7f_compatible_archs[] = { |
526 | "armv7f" , "armv7" , "armv6m" , "armv6" , "armv5" , |
527 | "armv4" , "arm" , "thumbv7f" , "thumbv7" , "thumbv6m" , |
528 | "thumbv6" , "thumbv5" , "thumbv4t" , "thumb" , |
529 | }; |
530 | return {g_armv7f_compatible_archs}; |
531 | } |
532 | case ArchSpec::eCore_arm_armv7k: { |
533 | static const char *g_armv7k_compatible_archs[] = { |
534 | "armv7k" , "armv7" , "armv6m" , "armv6" , "armv5" , |
535 | "armv4" , "arm" , "thumbv7k" , "thumbv7" , "thumbv6m" , |
536 | "thumbv6" , "thumbv5" , "thumbv4t" , "thumb" , |
537 | }; |
538 | return {g_armv7k_compatible_archs}; |
539 | } |
540 | case ArchSpec::eCore_arm_armv7s: { |
541 | static const char *g_armv7s_compatible_archs[] = { |
542 | "armv7s" , "armv7" , "armv6m" , "armv6" , "armv5" , |
543 | "armv4" , "arm" , "thumbv7s" , "thumbv7" , "thumbv6m" , |
544 | "thumbv6" , "thumbv5" , "thumbv4t" , "thumb" , |
545 | }; |
546 | return {g_armv7s_compatible_archs}; |
547 | } |
548 | case ArchSpec::eCore_arm_armv7m: { |
549 | static const char *g_armv7m_compatible_archs[] = { |
550 | "armv7m" , "armv7" , "armv6m" , "armv6" , "armv5" , |
551 | "armv4" , "arm" , "thumbv7m" , "thumbv7" , "thumbv6m" , |
552 | "thumbv6" , "thumbv5" , "thumbv4t" , "thumb" , |
553 | }; |
554 | return {g_armv7m_compatible_archs}; |
555 | } |
556 | case ArchSpec::eCore_arm_armv7em: { |
557 | static const char *g_armv7em_compatible_archs[] = { |
558 | "armv7em" , "armv7" , "armv6m" , "armv6" , "armv5" , |
559 | "armv4" , "arm" , "thumbv7em" , "thumbv7" , "thumbv6m" , |
560 | "thumbv6" , "thumbv5" , "thumbv4t" , "thumb" , |
561 | }; |
562 | return {g_armv7em_compatible_archs}; |
563 | } |
564 | case ArchSpec::eCore_arm_armv6m: { |
565 | static const char *g_armv6m_compatible_archs[] = { |
566 | "armv6m" , "armv6" , "armv5" , "armv4" , "arm" , |
567 | "thumbv6m" , "thumbv6" , "thumbv5" , "thumbv4t" , "thumb" , |
568 | }; |
569 | return {g_armv6m_compatible_archs}; |
570 | } |
571 | case ArchSpec::eCore_arm_armv6: { |
572 | static const char *g_armv6_compatible_archs[] = { |
573 | "armv6" , "armv5" , "armv4" , "arm" , |
574 | "thumbv6" , "thumbv5" , "thumbv4t" , "thumb" , |
575 | }; |
576 | return {g_armv6_compatible_archs}; |
577 | } |
578 | case ArchSpec::eCore_arm_armv5: { |
579 | static const char *g_armv5_compatible_archs[] = { |
580 | "armv5" , "armv4" , "arm" , "thumbv5" , "thumbv4t" , "thumb" , |
581 | }; |
582 | return {g_armv5_compatible_archs}; |
583 | } |
584 | case ArchSpec::eCore_arm_armv4: { |
585 | static const char *g_armv4_compatible_archs[] = { |
586 | "armv4" , |
587 | "arm" , |
588 | "thumbv4t" , |
589 | "thumb" , |
590 | }; |
591 | return {g_armv4_compatible_archs}; |
592 | } |
593 | } |
594 | return {}; |
595 | } |
596 | |
597 | /// The architecture selection rules for arm processors These cpu subtypes have |
598 | /// distinct names (e.g. armv7f) but armv7 binaries run fine on an armv7f |
599 | /// processor. |
600 | void PlatformDarwin::ARMGetSupportedArchitectures( |
601 | std::vector<ArchSpec> &archs, std::optional<llvm::Triple::OSType> os) { |
602 | const ArchSpec system_arch = GetSystemArchitecture(); |
603 | const ArchSpec::Core system_core = system_arch.GetCore(); |
604 | for (const char *arch : GetCompatibleArchs(core: system_core)) { |
605 | llvm::Triple triple; |
606 | triple.setArchName(arch); |
607 | triple.setVendor(llvm::Triple::VendorType::Apple); |
608 | if (os) |
609 | triple.setOS(*os); |
610 | archs.push_back(x: ArchSpec(triple)); |
611 | } |
612 | } |
613 | |
614 | static FileSpec GetXcodeSelectPath() { |
615 | static FileSpec g_xcode_select_filespec; |
616 | |
617 | if (!g_xcode_select_filespec) { |
618 | FileSpec xcode_select_cmd("/usr/bin/xcode-select" ); |
619 | if (FileSystem::Instance().Exists(file_spec: xcode_select_cmd)) { |
620 | int exit_status = -1; |
621 | int signo = -1; |
622 | std::string command_output; |
623 | Status status = |
624 | Host::RunShellCommand(command: "/usr/bin/xcode-select --print-path" , |
625 | working_dir: FileSpec(), // current working directory |
626 | status_ptr: &exit_status, signo_ptr: &signo, command_output: &command_output, |
627 | timeout: std::chrono::seconds(2), // short timeout |
628 | run_in_shell: false); // don't run in a shell |
629 | if (status.Success() && exit_status == 0 && !command_output.empty()) { |
630 | size_t first_non_newline = command_output.find_last_not_of(s: "\r\n" ); |
631 | if (first_non_newline != std::string::npos) { |
632 | command_output.erase(pos: first_non_newline + 1); |
633 | } |
634 | g_xcode_select_filespec = FileSpec(command_output); |
635 | } |
636 | } |
637 | } |
638 | |
639 | return g_xcode_select_filespec; |
640 | } |
641 | |
642 | BreakpointSP PlatformDarwin::SetThreadCreationBreakpoint(Target &target) { |
643 | BreakpointSP bp_sp; |
644 | static const char *g_bp_names[] = { |
645 | "start_wqthread" , "_pthread_wqthread" , "_pthread_start" , |
646 | }; |
647 | |
648 | static const char *g_bp_modules[] = {"libsystem_c.dylib" , |
649 | "libSystem.B.dylib" }; |
650 | |
651 | FileSpecList bp_modules; |
652 | for (size_t i = 0; i < std::size(g_bp_modules); i++) { |
653 | const char *bp_module = g_bp_modules[i]; |
654 | bp_modules.EmplaceBack(args&: bp_module); |
655 | } |
656 | |
657 | bool internal = true; |
658 | bool hardware = false; |
659 | LazyBool skip_prologue = eLazyBoolNo; |
660 | bp_sp = target.CreateBreakpoint(containingModules: &bp_modules, containingSourceFiles: nullptr, func_names: g_bp_names, |
661 | num_names: std::size(g_bp_names), func_name_type_mask: eFunctionNameTypeFull, |
662 | language: eLanguageTypeUnknown, offset: 0, skip_prologue, |
663 | internal, request_hardware: hardware); |
664 | bp_sp->SetBreakpointKind("thread-creation" ); |
665 | |
666 | return bp_sp; |
667 | } |
668 | |
669 | uint32_t |
670 | PlatformDarwin::GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) { |
671 | const FileSpec &shell = launch_info.GetShell(); |
672 | if (!shell) |
673 | return 1; |
674 | |
675 | std::string shell_string = shell.GetPath(); |
676 | const char *shell_name = strrchr(s: shell_string.c_str(), c: '/'); |
677 | if (shell_name == nullptr) |
678 | shell_name = shell_string.c_str(); |
679 | else |
680 | shell_name++; |
681 | |
682 | if (strcmp(s1: shell_name, s2: "sh" ) == 0) { |
683 | // /bin/sh re-exec's itself as /bin/bash requiring another resume. But it |
684 | // only does this if the COMMAND_MODE environment variable is set to |
685 | // "legacy". |
686 | if (launch_info.GetEnvironment().lookup(Key: "COMMAND_MODE" ) == "legacy" ) |
687 | return 2; |
688 | return 1; |
689 | } else if (strcmp(s1: shell_name, s2: "csh" ) == 0 || |
690 | strcmp(s1: shell_name, s2: "tcsh" ) == 0 || |
691 | strcmp(s1: shell_name, s2: "zsh" ) == 0) { |
692 | // csh and tcsh always seem to re-exec themselves. |
693 | return 2; |
694 | } else |
695 | return 1; |
696 | } |
697 | |
698 | lldb::ProcessSP PlatformDarwin::DebugProcess(ProcessLaunchInfo &launch_info, |
699 | Debugger &debugger, Target &target, |
700 | Status &error) { |
701 | ProcessSP process_sp; |
702 | |
703 | if (IsHost()) { |
704 | // We are going to hand this process off to debugserver which will be in |
705 | // charge of setting the exit status. However, we still need to reap it |
706 | // from lldb. So, make sure we use a exit callback which does not set exit |
707 | // status. |
708 | launch_info.SetMonitorProcessCallback( |
709 | &ProcessLaunchInfo::NoOpMonitorCallback); |
710 | process_sp = Platform::DebugProcess(launch_info, debugger, target, error); |
711 | } else { |
712 | if (m_remote_platform_sp) |
713 | process_sp = m_remote_platform_sp->DebugProcess(launch_info, debugger, |
714 | target, error); |
715 | else |
716 | error.SetErrorString("the platform is not currently connected" ); |
717 | } |
718 | return process_sp; |
719 | } |
720 | |
721 | void PlatformDarwin::CalculateTrapHandlerSymbolNames() { |
722 | m_trap_handlers.push_back(x: ConstString("_sigtramp" )); |
723 | } |
724 | |
725 | static FileSpec GetCommandLineToolsLibraryPath() { |
726 | static FileSpec g_command_line_tools_filespec; |
727 | |
728 | if (!g_command_line_tools_filespec) { |
729 | FileSpec command_line_tools_path(GetXcodeSelectPath()); |
730 | command_line_tools_path.AppendPathComponent(component: "Library" ); |
731 | if (FileSystem::Instance().Exists(file_spec: command_line_tools_path)) { |
732 | g_command_line_tools_filespec = command_line_tools_path; |
733 | } |
734 | } |
735 | |
736 | return g_command_line_tools_filespec; |
737 | } |
738 | |
739 | FileSystem::EnumerateDirectoryResult PlatformDarwin::DirectoryEnumerator( |
740 | void *baton, llvm::sys::fs::file_type file_type, llvm::StringRef path) { |
741 | SDKEnumeratorInfo *enumerator_info = static_cast<SDKEnumeratorInfo *>(baton); |
742 | |
743 | FileSpec spec(path); |
744 | if (XcodeSDK::SDKSupportsModules(desired_type: enumerator_info->sdk_type, sdk_path: spec)) { |
745 | enumerator_info->found_path = spec; |
746 | return FileSystem::EnumerateDirectoryResult::eEnumerateDirectoryResultNext; |
747 | } |
748 | |
749 | return FileSystem::EnumerateDirectoryResult::eEnumerateDirectoryResultNext; |
750 | } |
751 | |
752 | FileSpec PlatformDarwin::FindSDKInXcodeForModules(XcodeSDK::Type sdk_type, |
753 | const FileSpec &sdks_spec) { |
754 | // Look inside Xcode for the required installed iOS SDK version |
755 | |
756 | if (!FileSystem::Instance().IsDirectory(file_spec: sdks_spec)) { |
757 | return FileSpec(); |
758 | } |
759 | |
760 | const bool find_directories = true; |
761 | const bool find_files = false; |
762 | const bool find_other = true; // include symlinks |
763 | |
764 | SDKEnumeratorInfo enumerator_info; |
765 | |
766 | enumerator_info.sdk_type = sdk_type; |
767 | |
768 | FileSystem::Instance().EnumerateDirectory( |
769 | path: sdks_spec.GetPath(), find_directories, find_files, find_other, |
770 | callback: DirectoryEnumerator, callback_baton: &enumerator_info); |
771 | |
772 | if (FileSystem::Instance().IsDirectory(file_spec: enumerator_info.found_path)) |
773 | return enumerator_info.found_path; |
774 | else |
775 | return FileSpec(); |
776 | } |
777 | |
778 | FileSpec PlatformDarwin::GetSDKDirectoryForModules(XcodeSDK::Type sdk_type) { |
779 | FileSpec sdks_spec = HostInfo::GetXcodeContentsDirectory(); |
780 | sdks_spec.AppendPathComponent(component: "Developer" ); |
781 | sdks_spec.AppendPathComponent(component: "Platforms" ); |
782 | |
783 | switch (sdk_type) { |
784 | case XcodeSDK::Type::MacOSX: |
785 | sdks_spec.AppendPathComponent(component: "MacOSX.platform" ); |
786 | break; |
787 | case XcodeSDK::Type::iPhoneSimulator: |
788 | sdks_spec.AppendPathComponent(component: "iPhoneSimulator.platform" ); |
789 | break; |
790 | case XcodeSDK::Type::iPhoneOS: |
791 | sdks_spec.AppendPathComponent(component: "iPhoneOS.platform" ); |
792 | break; |
793 | case XcodeSDK::Type::WatchSimulator: |
794 | sdks_spec.AppendPathComponent(component: "WatchSimulator.platform" ); |
795 | break; |
796 | case XcodeSDK::Type::AppleTVSimulator: |
797 | sdks_spec.AppendPathComponent(component: "AppleTVSimulator.platform" ); |
798 | break; |
799 | case XcodeSDK::Type::XRSimulator: |
800 | sdks_spec.AppendPathComponent(component: "XRSimulator.platform" ); |
801 | break; |
802 | default: |
803 | llvm_unreachable("unsupported sdk" ); |
804 | } |
805 | |
806 | sdks_spec.AppendPathComponent(component: "Developer" ); |
807 | sdks_spec.AppendPathComponent(component: "SDKs" ); |
808 | |
809 | if (sdk_type == XcodeSDK::Type::MacOSX) { |
810 | llvm::VersionTuple version = HostInfo::GetOSVersion(); |
811 | |
812 | if (!version.empty()) { |
813 | if (XcodeSDK::SDKSupportsModules(type: XcodeSDK::Type::MacOSX, version)) { |
814 | // If the Xcode SDKs are not available then try to use the |
815 | // Command Line Tools one which is only for MacOSX. |
816 | if (!FileSystem::Instance().Exists(file_spec: sdks_spec)) { |
817 | sdks_spec = GetCommandLineToolsLibraryPath(); |
818 | sdks_spec.AppendPathComponent(component: "SDKs" ); |
819 | } |
820 | |
821 | // We slightly prefer the exact SDK for this machine. See if it is |
822 | // there. |
823 | |
824 | FileSpec native_sdk_spec = sdks_spec; |
825 | StreamString native_sdk_name; |
826 | native_sdk_name.Printf(format: "MacOSX%u.%u.sdk" , version.getMajor(), |
827 | version.getMinor().value_or(u: 0)); |
828 | native_sdk_spec.AppendPathComponent(component: native_sdk_name.GetString()); |
829 | |
830 | if (FileSystem::Instance().Exists(file_spec: native_sdk_spec)) { |
831 | return native_sdk_spec; |
832 | } |
833 | } |
834 | } |
835 | } |
836 | |
837 | return FindSDKInXcodeForModules(sdk_type, sdks_spec); |
838 | } |
839 | |
840 | std::tuple<llvm::VersionTuple, llvm::StringRef> |
841 | PlatformDarwin::ParseVersionBuildDir(llvm::StringRef dir) { |
842 | llvm::StringRef build; |
843 | llvm::StringRef version_str; |
844 | llvm::StringRef build_str; |
845 | std::tie(args&: version_str, args&: build_str) = dir.split(Separator: ' '); |
846 | llvm::VersionTuple version; |
847 | if (!version.tryParse(string: version_str) || |
848 | build_str.empty()) { |
849 | if (build_str.consume_front(Prefix: "(" )) { |
850 | size_t pos = build_str.find(C: ')'); |
851 | build = build_str.slice(Start: 0, End: pos); |
852 | } |
853 | } |
854 | |
855 | return std::make_tuple(args&: version, args&: build); |
856 | } |
857 | |
858 | llvm::Expected<StructuredData::DictionarySP> |
859 | PlatformDarwin::FetchExtendedCrashInformation(Process &process) { |
860 | StructuredData::DictionarySP extended_crash_info = |
861 | std::make_shared<StructuredData::Dictionary>(); |
862 | |
863 | StructuredData::ArraySP annotations = ExtractCrashInfoAnnotations(process); |
864 | if (annotations && annotations->GetSize()) |
865 | extended_crash_info->AddItem(key: "Crash-Info Annotations" , value_sp: annotations); |
866 | |
867 | StructuredData::DictionarySP app_specific_info = |
868 | ExtractAppSpecificInfo(process); |
869 | if (app_specific_info && app_specific_info->GetSize()) |
870 | extended_crash_info->AddItem(key: "Application Specific Information" , |
871 | value_sp: app_specific_info); |
872 | |
873 | return extended_crash_info->GetSize() ? extended_crash_info : nullptr; |
874 | } |
875 | |
876 | StructuredData::ArraySP |
877 | PlatformDarwin::(Process &process) { |
878 | Log *log = GetLog(mask: LLDBLog::Process); |
879 | |
880 | ConstString section_name("__crash_info" ); |
881 | Target &target = process.GetTarget(); |
882 | StructuredData::ArraySP array_sp = std::make_shared<StructuredData::Array>(); |
883 | |
884 | for (ModuleSP module : target.GetImages().Modules()) { |
885 | SectionList *sections = module->GetSectionList(); |
886 | |
887 | std::string module_name = module->GetSpecificationDescription(); |
888 | |
889 | // The DYDL module is skipped since it's always loaded when running the |
890 | // binary. |
891 | if (module_name == "/usr/lib/dyld" ) |
892 | continue; |
893 | |
894 | if (!sections) { |
895 | LLDB_LOG(log, "Module {0} doesn't have any section!" , module_name); |
896 | continue; |
897 | } |
898 | |
899 | SectionSP crash_info = sections->FindSectionByName(section_dstr: section_name); |
900 | if (!crash_info) { |
901 | LLDB_LOG(log, "Module {0} doesn't have section {1}!" , module_name, |
902 | section_name); |
903 | continue; |
904 | } |
905 | |
906 | addr_t load_addr = crash_info->GetLoadBaseAddress(target: &target); |
907 | |
908 | if (load_addr == LLDB_INVALID_ADDRESS) { |
909 | LLDB_LOG(log, "Module {0} has an invalid '{1}' section load address: {2}" , |
910 | module_name, section_name, load_addr); |
911 | continue; |
912 | } |
913 | |
914 | Status error; |
915 | CrashInfoAnnotations annotations; |
916 | size_t expected_size = sizeof(CrashInfoAnnotations); |
917 | size_t bytes_read = process.ReadMemoryFromInferior(vm_addr: load_addr, buf: &annotations, |
918 | size: expected_size, error); |
919 | |
920 | if (expected_size != bytes_read || error.Fail()) { |
921 | LLDB_LOG(log, "Failed to read {0} section from memory in module {1}: {2}" , |
922 | section_name, module_name, error); |
923 | continue; |
924 | } |
925 | |
926 | // initial support added for version 5 |
927 | if (annotations.version < 5) { |
928 | LLDB_LOG(log, |
929 | "Annotation version lower than 5 unsupported! Module {0} has " |
930 | "version {1} instead." , |
931 | module_name, annotations.version); |
932 | continue; |
933 | } |
934 | |
935 | if (!annotations.message) { |
936 | LLDB_LOG(log, "No message available for module {0}." , module_name); |
937 | continue; |
938 | } |
939 | |
940 | std::string message; |
941 | bytes_read = |
942 | process.ReadCStringFromMemory(vm_addr: annotations.message, out_str&: message, error); |
943 | |
944 | if (message.empty() || bytes_read != message.size() || error.Fail()) { |
945 | LLDB_LOG(log, "Failed to read the message from memory in module {0}: {1}" , |
946 | module_name, error); |
947 | continue; |
948 | } |
949 | |
950 | // Remove trailing newline from message |
951 | if (message.back() == '\n') |
952 | message.pop_back(); |
953 | |
954 | if (!annotations.message2) |
955 | LLDB_LOG(log, "No message2 available for module {0}." , module_name); |
956 | |
957 | std::string message2; |
958 | bytes_read = |
959 | process.ReadCStringFromMemory(vm_addr: annotations.message2, out_str&: message2, error); |
960 | |
961 | if (!message2.empty() && bytes_read == message2.size() && error.Success()) |
962 | if (message2.back() == '\n') |
963 | message2.pop_back(); |
964 | |
965 | StructuredData::DictionarySP entry_sp = |
966 | std::make_shared<StructuredData::Dictionary>(); |
967 | |
968 | entry_sp->AddStringItem(key: "image" , value: module->GetFileSpec().GetPath(denormalize: false)); |
969 | entry_sp->AddStringItem(key: "uuid" , value: module->GetUUID().GetAsString()); |
970 | entry_sp->AddStringItem(key: "message" , value: message); |
971 | entry_sp->AddStringItem(key: "message2" , value: message2); |
972 | entry_sp->AddIntegerItem(key: "abort-cause" , value: annotations.abort_cause); |
973 | |
974 | array_sp->AddItem(item: entry_sp); |
975 | } |
976 | |
977 | return array_sp; |
978 | } |
979 | |
980 | StructuredData::DictionarySP |
981 | PlatformDarwin::(Process &process) { |
982 | StructuredData::DictionarySP metadata_sp = process.GetMetadata(); |
983 | |
984 | if (!metadata_sp || !metadata_sp->GetSize() || !metadata_sp->HasKey(key: "asi" )) |
985 | return {}; |
986 | |
987 | StructuredData::Dictionary *asi; |
988 | if (!metadata_sp->GetValueForKeyAsDictionary(key: "asi" , result&: asi)) |
989 | return {}; |
990 | |
991 | StructuredData::DictionarySP dict_sp = |
992 | std::make_shared<StructuredData::Dictionary>(); |
993 | |
994 | auto flatten_asi_dict = [&dict_sp](llvm::StringRef key, |
995 | StructuredData::Object *val) -> bool { |
996 | if (!val) |
997 | return false; |
998 | |
999 | StructuredData::Array *arr = val->GetAsArray(); |
1000 | if (!arr || !arr->GetSize()) |
1001 | return false; |
1002 | |
1003 | dict_sp->AddItem(key, value_sp: arr->GetItemAtIndex(idx: 0)); |
1004 | return true; |
1005 | }; |
1006 | |
1007 | asi->ForEach(callback: flatten_asi_dict); |
1008 | |
1009 | return dict_sp; |
1010 | } |
1011 | |
1012 | void PlatformDarwin::AddClangModuleCompilationOptionsForSDKType( |
1013 | Target *target, std::vector<std::string> &options, XcodeSDK::Type sdk_type) { |
1014 | const std::vector<std::string> apple_arguments = { |
1015 | "-x" , "objective-c++" , "-fobjc-arc" , |
1016 | "-fblocks" , "-D_ISO646_H" , "-D__ISO646_H" , |
1017 | "-fgnuc-version=4.2.1" }; |
1018 | |
1019 | options.insert(position: options.end(), first: apple_arguments.begin(), last: apple_arguments.end()); |
1020 | |
1021 | StreamString minimum_version_option; |
1022 | bool use_current_os_version = false; |
1023 | // If the SDK type is for the host OS, use its version number. |
1024 | auto get_host_os = []() { return HostInfo::GetTargetTriple().getOS(); }; |
1025 | switch (sdk_type) { |
1026 | case XcodeSDK::Type::MacOSX: |
1027 | use_current_os_version = get_host_os() == llvm::Triple::MacOSX; |
1028 | break; |
1029 | case XcodeSDK::Type::iPhoneOS: |
1030 | use_current_os_version = get_host_os() == llvm::Triple::IOS; |
1031 | break; |
1032 | case XcodeSDK::Type::AppleTVOS: |
1033 | use_current_os_version = get_host_os() == llvm::Triple::TvOS; |
1034 | break; |
1035 | case XcodeSDK::Type::watchOS: |
1036 | use_current_os_version = get_host_os() == llvm::Triple::WatchOS; |
1037 | break; |
1038 | case XcodeSDK::Type::XROS: |
1039 | use_current_os_version = get_host_os() == llvm::Triple::XROS; |
1040 | break; |
1041 | default: |
1042 | break; |
1043 | } |
1044 | |
1045 | llvm::VersionTuple version; |
1046 | if (use_current_os_version) |
1047 | version = GetOSVersion(); |
1048 | else if (target) { |
1049 | // Our OS doesn't match our executable so we need to get the min OS version |
1050 | // from the object file |
1051 | ModuleSP exe_module_sp = target->GetExecutableModule(); |
1052 | if (exe_module_sp) { |
1053 | ObjectFile *object_file = exe_module_sp->GetObjectFile(); |
1054 | if (object_file) |
1055 | version = object_file->GetMinimumOSVersion(); |
1056 | } |
1057 | } |
1058 | // Only add the version-min options if we got a version from somewhere. |
1059 | // clang has no version-min clang flag for XROS. |
1060 | if (!version.empty() && sdk_type != XcodeSDK::Type::Linux && |
1061 | sdk_type != XcodeSDK::Type::XROS) { |
1062 | #define OPTION(PREFIX, NAME, VAR, ...) \ |
1063 | llvm::StringRef opt_##VAR = NAME; \ |
1064 | (void)opt_##VAR; |
1065 | #include "clang/Driver/Options.inc" |
1066 | #undef OPTION |
1067 | minimum_version_option << '-'; |
1068 | switch (sdk_type) { |
1069 | case XcodeSDK::Type::MacOSX: |
1070 | minimum_version_option << opt_mmacos_version_min_EQ; |
1071 | break; |
1072 | case XcodeSDK::Type::iPhoneSimulator: |
1073 | minimum_version_option << opt_mios_simulator_version_min_EQ; |
1074 | break; |
1075 | case XcodeSDK::Type::iPhoneOS: |
1076 | minimum_version_option << opt_mios_version_min_EQ; |
1077 | break; |
1078 | case XcodeSDK::Type::AppleTVSimulator: |
1079 | minimum_version_option << opt_mtvos_simulator_version_min_EQ; |
1080 | break; |
1081 | case XcodeSDK::Type::AppleTVOS: |
1082 | minimum_version_option << opt_mtvos_version_min_EQ; |
1083 | break; |
1084 | case XcodeSDK::Type::WatchSimulator: |
1085 | minimum_version_option << opt_mwatchos_simulator_version_min_EQ; |
1086 | break; |
1087 | case XcodeSDK::Type::watchOS: |
1088 | minimum_version_option << opt_mwatchos_version_min_EQ; |
1089 | break; |
1090 | case XcodeSDK::Type::XRSimulator: |
1091 | case XcodeSDK::Type::XROS: |
1092 | // FIXME: Pass the right argument once it exists. |
1093 | case XcodeSDK::Type::bridgeOS: |
1094 | case XcodeSDK::Type::Linux: |
1095 | case XcodeSDK::Type::unknown: |
1096 | if (Log *log = GetLog(LLDBLog::Host)) { |
1097 | XcodeSDK::Info info; |
1098 | info.type = sdk_type; |
1099 | LLDB_LOGF(log, "Clang modules on %s are not supported" , |
1100 | XcodeSDK::GetCanonicalName(info).c_str()); |
1101 | } |
1102 | return; |
1103 | } |
1104 | minimum_version_option << version.getAsString(); |
1105 | options.emplace_back(std::string(minimum_version_option.GetString())); |
1106 | } |
1107 | |
1108 | FileSpec sysroot_spec; |
1109 | |
1110 | if (target) { |
1111 | if (ModuleSP exe_module_sp = target->GetExecutableModule()) { |
1112 | auto path_or_err = ResolveSDKPathFromDebugInfo(*exe_module_sp); |
1113 | if (path_or_err) { |
1114 | sysroot_spec = FileSpec(*path_or_err); |
1115 | } else { |
1116 | LLDB_LOG_ERROR(GetLog(LLDBLog::Types | LLDBLog::Host), |
1117 | path_or_err.takeError(), |
1118 | "Failed to resolve SDK path: {0}" ); |
1119 | } |
1120 | } |
1121 | } |
1122 | |
1123 | if (!FileSystem::Instance().IsDirectory(path: sysroot_spec.GetPath())) { |
1124 | std::lock_guard<std::mutex> guard(m_mutex); |
1125 | sysroot_spec = GetSDKDirectoryForModules(sdk_type); |
1126 | } |
1127 | |
1128 | if (FileSystem::Instance().IsDirectory(path: sysroot_spec.GetPath())) { |
1129 | options.push_back("-isysroot" ); |
1130 | options.push_back(x: sysroot_spec.GetPath()); |
1131 | } |
1132 | } |
1133 | |
1134 | ConstString PlatformDarwin::GetFullNameForDylib(ConstString basename) { |
1135 | if (basename.IsEmpty()) |
1136 | return basename; |
1137 | |
1138 | StreamString stream; |
1139 | stream.Printf(format: "lib%s.dylib" , basename.GetCString()); |
1140 | return ConstString(stream.GetString()); |
1141 | } |
1142 | |
1143 | llvm::VersionTuple PlatformDarwin::GetOSVersion(Process *process) { |
1144 | if (process && GetPluginName().contains(Other: "-simulator" )) { |
1145 | lldb_private::ProcessInstanceInfo proc_info; |
1146 | if (Host::GetProcessInfo(pid: process->GetID(), proc_info)) { |
1147 | const Environment &env = proc_info.GetEnvironment(); |
1148 | |
1149 | llvm::VersionTuple result; |
1150 | if (!result.tryParse(string: env.lookup(Key: "SIMULATOR_RUNTIME_VERSION" ))) |
1151 | return result; |
1152 | |
1153 | std::string dyld_root_path = env.lookup(Key: "DYLD_ROOT_PATH" ); |
1154 | if (!dyld_root_path.empty()) { |
1155 | dyld_root_path += "/System/Library/CoreServices/SystemVersion.plist" ; |
1156 | ApplePropertyList system_version_plist(dyld_root_path.c_str()); |
1157 | std::string product_version; |
1158 | if (system_version_plist.GetValueAsString(key: "ProductVersion" , |
1159 | value&: product_version)) { |
1160 | if (!result.tryParse(string: product_version)) |
1161 | return result; |
1162 | } |
1163 | } |
1164 | } |
1165 | // For simulator platforms, do NOT call back through |
1166 | // Platform::GetOSVersion() as it might call Process::GetHostOSVersion() |
1167 | // which we don't want as it will be incorrect |
1168 | return llvm::VersionTuple(); |
1169 | } |
1170 | |
1171 | return Platform::GetOSVersion(process); |
1172 | } |
1173 | |
1174 | lldb_private::FileSpec PlatformDarwin::LocateExecutable(const char *basename) { |
1175 | // A collection of SBFileSpec whose SBFileSpec.m_directory members are filled |
1176 | // in with any executable directories that should be searched. |
1177 | static std::vector<FileSpec> g_executable_dirs; |
1178 | |
1179 | // Find the global list of directories that we will search for executables |
1180 | // once so we don't keep doing the work over and over. |
1181 | static llvm::once_flag g_once_flag; |
1182 | llvm::call_once(flag&: g_once_flag, F: []() { |
1183 | |
1184 | // When locating executables, trust the DEVELOPER_DIR first if it is set |
1185 | FileSpec xcode_contents_dir = HostInfo::GetXcodeContentsDirectory(); |
1186 | if (xcode_contents_dir) { |
1187 | FileSpec xcode_lldb_resources = xcode_contents_dir; |
1188 | xcode_lldb_resources.AppendPathComponent(component: "SharedFrameworks" ); |
1189 | xcode_lldb_resources.AppendPathComponent(component: "LLDB.framework" ); |
1190 | xcode_lldb_resources.AppendPathComponent(component: "Resources" ); |
1191 | if (FileSystem::Instance().Exists(file_spec: xcode_lldb_resources)) { |
1192 | FileSpec dir; |
1193 | dir.SetDirectory(xcode_lldb_resources.GetPathAsConstString()); |
1194 | g_executable_dirs.push_back(x: dir); |
1195 | } |
1196 | } |
1197 | // Xcode might not be installed so we also check for the Command Line Tools. |
1198 | FileSpec command_line_tools_dir = GetCommandLineToolsLibraryPath(); |
1199 | if (command_line_tools_dir) { |
1200 | FileSpec cmd_line_lldb_resources = command_line_tools_dir; |
1201 | cmd_line_lldb_resources.AppendPathComponent(component: "PrivateFrameworks" ); |
1202 | cmd_line_lldb_resources.AppendPathComponent(component: "LLDB.framework" ); |
1203 | cmd_line_lldb_resources.AppendPathComponent(component: "Resources" ); |
1204 | if (FileSystem::Instance().Exists(file_spec: cmd_line_lldb_resources)) { |
1205 | FileSpec dir; |
1206 | dir.SetDirectory(cmd_line_lldb_resources.GetPathAsConstString()); |
1207 | g_executable_dirs.push_back(x: dir); |
1208 | } |
1209 | } |
1210 | }); |
1211 | |
1212 | // Now search the global list of executable directories for the executable we |
1213 | // are looking for |
1214 | for (const auto &executable_dir : g_executable_dirs) { |
1215 | FileSpec executable_file; |
1216 | executable_file.SetDirectory(executable_dir.GetDirectory()); |
1217 | executable_file.SetFilename(basename); |
1218 | if (FileSystem::Instance().Exists(file_spec: executable_file)) |
1219 | return executable_file; |
1220 | } |
1221 | |
1222 | return FileSpec(); |
1223 | } |
1224 | |
1225 | lldb_private::Status |
1226 | PlatformDarwin::LaunchProcess(lldb_private::ProcessLaunchInfo &launch_info) { |
1227 | // Starting in Fall 2016 OSes, NSLog messages only get mirrored to stderr if |
1228 | // the OS_ACTIVITY_DT_MODE environment variable is set. (It doesn't require |
1229 | // any specific value; rather, it just needs to exist). We will set it here |
1230 | // as long as the IDE_DISABLED_OS_ACTIVITY_DT_MODE flag is not set. Xcode |
1231 | // makes use of IDE_DISABLED_OS_ACTIVITY_DT_MODE to tell |
1232 | // LLDB *not* to muck with the OS_ACTIVITY_DT_MODE flag when they |
1233 | // specifically want it unset. |
1234 | const char *disable_env_var = "IDE_DISABLED_OS_ACTIVITY_DT_MODE" ; |
1235 | auto &env_vars = launch_info.GetEnvironment(); |
1236 | if (!env_vars.count(Key: disable_env_var)) { |
1237 | // We want to make sure that OS_ACTIVITY_DT_MODE is set so that we get |
1238 | // os_log and NSLog messages mirrored to the target process stderr. |
1239 | env_vars.try_emplace(Key: "OS_ACTIVITY_DT_MODE" , Args: "enable" ); |
1240 | } |
1241 | |
1242 | // Let our parent class do the real launching. |
1243 | return PlatformPOSIX::LaunchProcess(launch_info); |
1244 | } |
1245 | |
1246 | lldb_private::Status PlatformDarwin::FindBundleBinaryInExecSearchPaths( |
1247 | const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, |
1248 | const FileSpecList *module_search_paths_ptr, |
1249 | llvm::SmallVectorImpl<ModuleSP> *old_modules, bool *did_create_ptr) { |
1250 | const FileSpec &platform_file = module_spec.GetFileSpec(); |
1251 | // See if the file is present in any of the module_search_paths_ptr |
1252 | // directories. |
1253 | if (!module_sp && module_search_paths_ptr && platform_file) { |
1254 | // create a vector of all the file / directory names in platform_file e.g. |
1255 | // this might be |
1256 | // /System/Library/PrivateFrameworks/UIFoundation.framework/UIFoundation |
1257 | // |
1258 | // We'll need to look in the module_search_paths_ptr directories for both |
1259 | // "UIFoundation" and "UIFoundation.framework" -- most likely the latter |
1260 | // will be the one we find there. |
1261 | |
1262 | std::vector<llvm::StringRef> path_parts = platform_file.GetComponents(); |
1263 | // We want the components in reverse order. |
1264 | std::reverse(first: path_parts.begin(), last: path_parts.end()); |
1265 | const size_t path_parts_size = path_parts.size(); |
1266 | |
1267 | size_t num_module_search_paths = module_search_paths_ptr->GetSize(); |
1268 | for (size_t i = 0; i < num_module_search_paths; ++i) { |
1269 | Log *log_verbose = GetLog(mask: LLDBLog::Host); |
1270 | LLDB_LOGF( |
1271 | log_verbose, |
1272 | "PlatformRemoteDarwinDevice::GetSharedModule searching for binary in " |
1273 | "search-path %s" , |
1274 | module_search_paths_ptr->GetFileSpecAtIndex(i).GetPath().c_str()); |
1275 | // Create a new FileSpec with this module_search_paths_ptr plus just the |
1276 | // filename ("UIFoundation"), then the parent dir plus filename |
1277 | // ("UIFoundation.framework/UIFoundation") etc - up to four names (to |
1278 | // handle "Foo.framework/Contents/MacOS/Foo") |
1279 | |
1280 | for (size_t j = 0; j < 4 && j < path_parts_size - 1; ++j) { |
1281 | FileSpec path_to_try(module_search_paths_ptr->GetFileSpecAtIndex(idx: i)); |
1282 | |
1283 | // Add the components backwards. For |
1284 | // .../PrivateFrameworks/UIFoundation.framework/UIFoundation path_parts |
1285 | // is |
1286 | // [0] UIFoundation |
1287 | // [1] UIFoundation.framework |
1288 | // [2] PrivateFrameworks |
1289 | // |
1290 | // and if 'j' is 2, we want to append path_parts[1] and then |
1291 | // path_parts[0], aka 'UIFoundation.framework/UIFoundation', to the |
1292 | // module_search_paths_ptr path. |
1293 | |
1294 | for (int k = j; k >= 0; --k) { |
1295 | path_to_try.AppendPathComponent(component: path_parts[k]); |
1296 | } |
1297 | |
1298 | if (FileSystem::Instance().Exists(file_spec: path_to_try)) { |
1299 | ModuleSpec new_module_spec(module_spec); |
1300 | new_module_spec.GetFileSpec() = path_to_try; |
1301 | Status new_error( |
1302 | Platform::GetSharedModule(module_spec: new_module_spec, process, module_sp, |
1303 | module_search_paths_ptr: nullptr, old_modules, did_create_ptr)); |
1304 | |
1305 | if (module_sp) { |
1306 | module_sp->SetPlatformFileSpec(path_to_try); |
1307 | return new_error; |
1308 | } |
1309 | } |
1310 | } |
1311 | } |
1312 | } |
1313 | return Status(); |
1314 | } |
1315 | |
1316 | std::string PlatformDarwin::FindComponentInPath(llvm::StringRef path, |
1317 | llvm::StringRef component) { |
1318 | auto begin = llvm::sys::path::begin(path); |
1319 | auto end = llvm::sys::path::end(path); |
1320 | for (auto it = begin; it != end; ++it) { |
1321 | if (it->contains(Other: component)) { |
1322 | llvm::SmallString<128> buffer; |
1323 | llvm::sys::path::append(path&: buffer, begin, end: ++it, |
1324 | style: llvm::sys::path::Style::posix); |
1325 | return buffer.str().str(); |
1326 | } |
1327 | } |
1328 | return {}; |
1329 | } |
1330 | |
1331 | FileSpec PlatformDarwin::GetCurrentToolchainDirectory() { |
1332 | if (FileSpec fspec = HostInfo::GetShlibDir()) |
1333 | return FileSpec(FindComponentInPath(path: fspec.GetPath(), component: ".xctoolchain" )); |
1334 | return {}; |
1335 | } |
1336 | |
1337 | FileSpec PlatformDarwin::GetCurrentCommandLineToolsDirectory() { |
1338 | if (FileSpec fspec = HostInfo::GetShlibDir()) |
1339 | return FileSpec(FindComponentInPath(path: fspec.GetPath(), component: "CommandLineTools" )); |
1340 | return {}; |
1341 | } |
1342 | |
1343 | llvm::Triple::OSType PlatformDarwin::GetHostOSType() { |
1344 | #if !defined(__APPLE__) |
1345 | return llvm::Triple::MacOSX; |
1346 | #else |
1347 | #if TARGET_OS_OSX |
1348 | return llvm::Triple::MacOSX; |
1349 | #elif TARGET_OS_IOS |
1350 | return llvm::Triple::IOS; |
1351 | #elif TARGET_OS_WATCH |
1352 | return llvm::Triple::WatchOS; |
1353 | #elif TARGET_OS_TV |
1354 | return llvm::Triple::TvOS; |
1355 | #elif TARGET_OS_BRIDGE |
1356 | return llvm::Triple::BridgeOS; |
1357 | #elif TARGET_OS_XR |
1358 | return llvm::Triple::XROS; |
1359 | #else |
1360 | #error "LLDB being compiled for an unrecognized Darwin OS" |
1361 | #endif |
1362 | #endif // __APPLE__ |
1363 | } |
1364 | |
1365 | llvm::Expected<std::pair<XcodeSDK, bool>> |
1366 | PlatformDarwin::GetSDKPathFromDebugInfo(Module &module) { |
1367 | SymbolFile *sym_file = module.GetSymbolFile(); |
1368 | if (!sym_file) |
1369 | return llvm::createStringError( |
1370 | EC: llvm::inconvertibleErrorCode(), |
1371 | S: llvm::formatv(Fmt: "No symbol file available for module '{0}'" , |
1372 | Vals: module.GetFileSpec().GetFilename().AsCString(value_if_empty: "" ))); |
1373 | |
1374 | bool found_public_sdk = false; |
1375 | bool found_internal_sdk = false; |
1376 | XcodeSDK merged_sdk; |
1377 | for (unsigned i = 0; i < sym_file->GetNumCompileUnits(); ++i) { |
1378 | if (auto cu_sp = sym_file->GetCompileUnitAtIndex(idx: i)) { |
1379 | auto cu_sdk = sym_file->ParseXcodeSDK(comp_unit&: *cu_sp); |
1380 | bool is_internal_sdk = cu_sdk.IsAppleInternalSDK(); |
1381 | found_public_sdk |= !is_internal_sdk; |
1382 | found_internal_sdk |= is_internal_sdk; |
1383 | |
1384 | merged_sdk.Merge(other: cu_sdk); |
1385 | } |
1386 | } |
1387 | |
1388 | const bool found_mismatch = found_internal_sdk && found_public_sdk; |
1389 | |
1390 | return std::pair{std::move(merged_sdk), found_mismatch}; |
1391 | } |
1392 | |
1393 | llvm::Expected<std::string> |
1394 | PlatformDarwin::ResolveSDKPathFromDebugInfo(Module &module) { |
1395 | auto sdk_or_err = GetSDKPathFromDebugInfo(module); |
1396 | if (!sdk_or_err) |
1397 | return llvm::createStringError( |
1398 | EC: llvm::inconvertibleErrorCode(), |
1399 | S: llvm::formatv(Fmt: "Failed to parse SDK path from debug-info: {0}" , |
1400 | Vals: llvm::toString(E: sdk_or_err.takeError()))); |
1401 | |
1402 | auto [sdk, _] = std::move(*sdk_or_err); |
1403 | |
1404 | auto path_or_err = HostInfo::GetSDKRoot(options: HostInfo::SDKOptions{.XcodeSDKSelection: sdk}); |
1405 | if (!path_or_err) |
1406 | return llvm::createStringError( |
1407 | EC: llvm::inconvertibleErrorCode(), |
1408 | S: llvm::formatv(Fmt: "Error while searching for SDK (XcodeSDK '{0}'): {1}" , |
1409 | Vals: sdk.GetString(), |
1410 | Vals: llvm::toString(E: path_or_err.takeError()))); |
1411 | |
1412 | return path_or_err->str(); |
1413 | } |
1414 | |