diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:50:09 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:50:09 +0000 |
commit | f3fbd1c0586ff6ec7895991e6c28f61a503c36a8 (patch) | |
tree | 48d008fd3df8c0e73271a4b18474e0aac6dbfe33 /tools/debugserver/source/MacOSX | |
parent | 2fc5d2d1dfaf623ce4e24cd8590565902f8c557c (diff) |
Notes
Diffstat (limited to 'tools/debugserver/source/MacOSX')
-rw-r--r-- | tools/debugserver/source/MacOSX/CMakeLists.txt | 48 | ||||
-rw-r--r-- | tools/debugserver/source/MacOSX/MachException.cpp | 1 | ||||
-rw-r--r-- | tools/debugserver/source/MacOSX/MachProcess.h | 54 | ||||
-rw-r--r-- | tools/debugserver/source/MacOSX/MachProcess.mm | 544 | ||||
-rw-r--r-- | tools/debugserver/source/MacOSX/MachTask.h | 20 | ||||
-rw-r--r-- | tools/debugserver/source/MacOSX/MachTask.mm | 85 | ||||
-rw-r--r-- | tools/debugserver/source/MacOSX/Makefile | 54 | ||||
-rw-r--r-- | tools/debugserver/source/MacOSX/i386/Makefile | 19 | ||||
-rw-r--r-- | tools/debugserver/source/MacOSX/x86_64/Makefile | 19 |
9 files changed, 459 insertions, 385 deletions
diff --git a/tools/debugserver/source/MacOSX/CMakeLists.txt b/tools/debugserver/source/MacOSX/CMakeLists.txt index d319cb7b0babc..96cff0a22fc42 100644 --- a/tools/debugserver/source/MacOSX/CMakeLists.txt +++ b/tools/debugserver/source/MacOSX/CMakeLists.txt @@ -59,29 +59,31 @@ set_source_files_properties( target_link_libraries(debugserver ${DEBUGSERVER_USED_LIBS}) -# Sign the debugserver binary -set (CODESIGN_IDENTITY lldb_codesign) -execute_process( - COMMAND xcrun -f codesign_allocate - OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE CODESIGN_ALLOCATE - ) -# Older cmake versions don't support "-E env". -if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 3.2) - add_custom_command(TARGET debugserver - POST_BUILD - # Note: --entitlements option removed, as it causes errors when debugging. - # was: COMMAND CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --entitlements ${CMAKE_CURRENT_SOURCE_DIR}/../debugserver-entitlements.plist --force --sign ${CODESIGN_IDENTITY} debugserver - COMMAND CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --force --sign ${CODESIGN_IDENTITY} debugserver - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin - ) -else() - add_custom_command(TARGET debugserver - POST_BUILD - # Note: --entitlements option removed (see comment above). - COMMAND ${CMAKE_COMMAND} -E env CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --force --sign ${CODESIGN_IDENTITY} debugserver - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin - ) +set(LLDB_CODESIGN_IDENTITY "lldb_codesign" + CACHE STRING "Identity used for code signing. Set to empty string to skip the signing step.") +if (NOT ("${LLDB_CODESIGN_IDENTITY}" STREQUAL "")) + execute_process( + COMMAND xcrun -f codesign_allocate + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE CODESIGN_ALLOCATE + ) + # Older cmake versions don't support "-E env". + if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 3.2) + add_custom_command(TARGET debugserver + POST_BUILD + # Note: --entitlements option removed, as it causes errors when debugging. + # was: COMMAND CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --entitlements ${CMAKE_CURRENT_SOURCE_DIR}/../debugserver-entitlements.plist --force --sign ${LLDB_CODESIGN_IDENTITY} debugserver + COMMAND CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --force --sign ${LLDB_CODESIGN_IDENTITY} debugserver + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ) + else() + add_custom_command(TARGET debugserver + POST_BUILD + # Note: --entitlements option removed (see comment above). + COMMAND ${CMAKE_COMMAND} -E env CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --force --sign ${LLDB_CODESIGN_IDENTITY} debugserver + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ) + endif() endif() install(TARGETS debugserver diff --git a/tools/debugserver/source/MacOSX/MachException.cpp b/tools/debugserver/source/MacOSX/MachException.cpp index b7245796ae4c2..0b5459e3a1894 100644 --- a/tools/debugserver/source/MacOSX/MachException.cpp +++ b/tools/debugserver/source/MacOSX/MachException.cpp @@ -436,7 +436,6 @@ MachException::Message::Reply(MachProcess *process, int signal) if (state.task_port == process->Task().TaskPort()) { DNBLogThreaded("error: mach_msg() returned an error when replying to a mach exception: error = %u", err.Error()); - abort (); } else { diff --git a/tools/debugserver/source/MacOSX/MachProcess.h b/tools/debugserver/source/MacOSX/MachProcess.h index 3be0b2dbabb3c..094c13b8f0a80 100644 --- a/tools/debugserver/source/MacOSX/MachProcess.h +++ b/tools/debugserver/source/MacOSX/MachProcess.h @@ -15,8 +15,10 @@ #define __MachProcess_h__ #include <mach/mach.h> +#include <mach-o/loader.h> #include <sys/signal.h> #include <pthread.h> +#include <uuid/uuid.h> #include <vector> #include <CoreFoundation/CoreFoundation.h> @@ -46,6 +48,45 @@ public: MachProcess (); ~MachProcess (); + // A structure that can hold everything debugserver needs to know from + // a binary's Mach-O header / load commands. + + struct mach_o_segment + { + std::string name; + uint64_t vmaddr; + uint64_t vmsize; + uint64_t fileoff; + uint64_t filesize; + uint64_t maxprot; + uint64_t initprot; + uint64_t nsects; + uint64_t flags; + }; + + struct mach_o_information + { + struct mach_header_64 mach_header; + std::vector<struct mach_o_segment> segments; + uuid_t uuid; + std::string min_version_os_name; + std::string min_version_os_version; + }; + + struct binary_image_information + { + std::string filename; + uint64_t load_address; + uint64_t mod_date; // may not be available - 0 if so + struct mach_o_information macho_info; + + binary_image_information () : + filename (), + load_address (INVALID_NUB_ADDRESS), + mod_date (0) + { } + }; + //---------------------------------------------------------------------- // Child process control //---------------------------------------------------------------------- @@ -193,7 +234,15 @@ public: nub_addr_t GetPThreadT (nub_thread_t tid); nub_addr_t GetDispatchQueueT (nub_thread_t tid); nub_addr_t GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); + + + bool GetMachOInformationFromMemory (nub_addr_t mach_o_header_addr, int wordsize, struct mach_o_information &inf); + JSONGenerator::ObjectSP FormatDynamicLibrariesIntoJSON (const std::vector<struct binary_image_information> &image_infos); + void GetAllLoadedBinariesViaDYLDSPI (std::vector<struct binary_image_information> &image_infos); JSONGenerator::ObjectSP GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count); + JSONGenerator::ObjectSP GetLibrariesInfoForAddresses (nub_process_t pid, std::vector<uint64_t> &macho_addresses); + JSONGenerator::ObjectSP GetAllLoadedLibrariesInfos (nub_process_t pid); + JSONGenerator::ObjectSP GetSharedCacheInfo (nub_process_t pid); nub_size_t GetNumThreads () const; nub_thread_t GetThreadAtIndex (nub_size_t thread_idx) const; @@ -358,6 +407,11 @@ private: // as the sole reason for the process being stopped, we can auto resume // the process. bool m_did_exec; + + void * (*m_dyld_process_info_create) (task_t task, uint64_t timestamp, kern_return_t* kernelError); + void (*m_dyld_process_info_for_each_image) (void* info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)); + void (*m_dyld_process_info_release) (void* info); + void (*m_dyld_process_info_get_cache) (void* info, void* cacheInfo); }; diff --git a/tools/debugserver/source/MacOSX/MachProcess.mm b/tools/debugserver/source/MacOSX/MachProcess.mm index b9e06307a4aa7..ab0039edd88f3 100644 --- a/tools/debugserver/source/MacOSX/MachProcess.mm +++ b/tools/debugserver/source/MacOSX/MachProcess.mm @@ -12,8 +12,10 @@ //===----------------------------------------------------------------------===// #include "DNB.h" +#include <dlfcn.h> #include <inttypes.h> #include <mach/mach.h> +#include <mach/task.h> #include <signal.h> #include <spawn.h> #include <sys/fcntl.h> @@ -417,8 +419,17 @@ MachProcess::MachProcess() : m_image_infos_baton(NULL), m_sent_interrupt_signo (0), m_auto_resume_signo (0), - m_did_exec (false) + m_did_exec (false), + m_dyld_process_info_create (nullptr), + m_dyld_process_info_for_each_image (nullptr), + m_dyld_process_info_release (nullptr), + m_dyld_process_info_get_cache (nullptr) { + m_dyld_process_info_create = (void * (*) (task_t task, uint64_t timestamp, kern_return_t* kernelError)) dlsym (RTLD_DEFAULT, "_dyld_process_info_create"); + m_dyld_process_info_for_each_image = (void (*)(void *info, void (^)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path))) dlsym (RTLD_DEFAULT, "_dyld_process_info_for_each_image"); + m_dyld_process_info_release = (void (*) (void* info)) dlsym (RTLD_DEFAULT, "_dyld_process_info_release"); + m_dyld_process_info_get_cache = (void (*) (void* info, void* cacheInfo)) dlsym (RTLD_DEFAULT, "_dyld_process_info_get_cache"); + DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); } @@ -520,8 +531,232 @@ MachProcess::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_ return m_thread_list.GetTSDAddressForThread (tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); } +// Given an address, read the mach-o header and load commands out of memory to fill in +// the mach_o_information "inf" object. +// +// Returns false if there was an error in reading this mach-o file header/load commands. + +bool +MachProcess::GetMachOInformationFromMemory (nub_addr_t mach_o_header_addr, int wordsize, struct mach_o_information &inf) +{ + uint64_t load_cmds_p; + if (wordsize == 4) + { + struct mach_header header; + if (ReadMemory (mach_o_header_addr, sizeof (struct mach_header), &header) != sizeof (struct mach_header)) + { + return false; + } + load_cmds_p = mach_o_header_addr + sizeof (struct mach_header); + inf.mach_header.magic = header.magic; + inf.mach_header.cputype = header.cputype; + // high byte of cpusubtype is used for "capability bits", v. CPU_SUBTYPE_MASK, CPU_SUBTYPE_LIB64 in machine.h + inf.mach_header.cpusubtype = header.cpusubtype & 0x00ffffff; + inf.mach_header.filetype = header.filetype; + inf.mach_header.ncmds = header.ncmds; + inf.mach_header.sizeofcmds = header.sizeofcmds; + inf.mach_header.flags = header.flags; + } + else + { + struct mach_header_64 header; + if (ReadMemory (mach_o_header_addr, sizeof (struct mach_header_64), &header) != sizeof (struct mach_header_64)) + { + return false; + } + load_cmds_p = mach_o_header_addr + sizeof (struct mach_header_64); + inf.mach_header.magic = header.magic; + inf.mach_header.cputype = header.cputype; + // high byte of cpusubtype is used for "capability bits", v. CPU_SUBTYPE_MASK, CPU_SUBTYPE_LIB64 in machine.h + inf.mach_header.cpusubtype = header.cpusubtype & 0x00ffffff; + inf.mach_header.filetype = header.filetype; + inf.mach_header.ncmds = header.ncmds; + inf.mach_header.sizeofcmds = header.sizeofcmds; + inf.mach_header.flags = header.flags; + } + for (uint32_t j = 0; j < inf.mach_header.ncmds; j++) + { + struct load_command lc; + if (ReadMemory (load_cmds_p, sizeof (struct load_command), &lc) != sizeof (struct load_command)) + { + return false; + } + if (lc.cmd == LC_SEGMENT) + { + struct segment_command seg; + if (ReadMemory (load_cmds_p, sizeof (struct segment_command), &seg) != sizeof (struct segment_command)) + { + return false; + } + struct mach_o_segment this_seg; + char name[17]; + ::memset (name, 0, sizeof (name)); + memcpy (name, seg.segname, sizeof (seg.segname)); + this_seg.name = name; + this_seg.vmaddr = seg.vmaddr; + this_seg.vmsize = seg.vmsize; + this_seg.fileoff = seg.fileoff; + this_seg.filesize = seg.filesize; + this_seg.maxprot = seg.maxprot; + this_seg.initprot = seg.initprot; + this_seg.nsects = seg.nsects; + this_seg.flags = seg.flags; + inf.segments.push_back(this_seg); + } + if (lc.cmd == LC_SEGMENT_64) + { + struct segment_command_64 seg; + if (ReadMemory (load_cmds_p, sizeof (struct segment_command_64), &seg) != sizeof (struct segment_command_64)) + { + return false; + } + struct mach_o_segment this_seg; + char name[17]; + ::memset (name, 0, sizeof (name)); + memcpy (name, seg.segname, sizeof (seg.segname)); + this_seg.name = name; + this_seg.vmaddr = seg.vmaddr; + this_seg.vmsize = seg.vmsize; + this_seg.fileoff = seg.fileoff; + this_seg.filesize = seg.filesize; + this_seg.maxprot = seg.maxprot; + this_seg.initprot = seg.initprot; + this_seg.nsects = seg.nsects; + this_seg.flags = seg.flags; + inf.segments.push_back(this_seg); + } + if (lc.cmd == LC_UUID) + { + struct uuid_command uuidcmd; + if (ReadMemory (load_cmds_p, sizeof (struct uuid_command), &uuidcmd) == sizeof (struct uuid_command)) + uuid_copy (inf.uuid, uuidcmd.uuid); + } + bool lc_cmd_known = lc.cmd == LC_VERSION_MIN_IPHONEOS || lc.cmd == LC_VERSION_MIN_MACOSX; +#if defined(LC_VERSION_MIN_TVOS) + lc_cmd_known |= lc.cmd == LC_VERSION_MIN_TVOS; +#endif +#if defined(LC_VERSION_MIN_WATCHOS) + lc_cmd_known |= lc.cmd == LC_VERSION_MIN_WATCHOS; +#endif + if (lc_cmd_known) + { + struct version_min_command vers_cmd; + if (ReadMemory (load_cmds_p, sizeof (struct version_min_command), &vers_cmd) != sizeof (struct version_min_command)) + { + return false; + } + switch (lc.cmd) + { + case LC_VERSION_MIN_IPHONEOS: + inf.min_version_os_name = "iphoneos"; + break; + case LC_VERSION_MIN_MACOSX: + inf.min_version_os_name = "macosx"; + break; +#if defined(LC_VERSION_MIN_TVOS) + case LC_VERSION_MIN_TVOS: + inf.min_version_os_name = "tvos"; + break; +#endif +#if defined(LC_VERSION_MIN_WATCHOS) + case LC_VERSION_MIN_WATCHOS: + inf.min_version_os_name = "watchos"; + break; +#endif + default: + return false; + } + uint32_t xxxx = vers_cmd.sdk >> 16; + uint32_t yy = (vers_cmd.sdk >> 8) & 0xffu; + uint32_t zz = vers_cmd.sdk & 0xffu; + inf.min_version_os_version = ""; + inf.min_version_os_version += std::to_string(xxxx); + inf.min_version_os_version += "."; + inf.min_version_os_version += std::to_string(yy); + if (zz != 0) + { + inf.min_version_os_version += "."; + inf.min_version_os_version += std::to_string(zz); + } + } + load_cmds_p += lc.cmdsize; + } + return true; +} + +// Given completely filled in array of binary_image_information structures, create a JSONGenerator object +// with all the details we want to send to lldb. +JSONGenerator::ObjectSP +MachProcess::FormatDynamicLibrariesIntoJSON (const std::vector<struct binary_image_information> &image_infos) +{ + + JSONGenerator::ArraySP image_infos_array_sp (new JSONGenerator::Array()); + + const size_t image_count = image_infos.size(); + + for (size_t i = 0; i < image_count; i++) + { + JSONGenerator::DictionarySP image_info_dict_sp (new JSONGenerator::Dictionary()); + image_info_dict_sp->AddIntegerItem ("load_address", image_infos[i].load_address); + image_info_dict_sp->AddIntegerItem ("mod_date", image_infos[i].mod_date); + image_info_dict_sp->AddStringItem ("pathname", image_infos[i].filename); + + uuid_string_t uuidstr; + uuid_unparse_upper (image_infos[i].macho_info.uuid, uuidstr); + image_info_dict_sp->AddStringItem ("uuid", uuidstr); + + if (image_infos[i].macho_info.min_version_os_name.empty() == false + && image_infos[i].macho_info.min_version_os_version.empty() == false) + { + image_info_dict_sp->AddStringItem ("min_version_os_name", image_infos[i].macho_info.min_version_os_name); + image_info_dict_sp->AddStringItem ("min_version_os_sdk", image_infos[i].macho_info.min_version_os_version); + } + + JSONGenerator::DictionarySP mach_header_dict_sp (new JSONGenerator::Dictionary()); + mach_header_dict_sp->AddIntegerItem ("magic", image_infos[i].macho_info.mach_header.magic); + mach_header_dict_sp->AddIntegerItem ("cputype", (uint32_t) image_infos[i].macho_info.mach_header.cputype); + mach_header_dict_sp->AddIntegerItem ("cpusubtype", (uint32_t) image_infos[i].macho_info.mach_header.cpusubtype); + mach_header_dict_sp->AddIntegerItem ("filetype", image_infos[i].macho_info.mach_header.filetype); + +// DynamicLoaderMacOSX doesn't currently need these fields, so don't send them. +// mach_header_dict_sp->AddIntegerItem ("ncmds", image_infos[i].macho_info.mach_header.ncmds); +// mach_header_dict_sp->AddIntegerItem ("sizeofcmds", image_infos[i].macho_info.mach_header.sizeofcmds); +// mach_header_dict_sp->AddIntegerItem ("flags", image_infos[i].macho_info.mach_header.flags); + image_info_dict_sp->AddItem ("mach_header", mach_header_dict_sp); + + JSONGenerator::ArraySP segments_sp (new JSONGenerator::Array()); + for (size_t j = 0; j < image_infos[i].macho_info.segments.size(); j++) + { + JSONGenerator::DictionarySP segment_sp (new JSONGenerator::Dictionary()); + segment_sp->AddStringItem ("name", image_infos[i].macho_info.segments[j].name); + segment_sp->AddIntegerItem ("vmaddr", image_infos[i].macho_info.segments[j].vmaddr); + segment_sp->AddIntegerItem ("vmsize", image_infos[i].macho_info.segments[j].vmsize); + segment_sp->AddIntegerItem ("fileoff", image_infos[i].macho_info.segments[j].fileoff); + segment_sp->AddIntegerItem ("filesize", image_infos[i].macho_info.segments[j].filesize); + segment_sp->AddIntegerItem ("maxprot", image_infos[i].macho_info.segments[j].maxprot); + +// DynamicLoaderMacOSX doesn't currently need these fields, so don't send them. +// segment_sp->AddIntegerItem ("initprot", image_infos[i].macho_info.segments[j].initprot); +// segment_sp->AddIntegerItem ("nsects", image_infos[i].macho_info.segments[j].nsects); +// segment_sp->AddIntegerItem ("flags", image_infos[i].macho_info.segments[j].flags); + segments_sp->AddItem (segment_sp); + } + image_info_dict_sp->AddItem ("segments", segments_sp); + + image_infos_array_sp->AddItem (image_info_dict_sp); + } + JSONGenerator::DictionarySP reply_sp (new JSONGenerator::Dictionary());; + reply_sp->AddItem ("images", image_infos_array_sp); + return reply_sp; +} + +// Get the shared library information using the old (pre-macOS 10.12, pre-iOS 10, pre-tvOS 10, pre-watchOS 3) +// code path. We'll be given the address of an array of structures in the form +// {void* load_addr, void* mod_date, void* pathname} +// +// In macOS 10.12 etc and newer, we'll use SPI calls into dyld to gather this information. JSONGenerator::ObjectSP MachProcess::GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) { @@ -536,29 +771,7 @@ MachProcess::GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image if (processInfo.kp_proc.p_flag & P_LP64) pointer_size = 8; - struct segment - { - std::string name; - uint64_t vmaddr; - uint64_t vmsize; - uint64_t fileoff; - uint64_t filesize; - uint64_t maxprot; - uint64_t initprot; - uint64_t nsects; - uint64_t flags; - }; - - struct image_info - { - uint64_t load_address; - std::string pathname; - uint64_t mod_date; - struct mach_header_64 mach_header; - std::vector<struct segment> segments; - uuid_t uuid; - }; - std::vector<image_info> image_infos; + std::vector<struct binary_image_information> image_infos; size_t image_infos_size = image_count * 3 * pointer_size; uint8_t *image_info_buf = (uint8_t *) malloc (image_infos_size); @@ -577,7 +790,7 @@ MachProcess::GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image for (size_t i = 0; i < image_count; i++) { - struct image_info info; + struct binary_image_information info; nub_addr_t pathname_address; if (pointer_size == 4) { @@ -604,13 +817,13 @@ MachProcess::GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image pathname_address = pathname_address_64; } char strbuf[17]; - info.pathname = ""; + info.filename = ""; uint64_t pathname_ptr = pathname_address; bool still_reading = true; while (still_reading && ReadMemory (pathname_ptr, sizeof (strbuf) - 1, strbuf) == sizeof (strbuf) - 1) { strbuf[sizeof(strbuf) - 1] = '\0'; - info.pathname += strbuf; + info.filename += strbuf; pathname_ptr += sizeof (strbuf) - 1; // Stop if we found nul byte indicating the end of the string for (size_t i = 0; i < sizeof(strbuf) - 1; i++) @@ -622,7 +835,7 @@ MachProcess::GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image } } } - uuid_clear (info.uuid); + uuid_clear (info.macho_info.uuid); image_infos.push_back (info); } if (image_infos.size() == 0) @@ -630,157 +843,161 @@ MachProcess::GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image return reply_sp; } + free (image_info_buf); //// Second, read the mach header / load commands for all the dylibs - for (size_t i = 0; i < image_count; i++) { - uint64_t load_cmds_p; - if (pointer_size == 4) - { - struct mach_header header; - if (ReadMemory (image_infos[i].load_address, sizeof (struct mach_header), &header) != sizeof (struct mach_header)) - { - return reply_sp; - } - load_cmds_p = image_infos[i].load_address + sizeof (struct mach_header); - image_infos[i].mach_header.magic = header.magic; - image_infos[i].mach_header.cputype = header.cputype; - image_infos[i].mach_header.cpusubtype = header.cpusubtype; - image_infos[i].mach_header.filetype = header.filetype; - image_infos[i].mach_header.ncmds = header.ncmds; - image_infos[i].mach_header.sizeofcmds = header.sizeofcmds; - image_infos[i].mach_header.flags = header.flags; - } - else - { - struct mach_header_64 header; - if (ReadMemory (image_infos[i].load_address, sizeof (struct mach_header_64), &header) != sizeof (struct mach_header_64)) - { - return reply_sp; - } - load_cmds_p = image_infos[i].load_address + sizeof (struct mach_header_64); - image_infos[i].mach_header.magic = header.magic; - image_infos[i].mach_header.cputype = header.cputype; - image_infos[i].mach_header.cpusubtype = header.cpusubtype; - image_infos[i].mach_header.filetype = header.filetype; - image_infos[i].mach_header.ncmds = header.ncmds; - image_infos[i].mach_header.sizeofcmds = header.sizeofcmds; - image_infos[i].mach_header.flags = header.flags; - } - for (uint32_t j = 0; j < image_infos[i].mach_header.ncmds; j++) + if (!GetMachOInformationFromMemory (image_infos[i].load_address, pointer_size, image_infos[i].macho_info)) { - struct load_command lc; - if (ReadMemory (load_cmds_p, sizeof (struct load_command), &lc) != sizeof (struct load_command)) - { - return reply_sp; - } - if (lc.cmd == LC_SEGMENT) - { - struct segment_command seg; - if (ReadMemory (load_cmds_p, sizeof (struct segment_command), &seg) != sizeof (struct segment_command)) - { - return reply_sp; - } - struct segment this_seg; - char name[17]; - ::memset (name, 0, sizeof (name)); - memcpy (name, seg.segname, sizeof (seg.segname)); - this_seg.name = name; - this_seg.vmaddr = seg.vmaddr; - this_seg.vmsize = seg.vmsize; - this_seg.fileoff = seg.fileoff; - this_seg.filesize = seg.filesize; - this_seg.maxprot = seg.maxprot; - this_seg.initprot = seg.initprot; - this_seg.nsects = seg.nsects; - this_seg.flags = seg.flags; - image_infos[i].segments.push_back(this_seg); - } - if (lc.cmd == LC_SEGMENT_64) - { - struct segment_command_64 seg; - if (ReadMemory (load_cmds_p, sizeof (struct segment_command_64), &seg) != sizeof (struct segment_command_64)) - { - return reply_sp; - } - struct segment this_seg; - char name[17]; - ::memset (name, 0, sizeof (name)); - memcpy (name, seg.segname, sizeof (seg.segname)); - this_seg.name = name; - this_seg.vmaddr = seg.vmaddr; - this_seg.vmsize = seg.vmsize; - this_seg.fileoff = seg.fileoff; - this_seg.filesize = seg.filesize; - this_seg.maxprot = seg.maxprot; - this_seg.initprot = seg.initprot; - this_seg.nsects = seg.nsects; - this_seg.flags = seg.flags; - image_infos[i].segments.push_back(this_seg); - } - if (lc.cmd == LC_UUID) - { - struct uuid_command uuidcmd; - if (ReadMemory (load_cmds_p, sizeof (struct uuid_command), &uuidcmd) == sizeof (struct uuid_command)) - uuid_copy (image_infos[i].uuid, uuidcmd.uuid); - } - load_cmds_p += lc.cmdsize; + return reply_sp; } } - //// Thrid, format all of the above in the JSONGenerator object. + //// Third, format all of the above in the JSONGenerator object. - JSONGenerator::ArraySP image_infos_array_sp (new JSONGenerator::Array()); + return FormatDynamicLibrariesIntoJSON (image_infos); + } + + return reply_sp; +} + +// From dyld SPI header dyld_process_info.h +typedef void* dyld_process_info; +struct dyld_process_cache_info +{ + uuid_t cacheUUID; // UUID of cache used by process + uint64_t cacheBaseAddress; // load address of dyld shared cache + bool noCache; // process is running without a dyld cache + bool privateCache; // process is using a private copy of its dyld cache +}; + + +// Use the dyld SPI present in macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer to get +// the load address, uuid, and filenames of all the libraries. +// This only fills in those three fields in the 'struct binary_image_information' - call +// GetMachOInformationFromMemory to fill in the mach-o header/load command details. +void +MachProcess::GetAllLoadedBinariesViaDYLDSPI (std::vector<struct binary_image_information> &image_infos) +{ + kern_return_t kern_ret; + if (m_dyld_process_info_create) + { + dyld_process_info info = m_dyld_process_info_create (m_task.TaskPort(), 0, &kern_ret); + if (info) + { + m_dyld_process_info_for_each_image (info, ^(uint64_t mach_header_addr, const uuid_t uuid, const char *path) { + struct binary_image_information image; + image.filename = path; + uuid_copy (image.macho_info.uuid, uuid); + image.load_address = mach_header_addr; + image_infos.push_back (image); + }); + m_dyld_process_info_release (info); + } + } +} + +// Fetch information about all shared libraries using the dyld SPIs that exist in +// macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer. +JSONGenerator::ObjectSP +MachProcess::GetAllLoadedLibrariesInfos (nub_process_t pid) +{ + JSONGenerator::DictionarySP reply_sp; + + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }; + struct kinfo_proc processInfo; + size_t bufsize = sizeof(processInfo); + if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0) + { + uint32_t pointer_size = 4; + if (processInfo.kp_proc.p_flag & P_LP64) + pointer_size = 8; + + std::vector<struct binary_image_information> image_infos; + GetAllLoadedBinariesViaDYLDSPI (image_infos); + const size_t image_count = image_infos.size(); for (size_t i = 0; i < image_count; i++) { - JSONGenerator::DictionarySP image_info_dict_sp (new JSONGenerator::Dictionary()); - image_info_dict_sp->AddIntegerItem ("load_address", image_infos[i].load_address); - image_info_dict_sp->AddIntegerItem ("mod_date", image_infos[i].mod_date); - image_info_dict_sp->AddStringItem ("pathname", image_infos[i].pathname); + GetMachOInformationFromMemory (image_infos[i].load_address, pointer_size, image_infos[i].macho_info); + } + return FormatDynamicLibrariesIntoJSON (image_infos); + } + return reply_sp; +} - uuid_string_t uuidstr; - uuid_unparse_upper (image_infos[i].uuid, uuidstr); - image_info_dict_sp->AddStringItem ("uuid", uuidstr); +// Fetch information about the shared libraries at the given load addresses using the +// dyld SPIs that exist in macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer. +JSONGenerator::ObjectSP +MachProcess::GetLibrariesInfoForAddresses (nub_process_t pid, std::vector<uint64_t> &macho_addresses) +{ + JSONGenerator::DictionarySP reply_sp; - JSONGenerator::DictionarySP mach_header_dict_sp (new JSONGenerator::Dictionary()); - mach_header_dict_sp->AddIntegerItem ("magic", image_infos[i].mach_header.magic); - mach_header_dict_sp->AddIntegerItem ("cputype", image_infos[i].mach_header.cputype); - mach_header_dict_sp->AddIntegerItem ("cpusubtype", image_infos[i].mach_header.cpusubtype); - mach_header_dict_sp->AddIntegerItem ("filetype", image_infos[i].mach_header.filetype); + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }; + struct kinfo_proc processInfo; + size_t bufsize = sizeof(processInfo); + if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0) + { + uint32_t pointer_size = 4; + if (processInfo.kp_proc.p_flag & P_LP64) + pointer_size = 8; -// DynamicLoaderMacOSX doesn't currently need these fields, so don't send them. -// mach_header_dict_sp->AddIntegerItem ("ncmds", image_infos[i].mach_header.ncmds); -// mach_header_dict_sp->AddIntegerItem ("sizeofcmds", image_infos[i].mach_header.sizeofcmds); -// mach_header_dict_sp->AddIntegerItem ("flags", image_infos[i].mach_header.flags); - image_info_dict_sp->AddItem ("mach_header", mach_header_dict_sp); + std::vector<struct binary_image_information> all_image_infos; + GetAllLoadedBinariesViaDYLDSPI (all_image_infos); - JSONGenerator::ArraySP segments_sp (new JSONGenerator::Array()); - for (size_t j = 0; j < image_infos[i].segments.size(); j++) + std::vector<struct binary_image_information> 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++) + { + for (size_t j = 0; j < all_image_infos_count; j++) { - JSONGenerator::DictionarySP segment_sp (new JSONGenerator::Dictionary()); - segment_sp->AddStringItem ("name", image_infos[i].segments[j].name); - segment_sp->AddIntegerItem ("vmaddr", image_infos[i].segments[j].vmaddr); - segment_sp->AddIntegerItem ("vmsize", image_infos[i].segments[j].vmsize); - segment_sp->AddIntegerItem ("fileoff", image_infos[i].segments[j].fileoff); - segment_sp->AddIntegerItem ("filesize", image_infos[i].segments[j].filesize); - segment_sp->AddIntegerItem ("maxprot", image_infos[i].segments[j].maxprot); - -// DynamicLoaderMacOSX doesn't currently need these fields, so don't send them. -// segment_sp->AddIntegerItem ("initprot", image_infos[i].segments[j].initprot); -// segment_sp->AddIntegerItem ("nsects", image_infos[i].segments[j].nsects); -// segment_sp->AddIntegerItem ("flags", image_infos[i].segments[j].flags); - segments_sp->AddItem (segment_sp); + if (all_image_infos[j].load_address == macho_addresses[i]) + { + image_infos.push_back (all_image_infos[j]); + } } - image_info_dict_sp->AddItem ("segments", segments_sp); + } - image_infos_array_sp->AddItem (image_info_dict_sp); + const size_t image_infos_count = image_infos.size(); + for (size_t i = 0; i < image_infos_count; i++) + { + GetMachOInformationFromMemory (image_infos[i].load_address, pointer_size, image_infos[i].macho_info); + } + return FormatDynamicLibrariesIntoJSON (image_infos); + } + return reply_sp; +} + +// From dyld's internal podyld_process_info.h: + +JSONGenerator::ObjectSP +MachProcess::GetSharedCacheInfo (nub_process_t pid) +{ + JSONGenerator::DictionarySP reply_sp (new JSONGenerator::Dictionary());; + kern_return_t kern_ret; + if (m_dyld_process_info_create && m_dyld_process_info_get_cache) + { + dyld_process_info info = m_dyld_process_info_create (m_task.TaskPort(), 0, &kern_ret); + if (info) + { + struct dyld_process_cache_info shared_cache_info; + m_dyld_process_info_get_cache (info, &shared_cache_info); + + reply_sp->AddIntegerItem ("shared_cache_base_address", shared_cache_info.cacheBaseAddress); + + uuid_string_t uuidstr; + uuid_unparse_upper (shared_cache_info.cacheUUID, uuidstr); + reply_sp->AddStringItem ("shared_cache_uuid", uuidstr); + + reply_sp->AddBooleanItem ("no_shared_cache", shared_cache_info.noCache); + reply_sp->AddBooleanItem ("shared_cache_private_cache", shared_cache_info.privateCache); + + m_dyld_process_info_release (info); } - reply_sp.reset (new JSONGenerator::Dictionary()); - reply_sp->AddItem ("images", image_infos_array_sp); } return reply_sp; } @@ -1048,6 +1265,7 @@ MachProcess::Interrupt() if (Signal (m_sent_interrupt_signo)) { DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - sent %i signal to interrupt process", m_sent_interrupt_signo); + return true; } else { @@ -2274,9 +2492,9 @@ MachProcess::GetGenealogyImageInfo (size_t idx) bool MachProcess::GetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *patch) { - bool success = false; - -#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000) +#if defined (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101000) + return false; +#else NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSOperatingSystemVersion vers = [[NSProcessInfo processInfo] operatingSystemVersion]; @@ -2287,12 +2505,10 @@ MachProcess::GetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *pa if (patch) *patch = vers.patchVersion; - success = true; - [pool drain]; + + return true; #endif - - return success; } // Do the process specific setup for attach. If this returns NULL, then there's no diff --git a/tools/debugserver/source/MacOSX/MachTask.h b/tools/debugserver/source/MacOSX/MachTask.h index 96b991478c789..d8021e8f7fe3b 100644 --- a/tools/debugserver/source/MacOSX/MachTask.h +++ b/tools/debugserver/source/MacOSX/MachTask.h @@ -93,26 +93,6 @@ public: const MachProcess * Process () const { return m_process; } nub_size_t PageSize (); - - bool HasMallocLoggingEnabled (); - - // enumerate the malloc records for a given address (starting with Mac OS X 10.6 Snow Leopard it should include - // all allocations that *include* address, rather than just those *starting* at address) - bool EnumerateMallocRecords (mach_vm_address_t address, - MachMallocEvent *event_buffer, - uint32_t buffer_size, - uint32_t *count); - - // enumerate every malloc record generated by this task, no matter what the address - bool EnumerateMallocRecords (MachMallocEvent *event_buffer, - uint32_t buffer_size, - uint32_t *count); - - // given a malloc event, report every stack frame that led to this event - bool EnumerateMallocFrames (MachMallocEventId event_id, - mach_vm_address_t *function_addresses_buffer, - uint32_t buffer_size, - uint32_t *count); protected: MachProcess * m_process; // The mach process that owns this MachTask diff --git a/tools/debugserver/source/MacOSX/MachTask.mm b/tools/debugserver/source/MacOSX/MachTask.mm index 9c725663d5593..cc1d6a38ec050 100644 --- a/tools/debugserver/source/MacOSX/MachTask.mm +++ b/tools/debugserver/source/MacOSX/MachTask.mm @@ -40,7 +40,6 @@ #include "DNBLog.h" #include "MachProcess.h" #include "DNBDataRef.h" -#include "stack_logging.h" #ifdef WITH_SPRINGBOARD @@ -1053,90 +1052,6 @@ MachTask::DeallocateMemory (nub_addr_t addr) return false; } -static void foundStackLog(mach_stack_logging_record_t record, void *context) { - *((bool*)context) = true; -} - -bool -MachTask::HasMallocLoggingEnabled () -{ - bool found = false; - - __mach_stack_logging_enumerate_records(m_task, 0x0, foundStackLog, &found); - return found; -} - -struct history_enumerator_impl_data -{ - MachMallocEvent *buffer; - uint32_t *position; - uint32_t count; -}; - -static void history_enumerator_impl(mach_stack_logging_record_t record, void* enum_obj) -{ - history_enumerator_impl_data *data = (history_enumerator_impl_data*)enum_obj; - - if (*data->position >= data->count) - return; - - data->buffer[*data->position].m_base_address = record.address; - data->buffer[*data->position].m_size = record.argument; - data->buffer[*data->position].m_event_id = record.stack_identifier; - data->buffer[*data->position].m_event_type = record.type_flags == stack_logging_type_alloc ? eMachMallocEventTypeAlloc : - record.type_flags == stack_logging_type_dealloc ? eMachMallocEventTypeDealloc : - eMachMallocEventTypeOther; - *data->position+=1; -} - -bool -MachTask::EnumerateMallocRecords (MachMallocEvent *event_buffer, - uint32_t buffer_size, - uint32_t *count) -{ - return EnumerateMallocRecords(0, - event_buffer, - buffer_size, - count); -} - -bool -MachTask::EnumerateMallocRecords (mach_vm_address_t address, - MachMallocEvent *event_buffer, - uint32_t buffer_size, - uint32_t *count) -{ - if (!event_buffer || !count) - return false; - - if (buffer_size == 0) - return false; - - *count = 0; - history_enumerator_impl_data data = { event_buffer, count, buffer_size }; - __mach_stack_logging_enumerate_records(m_task, address, history_enumerator_impl, &data); - return (*count > 0); -} - -bool -MachTask::EnumerateMallocFrames (MachMallocEventId event_id, - mach_vm_address_t *function_addresses_buffer, - uint32_t buffer_size, - uint32_t *count) -{ - if (!function_addresses_buffer || !count) - return false; - - if (buffer_size == 0) - return false; - - __mach_stack_logging_frames_for_uniqued_stack(m_task, event_id, &function_addresses_buffer[0], buffer_size, count); - *count -= 1; - if (function_addresses_buffer[*count-1] < PageSize()) - *count -= 1; - return (*count > 0); -} - nub_size_t MachTask::PageSize () { diff --git a/tools/debugserver/source/MacOSX/Makefile b/tools/debugserver/source/MacOSX/Makefile deleted file mode 100644 index d047444a9c811..0000000000000 --- a/tools/debugserver/source/MacOSX/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -##===- tools/debugserver/source/MacOSX/Makefile ------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -LLDB_LEVEL := ../../../.. - -DIRS := i386 x86_64 - -TOOLNAME = debugserver - -CODESIGN_TOOLS := 1 - -TOOL_CODESIGN_IDENTITY := lldb_codesign - -LLVMLibsOptions += -llldbDebugserverCommon -llldbUtility -llldbDebugserverMacOSX_I386 -llldbDebugserverMacOSX_X86_64 \ - -framework Foundation -framework CoreFoundation - -GENERATED_MACH_SOURCES = $(PROJ_OBJ_DIR)/mach_excServer.c $(PROJ_OBJ_DIR)/mach_excUser.c - -SOURCES := CFBundle.cpp \ - CFData.cpp \ - CFString.cpp \ - MachException.cpp \ - MachProcess.cpp \ - MachTask.cpp \ - MachThread.cpp \ - MachThreadList.cpp \ - MachVMMemory.cpp \ - MachVMRegion.cpp - -BUILT_SOURCES = $(GENERATED_MACH_SOURCES) $(PROJ_OBJ_DIR)/HasAVX.o - -CPP.Flags += -I$(PROJ_OBJ_DIR)/../.. -I$(PROJ_SRC_DIR)/.. - -LD.Flags += -Wl,-sectcreate,__TEXT,__info_plist,$(PROJ_SRC_DIR)/../../resources/lldb-debugserver-Info.plist - -include $(LLDB_LEVEL)/Makefile - -ObjectsO += $(PROJ_OBJ_DIR)/HasAVX.o - -$(PROJ_OBJ_DIR)/HasAVX.o: $(PROJ_SRC_DIR)/HasAVX.s - $(Echo) "Compiling HasAVX.s for $(BuildMode) build" $(PIC_FLAG) - $(CC) $(TargetCommonOpts) $(CompileCommonOpts) -c $< -o $@ - -ifeq ($(HOST_OS),Darwin) -LLVMLibsOptions += -Wl,-rpath,@loader_path/../lib/ -endif - -$(GENERATED_MACH_SOURCES): - mig -I$(PROJ_OBJ_DIR)/../.. $(PROJ_SRC_DIR)/dbgnub-mig.defs
\ No newline at end of file diff --git a/tools/debugserver/source/MacOSX/i386/Makefile b/tools/debugserver/source/MacOSX/i386/Makefile deleted file mode 100644 index f770b19834bb5..0000000000000 --- a/tools/debugserver/source/MacOSX/i386/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -##===- tools/debugserver/source/MacOSX/i386/Makefile -------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LLDB_LEVEL := ../../../../.. - -LIBRARYNAME := lldbDebugserverMacOSX_I386 -BUILD_ARCHIVE = 1 - -SOURCES := DNBArchImplI386.cpp - -include $(LLDB_LEVEL)/Makefile - -CPP.Flags += -I$(PROJ_SRC_DIR)/.. -I$(PROJ_SRC_DIR)/../.. -I$(PROJ_OBJ_DIR)/../../..
\ No newline at end of file diff --git a/tools/debugserver/source/MacOSX/x86_64/Makefile b/tools/debugserver/source/MacOSX/x86_64/Makefile deleted file mode 100644 index bf488c8592421..0000000000000 --- a/tools/debugserver/source/MacOSX/x86_64/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -##===- tools/debugserver/source/MacOSX/i386/Makefile -------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LLDB_LEVEL := ../../../../.. - -LIBRARYNAME := lldbDebugserverMacOSX_X86_64 -BUILD_ARCHIVE = 1 - -SOURCES := DNBArchImplX86_64.cpp - -include $(LLDB_LEVEL)/Makefile - -CPP.Flags += -I$(PROJ_SRC_DIR)/.. -I$(PROJ_SRC_DIR)/../.. -I$(PROJ_OBJ_DIR)/../../..
\ No newline at end of file |