Skip to content

Commit 91e017a

Browse files
authored
Merge pull request #7062 from apple/cp/r101194149-new-mechanism-for-being-notified-about-binary-image-changes-darwin
Change the dyld notification function that lldb puts a breakpoint in
2 parents d62d600 + 0790206 commit 91e017a

File tree

2 files changed

+165
-27
lines changed

2 files changed

+165
-27
lines changed

lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp

Lines changed: 163 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,13 @@ bool DynamicLoaderMacOS::NotifyBreakpointHit(void *baton,
233233
StoppointCallbackContext *context,
234234
lldb::user_id_t break_id,
235235
lldb::user_id_t break_loc_id) {
236-
// Let the event know that the images have changed
237-
// DYLD passes three arguments to the notification breakpoint.
238-
// Arg1: enum dyld_notify_mode mode - 0 = adding, 1 = removing, 2 = remove
239-
// all Arg2: unsigned long icount - Number of shared libraries
240-
// added/removed Arg3: uint64_t mach_headers[] - Array of load addresses
241-
// of binaries added/removed
236+
//
237+
// Our breakpoint on
238+
//
239+
// void lldb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount,
240+
// const dyld_image_info info[])
241+
//
242+
// has been hit. We need to read the arguments.
242243

243244
DynamicLoaderMacOS *dyld_instance = (DynamicLoaderMacOS *)baton;
244245

@@ -268,9 +269,10 @@ bool DynamicLoaderMacOS::NotifyBreakpointHit(void *baton,
268269
ValueList argument_values;
269270

270271
Value mode_value; // enum dyld_notify_mode { dyld_notify_adding=0,
271-
// dyld_notify_removing=1, dyld_notify_remove_all=2 };
272-
Value count_value; // unsigned long count
273-
Value headers_value; // uint64_t machHeaders[] (aka void*)
272+
// dyld_notify_removing=1, dyld_notify_remove_all=2,
273+
// dyld_notify_dyld_moved=3 };
274+
Value count_value; // uint32_t
275+
Value headers_value; // struct dyld_image_info machHeaders[]
274276

275277
CompilerType clang_void_ptr_type =
276278
scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
@@ -284,13 +286,8 @@ bool DynamicLoaderMacOS::NotifyBreakpointHit(void *baton,
284286
mode_value.SetValueType(Value::ValueType::Scalar);
285287
mode_value.SetCompilerType(clang_uint32_type);
286288

287-
if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 4) {
288-
count_value.SetValueType(Value::ValueType::Scalar);
289-
count_value.SetCompilerType(clang_uint32_type);
290-
} else {
291-
count_value.SetValueType(Value::ValueType::Scalar);
292-
count_value.SetCompilerType(clang_uint64_type);
293-
}
289+
count_value.SetValueType(Value::ValueType::Scalar);
290+
count_value.SetCompilerType(clang_uint32_type);
294291

295292
headers_value.SetValueType(Value::ValueType::Scalar);
296293
headers_value.SetCompilerType(clang_void_ptr_type);
@@ -312,12 +309,30 @@ bool DynamicLoaderMacOS::NotifyBreakpointHit(void *baton,
312309
argument_values.GetValueAtIndex(2)->GetScalar().ULongLong(-1);
313310
if (header_array != static_cast<uint64_t>(-1)) {
314311
std::vector<addr_t> image_load_addresses;
312+
// header_array points to an array of image_infos_count elements,
313+
// each is
314+
// struct dyld_image_info {
315+
// const struct mach_header* imageLoadAddress;
316+
// const char* imageFilePath;
317+
// uintptr_t imageFileModDate;
318+
// };
319+
//
320+
// and we only need the imageLoadAddress fields.
321+
322+
const int addrsize =
323+
process->GetTarget().GetArchitecture().GetAddressByteSize();
315324
for (uint64_t i = 0; i < image_infos_count; i++) {
316325
Status error;
317-
addr_t addr = process->ReadUnsignedIntegerFromMemory(
318-
header_array + (8 * i), 8, LLDB_INVALID_ADDRESS, error);
319-
if (addr != LLDB_INVALID_ADDRESS) {
326+
addr_t dyld_image_info = header_array + (addrsize * 3 * i);
327+
addr_t addr =
328+
process->ReadPointerFromMemory(dyld_image_info, error);
329+
if (error.Success()) {
320330
image_load_addresses.push_back(addr);
331+
} else {
332+
Debugger::ReportWarning(
333+
"DynamicLoaderMacOS::NotifyBreakpointHit unable "
334+
"to read binary mach-o load address at 0x%" PRIx64,
335+
addr);
321336
}
322337
}
323338
if (dyld_mode == 0) {
@@ -362,10 +377,16 @@ bool DynamicLoaderMacOS::NotifyBreakpointHit(void *baton,
362377
Status error;
363378
addr_t notification_addr =
364379
process->ReadPointerFromMemory(notification_location, error);
365-
if (ABISP abi_sp = process->GetABI())
366-
notification_addr = abi_sp->FixCodeAddress(notification_addr);
367-
368-
dyld_instance->SetDYLDHandoverBreakpoint(notification_addr);
380+
if (!error.Success()) {
381+
Debugger::ReportWarning(
382+
"DynamicLoaderMacOS::NotifyBreakpointHit unable "
383+
"to read address of dyld-handover notification function at "
384+
"0x%" PRIx64,
385+
notification_location);
386+
} else {
387+
notification_addr = process->FixCodeAddress(notification_addr);
388+
dyld_instance->SetDYLDHandoverBreakpoint(notification_addr);
389+
}
369390
}
370391
}
371392
}
@@ -417,7 +438,80 @@ void DynamicLoaderMacOS::PutToLog(Log *log) const {
417438
return;
418439
}
419440

441+
// Look in dyld's dyld_all_image_infos structure for the address
442+
// of the notification function.
443+
// We can find the address of dyld_all_image_infos by a system
444+
// call, even if we don't have a dyld binary registered in lldb's
445+
// image list.
446+
// At process launch time - before dyld has executed any instructions -
447+
// the address of the notification function is not a resolved vm address
448+
// yet. dyld_all_image_infos also has a field with its own address
449+
// in it, and this will also be unresolved when we're at this state.
450+
// So we can compare the address of the object with this field and if
451+
// they differ, dyld hasn't started executing yet and we can't get the
452+
// notification address this way.
453+
addr_t DynamicLoaderMacOS::GetNotificationFuncAddrFromImageInfos() {
454+
addr_t notification_addr = LLDB_INVALID_ADDRESS;
455+
if (!m_process)
456+
return notification_addr;
457+
458+
addr_t all_image_infos_addr = m_process->GetImageInfoAddress();
459+
if (all_image_infos_addr == LLDB_INVALID_ADDRESS)
460+
return notification_addr;
461+
462+
const uint32_t addr_size =
463+
m_process->GetTarget().GetArchitecture().GetAddressByteSize();
464+
offset_t registered_infos_addr_offset =
465+
sizeof(uint32_t) + // version
466+
sizeof(uint32_t) + // infoArrayCount
467+
addr_size + // infoArray
468+
addr_size + // notification
469+
addr_size + // processDetachedFromSharedRegion +
470+
// libSystemInitialized + pad
471+
addr_size + // dyldImageLoadAddress
472+
addr_size + // jitInfo
473+
addr_size + // dyldVersion
474+
addr_size + // errorMessage
475+
addr_size + // terminationFlags
476+
addr_size + // coreSymbolicationShmPage
477+
addr_size + // systemOrderFlag
478+
addr_size + // uuidArrayCount
479+
addr_size; // uuidArray
480+
// dyldAllImageInfosAddress
481+
482+
// If the dyldAllImageInfosAddress does not match
483+
// the actual address of this struct, dyld has not started
484+
// executing yet. The 'notification' field can't be used by
485+
// lldb until it's resolved to an actual address.
486+
Status error;
487+
addr_t registered_infos_addr = m_process->ReadPointerFromMemory(
488+
all_image_infos_addr + registered_infos_addr_offset, error);
489+
if (!error.Success())
490+
return notification_addr;
491+
if (registered_infos_addr != all_image_infos_addr)
492+
return notification_addr;
493+
494+
offset_t notification_fptr_offset = sizeof(uint32_t) + // version
495+
sizeof(uint32_t) + // infoArrayCount
496+
addr_size; // infoArray
497+
498+
addr_t notification_fptr = m_process->ReadPointerFromMemory(
499+
all_image_infos_addr + notification_fptr_offset, error);
500+
if (error.Success())
501+
notification_addr = m_process->FixCodeAddress(notification_fptr);
502+
return notification_addr;
503+
}
504+
505+
// We want to put a breakpoint on dyld's lldb_image_notifier()
506+
// but we may have attached to the process during the
507+
// transition from on-disk-dyld to shared-cache-dyld, so there's
508+
// officially no dyld binary loaded in the process (libdyld will
509+
// report none when asked), but the kernel can find the dyld_all_image_infos
510+
// struct and the function pointer for lldb_image_notifier is in
511+
// that struct.
420512
bool DynamicLoaderMacOS::SetNotificationBreakpoint() {
513+
514+
// First try to find the notification breakpoint function by name
421515
if (m_break_id == LLDB_INVALID_BREAK_ID) {
422516
ModuleSP dyld_sp(GetDYLDModule());
423517
if (dyld_sp) {
@@ -431,14 +525,56 @@ bool DynamicLoaderMacOS::SetNotificationBreakpoint() {
431525
Breakpoint *breakpoint =
432526
m_process->GetTarget()
433527
.CreateBreakpoint(&dyld_filelist, source_files,
434-
"_dyld_debugger_notification",
435-
eFunctionNameTypeFull, eLanguageTypeC, 0,
436-
skip_prologue, internal, hardware)
528+
"lldb_image_notifier", eFunctionNameTypeFull,
529+
eLanguageTypeUnknown, 0, skip_prologue,
530+
internal, hardware)
437531
.get();
438532
breakpoint->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this,
439533
true);
440534
breakpoint->SetBreakpointKind("shared-library-event");
441-
m_break_id = breakpoint->GetID();
535+
if (breakpoint->HasResolvedLocations())
536+
m_break_id = breakpoint->GetID();
537+
else
538+
m_process->GetTarget().RemoveBreakpointByID(breakpoint->GetID());
539+
540+
if (m_break_id == LLDB_INVALID_BREAK_ID) {
541+
Breakpoint *breakpoint =
542+
m_process->GetTarget()
543+
.CreateBreakpoint(&dyld_filelist, source_files,
544+
"gdb_image_notifier", eFunctionNameTypeFull,
545+
eLanguageTypeUnknown, 0, skip_prologue,
546+
internal, hardware)
547+
.get();
548+
breakpoint->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this,
549+
true);
550+
breakpoint->SetBreakpointKind("shared-library-event");
551+
if (breakpoint->HasResolvedLocations())
552+
m_break_id = breakpoint->GetID();
553+
else
554+
m_process->GetTarget().RemoveBreakpointByID(breakpoint->GetID());
555+
}
556+
}
557+
}
558+
559+
// Failing that, find dyld_all_image_infos struct in memory,
560+
// read the notification function pointer at the offset.
561+
if (m_break_id == LLDB_INVALID_BREAK_ID) {
562+
addr_t notification_addr = GetNotificationFuncAddrFromImageInfos();
563+
if (notification_addr != LLDB_INVALID_ADDRESS) {
564+
Address so_addr;
565+
// We may not have a dyld binary mapped to this address yet;
566+
// don't try to express the Address object as section+offset,
567+
// only as a raw load address.
568+
so_addr.SetRawAddress(notification_addr);
569+
Breakpoint *dyld_break =
570+
m_process->GetTarget().CreateBreakpoint(so_addr, true, false).get();
571+
dyld_break->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this,
572+
true);
573+
dyld_break->SetBreakpointKind("shared-library-event");
574+
if (dyld_break->HasResolvedLocations())
575+
m_break_id = dyld_break->GetID();
576+
else
577+
m_process->GetTarget().RemoveBreakpointByID(dyld_break->GetID());
442578
}
443579
}
444580
return m_break_id != LLDB_INVALID_BREAK_ID;

lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ class DynamicLoaderMacOS : public lldb_private::DynamicLoaderDarwin {
8686
lldb_private::StoppointCallbackContext *context,
8787
lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
8888

89+
lldb::addr_t GetNotificationFuncAddrFromImageInfos();
90+
8991
bool SetNotificationBreakpoint() override;
9092

9193
void ClearNotificationBreakpoint() override;

0 commit comments

Comments
 (0)