diff --git a/lldb/test/API/macosx/unregistered-macho/Makefile b/lldb/test/API/macosx/unregistered-macho/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/unregistered-macho/Makefile @@ -0,0 +1,3 @@ +C_SOURCES = main.c + +include Makefile.rules diff --git a/lldb/test/API/macosx/unregistered-macho/TestUnregisteredMacho.py b/lldb/test/API/macosx/unregistered-macho/TestUnregisteredMacho.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/unregistered-macho/TestUnregisteredMacho.py @@ -0,0 +1,34 @@ +"""Test that debugserver will parse a mach-o in inferior memory even if it's not loaded.""" + +import os +import re +import subprocess + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestUnregisteredMacho(TestBase): + + # newer debugserver required for jGetLoadedDynamicLibrariesInfos + # to support this + @skipIfOutOfTreeDebugserver + @no_debug_info_test + @skipUnlessDarwin + def test(self): + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.c")) + + frame = thread.GetFrameAtIndex(0) + macho_buf = frame.GetValueForVariablePath("macho_buf") + gdb_packet = "process plugin packet send 'jGetLoadedDynamicLibrariesInfos:{\"solib_addresses\":[%d]}]'" % macho_buf.GetValueAsUnsigned() + + # Send the jGetLoadedDynamicLibrariesInfos packet + # to debugserver, asking it to parse the mach-o binary + # at this address and give us the UUID etc, even though + # dyld doesn't think there is a binary at that address. + # We won't get a pathname for the binary (from dyld), but + # we will get to the LC_UUID and include that. + self.expect (gdb_packet, substrs=['"pathname":""', '"uuid":"1B4E28BA-2FA1-11D2-883F-B9A761BDE3FB"']) diff --git a/lldb/test/API/macosx/unregistered-macho/main.c b/lldb/test/API/macosx/unregistered-macho/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/unregistered-macho/main.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include + +int main () +{ + int size_of_mh_and_cmds = sizeof (struct mach_header_64) + + sizeof (struct segment_command_64) + + sizeof (struct uuid_command); + uint8_t *macho_buf = (uint8_t *) malloc + (size_of_mh_and_cmds); + uint8_t *p = macho_buf; + struct mach_header_64 mh; + mh.magic = MH_MAGIC_64; + mh.cputype = CPU_TYPE_ARM64; + mh.cpusubtype = 0; + mh.filetype = MH_EXECUTE; + mh.ncmds = 2; + mh.sizeofcmds = size_of_mh_and_cmds; + mh.flags = MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL | MH_PIE; + + memcpy (p, &mh, sizeof (mh)); + p += sizeof (mh); + + struct segment_command_64 seg; + seg.cmd = LC_SEGMENT_64; + seg.cmdsize = sizeof (seg); + strcpy (seg.segname, "__TEXT"); + seg.vmaddr = 0x5000; + seg.vmsize = 0x1000; + seg.fileoff = 0; + seg.filesize = 0; + seg.maxprot = 0; + seg.initprot = 0; + seg.nsects = 0; + seg.flags = 0; + + memcpy (p, &seg, sizeof (seg)); + p += sizeof (seg); + + struct uuid_command uuid; + uuid.cmd = LC_UUID; + uuid.cmdsize = sizeof (uuid); + uuid_clear (uuid.uuid); + uuid_parse ("1b4e28ba-2fa1-11d2-883f-b9a761bde3fb", uuid.uuid); + + memcpy (p, &uuid, sizeof (uuid)); + p += sizeof (uuid); + + // If this needs to be debugged, the memory buffer can be written + // to a file with + // (lldb) mem rea -b -o /tmp/t -c `p - macho_buf` macho_buf + // (lldb) platform shell otool -hlv /tmp/t + // to verify that it is well formed. + + // And inside lldb, it should be inspectable via + // (lldb) script print(lldb.frame.locals["macho_buf"][0].GetValueAsUnsigned()) + // 105553162403968 + // (lldb) process plugin packet send 'jGetLoadedDynamicLibrariesInfos:{"solib_addresses":[105553162403968]}]' + + return 0; // break here +} diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm @@ -1164,6 +1164,8 @@ int pointer_size = GetInferiorAddrSize(pid); + // Collect the list of all binaries that dyld knows about in + // the inferior process. std::vector all_image_infos; GetAllLoadedBinariesViaDYLDSPI(all_image_infos); uint32_t platform = GetPlatform(); @@ -1171,12 +1173,26 @@ std::vector image_infos; const size_t macho_addresses_count = macho_addresses.size(); const size_t all_image_infos_count = all_image_infos.size(); + for (size_t i = 0; i < macho_addresses_count; i++) { + bool found_matching_entry = false; for (size_t j = 0; j < all_image_infos_count; j++) { if (all_image_infos[j].load_address == macho_addresses[i]) { image_infos.push_back(all_image_infos[j]); + found_matching_entry = true; } } + if (!found_matching_entry) { + // dyld doesn't think there is a binary at this address, + // but maybe there isn't a binary YET - let's look in memory + // for a proper mach-o header etc and return what we can. + // We will have an empty filename for the binary (because dyld + // doesn't know about it yet) but we can read all of the mach-o + // load commands from memory directly. + struct binary_image_information entry; + entry.load_address = macho_addresses[i]; + image_infos.push_back(entry); + } } const size_t image_infos_count = image_infos.size();