xref: /llvm-project/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp (revision c4fb7180cbbe977f1ab1ce945a691550f8fdd1fb)
1 //===-- DynamicLoaderMacOS.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 "lldb/Breakpoint/StoppointCallbackContext.h"
10 #include "lldb/Core/Debugger.h"
11 #include "lldb/Core/Module.h"
12 #include "lldb/Core/PluginManager.h"
13 #include "lldb/Core/Section.h"
14 #include "lldb/Symbol/ObjectFile.h"
15 #include "lldb/Symbol/SymbolVendor.h"
16 #include "lldb/Target/ABI.h"
17 #include "lldb/Target/SectionLoadList.h"
18 #include "lldb/Target/StackFrame.h"
19 #include "lldb/Target/Target.h"
20 #include "lldb/Target/Thread.h"
21 #include "lldb/Utility/LLDBLog.h"
22 #include "lldb/Utility/Log.h"
23 #include "lldb/Utility/State.h"
24 
25 #include "DynamicLoaderDarwin.h"
26 #include "DynamicLoaderMacOS.h"
27 
28 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 
33 // Create an instance of this class. This function is filled into the plugin
34 // info class that gets handed out by the plugin factory and allows the lldb to
35 // instantiate an instance of this class.
36 DynamicLoader *DynamicLoaderMacOS::CreateInstance(Process *process,
37                                                   bool force) {
38   bool create = force;
39   if (!create) {
40     create = true;
41     Module *exe_module = process->GetTarget().GetExecutableModulePointer();
42     if (exe_module) {
43       ObjectFile *object_file = exe_module->GetObjectFile();
44       if (object_file) {
45         create = (object_file->GetStrata() == ObjectFile::eStrataUser);
46       }
47     }
48 
49     if (create) {
50       const llvm::Triple &triple_ref =
51           process->GetTarget().GetArchitecture().GetTriple();
52       switch (triple_ref.getOS()) {
53       case llvm::Triple::Darwin:
54       case llvm::Triple::MacOSX:
55       case llvm::Triple::IOS:
56       case llvm::Triple::TvOS:
57       case llvm::Triple::WatchOS:
58       case llvm::Triple::XROS:
59       case llvm::Triple::BridgeOS:
60         create = triple_ref.getVendor() == llvm::Triple::Apple;
61         break;
62       default:
63         create = false;
64         break;
65       }
66     }
67   }
68 
69   if (!UseDYLDSPI(process)) {
70     create = false;
71   }
72 
73   if (create)
74     return new DynamicLoaderMacOS(process);
75   return nullptr;
76 }
77 
78 // Constructor
79 DynamicLoaderMacOS::DynamicLoaderMacOS(Process *process)
80     : DynamicLoaderDarwin(process), m_image_infos_stop_id(UINT32_MAX),
81       m_break_id(LLDB_INVALID_BREAK_ID),
82       m_dyld_handover_break_id(LLDB_INVALID_BREAK_ID), m_mutex(),
83       m_maybe_image_infos_address(LLDB_INVALID_ADDRESS),
84       m_libsystem_fully_initalized(false) {}
85 
86 // Destructor
87 DynamicLoaderMacOS::~DynamicLoaderMacOS() {
88   if (LLDB_BREAK_ID_IS_VALID(m_break_id))
89     m_process->GetTarget().RemoveBreakpointByID(m_break_id);
90   if (LLDB_BREAK_ID_IS_VALID(m_dyld_handover_break_id))
91     m_process->GetTarget().RemoveBreakpointByID(m_dyld_handover_break_id);
92 }
93 
94 bool DynamicLoaderMacOS::ProcessDidExec() {
95   std::lock_guard<std::recursive_mutex> baseclass_guard(GetMutex());
96   bool did_exec = false;
97   if (m_process) {
98     // If we are stopped after an exec, we will have only one thread...
99     if (m_process->GetThreadList().GetSize() == 1) {
100       // Maybe we still have an image infos address around?  If so see
101       // if that has changed, and if so we have exec'ed.
102       if (m_maybe_image_infos_address != LLDB_INVALID_ADDRESS) {
103         lldb::addr_t image_infos_address = m_process->GetImageInfoAddress();
104         if (image_infos_address != m_maybe_image_infos_address) {
105           // We don't really have to reset this here, since we are going to
106           // call DoInitialImageFetch right away to handle the exec.  But in
107           // case anybody looks at it in the meantime, it can't hurt.
108           m_maybe_image_infos_address = image_infos_address;
109           did_exec = true;
110         }
111       }
112 
113       if (!did_exec) {
114         // See if we are stopped at '_dyld_start'
115         ThreadSP thread_sp(m_process->GetThreadList().GetThreadAtIndex(0));
116         if (thread_sp) {
117           lldb::StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0));
118           if (frame_sp) {
119             const Symbol *symbol =
120                 frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol;
121             if (symbol) {
122               if (symbol->GetName() == "_dyld_start")
123                 did_exec = true;
124             }
125           }
126         }
127       }
128     }
129   }
130 
131   if (did_exec) {
132     m_libpthread_module_wp.reset();
133     m_pthread_getspecific_addr.Clear();
134     m_libsystem_fully_initalized = false;
135   }
136   return did_exec;
137 }
138 
139 // Clear out the state of this class.
140 void DynamicLoaderMacOS::DoClear() {
141   std::lock_guard<std::recursive_mutex> guard(m_mutex);
142 
143   if (LLDB_BREAK_ID_IS_VALID(m_break_id))
144     m_process->GetTarget().RemoveBreakpointByID(m_break_id);
145   if (LLDB_BREAK_ID_IS_VALID(m_dyld_handover_break_id))
146     m_process->GetTarget().RemoveBreakpointByID(m_dyld_handover_break_id);
147 
148   m_break_id = LLDB_INVALID_BREAK_ID;
149   m_dyld_handover_break_id = LLDB_INVALID_BREAK_ID;
150   m_libsystem_fully_initalized = false;
151 }
152 
153 bool DynamicLoaderMacOS::IsFullyInitialized() {
154   if (m_libsystem_fully_initalized)
155     return true;
156 
157   StructuredData::ObjectSP process_state_sp(
158       m_process->GetDynamicLoaderProcessState());
159   if (!process_state_sp)
160     return true;
161   if (process_state_sp->GetAsDictionary()->HasKey("error"))
162     return true;
163   if (!process_state_sp->GetAsDictionary()->HasKey("process_state string"))
164     return true;
165   std::string proc_state = process_state_sp->GetAsDictionary()
166                                ->GetValueForKey("process_state string")
167                                ->GetAsString()
168                                ->GetValue()
169                                .str();
170   if (proc_state == "dyld_process_state_not_started" ||
171       proc_state == "dyld_process_state_dyld_initialized" ||
172       proc_state == "dyld_process_state_terminated_before_inits") {
173     return false;
174   }
175   m_libsystem_fully_initalized = true;
176   return true;
177 }
178 
179 // Check if we have found DYLD yet
180 bool DynamicLoaderMacOS::DidSetNotificationBreakpoint() {
181   return LLDB_BREAK_ID_IS_VALID(m_break_id);
182 }
183 
184 void DynamicLoaderMacOS::ClearNotificationBreakpoint() {
185   if (LLDB_BREAK_ID_IS_VALID(m_break_id)) {
186     m_process->GetTarget().RemoveBreakpointByID(m_break_id);
187     m_break_id = LLDB_INVALID_BREAK_ID;
188   }
189 }
190 
191 // Try and figure out where dyld is by first asking the Process if it knows
192 // (which currently calls down in the lldb::Process to get the DYLD info
193 // (available on SnowLeopard only). If that fails, then check in the default
194 // addresses.
195 void DynamicLoaderMacOS::DoInitialImageFetch() {
196   Log *log = GetLog(LLDBLog::DynamicLoader);
197 
198   // Remove any binaries we pre-loaded in the Target before
199   // launching/attaching. If the same binaries are present in the process,
200   // we'll get them from the shared module cache, we won't need to re-load them
201   // from disk.
202   UnloadAllImages();
203 
204   StructuredData::ObjectSP all_image_info_json_sp(
205       m_process->GetLoadedDynamicLibrariesInfos());
206   ImageInfo::collection image_infos;
207   if (all_image_info_json_sp.get() &&
208       all_image_info_json_sp->GetAsDictionary() &&
209       all_image_info_json_sp->GetAsDictionary()->HasKey("images") &&
210       all_image_info_json_sp->GetAsDictionary()
211           ->GetValueForKey("images")
212           ->GetAsArray()) {
213     if (JSONImageInformationIntoImageInfo(all_image_info_json_sp,
214                                           image_infos)) {
215       LLDB_LOGF(log, "Initial module fetch:  Adding %" PRId64 " modules.\n",
216                 (uint64_t)image_infos.size());
217 
218       auto images = PreloadModulesFromImageInfos(image_infos);
219       UpdateSpecialBinariesFromPreloadedModules(images);
220       AddModulesUsingPreloadedModules(images);
221     }
222   }
223 
224   m_dyld_image_infos_stop_id = m_process->GetStopID();
225   m_maybe_image_infos_address = m_process->GetImageInfoAddress();
226 }
227 
228 bool DynamicLoaderMacOS::NeedToDoInitialImageFetch() { return true; }
229 
230 // Static callback function that gets called when our DYLD notification
231 // breakpoint gets hit. We update all of our image infos and then let our super
232 // class DynamicLoader class decide if we should stop or not (based on global
233 // preference).
234 bool DynamicLoaderMacOS::NotifyBreakpointHit(void *baton,
235                                              StoppointCallbackContext *context,
236                                              lldb::user_id_t break_id,
237                                              lldb::user_id_t break_loc_id) {
238   //
239   // Our breakpoint on
240   //
241   // void lldb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount,
242   // const dyld_image_info info[])
243   //
244   // has been hit.  We need to read the arguments.
245 
246   DynamicLoaderMacOS *dyld_instance = (DynamicLoaderMacOS *)baton;
247 
248   ExecutionContext exe_ctx(context->exe_ctx_ref);
249   Process *process = exe_ctx.GetProcessPtr();
250 
251   // This is a sanity check just in case this dyld_instance is an old dyld
252   // plugin's breakpoint still lying around.
253   if (process != dyld_instance->m_process)
254     return false;
255 
256   if (dyld_instance->m_image_infos_stop_id != UINT32_MAX &&
257       process->GetStopID() < dyld_instance->m_image_infos_stop_id) {
258     return false;
259   }
260 
261   const lldb::ABISP &abi = process->GetABI();
262   if (abi) {
263     // Build up the value array to store the three arguments given above, then
264     // get the values from the ABI:
265 
266     TypeSystemClangSP scratch_ts_sp =
267         ScratchTypeSystemClang::GetForTarget(process->GetTarget());
268     if (!scratch_ts_sp)
269       return false;
270 
271     ValueList argument_values;
272 
273     Value mode_value;    // enum dyld_notify_mode { dyld_notify_adding=0,
274                          // dyld_notify_removing=1, dyld_notify_remove_all=2,
275                          // dyld_notify_dyld_moved=3 };
276     Value count_value;   // uint32_t
277     Value headers_value; // struct dyld_image_info machHeaders[]
278 
279     CompilerType clang_void_ptr_type =
280         scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
281     CompilerType clang_uint32_type =
282         scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint,
283                                                            32);
284     CompilerType clang_uint64_type =
285         scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint,
286                                                            32);
287 
288     mode_value.SetValueType(Value::ValueType::Scalar);
289     mode_value.SetCompilerType(clang_uint32_type);
290 
291     count_value.SetValueType(Value::ValueType::Scalar);
292     count_value.SetCompilerType(clang_uint32_type);
293 
294     headers_value.SetValueType(Value::ValueType::Scalar);
295     headers_value.SetCompilerType(clang_void_ptr_type);
296 
297     argument_values.PushValue(mode_value);
298     argument_values.PushValue(count_value);
299     argument_values.PushValue(headers_value);
300 
301     if (abi->GetArgumentValues(exe_ctx.GetThreadRef(), argument_values)) {
302       uint32_t dyld_mode =
303           argument_values.GetValueAtIndex(0)->GetScalar().UInt(-1);
304       if (dyld_mode != static_cast<uint32_t>(-1)) {
305         // Okay the mode was right, now get the number of elements, and the
306         // array of new elements...
307         uint32_t image_infos_count =
308             argument_values.GetValueAtIndex(1)->GetScalar().UInt(-1);
309         if (image_infos_count != static_cast<uint32_t>(-1)) {
310           addr_t header_array =
311               argument_values.GetValueAtIndex(2)->GetScalar().ULongLong(-1);
312           if (header_array != static_cast<uint64_t>(-1)) {
313             std::vector<addr_t> image_load_addresses;
314             // header_array points to an array of image_infos_count elements,
315             // each is
316             // struct dyld_image_info {
317             //   const struct mach_header* imageLoadAddress;
318             //   const char*               imageFilePath;
319             //   uintptr_t                 imageFileModDate;
320             // };
321             //
322             // and we only need the imageLoadAddress fields.
323 
324             const int addrsize =
325                 process->GetTarget().GetArchitecture().GetAddressByteSize();
326             for (uint64_t i = 0; i < image_infos_count; i++) {
327               Status error;
328               addr_t dyld_image_info = header_array + (addrsize * 3 * i);
329               addr_t addr =
330                   process->ReadPointerFromMemory(dyld_image_info, error);
331               if (error.Success()) {
332                 image_load_addresses.push_back(addr);
333               } else {
334                 Debugger::ReportWarning(
335                     "DynamicLoaderMacOS::NotifyBreakpointHit unable "
336                     "to read binary mach-o load address at 0x%" PRIx64,
337                     addr);
338               }
339             }
340             if (dyld_mode == 0) {
341               // dyld_notify_adding
342               if (process->GetTarget().GetImages().GetSize() == 0) {
343                 // When all images have been removed, we're doing the
344                 // dyld handover from a launch-dyld to a shared-cache-dyld,
345                 // and we've just hit our one-shot address breakpoint in
346                 // the sc-dyld.  Note that the image addresses passed to
347                 // this function are inferior sizeof(void*) not uint64_t's
348                 // like our normal notification, so don't even look at
349                 // image_load_addresses.
350 
351                 dyld_instance->ClearDYLDHandoverBreakpoint();
352 
353                 dyld_instance->DoInitialImageFetch();
354                 dyld_instance->SetNotificationBreakpoint();
355               } else {
356                 dyld_instance->AddBinaries(image_load_addresses);
357               }
358             } else if (dyld_mode == 1) {
359               // dyld_notify_removing
360               dyld_instance->UnloadImages(image_load_addresses);
361             } else if (dyld_mode == 2) {
362               // dyld_notify_remove_all
363               dyld_instance->UnloadAllImages();
364             } else if (dyld_mode == 3 && image_infos_count == 1) {
365               // dyld_image_dyld_moved
366 
367               dyld_instance->ClearNotificationBreakpoint();
368               dyld_instance->UnloadAllImages();
369               dyld_instance->ClearDYLDModule();
370               process->GetTarget().GetImages().Clear();
371               process->GetTarget().ClearSectionLoadList();
372 
373               addr_t all_image_infos = process->GetImageInfoAddress();
374               int addr_size =
375                   process->GetTarget().GetArchitecture().GetAddressByteSize();
376               addr_t notification_location = all_image_infos + 4 + // version
377                                              4 +        // infoArrayCount
378                                              addr_size; // infoArray
379               Status error;
380               addr_t notification_addr =
381                   process->ReadPointerFromMemory(notification_location, error);
382               if (!error.Success()) {
383                 Debugger::ReportWarning(
384                     "DynamicLoaderMacOS::NotifyBreakpointHit unable "
385                     "to read address of dyld-handover notification function at "
386                     "0x%" PRIx64,
387                     notification_location);
388               } else {
389                 notification_addr = process->FixCodeAddress(notification_addr);
390                 dyld_instance->SetDYLDHandoverBreakpoint(notification_addr);
391               }
392             }
393           }
394         }
395       }
396     }
397   } else {
398     Target &target = process->GetTarget();
399     Debugger::ReportWarning(
400         "no ABI plugin located for triple " +
401             target.GetArchitecture().GetTriple().getTriple() +
402             ": shared libraries will not be registered",
403         target.GetDebugger().GetID());
404   }
405 
406   // Return true to stop the target, false to just let the target run
407   return dyld_instance->GetStopWhenImagesChange();
408 }
409 
410 void DynamicLoaderMacOS::AddBinaries(
411     const std::vector<lldb::addr_t> &load_addresses) {
412   Log *log = GetLog(LLDBLog::DynamicLoader);
413   ImageInfo::collection image_infos;
414 
415   LLDB_LOGF(log, "Adding %" PRId64 " modules.",
416             (uint64_t)load_addresses.size());
417   StructuredData::ObjectSP binaries_info_sp =
418       m_process->GetLoadedDynamicLibrariesInfos(load_addresses);
419   if (binaries_info_sp.get() && binaries_info_sp->GetAsDictionary() &&
420       binaries_info_sp->GetAsDictionary()->HasKey("images") &&
421       binaries_info_sp->GetAsDictionary()
422           ->GetValueForKey("images")
423           ->GetAsArray() &&
424       binaries_info_sp->GetAsDictionary()
425               ->GetValueForKey("images")
426               ->GetAsArray()
427               ->GetSize() == load_addresses.size()) {
428     if (JSONImageInformationIntoImageInfo(binaries_info_sp, image_infos)) {
429       auto images = PreloadModulesFromImageInfos(image_infos);
430       UpdateSpecialBinariesFromPreloadedModules(images);
431       AddModulesUsingPreloadedModules(images);
432     }
433     m_dyld_image_infos_stop_id = m_process->GetStopID();
434   }
435 }
436 
437 // Dump the _dyld_all_image_infos members and all current image infos that we
438 // have parsed to the file handle provided.
439 void DynamicLoaderMacOS::PutToLog(Log *log) const {
440   if (log == nullptr)
441     return;
442 }
443 
444 // Look in dyld's dyld_all_image_infos structure for the address
445 // of the notification function.
446 // We can find the address of dyld_all_image_infos by a system
447 // call, even if we don't have a dyld binary registered in lldb's
448 // image list.
449 // At process launch time - before dyld has executed any instructions -
450 // the address of the notification function is not a resolved vm address
451 // yet.  dyld_all_image_infos also has a field with its own address
452 // in it, and this will also be unresolved when we're at this state.
453 // So we can compare the address of the object with this field and if
454 // they differ, dyld hasn't started executing yet and we can't get the
455 // notification address this way.
456 addr_t DynamicLoaderMacOS::GetNotificationFuncAddrFromImageInfos() {
457   addr_t notification_addr = LLDB_INVALID_ADDRESS;
458   if (!m_process)
459     return notification_addr;
460 
461   addr_t all_image_infos_addr = m_process->GetImageInfoAddress();
462   if (all_image_infos_addr == LLDB_INVALID_ADDRESS)
463     return notification_addr;
464 
465   const uint32_t addr_size =
466       m_process->GetTarget().GetArchitecture().GetAddressByteSize();
467   offset_t registered_infos_addr_offset =
468       sizeof(uint32_t) + // version
469       sizeof(uint32_t) + // infoArrayCount
470       addr_size +        // infoArray
471       addr_size +        // notification
472       addr_size +        // processDetachedFromSharedRegion +
473                          // libSystemInitialized + pad
474       addr_size +        // dyldImageLoadAddress
475       addr_size +        // jitInfo
476       addr_size +        // dyldVersion
477       addr_size +        // errorMessage
478       addr_size +        // terminationFlags
479       addr_size +        // coreSymbolicationShmPage
480       addr_size +        // systemOrderFlag
481       addr_size +        // uuidArrayCount
482       addr_size;         // uuidArray
483                          // dyldAllImageInfosAddress
484 
485   // If the dyldAllImageInfosAddress does not match
486   // the actual address of this struct, dyld has not started
487   // executing yet.  The 'notification' field can't be used by
488   // lldb until it's resolved to an actual address.
489   Status error;
490   addr_t registered_infos_addr = m_process->ReadPointerFromMemory(
491       all_image_infos_addr + registered_infos_addr_offset, error);
492   if (!error.Success())
493     return notification_addr;
494   if (registered_infos_addr != all_image_infos_addr)
495     return notification_addr;
496 
497   offset_t notification_fptr_offset = sizeof(uint32_t) + // version
498                                       sizeof(uint32_t) + // infoArrayCount
499                                       addr_size;         // infoArray
500 
501   addr_t notification_fptr = m_process->ReadPointerFromMemory(
502       all_image_infos_addr + notification_fptr_offset, error);
503   if (error.Success())
504     notification_addr = m_process->FixCodeAddress(notification_fptr);
505   return notification_addr;
506 }
507 
508 // We want to put a breakpoint on dyld's lldb_image_notifier()
509 // but we may have attached to the process during the
510 // transition from on-disk-dyld to shared-cache-dyld, so there's
511 // officially no dyld binary loaded in the process (libdyld will
512 // report none when asked), but the kernel can find the dyld_all_image_infos
513 // struct and the function pointer for lldb_image_notifier is in
514 // that struct.
515 bool DynamicLoaderMacOS::SetNotificationBreakpoint() {
516 
517   // First try to find the notification breakpoint function by name
518   if (m_break_id == LLDB_INVALID_BREAK_ID) {
519     ModuleSP dyld_sp(GetDYLDModule());
520     if (dyld_sp) {
521       bool internal = true;
522       bool hardware = false;
523       LazyBool skip_prologue = eLazyBoolNo;
524       FileSpecList *source_files = nullptr;
525       FileSpecList dyld_filelist;
526       dyld_filelist.Append(dyld_sp->GetFileSpec());
527 
528       Breakpoint *breakpoint =
529           m_process->GetTarget()
530               .CreateBreakpoint(&dyld_filelist, source_files,
531                                 "lldb_image_notifier", eFunctionNameTypeFull,
532                                 eLanguageTypeUnknown, 0, skip_prologue,
533                                 internal, hardware)
534               .get();
535       breakpoint->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this,
536                               true);
537       breakpoint->SetBreakpointKind("shared-library-event");
538       if (breakpoint->HasResolvedLocations())
539         m_break_id = breakpoint->GetID();
540       else
541         m_process->GetTarget().RemoveBreakpointByID(breakpoint->GetID());
542 
543       if (m_break_id == LLDB_INVALID_BREAK_ID) {
544         Breakpoint *breakpoint =
545             m_process->GetTarget()
546                 .CreateBreakpoint(&dyld_filelist, source_files,
547                                   "gdb_image_notifier", eFunctionNameTypeFull,
548                                   eLanguageTypeUnknown, 0, skip_prologue,
549                                   internal, hardware)
550                 .get();
551         breakpoint->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this,
552                                 true);
553         breakpoint->SetBreakpointKind("shared-library-event");
554         if (breakpoint->HasResolvedLocations())
555           m_break_id = breakpoint->GetID();
556         else
557           m_process->GetTarget().RemoveBreakpointByID(breakpoint->GetID());
558       }
559     }
560   }
561 
562   // Failing that, find dyld_all_image_infos struct in memory,
563   // read the notification function pointer at the offset.
564   if (m_break_id == LLDB_INVALID_BREAK_ID) {
565     addr_t notification_addr = GetNotificationFuncAddrFromImageInfos();
566     if (notification_addr != LLDB_INVALID_ADDRESS) {
567       Address so_addr;
568       // We may not have a dyld binary mapped to this address yet;
569       // don't try to express the Address object as section+offset,
570       // only as a raw load address.
571       so_addr.SetRawAddress(notification_addr);
572       Breakpoint *dyld_break =
573           m_process->GetTarget().CreateBreakpoint(so_addr, true, false).get();
574       dyld_break->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this,
575                               true);
576       dyld_break->SetBreakpointKind("shared-library-event");
577       if (dyld_break->HasResolvedLocations())
578         m_break_id = dyld_break->GetID();
579       else
580         m_process->GetTarget().RemoveBreakpointByID(dyld_break->GetID());
581     }
582   }
583   return m_break_id != LLDB_INVALID_BREAK_ID;
584 }
585 
586 bool DynamicLoaderMacOS::SetDYLDHandoverBreakpoint(
587     addr_t notification_address) {
588   if (m_dyld_handover_break_id == LLDB_INVALID_BREAK_ID) {
589     BreakpointSP dyld_handover_bp = m_process->GetTarget().CreateBreakpoint(
590         notification_address, true, false);
591     dyld_handover_bp->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this,
592                                   true);
593     dyld_handover_bp->SetOneShot(true);
594     m_dyld_handover_break_id = dyld_handover_bp->GetID();
595     return true;
596   }
597   return false;
598 }
599 
600 void DynamicLoaderMacOS::ClearDYLDHandoverBreakpoint() {
601   if (LLDB_BREAK_ID_IS_VALID(m_dyld_handover_break_id))
602     m_process->GetTarget().RemoveBreakpointByID(m_dyld_handover_break_id);
603   m_dyld_handover_break_id = LLDB_INVALID_BREAK_ID;
604 }
605 
606 addr_t
607 DynamicLoaderMacOS::GetDyldLockVariableAddressFromModule(Module *module) {
608   SymbolContext sc;
609   Target &target = m_process->GetTarget();
610   if (Symtab *symtab = module->GetSymtab()) {
611     std::vector<uint32_t> match_indexes;
612     ConstString g_symbol_name("_dyld_global_lock_held");
613     uint32_t num_matches = 0;
614     num_matches =
615         symtab->AppendSymbolIndexesWithName(g_symbol_name, match_indexes);
616     if (num_matches == 1) {
617       Symbol *symbol = symtab->SymbolAtIndex(match_indexes[0]);
618       if (symbol &&
619           (symbol->ValueIsAddress() || symbol->GetAddressRef().IsValid())) {
620         return symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
621       }
622     }
623   }
624   return LLDB_INVALID_ADDRESS;
625 }
626 
627 //  Look for this symbol:
628 //
629 //  int __attribute__((visibility("hidden")))           _dyld_global_lock_held =
630 //  0;
631 //
632 //  in libdyld.dylib.
633 Status DynamicLoaderMacOS::CanLoadImage() {
634   Status error;
635   addr_t symbol_address = LLDB_INVALID_ADDRESS;
636   ConstString g_libdyld_name("libdyld.dylib");
637   Target &target = m_process->GetTarget();
638   const ModuleList &target_modules = target.GetImages();
639   std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
640 
641   // Find any modules named "libdyld.dylib" and look for the symbol there first
642   for (ModuleSP module_sp : target.GetImages().ModulesNoLocking()) {
643     if (module_sp) {
644       if (module_sp->GetFileSpec().GetFilename() == g_libdyld_name) {
645         symbol_address = GetDyldLockVariableAddressFromModule(module_sp.get());
646         if (symbol_address != LLDB_INVALID_ADDRESS)
647           break;
648       }
649     }
650   }
651 
652   // Search through all modules looking for the symbol in them
653   if (symbol_address == LLDB_INVALID_ADDRESS) {
654     for (ModuleSP module_sp : target.GetImages().Modules()) {
655       if (module_sp) {
656         addr_t symbol_address =
657             GetDyldLockVariableAddressFromModule(module_sp.get());
658         if (symbol_address != LLDB_INVALID_ADDRESS)
659           break;
660       }
661     }
662   }
663 
664   // Default assumption is that it is OK to load images. Only say that we
665   // cannot load images if we find the symbol in libdyld and it indicates that
666   // we cannot.
667 
668   if (symbol_address != LLDB_INVALID_ADDRESS) {
669     {
670       int lock_held =
671           m_process->ReadUnsignedIntegerFromMemory(symbol_address, 4, 0, error);
672       if (lock_held != 0) {
673         error =
674             Status::FromErrorString("dyld lock held - unsafe to load images.");
675       }
676     }
677   } else {
678     // If we were unable to find _dyld_global_lock_held in any modules, or it
679     // is not loaded into memory yet, we may be at process startup (sitting  at
680     // _dyld_start) - so we should not allow dlopen calls. But if we found more
681     // than one module then we are clearly past _dyld_start so in that case
682     // we'll default to "it's safe".
683     if (target.GetImages().GetSize() <= 1)
684       error = Status::FromErrorString("could not find the dyld library or "
685                                       "the dyld lock symbol");
686   }
687   return error;
688 }
689 
690 bool DynamicLoaderMacOS::GetSharedCacheInformation(
691     lldb::addr_t &base_address, UUID &uuid, LazyBool &using_shared_cache,
692     LazyBool &private_shared_cache) {
693   base_address = LLDB_INVALID_ADDRESS;
694   uuid.Clear();
695   using_shared_cache = eLazyBoolCalculate;
696   private_shared_cache = eLazyBoolCalculate;
697 
698   if (m_process) {
699     StructuredData::ObjectSP info = m_process->GetSharedCacheInfo();
700     StructuredData::Dictionary *info_dict = nullptr;
701     if (info.get() && info->GetAsDictionary()) {
702       info_dict = info->GetAsDictionary();
703     }
704 
705     // {"shared_cache_base_address":140735683125248,"shared_cache_uuid
706     // ":"DDB8D70C-
707     // C9A2-3561-B2C8-BE48A4F33F96","no_shared_cache":false,"shared_cache_private_cache":false}
708 
709     if (info_dict && info_dict->HasKey("shared_cache_uuid") &&
710         info_dict->HasKey("no_shared_cache") &&
711         info_dict->HasKey("shared_cache_base_address")) {
712       base_address = info_dict->GetValueForKey("shared_cache_base_address")
713                          ->GetUnsignedIntegerValue(LLDB_INVALID_ADDRESS);
714       std::string uuid_str = std::string(
715           info_dict->GetValueForKey("shared_cache_uuid")->GetStringValue());
716       if (!uuid_str.empty())
717         uuid.SetFromStringRef(uuid_str);
718       if (!info_dict->GetValueForKey("no_shared_cache")->GetBooleanValue())
719         using_shared_cache = eLazyBoolYes;
720       else
721         using_shared_cache = eLazyBoolNo;
722       if (info_dict->GetValueForKey("shared_cache_private_cache")
723               ->GetBooleanValue())
724         private_shared_cache = eLazyBoolYes;
725       else
726         private_shared_cache = eLazyBoolNo;
727 
728       return true;
729     }
730   }
731   return false;
732 }
733 
734 void DynamicLoaderMacOS::Initialize() {
735   PluginManager::RegisterPlugin(GetPluginNameStatic(),
736                                 GetPluginDescriptionStatic(), CreateInstance);
737 }
738 
739 void DynamicLoaderMacOS::Terminate() {
740   PluginManager::UnregisterPlugin(CreateInstance);
741 }
742 
743 llvm::StringRef DynamicLoaderMacOS::GetPluginDescriptionStatic() {
744   return "Dynamic loader plug-in that watches for shared library loads/unloads "
745          "in MacOSX user processes.";
746 }
747