diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
commit | 9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc (patch) | |
tree | dd2a1ddf0476664c2b823409c36cbccd52662ca7 /source | |
parent | 3bd2e91faeb9eeec1aae82c64a3253afff551cfd (diff) | |
download | src-test2-9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc.tar.gz src-test2-9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc.zip |
Notes
Diffstat (limited to 'source')
386 files changed, 63539 insertions, 2225 deletions
diff --git a/source/API/CMakeLists.txt b/source/API/CMakeLists.txt new file mode 100644 index 000000000000..06a26e17c929 --- /dev/null +++ b/source/API/CMakeLists.txt @@ -0,0 +1,119 @@ +if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) + add_definitions( -DEXPORT_LIBLLDB ) +endif() + +# Include this so that add_lldb_library() has the list of dependencies +# for liblldb to link against +include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake) + +add_lldb_library(liblldb SHARED + SBAddress.cpp + SBAttachInfo.cpp + SBBlock.cpp + SBBreakpoint.cpp + SBBreakpointLocation.cpp + SBBroadcaster.cpp + SBCommandInterpreter.cpp + SBCommandReturnObject.cpp + SBCommunication.cpp + SBCompileUnit.cpp + SBData.cpp + SBDebugger.cpp + SBDeclaration.cpp + SBError.cpp + SBEvent.cpp + SBExecutionContext.cpp + SBExpressionOptions.cpp + SBFileSpec.cpp + SBFileSpecList.cpp + SBFrame.cpp + SBFunction.cpp + SBHostOS.cpp + SBInstruction.cpp + SBInstructionList.cpp + SBLanguageRuntime.cpp + SBLaunchInfo.cpp + SBLineEntry.cpp + SBListener.cpp + SBModule.cpp + SBModuleSpec.cpp + SBPlatform.cpp + SBProcess.cpp + SBQueue.cpp + SBQueueItem.cpp + SBSection.cpp + SBSourceManager.cpp + SBStream.cpp + SBStringList.cpp + SBSymbol.cpp + SBSymbolContext.cpp + SBSymbolContextList.cpp + SBTarget.cpp + SBThread.cpp + SBThreadCollection.cpp + SBThreadPlan.cpp + SBType.cpp + SBTypeCategory.cpp + SBTypeEnumMember.cpp + SBTypeFilter.cpp + SBTypeFormat.cpp + SBTypeNameSpecifier.cpp + SBTypeSummary.cpp + SBTypeSynthetic.cpp + SBValue.cpp + SBValueList.cpp + SBVariablesOptions.cpp + SBWatchpoint.cpp + SBUnixSignals.cpp + SystemInitializerFull.cpp + ${LLDB_WRAP_PYTHON} + ) + +# This should not be part of LLDBDependencies.cmake, because we don't +# want every single library taking a dependency on the script interpreters. +target_link_libraries(liblldb PRIVATE + lldbPluginScriptInterpreterNone + lldbPluginScriptInterpreterPython + ) + +set_target_properties(liblldb + PROPERTIES + VERSION ${LLDB_VERSION} + ) + +if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") + if (NOT LLDB_EXPORT_ALL_SYMBOLS) + # If we're not exporting all symbols, we'll want to explicitly set + # the exported symbols here. This prevents 'log enable --stack ...' + # from working on some systems but limits the liblldb size. + MESSAGE("-- Symbols (liblldb): only exporting liblldb.exports symbols") + add_llvm_symbol_exports(liblldb ${CMAKE_CURRENT_SOURCE_DIR}/liblldb.exports) + else() + # Don't use an explicit export. Instead, tell the linker to + # export all symbols. + MESSAGE("-- Symbols (liblldb): exporting all symbols") + # Darwin linker doesn't need this extra step. + if (NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") + lldb_append_link_flags(liblldb "-Wl,--export-dynamic") + endif() + endif() +endif() + +if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) + # Only MSVC has the ABI compatibility problem and avoids using FindPythonLibs, + # so only it needs to explicitly link against ${PYTHON_LIBRARY} + if (MSVC AND NOT LLDB_DISABLE_PYTHON) + target_link_libraries(liblldb PRIVATE ${PYTHON_LIBRARY}) + endif() +else() + set_target_properties(liblldb + PROPERTIES + OUTPUT_NAME lldb + ) +endif() + +if (LLDB_WRAP_PYTHON) + add_dependencies(liblldb swig_wrapper) +endif() +target_link_libraries(liblldb ${cmake_2_8_12_PRIVATE} ${LLDB_SYSTEM_LIBS}) + diff --git a/source/API/Makefile b/source/API/Makefile new file mode 100644 index 000000000000..e35b2c37735e --- /dev/null +++ b/source/API/Makefile @@ -0,0 +1,18 @@ +##===- source/API/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 := lldbAPI +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile + +ifeq ($(HOST_OS),MingW) +CXXFLAGS += -DEXPORT_LIBLLDB +endif diff --git a/source/API/SBProcess.cpp b/source/API/SBProcess.cpp index dceadeca69e5..42554726c018 100644 --- a/source/API/SBProcess.cpp +++ b/source/API/SBProcess.cpp @@ -995,7 +995,14 @@ SBProcess::GetStateFromEvent (const SBEvent &event) bool SBProcess::GetRestartedFromEvent (const SBEvent &event) { - return Process::ProcessEventData::GetRestartedFromEvent (event.get()); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool ret_val = Process::ProcessEventData::GetRestartedFromEvent (event.get()); + + if (log) + log->Printf ("SBProcess::%s (event.sp=%p) => %d", __FUNCTION__, event.get(), ret_val); + + return ret_val; } size_t diff --git a/source/API/SystemInitializerFull.cpp b/source/API/SystemInitializerFull.cpp index f223357824e8..cbc01a6cff9a 100644 --- a/source/API/SystemInitializerFull.cpp +++ b/source/API/SystemInitializerFull.cpp @@ -102,7 +102,7 @@ PyInit__lldb(void); #define LLDBSwigPyInit PyInit__lldb #else -extern "C" void +extern "C" void init_lldb(void); #define LLDBSwigPyInit init_lldb @@ -308,7 +308,7 @@ SystemInitializerFull::Initialize() SystemRuntimeMacOSX::Initialize(); RenderScriptRuntime::Initialize(); GoLanguageRuntime::Initialize(); - + CPlusPlusLanguage::Initialize(); GoLanguage::Initialize(); ObjCLanguage::Initialize(); @@ -430,7 +430,7 @@ SystemInitializerFull::Terminate() GoLanguage::Terminate(); ObjCLanguage::Terminate(); ObjCPlusPlusLanguage::Terminate(); - + #if defined(__APPLE__) ProcessMachCore::Terminate(); ProcessKDP::Terminate(); diff --git a/source/Breakpoint/CMakeLists.txt b/source/Breakpoint/CMakeLists.txt new file mode 100644 index 000000000000..85494b15aa08 --- /dev/null +++ b/source/Breakpoint/CMakeLists.txt @@ -0,0 +1,23 @@ +add_lldb_library(lldbBreakpoint + Breakpoint.cpp + BreakpointID.cpp + BreakpointIDList.cpp + BreakpointList.cpp + BreakpointLocation.cpp + BreakpointLocationCollection.cpp + BreakpointLocationList.cpp + BreakpointOptions.cpp + BreakpointResolver.cpp + BreakpointResolverAddress.cpp + BreakpointResolverFileLine.cpp + BreakpointResolverFileRegex.cpp + BreakpointResolverName.cpp + BreakpointSite.cpp + BreakpointSiteList.cpp + Stoppoint.cpp + StoppointCallbackContext.cpp + StoppointLocation.cpp + Watchpoint.cpp + WatchpointList.cpp + WatchpointOptions.cpp + ) diff --git a/source/Breakpoint/Makefile b/source/Breakpoint/Makefile new file mode 100644 index 000000000000..223e4c24465f --- /dev/null +++ b/source/Breakpoint/Makefile @@ -0,0 +1,14 @@ +##===- source/Breakpoint/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 := lldbBreakpoint +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt new file mode 100644 index 000000000000..f23af477bf89 --- /dev/null +++ b/source/CMakeLists.txt @@ -0,0 +1,92 @@ +include_directories(.) + +if ( CMAKE_SYSTEM_NAME MATCHES "Linux" ) +include_directories( + Plugins/Process/Linux + Plugins/Process/POSIX + ) +endif () + +if ( CMAKE_SYSTEM_NAME MATCHES "FreeBSD" ) +include_directories( + Plugins/Process/FreeBSD + Plugins/Process/POSIX + ) +endif () + +if ( CMAKE_SYSTEM_NAME MATCHES "NetBSD" ) +include_directories( + Plugins/Process/POSIX + ) +endif () + + +set(lldbBase_SOURCES + lldb.cpp + ) + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + set(LLDB_VERS_GENERATED_FILE ${LLDB_BINARY_DIR}/source/LLDB_vers.c) + add_custom_command(OUTPUT ${LLDB_VERS_GENERATED_FILE} + COMMAND ${LLDB_SOURCE_DIR}/scripts/generate-vers.pl + ${LLDB_SOURCE_DIR}/lldb.xcodeproj/project.pbxproj liblldb_core + > ${LLDB_VERS_GENERATED_FILE} + DEPENDS ${LLDB_SOURCE_DIR}/scripts/generate-vers.pl + ${LLDB_SOURCE_DIR}/lldb.xcodeproj/project.pbxproj) + set_source_files_properties(${LLDB_VERS_GENERATED_FILE} PROPERTIES GENERATED 1) + # Add this to lldbBase since lldb.cpp uses the symbol defined here. + list(APPEND lldbBase_SOURCES ${LLDB_VERS_GENERATED_FILE}) + add_custom_target(lldbGeneratedVersion + DEPENDS ${LLDB_VERS_GENERATED_FILE}) +endif() + +add_lldb_library(lldbBase + ${lldbBase_SOURCES} + ) + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_dependencies(lldbBase lldbGeneratedVersion) +endif() + +add_subdirectory(Breakpoint) +add_subdirectory(Commands) +add_subdirectory(Core) +add_subdirectory(DataFormatters) +add_subdirectory(Expression) +add_subdirectory(Host) +add_subdirectory(Initialization) +add_subdirectory(Interpreter) +add_subdirectory(Plugins) +add_subdirectory(Symbol) +add_subdirectory(Target) +add_subdirectory(Utility) + +# Build API last. Since liblldb needs to link against every other target, it needs +# those targets to have already been created. +add_subdirectory(API) + +# Determine LLDB revision and repository. GetSourceVersion and GetRepositoryPath are shell-scripts, and as +# such will not work on Windows. +if ( NOT CMAKE_SYSTEM_NAME MATCHES "Windows" ) + execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetSourceVersion ${LLDB_SOURCE_DIR} + OUTPUT_VARIABLE LLDB_REVISION) + if ( LLDB_REVISION ) + string(REGEX REPLACE "(\r?\n)+$" "" LLDB_REVISION ${LLDB_REVISION}) + endif() + + execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetRepositoryPath ${LLDB_SOURCE_DIR} + OUTPUT_VARIABLE LLDB_REPOSITORY) + if ( LLDB_REPOSITORY ) + # Replace newline characters with spaces + string(REGEX REPLACE "(\r?\n)+" " " LLDB_REPOSITORY ${LLDB_REPOSITORY}) + + # Remove trailing spaces + string(REGEX REPLACE "(\ )+$" "" LLDB_REPOSITORY ${LLDB_REPOSITORY}) + endif() + + set_property( + SOURCE lldb.cpp + PROPERTY COMPILE_DEFINITIONS "LLDB_REVISION=\"${LLDB_REVISION}\"" "LLDB_REPOSITORY=\"${LLDB_REPOSITORY}\"") +endif () +# FIXME: implement svn/git revision and repository parsing solution on Windows. There is an SVN-only +# revision parsing solution in tools/clang/lib/Basic/CMakelists.txt. diff --git a/source/Commands/CMakeLists.txt b/source/Commands/CMakeLists.txt new file mode 100644 index 000000000000..8805bbf6dc94 --- /dev/null +++ b/source/Commands/CMakeLists.txt @@ -0,0 +1,32 @@ +add_lldb_library(lldbCommands + CommandCompletions.cpp + CommandObjectApropos.cpp + CommandObjectArgs.cpp + CommandObjectBreakpoint.cpp + CommandObjectBreakpointCommand.cpp + CommandObjectBugreport.cpp + CommandObjectCommands.cpp + CommandObjectDisassemble.cpp + CommandObjectExpression.cpp + CommandObjectFrame.cpp + CommandObjectGUI.cpp + CommandObjectHelp.cpp + CommandObjectLog.cpp + CommandObjectMemory.cpp + CommandObjectMultiword.cpp + CommandObjectPlatform.cpp + CommandObjectPlugin.cpp + CommandObjectProcess.cpp + CommandObjectQuit.cpp + CommandObjectRegister.cpp + CommandObjectSettings.cpp + CommandObjectSource.cpp + CommandObjectSyntax.cpp + CommandObjectTarget.cpp + CommandObjectThread.cpp + CommandObjectType.cpp + CommandObjectVersion.cpp + CommandObjectWatchpoint.cpp + CommandObjectWatchpointCommand.cpp + CommandObjectLanguage.cpp + ) diff --git a/source/Commands/CommandObjectSource.cpp b/source/Commands/CommandObjectSource.cpp index 08c8d46d1ee0..a9e52d1a76f3 100644 --- a/source/Commands/CommandObjectSource.cpp +++ b/source/Commands/CommandObjectSource.cpp @@ -27,6 +27,7 @@ #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StackFrame.h" #include "lldb/Target/TargetList.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/Options.h" @@ -34,9 +35,11 @@ using namespace lldb; using namespace lldb_private; -//------------------------------------------------------------------------- -// CommandObjectSourceInfo -//------------------------------------------------------------------------- + +#pragma mark CommandObjectSourceInfo +//---------------------------------------------------------------------- +// CommandObjectSourceInfo - debug line entries dumping command +//---------------------------------------------------------------------- class CommandObjectSourceInfo : public CommandObjectParsed { @@ -44,14 +47,9 @@ class CommandObjectSourceInfo : public CommandObjectParsed class CommandOptions : public Options { public: - CommandOptions (CommandInterpreter &interpreter) : - Options(interpreter) - { - } + CommandOptions (CommandInterpreter &interpreter) : Options(interpreter) {} - ~CommandOptions () override - { - } + ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override @@ -60,19 +58,44 @@ class CommandObjectSourceInfo : public CommandObjectParsed const int short_option = g_option_table[option_idx].short_option; switch (short_option) { - case 'l': - start_line = StringConvert::ToUInt32 (option_arg, 0); - if (start_line == 0) - error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); - break; - - case 'f': - file_name = option_arg; - break; - - default: - error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + case 'l': + start_line = StringConvert::ToUInt32(option_arg, 0); + if (start_line == 0) + error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); + break; + + case 'e': + end_line = StringConvert::ToUInt32(option_arg, 0); + if (end_line == 0) + error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); + break; + + case 'c': + num_lines = StringConvert::ToUInt32(option_arg, 0); + if (num_lines == 0) + error.SetErrorStringWithFormat("invalid line count: '%s'", option_arg); + break; + + case 'f': + file_name = option_arg; + break; + + case 'n': + symbol_name = option_arg; + break; + + case 'a': + { + ExecutionContext exe_ctx(m_interpreter.GetExecutionContext()); + address = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + } break; + case 's': + modules.push_back(std::string(option_arg)); + break; + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; } return error; @@ -83,10 +106,15 @@ class CommandObjectSourceInfo : public CommandObjectParsed { file_spec.Clear(); file_name.clear(); + symbol_name.clear(); + address = LLDB_INVALID_ADDRESS; start_line = 0; + end_line = 0; + num_lines = 0; + modules.clear(); } - const OptionDefinition* + const OptionDefinition * GetDefinitions () override { return g_option_table; @@ -96,24 +124,24 @@ class CommandObjectSourceInfo : public CommandObjectParsed // Instance variables to hold the values for command options. FileSpec file_spec; std::string file_name; + std::string symbol_name; + lldb::addr_t address; uint32_t start_line; - + uint32_t end_line; + uint32_t num_lines; + STLStringArray modules; }; - -public: - CommandObjectSourceInfo(CommandInterpreter &interpreter) : - CommandObjectParsed (interpreter, - "source info", - "Display information about the source lines from the current executable's debug info.", - "source info [<cmd-options>]"), - m_options (interpreter) - { - } - ~CommandObjectSourceInfo () override +public: + CommandObjectSourceInfo (CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "source info", "Display source line information (as specified) based " + "on the current executable's debug info.", + NULL, eCommandRequiresTarget), + m_options(interpreter) { } + ~CommandObjectSourceInfo () override {} Options * GetOptions () override @@ -122,25 +150,576 @@ public: } protected: + + // Dump the line entries in each symbol context. + // Return the number of entries found. + // If module_list is set, only dump lines contained in one of the modules. + // If file_spec is set, only dump lines in the file. + // If the start_line option was specified, don't print lines less than start_line. + // If the end_line option was specified, don't print lines greater than end_line. + // If the num_lines option was specified, dont print more than num_lines entries. + uint32_t + DumpLinesInSymbolContexts (Stream &strm, const SymbolContextList &sc_list, + const ModuleList &module_list, const FileSpec &file_spec) + { + uint32_t start_line = m_options.start_line; + uint32_t end_line = m_options.end_line; + uint32_t num_lines = m_options.num_lines; + Target *target = m_exe_ctx.GetTargetPtr(); + + uint32_t num_matches = 0; + bool has_path = false; + if (file_spec) + { + assert(file_spec.GetFilename().AsCString()); + has_path = (file_spec.GetDirectory().AsCString() != 0); + } + + // Dump all the line entries for the file in the list. + ConstString last_module_file_name; + uint32_t num_scs = sc_list.GetSize(); + for (uint32_t i = 0; i < num_scs; ++i) + { + SymbolContext sc; + sc_list.GetContextAtIndex(i, sc); + if (sc.comp_unit) + { + Module *module = sc.module_sp.get(); + CompileUnit *cu = sc.comp_unit; + const LineEntry &line_entry = sc.line_entry; + assert(module && cu); + + // Are we looking for specific modules, files or lines? + if (module_list.GetSize() && module_list.GetIndexForModule(module) == LLDB_INVALID_INDEX32) + continue; + if (file_spec && !lldb_private::FileSpec::Equal(file_spec, line_entry.file, has_path)) + continue; + if (start_line > 0 && line_entry.line < start_line) + continue; + if (end_line > 0 && line_entry.line > end_line) + continue; + if (num_lines > 0 && num_matches > num_lines) + continue; + + // Print a new header if the module changed. + const ConstString &module_file_name = module->GetFileSpec().GetFilename(); + assert(module_file_name); + if (module_file_name != last_module_file_name) + { + if (num_matches > 0) + strm << "\n\n"; + strm << "Lines found in module `" << module_file_name << "\n"; + } + // Dump the line entry. + line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu, + target, /*show_address_only=*/false); + strm << "\n"; + last_module_file_name = module_file_name; + num_matches++; + } + } + return num_matches; + } + + // Dump the requested line entries for the file in the compilation unit. + // Return the number of entries found. + // If module_list is set, only dump lines contained in one of the modules. + // If the start_line option was specified, don't print lines less than start_line. + // If the end_line option was specified, don't print lines greater than end_line. + // If the num_lines option was specified, dont print more than num_lines entries. + uint32_t + DumpFileLinesInCompUnit (Stream &strm, Module *module, CompileUnit *cu, const FileSpec &file_spec) + { + uint32_t start_line = m_options.start_line; + uint32_t end_line = m_options.end_line; + uint32_t num_lines = m_options.num_lines; + Target *target = m_exe_ctx.GetTargetPtr(); + + uint32_t num_matches = 0; + assert(module); + if (cu) + { + assert(file_spec.GetFilename().AsCString()); + bool has_path = (file_spec.GetDirectory().AsCString() != 0); + const FileSpecList &cu_file_list = cu->GetSupportFiles(); + size_t file_idx = cu_file_list.FindFileIndex(0, file_spec, has_path); + if (file_idx != UINT32_MAX) + { + // Update the file to how it appears in the CU. + const FileSpec &cu_file_spec = cu_file_list.GetFileSpecAtIndex(file_idx); + + // Dump all matching lines at or above start_line for the file in the CU. + const ConstString &file_spec_name = file_spec.GetFilename(); + const ConstString &module_file_name = module->GetFileSpec().GetFilename(); + bool cu_header_printed = false; + uint32_t line = start_line; + while (true) + { + LineEntry line_entry; + + // Find the lowest index of a line entry with a line equal to + // or higher than 'line'. + uint32_t start_idx = 0; + start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, + /*exact=*/false, &line_entry); + if (start_idx == UINT32_MAX) + // No more line entries for our file in this CU. + break; + + if (end_line > 0 && line_entry.line > end_line) + break; + + // Loop through to find any other entries for this line, dumping each. + line = line_entry.line; + do + { + num_matches++; + if (num_lines > 0 && num_matches > num_lines) + break; + assert(lldb_private::FileSpec::Equal(cu_file_spec, line_entry.file, has_path)); + if (!cu_header_printed) + { + if (num_matches > 0) + strm << "\n\n"; + strm << "Lines found for file " << file_spec_name + << " in compilation unit " << cu->GetFilename() + << " in `" << module_file_name << "\n"; + cu_header_printed = true; + } + line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu, + target, /*show_address_only=*/false); + strm << "\n"; + + // Anymore after this one? + start_idx++; + start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, + /*exact=*/true, &line_entry); + } while (start_idx != UINT32_MAX); + + // Try the next higher line, starting over at start_idx 0. + line++; + } + } + } + return num_matches; + } + + // Dump the requested line entries for the file in the module. + // Return the number of entries found. + // If module_list is set, only dump lines contained in one of the modules. + // If the start_line option was specified, don't print lines less than start_line. + // If the end_line option was specified, don't print lines greater than end_line. + // If the num_lines option was specified, dont print more than num_lines entries. + uint32_t + DumpFileLinesInModule (Stream &strm, Module *module, const FileSpec &file_spec) + { + uint32_t num_matches = 0; + if (module) + { + // Look through all the compilation units (CUs) in this module for ones that + // contain lines of code from this source file. + for (size_t i = 0; i < module->GetNumCompileUnits(); i++) + { + // Look for a matching source file in this CU. + CompUnitSP cu_sp(module->GetCompileUnitAtIndex(i)); + if (cu_sp) + { + num_matches += DumpFileLinesInCompUnit(strm, module, cu_sp.get(), file_spec); + } + } + } + return num_matches; + } + + // Given an address and a list of modules, append the symbol contexts of all line entries + // containing the address found in the modules and return the count of matches. If none + // is found, return an error in 'error_strm'. + size_t + GetSymbolContextsForAddress (const ModuleList &module_list, lldb::addr_t addr, + SymbolContextList &sc_list, StreamString &error_strm) + { + Address so_addr; + size_t num_matches = 0; + assert(module_list.GetSize() > 0); + Target *target = m_exe_ctx.GetTargetPtr(); + if (target->GetSectionLoadList().IsEmpty()) + { + // The target isn't loaded yet, we need to lookup the file address in + // all modules. Note: the module list option does not apply to addresses. + const size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; ++i) + { + ModuleSP module_sp(module_list.GetModuleAtIndex(i)); + if (!module_sp) + continue; + if (module_sp->ResolveFileAddress(addr, so_addr)) + { + SymbolContext sc; + sc.Clear(true); + if (module_sp->ResolveSymbolContextForAddress(so_addr, eSymbolContextEverything, sc) & + eSymbolContextLineEntry) + { + sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false); + ++num_matches; + } + } + } + if (num_matches == 0) + error_strm.Printf("Source information for file address 0x%" PRIx64 + " not found in any modules.\n", addr); + } + else + { + // The target has some things loaded, resolve this address to a + // compile unit + file + line and display + if (target->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) + { + ModuleSP module_sp(so_addr.GetModule()); + // Check to make sure this module is in our list. + if (module_sp && + module_list.GetIndexForModule(module_sp.get()) != LLDB_INVALID_INDEX32) + { + SymbolContext sc; + sc.Clear(true); + if (module_sp->ResolveSymbolContextForAddress(so_addr, eSymbolContextEverything, sc) & + eSymbolContextLineEntry) + { + sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false); + ++num_matches; + } + else + { + StreamString addr_strm; + so_addr.Dump(&addr_strm, NULL, Address::DumpStyleModuleWithFileAddress); + error_strm.Printf("Address 0x%" PRIx64 " resolves to %s, but there is" + " no source information available for this address.\n", + addr, addr_strm.GetData()); + } + } + else + { + StreamString addr_strm; + so_addr.Dump(&addr_strm, NULL, Address::DumpStyleModuleWithFileAddress); + error_strm.Printf("Address 0x%" PRIx64 " resolves to %s, but it cannot" + " be found in any modules.\n", + addr, addr_strm.GetData()); + } + } + else + error_strm.Printf("Unable to resolve address 0x%" PRIx64 ".\n", addr); + } + return num_matches; + } + + // Dump the line entries found in functions matching the name specified in the option. bool - DoExecute (Args& command, CommandReturnObject &result) override + DumpLinesInFunctions (CommandReturnObject &result) { - result.AppendError ("Not yet implemented"); - result.SetStatus (eReturnStatusFailed); - return false; + SymbolContextList sc_list_funcs; + ConstString name(m_options.symbol_name.c_str()); + SymbolContextList sc_list_lines; + Target *target = m_exe_ctx.GetTargetPtr(); + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + + // Note: module_list can't be const& because FindFunctionSymbols isn't const. + ModuleList module_list = (m_module_list.GetSize() > 0) ? + m_module_list : target->GetImages(); + size_t num_matches = module_list.FindFunctions(name, + eFunctionNameTypeAuto, + /*include_symbols=*/false, + /*include_inlines=*/true, + /*append=*/true, + sc_list_funcs); + if (!num_matches) + { + // If we didn't find any functions with that name, try searching for + // symbols that line up exactly with function addresses. + SymbolContextList sc_list_symbols; + size_t num_symbol_matches = module_list.FindFunctionSymbols(name, + eFunctionNameTypeAuto, + sc_list_symbols); + for (size_t i = 0; i < num_symbol_matches; i++) + { + SymbolContext sc; + sc_list_symbols.GetContextAtIndex(i, sc); + if (sc.symbol && sc.symbol->ValueIsAddress()) + { + const Address &base_address = sc.symbol->GetAddressRef(); + Function *function = base_address.CalculateSymbolContextFunction(); + if (function) + { + sc_list_funcs.Append(SymbolContext(function)); + num_matches++; + } + } + } + } + if (num_matches == 0) + { + result.AppendErrorWithFormat("Could not find function named \'%s\'.\n", + m_options.symbol_name.c_str()); + return false; + } + for (size_t i = 0; i < num_matches; i++) + { + SymbolContext sc; + sc_list_funcs.GetContextAtIndex(i, sc); + bool context_found_for_symbol = false; + // Loop through all the ranges in the function. + AddressRange range; + for (uint32_t r = 0; + sc.GetAddressRange(eSymbolContextEverything, + r, + /*use_inline_block_range=*/true, + range); + ++r) + { + // Append the symbol contexts for each address in the range to sc_list_lines. + const Address &base_address = range.GetBaseAddress(); + const addr_t size = range.GetByteSize(); + lldb::addr_t start_addr = base_address.GetLoadAddress(target); + if (start_addr == LLDB_INVALID_ADDRESS) + start_addr = base_address.GetFileAddress(); + lldb::addr_t end_addr = start_addr + size; + for (lldb::addr_t addr = start_addr; addr < end_addr; addr += addr_byte_size) + { + StreamString error_strm; + if (!GetSymbolContextsForAddress(module_list, addr, sc_list_lines, error_strm)) + result.AppendWarningWithFormat("in symbol '%s': %s", + sc.GetFunctionName().AsCString(), + error_strm.GetData()); + else + context_found_for_symbol = true; + } + } + if (!context_found_for_symbol) + result.AppendWarningWithFormat("Unable to find line information" + " for matching symbol '%s'.\n", + sc.GetFunctionName().AsCString()); + } + if (sc_list_lines.GetSize() == 0) + { + result.AppendErrorWithFormat("No line information could be found" + " for any symbols matching '%s'.\n", + name.AsCString()); + return false; + } + FileSpec file_spec; + if (!DumpLinesInSymbolContexts(result.GetOutputStream(), + sc_list_lines, module_list, file_spec)) + { + result.AppendErrorWithFormat("Unable to dump line information for symbol '%s'.\n", + name.AsCString()); + return false; + } + return true; + } + + // Dump the line entries found for the address specified in the option. + bool + DumpLinesForAddress (CommandReturnObject &result) + { + Target *target = m_exe_ctx.GetTargetPtr(); + SymbolContextList sc_list; + + StreamString error_strm; + if (!GetSymbolContextsForAddress(target->GetImages(), m_options.address, sc_list, error_strm)) + { + result.AppendErrorWithFormat("%s.\n", error_strm.GetData()); + return false; + } + ModuleList module_list; + FileSpec file_spec; + if (!DumpLinesInSymbolContexts(result.GetOutputStream(), + sc_list, module_list, file_spec)) + { + result.AppendErrorWithFormat("No modules contain load address 0x%" PRIx64 ".\n", + m_options.address); + return false; + } + return true; + } + + // Dump the line entries found in the file specified in the option. + bool + DumpLinesForFile (CommandReturnObject &result) + { + FileSpec file_spec(m_options.file_name, false); + const char *filename = m_options.file_name.c_str(); + Target *target = m_exe_ctx.GetTargetPtr(); + const ModuleList &module_list = (m_module_list.GetSize() > 0) ? + m_module_list : target->GetImages(); + + bool displayed_something = false; + const size_t num_modules = module_list.GetSize(); + for (uint32_t i = 0; i < num_modules; ++i) + { + // Dump lines for this module. + Module *module = module_list.GetModulePointerAtIndex(i); + assert(module); + if (DumpFileLinesInModule(result.GetOutputStream(), module, file_spec)) + displayed_something = true; + } + if (!displayed_something) + { + result.AppendErrorWithFormat("No source filenames matched '%s'.\n", filename); + return false; + } + return true; + } + + // Dump the line entries for the current frame. + bool + DumpLinesForFrame (CommandReturnObject &result) + { + StackFrame *cur_frame = m_exe_ctx.GetFramePtr(); + if (cur_frame == NULL) + { + result.AppendError("No selected frame to use to find the default source."); + return false; + } + else if (!cur_frame->HasDebugInformation()) + { + result.AppendError("No debug info for the selected frame."); + return false; + } + else + { + const SymbolContext &sc = cur_frame->GetSymbolContext(eSymbolContextLineEntry); + SymbolContextList sc_list; + sc_list.Append(sc); + ModuleList module_list; + FileSpec file_spec; + if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list, module_list, file_spec)) + { + result.AppendError("No source line info available for the selected frame."); + return false; + } + } + return true; + } + + bool + DoExecute (Args &command, CommandReturnObject &result) override + { + const size_t argc = command.GetArgumentCount(); + + if (argc != 0) + { + result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", + GetCommandName()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Target *target = m_exe_ctx.GetTargetPtr(); + if (target == NULL) + { + target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError("invalid target, create a debug target using the " + "'target create' command."); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + result.GetOutputStream().SetAddressByteSize(addr_byte_size); + result.GetErrorStream().SetAddressByteSize(addr_byte_size); + + // Collect the list of modules to search. + m_module_list.Clear(); + if (m_options.modules.size() > 0) + { + for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) + { + FileSpec module_file_spec(m_options.modules[i].c_str(), false); + if (module_file_spec) + { + ModuleSpec module_spec(module_file_spec); + if (target->GetImages().FindModules(module_spec, m_module_list) == 0) + result.AppendWarningWithFormat("No module found for '%s'.\n", + m_options.modules[i].c_str()); + } + } + if (!m_module_list.GetSize()) + { + result.AppendError("No modules match the input."); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + else if (target->GetImages().GetSize() == 0) + { + result.AppendError("The target has no associated executable images."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // Check the arguments to see what lines we should dump. + if (!m_options.symbol_name.empty()) + { + // Print lines for symbol. + if (DumpLinesInFunctions(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + else if (m_options.address != LLDB_INVALID_ADDRESS) + { + // Print lines for an address. + if (DumpLinesForAddress(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + else if (!m_options.file_name.empty()) + { + // Dump lines for a file. + if (DumpLinesForFile(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + else + { + // Dump the line for the current frame. + if (DumpLinesForFrame(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + return result.Succeeded(); } CommandOptions m_options; + ModuleList m_module_list; }; -OptionDefinition -CommandObjectSourceInfo::CommandOptions::g_option_table[] = -{ -{ LLDB_OPT_SET_1, false, "line", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, -{ LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, NULL, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, -{ 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } +OptionDefinition CommandObjectSourceInfo::CommandOptions::g_option_table[] = { + {LLDB_OPT_SET_ALL, false, "count", 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeCount, + "The number of line entries to display."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "shlib", 's', OptionParser::eRequiredArgument, NULL, NULL, + CommandCompletions::eModuleCompletion, eArgTypeShlibName, + "Look up the source in the given module or shared library (can be " + "specified more than once)."}, + {LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, NULL, NULL, + CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, + {LLDB_OPT_SET_1, false, "line", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, + "The line number at which to start the displaying lines."}, + {LLDB_OPT_SET_1, false, "end-line", 'e', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, + "The line number at which to stop displaying lines."}, + {LLDB_OPT_SET_2, false, "name", 'n', OptionParser::eRequiredArgument, NULL, NULL, + CommandCompletions::eSymbolCompletion, eArgTypeSymbol, "The name of a function whose source to display."}, + {LLDB_OPT_SET_3, false, "address", 'a', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeAddressOrExpression, + "Lookup the address and display the source information for the " + "corresponding file and line."}, + {0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL} }; + #pragma mark CommandObjectSourceList //------------------------------------------------------------------------- // CommandObjectSourceList @@ -906,7 +1485,6 @@ CommandObjectSourceList::CommandOptions::g_option_table[] = }; #pragma mark CommandObjectMultiwordSource - //------------------------------------------------------------------------- // CommandObjectMultiwordSource //------------------------------------------------------------------------- @@ -917,8 +1495,7 @@ CommandObjectMultiwordSource::CommandObjectMultiwordSource (CommandInterpreter & "A set of commands for accessing source file information", "source <subcommand> [<subcommand-options>]") { - // "source info" isn't implemented yet... - //LoadSubCommand ("info", CommandObjectSP (new CommandObjectSourceInfo (interpreter))); + LoadSubCommand ("info", CommandObjectSP (new CommandObjectSourceInfo (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectSourceList (interpreter))); } diff --git a/source/Commands/CommandObjectTarget.cpp b/source/Commands/CommandObjectTarget.cpp index 026ae3570854..57ec1c953fcf 100644 --- a/source/Commands/CommandObjectTarget.cpp +++ b/source/Commands/CommandObjectTarget.cpp @@ -2577,8 +2577,9 @@ protected: if (command.GetArgumentCount() == 0) { - result.AppendErrorWithFormat ("\nSyntax: %s\n", m_cmd_syntax.c_str()); + result.AppendError ("file option must be specified."); result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); } else { diff --git a/source/Commands/CommandObjectType.cpp b/source/Commands/CommandObjectType.cpp index b57ac70f8f1a..2b803e7eb0d3 100644 --- a/source/Commands/CommandObjectType.cpp +++ b/source/Commands/CommandObjectType.cpp @@ -1396,11 +1396,6 @@ protected: auto category_closure = [&result, &formatter_regex] (const lldb::TypeCategoryImplSP& category) -> void { result.GetOutputStream().Printf("-----------------------\nCategory: %s\n-----------------------\n", category->GetName()); - - typedef const std::shared_ptr<FormatterType> Bar; - typedef std::function<bool(ConstString,Bar)> Func1Type; - typedef std::function<bool(RegularExpressionSP,Bar)> Func2Type; - TypeCategoryImpl::ForEachCallbacks<FormatterType> foreach; foreach.SetExact([&result, &formatter_regex] (ConstString name, const FormatterSharedPointer& format_sp) -> bool { if (formatter_regex) diff --git a/source/Commands/Makefile b/source/Commands/Makefile new file mode 100644 index 000000000000..1a66485a0c21 --- /dev/null +++ b/source/Commands/Makefile @@ -0,0 +1,16 @@ +##===- source/Commands/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 := lldbCommands +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile + +EXTRA_OPTIONS += -Wno-four-char-constants
\ No newline at end of file diff --git a/source/Core/CMakeLists.txt b/source/Core/CMakeLists.txt new file mode 100644 index 000000000000..b975fb88e784 --- /dev/null +++ b/source/Core/CMakeLists.txt @@ -0,0 +1,76 @@ +add_lldb_library(lldbCore + Address.cpp + AddressRange.cpp + AddressResolver.cpp + AddressResolverFileLine.cpp + AddressResolverName.cpp + ArchSpec.cpp + Baton.cpp + Broadcaster.cpp + Communication.cpp + Connection.cpp + ConnectionMachPort.cpp + ConnectionSharedMemory.cpp + ConstString.cpp + CxaDemangle.cpp + DataBufferHeap.cpp + DataBufferMemoryMap.cpp + DataEncoder.cpp + DataExtractor.cpp + Debugger.cpp + Disassembler.cpp + DynamicLoader.cpp + EmulateInstruction.cpp + Error.cpp + Event.cpp + FastDemangle.cpp + FileLineResolver.cpp + FileSpecList.cpp + FormatEntity.cpp + History.cpp + IOHandler.cpp + Listener.cpp + Log.cpp + Logging.cpp + Mangled.cpp + Module.cpp + ModuleChild.cpp + ModuleList.cpp + Opcode.cpp + PluginManager.cpp + RegisterValue.cpp + RegularExpression.cpp + Scalar.cpp + SearchFilter.cpp + Section.cpp + SourceManager.cpp + State.cpp + Stream.cpp + StreamAsynchronousIO.cpp + StreamCallback.cpp + StreamFile.cpp + StreamGDBRemote.cpp + StreamString.cpp + StringList.cpp + StructuredData.cpp + Timer.cpp + UserID.cpp + UserSettingsController.cpp + UUID.cpp + Value.cpp + ValueObject.cpp + ValueObjectCast.cpp + ValueObjectChild.cpp + ValueObjectConstResult.cpp + ValueObjectConstResultCast.cpp + ValueObjectConstResultChild.cpp + ValueObjectConstResultImpl.cpp + ValueObjectDynamicValue.cpp + ValueObjectList.cpp + ValueObjectMemory.cpp + ValueObjectRegister.cpp + ValueObjectSyntheticFilter.cpp + ValueObjectVariable.cpp + VMRange.cpp + ) + diff --git a/source/Core/Makefile b/source/Core/Makefile new file mode 100644 index 000000000000..b7773e3f571d --- /dev/null +++ b/source/Core/Makefile @@ -0,0 +1,14 @@ +##===- source/Core/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 := lldbCore +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Core/StringList.cpp b/source/Core/StringList.cpp index 4e07ba4a4579..ce197ac7bd48 100644 --- a/source/Core/StringList.cpp +++ b/source/Core/StringList.cpp @@ -11,6 +11,8 @@ #include "lldb/Core/StreamString.h" #include "lldb/Host/FileSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" #include <string> @@ -305,12 +307,29 @@ StringList::operator << (const char* str) } StringList& +StringList::operator << (const std::string& str) +{ + AppendString(str); + return *this; +} + +StringList& StringList::operator << (StringList strings) { AppendList(strings); return *this; } +StringList& +StringList::operator = (const std::vector<std::string> &rhs) +{ + Clear(); + for (const auto &s : rhs) + m_strings.push_back(s); + + return *this; +} + size_t StringList::AutoComplete (const char *s, StringList &matches, size_t &exact_idx) const { @@ -339,3 +358,21 @@ StringList::AutoComplete (const char *s, StringList &matches, size_t &exact_idx) return matches.GetSize(); } +void +StringList::LogDump(Log *log, const char *name) +{ + if (!log) + return; + + StreamString strm; + if (name) + strm.Printf("Begin %s:\n", name); + for (const auto &s : m_strings) { + strm.Indent(); + strm.Printf("%s\n", s.c_str()); + } + if (name) + strm.Printf("End %s.\n", name); + + log->Debug("%s", strm.GetData()); +} diff --git a/source/DataFormatters/CMakeLists.txt b/source/DataFormatters/CMakeLists.txt new file mode 100644 index 000000000000..bfb5c8b9f68b --- /dev/null +++ b/source/DataFormatters/CMakeLists.txt @@ -0,0 +1,19 @@ +add_lldb_library(lldbDataFormatters + CXXFunctionPointer.cpp + DataVisualization.cpp + DumpValueObjectOptions.cpp + FormatCache.cpp + FormatClasses.cpp + FormatManager.cpp + FormattersHelpers.cpp + LanguageCategory.cpp + StringPrinter.cpp + TypeCategory.cpp + TypeCategoryMap.cpp + TypeFormat.cpp + TypeSummary.cpp + TypeSynthetic.cpp + TypeValidator.cpp + ValueObjectPrinter.cpp + VectorType.cpp + ) diff --git a/source/DataFormatters/Makefile b/source/DataFormatters/Makefile new file mode 100644 index 000000000000..4eb3249e5a58 --- /dev/null +++ b/source/DataFormatters/Makefile @@ -0,0 +1,14 @@ +##===- source/DataFormatters/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 := lldbDataFormatters +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Expression/CMakeLists.txt b/source/Expression/CMakeLists.txt new file mode 100644 index 000000000000..52392f13319a --- /dev/null +++ b/source/Expression/CMakeLists.txt @@ -0,0 +1,16 @@ +add_lldb_library(lldbExpression + DWARFExpression.cpp + Expression.cpp + ExpressionSourceCode.cpp + ExpressionVariable.cpp + FunctionCaller.cpp + IRDynamicChecks.cpp + IRExecutionUnit.cpp + IRInterpreter.cpp + IRMemoryMap.cpp + LLVMUserExpression.cpp + Materializer.cpp + REPL.cpp + UserExpression.cpp + UtilityFunction.cpp + ) diff --git a/source/Expression/Makefile b/source/Expression/Makefile new file mode 100644 index 000000000000..495f094d3900 --- /dev/null +++ b/source/Expression/Makefile @@ -0,0 +1,14 @@ +##===- source/Expression/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 := lldbExpression +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Host/CMakeLists.txt b/source/Host/CMakeLists.txt new file mode 100644 index 000000000000..776fcfb44ffa --- /dev/null +++ b/source/Host/CMakeLists.txt @@ -0,0 +1,190 @@ +macro(add_host_subdirectory group) + list(APPEND HOST_SOURCES ${ARGN}) + source_group(${group} FILES ${ARGN}) +endmacro() + +add_host_subdirectory(common + common/Condition.cpp + common/File.cpp + common/FileCache.cpp + common/FileSpec.cpp + common/FileSystem.cpp + common/GetOptInc.cpp + common/Host.cpp + common/HostInfoBase.cpp + common/HostNativeThreadBase.cpp + common/HostProcess.cpp + common/HostThread.cpp + common/IOObject.cpp + common/LockFileBase.cpp + common/Mutex.cpp + common/MonitoringProcessLauncher.cpp + common/NativeBreakpoint.cpp + common/NativeBreakpointList.cpp + common/NativeWatchpointList.cpp + common/NativeProcessProtocol.cpp + common/NativeRegisterContext.cpp + common/NativeRegisterContextRegisterInfo.cpp + common/NativeThreadProtocol.cpp + common/OptionParser.cpp + common/PipeBase.cpp + common/ProcessRunLock.cpp + common/Socket.cpp + common/SocketAddress.cpp + common/SoftwareBreakpoint.cpp + common/StringConvert.cpp + common/Symbols.cpp + common/TCPSocket.cpp + common/Terminal.cpp + common/ThisThread.cpp + common/ThreadLauncher.cpp + common/TimeValue.cpp + common/XML.cpp + common/UDPSocket.cpp + ) + +# Keep track of whether we want to provide a define for the +# Python's architecture-specific lib path (i.e. where a +# Python lldb module would go). +set (get_python_libdir 0) + +if (NOT LLDB_DISABLE_LIBEDIT) + add_host_subdirectory(common + common/Editline.cpp + ) +endif() + +add_host_subdirectory(posix + posix/ConnectionFileDescriptorPosix.cpp + ) + +if (CMAKE_SYSTEM_NAME MATCHES "Windows") + add_host_subdirectory(windows + windows/Condition.cpp + windows/ConnectionGenericFileWindows.cpp + windows/EditLineWin.cpp + windows/FileSystem.cpp + windows/Host.cpp + windows/HostInfoWindows.cpp + windows/HostProcessWindows.cpp + windows/HostThreadWindows.cpp + windows/LockFileWindows.cpp + windows/Mutex.cpp + windows/PipeWindows.cpp + windows/ProcessLauncherWindows.cpp + windows/ProcessRunLock.cpp + windows/ThisThread.cpp + windows/Windows.cpp + ) +else() + if (NOT LLDB_DISABLE_PYTHON) + # We'll grab the arch-specific python libdir on POSIX systems. + set (get_python_libdir 1) + endif() + + add_host_subdirectory(posix + posix/DomainSocket.cpp + posix/FileSystem.cpp + posix/HostInfoPosix.cpp + posix/HostProcessPosix.cpp + posix/HostThreadPosix.cpp + posix/LockFilePosix.cpp + posix/MainLoopPosix.cpp + posix/PipePosix.cpp + ) + + if (NOT __ANDROID_NDK__) + add_host_subdirectory(posix + posix/ProcessLauncherPosix.cpp + ) + endif() + + if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) + add_host_subdirectory(macosx + macosx/Host.mm + macosx/HostInfoMacOSX.mm + macosx/HostThreadMacOSX.mm + macosx/Symbols.cpp + macosx/ThisThread.cpp + macosx/cfcpp/CFCBundle.cpp + macosx/cfcpp/CFCData.cpp + macosx/cfcpp/CFCMutableArray.cpp + macosx/cfcpp/CFCMutableDictionary.cpp + macosx/cfcpp/CFCMutableSet.cpp + macosx/cfcpp/CFCString.cpp + ) + + elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") + if (__ANDROID_NDK__) + add_host_subdirectory(android + android/HostInfoAndroid.cpp + android/LibcGlue.cpp + android/ProcessLauncherAndroid.cpp + linux/AbstractSocket.cpp + linux/Host.cpp + linux/HostInfoLinux.cpp + linux/HostThreadLinux.cpp + linux/LibcGlue.cpp + linux/ThisThread.cpp + ) + else() + add_host_subdirectory(linux + linux/AbstractSocket.cpp + linux/Host.cpp + linux/HostInfoLinux.cpp + linux/HostThreadLinux.cpp + linux/LibcGlue.cpp + linux/ThisThread.cpp + ) + endif() + + elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + add_host_subdirectory(freebsd + freebsd/Host.cpp + freebsd/HostInfoFreeBSD.cpp + freebsd/HostThreadFreeBSD.cpp + freebsd/ThisThread.cpp + ) + + elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + add_host_subdirectory(netbsd + netbsd/Host.cpp + netbsd/HostInfoNetBSD.cpp + netbsd/HostThreadNetBSD.cpp + netbsd/ThisThread.cpp + ) + endif() +endif() + +if (${get_python_libdir}) + # Call a python script to gather the arch-specific libdir for + # modules like the lldb module. + execute_process( + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/get_relative_lib_dir.py + RESULT_VARIABLE get_libdir_status + OUTPUT_VARIABLE relative_libdir + ) + if (get_libdir_status EQUAL 0) + add_definitions(-DLLDB_PYTHON_RELATIVE_LIBDIR="${relative_libdir}") + endif() +endif() + +if (${get_python_libdir}) + # Call a python script to gather the arch-specific libdir for + # modules like the lldb module. + execute_process( + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/get_relative_lib_dir.py + RESULT_VARIABLE get_libdir_status + OUTPUT_VARIABLE relative_libdir + ) + if (get_libdir_status EQUAL 0) + add_definitions(-DLLDB_PYTHON_RELATIVE_LIBDIR="${relative_libdir}") + endif() +endif() + +add_lldb_library(lldbHost ${HOST_SOURCES}) + +if (CMAKE_SYSTEM_NAME MATCHES "NetBSD") +target_link_libraries(lldbHost kvm) +endif () diff --git a/source/Host/Makefile b/source/Host/Makefile new file mode 100644 index 000000000000..da90c8c364a3 --- /dev/null +++ b/source/Host/Makefile @@ -0,0 +1,65 @@ +##===- source/Host/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 := ../.. +LEVEL := $(LLDB_LEVEL)/../.. + +include $(LEVEL)/Makefile.config + +define DIR_SOURCES +SOURCES += $$(addprefix $(1)/,$$(notdir $$(wildcard $$(PROJ_SRC_DIR)/$(1)/*.cpp \ + $$(PROJ_SRC_DIR)/*.cc $$(PROJ_SRC_DIR)/$(1)/*.c $$(PROJ_SRC_DIR)/$(1)/*.mm))) +endef + +$(eval $(call DIR_SOURCES,common)) + +ifeq ($(HOST_OS),Darwin) +$(eval $(call DIR_SOURCES,posix)) +$(eval $(call DIR_SOURCES,macosx)) +CFCPP_SOURCES = \ + $(addprefix macosx/cfcpp/,$(notdir $(wildcard $(PROJ_SRC_DIR)/macosx/cfcpp/*.cpp))) +SOURCES += $(CFCPP_SOURCES) + +CFCPP_BaseNameSources := $(sort $(basename $(CFCPP_SOURCES))) +CFCPP_OBJECTS := $(CFCPP_BaseNameSources:%=$(ObjDir)/%.o) + +# Make sure the cfcpp output directory exists +$(CFCPP_OBJECTS): $(ObjDir)/cfcpp/.dir +endif + +ifeq ($(HOST_OS),Linux) +$(eval $(call DIR_SOURCES,posix)) +$(eval $(call DIR_SOURCES,linux)) +endif + +ifneq (,$(filter $(HOST_OS), FreeBSD GNU/kFreeBSD)) +$(eval $(call DIR_SOURCES,posix)) +$(eval $(call DIR_SOURCES,freebsd)) +endif + +ifeq ($(HOST_OS),NetBSD) +$(eval $(call DIR_SOURCES,posix)) +$(eval $(call DIR_SOURCES,netbsd)) +endif + +ifeq ($(HOST_OS),MingW) +$(eval $(call DIR_SOURCES,windows)) +SOURCES += posix/ConnectionFileDescriptorPosix.cpp +endif + +ifeq ($(HOST_OS),Android) +$(eval $(call DIR_SOURCES,posix)) +$(eval $(call DIR_SOURCES,linux)) +$(eval $(call DIR_SOURCES,android)) +endif + +LIBRARYNAME := lldbHost +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Host/android/HostInfoAndroid.cpp b/source/Host/android/HostInfoAndroid.cpp new file mode 100644 index 000000000000..3fa50ec8ddfd --- /dev/null +++ b/source/Host/android/HostInfoAndroid.cpp @@ -0,0 +1,104 @@ +//===-- HostInfoAndroid.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/android/HostInfoAndroid.h" +#include "lldb/Host/linux/HostInfoLinux.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +using namespace lldb_private; +using namespace llvm; + +void +HostInfoAndroid::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) +{ + HostInfoLinux::ComputeHostArchitectureSupport(arch_32, arch_64); + + if (arch_32.IsValid()) + { + arch_32.GetTriple().setEnvironment(llvm::Triple::Android); + } + if (arch_64.IsValid()) + { + arch_64.GetTriple().setEnvironment(llvm::Triple::Android); + } +} + +FileSpec +HostInfoAndroid::GetDefaultShell() +{ + return FileSpec("/system/bin/sh", false); +} + +FileSpec +HostInfoAndroid::ResolveLibraryPath(const std::string& module_path, const ArchSpec& arch) +{ + static const char* const ld_library_path_separator = ":"; + static const char* const default_lib32_path[] = { + "/vendor/lib", + "/system/lib", + nullptr + }; + static const char* const default_lib64_path[] = { + "/vendor/lib64", + "/system/lib64", + nullptr + }; + + if (module_path.empty() || module_path[0] == '/') + return FileSpec(module_path.c_str(), true); + + SmallVector<StringRef, 4> ld_paths; + + if (const char* ld_library_path = ::getenv("LD_LIBRARY_PATH")) + StringRef(ld_library_path).split(ld_paths, StringRef(ld_library_path_separator), -1, false); + + const char* const* default_lib_path = nullptr; + switch (arch.GetAddressByteSize()) + { + case 4: + default_lib_path = default_lib32_path; + break; + case 8: + default_lib_path = default_lib64_path; + break; + default: + assert(false && "Unknown address byte size"); + return FileSpec(); + } + + for(const char* const* it = default_lib_path; *it; ++it) + ld_paths.push_back(StringRef(*it)); + + for (const StringRef& path : ld_paths) + { + FileSpec file_candidate(path.str().c_str(), true); + file_candidate.AppendPathComponent(module_path.c_str()); + + if (file_candidate.Exists()) + return file_candidate; + } + + return FileSpec(); +} + +bool +HostInfoAndroid::ComputeTempFileBaseDirectory(FileSpec &file_spec) +{ + bool success = HostInfoLinux::ComputeTempFileBaseDirectory(file_spec); + + // On Android, there is no path which is guaranteed to be writable. If the user has not + // provided a path via an environment variable, the generic algorithm will deduce /tmp, which + // is plain wrong. In that case we have an invalid directory, we substitute the path with + // /data/local/tmp, which is correct at least in some cases (i.e., when running as shell user). + if (!success || !file_spec.Exists()) + file_spec = FileSpec("/data/local/tmp", false); + + return file_spec.Exists(); +} diff --git a/source/Host/android/LibcGlue.cpp b/source/Host/android/LibcGlue.cpp new file mode 100644 index 000000000000..3842fb6c2a8e --- /dev/null +++ b/source/Host/android/LibcGlue.cpp @@ -0,0 +1,40 @@ +//===-- LibcGlue.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This files adds functions missing from libc on earlier versions of Android + +#include <android/api-level.h> + +#include <sys/syscall.h> + +#if __ANDROID_API__ < 21 + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <signal.h> + +#include "lldb/Host/Time.h" + +time_t timegm(struct tm* t) +{ + return (time_t) timegm64(t); +} + +int signalfd (int fd, const sigset_t *mask, int flags) +{ + return syscall(__NR_signalfd4, fd, mask, _NSIG / 8, flags); +} + +int posix_openpt(int flags) +{ + return open("/dev/ptmx", flags); +} + +#endif diff --git a/source/Host/android/ProcessLauncherAndroid.cpp b/source/Host/android/ProcessLauncherAndroid.cpp new file mode 100644 index 000000000000..24eebc8c030f --- /dev/null +++ b/source/Host/android/ProcessLauncherAndroid.cpp @@ -0,0 +1,108 @@ +//===-- ProcessLauncherAndroid.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/android/ProcessLauncherAndroid.h" + +#include "lldb/Target/ProcessLaunchInfo.h" + +#include <limits.h> + +using namespace lldb; +using namespace lldb_private; + +static bool +DupDescriptor(const FileSpec &file_spec, int fd, int flags) +{ + int target_fd = ::open(file_spec.GetCString(), flags, 0666); + + if (target_fd == -1) + return false; + + if (::dup2(target_fd, fd) == -1) + return false; + + return (::close(target_fd) == -1) ? false : true; +} + +// If there is no PATH variable specified inside the environment then set the path to /system/bin. +// It is required because the default path used by execve() is wrong on android. +static void +FixupEnvironment(Args& env) +{ + static const char* path = "PATH="; + static const int path_len = ::strlen(path); + for (const char** args = env.GetConstArgumentVector(); *args; ++args) + if (::strncmp(path, *args, path_len) == 0) + return; + env.AppendArgument("PATH=/system/bin"); +} + +HostProcess +ProcessLauncherAndroid::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) +{ + // TODO: Handle other launch parameters specified in launc_info + + char exe_path[PATH_MAX]; + launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); + + lldb::pid_t pid = ::fork(); + if (pid == static_cast<lldb::pid_t>(-1)) + { + // Fork failed + error.SetErrorStringWithFormat("Fork failed with error message: %s", strerror(errno)); + return HostProcess(LLDB_INVALID_PROCESS_ID); + } + else if (pid == 0) + { + if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO)) { + FileSpec file_spec = file_action->GetFileSpec(); + if (file_spec) + if (!DupDescriptor(file_spec, STDIN_FILENO, O_RDONLY)) + exit(-1); + } + + if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDOUT_FILENO)) { + FileSpec file_spec = file_action->GetFileSpec(); + if (file_spec) + if (!DupDescriptor(file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) + exit(-1); + } + + if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDERR_FILENO)) { + FileSpec file_spec = file_action->GetFileSpec(); + if (file_spec) + if (!DupDescriptor(file_spec, STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) + exit(-1); + } + + // Child process + const char **argv = launch_info.GetArguments().GetConstArgumentVector(); + + Args env = launch_info.GetEnvironmentEntries(); + FixupEnvironment(env); + const char **envp = env.GetConstArgumentVector(); + + FileSpec working_dir = launch_info.GetWorkingDirectory(); + if (working_dir) + { + if (::chdir(working_dir.GetCString()) != 0) + exit(-1); + } + + execve(argv[0], + const_cast<char *const *>(argv), + const_cast<char *const *>(envp)); + exit(-1); + } + + return HostProcess(pid); +} diff --git a/source/Host/linux/AbstractSocket.cpp b/source/Host/linux/AbstractSocket.cpp new file mode 100644 index 000000000000..8ac0107123bb --- /dev/null +++ b/source/Host/linux/AbstractSocket.cpp @@ -0,0 +1,31 @@ +//===-- AbstractSocket.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/linux/AbstractSocket.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +AbstractSocket::AbstractSocket(bool child_processes_inherit, Error &error) + : DomainSocket(ProtocolUnixAbstract, child_processes_inherit, error) +{ +} + +size_t +AbstractSocket::GetNameOffset() const +{ + return 1; +} + +void +AbstractSocket::DeleteSocketFile(llvm::StringRef name) +{ +} diff --git a/source/Host/linux/Host.cpp b/source/Host/linux/Host.cpp new file mode 100644 index 000000000000..cb7369fe7aec --- /dev/null +++ b/source/Host/linux/Host.cpp @@ -0,0 +1,393 @@ +//===-- source/Host/linux/Host.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <stdio.h> +#include <sys/utsname.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" + +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#ifdef __ANDROID_NDK__ +#include "lldb/Host/android/Android.h" +#endif +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" + +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Symbol/ObjectFile.h" +#include "Plugins/Process/Linux/ProcFileReader.h" + +using namespace lldb; +using namespace lldb_private; + +typedef enum ProcessStateFlags +{ + eProcessStateRunning = (1u << 0), // Running + eProcessStateSleeping = (1u << 1), // Sleeping in an interruptible wait + eProcessStateWaiting = (1u << 2), // Waiting in an uninterruptible disk sleep + eProcessStateZombie = (1u << 3), // Zombie + eProcessStateTracedOrStopped = (1u << 4), // Traced or stopped (on a signal) + eProcessStatePaging = (1u << 5) // Paging +} ProcessStateFlags; + +typedef struct ProcessStatInfo +{ + lldb::pid_t ppid; // Parent Process ID + uint32_t fProcessState; // ProcessStateFlags +} ProcessStatInfo; + +// Get the process info with additional information from /proc/$PID/stat (like process state, and tracer pid). +static bool GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, ProcessStatInfo &stat_info, lldb::pid_t &tracerpid); + +static bool +ReadProcPseudoFileStat (lldb::pid_t pid, ProcessStatInfo& stat_info) +{ + // Read the /proc/$PID/stat file. + lldb::DataBufferSP buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer (pid, "stat"); + + // The filename of the executable is stored in parenthesis right after the pid. We look for the closing + // parenthesis for the filename and work from there in case the name has something funky like ')' in it. + const char *filename_end = strrchr ((const char *)buf_sp->GetBytes(), ')'); + if (filename_end) + { + char state = '\0'; + int ppid = LLDB_INVALID_PROCESS_ID; + + // Read state and ppid. + sscanf (filename_end + 1, " %c %d", &state, &ppid); + + stat_info.ppid = ppid; + + switch (state) + { + case 'R': + stat_info.fProcessState |= eProcessStateRunning; + break; + case 'S': + stat_info.fProcessState |= eProcessStateSleeping; + break; + case 'D': + stat_info.fProcessState |= eProcessStateWaiting; + break; + case 'Z': + stat_info.fProcessState |= eProcessStateZombie; + break; + case 'T': + stat_info.fProcessState |= eProcessStateTracedOrStopped; + break; + case 'W': + stat_info.fProcessState |= eProcessStatePaging; + break; + } + + return true; + } + + return false; +} + +static void +GetLinuxProcessUserAndGroup (lldb::pid_t pid, ProcessInstanceInfo &process_info, lldb::pid_t &tracerpid) +{ + tracerpid = 0; + uint32_t rUid = UINT32_MAX; // Real User ID + uint32_t eUid = UINT32_MAX; // Effective User ID + uint32_t rGid = UINT32_MAX; // Real Group ID + uint32_t eGid = UINT32_MAX; // Effective Group ID + + // Read the /proc/$PID/status file and parse the Uid:, Gid:, and TracerPid: fields. + lldb::DataBufferSP buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer (pid, "status"); + + static const char uid_token[] = "Uid:"; + char *buf_uid = strstr ((char *)buf_sp->GetBytes(), uid_token); + if (buf_uid) + { + // Real, effective, saved set, and file system UIDs. Read the first two. + buf_uid += sizeof(uid_token); + rUid = strtol (buf_uid, &buf_uid, 10); + eUid = strtol (buf_uid, &buf_uid, 10); + } + + static const char gid_token[] = "Gid:"; + char *buf_gid = strstr ((char *)buf_sp->GetBytes(), gid_token); + if (buf_gid) + { + // Real, effective, saved set, and file system GIDs. Read the first two. + buf_gid += sizeof(gid_token); + rGid = strtol (buf_gid, &buf_gid, 10); + eGid = strtol (buf_gid, &buf_gid, 10); + } + + static const char tracerpid_token[] = "TracerPid:"; + char *buf_tracerpid = strstr((char *)buf_sp->GetBytes(), tracerpid_token); + if (buf_tracerpid) + { + // Tracer PID. 0 if we're not being debugged. + buf_tracerpid += sizeof(tracerpid_token); + tracerpid = strtol (buf_tracerpid, &buf_tracerpid, 10); + } + + process_info.SetUserID (rUid); + process_info.SetEffectiveUserID (eUid); + process_info.SetGroupID (rGid); + process_info.SetEffectiveGroupID (eGid); +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ + return process_linux::ProcFileReader::ReadIntoDataBuffer (process->GetID(), "auxv"); +} + +lldb::DataBufferSP +Host::GetAuxvData (lldb::pid_t pid) +{ + return process_linux::ProcFileReader::ReadIntoDataBuffer (pid, "auxv"); +} + +static bool +IsDirNumeric(const char *dname) +{ + for (; *dname; dname++) + { + if (!isdigit (*dname)) + return false; + } + return true; +} + +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + static const char procdir[] = "/proc/"; + + DIR *dirproc = opendir (procdir); + if (dirproc) + { + struct dirent *direntry = NULL; + const uid_t our_uid = getuid(); + const lldb::pid_t our_pid = getpid(); + bool all_users = match_info.GetMatchAllUsers(); + + while ((direntry = readdir (dirproc)) != NULL) + { + if (direntry->d_type != DT_DIR || !IsDirNumeric (direntry->d_name)) + continue; + + lldb::pid_t pid = atoi (direntry->d_name); + + // Skip this process. + if (pid == our_pid) + continue; + + lldb::pid_t tracerpid; + ProcessStatInfo stat_info; + ProcessInstanceInfo process_info; + + if (!GetProcessAndStatInfo (pid, process_info, stat_info, tracerpid)) + continue; + + // Skip if process is being debugged. + if (tracerpid != 0) + continue; + + // Skip zombies. + if (stat_info.fProcessState & eProcessStateZombie) + continue; + + // Check for user match if we're not matching all users and not running as root. + if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid)) + continue; + + if (match_info.Matches (process_info)) + { + process_infos.Append (process_info); + } + } + + closedir (dirproc); + } + + return process_infos.GetSize(); +} + +bool +Host::FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach) +{ + bool tids_changed = false; + static const char procdir[] = "/proc/"; + static const char taskdir[] = "/task/"; + std::string process_task_dir = procdir + std::to_string(pid) + taskdir; + DIR *dirproc = opendir (process_task_dir.c_str()); + + if (dirproc) + { + struct dirent *direntry = NULL; + while ((direntry = readdir (dirproc)) != NULL) + { + if (direntry->d_type != DT_DIR || !IsDirNumeric (direntry->d_name)) + continue; + + lldb::tid_t tid = atoi(direntry->d_name); + TidMap::iterator it = tids_to_attach.find(tid); + if (it == tids_to_attach.end()) + { + tids_to_attach.insert(TidPair(tid, false)); + tids_changed = true; + } + } + closedir (dirproc); + } + + return tids_changed; +} + +static bool +GetELFProcessCPUType (const char *exe_path, ProcessInstanceInfo &process_info) +{ + // Clear the architecture. + process_info.GetArchitecture().Clear(); + + ModuleSpecList specs; + FileSpec filespec (exe_path, false); + const size_t num_specs = ObjectFile::GetModuleSpecifications (filespec, 0, 0, specs); + // GetModuleSpecifications() could fail if the executable has been deleted or is locked. + // But it shouldn't return more than 1 architecture. + assert(num_specs <= 1 && "Linux plugin supports only a single architecture"); + if (num_specs == 1) + { + ModuleSpec module_spec; + if (specs.GetModuleSpecAtIndex (0, module_spec) && module_spec.GetArchitecture().IsValid()) + { + process_info.GetArchitecture () = module_spec.GetArchitecture(); + return true; + } + } + return false; +} + +static bool +GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, ProcessStatInfo &stat_info, lldb::pid_t &tracerpid) +{ + tracerpid = 0; + process_info.Clear(); + ::memset (&stat_info, 0, sizeof(stat_info)); + stat_info.ppid = LLDB_INVALID_PROCESS_ID; + + // Use special code here because proc/[pid]/exe is a symbolic link. + char link_path[PATH_MAX]; + char exe_path[PATH_MAX] = ""; + if (snprintf (link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", pid) <= 0) + return false; + + ssize_t len = readlink (link_path, exe_path, sizeof(exe_path) - 1); + if (len <= 0) + return false; + + // readlink does not append a null byte. + exe_path[len] = 0; + + // If the binary has been deleted, the link name has " (deleted)" appended. + // Remove if there. + static const ssize_t deleted_len = strlen(" (deleted)"); + if (len > deleted_len && + !strcmp(exe_path + len - deleted_len, " (deleted)")) + { + exe_path[len - deleted_len] = 0; + } + else + { + GetELFProcessCPUType (exe_path, process_info); + } + + process_info.SetProcessID(pid); + process_info.GetExecutableFile().SetFile(exe_path, false); + process_info.GetArchitecture().MergeFrom(HostInfo::GetArchitecture()); + + lldb::DataBufferSP buf_sp; + + // Get the process environment. + buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer(pid, "environ"); + Args &info_env = process_info.GetEnvironmentEntries(); + char *next_var = (char *)buf_sp->GetBytes(); + char *end_buf = next_var + buf_sp->GetByteSize(); + while (next_var < end_buf && 0 != *next_var) + { + info_env.AppendArgument(next_var); + next_var += strlen(next_var) + 1; + } + + // Get the command line used to start the process. + buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer(pid, "cmdline"); + + // Grab Arg0 first, if there is one. + char *cmd = (char *)buf_sp->GetBytes(); + if (cmd) + { + process_info.SetArg0(cmd); + + // Now process any remaining arguments. + Args &info_args = process_info.GetArguments(); + char *next_arg = cmd + strlen(cmd) + 1; + end_buf = cmd + buf_sp->GetByteSize(); + while (next_arg < end_buf && 0 != *next_arg) + { + info_args.AppendArgument(next_arg); + next_arg += strlen(next_arg) + 1; + } + } + + // Read /proc/$PID/stat to get our parent pid. + if (ReadProcPseudoFileStat (pid, stat_info)) + { + process_info.SetParentProcessID (stat_info.ppid); + } + + // Get User and Group IDs and get tracer pid. + GetLinuxProcessUserAndGroup (pid, process_info, tracerpid); + + return true; +} + +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + lldb::pid_t tracerpid; + ProcessStatInfo stat_info; + + return GetProcessAndStatInfo (pid, process_info, stat_info, tracerpid); +} + +size_t +Host::GetEnvironment (StringList &env) +{ + char **host_env = environ; + char *env_entry; + size_t i; + for (i=0; (env_entry = host_env[i]) != NULL; ++i) + env.AppendString(env_entry); + return i; +} + +Error +Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) +{ + return Error("unimplemented"); +} diff --git a/source/Host/linux/HostInfoLinux.cpp b/source/Host/linux/HostInfoLinux.cpp new file mode 100644 index 000000000000..4732a2a571b6 --- /dev/null +++ b/source/Host/linux/HostInfoLinux.cpp @@ -0,0 +1,282 @@ +//===-- HostInfoLinux.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Host/linux/HostInfoLinux.h" + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <sys/utsname.h> + +#include <algorithm> +#include <mutex> // std::once + +using namespace lldb_private; + +namespace +{ +struct HostInfoLinuxFields +{ + HostInfoLinuxFields() + : m_os_major(0) + , m_os_minor(0) + , m_os_update(0) + { + } + + std::string m_distribution_id; + uint32_t m_os_major; + uint32_t m_os_minor; + uint32_t m_os_update; +}; + +HostInfoLinuxFields *g_fields = nullptr; +} + +void +HostInfoLinux::Initialize() +{ + HostInfoPosix::Initialize(); + + g_fields = new HostInfoLinuxFields(); +} + +uint32_t +HostInfoLinux::GetMaxThreadNameLength() +{ + return 16; +} + +bool +HostInfoLinux::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) +{ + static bool success = false; + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + + struct utsname un; + if (uname(&un) == 0) + { + int status = sscanf(un.release, "%u.%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor, &g_fields->m_os_update); + if (status == 3) + success = true; + else + { + // Some kernels omit the update version, so try looking for just "X.Y" and + // set update to 0. + g_fields->m_os_update = 0; + status = sscanf(un.release, "%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor); + if (status == 2) + success = true; + } + } + }); + + + major = g_fields->m_os_major; + minor = g_fields->m_os_minor; + update = g_fields->m_os_update; + return success; +} + +bool +HostInfoLinux::GetOSBuildString(std::string &s) +{ + struct utsname un; + ::memset(&un, 0, sizeof(utsname)); + s.clear(); + + if (uname(&un) < 0) + return false; + + s.assign(un.release); + return true; +} + +bool +HostInfoLinux::GetOSKernelDescription(std::string &s) +{ + struct utsname un; + + ::memset(&un, 0, sizeof(utsname)); + s.clear(); + + if (uname(&un) < 0) + return false; + + s.assign(un.version); + return true; +} + +llvm::StringRef +HostInfoLinux::GetDistributionId() +{ + // Try to run 'lbs_release -i', and use that response + // for the distribution id. + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST)); + if (log) + log->Printf("attempting to determine Linux distribution..."); + + // check if the lsb_release command exists at one of the + // following paths + const char *const exe_paths[] = {"/bin/lsb_release", "/usr/bin/lsb_release"}; + + for (size_t exe_index = 0; exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) + { + const char *const get_distribution_info_exe = exe_paths[exe_index]; + if (access(get_distribution_info_exe, F_OK)) + { + // this exe doesn't exist, move on to next exe + if (log) + log->Printf("executable doesn't exist: %s", get_distribution_info_exe); + continue; + } + + // execute the distribution-retrieval command, read output + std::string get_distribution_id_command(get_distribution_info_exe); + get_distribution_id_command += " -i"; + + FILE *file = popen(get_distribution_id_command.c_str(), "r"); + if (!file) + { + if (log) + log->Printf("failed to run command: \"%s\", cannot retrieve " + "platform information", + get_distribution_id_command.c_str()); + break; + } + + // retrieve the distribution id string. + char distribution_id[256] = {'\0'}; + if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != NULL) + { + if (log) + log->Printf("distribution id command returned \"%s\"", distribution_id); + + const char *const distributor_id_key = "Distributor ID:\t"; + if (strstr(distribution_id, distributor_id_key)) + { + // strip newlines + std::string id_string(distribution_id + strlen(distributor_id_key)); + id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), id_string.end()); + + // lower case it and convert whitespace to underscores + std::transform(id_string.begin(), id_string.end(), id_string.begin(), [](char ch) + { + return tolower(isspace(ch) ? '_' : ch); + }); + + g_fields->m_distribution_id = id_string; + if (log) + log->Printf("distribution id set to \"%s\"", g_fields->m_distribution_id.c_str()); + } + else + { + if (log) + log->Printf("failed to find \"%s\" field in \"%s\"", distributor_id_key, distribution_id); + } + } + else + { + if (log) + log->Printf("failed to retrieve distribution id, \"%s\" returned no" + " lines", + get_distribution_id_command.c_str()); + } + + // clean up the file + pclose(file); + } + }); + + return g_fields->m_distribution_id.c_str(); +} + +FileSpec +HostInfoLinux::GetProgramFileSpec() +{ + static FileSpec g_program_filespec; + + if (!g_program_filespec) + { + char exe_path[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); + if (len > 0) + { + exe_path[len] = 0; + g_program_filespec.SetFile(exe_path, false); + } + } + + return g_program_filespec; +} + +bool +HostInfoLinux::ComputeSupportExeDirectory(FileSpec &file_spec) +{ + if (HostInfoPosix::ComputeSupportExeDirectory(file_spec) && + file_spec.IsAbsolute() && + file_spec.Exists()) + return true; + file_spec.GetDirectory() = GetProgramFileSpec().GetDirectory(); + return !file_spec.GetDirectory().IsEmpty(); +} + +bool +HostInfoLinux::ComputeSystemPluginsDirectory(FileSpec &file_spec) +{ + FileSpec temp_file("/usr/lib/lldb", true); + file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); + return true; +} + +bool +HostInfoLinux::ComputeUserPluginsDirectory(FileSpec &file_spec) +{ + // XDG Base Directory Specification + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. + const char *xdg_data_home = getenv("XDG_DATA_HOME"); + if (xdg_data_home && xdg_data_home[0]) + { + std::string user_plugin_dir(xdg_data_home); + user_plugin_dir += "/lldb"; + file_spec.GetDirectory().SetCString(user_plugin_dir.c_str()); + } + else + file_spec.GetDirectory().SetCString("~/.local/share/lldb"); + return true; +} + +void +HostInfoLinux::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) +{ + HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); + + const char *distribution_id = GetDistributionId().data(); + + // On Linux, "unknown" in the vendor slot isn't what we want for the default + // triple. It's probably an artifact of config.guess. + if (arch_32.IsValid()) + { + arch_32.SetDistributionId(distribution_id); + if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) + arch_32.GetTriple().setVendorName(llvm::StringRef()); + } + if (arch_64.IsValid()) + { + arch_64.SetDistributionId(distribution_id); + if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) + arch_64.GetTriple().setVendorName(llvm::StringRef()); + } +} diff --git a/source/Host/linux/HostThreadLinux.cpp b/source/Host/linux/HostThreadLinux.cpp new file mode 100644 index 000000000000..2312ced0107b --- /dev/null +++ b/source/Host/linux/HostThreadLinux.cpp @@ -0,0 +1,52 @@ +//===-- HostThreadLinux.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataBuffer.h" +#include "lldb/Host/linux/HostThreadLinux.h" +#include "Plugins/Process/Linux/ProcFileReader.h" + +#include "llvm/ADT/SmallVector.h" + +#include <pthread.h> + +using namespace lldb_private; + +HostThreadLinux::HostThreadLinux() + : HostThreadPosix() +{ +} + +HostThreadLinux::HostThreadLinux(lldb::thread_t thread) + : HostThreadPosix(thread) +{ +} + +void +HostThreadLinux::SetName(lldb::thread_t thread, llvm::StringRef name) +{ +#if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || defined(__ANDROID__) + ::pthread_setname_np(thread, name.data()); +#else + (void) thread; + (void) name; +#endif +} + +void +HostThreadLinux::GetName(lldb::thread_t thread, llvm::SmallVectorImpl<char> &name) +{ + // Read /proc/$TID/comm file. + lldb::DataBufferSP buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer(thread, "comm"); + const char *comm_str = (const char *)buf_sp->GetBytes(); + const char *cr_str = ::strchr(comm_str, '\n'); + size_t length = cr_str ? (cr_str - comm_str) : strlen(comm_str); + + name.clear(); + name.append(comm_str, comm_str + length); +} diff --git a/source/Host/linux/LibcGlue.cpp b/source/Host/linux/LibcGlue.cpp new file mode 100644 index 000000000000..63d026f76c62 --- /dev/null +++ b/source/Host/linux/LibcGlue.cpp @@ -0,0 +1,30 @@ +//===-- LibcGlue.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This file adds functions missing from libc on older versions of linux + +#include <unistd.h> +#include <sys/syscall.h> +#include <lldb/Host/linux/Uio.h> +#include <cerrno> + +#ifndef HAVE_PROCESS_VM_READV // If the syscall wrapper is not available, provide one. +ssize_t process_vm_readv(::pid_t pid, + const struct iovec *local_iov, unsigned long liovcnt, + const struct iovec *remote_iov, unsigned long riovcnt, + unsigned long flags) +{ +#ifdef HAVE_NR_PROCESS_VM_READV // If we have the syscall number, we can issue the syscall ourselves. + return syscall(__NR_process_vm_readv, pid, local_iov, liovcnt, remote_iov, riovcnt, flags); +#else // If not, let's pretend the syscall is not present. + errno = ENOSYS; + return -1; +#endif +} +#endif diff --git a/source/Host/linux/ThisThread.cpp b/source/Host/linux/ThisThread.cpp new file mode 100644 index 000000000000..1c68c8ba16d7 --- /dev/null +++ b/source/Host/linux/ThisThread.cpp @@ -0,0 +1,29 @@ +//===-- ThisThread.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Host/ThisThread.h" + +#include "llvm/ADT/SmallVector.h" + +#include <pthread.h> + +using namespace lldb_private; + +void +ThisThread::SetName(llvm::StringRef name) +{ + HostNativeThread::SetName(::pthread_self(), name); +} + +void +ThisThread::GetName(llvm::SmallVectorImpl<char> &name) +{ + HostNativeThread::GetName(::pthread_self(), name); +} diff --git a/source/Host/macosx/Host.mm b/source/Host/macosx/Host.mm new file mode 100644 index 000000000000..2db27a276e4a --- /dev/null +++ b/source/Host/macosx/Host.mm @@ -0,0 +1,1587 @@ +//===-- Host.mm -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Host.h" + +#include <AvailabilityMacros.h> + +#if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 +#define NO_XPC_SERVICES 1 +#endif + +#if !defined(NO_XPC_SERVICES) +#define __XPC_PRIVATE_H__ +#include <xpc/xpc.h> + +#define LaunchUsingXPCRightName "com.apple.dt.Xcode.RootDebuggingXPCService" + +// These XPC messaging keys are used for communication between Host.mm and the XPC service. +#define LauncherXPCServiceAuthKey "auth-key" +#define LauncherXPCServiceArgPrefxKey "arg" +#define LauncherXPCServiceEnvPrefxKey "env" +#define LauncherXPCServiceCPUTypeKey "cpuType" +#define LauncherXPCServicePosixspawnFlagsKey "posixspawnFlags" +#define LauncherXPCServiceStdInPathKeyKey "stdInPath" +#define LauncherXPCServiceStdOutPathKeyKey "stdOutPath" +#define LauncherXPCServiceStdErrPathKeyKey "stdErrPath" +#define LauncherXPCServiceChildPIDKey "childPID" +#define LauncherXPCServiceErrorTypeKey "errorType" +#define LauncherXPCServiceCodeTypeKey "errorCode" + +#endif + +#include "llvm/Support/Host.h" + +#include <asl.h> +#include <crt_externs.h> +#include <grp.h> +#include <libproc.h> +#include <pwd.h> +#include <spawn.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/proc.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <unistd.h> + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/StructuredData.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/CleanUp.h" +#include "lldb/Utility/NameMatches.h" + +#include "cfcpp/CFCBundle.h" +#include "cfcpp/CFCMutableArray.h" +#include "cfcpp/CFCMutableDictionary.h" +#include "cfcpp/CFCReleaser.h" +#include "cfcpp/CFCString.h" + + +#include <objc/objc-auto.h> + +#include <CoreFoundation/CoreFoundation.h> +#include <Foundation/Foundation.h> + +#ifndef _POSIX_SPAWN_DISABLE_ASLR +#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 +#endif + +extern "C" +{ + int __pthread_chdir(const char *path); + int __pthread_fchdir (int fildes); +} + +using namespace lldb; +using namespace lldb_private; + +bool +Host::GetBundleDirectory (const FileSpec &file, FileSpec &bundle_directory) +{ +#if defined (__APPLE__) + if (file.GetFileType () == FileSpec::eFileTypeDirectory) + { + char path[PATH_MAX]; + if (file.GetPath(path, sizeof(path))) + { + CFCBundle bundle (path); + if (bundle.GetPath (path, sizeof(path))) + { + bundle_directory.SetFile (path, false); + return true; + } + } + } +#endif + bundle_directory.Clear(); + return false; +} + + +bool +Host::ResolveExecutableInBundle (FileSpec &file) +{ +#if defined (__APPLE__) + if (file.GetFileType () == FileSpec::eFileTypeDirectory) + { + char path[PATH_MAX]; + if (file.GetPath(path, sizeof(path))) + { + CFCBundle bundle (path); + CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL ()); + if (url.get()) + { + if (::CFURLGetFileSystemRepresentation (url.get(), YES, (UInt8*)path, sizeof(path))) + { + file.SetFile(path, false); + return true; + } + } + } + } +#endif + return false; +} + +static void * +AcceptPIDFromInferior (void *arg) +{ + const char *connect_url = (const char *)arg; + ConnectionFileDescriptor file_conn; + Error error; + if (file_conn.Connect (connect_url, &error) == eConnectionStatusSuccess) + { + char pid_str[256]; + ::memset (pid_str, 0, sizeof(pid_str)); + ConnectionStatus status; + const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), 0, status, NULL); + if (pid_str_len > 0) + { + int pid = atoi (pid_str); + return (void *)(intptr_t)pid; + } + } + return NULL; +} + +static bool +WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) +{ + const int time_delta_usecs = 100000; + const int num_retries = timeout_in_seconds/time_delta_usecs; + for (int i=0; i<num_retries; i++) + { + struct proc_bsdinfo bsd_info; + int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO, + (uint64_t) 0, + &bsd_info, + PROC_PIDTBSDINFO_SIZE); + + switch (error) + { + case EINVAL: + case ENOTSUP: + case ESRCH: + case EPERM: + return false; + + default: + break; + + case 0: + if (bsd_info.pbi_status == SSTOP) + return true; + } + ::usleep (time_delta_usecs); + } + return false; +} +#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + +//static lldb::pid_t +//LaunchInNewTerminalWithCommandFile +//( +// const char **argv, +// const char **envp, +// const char *working_dir, +// const ArchSpec *arch_spec, +// bool stop_at_entry, +// bool disable_aslr +//) +//{ +// if (!argv || !argv[0]) +// return LLDB_INVALID_PROCESS_ID; +// +// OSStatus error = 0; +// +// FileSpec program (argv[0], false); +// +// +// std::string unix_socket_name; +// +// char temp_file_path[PATH_MAX]; +// const char *tmpdir = ::getenv ("TMPDIR"); +// if (tmpdir == NULL) +// tmpdir = "/tmp/"; +// ::snprintf (temp_file_path, sizeof(temp_file_path), "%s%s-XXXXXX", tmpdir, program.GetFilename().AsCString()); +// +// if (::mktemp (temp_file_path) == NULL) +// return LLDB_INVALID_PROCESS_ID; +// +// unix_socket_name.assign (temp_file_path); +// +// ::strlcat (temp_file_path, ".command", sizeof (temp_file_path)); +// +// StreamFile command_file; +// command_file.GetFile().Open (temp_file_path, +// File::eOpenOptionWrite | File::eOpenOptionCanCreate, +// lldb::eFilePermissionsDefault); +// +// if (!command_file.GetFile().IsValid()) +// return LLDB_INVALID_PROCESS_ID; +// +// FileSpec darwin_debug_file_spec; +// if (!HostInfo::GetLLDBPath (ePathTypeSupportExecutableDir, darwin_debug_file_spec)) +// return LLDB_INVALID_PROCESS_ID; +// darwin_debug_file_spec.GetFilename().SetCString("darwin-debug"); +// +// if (!darwin_debug_file_spec.Exists()) +// return LLDB_INVALID_PROCESS_ID; +// +// char launcher_path[PATH_MAX]; +// darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path)); +// command_file.Printf("\"%s\" ", launcher_path); +// +// command_file.Printf("--unix-socket=%s ", unix_socket_name.c_str()); +// +// if (arch_spec && arch_spec->IsValid()) +// { +// command_file.Printf("--arch=%s ", arch_spec->GetArchitectureName()); +// } +// +// if (disable_aslr) +// { +// command_file.PutCString("--disable-aslr "); +// } +// +// command_file.PutCString("-- "); +// +// if (argv) +// { +// for (size_t i=0; argv[i] != NULL; ++i) +// { +// command_file.Printf("\"%s\" ", argv[i]); +// } +// } +// command_file.PutCString("\necho Process exited with status $?\n"); +// command_file.GetFile().Close(); +// if (::chmod (temp_file_path, S_IRWXU | S_IRWXG) != 0) +// return LLDB_INVALID_PROCESS_ID; +// +// CFCMutableDictionary cf_env_dict; +// +// const bool can_create = true; +// if (envp) +// { +// for (size_t i=0; envp[i] != NULL; ++i) +// { +// const char *env_entry = envp[i]; +// const char *equal_pos = strchr(env_entry, '='); +// if (equal_pos) +// { +// std::string env_key (env_entry, equal_pos); +// std::string env_val (equal_pos + 1); +// CFCString cf_env_key (env_key.c_str(), kCFStringEncodingUTF8); +// CFCString cf_env_val (env_val.c_str(), kCFStringEncodingUTF8); +// cf_env_dict.AddValue (cf_env_key.get(), cf_env_val.get(), can_create); +// } +// } +// } +// +// LSApplicationParameters app_params; +// ::memset (&app_params, 0, sizeof (app_params)); +// app_params.flags = kLSLaunchDontAddToRecents | kLSLaunchAsync; +// app_params.argv = NULL; +// app_params.environment = (CFDictionaryRef)cf_env_dict.get(); +// +// CFCReleaser<CFURLRef> command_file_url (::CFURLCreateFromFileSystemRepresentation (NULL, +// (const UInt8 *)temp_file_path, +// strlen(temp_file_path), +// false)); +// +// CFCMutableArray urls; +// +// // Terminal.app will open the ".command" file we have created +// // and run our process inside it which will wait at the entry point +// // for us to attach. +// urls.AppendValue(command_file_url.get()); +// +// +// lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; +// +// Error lldb_error; +// // Sleep and wait a bit for debugserver to start to listen... +// char connect_url[128]; +// ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name.c_str()); +// +// // Spawn a new thread to accept incoming connection on the connect_url +// // so we can grab the pid from the inferior +// lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name.c_str(), +// AcceptPIDFromInferior, +// connect_url, +// &lldb_error); +// +// ProcessSerialNumber psn; +// error = LSOpenURLsWithRole(urls.get(), kLSRolesShell, NULL, &app_params, &psn, 1); +// if (error == noErr) +// { +// thread_result_t accept_thread_result = NULL; +// if (Host::ThreadJoin (accept_thread, &accept_thread_result, &lldb_error)) +// { +// if (accept_thread_result) +// { +// pid = (intptr_t)accept_thread_result; +// +// // Wait for process to be stopped the entry point by watching +// // for the process status to be set to SSTOP which indicates it it +// // SIGSTOP'ed at the entry point +// WaitForProcessToSIGSTOP (pid, 5); +// } +// } +// } +// else +// { +// Host::ThreadCancel (accept_thread, &lldb_error); +// } +// +// return pid; +//} + +const char *applscript_in_new_tty = +"tell application \"Terminal\"\n" +" do script \"%s\"\n" +"end tell\n"; + + +const char *applscript_in_existing_tty = "\ +set the_shell_script to \"%s\"\n\ +tell application \"Terminal\"\n\ + repeat with the_window in (get windows)\n\ + repeat with the_tab in tabs of the_window\n\ + set the_tty to tty in the_tab\n\ + if the_tty contains \"%s\" then\n\ + if the_tab is not busy then\n\ + set selected of the_tab to true\n\ + set frontmost of the_window to true\n\ + do script the_shell_script in the_tab\n\ + return\n\ + end if\n\ + end if\n\ + end repeat\n\ + end repeat\n\ + do script the_shell_script\n\ +end tell\n"; + + +static Error +LaunchInNewTerminalWithAppleScript (const char *exe_path, ProcessLaunchInfo &launch_info) +{ + Error error; + char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; + if (::mktemp (unix_socket_name) == NULL) + { + error.SetErrorString ("failed to make temporary path for a unix socket"); + return error; + } + + StreamString command; + FileSpec darwin_debug_file_spec; + if (!HostInfo::GetLLDBPath(ePathTypeSupportExecutableDir, darwin_debug_file_spec)) + { + error.SetErrorString ("can't locate the 'darwin-debug' executable"); + return error; + } + + darwin_debug_file_spec.GetFilename().SetCString("darwin-debug"); + + if (!darwin_debug_file_spec.Exists()) + { + error.SetErrorStringWithFormat ("the 'darwin-debug' executable doesn't exists at '%s'", + darwin_debug_file_spec.GetPath().c_str()); + return error; + } + + char launcher_path[PATH_MAX]; + darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path)); + + const ArchSpec &arch_spec = launch_info.GetArchitecture(); + // Only set the architecture if it is valid and if it isn't Haswell (x86_64h). + if (arch_spec.IsValid() && arch_spec.GetCore() != ArchSpec::eCore_x86_64_x86_64h) + command.Printf("arch -arch %s ", arch_spec.GetArchitectureName()); + + command.Printf("'%s' --unix-socket=%s", launcher_path, unix_socket_name); + + if (arch_spec.IsValid()) + command.Printf(" --arch=%s", arch_spec.GetArchitectureName()); + + FileSpec working_dir{launch_info.GetWorkingDirectory()}; + if (working_dir) + command.Printf(" --working-dir '%s'", working_dir.GetCString()); + else + { + char cwd[PATH_MAX]; + if (getcwd(cwd, PATH_MAX)) + command.Printf(" --working-dir '%s'", cwd); + } + + if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR)) + command.PutCString(" --disable-aslr"); + + // We are launching on this host in a terminal. So compare the environment on the host + // to what is supplied in the launch_info. Any items that aren't in the host environment + // need to be sent to darwin-debug. If we send all environment entries, we might blow the + // max command line length, so we only send user modified entries. + const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector (); + + StringList host_env; + const size_t host_env_count = Host::GetEnvironment (host_env); + + if (envp && envp[0]) + { + const char *env_entry; + for (size_t env_idx = 0; (env_entry = envp[env_idx]) != NULL; ++env_idx) + { + bool add_entry = true; + for (size_t i=0; i<host_env_count; ++i) + { + const char *host_env_entry = host_env.GetStringAtIndex(i); + if (strcmp(env_entry, host_env_entry) == 0) + { + add_entry = false; + break; + } + } + if (add_entry) + { + command.Printf(" --env='%s'", env_entry); + } + } + } + + command.PutCString(" -- "); + + const char **argv = launch_info.GetArguments().GetConstArgumentVector (); + if (argv) + { + for (size_t i=0; argv[i] != NULL; ++i) + { + if (i==0) + command.Printf(" '%s'", exe_path); + else + command.Printf(" '%s'", argv[i]); + } + } + else + { + command.Printf(" '%s'", exe_path); + } + command.PutCString (" ; echo Process exited with status $?"); + if (launch_info.GetFlags().Test(lldb::eLaunchFlagCloseTTYOnExit)) + command.PutCString (" ; exit"); + + StreamString applescript_source; + + const char *tty_command = command.GetString().c_str(); +// if (tty_name && tty_name[0]) +// { +// applescript_source.Printf (applscript_in_existing_tty, +// tty_command, +// tty_name); +// } +// else +// { + applescript_source.Printf (applscript_in_new_tty, + tty_command); +// } + + + + const char *script_source = applescript_source.GetString().c_str(); + //puts (script_source); + NSAppleScript* applescript = [[NSAppleScript alloc] initWithSource:[NSString stringWithCString:script_source encoding:NSUTF8StringEncoding]]; + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + Error lldb_error; + // Sleep and wait a bit for debugserver to start to listen... + ConnectionFileDescriptor file_conn; + char connect_url[128]; + ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name); + + // Spawn a new thread to accept incoming connection on the connect_url + // so we can grab the pid from the inferior. We have to do this because we + // are sending an AppleScript that will launch a process in Terminal.app, + // in a shell and the shell will fork/exec a couple of times before we get + // to the process that we wanted to launch. So when our process actually + // gets launched, we will handshake with it and get the process ID for it. + HostThread accept_thread = ThreadLauncher::LaunchThread(unix_socket_name, AcceptPIDFromInferior, connect_url, &lldb_error); + + [applescript executeAndReturnError:nil]; + + thread_result_t accept_thread_result = NULL; + lldb_error = accept_thread.Join(&accept_thread_result); + if (lldb_error.Success() && accept_thread_result) + { + pid = (intptr_t)accept_thread_result; + + // Wait for process to be stopped at the entry point by watching + // for the process status to be set to SSTOP which indicates it it + // SIGSTOP'ed at the entry point + WaitForProcessToSIGSTOP(pid, 5); + } + + FileSystem::Unlink(FileSpec{unix_socket_name, false}); + [applescript release]; + if (pid != LLDB_INVALID_PROCESS_ID) + launch_info.SetProcessID (pid); + return error; +} + +#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + + +// On MacOSX CrashReporter will display a string for each shared library if +// the shared library has an exported symbol named "__crashreporter_info__". + +static Mutex& +GetCrashReporterMutex () +{ + static Mutex g_mutex; + return g_mutex; +} + +extern "C" { + const char *__crashreporter_info__ = NULL; +} + +asm(".desc ___crashreporter_info__, 0x10"); + +void +Host::SetCrashDescriptionWithFormat (const char *format, ...) +{ + static StreamString g_crash_description; + Mutex::Locker locker (GetCrashReporterMutex ()); + + if (format) + { + va_list args; + va_start (args, format); + g_crash_description.GetString().clear(); + g_crash_description.PrintfVarArg(format, args); + va_end (args); + __crashreporter_info__ = g_crash_description.GetData(); + } + else + { + __crashreporter_info__ = NULL; + } +} + +void +Host::SetCrashDescription (const char *cstr) +{ + Mutex::Locker locker (GetCrashReporterMutex ()); + static std::string g_crash_description; + if (cstr) + { + g_crash_description.assign (cstr); + __crashreporter_info__ = g_crash_description.c_str(); + } + else + { + __crashreporter_info__ = NULL; + } +} + +bool +Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no) +{ +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + return false; +#else + // We attach this to an 'odoc' event to specify a particular selection + typedef struct { + int16_t reserved0; // must be zero + int16_t fLineNumber; + int32_t fSelStart; + int32_t fSelEnd; + uint32_t reserved1; // must be zero + uint32_t reserved2; // must be zero + } BabelAESelInfo; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST)); + char file_path[PATH_MAX]; + file_spec.GetPath(file_path, PATH_MAX); + CFCString file_cfstr (file_path, kCFStringEncodingUTF8); + CFCReleaser<CFURLRef> file_URL (::CFURLCreateWithFileSystemPath (NULL, + file_cfstr.get(), + kCFURLPOSIXPathStyle, + false)); + + if (log) + log->Printf("Sending source file: \"%s\" and line: %d to external editor.\n", file_path, line_no); + + long error; + BabelAESelInfo file_and_line_info = + { + 0, // reserved0 + (int16_t)(line_no - 1), // fLineNumber (zero based line number) + 1, // fSelStart + 1024, // fSelEnd + 0, // reserved1 + 0 // reserved2 + }; + + AEKeyDesc file_and_line_desc; + + error = ::AECreateDesc (typeUTF8Text, + &file_and_line_info, + sizeof (file_and_line_info), + &(file_and_line_desc.descContent)); + + if (error != noErr) + { + if (log) + log->Printf("Error creating AEDesc: %ld.\n", error); + return false; + } + + file_and_line_desc.descKey = keyAEPosition; + + static std::string g_app_name; + static FSRef g_app_fsref; + + LSApplicationParameters app_params; + ::memset (&app_params, 0, sizeof (app_params)); + app_params.flags = kLSLaunchDefaults | + kLSLaunchDontAddToRecents | + kLSLaunchDontSwitch; + + char *external_editor = ::getenv ("LLDB_EXTERNAL_EDITOR"); + + if (external_editor) + { + if (log) + log->Printf("Looking for external editor \"%s\".\n", external_editor); + + if (g_app_name.empty() || strcmp (g_app_name.c_str(), external_editor) != 0) + { + CFCString editor_name (external_editor, kCFStringEncodingUTF8); + error = ::LSFindApplicationForInfo (kLSUnknownCreator, + NULL, + editor_name.get(), + &g_app_fsref, + NULL); + + // If we found the app, then store away the name so we don't have to re-look it up. + if (error != noErr) + { + if (log) + log->Printf("Could not find External Editor application, error: %ld.\n", error); + return false; + } + + } + app_params.application = &g_app_fsref; + } + + ProcessSerialNumber psn; + CFCReleaser<CFArrayRef> file_array(CFArrayCreate (NULL, (const void **) file_URL.ptr_address(false), 1, NULL)); + error = ::LSOpenURLsWithRole (file_array.get(), + kLSRolesAll, + &file_and_line_desc, + &app_params, + &psn, + 1); + + AEDisposeDesc (&(file_and_line_desc.descContent)); + + if (error != noErr) + { + if (log) + log->Printf("LSOpenURLsWithRole failed, error: %ld.\n", error); + + return false; + } + + return true; +#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) +} + +size_t +Host::GetEnvironment (StringList &env) +{ + char **host_env = *_NSGetEnviron(); + char *env_entry; + size_t i; + for (i=0; (env_entry = host_env[i]) != NULL; ++i) + env.AppendString(env_entry); + return i; + +} + +static bool +GetMacOSXProcessCPUType (ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + // Make a new mib to stay thread safe + int mib[CTL_MAXNAME]={0,}; + size_t mib_len = CTL_MAXNAME; + if (::sysctlnametomib("sysctl.proc_cputype", mib, &mib_len)) + return false; + + mib[mib_len] = process_info.GetProcessID(); + mib_len++; + + cpu_type_t cpu, sub = 0; + size_t len = sizeof(cpu); + if (::sysctl (mib, mib_len, &cpu, &len, 0, 0) == 0) + { + switch (cpu) + { + case CPU_TYPE_I386: sub = CPU_SUBTYPE_I386_ALL; break; + case CPU_TYPE_X86_64: sub = CPU_SUBTYPE_X86_64_ALL; break; + +#if defined (CPU_TYPE_ARM64) && defined (CPU_SUBTYPE_ARM64_ALL) + case CPU_TYPE_ARM64: sub = CPU_SUBTYPE_ARM64_ALL; break; +#endif + + case CPU_TYPE_ARM: + { + // Note that we fetched the cpu type from the PROCESS but we can't get a cpusubtype of the + // process -- we can only get the host's cpu subtype. + uint32_t cpusubtype = 0; + len = sizeof(cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + sub = cpusubtype; + + bool host_cpu_is_64bit; + uint32_t is64bit_capable; + size_t is64bit_capable_len = sizeof (is64bit_capable); + if (sysctlbyname("hw.cpu64bit_capable", &is64bit_capable, &is64bit_capable_len, NULL, 0) == 0) + host_cpu_is_64bit = true; + else + host_cpu_is_64bit = false; + + // if the host is an armv8 device, its cpusubtype will be in CPU_SUBTYPE_ARM64 numbering + // and we need to rewrite it to a reasonable CPU_SUBTYPE_ARM value instead. + + if (host_cpu_is_64bit) + { + sub = CPU_SUBTYPE_ARM_V7; + } + } + break; + + default: + break; + } + process_info.GetArchitecture ().SetArchitecture (eArchTypeMachO, cpu, sub); + return true; + } + } + process_info.GetArchitecture().Clear(); + return false; +} + +static bool +GetMacOSXProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr, + ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + int proc_args_mib[3] = { CTL_KERN, KERN_PROCARGS2, (int)process_info.GetProcessID() }; + + size_t arg_data_size = 0; + if (::sysctl (proc_args_mib, 3, nullptr, &arg_data_size, NULL, 0) || arg_data_size == 0) + arg_data_size = 8192; + + // Add a few bytes to the calculated length, I know we need to add at least one byte + // to this number otherwise we get junk back, so add 128 just in case... + DataBufferHeap arg_data(arg_data_size+128, 0); + arg_data_size = arg_data.GetByteSize(); + if (::sysctl (proc_args_mib, 3, arg_data.GetBytes(), &arg_data_size , NULL, 0) == 0) + { + DataExtractor data (arg_data.GetBytes(), arg_data_size, endian::InlHostByteOrder(), sizeof(void *)); + lldb::offset_t offset = 0; + uint32_t argc = data.GetU32 (&offset); + llvm::Triple &triple = process_info.GetArchitecture().GetTriple(); + const llvm::Triple::ArchType triple_arch = triple.getArch(); + const bool check_for_ios_simulator = (triple_arch == llvm::Triple::x86 || triple_arch == llvm::Triple::x86_64); + const char *cstr = data.GetCStr (&offset); + if (cstr) + { + process_info.GetExecutableFile().SetFile(cstr, false); + + if (match_info_ptr == NULL || + NameMatches (process_info.GetExecutableFile().GetFilename().GetCString(), + match_info_ptr->GetNameMatchType(), + match_info_ptr->GetProcessInfo().GetName())) + { + // Skip NULLs + while (1) + { + const uint8_t *p = data.PeekData(offset, 1); + if ((p == NULL) || (*p != '\0')) + break; + ++offset; + } + // Now extract all arguments + Args &proc_args = process_info.GetArguments(); + for (int i=0; i<static_cast<int>(argc); ++i) + { + cstr = data.GetCStr(&offset); + if (cstr) + proc_args.AppendArgument(cstr); + } + + Args &proc_env = process_info.GetEnvironmentEntries (); + while ((cstr = data.GetCStr(&offset))) + { + if (cstr[0] == '\0') + break; + + if (check_for_ios_simulator) + { + if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == 0) + process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::IOS); + else + process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::MacOSX); + } + + proc_env.AppendArgument(cstr); + } + return true; + } + } + } + } + return false; +} + +static bool +GetMacOSXProcessUserAndGroup (ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + int mib[4]; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = process_info.GetProcessID(); + struct kinfo_proc proc_kinfo; + size_t proc_kinfo_size = sizeof(struct kinfo_proc); + + if (::sysctl (mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) + { + if (proc_kinfo_size > 0) + { + process_info.SetParentProcessID (proc_kinfo.kp_eproc.e_ppid); + process_info.SetUserID (proc_kinfo.kp_eproc.e_pcred.p_ruid); + process_info.SetGroupID (proc_kinfo.kp_eproc.e_pcred.p_rgid); + process_info.SetEffectiveUserID (proc_kinfo.kp_eproc.e_ucred.cr_uid); + if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0) + process_info.SetEffectiveGroupID (proc_kinfo.kp_eproc.e_ucred.cr_groups[0]); + else + process_info.SetEffectiveGroupID (UINT32_MAX); + return true; + } + } + } + process_info.SetParentProcessID (LLDB_INVALID_PROCESS_ID); + process_info.SetUserID (UINT32_MAX); + process_info.SetGroupID (UINT32_MAX); + process_info.SetEffectiveUserID (UINT32_MAX); + process_info.SetEffectiveGroupID (UINT32_MAX); + return false; +} + + +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + std::vector<struct kinfo_proc> kinfos; + + int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + + size_t pid_data_size = 0; + if (::sysctl (mib, 4, NULL, &pid_data_size, NULL, 0) != 0) + return 0; + + // Add a few extra in case a few more show up + const size_t estimated_pid_count = (pid_data_size / sizeof(struct kinfo_proc)) + 10; + + kinfos.resize (estimated_pid_count); + pid_data_size = kinfos.size() * sizeof(struct kinfo_proc); + + if (::sysctl (mib, 4, &kinfos[0], &pid_data_size, NULL, 0) != 0) + return 0; + + const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc)); + + bool all_users = match_info.GetMatchAllUsers(); + const lldb::pid_t our_pid = getpid(); + const uid_t our_uid = getuid(); + for (size_t i = 0; i < actual_pid_count; i++) + { + const struct kinfo_proc &kinfo = kinfos[i]; + + bool kinfo_user_matches = false; + if (all_users) + kinfo_user_matches = true; + else + kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid; + + // Special case, if lldb is being run as root we can attach to anything. + if (our_uid == 0) + kinfo_user_matches = true; + + if (kinfo_user_matches == false || // Make sure the user is acceptable + static_cast<lldb::pid_t>(kinfo.kp_proc.p_pid) == our_pid || // Skip this process + kinfo.kp_proc.p_pid == 0 || // Skip kernel (kernel pid is zero) + kinfo.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains... + kinfo.kp_proc.p_flag & P_TRACED || // Being debugged? + kinfo.kp_proc.p_flag & P_WEXIT || // Working on exiting? + kinfo.kp_proc.p_flag & P_TRANSLATED) // Skip translated ppc (Rosetta) + continue; + + ProcessInstanceInfo process_info; + process_info.SetProcessID (kinfo.kp_proc.p_pid); + process_info.SetParentProcessID (kinfo.kp_eproc.e_ppid); + process_info.SetUserID (kinfo.kp_eproc.e_pcred.p_ruid); + process_info.SetGroupID (kinfo.kp_eproc.e_pcred.p_rgid); + process_info.SetEffectiveUserID (kinfo.kp_eproc.e_ucred.cr_uid); + if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0) + process_info.SetEffectiveGroupID (kinfo.kp_eproc.e_ucred.cr_groups[0]); + else + process_info.SetEffectiveGroupID (UINT32_MAX); + + // Make sure our info matches before we go fetch the name and cpu type + if (match_info.Matches (process_info)) + { + // Get CPU type first so we can know to look for iOS simulator is we have x86 or x86_64 + if (GetMacOSXProcessCPUType (process_info)) + { + if (GetMacOSXProcessArgs (&match_info, process_info)) + { + if (match_info.Matches (process_info)) + process_infos.Append (process_info); + } + } + } + } + return process_infos.GetSize(); +} + +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + process_info.SetProcessID(pid); + bool success = false; + + // Get CPU type first so we can know to look for iOS simulator is we have x86 or x86_64 + if (GetMacOSXProcessCPUType (process_info)) + success = true; + + if (GetMacOSXProcessArgs (NULL, process_info)) + success = true; + + if (GetMacOSXProcessUserAndGroup (process_info)) + success = true; + + if (success) + return true; + + process_info.Clear(); + return false; +} + +#if !NO_XPC_SERVICES +static void +PackageXPCArguments (xpc_object_t message, const char *prefix, const Args& args) +{ + size_t count = args.GetArgumentCount(); + char buf[50]; // long enough for 'argXXX' + memset(buf, 0, 50); + sprintf(buf, "%sCount", prefix); + xpc_dictionary_set_int64(message, buf, count); + for (size_t i=0; i<count; i++) { + memset(buf, 0, 50); + sprintf(buf, "%s%zi", prefix, i); + xpc_dictionary_set_string(message, buf, args.GetArgumentAtIndex(i)); + } +} + +/* + A valid authorizationRef means that + - there is the LaunchUsingXPCRightName rights in the /etc/authorization + - we have successfully copied the rights to be send over the XPC wire + Once obtained, it will be valid for as long as the process lives. + */ +static AuthorizationRef authorizationRef = NULL; +static Error +getXPCAuthorization (ProcessLaunchInfo &launch_info) +{ + Error error; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); + + if ((launch_info.GetUserID() == 0) && !authorizationRef) + { + OSStatus createStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef); + if (createStatus != errAuthorizationSuccess) + { + error.SetError(1, eErrorTypeGeneric); + error.SetErrorString("Can't create authorizationRef."); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + return error; + } + + OSStatus rightsStatus = AuthorizationRightGet(LaunchUsingXPCRightName, NULL); + if (rightsStatus != errAuthorizationSuccess) + { + // No rights in the security database, Create it with the right prompt. + CFStringRef prompt = CFSTR("Xcode is trying to take control of a root process."); + CFStringRef keys[] = { CFSTR("en") }; + CFTypeRef values[] = { prompt }; + CFDictionaryRef promptDict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFStringRef keys1[] = { CFSTR("class"), CFSTR("group"), CFSTR("comment"), CFSTR("default-prompt"), CFSTR("shared") }; + CFTypeRef values1[] = { CFSTR("user"), CFSTR("admin"), CFSTR(LaunchUsingXPCRightName), promptDict, kCFBooleanFalse }; + CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + rightsStatus = AuthorizationRightSet(authorizationRef, LaunchUsingXPCRightName, dict, NULL, NULL, NULL); + CFRelease(promptDict); + CFRelease(dict); + } + + OSStatus copyRightStatus = errAuthorizationDenied; + if (rightsStatus == errAuthorizationSuccess) + { + AuthorizationItem item1 = { LaunchUsingXPCRightName, 0, NULL, 0 }; + AuthorizationItem items[] = {item1}; + AuthorizationRights requestedRights = {1, items }; + AuthorizationFlags authorizationFlags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights; + copyRightStatus = AuthorizationCopyRights(authorizationRef, &requestedRights, kAuthorizationEmptyEnvironment, authorizationFlags, NULL); + } + + if (copyRightStatus != errAuthorizationSuccess) + { + // Eventually when the commandline supports running as root and the user is not + // logged in in the current audit session, we will need the trick in gdb where + // we ask the user to type in the root passwd in the terminal. + error.SetError(2, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Launching as root needs root authorization."); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + + if (authorizationRef) + { + AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); + authorizationRef = NULL; + } + } + } + + return error; +} +#endif + +static Error +LaunchProcessXPC(const char *exe_path, ProcessLaunchInfo &launch_info, lldb::pid_t &pid) +{ +#if !NO_XPC_SERVICES + Error error = getXPCAuthorization(launch_info); + if (error.Fail()) + return error; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); + + uid_t requested_uid = launch_info.GetUserID(); + const char *xpc_service = nil; + bool send_auth = false; + AuthorizationExternalForm extForm; + if (requested_uid == 0) + { + if (AuthorizationMakeExternalForm(authorizationRef, &extForm) == errAuthorizationSuccess) + { + send_auth = true; + } + else + { + error.SetError(3, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Launching root via XPC needs to externalize authorization reference."); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + return error; + } + xpc_service = LaunchUsingXPCRightName; + } + else + { + error.SetError(4, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Launching via XPC is only currently available for root."); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + return error; + } + + xpc_connection_t conn = xpc_connection_create(xpc_service, NULL); + + xpc_connection_set_event_handler(conn, ^(xpc_object_t event) { + xpc_type_t type = xpc_get_type(event); + + if (type == XPC_TYPE_ERROR) { + if (event == XPC_ERROR_CONNECTION_INTERRUPTED) { + // The service has either canceled itself, crashed, or been terminated. + // The XPC connection is still valid and sending a message to it will re-launch the service. + // If the service is state-full, this is the time to initialize the new service. + return; + } else if (event == XPC_ERROR_CONNECTION_INVALID) { + // The service is invalid. Either the service name supplied to xpc_connection_create() is incorrect + // or we (this process) have canceled the service; we can do any cleanup of application state at this point. + // printf("Service disconnected"); + return; + } else { + // printf("Unexpected error from service: %s", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); + } + + } else { + // printf("Received unexpected event in handler"); + } + }); + + xpc_connection_set_finalizer_f (conn, xpc_finalizer_t(xpc_release)); + xpc_connection_resume (conn); + xpc_object_t message = xpc_dictionary_create (nil, nil, 0); + + if (send_auth) + { + xpc_dictionary_set_data(message, LauncherXPCServiceAuthKey, extForm.bytes, sizeof(AuthorizationExternalForm)); + } + + PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey, launch_info.GetArguments()); + PackageXPCArguments(message, LauncherXPCServiceEnvPrefxKey, launch_info.GetEnvironmentEntries()); + + // Posix spawn stuff. + xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, launch_info.GetArchitecture().GetMachOCPUType()); + xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, Host::GetPosixspawnFlags(launch_info)); + const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO); + if (file_action && file_action->GetPath()) + { + xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, file_action->GetPath()); + } + file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); + if (file_action && file_action->GetPath()) + { + xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey, file_action->GetPath()); + } + file_action = launch_info.GetFileActionForFD(STDERR_FILENO); + if (file_action && file_action->GetPath()) + { + xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey, file_action->GetPath()); + } + + xpc_object_t reply = xpc_connection_send_message_with_reply_sync(conn, message); + xpc_type_t returnType = xpc_get_type(reply); + if (returnType == XPC_TYPE_DICTIONARY) + { + pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey); + if (pid == 0) + { + int errorType = xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey); + int errorCode = xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey); + + error.SetError(errorCode, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Problems with launching via XPC. Error type : %i, code : %i", errorType, errorCode); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + + if (authorizationRef) + { + AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); + authorizationRef = NULL; + } + } + } + else if (returnType == XPC_TYPE_ERROR) + { + error.SetError(5, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Problems with launching via XPC. XPC error : %s", xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION)); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + } + + return error; +#else + Error error; + return error; +#endif +} + +static bool +ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) +{ + bool result = false; + +#if !NO_XPC_SERVICES + bool launchingAsRoot = launch_info.GetUserID() == 0; + bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0; + + if (launchingAsRoot && !currentUserIsRoot) + { + // If current user is already root, we don't need XPC's help. + result = true; + } +#endif + + return result; +} + +Error +Host::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + char exe_path[PATH_MAX]; + PlatformSP host_platform_sp (Platform::GetHostPlatform ()); + + ModuleSpec exe_module_spec(launch_info.GetExecutableFile(), launch_info.GetArchitecture()); + + FileSpec::FileType file_type = exe_module_spec.GetFileSpec().GetFileType(); + if (file_type != FileSpec::eFileTypeRegular) + { + lldb::ModuleSP exe_module_sp; + error = host_platform_sp->ResolveExecutable (exe_module_spec, + exe_module_sp, + NULL); + + if (error.Fail()) + return error; + + if (exe_module_sp) + exe_module_spec.GetFileSpec() = exe_module_sp->GetFileSpec(); + } + + if (exe_module_spec.GetFileSpec().Exists()) + { + exe_module_spec.GetFileSpec().GetPath (exe_path, sizeof(exe_path)); + } + else + { + launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path); + return error; + } + + if (launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)) + { +#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + return LaunchInNewTerminalWithAppleScript (exe_path, launch_info); +#else + error.SetErrorString ("launching a process in a new terminal is not supported on iOS devices"); + return error; +#endif + } + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + if (ShouldLaunchUsingXPC(launch_info)) + { + error = LaunchProcessXPC(exe_path, launch_info, pid); + } + else + { + error = LaunchProcessPosixSpawn(exe_path, launch_info, pid); + } + + if (pid != LLDB_INVALID_PROCESS_ID) + { + // If all went well, then set the process ID into the launch info + launch_info.SetProcessID(pid); + + // Make sure we reap any processes we spawn or we will have zombies. + if (!launch_info.MonitorProcess()) + { + const bool monitor_signals = false; + Host::MonitorChildProcessCallback callback = nullptr; + + if (!launch_info.GetFlags().Test(lldb::eLaunchFlagDontSetExitStatus)) + callback = Process::SetProcessExitStatus; + + StartMonitoringChildProcess (callback, + NULL, + pid, + monitor_signals); + } + } + else + { + // Invalid process ID, something didn't go well + if (error.Success()) + error.SetErrorString ("process launch failed for unknown reasons"); + } + return error; +} + +Error +Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) +{ + Error error; + if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) + { + FileSpec expand_tool_spec; + if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, expand_tool_spec)) + { + error.SetErrorString("could not get support executable directory for lldb-argdumper tool"); + return error; + } + expand_tool_spec.AppendPathComponent("lldb-argdumper"); + if (!expand_tool_spec.Exists()) + { + error.SetErrorStringWithFormat("could not find the lldb-argdumper tool: %s", expand_tool_spec.GetPath().c_str()); + return error; + } + + StreamString expand_tool_spec_stream; + expand_tool_spec_stream.Printf("\"%s\"",expand_tool_spec.GetPath().c_str()); + + Args expand_command(expand_tool_spec_stream.GetData()); + expand_command.AppendArguments (launch_info.GetArguments()); + + int status; + std::string output; + FileSpec cwd(launch_info.GetWorkingDirectory()); + if (!cwd.Exists()) + { + char *wd = getcwd(nullptr, 0); + if (wd == nullptr) + { + error.SetErrorStringWithFormat("cwd does not exist; cannot launch with shell argument expansion"); + return error; + } + else + { + FileSpec working_dir(wd, false); + free(wd); + launch_info.SetWorkingDirectory(working_dir); + + } + } + RunShellCommand(expand_command, cwd, &status, nullptr, &output, 10); + + if (status != 0) + { + error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", status); + return error; + } + + auto data_sp = StructuredData::ParseJSON(output); + if (!data_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto dict_sp = data_sp->GetAsDictionary(); + if (!data_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments"); + if (!args_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto args_array_sp = args_sp->GetAsArray(); + if (!args_array_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + launch_info.GetArguments().Clear(); + + for (size_t i = 0; + i < args_array_sp->GetSize(); + i++) + { + auto item_sp = args_array_sp->GetItemAtIndex(i); + if (!item_sp) + continue; + auto str_sp = item_sp->GetAsString(); + if (!str_sp) + continue; + + launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str()); + } + } + + return error; +} + +HostThread +Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals) +{ + unsigned long mask = DISPATCH_PROC_EXIT; + if (monitor_signals) + mask |= DISPATCH_PROC_SIGNAL; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); + + + dispatch_source_t source = ::dispatch_source_create (DISPATCH_SOURCE_TYPE_PROC, + pid, + mask, + ::dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT,0)); + + if (log) + log->Printf ("Host::StartMonitoringChildProcess (callback=%p, baton=%p, pid=%i, monitor_signals=%i) source = %p\n", + callback, + callback_baton, + (int)pid, + monitor_signals, + source); + + if (source) + { + ::dispatch_source_set_cancel_handler (source, ^{ + ::dispatch_release (source); + }); + ::dispatch_source_set_event_handler (source, ^{ + + int status= 0; + int wait_pid = 0; + bool cancel = false; + bool exited = false; + do + { + wait_pid = ::waitpid (pid, &status, 0); + } while (wait_pid < 0 && errno == EINTR); + + if (wait_pid >= 0) + { + int signal = 0; + int exit_status = 0; + const char *status_cstr = NULL; + if (WIFSTOPPED(status)) + { + signal = WSTOPSIG(status); + status_cstr = "STOPPED"; + } + else if (WIFEXITED(status)) + { + exit_status = WEXITSTATUS(status); + status_cstr = "EXITED"; + exited = true; + } + else if (WIFSIGNALED(status)) + { + signal = WTERMSIG(status); + status_cstr = "SIGNALED"; + exited = true; + exit_status = -1; + } + else + { + status_cstr = "???"; + } + + if (log) + log->Printf ("::waitpid (pid = %llu, &status, 0) => pid = %i, status = 0x%8.8x (%s), signal = %i, exit_status = %i", + pid, + wait_pid, + status, + status_cstr, + signal, + exit_status); + + if (callback) + cancel = callback (callback_baton, pid, exited, signal, exit_status); + + if (exited || cancel) + { + ::dispatch_source_cancel(source); + } + } + }); + + ::dispatch_resume (source); + } + return HostThread(); +} + +//---------------------------------------------------------------------- +// Log to both stderr and to ASL Logging when running on MacOSX. +//---------------------------------------------------------------------- +void +Host::SystemLog (SystemLogType type, const char *format, va_list args) +{ + if (format && format[0]) + { + static aslmsg g_aslmsg = NULL; + if (g_aslmsg == NULL) + { + g_aslmsg = ::asl_new (ASL_TYPE_MSG); + char asl_key_sender[PATH_MAX]; + snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.LLDB.framework"); + ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender); + } + + // Copy the va_list so we can log this message twice + va_list copy_args; + va_copy (copy_args, args); + // Log to stderr + ::vfprintf (stderr, format, copy_args); + va_end (copy_args); + + int asl_level; + switch (type) + { + case eSystemLogError: + asl_level = ASL_LEVEL_ERR; + break; + + case eSystemLogWarning: + asl_level = ASL_LEVEL_WARNING; + break; + } + + // Log to ASL + ::asl_vlog (NULL, g_aslmsg, asl_level, format, args); + } +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ + return lldb::DataBufferSP(); +} diff --git a/source/Host/macosx/HostInfoMacOSX.mm b/source/Host/macosx/HostInfoMacOSX.mm new file mode 100644 index 000000000000..f5a0540e8774 --- /dev/null +++ b/source/Host/macosx/HostInfoMacOSX.mm @@ -0,0 +1,372 @@ +//===-- HostInfoMacOSX.mm ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if !defined(LLDB_DISABLE_PYTHON) +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" +#endif + +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/macosx/HostInfoMacOSX.h" +#include "lldb/Core/Log.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/SafeMachO.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +// C++ Includes +#include <string> + +// C inclues +#include <stdlib.h> +#include <sys/sysctl.h> +#include <sys/syslimits.h> +#include <sys/types.h> + +// Objective C/C++ includes +#include <CoreFoundation/CoreFoundation.h> +#include <Foundation/Foundation.h> +#include <mach-o/dyld.h> +#include <objc/objc-auto.h> + +// These are needed when compiling on systems +// that do not yet have these definitions +#include <AvailabilityMacros.h> +#ifndef CPU_SUBTYPE_X86_64_H +#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) +#endif +#ifndef CPU_TYPE_ARM64 +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM|CPU_ARCH_ABI64) +#endif + +#include <TargetConditionals.h> // for TARGET_OS_TV, TARGET_OS_WATCH + +using namespace lldb_private; + +bool +HostInfoMacOSX::GetOSBuildString(std::string &s) +{ + int mib[2] = {CTL_KERN, KERN_OSVERSION}; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) + { + s.assign(cstr, cstr_len); + return true; + } + + s.clear(); + return false; +} + +bool +HostInfoMacOSX::GetOSKernelDescription(std::string &s) +{ + int mib[2] = {CTL_KERN, KERN_VERSION}; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) + { + s.assign(cstr, cstr_len); + return true; + } + s.clear(); + return false; +} + +bool +HostInfoMacOSX::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) +{ + static uint32_t g_major = 0; + static uint32_t g_minor = 0; + static uint32_t g_update = 0; + + if (g_major == 0) + { + @autoreleasepool + { + NSDictionary *version_info = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; + NSString *version_value = [version_info objectForKey:@"ProductVersion"]; + const char *version_str = [version_value UTF8String]; + if (version_str) + Args::StringToVersion(version_str, g_major, g_minor, g_update); + } + } + + if (g_major != 0) + { + major = g_major; + minor = g_minor; + update = g_update; + return true; + } + return false; +} + +FileSpec +HostInfoMacOSX::GetProgramFileSpec() +{ + static FileSpec g_program_filespec; + if (!g_program_filespec) + { + char program_fullpath[PATH_MAX]; + // If DST is NULL, then return the number of bytes needed. + uint32_t len = sizeof(program_fullpath); + int err = _NSGetExecutablePath(program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile(program_fullpath, false); + else if (err == -1) + { + char *large_program_fullpath = (char *)::malloc(len + 1); + + err = _NSGetExecutablePath(large_program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile(large_program_fullpath, false); + + ::free(large_program_fullpath); + } + } + return g_program_filespec; +} + +bool +HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + // Shallow bundle + raw_path.resize(framework_pos); +#else + // Normal bundle + raw_path.resize(framework_pos); + raw_path.append("/Resources"); +#endif + } + else + { + // Find the bin path relative to the lib path where the cmake-based + // OS X .dylib lives. This is not going to work if the bin and lib + // dir are not both in the same dir. + // + // It is not going to work to do it by the executable path either, + // as in the case of a python script, the executable is python, not + // the lldb driver. + raw_path.append("/../bin"); + FileSpec support_dir_spec(raw_path, true); + if (!support_dir_spec.Exists() || !support_dir_spec.IsDirectory()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoMacOSX::%s(): failed to find support directory", + __FUNCTION__); + return false; + } + + // Get normalization from support_dir_spec. Note the FileSpec resolve + // does not remove '..' in the path. + char *const dir_realpath = realpath(support_dir_spec.GetPath().c_str(), NULL); + if (dir_realpath) + { + raw_path = dir_realpath; + free(dir_realpath); + } + else + { + raw_path = support_dir_spec.GetPath(); + } + } + + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return (bool)file_spec.GetDirectory(); +} + +bool +HostInfoMacOSX::ComputeHeaderDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!HostInfo::GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Headers"); + } + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +} + +bool +HostInfoMacOSX::ComputePythonDirectory(FileSpec &file_spec) +{ +#ifndef LLDB_DISABLE_PYTHON + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Resources/Python"); + } + else + { + llvm::SmallString<256> python_version_dir; + llvm::raw_svector_ostream os(python_version_dir); + os << "/python" << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION << "/site-packages"; + + // We may get our string truncated. Should we protect this with an assert? + raw_path.append(python_version_dir.c_str()); + } + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +#else + return false; +#endif +} + +bool +HostInfoMacOSX::ComputeClangDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!GetLLDBPath (lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Resources/Clang"); + } + file_spec.SetFile (raw_path.c_str(), true); + return true; +} + +bool +HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos == std::string::npos) + return false; + + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Resources/PlugIns"); + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +} + +bool +HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) +{ + FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns", true); + file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); + return true; +} + +void +HostInfoMacOSX::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) +{ + // All apple systems support 32 bit execution. + uint32_t cputype, cpusubtype; + uint32_t is_64_bit_capable = false; + size_t len = sizeof(cputype); + ArchSpec host_arch; + // These will tell us about the kernel architecture, which even on a 64 + // bit machine can be 32 bit... + if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) + { + len = sizeof(cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) + cpusubtype = CPU_TYPE_ANY; + + len = sizeof(is_64_bit_capable); + ::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0); + + if (is_64_bit_capable) + { + if (cputype & CPU_ARCH_ABI64) + { + // We have a 64 bit kernel on a 64 bit system + arch_64.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); + } + else + { + // We have a 64 bit kernel that is returning a 32 bit cputype, the + // cpusubtype will be correct as if it were for a 64 bit architecture + arch_64.SetArchitecture(eArchTypeMachO, cputype | CPU_ARCH_ABI64, cpusubtype); + } + + // Now we need modify the cpusubtype for the 32 bit slices. + uint32_t cpusubtype32 = cpusubtype; +#if defined(__i386__) || defined(__x86_64__) + if (cpusubtype == CPU_SUBTYPE_486 || cpusubtype == CPU_SUBTYPE_X86_64_H) + cpusubtype32 = CPU_SUBTYPE_I386_ALL; +#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) + cpusubtype32 = CPU_SUBTYPE_ARM_V7S; +#endif + arch_32.SetArchitecture(eArchTypeMachO, cputype & ~(CPU_ARCH_MASK), cpusubtype32); + + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) + { + // When running on a watch or tv, report the host os correctly +#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1 + arch_32.GetTriple().setOS(llvm::Triple::TvOS); + arch_64.GetTriple().setOS(llvm::Triple::TvOS); +#else + arch_32.GetTriple().setOS(llvm::Triple::IOS); + arch_64.GetTriple().setOS(llvm::Triple::IOS); +#endif + } + else + { + arch_32.GetTriple().setOS(llvm::Triple::MacOSX); + arch_64.GetTriple().setOS(llvm::Triple::MacOSX); + } + } + else + { + // We have a 32 bit kernel on a 32 bit system + arch_32.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + arch_32.GetTriple().setOS(llvm::Triple::WatchOS); +#else + arch_32.GetTriple().setOS(llvm::Triple::IOS); +#endif + arch_64.Clear(); + } + } +} diff --git a/source/Host/macosx/HostThreadMacOSX.mm b/source/Host/macosx/HostThreadMacOSX.mm new file mode 100644 index 000000000000..c84a78efd9e6 --- /dev/null +++ b/source/Host/macosx/HostThreadMacOSX.mm @@ -0,0 +1,102 @@ +//===-- HostThreadMacOSX.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/macosx/HostThreadMacOSX.h" +#include "lldb/Host/Host.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <Foundation/Foundation.h> + +#include <objc/objc-auto.h> +#include <pthread.h> + +using namespace lldb_private; + +namespace +{ + +pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT; +pthread_key_t g_thread_create_key = 0; + +class MacOSXDarwinThread +{ + public: + MacOSXDarwinThread() + : m_pool(nil) + { + // Register our thread with the collector if garbage collection is enabled. + if (objc_collectingEnabled()) + { +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 + // On Leopard and earlier there is no way objc_registerThreadWithCollector + // function, so we do it manually. + auto_zone_register_thread(auto_zone()); +#else + // On SnowLeopard and later we just call the thread registration function. + objc_registerThreadWithCollector(); +#endif + } + else + { + m_pool = [[NSAutoreleasePool alloc] init]; + } + } + + ~MacOSXDarwinThread() + { + if (m_pool) + { + [m_pool drain]; + m_pool = nil; + } + } + + static void + PThreadDestructor(void *v) + { + if (v) + delete static_cast<MacOSXDarwinThread *>(v); + ::pthread_setspecific(g_thread_create_key, NULL); + } + + protected: + NSAutoreleasePool *m_pool; + + private: + DISALLOW_COPY_AND_ASSIGN(MacOSXDarwinThread); +}; + +void +InitThreadCreated() +{ + ::pthread_key_create(&g_thread_create_key, MacOSXDarwinThread::PThreadDestructor); +} +} // namespace + +HostThreadMacOSX::HostThreadMacOSX() + : HostThreadPosix() +{ +} + +HostThreadMacOSX::HostThreadMacOSX(lldb::thread_t thread) + : HostThreadPosix(thread) +{ +} + +lldb::thread_result_t +HostThreadMacOSX::ThreadCreateTrampoline(lldb::thread_arg_t arg) +{ + ::pthread_once(&g_thread_create_once, InitThreadCreated); + if (g_thread_create_key) + { + ::pthread_setspecific(g_thread_create_key, new MacOSXDarwinThread()); + } + + return HostThreadPosix::ThreadCreateTrampoline(arg); +} diff --git a/source/Host/macosx/Symbols.cpp b/source/Host/macosx/Symbols.cpp new file mode 100644 index 000000000000..f6a18febe6da --- /dev/null +++ b/source/Host/macosx/Symbols.cpp @@ -0,0 +1,559 @@ +//===-- Symbols.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Symbols.h" + +// C Includes +#include <dirent.h> +#include <pwd.h> +#include "lldb/Utility/SafeMachO.h" + +// C++ Includes +// Other libraries and framework includes +#include <CoreFoundation/CoreFoundation.h> + +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/CleanUp.h" +#include "Host/macosx/cfcpp/CFCBundle.h" +#include "Host/macosx/cfcpp/CFCData.h" +#include "Host/macosx/cfcpp/CFCReleaser.h" +#include "Host/macosx/cfcpp/CFCString.h" +#include "mach/machine.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::MachO; + +#if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices +extern "C" { + +CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url); +CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url); + +} +#endif + +int +LocateMacOSXFilesUsingDebugSymbols +( + const ModuleSpec &module_spec, + ModuleSpec &return_module_spec +) +{ + return_module_spec = module_spec; + return_module_spec.GetFileSpec().Clear(); + return_module_spec.GetSymbolFileSpec().Clear(); + + int items_found = 0; + +#if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices + + const UUID *uuid = module_spec.GetUUIDPtr(); + const ArchSpec *arch = module_spec.GetArchitecturePtr(); + + if (uuid && uuid->IsValid()) + { + // Try and locate the dSYM file using DebugSymbols first + const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes(); + if (module_uuid != NULL) + { + CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes (NULL, + module_uuid[0], + module_uuid[1], + module_uuid[2], + module_uuid[3], + module_uuid[4], + module_uuid[5], + module_uuid[6], + module_uuid[7], + module_uuid[8], + module_uuid[9], + module_uuid[10], + module_uuid[11], + module_uuid[12], + module_uuid[13], + module_uuid[14], + module_uuid[15])); + + if (module_uuid_ref.get()) + { + CFCReleaser<CFURLRef> exec_url; + const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (exec_fspec) + { + char exec_cf_path[PATH_MAX]; + if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path))) + exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL, + (const UInt8 *)exec_cf_path, + strlen(exec_cf_path), + FALSE)); + } + + CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get())); + char path[PATH_MAX]; + + if (dsym_url.get()) + { + if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + if (log) + { + log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for the dSYM", path, uuid->GetAsString().c_str()); + } + FileSpec dsym_filespec(path, path[0] == '~'); + + if (dsym_filespec.GetFileType () == FileSpec::eFileTypeDirectory) + { + dsym_filespec = Symbols::FindSymbolFileInBundle (dsym_filespec, uuid, arch); + ++items_found; + } + else + { + ++items_found; + } + return_module_spec.GetSymbolFileSpec() = dsym_filespec; + } + + bool success = false; + if (log) + { + if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for an exec file", path, uuid->GetAsString().c_str()); + } + + } + + CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get())); + CFDictionaryRef uuid_dict = NULL; + if (dict.get()) + { + CFCString uuid_cfstr (uuid->GetAsString().c_str()); + uuid_dict = static_cast<CFDictionaryRef>(::CFDictionaryGetValue (dict.get(), uuid_cfstr.get())); + } + if (uuid_dict) + { + CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSymbolRichExecutable"))); + if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path))) + { + if (log) + { + log->Printf ("plist bundle has exec path of %s for UUID %s", path, uuid->GetAsString().c_str()); + } + ++items_found; + FileSpec exec_filespec (path, path[0] == '~'); + if (exec_filespec.Exists()) + { + success = true; + return_module_spec.GetFileSpec() = exec_filespec; + } + } + } + + if (!success) + { + // No dictionary, check near the dSYM bundle for an executable that matches... + if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + char *dsym_extension_pos = ::strstr (path, ".dSYM"); + if (dsym_extension_pos) + { + *dsym_extension_pos = '\0'; + if (log) + { + log->Printf ("Looking for executable binary next to dSYM bundle with name with name %s", path); + } + FileSpec file_spec (path, true); + ModuleSpecList module_specs; + ModuleSpec matched_module_spec; + switch (file_spec.GetFileType()) + { + case FileSpec::eFileTypeDirectory: // Bundle directory? + { + CFCBundle bundle (path); + CFCReleaser<CFURLRef> bundle_exe_url (bundle.CopyExecutableURL ()); + if (bundle_exe_url.get()) + { + if (::CFURLGetFileSystemRepresentation (bundle_exe_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + FileSpec bundle_exe_file_spec (path, true); + if (ObjectFile::GetModuleSpecifications(bundle_exe_file_spec, 0, 0, module_specs) && + module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) + + { + ++items_found; + return_module_spec.GetFileSpec() = bundle_exe_file_spec; + if (log) + { + log->Printf ("Executable binary %s next to dSYM is compatible; using", path); + } + } + } + } + } + break; + + case FileSpec::eFileTypePipe: // Forget pipes + case FileSpec::eFileTypeSocket: // We can't process socket files + case FileSpec::eFileTypeInvalid: // File doesn't exist... + break; + + case FileSpec::eFileTypeUnknown: + case FileSpec::eFileTypeRegular: + case FileSpec::eFileTypeSymbolicLink: + case FileSpec::eFileTypeOther: + if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, module_specs) && + module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) + + { + ++items_found; + return_module_spec.GetFileSpec() = file_spec; + if (log) + { + log->Printf ("Executable binary %s next to dSYM is compatible; using", path); + } + } + break; + } + } + } + } + } + } + } + } +#endif // #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) + + return items_found; +} + +FileSpec +Symbols::FindSymbolFileInBundle (const FileSpec& dsym_bundle_fspec, + const lldb_private::UUID *uuid, + const ArchSpec *arch) +{ + char path[PATH_MAX]; + + FileSpec dsym_fspec; + + if (dsym_bundle_fspec.GetPath(path, sizeof(path))) + { + ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1); + + lldb_utility::CleanUp <DIR *, int> dirp (opendir(path), NULL, closedir); + if (dirp.is_valid()) + { + dsym_fspec.GetDirectory().SetCString(path); + struct dirent* dp; + while ((dp = readdir(dirp.get())) != NULL) + { + // Only search directories + if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) + { + if (dp->d_namlen == 1 && dp->d_name[0] == '.') + continue; + + if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') + continue; + } + + if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) + { + dsym_fspec.GetFilename().SetCString(dp->d_name); + ModuleSpecList module_specs; + if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) + { + ModuleSpec spec; + for (size_t i = 0; i < module_specs.GetSize(); ++i) + { + assert(module_specs.GetModuleSpecAtIndex(i, spec)); + if ((uuid == NULL || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) && + (arch == NULL || (spec.GetArchitecturePtr() && spec.GetArchitecture().IsCompatibleMatch(*arch)))) + { + return dsym_fspec; + } + } + } + } + } + } + } + dsym_fspec.Clear(); + return dsym_fspec; +} + +static bool +GetModuleSpecInfoFromUUIDDictionary (CFDictionaryRef uuid_dict, ModuleSpec &module_spec) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + bool success = false; + if (uuid_dict != NULL && CFGetTypeID (uuid_dict) == CFDictionaryGetTypeID ()) + { + std::string str; + CFStringRef cf_str; + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSymbolRichExecutable")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + if (CFCString::FileSystemRepresentation(cf_str, str)) + { + module_spec.GetFileSpec().SetFile (str.c_str(), true); + if (log) + { + log->Printf ("From dsymForUUID plist: Symbol rich executable is at '%s'", str.c_str()); + } + } + } + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGDSYMPath")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + if (CFCString::FileSystemRepresentation(cf_str, str)) + { + module_spec.GetSymbolFileSpec().SetFile (str.c_str(), true); + success = true; + if (log) + { + log->Printf ("From dsymForUUID plist: dSYM is at '%s'", str.c_str()); + } + } + } + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGArchitecture")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + if (CFCString::FileSystemRepresentation(cf_str, str)) + module_spec.GetArchitecture().SetTriple(str.c_str()); + } + + std::string DBGBuildSourcePath; + std::string DBGSourcePath; + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGBuildSourcePath")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath); + } + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSourcePath")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + CFCString::FileSystemRepresentation(cf_str, DBGSourcePath); + } + + if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) + { + module_spec.GetSourceMappingList().Append (ConstString(DBGBuildSourcePath.c_str()), ConstString(DBGSourcePath.c_str()), true); + } + } + return success; +} + + +bool +Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup) +{ + bool success = false; + const UUID *uuid_ptr = module_spec.GetUUIDPtr(); + const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr(); + + // It's expensive to check for the DBGShellCommands defaults setting, only do it once per + // lldb run and cache the result. + static bool g_have_checked_for_dbgshell_command = false; + static const char *g_dbgshell_command = NULL; + if (g_have_checked_for_dbgshell_command == false) + { + g_have_checked_for_dbgshell_command = true; + CFTypeRef defaults_setting = CFPreferencesCopyAppValue (CFSTR ("DBGShellCommands"), CFSTR ("com.apple.DebugSymbols")); + if (defaults_setting && CFGetTypeID (defaults_setting) == CFStringGetTypeID()) + { + char cstr_buf[PATH_MAX]; + if (CFStringGetCString ((CFStringRef) defaults_setting, cstr_buf, sizeof (cstr_buf), kCFStringEncodingUTF8)) + { + g_dbgshell_command = strdup (cstr_buf); // this malloc'ed memory will never be freed + } + } + if (defaults_setting) + { + CFRelease (defaults_setting); + } + } + + // When g_dbgshell_command is NULL, the user has not enabled the use of an external program + // to find the symbols, don't run it for them. + if (force_lookup == false && g_dbgshell_command == NULL) + { + return false; + } + + if (uuid_ptr || (file_spec_ptr && file_spec_ptr->Exists())) + { + static bool g_located_dsym_for_uuid_exe = false; + static bool g_dsym_for_uuid_exe_exists = false; + static char g_dsym_for_uuid_exe_path[PATH_MAX]; + if (!g_located_dsym_for_uuid_exe) + { + g_located_dsym_for_uuid_exe = true; + const char *dsym_for_uuid_exe_path_cstr = getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE"); + FileSpec dsym_for_uuid_exe_spec; + if (dsym_for_uuid_exe_path_cstr) + { + dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr, true); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + } + + if (!g_dsym_for_uuid_exe_exists) + { + dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID", false); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + if (!g_dsym_for_uuid_exe_exists) + { + long bufsize; + if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1) + { + char buffer[bufsize]; + struct passwd pwd; + struct passwd *tilde_rc = NULL; + // we are a library so we need to use the reentrant version of getpwnam() + if (getpwnam_r ("rc", &pwd, buffer, bufsize, &tilde_rc) == 0 + && tilde_rc + && tilde_rc->pw_dir) + { + std::string dsymforuuid_path(tilde_rc->pw_dir); + dsymforuuid_path += "/bin/dsymForUUID"; + dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(), false); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + } + } + } + } + if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL) + { + dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command, true); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + } + + if (g_dsym_for_uuid_exe_exists) + dsym_for_uuid_exe_spec.GetPath (g_dsym_for_uuid_exe_path, sizeof(g_dsym_for_uuid_exe_path)); + } + if (g_dsym_for_uuid_exe_exists) + { + std::string uuid_str; + char file_path[PATH_MAX]; + file_path[0] = '\0'; + + if (uuid_ptr) + uuid_str = uuid_ptr->GetAsString(); + + if (file_spec_ptr) + file_spec_ptr->GetPath(file_path, sizeof(file_path)); + + StreamString command; + if (!uuid_str.empty()) + command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, uuid_str.c_str()); + else if (file_path[0] != '\0') + command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, file_path); + + if (!command.GetString().empty()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + int exit_status = -1; + int signo = -1; + std::string command_output; + if (log) + { + if (!uuid_str.empty()) + log->Printf("Calling %s with UUID %s to find dSYM", g_dsym_for_uuid_exe_path, uuid_str.c_str()); + else if (file_path[0] != '\0') + log->Printf("Calling %s with file %s to find dSYM", g_dsym_for_uuid_exe_path, file_path); + } + Error error = Host::RunShellCommand (command.GetData(), + NULL, // current working directory + &exit_status, // Exit status + &signo, // Signal int * + &command_output, // Command output + 30, // Large timeout to allow for long dsym download times + false); // Don't run in a shell (we don't need shell expansion) + if (error.Success() && exit_status == 0 && !command_output.empty()) + { + CFCData data (CFDataCreateWithBytesNoCopy (NULL, + (const UInt8 *)command_output.data(), + command_output.size(), + kCFAllocatorNull)); + + CFCReleaser<CFDictionaryRef> plist((CFDictionaryRef)::CFPropertyListCreateFromXMLData (NULL, data.get(), kCFPropertyListImmutable, NULL)); + + if (plist.get() && CFGetTypeID (plist.get()) == CFDictionaryGetTypeID ()) + { + if (!uuid_str.empty()) + { + CFCString uuid_cfstr(uuid_str.c_str()); + CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue (plist.get(), uuid_cfstr.get()); + success = GetModuleSpecInfoFromUUIDDictionary (uuid_dict, module_spec); + } + else + { + const CFIndex num_values = ::CFDictionaryGetCount(plist.get()); + if (num_values > 0) + { + std::vector<CFStringRef> keys (num_values, NULL); + std::vector<CFDictionaryRef> values (num_values, NULL); + ::CFDictionaryGetKeysAndValues(plist.get(), NULL, (const void **)&values[0]); + if (num_values == 1) + { + return GetModuleSpecInfoFromUUIDDictionary (values[0], module_spec); + } + else + { + for (CFIndex i=0; i<num_values; ++i) + { + ModuleSpec curr_module_spec; + if (GetModuleSpecInfoFromUUIDDictionary (values[i], curr_module_spec)) + { + if (module_spec.GetArchitecture().IsCompatibleMatch(curr_module_spec.GetArchitecture())) + { + module_spec = curr_module_spec; + return true; + } + } + } + } + } + } + } + } + else + { + if (log) + { + if (!uuid_str.empty()) + log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, uuid_str.c_str()); + else if (file_path[0] != '\0') + log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, file_path); + } + } + } + } + } + return success; +} + diff --git a/source/Host/macosx/ThisThread.cpp b/source/Host/macosx/ThisThread.cpp new file mode 100644 index 000000000000..95c7f2bf1e38 --- /dev/null +++ b/source/Host/macosx/ThisThread.cpp @@ -0,0 +1,39 @@ +//===-- ThisThread.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/ThisThread.h" + +#include <pthread.h> + +using namespace lldb_private; + +void +ThisThread::SetName(llvm::StringRef name) +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + ::pthread_setname_np(name); +#endif +} + +void +ThisThread::GetName(llvm::SmallVectorImpl<char> &name) +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + char pthread_name[1024]; + dispatch_queue_t current_queue = ::dispatch_get_current_queue(); + if (current_queue != NULL) + { + const char *queue_name = dispatch_queue_get_label(current_queue); + if (queue_name && queue_name[0]) + { + name = queue_name; + } + } +#endif +} diff --git a/source/Host/macosx/cfcpp/CFCBundle.cpp b/source/Host/macosx/cfcpp/CFCBundle.cpp new file mode 100644 index 000000000000..71b074993661 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCBundle.cpp @@ -0,0 +1,99 @@ +//===-- CFCBundle.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCBundle.h" +#include "CFCString.h" + +//---------------------------------------------------------------------- +// CFCBundle constructor +//---------------------------------------------------------------------- +CFCBundle::CFCBundle(const char *path) : + CFCReleaser<CFBundleRef>() +{ + if (path && path[0]) + SetPath(path); +} + +CFCBundle::CFCBundle(CFURLRef url) : + CFCReleaser<CFBundleRef>(url ? CFBundleCreate(NULL, url) : NULL) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCBundle::~CFCBundle() +{ +} + +//---------------------------------------------------------------------- +// Set the path for a bundle by supplying a +//---------------------------------------------------------------------- +bool +CFCBundle::SetPath (const char *path) +{ + CFAllocatorRef alloc = kCFAllocatorDefault; + // Release our old bundle and URL + reset(); + + // Make a CFStringRef from the supplied path + CFCString cf_path; + cf_path.SetFileSystemRepresentation(path); + if (cf_path.get()) + { + // Make our Bundle URL + CFCReleaser<CFURLRef> bundle_url (::CFURLCreateWithFileSystemPath (alloc, cf_path.get(), kCFURLPOSIXPathStyle, true)); + if (bundle_url.get()) + reset (::CFBundleCreate (alloc, bundle_url.get())); + } + return get() != NULL; +} + +bool +CFCBundle::GetPath (char *dst, size_t dst_len) +{ + CFBundleRef bundle = get(); + if (bundle) + { + CFCReleaser<CFURLRef> bundle_url (CFBundleCopyBundleURL (bundle)); + if (bundle_url.get()) + { + Boolean resolveAgainstBase = 0; + return ::CFURLGetFileSystemRepresentation (bundle_url.get(), resolveAgainstBase, (UInt8 *)dst, dst_len) != 0; + } + } + return false; +} + +CFStringRef +CFCBundle::GetIdentifier () const +{ + CFBundleRef bundle = get(); + if (bundle != NULL) + return ::CFBundleGetIdentifier (bundle); + return NULL; +} + +CFTypeRef +CFCBundle::GetValueForInfoDictionaryKey(CFStringRef key) const +{ + CFBundleRef bundle = get(); + if (bundle != NULL) + return ::CFBundleGetValueForInfoDictionaryKey(bundle, key); + return NULL; +} + +CFURLRef +CFCBundle::CopyExecutableURL () const +{ + CFBundleRef bundle = get(); + if (bundle != NULL) + return CFBundleCopyExecutableURL(bundle); + return NULL; +} diff --git a/source/Host/macosx/cfcpp/CFCBundle.h b/source/Host/macosx/cfcpp/CFCBundle.h new file mode 100644 index 000000000000..1cd1b681af84 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCBundle.h @@ -0,0 +1,50 @@ +//===-- CFCBundle.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFBundle_h_ +#define CoreFoundationCPP_CFBundle_h_ + +#include "CFCReleaser.h" + +class CFCBundle : public CFCReleaser<CFBundleRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCBundle (const char *path = NULL); + CFCBundle (CFURLRef url); + + virtual + ~CFCBundle(); + + CFURLRef + CopyExecutableURL () const; + + CFStringRef + GetIdentifier () const; + + CFTypeRef + GetValueForInfoDictionaryKey(CFStringRef key) const; + + bool + GetPath (char *dst, size_t dst_len); + + bool + SetPath (const char *path); + +private: + // Disallow copy and assignment constructors + CFCBundle(const CFCBundle&); + + const CFCBundle& + operator=(const CFCBundle&); +}; + +#endif // #ifndef CoreFoundationCPP_CFBundle_h_ diff --git a/source/Host/macosx/cfcpp/CFCData.cpp b/source/Host/macosx/cfcpp/CFCData.cpp new file mode 100644 index 000000000000..4f49368ad8ad --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCData.cpp @@ -0,0 +1,82 @@ +//===-- CFCData.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCData.h" + +//---------------------------------------------------------------------- +// CFCData constructor +//---------------------------------------------------------------------- +CFCData::CFCData(CFDataRef data) : + CFCReleaser<CFDataRef>(data) +{ + +} + +//---------------------------------------------------------------------- +// CFCData copy constructor +//---------------------------------------------------------------------- +CFCData::CFCData(const CFCData& rhs) : + CFCReleaser<CFDataRef>(rhs) +{ + +} + +//---------------------------------------------------------------------- +// CFCData copy constructor +//---------------------------------------------------------------------- +CFCData& +CFCData::operator=(const CFCData& rhs) + +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCData::~CFCData() +{ +} + + +CFIndex +CFCData::GetLength() const +{ + CFDataRef data = get(); + if (data) + return CFDataGetLength (data); + return 0; +} + + +const uint8_t* +CFCData::GetBytePtr() const +{ + CFDataRef data = get(); + if (data) + return CFDataGetBytePtr (data); + return NULL; +} + +CFDataRef +CFCData::Serialize(CFPropertyListRef plist, CFPropertyListFormat format) +{ + CFAllocatorRef alloc = kCFAllocatorDefault; + reset(); + CFCReleaser<CFWriteStreamRef> stream (::CFWriteStreamCreateWithAllocatedBuffers (alloc, alloc)); + ::CFWriteStreamOpen (stream.get()); + CFIndex len = ::CFPropertyListWriteToStream (plist, stream.get(), format, NULL); + if (len > 0) + reset((CFDataRef)::CFWriteStreamCopyProperty (stream.get(), kCFStreamPropertyDataWritten)); + ::CFWriteStreamClose (stream.get()); + return get(); +} + diff --git a/source/Host/macosx/cfcpp/CFCData.h b/source/Host/macosx/cfcpp/CFCData.h new file mode 100644 index 000000000000..6a718f54c055 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCData.h @@ -0,0 +1,35 @@ +//===-- CFCData.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFData_h_ +#define CoreFoundationCPP_CFData_h_ + +#include "CFCReleaser.h" + +class CFCData : public CFCReleaser<CFDataRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCData(CFDataRef data = NULL); + CFCData(const CFCData& rhs); + CFCData& operator=(const CFCData& rhs); + virtual ~CFCData(); + + CFDataRef Serialize(CFPropertyListRef plist, CFPropertyListFormat format); + const uint8_t* GetBytePtr () const; + CFIndex GetLength () const; +protected: + //------------------------------------------------------------------ + // Classes that inherit from CFCData can see and modify these + //------------------------------------------------------------------ +}; + +#endif // #ifndef CoreFoundationCPP_CFData_h_ diff --git a/source/Host/macosx/cfcpp/CFCMutableArray.cpp b/source/Host/macosx/cfcpp/CFCMutableArray.cpp new file mode 100644 index 000000000000..c3c0a11193a7 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableArray.cpp @@ -0,0 +1,166 @@ +//===-- CFCMutableArray.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableArray.h" +#include "CFCString.h" + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableArray::CFCMutableArray(CFMutableArrayRef s) : + CFCReleaser<CFMutableArrayRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableArray copy constructor +//---------------------------------------------------------------------- +CFCMutableArray::CFCMutableArray(const CFCMutableArray& rhs) : + CFCReleaser<CFMutableArrayRef> (rhs) // NOTE: this won't make a copy of the array, just add a new reference to it +{ +} + +//---------------------------------------------------------------------- +// CFCMutableArray copy constructor +//---------------------------------------------------------------------- +CFCMutableArray& +CFCMutableArray::operator=(const CFCMutableArray& rhs) +{ + if (this != &rhs) + *this = rhs; // NOTE: this operator won't make a copy of the array, just add a new reference to it + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableArray::~CFCMutableArray() +{ +} + + +CFIndex +CFCMutableArray::GetCount() const +{ + CFMutableArrayRef array = get(); + if (array) + return ::CFArrayGetCount (array); + return 0; +} + +CFIndex +CFCMutableArray::GetCountOfValue(CFRange range, const void *value) const +{ + CFMutableArrayRef array = get(); + if (array) + return ::CFArrayGetCountOfValue (array, range, value); + return 0; +} + +CFIndex +CFCMutableArray::GetCountOfValue(const void *value) const +{ + CFMutableArrayRef array = get(); + if (array) + return ::CFArrayGetCountOfValue (array, CFRangeMake(0, GetCount()), value); + return 0; +} + +const void * +CFCMutableArray::GetValueAtIndex(CFIndex idx) const +{ + CFMutableArrayRef array = get(); + if (array) + { + const CFIndex num_array_items = ::CFArrayGetCount (array); + if (0 <= idx && idx < num_array_items) + { + return ::CFArrayGetValueAtIndex (array, idx); + } + } + return NULL; +} + +bool +CFCMutableArray::SetValueAtIndex(CFIndex idx, const void *value) +{ + CFMutableArrayRef array = get(); + if (array != NULL) + { + const CFIndex num_array_items = ::CFArrayGetCount (array); + if (0 <= idx && idx < num_array_items) + { + ::CFArraySetValueAtIndex (array, idx, value); + return true; + } + } + return false; +} + + +bool +CFCMutableArray::AppendValue(const void *value, bool can_create) +{ + CFMutableArrayRef array = get(); + if (array == NULL) + { + if (can_create == false) + return false; + array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + reset ( array ); + } + if (array != NULL) + { + ::CFArrayAppendValue(array, value); + return true; + } + return false; +} + + +bool +CFCMutableArray::AppendCStringAsCFString (const char *s, CFStringEncoding encoding, bool can_create) +{ + CFMutableArrayRef array = get(); + if (array == NULL) + { + if (can_create == false) + return false; + array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + reset ( array ); + } + if (array != NULL) + { + CFCString cf_str (s, encoding); + ::CFArrayAppendValue (array, cf_str.get()); + return true; + } + return false; +} + +bool +CFCMutableArray::AppendFileSystemRepresentationAsCFString (const char *s, bool can_create) +{ + CFMutableArrayRef array = get(); + if (array == NULL) + { + if (can_create == false) + return false; + array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + reset ( array ); + } + if (array != NULL) + { + CFCString cf_path; + cf_path.SetFileSystemRepresentation(s); + ::CFArrayAppendValue (array, cf_path.get()); + return true; + } + return false; +} diff --git a/source/Host/macosx/cfcpp/CFCMutableArray.h b/source/Host/macosx/cfcpp/CFCMutableArray.h new file mode 100644 index 000000000000..f78cd92ffab1 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableArray.h @@ -0,0 +1,39 @@ +//===-- CFCMutableArray.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableArray_h_ +#define CoreFoundationCPP_CFMutableArray_h_ + +#include "CFCReleaser.h" + +class CFCMutableArray : public CFCReleaser<CFMutableArrayRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCMutableArray(CFMutableArrayRef array = NULL); + CFCMutableArray(const CFCMutableArray& rhs); // This will copy the array contents into a new array + CFCMutableArray& operator=(const CFCMutableArray& rhs); // This will re-use the same array and just bump the ref count + virtual ~CFCMutableArray(); + + CFIndex GetCount() const; + CFIndex GetCountOfValue(const void *value) const; + CFIndex GetCountOfValue(CFRange range, const void *value) const; + const void * GetValueAtIndex(CFIndex idx) const; + bool SetValueAtIndex(CFIndex idx, const void *value); + bool AppendValue(const void *value, bool can_create = true); // Appends value and optionally creates a CFCMutableArray if this class doesn't contain one + bool AppendCStringAsCFString (const char *cstr, + CFStringEncoding encoding = kCFStringEncodingUTF8, + bool can_create = true); + bool AppendFileSystemRepresentationAsCFString (const char *s, + bool can_create = true); +}; + +#endif // #ifndef CoreFoundationCPP_CFMutableArray_h_ diff --git a/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp b/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp new file mode 100644 index 000000000000..bce023bfd616 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp @@ -0,0 +1,529 @@ +//===-- CFCMutableDictionary.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableDictionary.h" +#include "CFCString.h" +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableDictionary::CFCMutableDictionary(CFMutableDictionaryRef s) : + CFCReleaser<CFMutableDictionaryRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableDictionary copy constructor +//---------------------------------------------------------------------- +CFCMutableDictionary::CFCMutableDictionary(const CFCMutableDictionary& rhs) : + CFCReleaser<CFMutableDictionaryRef> (rhs) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableDictionary copy constructor +//---------------------------------------------------------------------- +const CFCMutableDictionary& +CFCMutableDictionary::operator=(const CFCMutableDictionary& rhs) +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableDictionary::~CFCMutableDictionary() +{ +} + + +CFIndex +CFCMutableDictionary::GetCount() const +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetCount (dict); + return 0; +} + +CFIndex +CFCMutableDictionary::GetCountOfKey(const void *key) const + +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetCountOfKey (dict, key); + return 0; +} + +CFIndex +CFCMutableDictionary::GetCountOfValue(const void *value) const + +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetCountOfValue (dict, value); + return 0; +} + +void +CFCMutableDictionary::GetKeysAndValues(const void **keys, const void **values) const +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryGetKeysAndValues (dict, keys, values); +} + + +const void * +CFCMutableDictionary::GetValue(const void *key) const + +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetValue (dict, key); + return NULL; +} + +Boolean +CFCMutableDictionary::GetValueIfPresent(const void *key, const void **value_handle) const +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetValueIfPresent (dict, key, value_handle); + return false; +} + + +CFMutableDictionaryRef +CFCMutableDictionary::Dictionary(bool can_create) +{ + CFMutableDictionaryRef dict = get(); + if (can_create && dict == NULL) + { + dict = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + reset ( dict ); + } + return dict; +} + +bool +CFCMutableDictionary::AddValue(CFStringRef key, const void *value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, value); + return true; + } + return false; +} + +bool +CFCMutableDictionary::SetValue(CFStringRef key, const void *value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, value); + return true; + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt8(CFStringRef key, int8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt8Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt8(CFStringRef key, int8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt8Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt16(CFStringRef key, int16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt16(CFStringRef key, int16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt32(CFStringRef key, int32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt32(CFStringRef key, int32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt64(CFStringRef key, int64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt64(CFStringRef key, int64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueUInt8(CFStringRef key, uint8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int16_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueUInt8(CFStringRef key, uint8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int16_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + + +bool +CFCMutableDictionary::AddValueUInt16(CFStringRef key, uint16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int32_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueUInt16(CFStringRef key, uint16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int32_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueUInt32(CFStringRef key, uint32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int64_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueUInt32(CFStringRef key, uint32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int64_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + + +bool +CFCMutableDictionary::AddValueUInt64(CFStringRef key, uint64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // The number may appear negative if the MSBit is set in "value". Due to a limitation of + // CFNumber, there isn't a way to have it show up otherwise as of this writing. + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + + +bool +CFCMutableDictionary::SetValueUInt64(CFStringRef key, uint64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // The number may appear negative if the MSBit is set in "value". Due to a limitation of + // CFNumber, there isn't a way to have it show up otherwise as of this writing. + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueDouble(CFStringRef key, double value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // The number may appear negative if the MSBit is set in "value". Due to a limitation of + // CFNumber, there isn't a way to have it show up otherwise as of this writing. + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberDoubleType, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueDouble(CFStringRef key, double value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // The number may appear negative if the MSBit is set in "value". Due to a limitation of + // CFNumber, there isn't a way to have it show up otherwise as of this writing. + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberDoubleType, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueCString(CFStringRef key, const char *cstr, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCString cf_str(cstr, kCFStringEncodingUTF8); + if (cf_str.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_str.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueCString(CFStringRef key, const char *cstr, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCString cf_str(cstr, kCFStringEncodingUTF8); + if (cf_str.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_str.get()); + return true; + } + } + return false; +} + + +void +CFCMutableDictionary::RemoveAllValues() +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryRemoveAllValues(dict); +} + +void +CFCMutableDictionary::RemoveValue(const void *value) +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryRemoveValue(dict, value); +} +void +CFCMutableDictionary::ReplaceValue(const void *key, const void *value) +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryReplaceValue (dict, key, value); +} + diff --git a/source/Host/macosx/cfcpp/CFCMutableDictionary.h b/source/Host/macosx/cfcpp/CFCMutableDictionary.h new file mode 100644 index 000000000000..a1cfb68f569e --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableDictionary.h @@ -0,0 +1,79 @@ +//===-- CFCMutableDictionary.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableDictionary_h_ +#define CoreFoundationCPP_CFMutableDictionary_h_ + +#include "CFCReleaser.h" + +class CFCMutableDictionary : public CFCReleaser<CFMutableDictionaryRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCMutableDictionary(CFMutableDictionaryRef s = NULL); + CFCMutableDictionary(const CFCMutableDictionary& rhs); + virtual ~CFCMutableDictionary(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const CFCMutableDictionary& + operator=(const CFCMutableDictionary& rhs); + + + CFIndex GetCount() const; + CFIndex GetCountOfKey(const void *value) const; + CFIndex GetCountOfValue(const void *value) const; + void GetKeysAndValues(const void **keys, const void **values) const; + const void * GetValue(const void *key) const; + Boolean GetValueIfPresent(const void *key, const void **value_handle) const; + bool AddValue(CFStringRef key, const void *value, bool can_create = false); + bool SetValue(CFStringRef key, const void *value, bool can_create = false); + bool AddValueSInt8(CFStringRef key, int8_t value, bool can_create = false); + bool SetValueSInt8(CFStringRef key, int8_t value, bool can_create = false); + bool AddValueSInt16(CFStringRef key, int16_t value, bool can_create = false); + bool SetValueSInt16(CFStringRef key, int16_t value, bool can_create = false); + bool AddValueSInt32(CFStringRef key, int32_t value, bool can_create = false); + bool SetValueSInt32(CFStringRef key, int32_t value, bool can_create = false); + bool AddValueSInt64(CFStringRef key, int64_t value, bool can_create = false); + bool SetValueSInt64(CFStringRef key, int64_t value, bool can_create = false); + bool AddValueUInt8(CFStringRef key, uint8_t value, bool can_create = false); + bool SetValueUInt8(CFStringRef key, uint8_t value, bool can_create = false); + bool AddValueUInt16(CFStringRef key, uint16_t value, bool can_create = false); + bool SetValueUInt16(CFStringRef key, uint16_t value, bool can_create = false); + bool AddValueUInt32(CFStringRef key, uint32_t value, bool can_create = false); + bool SetValueUInt32(CFStringRef key, uint32_t value, bool can_create = false); + bool AddValueUInt64(CFStringRef key, uint64_t value, bool can_create = false); + bool SetValueUInt64(CFStringRef key, uint64_t value, bool can_create = false); + bool AddValueDouble(CFStringRef key, double value, bool can_create = false); + bool SetValueDouble(CFStringRef key, double value, bool can_create = false); + bool AddValueCString(CFStringRef key, const char *cstr, bool can_create = false); + bool SetValueCString(CFStringRef key, const char *cstr, bool can_create = false); + void RemoveValue(const void *value); + void ReplaceValue(const void *key, const void *value); + void RemoveAllValues(); + CFMutableDictionaryRef Dictionary(bool can_create); + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from CFCMutableDictionary can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For CFCMutableDictionary only + //------------------------------------------------------------------ + +}; + + +#endif // CoreFoundationCPP_CFMutableDictionary_h_ diff --git a/source/Host/macosx/cfcpp/CFCMutableSet.cpp b/source/Host/macosx/cfcpp/CFCMutableSet.cpp new file mode 100644 index 000000000000..afc09e180b6b --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableSet.cpp @@ -0,0 +1,114 @@ +//===-- CFCMutableSet.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableSet.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableSet::CFCMutableSet(CFMutableSetRef s) : + CFCReleaser<CFMutableSetRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableSet copy constructor +//---------------------------------------------------------------------- +CFCMutableSet::CFCMutableSet(const CFCMutableSet& rhs) : + CFCReleaser<CFMutableSetRef> (rhs) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableSet copy constructor +//---------------------------------------------------------------------- +const CFCMutableSet& +CFCMutableSet::operator=(const CFCMutableSet& rhs) +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableSet::~CFCMutableSet() +{ +} + + +CFIndex +CFCMutableSet::GetCount() const +{ + CFMutableSetRef set = get(); + if (set) + return ::CFSetGetCount (set); + return 0; +} + +CFIndex +CFCMutableSet::GetCountOfValue(const void *value) const +{ + CFMutableSetRef set = get(); + if (set) + return ::CFSetGetCountOfValue (set, value); + return 0; +} + +const void * +CFCMutableSet::GetValue(const void *value) const +{ + CFMutableSetRef set = get(); + if (set) + return ::CFSetGetValue(set, value); + return NULL; +} + + +const void * +CFCMutableSet::AddValue(const void *value, bool can_create) +{ + CFMutableSetRef set = get(); + if (set == NULL) + { + if (can_create == false) + return NULL; + set = ::CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks); + reset ( set ); + } + if (set != NULL) + { + ::CFSetAddValue(set, value); + return value; + } + return NULL; +} + +void +CFCMutableSet::RemoveValue(const void *value) +{ + CFMutableSetRef set = get(); + if (set) + ::CFSetRemoveValue(set, value); +} + +void +CFCMutableSet::RemoveAllValues() +{ + CFMutableSetRef set = get(); + if (set) + ::CFSetRemoveAllValues(set); +} + diff --git a/source/Host/macosx/cfcpp/CFCMutableSet.h b/source/Host/macosx/cfcpp/CFCMutableSet.h new file mode 100644 index 000000000000..78f7a8be81d2 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableSet.h @@ -0,0 +1,53 @@ +//===-- CFCMutableSet.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableSet_h_ +#define CoreFoundationCPP_CFMutableSet_h_ + +#include "CFCReleaser.h" + +class CFCMutableSet : public CFCReleaser<CFMutableSetRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCMutableSet(CFMutableSetRef s = NULL); + CFCMutableSet(const CFCMutableSet& rhs); + virtual ~CFCMutableSet(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const CFCMutableSet& + operator=(const CFCMutableSet& rhs); + + + CFIndex GetCount() const; + CFIndex GetCountOfValue(const void *value) const; + const void * GetValue(const void *value) const; + const void * AddValue(const void *value, bool can_create); + void RemoveValue(const void *value); + void RemoveAllValues(); + + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from CFCMutableSet can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For CFCMutableSet only + //------------------------------------------------------------------ + +}; + +#endif // CoreFoundationCPP_CFMutableSet_h_ diff --git a/source/Host/macosx/cfcpp/CFCReleaser.h b/source/Host/macosx/cfcpp/CFCReleaser.h new file mode 100644 index 000000000000..67dd2ead5799 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCReleaser.h @@ -0,0 +1,158 @@ +//===-- CFCReleaser.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFReleaser_h_ +#define CoreFoundationCPP_CFReleaser_h_ + +#include <CoreFoundation/CoreFoundation.h> + +#ifdef __cplusplus + +#include <assert.h> + +//---------------------------------------------------------------------- +// Templatized CF helper class that can own any CF pointer and will +// call CFRelease() on any valid pointer it owns unless that pointer is +// explicitly released using the release() member function. This class +// is designed to mimic the std::auto_ptr<T> class and has all of the +// same functions. The one thing to watch out for is the +// CFCReleaser<T>::release() function won't actually CFRelease any owned +// pointer, it is designed to relinquish ownership of the pointer just +// like std:auto_ptr<T>::release() does. +//---------------------------------------------------------------------- +template <class T> +class CFCReleaser +{ +public: + //---------------------------------------------------------- + // Constructor that takes a pointer to a CF object that is + // to be released when this object goes out of scope + //---------------------------------------------------------- + CFCReleaser(T ptr = NULL) : + _ptr(ptr) + { + } + + //---------------------------------------------------------- + // Copy constructor + // + // Note that copying a CFCReleaser will not transfer + // ownership of the contained pointer, but it will bump its + // reference count. This is where this class differs from + // std::auto_ptr. + //---------------------------------------------------------- + CFCReleaser(const CFCReleaser& rhs) : + _ptr(rhs.get()) + { + if (get()) + ::CFRetain(get()); + } + + + //---------------------------------------------------------- + // The destructor will release the pointer that it contains + // if it has a valid pointer. + //---------------------------------------------------------- + virtual ~CFCReleaser() + { + reset(); + } + + //---------------------------------------------------------- + // Assignment operator. + // + // Note that assigning one CFCReleaser to another will + // not transfer ownership of the contained pointer, but it + // will bump its reference count. This is where this class + // differs from std::auto_ptr. + //---------------------------------------------------------- + CFCReleaser& + operator= (const CFCReleaser<T>& rhs) + { + if (this != &rhs) + { + // Replace our owned pointer with the new one + reset(rhs.get()); + // Retain the current pointer that we own + if (get()) + ::CFRetain(get()); + } + return *this; + } + + //---------------------------------------------------------- + // Get the address of the contained type in case it needs + // to be passed to a function that will fill in a pointer + // value. The function currently will assert if _ptr is not + // NULL because the only time this method should be used is + // if another function will modify the contents, and we + // could leak a pointer if this is not NULL. If the + // assertion fires, check the offending code, or call + // reset() prior to using the "ptr_address()" member to make + // sure any owned objects has CFRelease called on it. + // I had to add the "enforce_null" bool here because some + // API's require the pointer address even though they don't change it. + //---------------------------------------------------------- + T* + ptr_address(bool enforce_null = true) + { + if (enforce_null) + assert (_ptr == NULL); + return &_ptr; + } + + //---------------------------------------------------------- + // Access the pointer itself + //---------------------------------------------------------- + T + get() + { + return _ptr; + } + + const T + get() const + { + return _ptr; + } + + + //---------------------------------------------------------- + // Set a new value for the pointer and CFRelease our old + // value if we had a valid one. + //---------------------------------------------------------- + void + reset(T ptr = NULL) + { + if ((_ptr != NULL) && (ptr != _ptr)) + ::CFRelease(_ptr); + _ptr = ptr; + } + + //---------------------------------------------------------- + // Release ownership without calling CFRelease. This class + // is designed to mimic std::auto_ptr<T>, so the release + // method releases ownership of the contained pointer + // and does NOT call CFRelease. + //---------------------------------------------------------- + T + release() + { + T tmp = _ptr; + _ptr = NULL; + return tmp; + } + +private: + T _ptr; +}; + +#endif // #ifdef __cplusplus +#endif // #ifndef CoreFoundationCPP_CFReleaser_h_ + diff --git a/source/Host/macosx/cfcpp/CFCString.cpp b/source/Host/macosx/cfcpp/CFCString.cpp new file mode 100644 index 000000000000..81a96b824999 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCString.cpp @@ -0,0 +1,195 @@ +//===-- CFCString.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCString.h" +#include <string> +#include <glob.h> + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCString::CFCString(CFStringRef s) : + CFCReleaser<CFStringRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCString copy constructor +//---------------------------------------------------------------------- +CFCString::CFCString(const CFCString& rhs) : + CFCReleaser<CFStringRef> (rhs) +{ + +} + +//---------------------------------------------------------------------- +// CFCString copy constructor +//---------------------------------------------------------------------- +CFCString& +CFCString::operator=(const CFCString& rhs) +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +CFCString::CFCString (const char *cstr, CFStringEncoding cstr_encoding) : + CFCReleaser<CFStringRef> () +{ + if (cstr && cstr[0]) + { + reset(::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding)); + } +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCString::~CFCString() +{ +} + +const char * +CFCString::GetFileSystemRepresentation(std::string& s) +{ + return CFCString::FileSystemRepresentation(get(), s); +} + +CFStringRef +CFCString::SetFileSystemRepresentation (const char *path) +{ + CFStringRef new_value = NULL; + if (path && path[0]) + new_value = ::CFStringCreateWithFileSystemRepresentation (kCFAllocatorDefault, path); + reset(new_value); + return get(); +} + + +CFStringRef +CFCString::SetFileSystemRepresentationFromCFType (CFTypeRef cf_type) +{ + CFStringRef new_value = NULL; + if (cf_type != NULL) + { + CFTypeID cf_type_id = ::CFGetTypeID(cf_type); + + if (cf_type_id == ::CFStringGetTypeID()) + { + // Retain since we are using the existing object + new_value = (CFStringRef)::CFRetain(cf_type); + } + else if (cf_type_id == ::CFURLGetTypeID()) + { + new_value = ::CFURLCopyFileSystemPath((CFURLRef)cf_type, kCFURLPOSIXPathStyle); + } + } + reset(new_value); + return get(); +} + +CFStringRef +CFCString::SetFileSystemRepresentationAndExpandTilde (const char *path) +{ + std::string expanded_path; + if (CFCString::ExpandTildeInPath(path, expanded_path)) + SetFileSystemRepresentation(expanded_path.c_str()); + else + reset(); + return get(); +} + +const char * +CFCString::UTF8(std::string& str) +{ + return CFCString::UTF8(get(), str); +} + +// Static function that puts a copy of the UTF8 contents of CF_STR into STR +// and returns the C string pointer that is contained in STR when successful, else +// NULL is returned. This allows the std::string parameter to own the extracted string, +// and also allows that string to be returned as a C string pointer that can be used. + +const char * +CFCString::UTF8 (CFStringRef cf_str, std::string& str) +{ + if (cf_str) + { + const CFStringEncoding encoding = kCFStringEncodingUTF8; + CFIndex max_utf8_str_len = CFStringGetLength (cf_str); + max_utf8_str_len = CFStringGetMaximumSizeForEncoding (max_utf8_str_len, encoding); + if (max_utf8_str_len > 0) + { + str.resize(max_utf8_str_len); + if (!str.empty()) + { + if (CFStringGetCString (cf_str, &str[0], str.size(), encoding)) + { + str.resize(strlen(str.c_str())); + return str.c_str(); + } + } + } + } + return NULL; +} + +const char* +CFCString::ExpandTildeInPath(const char* path, std::string &expanded_path) +{ + glob_t globbuf; + if (::glob (path, GLOB_TILDE, NULL, &globbuf) == 0) + { + expanded_path = globbuf.gl_pathv[0]; + ::globfree (&globbuf); + } + else + expanded_path.clear(); + + return expanded_path.c_str(); +} + +// Static function that puts a copy of the file system representation of CF_STR +// into STR and returns the C string pointer that is contained in STR when +// successful, else NULL is returned. This allows the std::string parameter +// to own the extracted string, and also allows that string to be returned as +// a C string pointer that can be used. + +const char * +CFCString::FileSystemRepresentation (CFStringRef cf_str, std::string& str) +{ + if (cf_str) + { + CFIndex max_length = ::CFStringGetMaximumSizeOfFileSystemRepresentation (cf_str); + if (max_length > 0) + { + str.resize(max_length); + if (!str.empty()) + { + if (::CFStringGetFileSystemRepresentation (cf_str, &str[0], str.size())) + { + str.erase(::strlen(str.c_str())); + return str.c_str(); + } + } + } + } + str.erase(); + return NULL; +} + + +CFIndex +CFCString::GetLength() const +{ + CFStringRef str = get(); + if (str) + return CFStringGetLength (str); + return 0; +} diff --git a/source/Host/macosx/cfcpp/CFCString.h b/source/Host/macosx/cfcpp/CFCString.h new file mode 100644 index 000000000000..27c090313ece --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCString.h @@ -0,0 +1,41 @@ +//===-- CFCString.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFString_h_ +#define CoreFoundationCPP_CFString_h_ + +#include <iosfwd> + +#include "CFCReleaser.h" + +class CFCString : public CFCReleaser<CFStringRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCString (CFStringRef cf_str = NULL); + CFCString (const char *s, CFStringEncoding encoding = kCFStringEncodingUTF8); + CFCString (const CFCString& rhs); + CFCString& operator= (const CFCString& rhs); + virtual ~CFCString (); + + const char * GetFileSystemRepresentation (std::string& str); + CFStringRef SetFileSystemRepresentation (const char *path); + CFStringRef SetFileSystemRepresentationFromCFType (CFTypeRef cf_type); + CFStringRef SetFileSystemRepresentationAndExpandTilde (const char *path); + const char * UTF8 (std::string& str); + CFIndex GetLength() const; + static const char *UTF8 (CFStringRef cf_str, std::string& str); + static const char *FileSystemRepresentation (CFStringRef cf_str, std::string& str); + static const char *ExpandTildeInPath(const char* path, std::string &expanded_path); + +}; + +#endif // #ifndef CoreFoundationCPP_CFString_h_ diff --git a/source/Host/macosx/cfcpp/CoreFoundationCPP.h b/source/Host/macosx/cfcpp/CoreFoundationCPP.h new file mode 100644 index 000000000000..6843e2649cda --- /dev/null +++ b/source/Host/macosx/cfcpp/CoreFoundationCPP.h @@ -0,0 +1,30 @@ +//===-- CoreFoundationCPP.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// CoreFoundationCPP.h +// CoreFoundationCPP +// +// Created by Greg Clayton on 4/23/09. +// +// +//---------------------------------------------------------------------- + +#ifndef CoreFoundationCPP_CoreFoundationCPP_H_ +#define CoreFoundationCPP_CoreFoundationCPP_H_ + +#include <CoreFoundationCPP/CFCBundle.h> +#include <CoreFoundationCPP/CFCData.h> +#include <CoreFoundationCPP/CFCReleaser.h> +#include <CoreFoundationCPP/CFCMutableArray.h> +#include <CoreFoundationCPP/CFCMutableDictionary.h> +#include <CoreFoundationCPP/CFCMutableSet.h> +#include <CoreFoundationCPP/CFCString.h> + +#endif // CoreFoundationCPP_CoreFoundationCPP_H_ diff --git a/source/Host/netbsd/Makefile b/source/Host/netbsd/Makefile new file mode 100644 index 000000000000..2502cc49c15b --- /dev/null +++ b/source/Host/netbsd/Makefile @@ -0,0 +1,14 @@ +##===- source/Host/netbsd/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 := lldbHostNetBSD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Host/windows/Condition.cpp b/source/Host/windows/Condition.cpp new file mode 100644 index 000000000000..2f16ad77d7a3 --- /dev/null +++ b/source/Host/windows/Condition.cpp @@ -0,0 +1,98 @@ +//===-- Condition.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <errno.h> + +#include "lldb/Host/Condition.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Host/windows/windows.h" + + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +// +// The default constructor will initialize a new pthread condition +// and maintain the condition in the object state. +//---------------------------------------------------------------------- +Condition::Condition () : + m_condition() +{ + m_condition = static_cast<PCONDITION_VARIABLE>(malloc(sizeof(CONDITION_VARIABLE))); + InitializeConditionVariable(static_cast<PCONDITION_VARIABLE>(m_condition)); +} + +//---------------------------------------------------------------------- +// Destructor +// +// Destroys the pthread condition that the object owns. +//---------------------------------------------------------------------- +Condition::~Condition () +{ + free(m_condition); +} + +//---------------------------------------------------------------------- +// Unblock all threads waiting for a condition variable +//---------------------------------------------------------------------- +int +Condition::Broadcast () +{ + WakeAllConditionVariable(static_cast<PCONDITION_VARIABLE>(m_condition)); + return 0; +} + +//---------------------------------------------------------------------- +// Unblocks one thread waiting for the condition variable +//---------------------------------------------------------------------- +int +Condition::Signal () +{ + WakeConditionVariable(static_cast<PCONDITION_VARIABLE>(m_condition)); + return 0; +} + +//---------------------------------------------------------------------- +// The Wait() function atomically blocks the current thread +// waiting on the owned condition variable, and unblocks the mutex +// specified by "mutex". The waiting thread unblocks only after +// another thread calls Signal(), or Broadcast() with the same +// condition variable, or if "abstime" is valid (non-NULL) this +// function will return when the system time reaches the time +// specified in "abstime". If "abstime" is NULL this function will +// wait for an infinite amount of time for the condition variable +// to be signaled or broadcasted. +// +// The current thread re-acquires the lock on "mutex". +//---------------------------------------------------------------------- +int +Condition::Wait (Mutex &mutex, const TimeValue *abstime, bool *timed_out) +{ + DWORD wait = INFINITE; + if (abstime != NULL) { + int wval = (*abstime - TimeValue::Now()) / 1000000; + if (wval < 0) wval = 0; + + wait = wval; + } + + int err = SleepConditionVariableCS(static_cast<PCONDITION_VARIABLE>(m_condition), static_cast<PCRITICAL_SECTION>(mutex.m_mutex), wait); + + if (timed_out != NULL) + { + if ((err == 0) && GetLastError() == ERROR_TIMEOUT) + *timed_out = true; + else + *timed_out = false; + } + + return err == 0; +} + diff --git a/source/Host/windows/ConnectionGenericFileWindows.cpp b/source/Host/windows/ConnectionGenericFileWindows.cpp new file mode 100644 index 000000000000..eebf3d4f633c --- /dev/null +++ b/source/Host/windows/ConnectionGenericFileWindows.cpp @@ -0,0 +1,354 @@ +//===-- ConnectionGenericFileWindows.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Host/windows/ConnectionGenericFileWindows.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +namespace +{ +// This is a simple helper class to package up the information needed to return from a Read/Write +// operation function. Since there is a lot of code to be run before exit regardless of whether the +// operation succeeded or failed, combined with many possible return paths, this is the cleanest +// way to represent it. +class ReturnInfo +{ + public: + void + Set(size_t bytes, ConnectionStatus status, DWORD error_code) + { + m_error.SetError(error_code, eErrorTypeWin32); + m_bytes = bytes; + m_status = status; + } + + void + Set(size_t bytes, ConnectionStatus status, llvm::StringRef error_msg) + { + m_error.SetErrorString(error_msg.data()); + m_bytes = bytes; + m_status = status; + } + + size_t + GetBytes() const + { + return m_bytes; + } + ConnectionStatus + GetStatus() const + { + return m_status; + } + const Error & + GetError() const + { + return m_error; + } + + private: + Error m_error; + size_t m_bytes; + ConnectionStatus m_status; +}; +} + +ConnectionGenericFile::ConnectionGenericFile() + : m_file(INVALID_HANDLE_VALUE) + , m_owns_file(false) +{ + ::ZeroMemory(&m_overlapped, sizeof(m_overlapped)); + ::ZeroMemory(&m_file_position, sizeof(m_file_position)); + InitializeEventHandles(); +} + +ConnectionGenericFile::ConnectionGenericFile(lldb::file_t file, bool owns_file) + : m_file(file) + , m_owns_file(owns_file) +{ + ::ZeroMemory(&m_overlapped, sizeof(m_overlapped)); + ::ZeroMemory(&m_file_position, sizeof(m_file_position)); + InitializeEventHandles(); +} + +ConnectionGenericFile::~ConnectionGenericFile() +{ + if (m_owns_file && IsConnected()) + ::CloseHandle(m_file); + + ::CloseHandle(m_event_handles[kBytesAvailableEvent]); + ::CloseHandle(m_event_handles[kInterruptEvent]); +} + +void +ConnectionGenericFile::InitializeEventHandles() +{ + m_event_handles[kInterruptEvent] = CreateEvent(NULL, FALSE, FALSE, NULL); + + // Note, we should use a manual reset event for the hEvent argument of the OVERLAPPED. This + // is because both WaitForMultipleObjects and GetOverlappedResult (if you set the bWait + // argument to TRUE) will wait for the event to be signalled. If we use an auto-reset event, + // WaitForMultipleObjects will reset the event, return successfully, and then + // GetOverlappedResult will block since the event is no longer signalled. + m_event_handles[kBytesAvailableEvent] = ::CreateEvent(NULL, TRUE, FALSE, NULL); +} + +bool +ConnectionGenericFile::IsConnected() const +{ + return m_file && (m_file != INVALID_HANDLE_VALUE); +} + +lldb::ConnectionStatus +ConnectionGenericFile::Connect(const char *s, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionGenericFile::Connect (url = '%s')", static_cast<void *>(this), s); + + if (strstr(s, "file://") != s) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", s); + return eConnectionStatusError; + } + + if (IsConnected()) + { + ConnectionStatus status = Disconnect(error_ptr); + if (status != eConnectionStatusSuccess) + return status; + } + + // file://PATH + const char *path = s + strlen("file://"); + // Open the file for overlapped access. If it does not exist, create it. We open it overlapped + // so that we can issue asynchronous reads and then use WaitForMultipleObjects to allow the read + // to be interrupted by an event object. + m_file = ::CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL); + if (m_file == INVALID_HANDLE_VALUE) + { + if (error_ptr) + error_ptr->SetError(::GetLastError(), eErrorTypeWin32); + return eConnectionStatusError; + } + + m_owns_file = true; + m_uri.assign(s); + return eConnectionStatusSuccess; +} + +lldb::ConnectionStatus +ConnectionGenericFile::Disconnect(Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionGenericFile::Disconnect ()", static_cast<void *>(this)); + + if (!IsConnected()) + return eConnectionStatusSuccess; + + // Reset the handle so that after we unblock any pending reads, subsequent calls to Read() will + // see a disconnected state. + HANDLE old_file = m_file; + m_file = INVALID_HANDLE_VALUE; + + // Set the disconnect event so that any blocking reads unblock, then cancel any pending IO operations. + ::CancelIoEx(old_file, &m_overlapped); + + // Close the file handle if we owned it, but don't close the event handles. We could always + // reconnect with the same Connection instance. + if (m_owns_file) + ::CloseHandle(old_file); + + ::ZeroMemory(&m_file_position, sizeof(m_file_position)); + m_owns_file = false; + m_uri.clear(); + return eConnectionStatusSuccess; +} + +size_t +ConnectionGenericFile::Read(void *dst, size_t dst_len, uint32_t timeout_usec, lldb::ConnectionStatus &status, Error *error_ptr) +{ + ReturnInfo return_info; + BOOL result = 0; + DWORD bytes_read = 0; + + if (error_ptr) + error_ptr->Clear(); + + if (!IsConnected()) + { + return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE); + goto finish; + } + + m_overlapped.hEvent = m_event_handles[kBytesAvailableEvent]; + + result = ::ReadFile(m_file, dst, dst_len, NULL, &m_overlapped); + if (result || ::GetLastError() == ERROR_IO_PENDING) + { + if (!result) + { + // The expected return path. The operation is pending. Wait for the operation to complete + // or be interrupted. + TimeValue time_value; + time_value.OffsetWithMicroSeconds(timeout_usec); + DWORD milliseconds = time_value.milliseconds(); + DWORD wait_result = ::WaitForMultipleObjects(llvm::array_lengthof(m_event_handles), m_event_handles, FALSE, milliseconds); + // All of the events are manual reset events, so make sure we reset them to non-signalled. + switch (wait_result) + { + case WAIT_OBJECT_0 + kBytesAvailableEvent: + break; + case WAIT_OBJECT_0 + kInterruptEvent: + return_info.Set(0, eConnectionStatusInterrupted, 0); + goto finish; + case WAIT_TIMEOUT: + return_info.Set(0, eConnectionStatusTimedOut, 0); + goto finish; + case WAIT_FAILED: + return_info.Set(0, eConnectionStatusError, ::GetLastError()); + goto finish; + } + } + // The data is ready. Figure out how much was read and return; + if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_read, FALSE)) + { + DWORD result_error = ::GetLastError(); + // ERROR_OPERATION_ABORTED occurs when someone calls Disconnect() during a blocking read. + // This triggers a call to CancelIoEx, which causes the operation to complete and the + // result to be ERROR_OPERATION_ABORTED. + if (result_error == ERROR_HANDLE_EOF || result_error == ERROR_OPERATION_ABORTED || result_error == ERROR_BROKEN_PIPE) + return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0); + else + return_info.Set(bytes_read, eConnectionStatusError, result_error); + } + else if (bytes_read == 0) + return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0); + else + return_info.Set(bytes_read, eConnectionStatusSuccess, 0); + + goto finish; + } + else if (::GetLastError() == ERROR_BROKEN_PIPE) + { + // The write end of a pipe was closed. This is equivalent to EOF. + return_info.Set(0, eConnectionStatusEndOfFile, 0); + } + else + { + // An unknown error occurred. Fail out. + return_info.Set(0, eConnectionStatusError, ::GetLastError()); + } + goto finish; + +finish: + status = return_info.GetStatus(); + if (error_ptr) + *error_ptr = return_info.GetError(); + + // kBytesAvailableEvent is a manual reset event. Make sure it gets reset here so that any + // subsequent operations don't immediately see bytes available. + ResetEvent(m_event_handles[kBytesAvailableEvent]); + + IncrementFilePointer(return_info.GetBytes()); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + { + log->Printf("%" PRIxPTR " ConnectionGenericFile::Read() handle = %" PRIxPTR ", dst = %" PRIxPTR ", dst_len = %" PRIu64 + ") => %" PRIu64 ", error = %s", + this, m_file, dst, static_cast<uint64_t>(dst_len), static_cast<uint64_t>(return_info.GetBytes()), + return_info.GetError().AsCString()); + } + + return return_info.GetBytes(); +} + +size_t +ConnectionGenericFile::Write(const void *src, size_t src_len, lldb::ConnectionStatus &status, Error *error_ptr) +{ + ReturnInfo return_info; + DWORD bytes_written = 0; + BOOL result = 0; + + if (error_ptr) + error_ptr->Clear(); + + if (!IsConnected()) + { + return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE); + goto finish; + } + + m_overlapped.hEvent = NULL; + + // Writes are not interruptible like reads are, so just block until it's done. + result = ::WriteFile(m_file, src, src_len, NULL, &m_overlapped); + if (!result && ::GetLastError() != ERROR_IO_PENDING) + { + return_info.Set(0, eConnectionStatusError, ::GetLastError()); + goto finish; + } + + if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_written, TRUE)) + { + return_info.Set(bytes_written, eConnectionStatusError, ::GetLastError()); + goto finish; + } + + return_info.Set(bytes_written, eConnectionStatusSuccess, 0); + goto finish; + +finish: + status = return_info.GetStatus(); + if (error_ptr) + *error_ptr = return_info.GetError(); + + IncrementFilePointer(return_info.GetBytes()); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + { + log->Printf("%" PRIxPTR " ConnectionGenericFile::Write() handle = %" PRIxPTR ", src = %" PRIxPTR ", src_len = %" PRIu64 + ") => %" PRIu64 ", error = %s", + this, m_file, src, static_cast<uint64_t>(src_len), static_cast<uint64_t>(return_info.GetBytes()), + return_info.GetError().AsCString()); + } + return return_info.GetBytes(); +} + +std::string +ConnectionGenericFile::GetURI() +{ + return m_uri; +} + +bool +ConnectionGenericFile::InterruptRead() +{ + return ::SetEvent(m_event_handles[kInterruptEvent]); +} + +void +ConnectionGenericFile::IncrementFilePointer(DWORD amount) +{ + LARGE_INTEGER old_pos; + old_pos.HighPart = m_overlapped.OffsetHigh; + old_pos.LowPart = m_overlapped.Offset; + old_pos.QuadPart += amount; + m_overlapped.Offset = old_pos.LowPart; + m_overlapped.OffsetHigh = old_pos.HighPart; +} diff --git a/source/Host/windows/EditLineWin.cpp b/source/Host/windows/EditLineWin.cpp new file mode 100644 index 000000000000..55fe52dc8ccd --- /dev/null +++ b/source/Host/windows/EditLineWin.cpp @@ -0,0 +1,435 @@ +//===-- EditLineWin.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// this file is only relevant for Visual C++ +#if defined( _WIN32 ) + +#include "lldb/Host/windows/windows.h" + +#include "lldb/Host/windows/editlinewin.h" +#include <vector> +#include <assert.h> + +// edit line EL_ADDFN function pointer type +typedef unsigned char(*el_addfn_func)(EditLine *e, int ch); +typedef const char* (*el_prompt_func)(EditLine *); + +// edit line wrapper binding container +struct el_binding +{ + // + const char *name; + const char *help; + // function pointer to callback routine + el_addfn_func func; + // ascii key this function is bound to + const char *key; +}; + +// stored key bindings +static std::vector<el_binding*> _bindings; + +//TODO: this should in fact be related to the exact edit line context we create +static void *clientData = NULL; + +// store the current prompt string +// default to what we expect to receive anyway +static const char *_prompt = "(lldb) "; + +#if !defined( _WIP_INPUT_METHOD ) + +static char * +el_get_s (char *buffer, int chars) +{ + return gets_s(buffer, chars); +} +#else + +static void +con_output (char _in) +{ + HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE ); + DWORD written = 0; + // get the cursor position + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo( hout, &info ); + // output this char + WriteConsoleOutputCharacterA( hout, &_in, 1, info.dwCursorPosition, &written ); + // advance cursor position + info.dwCursorPosition.X++; + SetConsoleCursorPosition( hout, info.dwCursorPosition ); +} + +static void +con_backspace (void) +{ + HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE ); + DWORD written = 0; + // get cursor position + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo( hout, &info ); + // nudge cursor backwards + info.dwCursorPosition.X--; + SetConsoleCursorPosition( hout, info.dwCursorPosition ); + // blank out the last character + WriteConsoleOutputCharacterA( hout, " ", 1, info.dwCursorPosition, &written ); +} + +static void +con_return (void) +{ + HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE ); + DWORD written = 0; + // get cursor position + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo( hout, &info ); + // move onto the new line + info.dwCursorPosition.X = 0; + info.dwCursorPosition.Y++; + SetConsoleCursorPosition( hout, info.dwCursorPosition ); +} + +static bool +runBind (char _key) +{ + for ( int i=0; i<_bindings.size(); i++ ) + { + el_binding *bind = _bindings[i]; + if ( bind->key[0] == _key ) + { + bind->func( (EditLine*) -1, _key ); + return true; + } + } + return false; +} + +// replacement get_s which is EL_BIND aware +static char * +el_get_s (char *buffer, int chars) +{ + // + char *head = buffer; + // + for ( ;; Sleep( 10 ) ) + { + // + INPUT_RECORD _record; + // + DWORD _read = 0; + if ( ReadConsoleInputA( GetStdHandle( STD_INPUT_HANDLE ), &_record, 1, &_read ) == FALSE ) + break; + // if we didn't read a key + if ( _read == 0 ) + continue; + // only interested in key events + if ( _record.EventType != KEY_EVENT ) + continue; + // is the key down + if (! _record.Event.KeyEvent.bKeyDown ) + continue; + // read the ascii key character + char _key = _record.Event.KeyEvent.uChar.AsciiChar; + // non ascii conformant key press + if ( _key == 0 ) + { + // check the scan code + // if VK_UP scroll back through history + // if VK_DOWN scroll forward through history + continue; + } + // try to execute any bind this key may have + if ( runBind( _key ) ) + continue; + // if we read a return key + if ( _key == '\n' || _key == '\r' ) + { + con_return( ); + break; + } + // key is backspace + if ( _key == 0x8 ) + { + // avoid deleting past beginning + if ( head > buffer ) + { + con_backspace( ); + head--; + } + continue; + } + + // add this key to the input buffer + if ( (head-buffer) < (chars-1) ) + { + con_output( _key ); + *(head++) = _key; + } + } + // insert end of line character + *head = '\0'; + + return buffer; +} +#endif + +// edit line initialize +EditLine * +el_init (const char *, FILE *, FILE *, FILE *) +{ + // + SetConsoleTitleA( "lldb" ); + // return dummy handle + return (EditLine*) -1; +} + +const char * +el_gets (EditLine *el, int *length) +{ + // print the prompt if we have one + if ( _prompt != NULL ) + printf("%s", _prompt); + // create a buffer for the user input + char *buffer = new char[ MAX_PATH ]; + // try to get user input string + if ( el_get_s( buffer, MAX_PATH ) ) + { + // get the string length in 'length' + while ( buffer[ *length ] != '\0' ) + (*length)++; + // return the input buffer + // remember that this memory has the be free'd somewhere + return buffer; + } + else + { + // on error + delete [] buffer; + return NULL; + } +} + +int +el_set (EditLine *el, int code, ...) +{ + va_list vl; + va_start(vl, code); + // + switch ( code ) + { + // edit line set prompt message + case ( EL_PROMPT ): + { + // EL_PROMPT, char *(*f)( EditLine *) + // define a prompt printing function as 'f', which is to return a string that + // contains the prompt. + + // get the function pointer from the arg list + void *func_vp = (void*)va_arg(vl, el_prompt_func); + // cast to suitable prototype + el_prompt_func func_fp = (el_prompt_func)func_vp; + // call to get the prompt as a string + _prompt = func_fp( el ); + } + break; + + case (EL_PROMPT_ESC) : + { + // EL_PROMPT, char *(*f)( EditLine *) + // define a prompt printing function as 'f', which is to return a string that + // contains the prompt. + + // get the function pointer from the arg list + void *func_vp = (void*)va_arg(vl, el_prompt_func); + va_arg(vl, int); + // call to get the prompt as a string + el_prompt_func func_fp = (el_prompt_func)func_vp; + _prompt = func_fp(el); + } + break; + + case ( EL_EDITOR ): + { + // EL_EDITOR, const char *mode + // set editing mode to "emacs" or "vi" + } + break; + case ( EL_HIST ): + { + // EL_HIST, History *(*fun)(History *, int op, ... ), const char *ptr + // defines which history function to use, which is usually history(). Ptr should be the + // value returned by history_init(). + } + break; + case ( EL_ADDFN ): + { + // EL_ADDFN, const char *name, const char *help, unsigned char (*func)(EditLine *e, int ch) + // add a user defined function, func), referred to as 'name' which is invoked when a key which is bound to 'name' is + // entered. 'help' is a description of 'name'. at invocation time, 'ch' is the key which caused the invocation. the + // return value of 'func()' should be one of: + // CC_NORM add a normal character + // CC_NEWLINE end of line was entered + // CC_EOF EOF was entered + // CC_ARGHACK expecting further command input as arguments, do nothing visually. + // CC_REFRESH refresh display. + // CC_REFRESH_BEEP refresh display and beep. + // CC_CURSOR cursor moved so update and perform CC_REFRESH + // CC_REDISPLAY redisplay entire input line. this is useful if a key binding outputs extra information. + // CC_ERROR an error occurred. beep and flush tty. + // CC_FATAL fatal error, reset tty to known state. + + el_binding *binding = new el_binding; + binding->name = va_arg( vl, const char *); + binding->help = va_arg( vl, const char *); + binding->func = va_arg( vl, el_addfn_func ); + binding->key = 0; + // add this to the bindings list + _bindings.push_back( binding ); + } + break; + case ( EL_BIND ): + { + // EL_BIND, const char *, ..., NULL + // perform the BIND built-in command. Refer to editrc(5) for more information. + + const char *name = va_arg( vl, const char* ); + + for ( int i=0; i<_bindings.size(); i++ ) + { + el_binding *bind = _bindings[i]; + if ( strcmp( bind->name, name ) == 0 ) + { + bind->key = va_arg( vl, const char * ); + break; + } + } + + } + break; + case ( EL_CLIENTDATA ): + { + clientData = va_arg(vl, void*); + } + break; + } + return 0; +} + +void +el_end (EditLine *el) +{ + //assert( !"Not implemented!" ); +} + +void +el_reset (EditLine *) +{ + assert( !"Not implemented!" ); +} + +int +el_getc (EditLine *, char *) +{ + assert( !"Not implemented!" ); + return 0; +} + +void +el_push (EditLine *, const char *) +{ +} + +void +el_beep (EditLine *) +{ + Beep( 1000, 500 ); +} + +int +el_parse (EditLine *, int, const char **) +{ + assert( !"Not implemented!" ); + return 0; +} + +int +el_get (EditLine *el, int code, ...) +{ + va_list vl; + va_start( vl, code ); + + switch ( code ) + { + case ( EL_CLIENTDATA ): + { + void **dout = va_arg( vl, void** ); + *dout = clientData; + } + break; + default: + assert( !"Not implemented!" ); + } + return 0; +} + +int +el_source (EditLine *el, const char *file) +{ + // init edit line by reading the contents of 'file' + // nothing to do here on windows... + return 0; +} + +void +el_resize (EditLine *) +{ + assert( !"Not implemented!" ); +} + +const LineInfo * +el_line (EditLine *el) +{ + return 0; +} + +int +el_insertstr (EditLine *, const char *) +{ +// assert( !"Not implemented!" ); + return 0; +} + +void +el_deletestr (EditLine *, int) +{ + assert( !"Not implemented!" ); +} + +History * +history_init (void) +{ + // return dummy handle + return (History*) -1; +} + +void +history_end (History *) +{ +// assert( !"Not implemented!" ); +} + +int +history (History *, HistEvent *, int op, ...) +{ + // perform operation 'op' on the history list with + // optional arguments as needed by the operation. + return 0; +} + +#endif diff --git a/source/Host/windows/FileSystem.cpp b/source/Host/windows/FileSystem.cpp new file mode 100644 index 000000000000..2cca12b92711 --- /dev/null +++ b/source/Host/windows/FileSystem.cpp @@ -0,0 +1,221 @@ +//===-- FileSystem.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/windows/windows.h" + +#include <shellapi.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/windows/AutoHandle.h" +#include "llvm/Support/FileSystem.h" + +using namespace lldb_private; + +const char * +FileSystem::DEV_NULL = "nul"; + +FileSpec::PathSyntax +FileSystem::GetNativePathSyntax() +{ + return FileSpec::ePathSyntaxWindows; +} + +Error +FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions) +{ + // On Win32, the mode parameter is ignored, as Windows files and directories support a + // different permission model than POSIX. + Error error; + const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true); + if (err_code) + { + error.SetErrorString(err_code.message().c_str()); + } + + return error; +} + +Error +FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse) +{ + Error error; + if (!recurse) + { + BOOL result = ::RemoveDirectory(file_spec.GetCString()); + if (!result) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + } + else + { + // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to + // indicate the end of the list. + std::string path_buffer{file_spec.GetPath()}; + path_buffer.push_back(0); + + SHFILEOPSTRUCT shfos = {0}; + shfos.wFunc = FO_DELETE; + shfos.pFrom = path_buffer.c_str(); + shfos.fFlags = FOF_NO_UI; + + int result = ::SHFileOperation(&shfos); + // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values. + if (result != 0) + error.SetErrorStringWithFormat("SHFileOperation failed"); + } + return error; +} + +Error +FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) +{ + Error error; + // Beware that Windows's permission model is different from Unix's, and it's + // not clear if this API is supposed to check ACLs. To match the caller's + // expectations as closely as possible, we'll use Microsoft's _stat, which + // attempts to emulate POSIX stat. This should be good enough for basic + // checks like FileSpec::Readable. + struct _stat file_stats; + if (::_stat(file_spec.GetCString(), &file_stats) == 0) + { + // The owner permission bits in "st_mode" currently match the definitions + // for the owner file mode bits. + file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC); + } + else + { + error.SetErrorToErrno(); + } + + return error; +} + +Error +FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) +{ + Error error; + error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__); + return error; +} + +lldb::user_id_t +FileSystem::GetFileSize(const FileSpec &file_spec) +{ + return file_spec.GetByteSize(); +} + +bool +FileSystem::GetFileExists(const FileSpec &file_spec) +{ + return file_spec.Exists(); +} + +Error +FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) +{ + Error error; + if (!::CreateHardLink(src.GetCString(), dst.GetCString(), nullptr)) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; +} + +int +FileSystem::GetHardlinkCount(const FileSpec &file_spec) +{ + HANDLE file_handle = ::CreateFile(file_spec.GetCString(), + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if (file_handle == INVALID_HANDLE_VALUE) + return -1; + + AutoHandle auto_file_handle(file_handle); + BY_HANDLE_FILE_INFORMATION file_info; + if (::GetFileInformationByHandle(file_handle, &file_info)) + return file_info.nNumberOfLinks; + + return -1; +} + +Error +FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) +{ + Error error; + DWORD attrib = ::GetFileAttributes(dst.GetCString()); + if (attrib == INVALID_FILE_ATTRIBUTES) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY); + DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; + BOOL result = ::CreateSymbolicLink(src.GetCString(), dst.GetCString(), flag); + if (!result) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; +} + +Error +FileSystem::Unlink(const FileSpec &file_spec) +{ + Error error; + BOOL result = ::DeleteFile(file_spec.GetCString()); + if (!result) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; +} + +Error +FileSystem::Readlink(const FileSpec &src, FileSpec &dst) +{ + Error error; + HANDLE h = ::CreateFile(src.GetCString(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT, NULL); + if (h == INVALID_HANDLE_VALUE) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + + char buf[PATH_MAX]; + // Subtract 1 from the path length since this function does not add a null terminator. + DWORD result = ::GetFinalPathNameByHandle(h, buf, sizeof(buf) - 1, + FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + if (result == 0) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + else + dst.SetFile(buf, false); + + ::CloseHandle(h); + return error; +} + +Error +FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) +{ + return Error("ResolveSymbolicLink() isn't implemented on Windows"); +} + +bool +FileSystem::IsLocal(const FileSpec &spec) +{ + if (spec) + { + // TODO: return true if the file is on a locally mounted file system + return true; + } + + return false; +} diff --git a/source/Host/windows/Host.cpp b/source/Host/windows/Host.cpp new file mode 100644 index 000000000000..2c9a139df256 --- /dev/null +++ b/source/Host/windows/Host.cpp @@ -0,0 +1,326 @@ +//===-- source/Host/windows/Host.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <stdio.h> +#include "lldb/Host/windows/windows.h" +#include "lldb/Host/windows/AutoHandle.h" + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" + +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StructuredData.h" + +// Windows includes +#include <TlHelp32.h> + +using namespace lldb; +using namespace lldb_private; + +namespace +{ + bool GetTripleForProcess(const FileSpec &executable, llvm::Triple &triple) + { + // Open the PE File as a binary file, and parse just enough information to determine the + // machine type. + File imageBinary( + executable.GetPath().c_str(), + File::eOpenOptionRead, + lldb::eFilePermissionsUserRead); + imageBinary.SeekFromStart(0x3c); + int32_t peOffset = 0; + uint32_t peHead = 0; + uint16_t machineType = 0; + size_t readSize = sizeof(peOffset); + imageBinary.Read(&peOffset, readSize); + imageBinary.SeekFromStart(peOffset); + imageBinary.Read(&peHead, readSize); + if (peHead != 0x00004550) // "PE\0\0", little-endian + return false; // Error: Can't find PE header + readSize = 2; + imageBinary.Read(&machineType, readSize); + triple.setVendor(llvm::Triple::PC); + triple.setOS(llvm::Triple::Win32); + triple.setArch(llvm::Triple::UnknownArch); + if (machineType == 0x8664) + triple.setArch(llvm::Triple::x86_64); + else if (machineType == 0x14c) + triple.setArch(llvm::Triple::x86); + + return true; + } + + bool GetExecutableForProcess(const AutoHandle &handle, std::string &path) + { + // Get the process image path. MAX_PATH isn't long enough, paths can actually be up to 32KB. + std::vector<char> buffer(32768); + DWORD dwSize = buffer.size(); + if (!::QueryFullProcessImageNameA(handle.get(), 0, &buffer[0], &dwSize)) + return false; + path.assign(&buffer[0]); + return true; + } + + void GetProcessExecutableAndTriple(const AutoHandle &handle, ProcessInstanceInfo &process) + { + // We may not have permissions to read the path from the process. So start off by + // setting the executable file to whatever Toolhelp32 gives us, and then try to + // enhance this with more detailed information, but fail gracefully. + std::string executable; + llvm::Triple triple; + triple.setVendor(llvm::Triple::PC); + triple.setOS(llvm::Triple::Win32); + triple.setArch(llvm::Triple::UnknownArch); + if (GetExecutableForProcess(handle, executable)) + { + FileSpec executableFile(executable.c_str(), false); + process.SetExecutableFile(executableFile, true); + GetTripleForProcess(executableFile, triple); + } + process.SetArchitecture(ArchSpec(triple)); + + // TODO(zturner): Add the ability to get the process user name. + } +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ + return 0; +} + +lldb::tid_t +Host::GetCurrentThreadID() +{ + return lldb::tid_t(::GetCurrentThreadId()); +} + +lldb::thread_t +Host::GetCurrentThread () +{ + return lldb::thread_t(::GetCurrentThread()); +} + +lldb::thread_key_t +Host::ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback) +{ + return TlsAlloc(); +} + +void* +Host::ThreadLocalStorageGet(lldb::thread_key_t key) +{ + return ::TlsGetValue (key); +} + +void +Host::ThreadLocalStorageSet(lldb::thread_key_t key, void *value) +{ + ::TlsSetValue (key, value); +} + +void +Host::Kill(lldb::pid_t pid, int signo) +{ + TerminateProcess((HANDLE) pid, 1); +} + + +const char * +Host::GetSignalAsCString(int signo) +{ + return NULL; +} + +FileSpec +Host::GetModuleFileSpecForHostAddress (const void *host_addr) +{ + FileSpec module_filespec; + + HMODULE hmodule = NULL; + if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)host_addr, &hmodule)) + return module_filespec; + + std::vector<char> buffer(MAX_PATH); + DWORD chars_copied = 0; + do { + chars_copied = ::GetModuleFileName(hmodule, &buffer[0], buffer.size()); + if (chars_copied == buffer.size() && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) + buffer.resize(buffer.size() * 2); + } while (chars_copied >= buffer.size()); + + module_filespec.SetFile(&buffer[0], false); + return module_filespec; +} + +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + process_infos.Clear(); + + AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); + if (!snapshot.IsValid()) + return 0; + + PROCESSENTRY32 pe = {0}; + pe.dwSize = sizeof(PROCESSENTRY32); + if (Process32First(snapshot.get(), &pe)) + { + do + { + AutoHandle handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID), nullptr); + + ProcessInstanceInfo process; + process.SetExecutableFile(FileSpec(pe.szExeFile, false), true); + process.SetProcessID(pe.th32ProcessID); + process.SetParentProcessID(pe.th32ParentProcessID); + GetProcessExecutableAndTriple(handle, process); + + if (match_info.MatchAllProcesses() || match_info.Matches(process)) + process_infos.Append(process); + } while (Process32Next(snapshot.get(), &pe)); + } + return process_infos.GetSize(); +} + +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + process_info.Clear(); + + AutoHandle handle(::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid), + nullptr); + if (!handle.IsValid()) + return false; + + process_info.SetProcessID(pid); + GetProcessExecutableAndTriple(handle, process_info); + + // Need to read the PEB to get parent process and command line arguments. + return true; +} + +HostThread +Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals) +{ + return HostThread(); +} + +Error +Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) +{ + Error error; + if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) + { + FileSpec expand_tool_spec; + if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, expand_tool_spec)) + { + error.SetErrorString("could not find support executable directory for the lldb-argdumper tool"); + return error; + } + expand_tool_spec.AppendPathComponent("lldb-argdumper.exe"); + if (!expand_tool_spec.Exists()) + { + error.SetErrorString("could not find the lldb-argdumper tool"); + return error; + } + + std::string quoted_cmd_string; + launch_info.GetArguments().GetQuotedCommandString(quoted_cmd_string); + std::replace(quoted_cmd_string.begin(), quoted_cmd_string.end(), '\\', '/'); + StreamString expand_command; + + expand_command.Printf("\"%s\" %s", + expand_tool_spec.GetPath().c_str(), + quoted_cmd_string.c_str()); + + int status; + std::string output; + RunShellCommand(expand_command.GetData(), launch_info.GetWorkingDirectory(), &status, nullptr, &output, 10); + + if (status != 0) + { + error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", status); + return error; + } + + auto data_sp = StructuredData::ParseJSON(output); + if (!data_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto dict_sp = data_sp->GetAsDictionary(); + if (!data_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments"); + if (!args_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto args_array_sp = args_sp->GetAsArray(); + if (!args_array_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + launch_info.GetArguments().Clear(); + + for (size_t i = 0; + i < args_array_sp->GetSize(); + i++) + { + auto item_sp = args_array_sp->GetItemAtIndex(i); + if (!item_sp) + continue; + auto str_sp = item_sp->GetAsString(); + if (!str_sp) + continue; + + launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str()); + } + } + + return error; +} + +size_t +Host::GetEnvironment(StringList &env) +{ + // The environment block on Windows is a contiguous buffer of NULL terminated strings, + // where the end of the environment block is indicated by two consecutive NULLs. + LPCH environment_block = ::GetEnvironmentStrings(); + env.Clear(); + while (*environment_block != '\0') + { + llvm::StringRef current_var(environment_block); + if (current_var[0] != '=') + env.AppendString(current_var); + + environment_block += current_var.size()+1; + } + return env.GetSize(); +} diff --git a/source/Host/windows/HostInfoWindows.cpp b/source/Host/windows/HostInfoWindows.cpp new file mode 100644 index 000000000000..6dce71d9172a --- /dev/null +++ b/source/Host/windows/HostInfoWindows.cpp @@ -0,0 +1,118 @@ +//===-- HostInfoWindows.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/windows/windows.h" + +#include <mutex> // std::once + +#include "lldb/Host/windows/HostInfoWindows.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" + +using namespace lldb_private; + +FileSpec HostInfoWindows::m_program_filespec; + +size_t +HostInfoWindows::GetPageSize() +{ + SYSTEM_INFO systemInfo; + GetNativeSystemInfo(&systemInfo); + return systemInfo.dwPageSize; +} + +bool +HostInfoWindows::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) +{ + OSVERSIONINFOEX info; + + ZeroMemory(&info, sizeof(OSVERSIONINFOEX)); + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); +#pragma warning(push) +#pragma warning(disable : 4996) + // Starting with Microsoft SDK for Windows 8.1, this function is deprecated in favor of the + // new Windows Version Helper APIs. Since we don't specify a minimum SDK version, it's easier + // to simply disable the warning rather than try to support both APIs. + if (GetVersionEx((LPOSVERSIONINFO)&info) == 0) + { + return false; + } +#pragma warning(pop) + + major = info.dwMajorVersion; + minor = info.dwMinorVersion; + update = info.wServicePackMajor; + + return true; +} + +bool +HostInfoWindows::GetOSBuildString(std::string &s) +{ + s.clear(); + uint32_t major, minor, update; + if (!GetOSVersion(major, minor, update)) + return false; + + llvm::raw_string_ostream stream(s); + stream << "Windows NT " << major << "." << minor << "." << update; + return true; +} + +bool +HostInfoWindows::GetOSKernelDescription(std::string &s) +{ + return GetOSBuildString(s); +} + +bool +HostInfoWindows::GetHostname(std::string &s) +{ + char buffer[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1; + if (!::GetComputerName(buffer, &dwSize)) + return false; + + s.assign(buffer, buffer + dwSize); + return true; +} + +FileSpec +HostInfoWindows::GetProgramFileSpec() +{ + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + char buffer[PATH_MAX]; + ::GetModuleFileName(NULL, buffer, sizeof(buffer)); + m_program_filespec.SetFile(buffer, false); + }); + return m_program_filespec; +} + +FileSpec +HostInfoWindows::GetDefaultShell() +{ + return FileSpec(::getenv("ComSpec"), false); +} + +bool +HostInfoWindows::ComputePythonDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + llvm::SmallString<64> path(lldb_file_spec.GetDirectory().AsCString()); + llvm::sys::path::remove_filename(path); + llvm::sys::path::append(path, "lib", "site-packages"); + std::replace(path.begin(), path.end(), '\\', '/'); + file_spec.GetDirectory().SetString(path.c_str()); + return true; +} diff --git a/source/Host/windows/HostProcessWindows.cpp b/source/Host/windows/HostProcessWindows.cpp new file mode 100644 index 000000000000..0f81c18d34af --- /dev/null +++ b/source/Host/windows/HostProcessWindows.cpp @@ -0,0 +1,137 @@ +//===-- HostProcessWindows.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Host/windows/HostProcessWindows.h" + +#include "llvm/ADT/STLExtras.h" + +#include <Psapi.h> + +using namespace lldb_private; + +namespace +{ +struct MonitorInfo +{ + HostProcess::MonitorCallback callback; + void *baton; + HANDLE process_handle; +}; +} + +HostProcessWindows::HostProcessWindows() + : HostNativeProcessBase() + , m_owns_handle(true) +{ +} + +HostProcessWindows::HostProcessWindows(lldb::process_t process) + : HostNativeProcessBase(process) + , m_owns_handle(true) +{ +} + +HostProcessWindows::~HostProcessWindows() +{ + Close(); +} + +void +HostProcessWindows::SetOwnsHandle(bool owns) +{ + m_owns_handle = owns; +} + +Error HostProcessWindows::Terminate() +{ + Error error; + if (m_process == nullptr) + error.SetError(ERROR_INVALID_HANDLE, lldb::eErrorTypeWin32); + + if (!::TerminateProcess(m_process, 0)) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + + return error; +} + +Error HostProcessWindows::GetMainModule(FileSpec &file_spec) const +{ + Error error; + if (m_process == nullptr) + error.SetError(ERROR_INVALID_HANDLE, lldb::eErrorTypeWin32); + + char path[MAX_PATH] = { 0 }; + if (::GetProcessImageFileName(m_process, path, llvm::array_lengthof(path))) + file_spec.SetFile(path, false); + else + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + + return error; +} + +lldb::pid_t HostProcessWindows::GetProcessId() const +{ + return (m_process == LLDB_INVALID_PROCESS) ? -1 : ::GetProcessId(m_process); +} + +bool HostProcessWindows::IsRunning() const +{ + if (m_process == nullptr) + return false; + + DWORD code = 0; + if (!::GetExitCodeProcess(m_process, &code)) + return false; + + return (code == STILL_ACTIVE); +} + +HostThread +HostProcessWindows::StartMonitoring(HostProcess::MonitorCallback callback, void *callback_baton, bool monitor_signals) +{ + HostThread monitor_thread; + MonitorInfo *info = new MonitorInfo; + info->callback = callback; + info->baton = callback_baton; + + // Since the life of this HostProcessWindows instance and the life of the process may be different, duplicate the handle so that + // the monitor thread can have ownership over its own copy of the handle. + HostThread result; + if (::DuplicateHandle(GetCurrentProcess(), m_process, GetCurrentProcess(), &info->process_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) + result = ThreadLauncher::LaunchThread("ChildProcessMonitor", HostProcessWindows::MonitorThread, info, nullptr); + return result; +} + +lldb::thread_result_t +HostProcessWindows::MonitorThread(void *thread_arg) +{ + DWORD exit_code; + + MonitorInfo *info = static_cast<MonitorInfo *>(thread_arg); + if (info) + { + ::WaitForSingleObject(info->process_handle, INFINITE); + ::GetExitCodeProcess(info->process_handle, &exit_code); + info->callback(info->baton, ::GetProcessId(info->process_handle), true, 0, exit_code); + ::CloseHandle(info->process_handle); + delete (info); + } + return 0; +} + +void HostProcessWindows::Close() +{ + if (m_owns_handle && m_process != LLDB_INVALID_PROCESS) + ::CloseHandle(m_process); + m_process = nullptr; +} diff --git a/source/Host/windows/HostThreadWindows.cpp b/source/Host/windows/HostThreadWindows.cpp new file mode 100644 index 000000000000..d59064fb1c15 --- /dev/null +++ b/source/Host/windows/HostThreadWindows.cpp @@ -0,0 +1,98 @@ +//===-- HostThreadWindows.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Error.h" + +#include "lldb/Host/windows/windows.h" +#include "lldb/Host/windows/HostThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +namespace +{ +void __stdcall ExitThreadProxy(ULONG_PTR dwExitCode) +{ + ::ExitThread(dwExitCode); +} +} + +HostThreadWindows::HostThreadWindows() + : HostNativeThreadBase() + , m_owns_handle(true) +{ +} + +HostThreadWindows::HostThreadWindows(lldb::thread_t thread) + : HostNativeThreadBase(thread) + , m_owns_handle(true) +{ +} + +HostThreadWindows::~HostThreadWindows() +{ + Reset(); +} + +void +HostThreadWindows::SetOwnsHandle(bool owns) +{ + m_owns_handle = owns; +} + +Error +HostThreadWindows::Join(lldb::thread_result_t *result) +{ + Error error; + if (IsJoinable()) + { + DWORD wait_result = ::WaitForSingleObject(m_thread, INFINITE); + if (WAIT_OBJECT_0 == wait_result && result) + { + DWORD exit_code = 0; + if (!::GetExitCodeThread(m_thread, &exit_code)) + *result = 0; + *result = exit_code; + } + else if (WAIT_OBJECT_0 != wait_result) + error.SetError(::GetLastError(), eErrorTypeWin32); + } + else + error.SetError(ERROR_INVALID_HANDLE, eErrorTypeWin32); + + Reset (); + return error; +} + +Error +HostThreadWindows::Cancel() +{ + Error error; + + DWORD result = ::QueueUserAPC(::ExitThreadProxy, m_thread, 0); + error.SetError(result, eErrorTypeWin32); + return error; +} + +lldb::tid_t +HostThreadWindows::GetThreadId() const +{ + return ::GetThreadId(m_thread); +} + +void +HostThreadWindows::Reset() +{ + if (m_owns_handle && m_thread != LLDB_INVALID_HOST_THREAD) + ::CloseHandle(m_thread); + + HostNativeThreadBase::Reset(); +} diff --git a/source/Host/windows/LockFileWindows.cpp b/source/Host/windows/LockFileWindows.cpp new file mode 100644 index 000000000000..161d3dbd8260 --- /dev/null +++ b/source/Host/windows/LockFileWindows.cpp @@ -0,0 +1,93 @@ +//===-- LockFileWindows.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/windows/LockFileWindows.h" + +#include <io.h> + +using namespace lldb; +using namespace lldb_private; + +namespace +{ + +Error fileLock (HANDLE file_handle, DWORD flags, const uint64_t start, const uint64_t len) +{ + if (start != 0) + return Error ("Non-zero start lock regions are not supported"); + + OVERLAPPED overlapped = {0}; + + if (!::LockFileEx (file_handle, flags, 0, len, 0, &overlapped) && ::GetLastError () != ERROR_IO_PENDING) + return Error (::GetLastError (), eErrorTypeWin32); + + DWORD bytes; + if (!::GetOverlappedResult (file_handle, &overlapped, &bytes, TRUE)) + return Error (::GetLastError (), eErrorTypeWin32); + + return Error (); +} + +} // namespace + +LockFileWindows::LockFileWindows (int fd) + : LockFileBase (fd), + m_file (reinterpret_cast<HANDLE> (_get_osfhandle (fd))) +{ +} + +LockFileWindows::~LockFileWindows () +{ + Unlock (); +} + +bool +LockFileWindows::IsValidFile () const +{ + return LockFileBase::IsValidFile() && m_file != INVALID_HANDLE_VALUE; +} + +Error +LockFileWindows::DoWriteLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_file, LOCKFILE_EXCLUSIVE_LOCK, start, len); +} + +Error +LockFileWindows::DoTryWriteLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, start, len); +} + +Error +LockFileWindows::DoReadLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_file, 0, start, len); +} + +Error +LockFileWindows::DoTryReadLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_file, LOCKFILE_FAIL_IMMEDIATELY, start, len); +} + +Error +LockFileWindows::DoUnlock () +{ + OVERLAPPED overlapped = {0}; + + if (!::UnlockFileEx (m_file, 0, m_len, 0, &overlapped) && ::GetLastError () != ERROR_IO_PENDING) + return Error (::GetLastError (), eErrorTypeWin32); + + DWORD bytes; + if (!::GetOverlappedResult (m_file, &overlapped, &bytes, TRUE)) + return Error (::GetLastError (), eErrorTypeWin32); + + return Error (); +} diff --git a/source/Host/windows/Mutex.cpp b/source/Host/windows/Mutex.cpp new file mode 100644 index 000000000000..ed90f46eb54b --- /dev/null +++ b/source/Host/windows/Mutex.cpp @@ -0,0 +1,109 @@ +//===-- Mutex.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/windows/windows.h" + +#include <string.h> +#include <stdio.h> + +#if 0 +// This logging is way too verbose to enable even for a log channel. +// This logging can be enabled by changing the "#if 0", but should be +// reverted prior to checking in. +#include <cstdio> +#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_LOG(fmt, ...) +#endif + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor. +// +// Creates a pthread mutex with no attributes. +//---------------------------------------------------------------------- +Mutex::Mutex () : + m_mutex() +{ + m_mutex = static_cast<PCRITICAL_SECTION>(malloc(sizeof(CRITICAL_SECTION))); + InitializeCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex)); +} + +//---------------------------------------------------------------------- +// Default constructor. +// +// Creates a pthread mutex with "type" as the mutex type. +//---------------------------------------------------------------------- +Mutex::Mutex (Mutex::Type type) : + m_mutex() +{ + m_mutex = static_cast<PCRITICAL_SECTION>(malloc(sizeof(CRITICAL_SECTION))); + InitializeCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex)); +} + +//---------------------------------------------------------------------- +// Destructor. +// +// Destroys the mutex owned by this object. +//---------------------------------------------------------------------- +Mutex::~Mutex() +{ + DeleteCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex)); + free(m_mutex); +} + +//---------------------------------------------------------------------- +// Locks the mutex owned by this object, if the mutex is already +// locked, the calling thread will block until the mutex becomes +// available. +// +// RETURNS +// The error code from the pthread_mutex_lock() function call. +//---------------------------------------------------------------------- +int +Mutex::Lock() +{ + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_lock (%p)...\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), m_mutex); + + EnterCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex)); + return 0; +} + +//---------------------------------------------------------------------- +// Attempts to lock the mutex owned by this object without blocking. +// If the mutex is already locked, TryLock() will not block waiting +// for the mutex, but will return an error condition. +// +// RETURNS +// The error code from the pthread_mutex_trylock() function call. +//---------------------------------------------------------------------- +int +Mutex::TryLock(const char *failure_message) +{ + return TryEnterCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex)) == 0; +} + +//---------------------------------------------------------------------- +// If the current thread holds the lock on the owned mutex, then +// Unlock() will unlock the mutex. Calling Unlock() on this object +// that the calling thread does not hold will result in undefined +// behavior. +// +// RETURNS +// The error code from the pthread_mutex_unlock() function call. +//---------------------------------------------------------------------- +int +Mutex::Unlock() +{ + LeaveCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex)); + return 0; +} diff --git a/source/Host/windows/PipeWindows.cpp b/source/Host/windows/PipeWindows.cpp new file mode 100644 index 000000000000..d4afd6e74943 --- /dev/null +++ b/source/Host/windows/PipeWindows.cpp @@ -0,0 +1,336 @@ +//===-- PipeWindows.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/windows/PipeWindows.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/raw_ostream.h" + +#include <fcntl.h> +#include <io.h> +#include <rpc.h> + +#include <atomic> +#include <string> + +using namespace lldb; +using namespace lldb_private; + +namespace +{ +std::atomic<uint32_t> g_pipe_serial(0); +} + +PipeWindows::PipeWindows() +{ + m_read = INVALID_HANDLE_VALUE; + m_write = INVALID_HANDLE_VALUE; + + m_read_fd = -1; + m_write_fd = -1; + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); +} + +PipeWindows::~PipeWindows() +{ + Close(); +} + +Error +PipeWindows::CreateNew(bool child_process_inherit) +{ + // Even for anonymous pipes, we open a named pipe. This is because you cannot get + // overlapped i/o on Windows without using a named pipe. So we synthesize a unique + // name. + uint32_t serial = g_pipe_serial.fetch_add(1); + std::string pipe_name; + llvm::raw_string_ostream pipe_name_stream(pipe_name); + pipe_name_stream << "lldb.pipe." << ::GetCurrentProcessId() << "." << serial; + pipe_name_stream.flush(); + + return CreateNew(pipe_name.c_str(), child_process_inherit); +} + +Error +PipeWindows::CreateNew(llvm::StringRef name, bool child_process_inherit) +{ + if (name.empty()) + return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32); + + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + std::string pipe_path = "\\\\.\\Pipe\\"; + pipe_path.append(name); + + // Always open for overlapped i/o. We implement blocking manually in Read and Write. + DWORD read_mode = FILE_FLAG_OVERLAPPED; + m_read = + ::CreateNamedPipe(pipe_path.c_str(), PIPE_ACCESS_INBOUND | read_mode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL); + if (INVALID_HANDLE_VALUE == m_read) + return Error(::GetLastError(), eErrorTypeWin32); + m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); + + // Open the write end of the pipe. + Error result = OpenNamedPipe(name, child_process_inherit, false); + if (!result.Success()) + { + CloseReadFileDescriptor(); + return result; + } + + return result; +} + +Error +PipeWindows::CreateWithUniqueName(llvm::StringRef prefix, bool child_process_inherit, llvm::SmallVectorImpl<char>& name) +{ + llvm::SmallString<128> pipe_name; + Error error; + ::UUID unique_id; + RPC_CSTR unique_string; + RPC_STATUS status = ::UuidCreate(&unique_id); + if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY) + status = ::UuidToStringA(&unique_id, &unique_string); + if (status == RPC_S_OK) + { + pipe_name = prefix; + pipe_name += "-"; + pipe_name += reinterpret_cast<char *>(unique_string); + ::RpcStringFreeA(&unique_string); + error = CreateNew(pipe_name, child_process_inherit); + } + else + { + error.SetError(status, eErrorTypeWin32); + } + if (error.Success()) + name = pipe_name; + return error; +} + +Error +PipeWindows::OpenAsReader(llvm::StringRef name, bool child_process_inherit) +{ + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + return OpenNamedPipe(name, child_process_inherit, true); +} + +Error +PipeWindows::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout) +{ + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + return OpenNamedPipe(name, child_process_inherit, false); +} + +Error +PipeWindows::OpenNamedPipe(llvm::StringRef name, bool child_process_inherit, bool is_read) +{ + if (name.empty()) + return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32); + + assert(is_read ? !CanRead() : !CanWrite()); + + SECURITY_ATTRIBUTES attributes = {0}; + attributes.bInheritHandle = child_process_inherit; + + std::string pipe_path = "\\\\.\\Pipe\\"; + pipe_path.append(name); + + if (is_read) + { + m_read = ::CreateFile(pipe_path.c_str(), GENERIC_READ, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (INVALID_HANDLE_VALUE == m_read) + return Error(::GetLastError(), eErrorTypeWin32); + + m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); + + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); + } + else + { + m_write = ::CreateFile(pipe_path.c_str(), GENERIC_WRITE, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (INVALID_HANDLE_VALUE == m_write) + return Error(::GetLastError(), eErrorTypeWin32); + + m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY); + + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); + } + + return Error(); +} + +int +PipeWindows::GetReadFileDescriptor() const +{ + return m_read_fd; +} + +int +PipeWindows::GetWriteFileDescriptor() const +{ + return m_write_fd; +} + +int +PipeWindows::ReleaseReadFileDescriptor() +{ + if (!CanRead()) + return -1; + int result = m_read_fd; + m_read_fd = -1; + if (m_read_overlapped.hEvent) + ::CloseHandle(m_read_overlapped.hEvent); + m_read = INVALID_HANDLE_VALUE; + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + return result; +} + +int +PipeWindows::ReleaseWriteFileDescriptor() +{ + if (!CanWrite()) + return -1; + int result = m_write_fd; + m_write_fd = -1; + m_write = INVALID_HANDLE_VALUE; + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); + return result; +} + +void +PipeWindows::CloseReadFileDescriptor() +{ + if (!CanRead()) + return; + + if (m_read_overlapped.hEvent) + ::CloseHandle(m_read_overlapped.hEvent); + _close(m_read_fd); + m_read = INVALID_HANDLE_VALUE; + m_read_fd = -1; + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); +} + +void +PipeWindows::CloseWriteFileDescriptor() +{ + if (!CanWrite()) + return; + + _close(m_write_fd); + m_write = INVALID_HANDLE_VALUE; + m_write_fd = -1; + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); +} + +void +PipeWindows::Close() +{ + CloseReadFileDescriptor(); + CloseWriteFileDescriptor(); +} + +Error +PipeWindows::Delete(llvm::StringRef name) +{ + return Error(); +} + +bool +PipeWindows::CanRead() const +{ + return (m_read != INVALID_HANDLE_VALUE); +} + +bool +PipeWindows::CanWrite() const +{ + return (m_write != INVALID_HANDLE_VALUE); +} + +HANDLE +PipeWindows::GetReadNativeHandle() +{ + return m_read; +} + +HANDLE +PipeWindows::GetWriteNativeHandle() +{ + return m_write; +} + +Error +PipeWindows::ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &duration, size_t &bytes_read) +{ + if (!CanRead()) + return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32); + + bytes_read = 0; + DWORD sys_bytes_read = size; + BOOL result = ::ReadFile(m_read, buf, sys_bytes_read, &sys_bytes_read, &m_read_overlapped); + if (!result && GetLastError() != ERROR_IO_PENDING) + return Error(::GetLastError(), eErrorTypeWin32); + + DWORD timeout = (duration == std::chrono::microseconds::zero()) ? INFINITE : duration.count() * 1000; + DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout); + if (wait_result != WAIT_OBJECT_0) + { + // The operation probably failed. However, if it timed out, we need to cancel the I/O. + // Between the time we returned from WaitForSingleObject and the time we call CancelIoEx, + // the operation may complete. If that hapens, CancelIoEx will fail and return ERROR_NOT_FOUND. + // If that happens, the original operation should be considered to have been successful. + bool failed = true; + DWORD failure_error = ::GetLastError(); + if (wait_result == WAIT_TIMEOUT) + { + BOOL cancel_result = CancelIoEx(m_read, &m_read_overlapped); + if (!cancel_result && GetLastError() == ERROR_NOT_FOUND) + failed = false; + } + if (failed) + return Error(failure_error, eErrorTypeWin32); + } + + // Now we call GetOverlappedResult setting bWait to false, since we've already waited + // as long as we're willing to. + if (!GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read, FALSE)) + return Error(::GetLastError(), eErrorTypeWin32); + + bytes_read = sys_bytes_read; + return Error(); +} + +Error +PipeWindows::Write(const void *buf, size_t num_bytes, size_t &bytes_written) +{ + if (!CanWrite()) + return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32); + + DWORD sys_bytes_written = 0; + BOOL write_result = ::WriteFile(m_write, buf, num_bytes, &sys_bytes_written, &m_write_overlapped); + if (!write_result && GetLastError() != ERROR_IO_PENDING) + return Error(::GetLastError(), eErrorTypeWin32); + + BOOL result = GetOverlappedResult(m_write, &m_write_overlapped, &sys_bytes_written, TRUE); + if (!result) + return Error(::GetLastError(), eErrorTypeWin32); + return Error(); +} diff --git a/source/Host/windows/ProcessLauncherWindows.cpp b/source/Host/windows/ProcessLauncherWindows.cpp new file mode 100644 index 000000000000..355dd40544f9 --- /dev/null +++ b/source/Host/windows/ProcessLauncherWindows.cpp @@ -0,0 +1,105 @@ +//===-- ProcessLauncherWindows.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Target/ProcessLaunchInfo.h" + +#include <string> +#include <vector> + +using namespace lldb; +using namespace lldb_private; + +HostProcess +ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) +{ + error.Clear(); + + std::string executable; + std::string commandLine; + std::vector<char> environment; + STARTUPINFO startupinfo = {0}; + PROCESS_INFORMATION pi = {0}; + + HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO); + HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO); + HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO); + + startupinfo.cb = sizeof(startupinfo); + startupinfo.dwFlags |= STARTF_USESTDHANDLES; + startupinfo.hStdError = stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE); + startupinfo.hStdInput = stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE); + startupinfo.hStdOutput = stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE); + + const char *hide_console_var = getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE"); + if (hide_console_var && llvm::StringRef(hide_console_var).equals_lower("true")) + { + startupinfo.dwFlags |= STARTF_USESHOWWINDOW; + startupinfo.wShowWindow = SW_HIDE; + } + + DWORD flags = CREATE_NEW_CONSOLE; + if (launch_info.GetFlags().Test(eLaunchFlagDebug)) + flags |= DEBUG_ONLY_THIS_PROCESS; + + executable = launch_info.GetExecutableFile().GetPath(); + launch_info.GetArguments().GetQuotedCommandString(commandLine); + BOOL result = ::CreateProcessA(executable.c_str(), const_cast<char *>(commandLine.c_str()), NULL, NULL, TRUE, flags, NULL, + launch_info.GetWorkingDirectory().GetCString(), &startupinfo, &pi); + if (result) + { + // Do not call CloseHandle on pi.hProcess, since we want to pass that back through the HostProcess. + ::CloseHandle(pi.hThread); + } + + if (stdin_handle) + ::CloseHandle(stdin_handle); + if (stdout_handle) + ::CloseHandle(stdout_handle); + if (stderr_handle) + ::CloseHandle(stderr_handle); + + if (!result) + error.SetError(::GetLastError(), eErrorTypeWin32); + return HostProcess(pi.hProcess); +} + +HANDLE +ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, int fd) +{ + const FileAction *action = launch_info.GetFileActionForFD(fd); + if (action == nullptr) + return NULL; + SECURITY_ATTRIBUTES secattr = {0}; + secattr.nLength = sizeof(SECURITY_ATTRIBUTES); + secattr.bInheritHandle = TRUE; + + const char *path = action->GetPath(); + DWORD access = 0; + DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + DWORD create = 0; + DWORD flags = 0; + if (fd == STDIN_FILENO) + { + access = GENERIC_READ; + create = OPEN_EXISTING; + flags = FILE_ATTRIBUTE_READONLY; + } + if (fd == STDOUT_FILENO || fd == STDERR_FILENO) + { + access = GENERIC_WRITE; + create = CREATE_ALWAYS; + if (fd == STDERR_FILENO) + flags = FILE_FLAG_WRITE_THROUGH; + } + + HANDLE result = ::CreateFile(path, access, share, &secattr, create, flags, NULL); + return (result == INVALID_HANDLE_VALUE) ? NULL : result; +} diff --git a/source/Host/windows/ProcessRunLock.cpp b/source/Host/windows/ProcessRunLock.cpp new file mode 100644 index 000000000000..1f21552a3062 --- /dev/null +++ b/source/Host/windows/ProcessRunLock.cpp @@ -0,0 +1,106 @@ +#include "lldb/Host/ProcessRunLock.h" +#include "lldb/Host/windows/windows.h" + +namespace +{ +#if defined(__MINGW32__) +// Taken from WinNT.h +typedef struct _RTL_SRWLOCK { + PVOID Ptr; +} RTL_SRWLOCK, *PRTL_SRWLOCK; + +// Taken from WinBase.h +typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK; +#endif +} + + +static PSRWLOCK GetLock(lldb::rwlock_t lock) +{ + return static_cast<PSRWLOCK>(lock); +} + +static bool ReadLock(lldb::rwlock_t rwlock) +{ + ::AcquireSRWLockShared(GetLock(rwlock)); + return true; +} + +static bool ReadUnlock(lldb::rwlock_t rwlock) +{ + ::ReleaseSRWLockShared(GetLock(rwlock)); + return true; +} + +static bool WriteLock(lldb::rwlock_t rwlock) +{ + ::AcquireSRWLockExclusive(GetLock(rwlock)); + return true; +} + +static bool WriteTryLock(lldb::rwlock_t rwlock) +{ + return !!::TryAcquireSRWLockExclusive(GetLock(rwlock)); +} + +static bool WriteUnlock(lldb::rwlock_t rwlock) +{ + ::ReleaseSRWLockExclusive(GetLock(rwlock)); + return true; +} + +using namespace lldb_private; + +ProcessRunLock::ProcessRunLock() + : m_running(false) +{ + m_rwlock = new SRWLOCK; + InitializeSRWLock(GetLock(m_rwlock)); +} + +ProcessRunLock::~ProcessRunLock() +{ + delete m_rwlock; +} + +bool ProcessRunLock::ReadTryLock() +{ + ::ReadLock(m_rwlock); + if (m_running == false) + return true; + ::ReadUnlock(m_rwlock); + return false; +} + +bool ProcessRunLock::ReadUnlock() +{ + return ::ReadUnlock(m_rwlock); +} + +bool ProcessRunLock::SetRunning () +{ + WriteLock(m_rwlock); + m_running = true; + WriteUnlock(m_rwlock); + return true; +} + +bool ProcessRunLock::TrySetRunning () +{ + if (WriteTryLock(m_rwlock)) + { + bool was_running = m_running; + m_running = true; + WriteUnlock(m_rwlock); + return !was_running; + } + return false; +} + +bool ProcessRunLock::SetStopped () +{ + WriteLock(m_rwlock); + m_running = false; + WriteUnlock(m_rwlock); + return true; +} diff --git a/source/Host/windows/ThisThread.cpp b/source/Host/windows/ThisThread.cpp new file mode 100644 index 000000000000..bcd5b8d1c1d0 --- /dev/null +++ b/source/Host/windows/ThisThread.cpp @@ -0,0 +1,66 @@ +//===-- ThisThread.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Error.h" + +#include "lldb/Host/windows/windows.h" +#include "lldb/Host/ThisThread.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" + +using namespace lldb; +using namespace lldb_private; + +#if defined(_MSC_VER) && !defined(__clang__) + +namespace +{ +static const DWORD MS_VC_EXCEPTION = 0x406D1388; + +#pragma pack(push, 8) +struct THREADNAME_INFO +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to thread name + DWORD dwThreadId; // Thread ID (-1 == current thread) + DWORD dwFlags; // Reserved. Do not use. +}; +#pragma pack(pop) +} + +#endif + +void +ThisThread::SetName(llvm::StringRef name) +{ +// Other compilers don't yet support SEH, so we can only set the thread if compiling with MSVC. +// TODO(zturner): Once clang-cl supports SEH, relax this conditional. +#if defined(_MSC_VER) && !defined(__clang__) + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name.data(); + info.dwThreadId = ::GetCurrentThreadId(); + info.dwFlags = 0; + + __try { + ::RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info); + } + __except(EXCEPTION_EXECUTE_HANDLER) {} +#endif +} + +void +ThisThread::GetName(llvm::SmallVectorImpl<char> &name) +{ + // Getting the thread name is not supported on Windows. + // TODO(zturner): In SetName(), make a TLS entry that contains the thread's name, and in this function + // try to extract that TLS entry. + name.clear(); +} diff --git a/source/Host/windows/Windows.cpp b/source/Host/windows/Windows.cpp new file mode 100644 index 000000000000..71bff7a9d2e2 --- /dev/null +++ b/source/Host/windows/Windows.cpp @@ -0,0 +1,231 @@ +//===-- Windows.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This file provides Windows support functions + +#include "lldb/Host/windows/windows.h" +#include "lldb/Host/windows/win32.h" + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <io.h> +#include <cerrno> +#include <ctype.h> + +// These prototypes are defined in <direct.h>, but it also defines chdir() and getcwd(), giving multiply defined errors +extern "C" +{ + char *_getcwd(char *buffer, int maxlen); + int _chdir(const char *path); +} + +int vasprintf(char **ret, const char *fmt, va_list ap) +{ + char *buf; + int len; + size_t buflen; + va_list ap2; + +#if defined(_MSC_VER) || defined(__MINGW64) + ap2 = ap; + len = _vscprintf(fmt, ap2); +#else + va_copy(ap2, ap); + len = vsnprintf(NULL, 0, fmt, ap2); +#endif + + if (len >= 0 && (buf = (char*) malloc ((buflen = (size_t) (len + 1)))) != NULL) { + len = vsnprintf(buf, buflen, fmt, ap); + *ret = buf; + } else { + *ret = NULL; + len = -1; + } + + va_end(ap2); + return len; +} + +char* strcasestr(const char *s, const char* find) +{ + char c, sc; + size_t len; + + if ((c = *find++) != 0) { + c = tolower((unsigned char) c); + len = strlen(find); + do { + do { + if ((sc = *s++) == 0) + return 0; + } while ((char) tolower((unsigned char) sc) != c); + } while (strncasecmp(s, find, len) != 0); + s--; + } + return ((char *) s); +} + +char* realpath(const char * name, char * resolved) +{ + char *retname = NULL; /* we will return this, if we fail */ + + /* SUSv3 says we must set `errno = EINVAL', and return NULL, + * if `name' is passed as a NULL pointer. + */ + + if (name == NULL) + errno = EINVAL; + + /* Otherwise, `name' must refer to a readable filesystem object, + * if we are going to resolve its absolute path name. + */ + + else if (access(name, 4) == 0) + { + /* If `name' didn't point to an existing entity, + * then we don't get to here; we simply fall past this block, + * returning NULL, with `errno' appropriately set by `access'. + * + * When we _do_ get to here, then we can use `_fullpath' to + * resolve the full path for `name' into `resolved', but first, + * check that we have a suitable buffer, in which to return it. + */ + + if ((retname = resolved) == NULL) + { + /* Caller didn't give us a buffer, so we'll exercise the + * option granted by SUSv3, and allocate one. + * + * `_fullpath' would do this for us, but it uses `malloc', and + * Microsoft's implementation doesn't set `errno' on failure. + * If we don't do this explicitly ourselves, then we will not + * know if `_fullpath' fails on `malloc' failure, or for some + * other reason, and we want to set `errno = ENOMEM' for the + * `malloc' failure case. + */ + + retname = (char*) malloc(_MAX_PATH); + } + + /* By now, we should have a valid buffer. + * If we don't, then we know that `malloc' failed, + * so we can set `errno = ENOMEM' appropriately. + */ + + if (retname == NULL) + errno = ENOMEM; + + /* Otherwise, when we do have a valid buffer, + * `_fullpath' should only fail if the path name is too long. + */ + + else if ((retname = _fullpath(retname, name, _MAX_PATH)) == NULL) + errno = ENAMETOOLONG; + } + + /* By the time we get to here, + * `retname' either points to the required resolved path name, + * or it is NULL, with `errno' set appropriately, either of which + * is our required return condition. + */ + + if (retname != NULL) + { + // Do a LongPath<->ShortPath roundtrip so that case is resolved by OS + int initialLength = strlen(retname); + TCHAR buffer[MAX_PATH]; + GetShortPathName(retname, buffer, MAX_PATH); + GetLongPathName(buffer, retname, initialLength + 1); + + // Force drive to be upper case + if (retname[1] == ':') + retname[0] = toupper(retname[0]); + } + + return retname; +} + +#ifdef _MSC_VER + +char* basename(char *path) +{ + char* l1 = strrchr(path, '\\'); + char* l2 = strrchr(path, '/'); + if (l2 > l1) l1 = l2; + if (!l1) return path; // no base name + return &l1[1]; +} + +// use _getcwd() instead of GetCurrentDirectory() because it updates errno +char* getcwd(char* path, int max) +{ + return _getcwd(path, max); +} + +// use _chdir() instead of SetCurrentDirectory() because it updates errno +int chdir(const char* path) +{ + return _chdir(path); +} + +char *dirname(char *path) +{ + char* l1 = strrchr(path, '\\'); + char* l2 = strrchr(path, '/'); + if (l2 > l1) l1 = l2; + if (!l1) return NULL; // no dir name + *l1 = 0; + return path; +} + +int strcasecmp(const char* s1, const char* s2) +{ + return stricmp(s1, s2); +} + +int strncasecmp(const char* s1, const char* s2, size_t n) +{ + return strnicmp(s1, s2, n); +} + +int usleep(uint32_t useconds) +{ + Sleep(useconds / 1000); + return 0; +} + +#if _MSC_VER < 1900 +namespace lldb_private { +int vsnprintf(char *buffer, size_t count, const char *format, va_list argptr) +{ + int old_errno = errno; + int r = ::vsnprintf(buffer, count, format, argptr); + int new_errno = errno; + buffer[count-1] = '\0'; + if (r == -1 || r == count) + { + FILE *nul = fopen("nul", "w"); + int bytes_written = ::vfprintf(nul, format, argptr); + fclose(nul); + if (bytes_written < count) + errno = new_errno; + else + { + errno = old_errno; + r = bytes_written; + } + } + return r; +} +} // namespace lldb_private +#endif + +#endif // _MSC_VER diff --git a/source/Initialization/CMakeLists.txt b/source/Initialization/CMakeLists.txt new file mode 100644 index 000000000000..f23a2b4339b8 --- /dev/null +++ b/source/Initialization/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_library(lldbInitialization + SystemInitializerCommon.cpp + SystemInitializer.cpp + SystemLifetimeManager.cpp + ) diff --git a/source/Initialization/Makefile b/source/Initialization/Makefile new file mode 100644 index 000000000000..a63f4733bb6e --- /dev/null +++ b/source/Initialization/Makefile @@ -0,0 +1,14 @@ +##===- source/Initialize/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 := lldbInitialization +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Interpreter/CMakeLists.txt b/source/Interpreter/CMakeLists.txt new file mode 100644 index 000000000000..b92128cd3011 --- /dev/null +++ b/source/Interpreter/CMakeLists.txt @@ -0,0 +1,45 @@ +add_lldb_library(lldbInterpreter + Args.cpp + CommandHistory.cpp + CommandInterpreter.cpp + CommandObject.cpp + CommandObjectRegexCommand.cpp + CommandObjectScript.cpp + CommandOptionValidators.cpp + CommandReturnObject.cpp + OptionGroupArchitecture.cpp + OptionGroupBoolean.cpp + OptionGroupFile.cpp + OptionGroupFormat.cpp + OptionGroupOutputFile.cpp + OptionGroupPlatform.cpp + OptionGroupString.cpp + OptionGroupUInt64.cpp + OptionGroupUUID.cpp + OptionGroupValueObjectDisplay.cpp + OptionValue.cpp + OptionValueArch.cpp + OptionValueArgs.cpp + OptionValueArray.cpp + OptionValueBoolean.cpp + OptionValueChar.cpp + OptionValueDictionary.cpp + OptionValueEnumeration.cpp + OptionValueFileSpec.cpp + OptionValueFileSpecLIst.cpp + OptionValueFormat.cpp + OptionValueFormatEntity.cpp + OptionValueLanguage.cpp + OptionValuePathMappings.cpp + OptionValueProperties.cpp + OptionValueRegex.cpp + OptionValueSInt64.cpp + OptionValueString.cpp + OptionValueUInt64.cpp + OptionValueUUID.cpp + OptionGroupVariable.cpp + OptionGroupWatchpoint.cpp + Options.cpp + Property.cpp + ScriptInterpreter.cpp + ) diff --git a/source/Interpreter/CommandHistory.cpp b/source/Interpreter/CommandHistory.cpp index bbe64b446acc..9d3c814697b0 100644 --- a/source/Interpreter/CommandHistory.cpp +++ b/source/Interpreter/CommandHistory.cpp @@ -130,9 +130,9 @@ CommandHistory::Dump (Stream& stream, size_t stop_idx) const { Mutex::Locker locker(m_mutex); - stop_idx = std::min(stop_idx, m_history.size() - 1); + stop_idx = std::min(stop_idx + 1, m_history.size()); for (size_t counter = start_idx; - counter <= stop_idx; + counter < stop_idx; counter++) { const std::string hist_item = m_history[counter]; diff --git a/source/Interpreter/Makefile b/source/Interpreter/Makefile new file mode 100644 index 000000000000..2f25e6796604 --- /dev/null +++ b/source/Interpreter/Makefile @@ -0,0 +1,51 @@ +##===- source/Interpreter/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 := lldbInterpreter +BUILD_ARCHIVE = 1 +include $(LLDB_LEVEL)/../../Makefile.config + +ifneq ($(HOST_OS),MingW) +ifeq (,$(findstring -DLLDB_DISABLE_PYTHON,$(CXXFLAGS))) +DO_BUILD_LLDBWrapPython = 1 +BUILT_SOURCES := LLDBWrapPython.cpp +endif +endif + +include $(LLDB_LEVEL)/Makefile +-include $(PROJ_OBJ_DIR)/LLDBWrapPython.cpp.d + +ifeq ($(DO_BUILD_LLDBWrapPython),1) +# Drop -Wfour-char-constants, which we are not currently clean with. +EXTRA_OPTIONS += -Wno-four-char-constants + +# Drop -Wself-assign, -Wmissing-field-initializers, -Wsometimes-uninitialized, +# -Wcast-qual, and -Wdeprecated-register which we are not clean with due to SWIG +# generated cpp source. +EXTRA_OPTIONS += -Wno-missing-field-initializers -Wno-self-assign -Wno-sometimes-uninitialized -Wno-cast-qual -Wno-deprecated-register + +PYTHON_DIR := $(PROJ_OBJ_ROOT)/$(BuildMode) + +SWIG_SOURCES := $(shell find $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts -type f -name '*.swig' -print) + +LLDBWrapPython.cpp lldb.py: $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/Python/modify-python-lldb.py \ + $(wildcard $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/interface/*.i) \ + ${SWIG_SOURCES} + $(Echo) Generating LLDBWrapPython.cpp + $(Verb) "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/prepare_bindings.py" "--src-root=$(PROJ_SRC_DIR)/$(LLDB_LEVEL)" "--target-dir=$(PROJ_OBJ_DIR)" "--config-build-dir=$(PROJ_OBJ_DIR)" "--prefix=$(PYTHON_DIR)" $(if $(DISABLE_AUTO_DEPENDENCIES),,-M) --find-swig + $(Verb) "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/finish-swig-wrapper-classes.sh" "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)" "$(PROJ_OBJ_DIR)" "$(PROJ_OBJ_DIR)" "$(PYTHON_DIR)" -m + +install-local:: lldb.py + $(Echo) Installing $(BuildMode) LLDB python modules + $(Verb) "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/prepare_bindings.py" "--src-root=$(PROJ_SRC_DIR)/$(LLDB_LEVEL)" "--target-dir=$(PROJ_OBJ_DIR)" "--config-build-dir=$(PROJ_OBJ_DIR)" "--prefix=$(DESTDIR)$(prefix)" --find-swig + +clean-local:: + $(Verb) $(RM) -f LLDBWrapPython.cpp lldb.py +endif diff --git a/source/Makefile b/source/Makefile new file mode 100644 index 000000000000..0ad56ffa5e0c --- /dev/null +++ b/source/Makefile @@ -0,0 +1,37 @@ +##===- source/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 := .. +PARALLEL_DIRS := API Initialization Breakpoint Commands Core DataFormatters Expression Host Interpreter Plugins Symbol Target Utility +LIBRARYNAME := lldbBase +BUILD_ARCHIVE = 1 + +# Although LLVM makefiles provide $(HOST_OS), we cannot use that here because it is defined by including the $(LLDB_LEVEL)/Makefile +# below. Instead, we use uname -s to detect the HOST_OS and generate LLDB_vers.c only on Mac. On Linux, the version number is +# calculated in the same way as Clang's version. +ifeq (Darwin,$(shell uname -s)) +BUILT_SOURCES = LLDB_vers.c +endif + +SOURCES := lldb.cpp + +include $(LLDB_LEVEL)/Makefile + +ifeq ($(HOST_OS),Darwin) +LLDB_vers.c: $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/generate-vers.pl $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/lldb.xcodeproj/project.pbxproj + "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/generate-vers.pl" "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/lldb.xcodeproj/project.pbxproj" liblldb_core > LLDB_vers.c +else +LLDB_REVISION := $(strip \ + $(shell $(LLVM_SRC_ROOT)/utils/GetSourceVersion $(LLVM_SRC_ROOT)/tools/lldb)) + +LLDB_REPOSITORY := $(strip \ + $(shell $(LLVM_SRC_ROOT)/utils/GetRepositoryPath $(LLVM_SRC_ROOT)/tools/lldb)) + +CPP.Defines += -DLLDB_REVISION='"$(LLDB_REVISION)"' -DLLDB_REPOSITORY='"$(LLDB_REPOSITORY)"' +endif diff --git a/source/Plugins/ABI/CMakeLists.txt b/source/Plugins/ABI/CMakeLists.txt new file mode 100644 index 000000000000..08452c5dad62 --- /dev/null +++ b/source/Plugins/ABI/CMakeLists.txt @@ -0,0 +1,12 @@ +add_subdirectory(SysV-arm) +add_subdirectory(SysV-arm64) +add_subdirectory(SysV-hexagon) +add_subdirectory(SysV-ppc) +add_subdirectory(SysV-ppc64) +add_subdirectory(SysV-mips) +add_subdirectory(SysV-mips64) +add_subdirectory(SysV-i386) +add_subdirectory(SysV-x86_64) +add_subdirectory(MacOSX-i386) +add_subdirectory(MacOSX-arm) +add_subdirectory(MacOSX-arm64) diff --git a/source/Plugins/ABI/MacOSX-arm/CMakeLists.txt b/source/Plugins/ABI/MacOSX-arm/CMakeLists.txt new file mode 100644 index 000000000000..ea8b011b1984 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABIMacOSX_arm + ABIMacOSX_arm.cpp + ) diff --git a/source/Plugins/ABI/MacOSX-arm/Makefile b/source/Plugins/ABI/MacOSX-arm/Makefile new file mode 100644 index 000000000000..18073266d34d --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/MacOSX-arm/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 := lldbPluginABIMacOSX_arm +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/MacOSX-arm64/CMakeLists.txt b/source/Plugins/ABI/MacOSX-arm64/CMakeLists.txt new file mode 100644 index 000000000000..498bb7218637 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABIMacOSX_arm64 + ABIMacOSX_arm64.cpp + ) diff --git a/source/Plugins/ABI/MacOSX-arm64/Makefile b/source/Plugins/ABI/MacOSX-arm64/Makefile new file mode 100644 index 000000000000..7fc6909e43cc --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/MacOSX-arm64/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 := lldbPluginABIMacOSX_arm64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/MacOSX-i386/CMakeLists.txt b/source/Plugins/ABI/MacOSX-i386/CMakeLists.txt new file mode 100644 index 000000000000..f6a543a66da7 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-i386/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABIMacOSX_i386 + ABIMacOSX_i386.cpp + ) diff --git a/source/Plugins/ABI/MacOSX-i386/Makefile b/source/Plugins/ABI/MacOSX-i386/Makefile new file mode 100644 index 000000000000..d9bc73922563 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-i386/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/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 := lldbPluginABIMacOSX_i386 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-arm/CMakeLists.txt b/source/Plugins/ABI/SysV-arm/CMakeLists.txt new file mode 100644 index 000000000000..c25ce5a1c633 --- /dev/null +++ b/source/Plugins/ABI/SysV-arm/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_arm + ABISysV_arm.cpp + ) diff --git a/source/Plugins/ABI/SysV-arm/Makefile b/source/Plugins/ABI/SysV-arm/Makefile new file mode 100644 index 000000000000..a1d95dc17320 --- /dev/null +++ b/source/Plugins/ABI/SysV-arm/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-arm/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 := lldbPluginABISysV_arm +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-arm64/CMakeLists.txt b/source/Plugins/ABI/SysV-arm64/CMakeLists.txt new file mode 100644 index 000000000000..0ddb37043ace --- /dev/null +++ b/source/Plugins/ABI/SysV-arm64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_arm64 + ABISysV_arm64.cpp + ) diff --git a/source/Plugins/ABI/SysV-arm64/Makefile b/source/Plugins/ABI/SysV-arm64/Makefile new file mode 100644 index 000000000000..a72ecd83404d --- /dev/null +++ b/source/Plugins/ABI/SysV-arm64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-arm64/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 := lldbPluginABISysV_arm64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-hexagon/CMakeLists.txt b/source/Plugins/ABI/SysV-hexagon/CMakeLists.txt new file mode 100644 index 000000000000..b8fbb0744719 --- /dev/null +++ b/source/Plugins/ABI/SysV-hexagon/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_hexagon + ABISysV_hexagon.cpp + ) diff --git a/source/Plugins/ABI/SysV-hexagon/Makefile b/source/Plugins/ABI/SysV-hexagon/Makefile new file mode 100644 index 000000000000..23733c7e551e --- /dev/null +++ b/source/Plugins/ABI/SysV-hexagon/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-hexagon/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 := lldbPluginABISysV_hexagon +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-i386/CMakeLists.txt b/source/Plugins/ABI/SysV-i386/CMakeLists.txt new file mode 100644 index 000000000000..3528f01ad75e --- /dev/null +++ b/source/Plugins/ABI/SysV-i386/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_i386 + ABISysV_i386.cpp + ) diff --git a/source/Plugins/ABI/SysV-i386/Makefile b/source/Plugins/ABI/SysV-i386/Makefile new file mode 100644 index 000000000000..0ac3cbee2d54 --- /dev/null +++ b/source/Plugins/ABI/SysV-i386/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-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 := lldbPluginABISysV_i386 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-mips/CMakeLists.txt b/source/Plugins/ABI/SysV-mips/CMakeLists.txt new file mode 100644 index 000000000000..0db3e5c1c7e0 --- /dev/null +++ b/source/Plugins/ABI/SysV-mips/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_mips + ABISysV_mips.cpp + ) diff --git a/source/Plugins/ABI/SysV-mips/Makefile b/source/Plugins/ABI/SysV-mips/Makefile new file mode 100644 index 000000000000..c61130628433 --- /dev/null +++ b/source/Plugins/ABI/SysV-mips/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-mips/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 := lldbPluginABISysV_mips +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-mips64/CMakeLists.txt b/source/Plugins/ABI/SysV-mips64/CMakeLists.txt new file mode 100644 index 000000000000..099464821edd --- /dev/null +++ b/source/Plugins/ABI/SysV-mips64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_mips64 + ABISysV_mips64.cpp + ) diff --git a/source/Plugins/ABI/SysV-mips64/Makefile b/source/Plugins/ABI/SysV-mips64/Makefile new file mode 100644 index 000000000000..b7e6dc615cb0 --- /dev/null +++ b/source/Plugins/ABI/SysV-mips64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-mips64/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 := lldbPluginABISysV_mips64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-ppc/CMakeLists.txt b/source/Plugins/ABI/SysV-ppc/CMakeLists.txt new file mode 100644 index 000000000000..a7784195f771 --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_ppc + ABISysV_ppc.cpp + ) diff --git a/source/Plugins/ABI/SysV-ppc/Makefile b/source/Plugins/ABI/SysV-ppc/Makefile new file mode 100644 index 000000000000..7a6d38d68337 --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-hexagon/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 := lldbPluginABISysV_ppc +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-ppc64/CMakeLists.txt b/source/Plugins/ABI/SysV-ppc64/CMakeLists.txt new file mode 100644 index 000000000000..7a33d2922047 --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_ppc64 + ABISysV_ppc64.cpp + ) diff --git a/source/Plugins/ABI/SysV-ppc64/Makefile b/source/Plugins/ABI/SysV-ppc64/Makefile new file mode 100644 index 000000000000..43fc41d42713 --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-hexagon/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 := lldbPluginABISysV_ppc64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-x86_64/CMakeLists.txt b/source/Plugins/ABI/SysV-x86_64/CMakeLists.txt new file mode 100644 index 000000000000..a2b62d7240fd --- /dev/null +++ b/source/Plugins/ABI/SysV-x86_64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_x86_64 + ABISysV_x86_64.cpp + ) diff --git a/source/Plugins/ABI/SysV-x86_64/Makefile b/source/Plugins/ABI/SysV-x86_64/Makefile new file mode 100644 index 000000000000..32990a64f956 --- /dev/null +++ b/source/Plugins/ABI/SysV-x86_64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-x86_64/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 := lldbPluginABISysV_x86_64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/CMakeLists.txt b/source/Plugins/CMakeLists.txt new file mode 100644 index 000000000000..928332fb6ba4 --- /dev/null +++ b/source/Plugins/CMakeLists.txt @@ -0,0 +1,20 @@ +add_subdirectory(ABI) +add_subdirectory(Disassembler) +add_subdirectory(DynamicLoader) +add_subdirectory(ExpressionParser) +add_subdirectory(Instruction) +add_subdirectory(InstrumentationRuntime) +add_subdirectory(JITLoader) +add_subdirectory(Language) +add_subdirectory(LanguageRuntime) +add_subdirectory(MemoryHistory) +add_subdirectory(ObjectContainer) +add_subdirectory(ObjectFile) +add_subdirectory(OperatingSystem) +add_subdirectory(Platform) +add_subdirectory(Process) +add_subdirectory(ScriptInterpreter) +add_subdirectory(SymbolFile) +add_subdirectory(SystemRuntime) +add_subdirectory(SymbolVendor) +add_subdirectory(UnwindAssembly) diff --git a/source/Plugins/Disassembler/CMakeLists.txt b/source/Plugins/Disassembler/CMakeLists.txt new file mode 100644 index 000000000000..6e3c904d5a60 --- /dev/null +++ b/source/Plugins/Disassembler/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(llvm) diff --git a/source/Plugins/Disassembler/llvm/CMakeLists.txt b/source/Plugins/Disassembler/llvm/CMakeLists.txt new file mode 100644 index 000000000000..ada81dc55e97 --- /dev/null +++ b/source/Plugins/Disassembler/llvm/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginDisassemblerLLVM + DisassemblerLLVMC.cpp + ) diff --git a/source/Plugins/Disassembler/llvm/Makefile b/source/Plugins/Disassembler/llvm/Makefile new file mode 100644 index 000000000000..a1309cdd081f --- /dev/null +++ b/source/Plugins/Disassembler/llvm/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Disassembler/llvm/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 := lldbPluginDisassemblerLLVM +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/CMakeLists.txt b/source/Plugins/DynamicLoader/CMakeLists.txt new file mode 100644 index 000000000000..8e1b316a3c25 --- /dev/null +++ b/source/Plugins/DynamicLoader/CMakeLists.txt @@ -0,0 +1,9 @@ +add_subdirectory(MacOSX-DYLD) +add_subdirectory(POSIX-DYLD) +add_subdirectory(Static) +add_subdirectory(Hexagon-DYLD) +add_subdirectory(Windows-DYLD) + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_subdirectory(Darwin-Kernel) +endif() diff --git a/source/Plugins/DynamicLoader/Darwin-Kernel/CMakeLists.txt b/source/Plugins/DynamicLoader/Darwin-Kernel/CMakeLists.txt new file mode 100644 index 000000000000..02752b78a4c7 --- /dev/null +++ b/source/Plugins/DynamicLoader/Darwin-Kernel/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginDynamicLoaderDarwinKernel + DynamicLoaderDarwinKernel.cpp + ) diff --git a/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp new file mode 100644 index 000000000000..218b0b7a1eee --- /dev/null +++ b/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp @@ -0,0 +1,1698 @@ +//===-- DynamicLoaderDarwinKernel.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/SafeMachO.h" + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "Plugins/Platform/MacOSX/PlatformDarwinKernel.h" + +#include "DynamicLoaderDarwinKernel.h" + +//#define ENABLE_DEBUG_PRINTF // COMMENT THIS LINE OUT PRIOR TO CHECKIN +#ifdef ENABLE_DEBUG_PRINTF +#include <stdio.h> +#define DEBUG_PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +using namespace lldb; +using namespace lldb_private; + +// Progressively greater amounts of scanning we will allow +// For some targets very early in startup, we can't do any random reads of memory or we can crash the device +// so a setting is needed that can completely disable the KASLR scans. + +enum KASLRScanType +{ + eKASLRScanNone = 0, // No reading into the inferior at all + eKASLRScanLowgloAddresses, // Check one word of memory for a possible kernel addr, then see if a kernel is there + eKASLRScanNearPC, // Scan backwards from the current $pc looking for kernel; checking at 96 locations total + eKASLRScanExhaustiveScan // Scan through the entire possible kernel address range looking for a kernel +}; + +OptionEnumValueElement +g_kaslr_kernel_scan_enum_values[] = +{ + { eKASLRScanNone, "none", "Do not read memory looking for a Darwin kernel when attaching." }, + { eKASLRScanLowgloAddresses, "basic", "Check for the Darwin kernel's load addr in the lowglo page (boot-args=debug) only." }, + { eKASLRScanNearPC, "fast-scan", "Scan near the pc value on attach to find the Darwin kernel's load address."}, + { eKASLRScanExhaustiveScan, "exhaustive-scan", "Scan through the entire potential address range of Darwin kernel (only on 32-bit targets)."}, + { 0, NULL, NULL } +}; + +static PropertyDefinition +g_properties[] = +{ + { "load-kexts" , OptionValue::eTypeBoolean, true, true, NULL, NULL, "Automatically loads kext images when attaching to a kernel." }, + { "scan-type", OptionValue::eTypeEnum, true, eKASLRScanNearPC, NULL, g_kaslr_kernel_scan_enum_values, "Control how many reads lldb will make while searching for a Darwin kernel on attach." }, + { NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL } +}; + +enum { + ePropertyLoadKexts, + ePropertyScanType +}; + +class DynamicLoaderDarwinKernelProperties : public Properties +{ +public: + + static ConstString & + GetSettingName () + { + static ConstString g_setting_name("darwin-kernel"); + return g_setting_name; + } + + DynamicLoaderDarwinKernelProperties() : + Properties () + { + m_collection_sp.reset (new OptionValueProperties(GetSettingName())); + m_collection_sp->Initialize(g_properties); + } + + virtual + ~DynamicLoaderDarwinKernelProperties() + { + } + + bool + GetLoadKexts() const + { + const uint32_t idx = ePropertyLoadKexts; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); + } + + KASLRScanType + GetScanType() const + { + const uint32_t idx = ePropertyScanType; + return (KASLRScanType) m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value); + } + + +}; + +typedef std::shared_ptr<DynamicLoaderDarwinKernelProperties> DynamicLoaderDarwinKernelPropertiesSP; + +static const DynamicLoaderDarwinKernelPropertiesSP & +GetGlobalProperties() +{ + static DynamicLoaderDarwinKernelPropertiesSP g_settings_sp; + if (!g_settings_sp) + g_settings_sp.reset (new DynamicLoaderDarwinKernelProperties ()); + return g_settings_sp; +} + +//---------------------------------------------------------------------- +// Create an instance of this class. This function is filled into +// the plugin info class that gets handed out by the plugin factory and +// allows the lldb to instantiate an instance of this class. +//---------------------------------------------------------------------- +DynamicLoader * +DynamicLoaderDarwinKernel::CreateInstance (Process* process, bool force) +{ + if (!force) + { + // If the user provided an executable binary and it is not a kernel, + // this plugin should not create an instance. + Module* exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) + { + ObjectFile *object_file = exe_module->GetObjectFile(); + if (object_file) + { + if (object_file->GetStrata() != ObjectFile::eStrataKernel) + { + return NULL; + } + } + } + + // If the target's architecture does not look like an Apple environment, + // this plugin should not create an instance. + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + switch (triple_ref.getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + case llvm::Triple::TvOS: + case llvm::Triple::WatchOS: + if (triple_ref.getVendor() != llvm::Triple::Apple) + { + return NULL; + } + break; + // If we have triple like armv7-unknown-unknown, we should try looking for a Darwin kernel. + case llvm::Triple::UnknownOS: + break; + default: + return NULL; + break; + } + } + + // At this point if there is an ExecutableModule, it is a kernel and the Target is some variant of an Apple system. + // If the Process hasn't provided the kernel load address, we need to look around in memory to find it. + + addr_t kernel_load_address = SearchForDarwinKernel (process); + if (kernel_load_address != LLDB_INVALID_ADDRESS) + { + process->SetCanRunCode(false); + return new DynamicLoaderDarwinKernel (process, kernel_load_address); + } + return NULL; +} + +lldb::addr_t +DynamicLoaderDarwinKernel::SearchForDarwinKernel (Process *process) +{ + addr_t kernel_load_address = process->GetImageInfoAddress(); + if (kernel_load_address == LLDB_INVALID_ADDRESS) + { + kernel_load_address = SearchForKernelAtSameLoadAddr (process); + if (kernel_load_address == LLDB_INVALID_ADDRESS) + { + kernel_load_address = SearchForKernelWithDebugHints (process); + if (kernel_load_address == LLDB_INVALID_ADDRESS) + { + kernel_load_address = SearchForKernelNearPC (process); + if (kernel_load_address == LLDB_INVALID_ADDRESS) + { + kernel_load_address = SearchForKernelViaExhaustiveSearch (process); + } + } + } + } + return kernel_load_address; +} + +//---------------------------------------------------------------------- +// Check if the kernel binary is loaded in memory without a slide. +// First verify that the ExecutableModule is a kernel before we proceed. +// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS. +//---------------------------------------------------------------------- +lldb::addr_t +DynamicLoaderDarwinKernel::SearchForKernelAtSameLoadAddr (Process *process) +{ + Module *exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module == NULL) + return LLDB_INVALID_ADDRESS; + + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + if (exe_objfile == NULL) + return LLDB_INVALID_ADDRESS; + + if (exe_objfile->GetType() != ObjectFile::eTypeExecutable || exe_objfile->GetStrata() != ObjectFile::eStrataKernel) + return LLDB_INVALID_ADDRESS; + + if (!exe_objfile->GetHeaderAddress().IsValid()) + return LLDB_INVALID_ADDRESS; + + if (CheckForKernelImageAtAddress (exe_objfile->GetHeaderAddress().GetFileAddress(), process) == exe_module->GetUUID()) + return exe_objfile->GetHeaderAddress().GetFileAddress(); + + return LLDB_INVALID_ADDRESS; +} + +//---------------------------------------------------------------------- +// If the debug flag is included in the boot-args nvram setting, the kernel's load address +// will be noted in the lowglo page at a fixed address +// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS. +//---------------------------------------------------------------------- +lldb::addr_t +DynamicLoaderDarwinKernel::SearchForKernelWithDebugHints (Process *process) +{ + if (GetGlobalProperties()->GetScanType() == eKASLRScanNone) + return LLDB_INVALID_ADDRESS; + + Error read_err; + addr_t addr = LLDB_INVALID_ADDRESS; + if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8) + { + addr = process->ReadUnsignedIntegerFromMemory (0xffffff8000002010ULL, 8, LLDB_INVALID_ADDRESS, read_err); + if (CheckForKernelImageAtAddress (addr, process).IsValid()) + { + return addr; + } + addr = process->ReadUnsignedIntegerFromMemory (0xffffff8000004010ULL, 8, LLDB_INVALID_ADDRESS, read_err); + if (CheckForKernelImageAtAddress (addr, process).IsValid()) + { + return addr; + } + } + else + { + addr = process->ReadUnsignedIntegerFromMemory (0xffff0110, 4, LLDB_INVALID_ADDRESS, read_err); + if (CheckForKernelImageAtAddress (addr, process).IsValid()) + { + return addr; + } + } + return LLDB_INVALID_ADDRESS; +} + +//---------------------------------------------------------------------- +// If the kernel is currently executing when lldb attaches, and we don't have +// a better way of finding the kernel's load address, try searching backwards +// from the current pc value looking for the kernel's Mach header in memory. +// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS. +//---------------------------------------------------------------------- +lldb::addr_t +DynamicLoaderDarwinKernel::SearchForKernelNearPC (Process *process) +{ + if (GetGlobalProperties()->GetScanType() == eKASLRScanNone + || GetGlobalProperties()->GetScanType() == eKASLRScanLowgloAddresses) + { + return LLDB_INVALID_ADDRESS; + } + + ThreadSP thread = process->GetThreadList().GetSelectedThread (); + if (thread.get() == NULL) + return LLDB_INVALID_ADDRESS; + addr_t pc = thread->GetRegisterContext ()->GetPC(LLDB_INVALID_ADDRESS); + + if (pc == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + addr_t kernel_range_low; + if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8) + { + kernel_range_low = 1ULL << 63; + } + else + { + kernel_range_low = 1ULL << 31; + } + + // Outside the normal kernel address range, this is probably userland code running right now + if (pc < kernel_range_low) + return LLDB_INVALID_ADDRESS; + + // The kernel will load at at one megabyte boundary (0x100000), or at that boundary plus + // an offset of one page (0x1000) or two, depending on the device. + + // Round the current pc down to the nearest one megabyte boundary - the place where we will start searching. + addr_t addr = pc & ~0xfffff; + + int i = 0; + while (i < 32 && pc >= kernel_range_low) + { + if (CheckForKernelImageAtAddress (addr, process).IsValid()) + return addr; + if (CheckForKernelImageAtAddress (addr + 0x1000, process).IsValid()) + return addr + 0x1000; + if (CheckForKernelImageAtAddress (addr + 0x2000, process).IsValid()) + return addr + 0x2000; + if (CheckForKernelImageAtAddress (addr + 0x4000, process).IsValid()) + return addr + 0x4000; + i++; + addr -= 0x100000; + } + + return LLDB_INVALID_ADDRESS; +} + +//---------------------------------------------------------------------- +// Scan through the valid address range for a kernel binary. +// This is uselessly slow in 64-bit environments so we don't even try it. +// This scan is not enabled by default even for 32-bit targets. +// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS. +//---------------------------------------------------------------------- +lldb::addr_t +DynamicLoaderDarwinKernel::SearchForKernelViaExhaustiveSearch (Process *process) +{ + if (GetGlobalProperties()->GetScanType() != eKASLRScanExhaustiveScan) + { + return LLDB_INVALID_ADDRESS; + } + + addr_t kernel_range_low, kernel_range_high; + if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8) + { + kernel_range_low = 1ULL << 63; + kernel_range_high = UINT64_MAX; + } + else + { + kernel_range_low = 1ULL << 31; + kernel_range_high = UINT32_MAX; + } + + // Stepping through memory at one-megabyte resolution looking for a kernel + // rarely works (fast enough) with a 64-bit address space -- for now, let's + // not even bother. We may be attaching to something which *isn't* a kernel + // and we don't want to spin for minutes on-end looking for a kernel. + if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8) + return LLDB_INVALID_ADDRESS; + + addr_t addr = kernel_range_low; + + while (addr >= kernel_range_low && addr < kernel_range_high) + { + if (CheckForKernelImageAtAddress (addr, process).IsValid()) + return addr; + if (CheckForKernelImageAtAddress (addr + 0x1000, process).IsValid()) + return addr + 0x1000; + if (CheckForKernelImageAtAddress (addr + 0x2000, process).IsValid()) + return addr + 0x2000; + if (CheckForKernelImageAtAddress (addr + 0x4000, process).IsValid()) + return addr + 0x4000; + addr += 0x100000; + } + return LLDB_INVALID_ADDRESS; +} + +//---------------------------------------------------------------------- +// Given an address in memory, look to see if there is a kernel image at that +// address. +// Returns a UUID; if a kernel was not found at that address, UUID.IsValid() will be false. +//---------------------------------------------------------------------- +lldb_private::UUID +DynamicLoaderDarwinKernel::CheckForKernelImageAtAddress (lldb::addr_t addr, Process *process) +{ + if (addr == LLDB_INVALID_ADDRESS) + return UUID(); + + // First try a quick test -- read the first 4 bytes and see if there is a valid Mach-O magic field there + // (the first field of the mach_header/mach_header_64 struct). + + Error read_error; + uint64_t result = process->ReadUnsignedIntegerFromMemory (addr, 4, LLDB_INVALID_ADDRESS, read_error); + if (result != llvm::MachO::MH_MAGIC_64 + && result != llvm::MachO::MH_MAGIC + && result != llvm::MachO::MH_CIGAM + && result != llvm::MachO::MH_CIGAM_64) + { + return UUID(); + } + + // Read the mach header and see whether it looks like a kernel + llvm::MachO::mach_header header; + if (process->DoReadMemory (addr, &header, sizeof(header), read_error) != sizeof(header)) + return UUID(); + + if (header.magic == llvm::MachO::MH_CIGAM || + header.magic == llvm::MachO::MH_CIGAM_64) + { + header.magic = llvm::ByteSwap_32(header.magic); + header.cputype = llvm::ByteSwap_32(header.cputype); + header.cpusubtype = llvm::ByteSwap_32(header.cpusubtype); + header.filetype = llvm::ByteSwap_32(header.filetype); + header.ncmds = llvm::ByteSwap_32(header.ncmds); + header.sizeofcmds = llvm::ByteSwap_32(header.sizeofcmds); + header.flags = llvm::ByteSwap_32(header.flags); + } + + // A kernel is an executable which does not have the dynamic link object flag set. + if (header.filetype == llvm::MachO::MH_EXECUTE + && (header.flags & llvm::MachO::MH_DYLDLINK) == 0) + { + // Create a full module to get the UUID + ModuleSP memory_module_sp = process->ReadModuleFromMemory (FileSpec ("temp_mach_kernel", false), addr); + if (!memory_module_sp.get()) + return UUID(); + + ObjectFile *exe_objfile = memory_module_sp->GetObjectFile(); + if (exe_objfile == NULL) + return UUID(); + + if (exe_objfile->GetType() == ObjectFile::eTypeExecutable && exe_objfile->GetStrata() == ObjectFile::eStrataKernel) + { + ArchSpec kernel_arch (eArchTypeMachO, header.cputype, header.cpusubtype); + if (!process->GetTarget().GetArchitecture().IsCompatibleMatch(kernel_arch)) + { + process->GetTarget().SetArchitecture (kernel_arch); + } + return memory_module_sp->GetUUID(); + } + } + + return UUID(); +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DynamicLoaderDarwinKernel::DynamicLoaderDarwinKernel (Process* process, lldb::addr_t kernel_addr) : + DynamicLoader(process), + m_kernel_load_address (kernel_addr), + m_kernel(), + m_kext_summary_header_ptr_addr (), + m_kext_summary_header_addr (), + m_kext_summary_header (), + m_known_kexts (), + m_mutex(Mutex::eMutexTypeRecursive), + m_break_id (LLDB_INVALID_BREAK_ID) +{ + Error error; + PlatformSP platform_sp(Platform::Create(PlatformDarwinKernel::GetPluginNameStatic(), error)); + // Only select the darwin-kernel Platform if we've been asked to load kexts. + // It can take some time to scan over all of the kext info.plists and that + // shouldn't be done if kext loading is explicitly disabled. + if (platform_sp.get() && GetGlobalProperties()->GetLoadKexts()) + { + process->GetTarget().SetPlatform (platform_sp); + } +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoaderDarwinKernel::~DynamicLoaderDarwinKernel() +{ + Clear(true); +} + +void +DynamicLoaderDarwinKernel::UpdateIfNeeded() +{ + LoadKernelModuleIfNeeded(); + SetNotificationBreakpointIfNeeded (); +} +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderDarwinKernel::DidAttach () +{ + PrivateInitialize(m_process); + UpdateIfNeeded(); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderDarwinKernel::DidLaunch () +{ + PrivateInitialize(m_process); + UpdateIfNeeded(); +} + + +//---------------------------------------------------------------------- +// Clear out the state of this class. +//---------------------------------------------------------------------- +void +DynamicLoaderDarwinKernel::Clear (bool clear_process) +{ + Mutex::Locker locker(m_mutex); + + if (m_process->IsAlive() && LLDB_BREAK_ID_IS_VALID(m_break_id)) + m_process->ClearBreakpointSiteByID(m_break_id); + + if (clear_process) + m_process = NULL; + m_kernel.Clear(); + m_known_kexts.clear(); + m_kext_summary_header_ptr_addr.Clear(); + m_kext_summary_header_addr.Clear(); + m_break_id = LLDB_INVALID_BREAK_ID; +} + + +bool +DynamicLoaderDarwinKernel::KextImageInfo::LoadImageAtFileAddress (Process *process) +{ + if (IsLoaded()) + return true; + + if (m_module_sp) + { + bool changed = false; + if (m_module_sp->SetLoadAddress (process->GetTarget(), 0, true, changed)) + m_load_process_stop_id = process->GetStopID(); + } + return false; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetModule (ModuleSP module_sp) +{ + m_module_sp = module_sp; + if (module_sp.get() && module_sp->GetObjectFile()) + { + if (module_sp->GetObjectFile()->GetType() == ObjectFile::eTypeExecutable + && module_sp->GetObjectFile()->GetStrata() == ObjectFile::eStrataKernel) + { + m_kernel_image = true; + } + else + { + m_kernel_image = false; + } + } +} + +ModuleSP +DynamicLoaderDarwinKernel::KextImageInfo::GetModule () +{ + return m_module_sp; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetLoadAddress (addr_t load_addr) +{ + m_load_address = load_addr; +} + +addr_t +DynamicLoaderDarwinKernel::KextImageInfo::GetLoadAddress () const +{ + return m_load_address; +} + +uint64_t +DynamicLoaderDarwinKernel::KextImageInfo::GetSize () const +{ + return m_size; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetSize (uint64_t size) +{ + m_size = size; +} + +uint32_t +DynamicLoaderDarwinKernel::KextImageInfo::GetProcessStopId () const +{ + return m_load_process_stop_id; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetProcessStopId (uint32_t stop_id) +{ + m_load_process_stop_id = stop_id; +} + +bool +DynamicLoaderDarwinKernel::KextImageInfo::operator== (const KextImageInfo &rhs) +{ + if (m_uuid.IsValid() || rhs.GetUUID().IsValid()) + { + if (m_uuid == rhs.GetUUID()) + { + return true; + } + return false; + } + + if (m_name == rhs.GetName() && m_load_address == rhs.GetLoadAddress()) + return true; + + return false; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetName (const char *name) +{ + m_name = name; +} + +std::string +DynamicLoaderDarwinKernel::KextImageInfo::GetName () const +{ + return m_name; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetUUID (const UUID &uuid) +{ + m_uuid = uuid; +} + +UUID +DynamicLoaderDarwinKernel::KextImageInfo::GetUUID () const +{ + return m_uuid; +} + +// Given the m_load_address from the kext summaries, and a UUID, try to create an in-memory +// Module at that address. Require that the MemoryModule have a matching UUID and detect +// if this MemoryModule is a kernel or a kext. +// +// Returns true if m_memory_module_sp is now set to a valid Module. + +bool +DynamicLoaderDarwinKernel::KextImageInfo::ReadMemoryModule (Process *process) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (m_memory_module_sp.get() != NULL) + return true; + if (m_load_address == LLDB_INVALID_ADDRESS) + return false; + + FileSpec file_spec; + file_spec.SetFile (m_name.c_str(), false); + + ModuleSP memory_module_sp = process->ReadModuleFromMemory (file_spec, m_load_address); + + if (memory_module_sp.get() == NULL) + return false; + + bool is_kernel = false; + if (memory_module_sp->GetObjectFile()) + { + if (memory_module_sp->GetObjectFile()->GetType() == ObjectFile::eTypeExecutable + && memory_module_sp->GetObjectFile()->GetStrata() == ObjectFile::eStrataKernel) + { + is_kernel = true; + } + else if (memory_module_sp->GetObjectFile()->GetType() == ObjectFile::eTypeSharedLibrary) + { + is_kernel = false; + } + } + + // If this is a kext, and the kernel specified what UUID we should find at this + // load address, require that the memory module have a matching UUID or something + // has gone wrong and we should discard it. + if (m_uuid.IsValid()) + { + if (m_uuid != memory_module_sp->GetUUID()) + { + if (log) + { + log->Printf ("KextImageInfo::ReadMemoryModule the kernel said to find uuid %s at 0x%" PRIx64 " but instead we found uuid %s, throwing it away", m_uuid.GetAsString().c_str(), m_load_address, memory_module_sp->GetUUID().GetAsString().c_str()); + } + return false; + } + } + + // If the in-memory Module has a UUID, let's use that. + if (!m_uuid.IsValid() && memory_module_sp->GetUUID().IsValid()) + { + m_uuid = memory_module_sp->GetUUID(); + } + + m_memory_module_sp = memory_module_sp; + m_kernel_image = is_kernel; + if (is_kernel) + { + if (log) + { + // This is unusual and probably not intended + log->Printf ("KextImageInfo::ReadMemoryModule read the kernel binary out of memory"); + } + if (memory_module_sp->GetArchitecture().IsValid()) + { + process->GetTarget().SetArchitecture(memory_module_sp->GetArchitecture()); + } + if (m_uuid.IsValid()) + { + ModuleSP exe_module_sp = process->GetTarget().GetExecutableModule(); + if (exe_module_sp.get() && exe_module_sp->GetUUID().IsValid()) + { + if (m_uuid != exe_module_sp->GetUUID()) + { + // The user specified a kernel binary that has a different UUID than + // the kernel actually running in memory. This never ends well; + // clear the user specified kernel binary from the Target. + + m_module_sp.reset(); + + ModuleList user_specified_kernel_list; + user_specified_kernel_list.Append (exe_module_sp); + process->GetTarget().GetImages().Remove (user_specified_kernel_list); + } + } + } + } + + return true; +} + +bool +DynamicLoaderDarwinKernel::KextImageInfo::IsKernel () const +{ + return m_kernel_image == true; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetIsKernel (bool is_kernel) +{ + m_kernel_image = is_kernel; +} + +bool +DynamicLoaderDarwinKernel::KextImageInfo::LoadImageUsingMemoryModule (Process *process) +{ + if (IsLoaded()) + return true; + + + Target &target = process->GetTarget(); + + // If we don't have / can't create a memory module for this kext, don't try to load it - we won't + // have the correct segment load addresses. + if (!ReadMemoryModule (process)) + { + return false; + } + + bool uuid_is_valid = m_uuid.IsValid(); + + if (IsKernel() && uuid_is_valid && m_memory_module_sp.get()) + { + Stream *s = target.GetDebugger().GetOutputFile().get(); + if (s) + { + s->Printf ("Kernel UUID: %s\n", m_memory_module_sp->GetUUID().GetAsString().c_str()); + s->Printf ("Load Address: 0x%" PRIx64 "\n", m_load_address); + } + } + + if (!m_module_sp) + { + // See if the kext has already been loaded into the target, probably by the user doing target modules add. + const ModuleList &target_images = target.GetImages(); + m_module_sp = target_images.FindModule(m_uuid); + + // Search for the kext on the local filesystem via the UUID + if (!m_module_sp && uuid_is_valid) + { + ModuleSpec module_spec; + module_spec.GetUUID() = m_uuid; + module_spec.GetArchitecture() = target.GetArchitecture(); + + // For the kernel, we really do need an on-disk file copy of the binary to do anything useful. + // This will force a clal to + if (IsKernel()) + { + if (Symbols::DownloadObjectAndSymbolFile (module_spec, true)) + { + if (module_spec.GetFileSpec().Exists()) + { + m_module_sp.reset(new Module (module_spec.GetFileSpec(), target.GetArchitecture())); + if (m_module_sp.get() && m_module_sp->MatchesModuleSpec (module_spec)) + { + ModuleList loaded_module_list; + loaded_module_list.Append (m_module_sp); + target.ModulesDidLoad (loaded_module_list); + } + } + } + } + + // If the current platform is PlatformDarwinKernel, create a ModuleSpec with the filename set + // to be the bundle ID for this kext, e.g. "com.apple.filesystems.msdosfs", and ask the platform + // to find it. + PlatformSP platform_sp (target.GetPlatform()); + if (!m_module_sp && platform_sp) + { + ConstString platform_name (platform_sp->GetPluginName()); + static ConstString g_platform_name (PlatformDarwinKernel::GetPluginNameStatic()); + if (platform_name == g_platform_name) + { + ModuleSpec kext_bundle_module_spec(module_spec); + FileSpec kext_filespec(m_name.c_str(), false); + kext_bundle_module_spec.GetFileSpec() = kext_filespec; + platform_sp->GetSharedModule (kext_bundle_module_spec, process, m_module_sp, &target.GetExecutableSearchPaths(), NULL, NULL); + } + } + + // Ask the Target to find this file on the local system, if possible. + // This will search in the list of currently-loaded files, look in the + // standard search paths on the system, and on a Mac it will try calling + // the DebugSymbols framework with the UUID to find the binary via its + // search methods. + if (!m_module_sp) + { + m_module_sp = target.GetSharedModule (module_spec); + } + + if (IsKernel() && !m_module_sp) + { + Stream *s = target.GetDebugger().GetOutputFile().get(); + if (s) + { + s->Printf ("WARNING: Unable to locate kernel binary on the debugger system.\n"); + } + } + } + + // If we managed to find a module, append it to the target's list of images. + // If we also have a memory module, require that they have matching UUIDs + if (m_module_sp) + { + bool uuid_match_ok = true; + if (m_memory_module_sp) + { + if (m_module_sp->GetUUID() != m_memory_module_sp->GetUUID()) + { + uuid_match_ok = false; + } + } + if (uuid_match_ok) + { + target.GetImages().AppendIfNeeded(m_module_sp); + if (IsKernel() && target.GetExecutableModulePointer() != m_module_sp.get()) + { + target.SetExecutableModule (m_module_sp, false); + } + } + } + } + + if (!m_module_sp && !IsKernel() && m_uuid.IsValid() && !m_name.empty()) + { + Stream *s = target.GetDebugger().GetOutputFile().get(); + if (s) + { + s->Printf ("warning: Can't find binary/dSYM for %s (%s)\n", + m_name.c_str(), m_uuid.GetAsString().c_str()); + } + } + + static ConstString g_section_name_LINKEDIT ("__LINKEDIT"); + + if (m_memory_module_sp && m_module_sp) + { + if (m_module_sp->GetUUID() == m_memory_module_sp->GetUUID()) + { + ObjectFile *ondisk_object_file = m_module_sp->GetObjectFile(); + ObjectFile *memory_object_file = m_memory_module_sp->GetObjectFile(); + + if (memory_object_file && ondisk_object_file) + { + // The memory_module for kexts may have an invalid __LINKEDIT seg; skip it. + const bool ignore_linkedit = !IsKernel (); + + SectionList *ondisk_section_list = ondisk_object_file->GetSectionList (); + SectionList *memory_section_list = memory_object_file->GetSectionList (); + if (memory_section_list && ondisk_section_list) + { + const uint32_t num_ondisk_sections = ondisk_section_list->GetSize(); + // There may be CTF sections in the memory image so we can't + // always just compare the number of sections (which are actually + // segments in mach-o parlance) + uint32_t sect_idx = 0; + + // Use the memory_module's addresses for each section to set the + // file module's load address as appropriate. We don't want to use + // a single slide value for the entire kext - different segments may + // be slid different amounts by the kext loader. + + uint32_t num_sections_loaded = 0; + for (sect_idx=0; sect_idx<num_ondisk_sections; ++sect_idx) + { + SectionSP ondisk_section_sp(ondisk_section_list->GetSectionAtIndex(sect_idx)); + if (ondisk_section_sp) + { + // Don't ever load __LINKEDIT as it may or may not be actually + // mapped into memory and there is no current way to tell. + // I filed rdar://problem/12851706 to track being able to tell + // if the __LINKEDIT is actually mapped, but until then, we need + // to not load the __LINKEDIT + if (ignore_linkedit && ondisk_section_sp->GetName() == g_section_name_LINKEDIT) + continue; + + const Section *memory_section = memory_section_list->FindSectionByName(ondisk_section_sp->GetName()).get(); + if (memory_section) + { + target.SetSectionLoadAddress (ondisk_section_sp, memory_section->GetFileAddress()); + ++num_sections_loaded; + } + } + } + if (num_sections_loaded > 0) + m_load_process_stop_id = process->GetStopID(); + else + m_module_sp.reset(); // No sections were loaded + } + else + m_module_sp.reset(); // One or both section lists + } + else + m_module_sp.reset(); // One or both object files missing + } + else + m_module_sp.reset(); // UUID mismatch + } + + bool is_loaded = IsLoaded(); + + if (is_loaded && m_module_sp && IsKernel()) + { + Stream *s = target.GetDebugger().GetOutputFile().get(); + if (s) + { + ObjectFile *kernel_object_file = m_module_sp->GetObjectFile(); + if (kernel_object_file) + { + addr_t file_address = kernel_object_file->GetHeaderAddress().GetFileAddress(); + if (m_load_address != LLDB_INVALID_ADDRESS && file_address != LLDB_INVALID_ADDRESS) + { + s->Printf ("Kernel slid 0x%" PRIx64 " in memory.\n", m_load_address - file_address); + } + } + { + s->Printf ("Loaded kernel file %s\n", + m_module_sp->GetFileSpec().GetPath().c_str()); + } + s->Flush (); + } + } + return is_loaded; +} + +uint32_t +DynamicLoaderDarwinKernel::KextImageInfo::GetAddressByteSize () +{ + if (m_memory_module_sp) + return m_memory_module_sp->GetArchitecture().GetAddressByteSize(); + if (m_module_sp) + return m_module_sp->GetArchitecture().GetAddressByteSize(); + return 0; +} + +lldb::ByteOrder +DynamicLoaderDarwinKernel::KextImageInfo::GetByteOrder() +{ + if (m_memory_module_sp) + return m_memory_module_sp->GetArchitecture().GetByteOrder(); + if (m_module_sp) + return m_module_sp->GetArchitecture().GetByteOrder(); + return endian::InlHostByteOrder(); +} + +lldb_private::ArchSpec +DynamicLoaderDarwinKernel::KextImageInfo::GetArchitecture () const +{ + if (m_memory_module_sp) + return m_memory_module_sp->GetArchitecture(); + if (m_module_sp) + return m_module_sp->GetArchitecture(); + return lldb_private::ArchSpec (); +} + + +//---------------------------------------------------------------------- +// Load the kernel module and initialize the "m_kernel" member. Return +// true _only_ if the kernel is loaded the first time through (subsequent +// calls to this function should return false after the kernel has been +// already loaded). +//---------------------------------------------------------------------- +void +DynamicLoaderDarwinKernel::LoadKernelModuleIfNeeded() +{ + if (!m_kext_summary_header_ptr_addr.IsValid()) + { + m_kernel.Clear(); + m_kernel.SetModule (m_process->GetTarget().GetExecutableModule()); + m_kernel.SetIsKernel(true); + + ConstString kernel_name("mach_kernel"); + if (m_kernel.GetModule().get() + && m_kernel.GetModule()->GetObjectFile() + && !m_kernel.GetModule()->GetObjectFile()->GetFileSpec().GetFilename().IsEmpty()) + { + kernel_name = m_kernel.GetModule()->GetObjectFile()->GetFileSpec().GetFilename(); + } + m_kernel.SetName (kernel_name.AsCString()); + + if (m_kernel.GetLoadAddress() == LLDB_INVALID_ADDRESS) + { + m_kernel.SetLoadAddress(m_kernel_load_address); + if (m_kernel.GetLoadAddress() == LLDB_INVALID_ADDRESS && m_kernel.GetModule()) + { + // We didn't get a hint from the process, so we will + // try the kernel at the address that it exists at in + // the file if we have one + ObjectFile *kernel_object_file = m_kernel.GetModule()->GetObjectFile(); + if (kernel_object_file) + { + addr_t load_address = kernel_object_file->GetHeaderAddress().GetLoadAddress(&m_process->GetTarget()); + addr_t file_address = kernel_object_file->GetHeaderAddress().GetFileAddress(); + if (load_address != LLDB_INVALID_ADDRESS && load_address != 0) + { + m_kernel.SetLoadAddress (load_address); + if (load_address != file_address) + { + // Don't accidentally relocate the kernel to the File address -- + // the Load address has already been set to its actual in-memory address. + // Mark it as IsLoaded. + m_kernel.SetProcessStopId (m_process->GetStopID()); + } + } + else + { + m_kernel.SetLoadAddress(file_address); + } + } + } + } + + if (m_kernel.GetLoadAddress() != LLDB_INVALID_ADDRESS) + { + if (!m_kernel.LoadImageUsingMemoryModule (m_process)) + { + m_kernel.LoadImageAtFileAddress (m_process); + } + } + + if (m_kernel.IsLoaded() && m_kernel.GetModule()) + { + static ConstString kext_summary_symbol ("gLoadedKextSummaries"); + const Symbol *symbol = m_kernel.GetModule()->FindFirstSymbolWithNameAndType (kext_summary_symbol, eSymbolTypeData); + if (symbol) + { + m_kext_summary_header_ptr_addr = symbol->GetAddress(); + // Update all image infos + ReadAllKextSummaries (); + } + } + else + { + m_kernel.Clear(); + } + } +} + +//---------------------------------------------------------------------- +// Static callback function that gets called when our DYLD notification +// breakpoint gets hit. We update all of our image infos and then +// let our super class DynamicLoader class decide if we should stop +// or not (based on global preference). +//---------------------------------------------------------------------- +bool +DynamicLoaderDarwinKernel::BreakpointHitCallback (void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + return static_cast<DynamicLoaderDarwinKernel*>(baton)->BreakpointHit (context, break_id, break_loc_id); +} + +bool +DynamicLoaderDarwinKernel::BreakpointHit (StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + Log *log(GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + if (log) + log->Printf ("DynamicLoaderDarwinKernel::BreakpointHit (...)\n"); + + ReadAllKextSummaries (); + + if (log) + PutToLog(log); + + return GetStopWhenImagesChange(); +} + + +bool +DynamicLoaderDarwinKernel::ReadKextSummaryHeader () +{ + Mutex::Locker locker(m_mutex); + + // the all image infos is already valid for this process stop ID + + if (m_kext_summary_header_ptr_addr.IsValid()) + { + const uint32_t addr_size = m_kernel.GetAddressByteSize (); + const ByteOrder byte_order = m_kernel.GetByteOrder(); + Error error; + // Read enough bytes for a "OSKextLoadedKextSummaryHeader" structure + // which is currently 4 uint32_t and a pointer. + uint8_t buf[24]; + DataExtractor data (buf, sizeof(buf), byte_order, addr_size); + const size_t count = 4 * sizeof(uint32_t) + addr_size; + const bool prefer_file_cache = false; + if (m_process->GetTarget().ReadPointerFromMemory (m_kext_summary_header_ptr_addr, + prefer_file_cache, + error, + m_kext_summary_header_addr)) + { + // We got a valid address for our kext summary header and make sure it isn't NULL + if (m_kext_summary_header_addr.IsValid() && + m_kext_summary_header_addr.GetFileAddress() != 0) + { + const size_t bytes_read = m_process->GetTarget().ReadMemory (m_kext_summary_header_addr, prefer_file_cache, buf, count, error); + if (bytes_read == count) + { + lldb::offset_t offset = 0; + m_kext_summary_header.version = data.GetU32(&offset); + if (m_kext_summary_header.version > 128) + { + Stream *s = m_process->GetTarget().GetDebugger().GetOutputFile().get(); + s->Printf ("WARNING: Unable to read kext summary header, got improbable version number %u\n", m_kext_summary_header.version); + // If we get an improbably large version number, we're probably getting bad memory. + m_kext_summary_header_addr.Clear(); + return false; + } + if (m_kext_summary_header.version >= 2) + { + m_kext_summary_header.entry_size = data.GetU32(&offset); + if (m_kext_summary_header.entry_size > 4096) + { + // If we get an improbably large entry_size, we're probably getting bad memory. + Stream *s = m_process->GetTarget().GetDebugger().GetOutputFile().get(); + s->Printf ("WARNING: Unable to read kext summary header, got improbable entry_size %u\n", m_kext_summary_header.entry_size); + m_kext_summary_header_addr.Clear(); + return false; + } + } + else + { + // Versions less than 2 didn't have an entry size, it was hard coded + m_kext_summary_header.entry_size = KERNEL_MODULE_ENTRY_SIZE_VERSION_1; + } + m_kext_summary_header.entry_count = data.GetU32(&offset); + if (m_kext_summary_header.entry_count > 10000) + { + // If we get an improbably large number of kexts, we're probably getting bad memory. + Stream *s = m_process->GetTarget().GetDebugger().GetOutputFile().get(); + s->Printf ("WARNING: Unable to read kext summary header, got improbable number of kexts %u\n", m_kext_summary_header.entry_count); + m_kext_summary_header_addr.Clear(); + return false; + } + return true; + } + } + } + } + m_kext_summary_header_addr.Clear(); + return false; +} + +// We've either (a) just attached to a new kernel, or (b) the kexts-changed breakpoint was hit +// and we need to figure out what kexts have been added or removed. +// Read the kext summaries from the inferior kernel memory, compare them against the +// m_known_kexts vector and update the m_known_kexts vector as needed to keep in sync with the +// inferior. + +bool +DynamicLoaderDarwinKernel::ParseKextSummaries (const Address &kext_summary_addr, uint32_t count) +{ + KextImageInfo::collection kext_summaries; + Log *log(GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + if (log) + log->Printf ("Kexts-changed breakpoint hit, there are %d kexts currently.\n", count); + + Mutex::Locker locker(m_mutex); + + if (!ReadKextSummaries (kext_summary_addr, count, kext_summaries)) + return false; + + // read the plugin.dynamic-loader.darwin-kernel.load-kexts setting -- if the user requested no + // kext loading, don't print any messages about kexts & don't try to read them. + const bool load_kexts = GetGlobalProperties()->GetLoadKexts(); + + // By default, all kexts we've loaded in the past are marked as "remove" and all of the kexts + // we just found out about from ReadKextSummaries are marked as "add". + std::vector<bool> to_be_removed(m_known_kexts.size(), true); + std::vector<bool> to_be_added(count, true); + + int number_of_new_kexts_being_added = 0; + int number_of_old_kexts_being_removed = m_known_kexts.size(); + + const uint32_t new_kexts_size = kext_summaries.size(); + const uint32_t old_kexts_size = m_known_kexts.size(); + + // The m_known_kexts vector may have entries that have been Cleared, + // or are a kernel. + for (uint32_t old_kext = 0; old_kext < old_kexts_size; old_kext++) + { + bool ignore = false; + KextImageInfo &image_info = m_known_kexts[old_kext]; + if (image_info.IsKernel()) + { + ignore = true; + } + else if (image_info.GetLoadAddress() == LLDB_INVALID_ADDRESS && !image_info.GetModule()) + { + ignore = true; + } + + if (ignore) + { + number_of_old_kexts_being_removed--; + to_be_removed[old_kext] = false; + } + } + + // Scan over the list of kexts we just read from the kernel, note those that + // need to be added and those already loaded. + for (uint32_t new_kext = 0; new_kext < new_kexts_size; new_kext++) + { + bool add_this_one = true; + for (uint32_t old_kext = 0; old_kext < old_kexts_size; old_kext++) + { + if (m_known_kexts[old_kext] == kext_summaries[new_kext]) + { + // We already have this kext, don't re-load it. + to_be_added[new_kext] = false; + // This kext is still present, do not remove it. + to_be_removed[old_kext] = false; + + number_of_old_kexts_being_removed--; + add_this_one = false; + break; + } + } + if (add_this_one) + { + number_of_new_kexts_being_added++; + } + } + + if (number_of_new_kexts_being_added == 0 && number_of_old_kexts_being_removed == 0) + return true; + + Stream *s = m_process->GetTarget().GetDebugger().GetOutputFile().get(); + if (s && load_kexts) + { + if (number_of_new_kexts_being_added > 0 && number_of_old_kexts_being_removed > 0) + { + s->Printf ("Loading %d kext modules and unloading %d kext modules ", number_of_new_kexts_being_added, number_of_old_kexts_being_removed); + } + else if (number_of_new_kexts_being_added > 0) + { + s->Printf ("Loading %d kext modules ", number_of_new_kexts_being_added); + } + else if (number_of_old_kexts_being_removed > 0) + { + s->Printf ("Unloading %d kext modules ", number_of_old_kexts_being_removed); + } + } + + if (log) + { + if (load_kexts) + { + log->Printf ("DynamicLoaderDarwinKernel::ParseKextSummaries: %d kexts added, %d kexts removed", number_of_new_kexts_being_added, number_of_old_kexts_being_removed); + } + else + { + log->Printf ("DynamicLoaderDarwinKernel::ParseKextSummaries kext loading is disabled, else would have %d kexts added, %d kexts removed", number_of_new_kexts_being_added, number_of_old_kexts_being_removed); + } + } + + + if (number_of_new_kexts_being_added > 0) + { + ModuleList loaded_module_list; + + const uint32_t num_of_new_kexts = kext_summaries.size(); + for (uint32_t new_kext = 0; new_kext < num_of_new_kexts; new_kext++) + { + if (to_be_added[new_kext] == true) + { + KextImageInfo &image_info = kext_summaries[new_kext]; + if (load_kexts) + { + if (!image_info.LoadImageUsingMemoryModule (m_process)) + { + image_info.LoadImageAtFileAddress (m_process); + } + } + + m_known_kexts.push_back(image_info); + + if (image_info.GetModule() && m_process->GetStopID() == image_info.GetProcessStopId()) + loaded_module_list.AppendIfNeeded (image_info.GetModule()); + + if (s && load_kexts) + s->Printf ("."); + + if (log) + kext_summaries[new_kext].PutToLog (log); + } + } + m_process->GetTarget().ModulesDidLoad (loaded_module_list); + } + + if (number_of_old_kexts_being_removed > 0) + { + ModuleList loaded_module_list; + const uint32_t num_of_old_kexts = m_known_kexts.size(); + for (uint32_t old_kext = 0; old_kext < num_of_old_kexts; old_kext++) + { + ModuleList unloaded_module_list; + if (to_be_removed[old_kext]) + { + KextImageInfo &image_info = m_known_kexts[old_kext]; + // You can't unload the kernel. + if (!image_info.IsKernel()) + { + if (image_info.GetModule()) + { + unloaded_module_list.AppendIfNeeded (image_info.GetModule()); + } + if (s) + s->Printf ("."); + image_info.Clear(); + // should pull it out of the KextImageInfos vector but that would mutate the list and invalidate + // the to_be_removed bool vector; leaving it in place once Cleared() is relatively harmless. + } + } + m_process->GetTarget().ModulesDidUnload (unloaded_module_list, false); + } + } + + if (s && load_kexts) + { + s->Printf (" done.\n"); + s->Flush (); + } + + return true; +} + +uint32_t +DynamicLoaderDarwinKernel::ReadKextSummaries (const Address &kext_summary_addr, + uint32_t image_infos_count, + KextImageInfo::collection &image_infos) +{ + const ByteOrder endian = m_kernel.GetByteOrder(); + const uint32_t addr_size = m_kernel.GetAddressByteSize(); + + image_infos.resize(image_infos_count); + const size_t count = image_infos.size() * m_kext_summary_header.entry_size; + DataBufferHeap data(count, 0); + Error error; + + const bool prefer_file_cache = false; + const size_t bytes_read = m_process->GetTarget().ReadMemory (kext_summary_addr, + prefer_file_cache, + data.GetBytes(), + data.GetByteSize(), + error); + if (bytes_read == count) + { + + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), endian, addr_size); + uint32_t i=0; + for (uint32_t kext_summary_offset = 0; + i < image_infos.size() && extractor.ValidOffsetForDataOfSize(kext_summary_offset, m_kext_summary_header.entry_size); + ++i, kext_summary_offset += m_kext_summary_header.entry_size) + { + lldb::offset_t offset = kext_summary_offset; + const void *name_data = extractor.GetData(&offset, KERNEL_MODULE_MAX_NAME); + if (name_data == NULL) + break; + image_infos[i].SetName ((const char *) name_data); + UUID uuid (extractor.GetData (&offset, 16), 16); + image_infos[i].SetUUID (uuid); + image_infos[i].SetLoadAddress (extractor.GetU64(&offset)); + image_infos[i].SetSize (extractor.GetU64(&offset)); + } + if (i < image_infos.size()) + image_infos.resize(i); + } + else + { + image_infos.clear(); + } + return image_infos.size(); +} + +bool +DynamicLoaderDarwinKernel::ReadAllKextSummaries () +{ + Mutex::Locker locker(m_mutex); + + if (ReadKextSummaryHeader ()) + { + if (m_kext_summary_header.entry_count > 0 && m_kext_summary_header_addr.IsValid()) + { + Address summary_addr (m_kext_summary_header_addr); + summary_addr.Slide(m_kext_summary_header.GetSize()); + if (!ParseKextSummaries (summary_addr, m_kext_summary_header.entry_count)) + { + m_known_kexts.clear(); + } + return true; + } + } + return false; +} + +//---------------------------------------------------------------------- +// Dump an image info structure to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderDarwinKernel::KextImageInfo::PutToLog (Log *log) const +{ + if (log == NULL) + return; + const uint8_t *u = (uint8_t *) m_uuid.GetBytes(); + + if (m_load_address == LLDB_INVALID_ADDRESS) + { + if (u) + { + log->Printf("\tuuid=%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X name=\"%s\" (UNLOADED)", + u[ 0], u[ 1], u[ 2], u[ 3], + u[ 4], u[ 5], u[ 6], u[ 7], + u[ 8], u[ 9], u[10], u[11], + u[12], u[13], u[14], u[15], + m_name.c_str()); + } + else + log->Printf("\tname=\"%s\" (UNLOADED)", m_name.c_str()); + } + else + { + if (u) + { + log->Printf("\taddr=0x%16.16" PRIx64 " size=0x%16.16" PRIx64 " uuid=%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X name=\"%s\"", + m_load_address, m_size, + u[ 0], u[ 1], u[ 2], u[ 3], u[ 4], u[ 5], u[ 6], u[ 7], + u[ 8], u[ 9], u[10], u[11], u[12], u[13], u[14], u[15], + m_name.c_str()); + } + else + { + log->Printf("\t[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") name=\"%s\"", + m_load_address, m_load_address+m_size, m_name.c_str()); + } + } +} + +//---------------------------------------------------------------------- +// Dump the _dyld_all_image_infos members and all current image infos +// that we have parsed to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderDarwinKernel::PutToLog(Log *log) const +{ + if (log == NULL) + return; + + Mutex::Locker locker(m_mutex); + log->Printf("gLoadedKextSummaries = 0x%16.16" PRIx64 " { version=%u, entry_size=%u, entry_count=%u }", + m_kext_summary_header_addr.GetFileAddress(), + m_kext_summary_header.version, + m_kext_summary_header.entry_size, + m_kext_summary_header.entry_count); + + size_t i; + const size_t count = m_known_kexts.size(); + if (count > 0) + { + log->PutCString("Loaded:"); + for (i = 0; i<count; i++) + m_known_kexts[i].PutToLog(log); + } +} + +void +DynamicLoaderDarwinKernel::PrivateInitialize(Process *process) +{ + DEBUG_PRINTF("DynamicLoaderDarwinKernel::%s() process state = %s\n", __FUNCTION__, StateAsCString(m_process->GetState())); + Clear(true); + m_process = process; +} + +void +DynamicLoaderDarwinKernel::SetNotificationBreakpointIfNeeded () +{ + if (m_break_id == LLDB_INVALID_BREAK_ID && m_kernel.GetModule()) + { + DEBUG_PRINTF("DynamicLoaderDarwinKernel::%s() process state = %s\n", __FUNCTION__, StateAsCString(m_process->GetState())); + + + const bool internal_bp = true; + const bool hardware = false; + const LazyBool skip_prologue = eLazyBoolNo; + FileSpecList module_spec_list; + module_spec_list.Append (m_kernel.GetModule()->GetFileSpec()); + Breakpoint *bp = m_process->GetTarget().CreateBreakpoint (&module_spec_list, + NULL, + "OSKextLoadedKextSummariesUpdated", + eFunctionNameTypeFull, + eLanguageTypeUnknown, + skip_prologue, + internal_bp, + hardware).get(); + + bp->SetCallback (DynamicLoaderDarwinKernel::BreakpointHitCallback, this, true); + m_break_id = bp->GetID(); + } +} + +//---------------------------------------------------------------------- +// Member function that gets called when the process state changes. +//---------------------------------------------------------------------- +void +DynamicLoaderDarwinKernel::PrivateProcessStateChanged (Process *process, StateType state) +{ + DEBUG_PRINTF("DynamicLoaderDarwinKernel::%s(%s)\n", __FUNCTION__, StateAsCString(state)); + switch (state) + { + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + case eStateDetached: + Clear(false); + break; + + case eStateStopped: + UpdateIfNeeded(); + break; + + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + break; + } +} + +ThreadPlanSP +DynamicLoaderDarwinKernel::GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) +{ + ThreadPlanSP thread_plan_sp; + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + log->Printf ("Could not find symbol for step through."); + return thread_plan_sp; +} + +Error +DynamicLoaderDarwinKernel::CanLoadImage () +{ + Error error; + error.SetErrorString("always unsafe to load or unload shared libraries in the darwin kernel"); + return error; +} + +void +DynamicLoaderDarwinKernel::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + DebuggerInitialize); +} + +void +DynamicLoaderDarwinKernel::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +void +DynamicLoaderDarwinKernel::DebuggerInitialize (lldb_private::Debugger &debugger) +{ + if (!PluginManager::GetSettingForDynamicLoaderPlugin (debugger, DynamicLoaderDarwinKernelProperties::GetSettingName())) + { + const bool is_global_setting = true; + PluginManager::CreateSettingForDynamicLoaderPlugin (debugger, + GetGlobalProperties()->GetValueProperties(), + ConstString ("Properties for the DynamicLoaderDarwinKernel plug-in."), + is_global_setting); + } +} + +lldb_private::ConstString +DynamicLoaderDarwinKernel::GetPluginNameStatic() +{ + static ConstString g_name("darwin-kernel"); + return g_name; +} + +const char * +DynamicLoaderDarwinKernel::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library loads/unloads in the MacOSX kernel."; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +DynamicLoaderDarwinKernel::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DynamicLoaderDarwinKernel::GetPluginVersion() +{ + return 1; +} + +lldb::ByteOrder +DynamicLoaderDarwinKernel::GetByteOrderFromMagic (uint32_t magic) +{ + switch (magic) + { + case llvm::MachO::MH_MAGIC: + case llvm::MachO::MH_MAGIC_64: + return endian::InlHostByteOrder(); + + case llvm::MachO::MH_CIGAM: + case llvm::MachO::MH_CIGAM_64: + if (endian::InlHostByteOrder() == lldb::eByteOrderBig) + return lldb::eByteOrderLittle; + else + return lldb::eByteOrderBig; + + default: + break; + } + return lldb::eByteOrderInvalid; +} + diff --git a/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h b/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h new file mode 100644 index 000000000000..7ebda48cec93 --- /dev/null +++ b/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h @@ -0,0 +1,371 @@ +//===-- DynamicLoaderDarwinKernel.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderDarwinKernel_h_ +#define liblldb_DynamicLoaderDarwinKernel_h_ + +// C Includes +// C++ Includes +#include <vector> +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" + +class DynamicLoaderDarwinKernel : public lldb_private::DynamicLoader +{ +public: + DynamicLoaderDarwinKernel(lldb_private::Process *process, lldb::addr_t kernel_addr); + + ~DynamicLoaderDarwinKernel() override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance (lldb_private::Process *process, bool force); + + static void + DebuggerInitialize (lldb_private::Debugger &debugger); + + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + void + DidAttach() override; + + void + DidLaunch() override; + + lldb::ThreadPlanSP + GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others) override; + + lldb_private::Error + CanLoadImage() override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + +protected: + void + PrivateInitialize (lldb_private::Process *process); + + void + PrivateProcessStateChanged (lldb_private::Process *process, + lldb::StateType state); + + void + UpdateIfNeeded(); + + void + LoadKernelModuleIfNeeded (); + + void + Clear (bool clear_process); + + void + PutToLog (lldb_private::Log *log) const; + + static bool + BreakpointHitCallback (void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + bool + BreakpointHit (lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + uint32_t + GetAddrByteSize() + { + return m_kernel.GetAddressByteSize(); + } + + static lldb::ByteOrder + GetByteOrderFromMagic (uint32_t magic); + + enum + { + KERNEL_MODULE_MAX_NAME = 64u, + // Versions less than 2 didn't have an entry size, + // they had a 64 bit name, 16 byte UUID, 8 byte addr, + // 8 byte size, 8 byte version, 4 byte load tag, and + // 4 byte flags + KERNEL_MODULE_ENTRY_SIZE_VERSION_1 = 64u + 16u + 8u + 8u + 8u + 4u + 4u + }; + + // class KextImageInfo represents a single kext or kernel binary image. + // The class was designed to hold the information from the OSKextLoadedKextSummary + // structure (in libkern/libkern/OSKextLibPrivate.h from xnu). The kernel maintains + // a list of loded kexts in memory (the OSKextLoadedKextSummaryHeader structure, + // which points to an array of OSKextLoadedKextSummary's). + // + // A KextImageInfos may have - + // + // 1. The load address, name, UUID, and size of a kext/kernel binary in memory + // (read straight out of the kernel's list-of-kexts loaded) + // 2. A ModuleSP based on a MemoryModule read out of the kernel's memory + // (very unlikely to have any symbolic information) + // 3. A ModuleSP for an on-disk copy of the kext binary, possibly with debug info + // or a dSYM + // + // For performance reasons, the developer may prefer that lldb not load the kexts out + // of memory at the start of a kernel session. But we should build up / maintain a + // list of kexts that the kernel has told us about so we can relocate a kext module + // later if the user explicitly adds it to the target. + + class KextImageInfo + { + public: + KextImageInfo () : + m_name (), + m_module_sp (), + m_memory_module_sp (), + m_load_process_stop_id (UINT32_MAX), + m_uuid (), + m_load_address (LLDB_INVALID_ADDRESS), + m_size (0), + m_kernel_image (false) + { } + + void + Clear () + { + m_load_address = LLDB_INVALID_ADDRESS; + m_size = 0; + m_name.clear (); + m_uuid.Clear(); + m_module_sp.reset(); + m_memory_module_sp.reset(); + m_load_process_stop_id = UINT32_MAX; + } + + bool + LoadImageAtFileAddress (lldb_private::Process *process); + + bool + LoadImageUsingMemoryModule (lldb_private::Process *process); + + bool + IsLoaded () + { + return m_load_process_stop_id != UINT32_MAX; + } + + void + SetLoadAddress (lldb::addr_t load_addr); // Address of the Mach-O header for this binary + + lldb::addr_t + GetLoadAddress () const; // Address of the Mach-O header for this binary + + lldb_private::UUID + GetUUID () const; + + void + SetUUID (const lldb_private::UUID &uuid); + + void + SetName (const char *); + + std::string + GetName () const; + + void + SetModule (lldb::ModuleSP module); + + lldb::ModuleSP + GetModule (); + + // try to fill in m_memory_module_sp from memory based on the m_load_address + bool + ReadMemoryModule (lldb_private::Process *process); + + bool + IsKernel () const; // true if this is the mach_kernel; false if this is a kext + + void + SetIsKernel (bool is_kernel); + + uint64_t + GetSize () const; + + void + SetSize (uint64_t size); + + uint32_t + GetProcessStopId () const; // the stop-id when this binary was first noticed + + void + SetProcessStopId (uint32_t stop_id); + + bool + operator== (const KextImageInfo &rhs); + + uint32_t + GetAddressByteSize (); // as determined by Mach-O header + + lldb::ByteOrder + GetByteOrder(); // as determined by Mach-O header + + lldb_private::ArchSpec + GetArchitecture () const; // as determined by Mach-O header + + void + PutToLog (lldb_private::Log *log) const; + + typedef std::vector<KextImageInfo> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + private: + std::string m_name; + lldb::ModuleSP m_module_sp; + lldb::ModuleSP m_memory_module_sp; + uint32_t m_load_process_stop_id; // the stop-id when this module was added to the Target + lldb_private::UUID m_uuid; // UUID for this dylib if it has one, else all zeros + lldb::addr_t m_load_address; + uint64_t m_size; + bool m_kernel_image; // true if this is the kernel, false if this is a kext + }; + + struct OSKextLoadedKextSummaryHeader + { + uint32_t version; + uint32_t entry_size; + uint32_t entry_count; + lldb::addr_t image_infos_addr; + + OSKextLoadedKextSummaryHeader() : + version (0), + entry_size (0), + entry_count (0), + image_infos_addr (LLDB_INVALID_ADDRESS) + { + } + + uint32_t + GetSize() + { + switch (version) + { + case 0: return 0; // Can't know the size without a valid version + case 1: return 8; // Version 1 only had a version + entry_count + default: break; + } + // Version 2 and above has version, entry_size, entry_count, and reserved + return 16; + } + + void + Clear() + { + version = 0; + entry_size = 0; + entry_count = 0; + image_infos_addr = LLDB_INVALID_ADDRESS; + } + + bool + IsValid() const + { + return version >= 1 || version <= 2; + } + }; + + void + RegisterNotificationCallbacks(); + + void + UnregisterNotificationCallbacks(); + + void + SetNotificationBreakpointIfNeeded (); + + bool + ReadAllKextSummaries (); + + bool + ReadKextSummaryHeader (); + + bool + ParseKextSummaries (const lldb_private::Address &kext_summary_addr, + uint32_t count); + + void + UpdateImageInfosHeaderAndLoadCommands(KextImageInfo::collection &image_infos, + uint32_t infos_count, + bool update_executable); + + uint32_t + ReadKextSummaries (const lldb_private::Address &kext_summary_addr, + uint32_t image_infos_count, + KextImageInfo::collection &image_infos); + + static lldb::addr_t + SearchForDarwinKernel (lldb_private::Process *process); + + static lldb::addr_t + SearchForKernelAtSameLoadAddr (lldb_private::Process *process); + + static lldb::addr_t + SearchForKernelWithDebugHints (lldb_private::Process *process); + + static lldb::addr_t + SearchForKernelNearPC (lldb_private::Process *process); + + static lldb::addr_t + SearchForKernelViaExhaustiveSearch (lldb_private::Process *process); + + static lldb_private::UUID + CheckForKernelImageAtAddress (lldb::addr_t addr, lldb_private::Process *process); + + lldb::addr_t m_kernel_load_address; + KextImageInfo m_kernel; // Info about the current kernel image being used + + lldb_private::Address m_kext_summary_header_ptr_addr; + lldb_private::Address m_kext_summary_header_addr; + OSKextLoadedKextSummaryHeader m_kext_summary_header; + KextImageInfo::collection m_known_kexts; + mutable lldb_private::Mutex m_mutex; + lldb::user_id_t m_break_id; + +private: + DISALLOW_COPY_AND_ASSIGN (DynamicLoaderDarwinKernel); +}; + +#endif // liblldb_DynamicLoaderDarwinKernel_h_ diff --git a/source/Plugins/DynamicLoader/Darwin-Kernel/Makefile b/source/Plugins/DynamicLoader/Darwin-Kernel/Makefile new file mode 100644 index 000000000000..d2342fd06772 --- /dev/null +++ b/source/Plugins/DynamicLoader/Darwin-Kernel/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Disassembler/llvm/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 := lldbPluginDynamicLoaderDarwinKernel +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/Hexagon-DYLD/CMakeLists.txt b/source/Plugins/DynamicLoader/Hexagon-DYLD/CMakeLists.txt new file mode 100644 index 000000000000..af15f284118f --- /dev/null +++ b/source/Plugins/DynamicLoader/Hexagon-DYLD/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lldb_library(lldbPluginDynamicLoaderHexagonDYLD + HexagonDYLDRendezvous.cpp + DynamicLoaderHexagonDYLD.cpp + ) diff --git a/source/Plugins/DynamicLoader/Hexagon-DYLD/Makefile b/source/Plugins/DynamicLoader/Hexagon-DYLD/Makefile new file mode 100644 index 000000000000..43334562ebb1 --- /dev/null +++ b/source/Plugins/DynamicLoader/Hexagon-DYLD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/DynamicLoader/Hexagon-DYLD/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 := lldbPluginDynamicLoaderHexagon +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/MacOSX-DYLD/CMakeLists.txt b/source/Plugins/DynamicLoader/MacOSX-DYLD/CMakeLists.txt new file mode 100644 index 000000000000..2c44877662f5 --- /dev/null +++ b/source/Plugins/DynamicLoader/MacOSX-DYLD/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginDynamicLoaderMacOSXDYLD + DynamicLoaderMacOSXDYLD.cpp + ) diff --git a/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp new file mode 100644 index 000000000000..fba11f6aea85 --- /dev/null +++ b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp @@ -0,0 +1,2075 @@ +//===-- DynamicLoaderMacOSXDYLD.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/StackFrame.h" + +#include "DynamicLoaderMacOSXDYLD.h" + +//#define ENABLE_DEBUG_PRINTF // COMMENT THIS LINE OUT PRIOR TO CHECKIN +#ifdef ENABLE_DEBUG_PRINTF +#include <stdio.h> +#define DEBUG_PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +#ifndef __APPLE__ +#include "Utility/UuidCompatibility.h" +#else +#include <uuid/uuid.h> +#endif + +using namespace lldb; +using namespace lldb_private; + +/// FIXME - The ObjC Runtime trampoline handler doesn't really belong here. +/// I am putting it here so I can invoke it in the Trampoline code here, but +/// it should be moved to the ObjC Runtime support when it is set up. + + +DynamicLoaderMacOSXDYLD::DYLDImageInfo * +DynamicLoaderMacOSXDYLD::GetImageInfo (Module *module) +{ + const UUID &module_uuid = module->GetUUID(); + DYLDImageInfo::collection::iterator pos, end = m_dyld_image_infos.end(); + + // First try just by UUID as it is the safest. + if (module_uuid.IsValid()) + { + for (pos = m_dyld_image_infos.begin(); pos != end; ++pos) + { + if (pos->uuid == module_uuid) + return &(*pos); + } + + if (m_dyld.uuid == module_uuid) + return &m_dyld; + } + + // Next try by platform path only for things that don't have a valid UUID + // since if a file has a valid UUID in real life it should also in the + // dyld info. This is the next safest because the paths in the dyld info + // are platform paths, not local paths. For local debugging platform == local + // paths. + const FileSpec &platform_file_spec = module->GetPlatformFileSpec(); + for (pos = m_dyld_image_infos.begin(); pos != end; ++pos) + { + if (pos->file_spec == platform_file_spec && pos->uuid.IsValid() == false) + return &(*pos); + } + + if (m_dyld.file_spec == platform_file_spec && m_dyld.uuid.IsValid() == false) + return &m_dyld; + + return NULL; +} + +//---------------------------------------------------------------------- +// Create an instance of this class. This function is filled into +// the plugin info class that gets handed out by the plugin factory and +// allows the lldb to instantiate an instance of this class. +//---------------------------------------------------------------------- +DynamicLoader * +DynamicLoaderMacOSXDYLD::CreateInstance (Process* process, bool force) +{ + bool create = force; + if (!create) + { + create = true; + Module* exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) + { + ObjectFile *object_file = exe_module->GetObjectFile(); + if (object_file) + { + create = (object_file->GetStrata() == ObjectFile::eStrataUser); + } + } + + if (create) + { + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + switch (triple_ref.getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + case llvm::Triple::TvOS: + case llvm::Triple::WatchOS: + create = triple_ref.getVendor() == llvm::Triple::Apple; + break; + default: + create = false; + break; + } + } + } + + if (create) + return new DynamicLoaderMacOSXDYLD (process); + return NULL; +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DynamicLoaderMacOSXDYLD::DynamicLoaderMacOSXDYLD (Process* process) : + DynamicLoader(process), + m_dyld(), + m_dyld_module_wp(), + m_dyld_all_image_infos_addr(LLDB_INVALID_ADDRESS), + m_dyld_all_image_infos(), + m_dyld_all_image_infos_stop_id (UINT32_MAX), + m_break_id(LLDB_INVALID_BREAK_ID), + m_dyld_image_infos(), + m_dyld_image_infos_stop_id (UINT32_MAX), + m_mutex(Mutex::eMutexTypeRecursive), + m_process_image_addr_is_all_images_infos (false) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoaderMacOSXDYLD::~DynamicLoaderMacOSXDYLD() +{ + Clear(true); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderMacOSXDYLD::DidAttach () +{ + PrivateInitialize(m_process); + LocateDYLD (); + SetNotificationBreakpoint (); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderMacOSXDYLD::DidLaunch () +{ + PrivateInitialize(m_process); + LocateDYLD (); + SetNotificationBreakpoint (); +} + +bool +DynamicLoaderMacOSXDYLD::ProcessDidExec () +{ + if (m_process) + { + // If we are stopped after an exec, we will have only one thread... + if (m_process->GetThreadList().GetSize() == 1) + { + // We know if a process has exec'ed if our "m_dyld_all_image_infos_addr" + // value differs from the Process' image info address. When a process + // execs itself it might cause a change if ASLR is enabled. + const addr_t shlib_addr = m_process->GetImageInfoAddress (); + if (m_process_image_addr_is_all_images_infos == true && shlib_addr != m_dyld_all_image_infos_addr) + { + // The image info address from the process is the 'dyld_all_image_infos' + // address and it has changed. + return true; + } + + if (m_process_image_addr_is_all_images_infos == false && shlib_addr == m_dyld.address) + { + // The image info address from the process is the mach_header + // address for dyld and it has changed. + return true; + } + + // ASLR might be disabled and dyld could have ended up in the same + // location. We should try and detect if we are stopped at '_dyld_start' + ThreadSP thread_sp (m_process->GetThreadList().GetThreadAtIndex(0)); + if (thread_sp) + { + lldb::StackFrameSP frame_sp (thread_sp->GetStackFrameAtIndex(0)); + if (frame_sp) + { + const Symbol *symbol = frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol; + if (symbol) + { + if (symbol->GetName() == ConstString("_dyld_start")) + return true; + } + } + } + } + } + return false; +} + + + +//---------------------------------------------------------------------- +// Clear out the state of this class. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Clear (bool clear_process) +{ + Mutex::Locker locker(m_mutex); + + if (LLDB_BREAK_ID_IS_VALID(m_break_id)) + m_process->GetTarget().RemoveBreakpointByID (m_break_id); + + if (clear_process) + m_process = NULL; + m_dyld.Clear(false); + m_dyld_all_image_infos_addr = LLDB_INVALID_ADDRESS; + m_dyld_all_image_infos.Clear(); + m_break_id = LLDB_INVALID_BREAK_ID; + m_dyld_image_infos.clear(); +} + +//---------------------------------------------------------------------- +// Check if we have found DYLD yet +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::DidSetNotificationBreakpoint() const +{ + return LLDB_BREAK_ID_IS_VALID (m_break_id); +} + +//---------------------------------------------------------------------- +// Try and figure out where dyld is by first asking the Process +// if it knows (which currently calls down in the lldb::Process +// to get the DYLD info (available on SnowLeopard only). If that fails, +// then check in the default addresses. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::LocateDYLD() +{ + if (m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS) + { + // Check the image info addr as it might point to the + // mach header for dyld, or it might point to the + // dyld_all_image_infos struct + const addr_t shlib_addr = m_process->GetImageInfoAddress (); + if (shlib_addr != LLDB_INVALID_ADDRESS) + { + ByteOrder byte_order = m_process->GetTarget().GetArchitecture().GetByteOrder(); + uint8_t buf[4]; + DataExtractor data (buf, sizeof(buf), byte_order, 4); + Error error; + if (m_process->ReadMemory (shlib_addr, buf, 4, error) == 4) + { + lldb::offset_t offset = 0; + uint32_t magic = data.GetU32 (&offset); + switch (magic) + { + case llvm::MachO::MH_MAGIC: + case llvm::MachO::MH_MAGIC_64: + case llvm::MachO::MH_CIGAM: + case llvm::MachO::MH_CIGAM_64: + m_process_image_addr_is_all_images_infos = false; + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(shlib_addr); + + default: + break; + } + } + // Maybe it points to the all image infos? + m_dyld_all_image_infos_addr = shlib_addr; + m_process_image_addr_is_all_images_infos = true; + } + } + + if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS) + { + if (ReadAllImageInfosStructure ()) + { + if (m_dyld_all_image_infos.dyldImageLoadAddress != LLDB_INVALID_ADDRESS) + return ReadDYLDInfoFromMemoryAndSetNotificationCallback (m_dyld_all_image_infos.dyldImageLoadAddress); + else + return ReadDYLDInfoFromMemoryAndSetNotificationCallback (m_dyld_all_image_infos_addr & 0xfffffffffff00000ull); + } + } + + // Check some default values + Module *executable = m_process->GetTarget().GetExecutableModulePointer(); + + if (executable) + { + const ArchSpec &exe_arch = executable->GetArchitecture(); + if (exe_arch.GetAddressByteSize() == 8) + { + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x7fff5fc00000ull); + } + else if (exe_arch.GetMachine() == llvm::Triple::arm || exe_arch.GetMachine() == llvm::Triple::thumb || exe_arch.GetMachine() == llvm::Triple::aarch64) + { + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x2fe00000); + } + else + { + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x8fe00000); + } + } + return false; +} + +ModuleSP +DynamicLoaderMacOSXDYLD::FindTargetModuleForDYLDImageInfo (DYLDImageInfo &image_info, bool can_create, bool *did_create_ptr) +{ + if (did_create_ptr) + *did_create_ptr = false; + + Target &target = m_process->GetTarget(); + const ModuleList &target_images = target.GetImages(); + ModuleSpec module_spec (image_info.file_spec); + module_spec.GetUUID() = image_info.uuid; + ModuleSP module_sp (target_images.FindFirstModule (module_spec)); + + if (module_sp && !module_spec.GetUUID().IsValid() && !module_sp->GetUUID().IsValid()) + { + // No UUID, we must rely upon the cached module modification + // time and the modification time of the file on disk + if (module_sp->GetModificationTime() != module_sp->GetFileSpec().GetModificationTime()) + module_sp.reset(); + } + + if (!module_sp) + { + if (can_create) + { + module_sp = target.GetSharedModule (module_spec); + if (!module_sp || module_sp->GetObjectFile() == NULL) + module_sp = m_process->ReadModuleFromMemory (image_info.file_spec, image_info.address); + + if (did_create_ptr) + *did_create_ptr = (bool) module_sp; + } + } + return module_sp; +} + +//---------------------------------------------------------------------- +// Assume that dyld is in memory at ADDR and try to parse it's load +// commands +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::ReadDYLDInfoFromMemoryAndSetNotificationCallback(lldb::addr_t addr) +{ + DataExtractor data; // Load command data + if (ReadMachHeader (addr, &m_dyld.header, &data)) + { + if (m_dyld.header.filetype == llvm::MachO::MH_DYLINKER) + { + m_dyld.address = addr; + ModuleSP dyld_module_sp; + if (ParseLoadCommands (data, m_dyld, &m_dyld.file_spec)) + { + if (m_dyld.file_spec) + { + dyld_module_sp = FindTargetModuleForDYLDImageInfo (m_dyld, true, NULL); + + if (dyld_module_sp) + UpdateImageLoadAddress (dyld_module_sp.get(), m_dyld); + } + } + + Target &target = m_process->GetTarget(); + + if (m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS && dyld_module_sp.get()) + { + static ConstString g_dyld_all_image_infos ("dyld_all_image_infos"); + const Symbol *symbol = dyld_module_sp->FindFirstSymbolWithNameAndType (g_dyld_all_image_infos, eSymbolTypeData); + if (symbol) + m_dyld_all_image_infos_addr = symbol->GetLoadAddress(&target); + } + + // Update all image infos + InitializeFromAllImageInfos (); + + // If we didn't have an executable before, but now we do, then the + // dyld module shared pointer might be unique and we may need to add + // it again (since Target::SetExecutableModule() will clear the + // images). So append the dyld module back to the list if it is + /// unique! + if (dyld_module_sp) + { + target.GetImages().AppendIfNeeded (dyld_module_sp); + + // At this point we should have read in dyld's module, and so we should set breakpoints in it: + ModuleList modules; + modules.Append(dyld_module_sp); + target.ModulesDidLoad(modules); + m_dyld_module_wp = dyld_module_sp; + } + return true; + } + } + return false; +} + +bool +DynamicLoaderMacOSXDYLD::NeedToLocateDYLD () const +{ + return m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS; +} + +//---------------------------------------------------------------------- +// Update the load addresses for all segments in MODULE using the +// updated INFO that is passed in. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::UpdateImageLoadAddress (Module *module, DYLDImageInfo& info) +{ + bool changed = false; + if (module) + { + ObjectFile *image_object_file = module->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + std::vector<uint32_t> inaccessible_segment_indexes; + // We now know the slide amount, so go through all sections + // and update the load addresses with the correct values. + const size_t num_segments = info.segments.size(); + for (size_t i=0; i<num_segments; ++i) + { + // Only load a segment if it has protections. Things like + // __PAGEZERO don't have any protections, and they shouldn't + // be slid + SectionSP section_sp(section_list->FindSectionByName(info.segments[i].name)); + + if (info.segments[i].maxprot == 0) + { + inaccessible_segment_indexes.push_back(i); + } + else + { + const addr_t new_section_load_addr = info.segments[i].vmaddr + info.slide; + static ConstString g_section_name_LINKEDIT ("__LINKEDIT"); + + if (section_sp) + { + // __LINKEDIT sections from files in the shared cache + // can overlap so check to see what the segment name is + // and pass "false" so we don't warn of overlapping + // "Section" objects, and "true" for all other sections. + const bool warn_multiple = section_sp->GetName() != g_section_name_LINKEDIT; + + changed = m_process->GetTarget().SetSectionLoadAddress (section_sp, new_section_load_addr, warn_multiple); + } + else + { + Host::SystemLog (Host::eSystemLogWarning, + "warning: unable to find and load segment named '%s' at 0x%" PRIx64 " in '%s' in macosx dynamic loader plug-in.\n", + info.segments[i].name.AsCString("<invalid>"), + (uint64_t)new_section_load_addr, + image_object_file->GetFileSpec().GetPath().c_str()); + } + } + } + + // If the loaded the file (it changed) and we have segments that + // are not readable or writeable, add them to the invalid memory + // region cache for the process. This will typically only be + // the __PAGEZERO segment in the main executable. We might be able + // to apply this more generally to more sections that have no + // protections in the future, but for now we are going to just + // do __PAGEZERO. + if (changed && !inaccessible_segment_indexes.empty()) + { + for (uint32_t i=0; i<inaccessible_segment_indexes.size(); ++i) + { + const uint32_t seg_idx = inaccessible_segment_indexes[i]; + SectionSP section_sp(section_list->FindSectionByName(info.segments[seg_idx].name)); + + if (section_sp) + { + static ConstString g_pagezero_section_name("__PAGEZERO"); + if (g_pagezero_section_name == section_sp->GetName()) + { + // __PAGEZERO never slides... + const lldb::addr_t vmaddr = info.segments[seg_idx].vmaddr; + const lldb::addr_t vmsize = info.segments[seg_idx].vmsize; + Process::LoadRange pagezero_range (vmaddr, vmsize); + m_process->AddInvalidMemoryRegion(pagezero_range); + } + } + } + } + } + } + } + // We might have an in memory image that was loaded as soon as it was created + if (info.load_stop_id == m_process->GetStopID()) + changed = true; + else if (changed) + { + // Update the stop ID when this library was updated + info.load_stop_id = m_process->GetStopID(); + } + return changed; +} + +//---------------------------------------------------------------------- +// Update the load addresses for all segments in MODULE using the +// updated INFO that is passed in. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::UnloadImageLoadAddress (Module *module, DYLDImageInfo& info) +{ + bool changed = false; + if (module) + { + ObjectFile *image_object_file = module->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + const size_t num_segments = info.segments.size(); + for (size_t i=0; i<num_segments; ++i) + { + SectionSP section_sp(section_list->FindSectionByName(info.segments[i].name)); + if (section_sp) + { + const addr_t old_section_load_addr = info.segments[i].vmaddr + info.slide; + if (m_process->GetTarget().SetSectionUnloaded (section_sp, old_section_load_addr)) + changed = true; + } + else + { + Host::SystemLog (Host::eSystemLogWarning, + "warning: unable to find and unload segment named '%s' in '%s' in macosx dynamic loader plug-in.\n", + info.segments[i].name.AsCString("<invalid>"), + image_object_file->GetFileSpec().GetPath().c_str()); + } + } + } + } + } + return changed; +} + + +//---------------------------------------------------------------------- +// Static callback function that gets called when our DYLD notification +// breakpoint gets hit. We update all of our image infos and then +// let our super class DynamicLoader class decide if we should stop +// or not (based on global preference). +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::NotifyBreakpointHit (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) +{ + // Let the event know that the images have changed + // DYLD passes three arguments to the notification breakpoint. + // Arg1: enum dyld_image_mode mode - 0 = adding, 1 = removing + // Arg2: uint32_t infoCount - Number of shared libraries added + // Arg3: dyld_image_info info[] - Array of structs of the form: + // const struct mach_header *imageLoadAddress + // const char *imageFilePath + // uintptr_t imageFileModDate (a time_t) + + DynamicLoaderMacOSXDYLD* dyld_instance = (DynamicLoaderMacOSXDYLD*) baton; + + // First step is to see if we've already initialized the all image infos. If we haven't then this function + // will do so and return true. In the course of initializing the all_image_infos it will read the complete + // current state, so we don't need to figure out what has changed from the data passed in to us. + + ExecutionContext exe_ctx (context->exe_ctx_ref); + Process *process = exe_ctx.GetProcessPtr(); + + // This is a sanity check just in case this dyld_instance is an old dyld plugin's breakpoint still lying around. + if (process != dyld_instance->m_process) + return false; + + if (dyld_instance->InitializeFromAllImageInfos()) + return dyld_instance->GetStopWhenImagesChange(); + + const lldb::ABISP &abi = process->GetABI(); + if (abi) + { + // Build up the value array to store the three arguments given above, then get the values from the ABI: + + ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext(); + ValueList argument_values; + Value input_value; + + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + CompilerType clang_uint32_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint, 32); + input_value.SetValueType (Value::eValueTypeScalar); + input_value.SetCompilerType (clang_uint32_type); +// input_value.SetContext (Value::eContextTypeClangType, clang_uint32_type); + argument_values.PushValue (input_value); + argument_values.PushValue (input_value); + input_value.SetCompilerType (clang_void_ptr_type); + // input_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type); + argument_values.PushValue (input_value); + + if (abi->GetArgumentValues (exe_ctx.GetThreadRef(), argument_values)) + { + uint32_t dyld_mode = argument_values.GetValueAtIndex(0)->GetScalar().UInt (-1); + if (dyld_mode != static_cast<uint32_t>(-1)) + { + // Okay the mode was right, now get the number of elements, and the array of new elements... + uint32_t image_infos_count = argument_values.GetValueAtIndex(1)->GetScalar().UInt (-1); + if (image_infos_count != static_cast<uint32_t>(-1)) + { + // Got the number added, now go through the array of added elements, putting out the mach header + // address, and adding the image. + // Note, I'm not putting in logging here, since the AddModules & RemoveModules functions do + // all the logging internally. + + lldb::addr_t image_infos_addr = argument_values.GetValueAtIndex(2)->GetScalar().ULongLong(); + if (dyld_mode == 0) + { + // This is add: + dyld_instance->AddModulesUsingImageInfosAddress (image_infos_addr, image_infos_count); + } + else + { + // This is remove: + dyld_instance->RemoveModulesUsingImageInfosAddress (image_infos_addr, image_infos_count); + } + + } + } + } + } + else + { + process->GetTarget().GetDebugger().GetAsyncErrorStream()->Printf("No ABI plugin located for triple %s -- shared libraries will not be registered!\n", process->GetTarget().GetArchitecture().GetTriple().getTriple().c_str()); + } + + // Return true to stop the target, false to just let the target run + return dyld_instance->GetStopWhenImagesChange(); +} + +bool +DynamicLoaderMacOSXDYLD::ReadAllImageInfosStructure () +{ + Mutex::Locker locker(m_mutex); + + // the all image infos is already valid for this process stop ID + if (m_process->GetStopID() == m_dyld_all_image_infos_stop_id) + return true; + + m_dyld_all_image_infos.Clear(); + if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS) + { + ByteOrder byte_order = m_process->GetTarget().GetArchitecture().GetByteOrder(); + uint32_t addr_size = 4; + if (m_dyld_all_image_infos_addr > UINT32_MAX) + addr_size = 8; + + uint8_t buf[256]; + DataExtractor data (buf, sizeof(buf), byte_order, addr_size); + lldb::offset_t offset = 0; + + const size_t count_v2 = sizeof (uint32_t) + // version + sizeof (uint32_t) + // infoArrayCount + addr_size + // infoArray + addr_size + // notification + addr_size + // processDetachedFromSharedRegion + libSystemInitialized + pad + addr_size; // dyldImageLoadAddress + const size_t count_v11 = count_v2 + + addr_size + // jitInfo + addr_size + // dyldVersion + addr_size + // errorMessage + addr_size + // terminationFlags + addr_size + // coreSymbolicationShmPage + addr_size + // systemOrderFlag + addr_size + // uuidArrayCount + addr_size + // uuidArray + addr_size + // dyldAllImageInfosAddress + addr_size + // initialImageCount + addr_size + // errorKind + addr_size + // errorClientOfDylibPath + addr_size + // errorTargetDylibPath + addr_size; // errorSymbol + const size_t count_v13 = count_v11 + + addr_size + // sharedCacheSlide + sizeof (uuid_t); // sharedCacheUUID + UNUSED_IF_ASSERT_DISABLED(count_v13); + assert (sizeof (buf) >= count_v13); + + Error error; + if (m_process->ReadMemory (m_dyld_all_image_infos_addr, buf, 4, error) == 4) + { + m_dyld_all_image_infos.version = data.GetU32(&offset); + // If anything in the high byte is set, we probably got the byte + // order incorrect (the process might not have it set correctly + // yet due to attaching to a program without a specified file). + if (m_dyld_all_image_infos.version & 0xff000000) + { + // We have guessed the wrong byte order. Swap it and try + // reading the version again. + if (byte_order == eByteOrderLittle) + byte_order = eByteOrderBig; + else + byte_order = eByteOrderLittle; + + data.SetByteOrder (byte_order); + offset = 0; + m_dyld_all_image_infos.version = data.GetU32(&offset); + } + } + else + { + return false; + } + + const size_t count = (m_dyld_all_image_infos.version >= 11) ? count_v11 : count_v2; + + const size_t bytes_read = m_process->ReadMemory (m_dyld_all_image_infos_addr, buf, count, error); + if (bytes_read == count) + { + offset = 0; + m_dyld_all_image_infos.version = data.GetU32(&offset); + m_dyld_all_image_infos.dylib_info_count = data.GetU32(&offset); + m_dyld_all_image_infos.dylib_info_addr = data.GetPointer(&offset); + m_dyld_all_image_infos.notification = data.GetPointer(&offset); + m_dyld_all_image_infos.processDetachedFromSharedRegion = data.GetU8(&offset); + m_dyld_all_image_infos.libSystemInitialized = data.GetU8(&offset); + // Adjust for padding. + offset += addr_size - 2; + m_dyld_all_image_infos.dyldImageLoadAddress = data.GetPointer(&offset); + if (m_dyld_all_image_infos.version >= 11) + { + offset += addr_size * 8; + uint64_t dyld_all_image_infos_addr = data.GetPointer(&offset); + + // When we started, we were given the actual address of the all_image_infos + // struct (probably via TASK_DYLD_INFO) in memory - this address is stored in + // m_dyld_all_image_infos_addr and is the most accurate address we have. + + // We read the dyld_all_image_infos struct from memory; it contains its own address. + // If the address in the struct does not match the actual address, + // the dyld we're looking at has been loaded at a different location (slid) from + // where it intended to load. The addresses in the dyld_all_image_infos struct + // are the original, non-slid addresses, and need to be adjusted. Most importantly + // the address of dyld and the notification address need to be adjusted. + + if (dyld_all_image_infos_addr != m_dyld_all_image_infos_addr) + { + uint64_t image_infos_offset = dyld_all_image_infos_addr - m_dyld_all_image_infos.dyldImageLoadAddress; + uint64_t notification_offset = m_dyld_all_image_infos.notification - m_dyld_all_image_infos.dyldImageLoadAddress; + m_dyld_all_image_infos.dyldImageLoadAddress = m_dyld_all_image_infos_addr - image_infos_offset; + m_dyld_all_image_infos.notification = m_dyld_all_image_infos.dyldImageLoadAddress + notification_offset; + } + } + m_dyld_all_image_infos_stop_id = m_process->GetStopID(); + return true; + } + } + return false; +} + + +// This method is an amalgamation of code from +// ReadMachHeader() +// ParseLoadCommands() +// UpdateImageInfosHeaderAndLoadCommands() +// but written to extract everything from the JSON packet from debugserver, instead of using memory reads. + +bool +DynamicLoaderMacOSXDYLD::AddModulesUsingInfosFromDebugserver (StructuredData::ObjectSP image_details, DYLDImageInfo::collection &image_infos) +{ + StructuredData::ObjectSP images_sp = image_details->GetAsDictionary()->GetValueForKey("images"); + if (images_sp.get() == nullptr) + return false; + + image_infos.resize (images_sp->GetAsArray()->GetSize()); + + uint32_t exe_idx = UINT32_MAX; + + for (size_t i = 0; i < image_infos.size(); i++) + { + StructuredData::ObjectSP image_sp = images_sp->GetAsArray()->GetItemAtIndex(i); + if (image_sp.get() == nullptr || image_sp->GetAsDictionary() == nullptr) + return false; + StructuredData::Dictionary *image = image_sp->GetAsDictionary(); + if (image->HasKey("load_address") == false + || image->HasKey("pathname") == false + || image->HasKey("mod_date") == false + || image->HasKey("mach_header") == false + || image->GetValueForKey("mach_header")->GetAsDictionary() == nullptr + || image->HasKey("segments") == false + || image->GetValueForKey("segments")->GetAsArray() == nullptr + || image->HasKey("uuid") == false ) + { + return false; + } + image_infos[i].address = image->GetValueForKey("load_address")->GetAsInteger()->GetValue(); + image_infos[i].mod_date = image->GetValueForKey("mod_date")->GetAsInteger()->GetValue(); + image_infos[i].file_spec.SetFile(image->GetValueForKey("pathname")->GetAsString()->GetValue().c_str(), false); + + StructuredData::Dictionary *mh = image->GetValueForKey("mach_header")->GetAsDictionary(); + image_infos[i].header.magic = mh->GetValueForKey("magic")->GetAsInteger()->GetValue(); + image_infos[i].header.cputype = mh->GetValueForKey("cputype")->GetAsInteger()->GetValue(); + image_infos[i].header.cpusubtype = mh->GetValueForKey("cpusubtype")->GetAsInteger()->GetValue(); + image_infos[i].header.filetype = mh->GetValueForKey("filetype")->GetAsInteger()->GetValue(); + + // Fields that aren't used by DynamicLoaderMacOSXDYLD so debugserver doesn't currently send them + // in the reply. + + if (mh->HasKey("flags")) + image_infos[i].header.flags = mh->GetValueForKey("flags")->GetAsInteger()->GetValue(); + else + image_infos[i].header.flags = 0; + + if (mh->HasKey("ncmds")) + image_infos[i].header.ncmds = mh->GetValueForKey("ncmds")->GetAsInteger()->GetValue(); + else + image_infos[i].header.ncmds = 0; + + if (mh->HasKey("sizeofcmds")) + image_infos[i].header.sizeofcmds = mh->GetValueForKey("sizeofcmds")->GetAsInteger()->GetValue(); + else + image_infos[i].header.sizeofcmds = 0; + + if (image_infos[i].header.filetype == llvm::MachO::MH_EXECUTE) + exe_idx = i; + + StructuredData::Array *segments = image->GetValueForKey("segments")->GetAsArray(); + uint32_t segcount = segments->GetSize(); + for (size_t j = 0; j < segcount; j++) + { + Segment segment; + StructuredData::Dictionary *seg = segments->GetItemAtIndex(j)->GetAsDictionary(); + segment.name = ConstString(seg->GetValueForKey("name")->GetAsString()->GetValue().c_str()); + segment.vmaddr = seg->GetValueForKey("vmaddr")->GetAsInteger()->GetValue(); + segment.vmsize = seg->GetValueForKey("vmsize")->GetAsInteger()->GetValue(); + segment.fileoff = seg->GetValueForKey("fileoff")->GetAsInteger()->GetValue(); + segment.filesize = seg->GetValueForKey("filesize")->GetAsInteger()->GetValue(); + segment.maxprot = seg->GetValueForKey("maxprot")->GetAsInteger()->GetValue(); + + // Fields that aren't used by DynamicLoaderMacOSXDYLD so debugserver doesn't currently send them + // in the reply. + + if (seg->HasKey("initprot")) + segment.initprot = seg->GetValueForKey("initprot")->GetAsInteger()->GetValue(); + else + segment.initprot = 0; + + if (seg->HasKey("flags")) + segment.flags = seg->GetValueForKey("flags")->GetAsInteger()->GetValue(); + else + segment.flags = 0; + + if (seg->HasKey("nsects")) + segment.nsects = seg->GetValueForKey("nsects")->GetAsInteger()->GetValue(); + else + segment.nsects = 0; + + image_infos[i].segments.push_back (segment); + } + + image_infos[i].uuid.SetFromCString (image->GetValueForKey("uuid")->GetAsString()->GetValue().c_str()); + + // All sections listed in the dyld image info structure will all + // either be fixed up already, or they will all be off by a single + // slide amount that is determined by finding the first segment + // that is at file offset zero which also has bytes (a file size + // that is greater than zero) in the object file. + + // Determine the slide amount (if any) + const size_t num_sections = image_infos[i].segments.size(); + for (size_t k = 0; k < num_sections; ++k) + { + // Iterate through the object file sections to find the + // first section that starts of file offset zero and that + // has bytes in the file... + if ((image_infos[i].segments[k].fileoff == 0 && image_infos[i].segments[k].filesize > 0) + || (image_infos[i].segments[k].name == ConstString("__TEXT"))) + { + image_infos[i].slide = image_infos[i].address - image_infos[i].segments[k].vmaddr; + // We have found the slide amount, so we can exit + // this for loop. + break; + } + } + } + + Target &target = m_process->GetTarget(); + + if (exe_idx < image_infos.size()) + { + const bool can_create = true; + ModuleSP exe_module_sp (FindTargetModuleForDYLDImageInfo (image_infos[exe_idx], can_create, NULL)); + + if (exe_module_sp) + { + UpdateImageLoadAddress (exe_module_sp.get(), image_infos[exe_idx]); + + if (exe_module_sp.get() != target.GetExecutableModulePointer()) + { + // Don't load dependent images since we are in dyld where we will know + // and find out about all images that are loaded. Also when setting the + // executable module, it will clear the targets module list, and if we + // have an in memory dyld module, it will get removed from the list + // so we will need to add it back after setting the executable module, + // so we first try and see if we already have a weak pointer to the + // dyld module, make it into a shared pointer, then add the executable, + // then re-add it back to make sure it is always in the list. + ModuleSP dyld_module_sp(m_dyld_module_wp.lock()); + + const bool get_dependent_images = false; + m_process->GetTarget().SetExecutableModule (exe_module_sp, + get_dependent_images); + + if (dyld_module_sp) + { + if(target.GetImages().AppendIfNeeded (dyld_module_sp)) + { + // Also add it to the section list. + UpdateImageLoadAddress(dyld_module_sp.get(), m_dyld); + } + } + } + } + } + return true; +} + +bool +DynamicLoaderMacOSXDYLD::AddModulesUsingImageInfosAddress (lldb::addr_t image_infos_addr, uint32_t image_infos_count) +{ + DYLDImageInfo::collection image_infos; + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + if (log) + log->Printf ("Adding %d modules.\n", image_infos_count); + + Mutex::Locker locker(m_mutex); + if (m_process->GetStopID() == m_dyld_image_infos_stop_id) + return true; + + StructuredData::ObjectSP image_infos_json_sp = m_process->GetLoadedDynamicLibrariesInfos (image_infos_addr, image_infos_count); + if (image_infos_json_sp.get() + && image_infos_json_sp->GetAsDictionary() + && image_infos_json_sp->GetAsDictionary()->HasKey("images") + && image_infos_json_sp->GetAsDictionary()->GetValueForKey("images")->GetAsArray() + && image_infos_json_sp->GetAsDictionary()->GetValueForKey("images")->GetAsArray()->GetSize() == image_infos_count) + { + bool return_value = false; + if (AddModulesUsingInfosFromDebugserver (image_infos_json_sp, image_infos)) + { + return_value = AddModulesUsingImageInfos (image_infos); + } + m_dyld_image_infos_stop_id = m_process->GetStopID(); + return return_value; + } + + if (!ReadImageInfos (image_infos_addr, image_infos_count, image_infos)) + return false; + + UpdateImageInfosHeaderAndLoadCommands (image_infos, image_infos_count, false); + bool return_value = AddModulesUsingImageInfos (image_infos); + m_dyld_image_infos_stop_id = m_process->GetStopID(); + return return_value; +} + +// Adds the modules in image_infos to m_dyld_image_infos. +// NB don't call this passing in m_dyld_image_infos. + +bool +DynamicLoaderMacOSXDYLD::AddModulesUsingImageInfos (DYLDImageInfo::collection &image_infos) +{ + // Now add these images to the main list. + ModuleList loaded_module_list; + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + Target &target = m_process->GetTarget(); + ModuleList& target_images = target.GetImages(); + + for (uint32_t idx = 0; idx < image_infos.size(); ++idx) + { + if (log) + { + log->Printf ("Adding new image at address=0x%16.16" PRIx64 ".", image_infos[idx].address); + image_infos[idx].PutToLog (log); + } + + m_dyld_image_infos.push_back(image_infos[idx]); + + ModuleSP image_module_sp (FindTargetModuleForDYLDImageInfo (image_infos[idx], true, NULL)); + + if (image_module_sp) + { + ObjectFile *objfile = image_module_sp->GetObjectFile (); + if (objfile) + { + SectionList *sections = objfile->GetSectionList(); + if (sections) + { + ConstString commpage_dbstr("__commpage"); + Section *commpage_section = sections->FindSectionByName(commpage_dbstr).get(); + if (commpage_section) + { + ModuleSpec module_spec (objfile->GetFileSpec(), image_infos[idx].GetArchitecture ()); + module_spec.GetObjectName() = commpage_dbstr; + ModuleSP commpage_image_module_sp(target_images.FindFirstModule (module_spec)); + if (!commpage_image_module_sp) + { + module_spec.SetObjectOffset (objfile->GetFileOffset() + commpage_section->GetFileOffset()); + module_spec.SetObjectSize (objfile->GetByteSize()); + commpage_image_module_sp = target.GetSharedModule (module_spec); + if (!commpage_image_module_sp || commpage_image_module_sp->GetObjectFile() == NULL) + { + commpage_image_module_sp = m_process->ReadModuleFromMemory (image_infos[idx].file_spec, + image_infos[idx].address); + // Always load a memory image right away in the target in case + // we end up trying to read the symbol table from memory... The + // __LINKEDIT will need to be mapped so we can figure out where + // the symbol table bits are... + bool changed = false; + UpdateImageLoadAddress (commpage_image_module_sp.get(), image_infos[idx]); + target.GetImages().Append(commpage_image_module_sp); + if (changed) + { + image_infos[idx].load_stop_id = m_process->GetStopID(); + loaded_module_list.AppendIfNeeded (commpage_image_module_sp); + } + } + } + } + } + } + + // UpdateImageLoadAddress will return true if any segments + // change load address. We need to check this so we don't + // mention that all loaded shared libraries are newly loaded + // each time we hit out dyld breakpoint since dyld will list all + // shared libraries each time. + if (UpdateImageLoadAddress (image_module_sp.get(), image_infos[idx])) + { + target_images.AppendIfNeeded(image_module_sp); + loaded_module_list.AppendIfNeeded (image_module_sp); + } + } + } + + if (loaded_module_list.GetSize() > 0) + { + if (log) + loaded_module_list.LogUUIDAndPaths (log, "DynamicLoaderMacOSXDYLD::ModulesDidLoad"); + m_process->GetTarget().ModulesDidLoad (loaded_module_list); + } + return true; +} + +bool +DynamicLoaderMacOSXDYLD::RemoveModulesUsingImageInfosAddress (lldb::addr_t image_infos_addr, uint32_t image_infos_count) +{ + DYLDImageInfo::collection image_infos; + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + + Mutex::Locker locker(m_mutex); + if (m_process->GetStopID() == m_dyld_image_infos_stop_id) + return true; + + // First read in the image_infos for the removed modules, and their headers & load commands. + if (!ReadImageInfos (image_infos_addr, image_infos_count, image_infos)) + { + if (log) + log->PutCString ("Failed reading image infos array."); + return false; + } + + if (log) + log->Printf ("Removing %d modules.", image_infos_count); + + ModuleList unloaded_module_list; + for (uint32_t idx = 0; idx < image_infos.size(); ++idx) + { + if (log) + { + log->Printf ("Removing module at address=0x%16.16" PRIx64 ".", image_infos[idx].address); + image_infos[idx].PutToLog (log); + } + + // Remove this image_infos from the m_all_image_infos. We do the comparison by address + // rather than by file spec because we can have many modules with the same "file spec" in the + // case that they are modules loaded from memory. + // + // Also copy over the uuid from the old entry to the removed entry so we can + // use it to lookup the module in the module list. + + DYLDImageInfo::collection::iterator pos, end = m_dyld_image_infos.end(); + for (pos = m_dyld_image_infos.begin(); pos != end; pos++) + { + if (image_infos[idx].address == (*pos).address) + { + image_infos[idx].uuid = (*pos).uuid; + + // Add the module from this image_info to the "unloaded_module_list". We'll remove them all at + // one go later on. + + ModuleSP unload_image_module_sp (FindTargetModuleForDYLDImageInfo (image_infos[idx], false, NULL)); + if (unload_image_module_sp.get()) + { + // When we unload, be sure to use the image info from the old list, + // since that has sections correctly filled in. + UnloadImageLoadAddress (unload_image_module_sp.get(), *pos); + unloaded_module_list.AppendIfNeeded (unload_image_module_sp); + } + else + { + if (log) + { + log->Printf ("Could not find module for unloading info entry:"); + image_infos[idx].PutToLog(log); + } + } + + // Then remove it from the m_dyld_image_infos: + + m_dyld_image_infos.erase(pos); + break; + } + } + + if (pos == end) + { + if (log) + { + log->Printf ("Could not find image_info entry for unloading image:"); + image_infos[idx].PutToLog(log); + } + } + } + if (unloaded_module_list.GetSize() > 0) + { + if (log) + { + log->PutCString("Unloaded:"); + unloaded_module_list.LogUUIDAndPaths (log, "DynamicLoaderMacOSXDYLD::ModulesDidUnload"); + } + m_process->GetTarget().GetImages().Remove (unloaded_module_list); + } + m_dyld_image_infos_stop_id = m_process->GetStopID(); + return true; +} + +bool +DynamicLoaderMacOSXDYLD::ReadImageInfos (lldb::addr_t image_infos_addr, + uint32_t image_infos_count, + DYLDImageInfo::collection &image_infos) +{ + const ByteOrder endian = m_dyld.GetByteOrder(); + const uint32_t addr_size = m_dyld.GetAddressByteSize(); + + image_infos.resize(image_infos_count); + const size_t count = image_infos.size() * 3 * addr_size; + DataBufferHeap info_data(count, 0); + Error error; + const size_t bytes_read = m_process->ReadMemory (image_infos_addr, + info_data.GetBytes(), + info_data.GetByteSize(), + error); + if (bytes_read == count) + { + lldb::offset_t info_data_offset = 0; + DataExtractor info_data_ref(info_data.GetBytes(), info_data.GetByteSize(), endian, addr_size); + for (size_t i = 0; i < image_infos.size() && info_data_ref.ValidOffset(info_data_offset); i++) + { + image_infos[i].address = info_data_ref.GetPointer(&info_data_offset); + lldb::addr_t path_addr = info_data_ref.GetPointer(&info_data_offset); + image_infos[i].mod_date = info_data_ref.GetPointer(&info_data_offset); + + char raw_path[PATH_MAX]; + m_process->ReadCStringFromMemory (path_addr, raw_path, sizeof(raw_path), error); + // don't resolve the path + if (error.Success()) + { + const bool resolve_path = false; + image_infos[i].file_spec.SetFile(raw_path, resolve_path); + } + } + return true; + } + else + { + return false; + } +} + +//---------------------------------------------------------------------- +// If we have found where the "_dyld_all_image_infos" lives in memory, +// read the current info from it, and then update all image load +// addresses (or lack thereof). Only do this if this is the first time +// we're reading the dyld infos. Return true if we actually read anything, +// and false otherwise. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::InitializeFromAllImageInfos () +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + + Mutex::Locker locker(m_mutex); + if (m_process->GetStopID() == m_dyld_image_infos_stop_id + || m_dyld_image_infos.size() != 0) + return false; + + if (ReadAllImageInfosStructure ()) + { + // Nothing to load or unload? + if (m_dyld_all_image_infos.dylib_info_count == 0) + return true; + + if (m_dyld_all_image_infos.dylib_info_addr == 0) + { + // DYLD is updating the images now. So we should say we have no images, and then we'll + // figure it out when we hit the added breakpoint. + return false; + } + else + { + if (!AddModulesUsingImageInfosAddress (m_dyld_all_image_infos.dylib_info_addr, + m_dyld_all_image_infos.dylib_info_count)) + { + DEBUG_PRINTF("%s", "unable to read all data for all_dylib_infos."); + m_dyld_image_infos.clear(); + } + } + + // Now we have one more bit of business. If there is a library left in the images for our target that + // doesn't have a load address, then it must be something that we were expecting to load (for instance we + // read a load command for it) but it didn't in fact load - probably because DYLD_*_PATH pointed + // to an equivalent version. We don't want it to stay in the target's module list or it will confuse + // us, so unload it here. + Target &target = m_process->GetTarget(); + const ModuleList &target_modules = target.GetImages(); + ModuleList not_loaded_modules; + Mutex::Locker modules_locker(target_modules.GetMutex()); + + size_t num_modules = target_modules.GetSize(); + for (size_t i = 0; i < num_modules; i++) + { + ModuleSP module_sp = target_modules.GetModuleAtIndexUnlocked (i); + if (!module_sp->IsLoadedInTarget (&target)) + { + if (log) + { + StreamString s; + module_sp->GetDescription (&s); + log->Printf ("Unloading pre-run module: %s.", s.GetData ()); + } + not_loaded_modules.Append (module_sp); + } + } + + if (not_loaded_modules.GetSize() != 0) + { + target.GetImages().Remove(not_loaded_modules); + } + + return true; + } + else + return false; +} + +//---------------------------------------------------------------------- +// Read a mach_header at ADDR into HEADER, and also fill in the load +// command data into LOAD_COMMAND_DATA if it is non-NULL. +// +// Returns true if we succeed, false if we fail for any reason. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::ReadMachHeader (lldb::addr_t addr, llvm::MachO::mach_header *header, DataExtractor *load_command_data) +{ + DataBufferHeap header_bytes(sizeof(llvm::MachO::mach_header), 0); + Error error; + size_t bytes_read = m_process->ReadMemory (addr, + header_bytes.GetBytes(), + header_bytes.GetByteSize(), + error); + if (bytes_read == sizeof(llvm::MachO::mach_header)) + { + lldb::offset_t offset = 0; + ::memset (header, 0, sizeof(llvm::MachO::mach_header)); + + // Get the magic byte unswapped so we can figure out what we are dealing with + DataExtractor data(header_bytes.GetBytes(), header_bytes.GetByteSize(), endian::InlHostByteOrder(), 4); + header->magic = data.GetU32(&offset); + lldb::addr_t load_cmd_addr = addr; + data.SetByteOrder(DynamicLoaderMacOSXDYLD::GetByteOrderFromMagic(header->magic)); + switch (header->magic) + { + case llvm::MachO::MH_MAGIC: + case llvm::MachO::MH_CIGAM: + data.SetAddressByteSize(4); + load_cmd_addr += sizeof(llvm::MachO::mach_header); + break; + + case llvm::MachO::MH_MAGIC_64: + case llvm::MachO::MH_CIGAM_64: + data.SetAddressByteSize(8); + load_cmd_addr += sizeof(llvm::MachO::mach_header_64); + break; + + default: + return false; + } + + // Read the rest of dyld's mach header + if (data.GetU32(&offset, &header->cputype, (sizeof(llvm::MachO::mach_header)/sizeof(uint32_t)) - 1)) + { + if (load_command_data == NULL) + return true; // We were able to read the mach_header and weren't asked to read the load command bytes + + DataBufferSP load_cmd_data_sp(new DataBufferHeap(header->sizeofcmds, 0)); + + size_t load_cmd_bytes_read = m_process->ReadMemory (load_cmd_addr, + load_cmd_data_sp->GetBytes(), + load_cmd_data_sp->GetByteSize(), + error); + + if (load_cmd_bytes_read == header->sizeofcmds) + { + // Set the load command data and also set the correct endian + // swap settings and the correct address size + load_command_data->SetData(load_cmd_data_sp, 0, header->sizeofcmds); + load_command_data->SetByteOrder(data.GetByteOrder()); + load_command_data->SetAddressByteSize(data.GetAddressByteSize()); + return true; // We successfully read the mach_header and the load command data + } + + return false; // We weren't able to read the load command data + } + } + return false; // We failed the read the mach_header +} + + +//---------------------------------------------------------------------- +// Parse the load commands for an image +//---------------------------------------------------------------------- +uint32_t +DynamicLoaderMacOSXDYLD::ParseLoadCommands (const DataExtractor& data, DYLDImageInfo& dylib_info, FileSpec *lc_id_dylinker) +{ + lldb::offset_t offset = 0; + uint32_t cmd_idx; + Segment segment; + dylib_info.Clear (true); + + for (cmd_idx = 0; cmd_idx < dylib_info.header.ncmds; cmd_idx++) + { + // Clear out any load command specific data from DYLIB_INFO since + // we are about to read it. + + if (data.ValidOffsetForDataOfSize (offset, sizeof(llvm::MachO::load_command))) + { + llvm::MachO::load_command load_cmd; + lldb::offset_t load_cmd_offset = offset; + load_cmd.cmd = data.GetU32 (&offset); + load_cmd.cmdsize = data.GetU32 (&offset); + switch (load_cmd.cmd) + { + case llvm::MachO::LC_SEGMENT: + { + segment.name.SetTrimmedCStringWithLength ((const char *)data.GetData(&offset, 16), 16); + // We are putting 4 uint32_t values 4 uint64_t values so + // we have to use multiple 32 bit gets below. + segment.vmaddr = data.GetU32 (&offset); + segment.vmsize = data.GetU32 (&offset); + segment.fileoff = data.GetU32 (&offset); + segment.filesize = data.GetU32 (&offset); + // Extract maxprot, initprot, nsects and flags all at once + data.GetU32(&offset, &segment.maxprot, 4); + dylib_info.segments.push_back (segment); + } + break; + + case llvm::MachO::LC_SEGMENT_64: + { + segment.name.SetTrimmedCStringWithLength ((const char *)data.GetData(&offset, 16), 16); + // Extract vmaddr, vmsize, fileoff, and filesize all at once + data.GetU64(&offset, &segment.vmaddr, 4); + // Extract maxprot, initprot, nsects and flags all at once + data.GetU32(&offset, &segment.maxprot, 4); + dylib_info.segments.push_back (segment); + } + break; + + case llvm::MachO::LC_ID_DYLINKER: + if (lc_id_dylinker) + { + const lldb::offset_t name_offset = load_cmd_offset + data.GetU32 (&offset); + const char *path = data.PeekCStr (name_offset); + lc_id_dylinker->SetFile (path, true); + } + break; + + case llvm::MachO::LC_UUID: + dylib_info.uuid.SetBytes(data.GetData (&offset, 16)); + break; + + default: + break; + } + // Set offset to be the beginning of the next load command. + offset = load_cmd_offset + load_cmd.cmdsize; + } + } + + // All sections listed in the dyld image info structure will all + // either be fixed up already, or they will all be off by a single + // slide amount that is determined by finding the first segment + // that is at file offset zero which also has bytes (a file size + // that is greater than zero) in the object file. + + // Determine the slide amount (if any) + const size_t num_sections = dylib_info.segments.size(); + for (size_t i = 0; i < num_sections; ++i) + { + // Iterate through the object file sections to find the + // first section that starts of file offset zero and that + // has bytes in the file... + if ((dylib_info.segments[i].fileoff == 0 && dylib_info.segments[i].filesize > 0) || (dylib_info.segments[i].name == ConstString("__TEXT"))) + { + dylib_info.slide = dylib_info.address - dylib_info.segments[i].vmaddr; + // We have found the slide amount, so we can exit + // this for loop. + break; + } + } + return cmd_idx; +} + +//---------------------------------------------------------------------- +// Read the mach_header and load commands for each image that the +// _dyld_all_image_infos structure points to and cache the results. +//---------------------------------------------------------------------- + +void +DynamicLoaderMacOSXDYLD::UpdateImageInfosHeaderAndLoadCommands(DYLDImageInfo::collection &image_infos, + uint32_t infos_count, + bool update_executable) +{ + uint32_t exe_idx = UINT32_MAX; + // Read any UUID values that we can get + for (uint32_t i = 0; i < infos_count; i++) + { + if (!image_infos[i].UUIDValid()) + { + DataExtractor data; // Load command data + if (!ReadMachHeader (image_infos[i].address, &image_infos[i].header, &data)) + continue; + + ParseLoadCommands (data, image_infos[i], NULL); + + if (image_infos[i].header.filetype == llvm::MachO::MH_EXECUTE) + exe_idx = i; + + } + } + + Target &target = m_process->GetTarget(); + + if (exe_idx < image_infos.size()) + { + const bool can_create = true; + ModuleSP exe_module_sp (FindTargetModuleForDYLDImageInfo (image_infos[exe_idx], can_create, NULL)); + + if (exe_module_sp) + { + UpdateImageLoadAddress (exe_module_sp.get(), image_infos[exe_idx]); + + if (exe_module_sp.get() != target.GetExecutableModulePointer()) + { + // Don't load dependent images since we are in dyld where we will know + // and find out about all images that are loaded. Also when setting the + // executable module, it will clear the targets module list, and if we + // have an in memory dyld module, it will get removed from the list + // so we will need to add it back after setting the executable module, + // so we first try and see if we already have a weak pointer to the + // dyld module, make it into a shared pointer, then add the executable, + // then re-add it back to make sure it is always in the list. + ModuleSP dyld_module_sp(m_dyld_module_wp.lock()); + + const bool get_dependent_images = false; + m_process->GetTarget().SetExecutableModule (exe_module_sp, + get_dependent_images); + + if (dyld_module_sp) + { + if(target.GetImages().AppendIfNeeded (dyld_module_sp)) + { + // Also add it to the section list. + UpdateImageLoadAddress(dyld_module_sp.get(), m_dyld); + } + } + } + } + } +} + +//---------------------------------------------------------------------- +// On Mac OS X libobjc (the Objective-C runtime) has several critical dispatch +// functions written in hand-written assembly, and also have hand-written unwind +// information in the eh_frame section. Normally we prefer analyzing the +// assembly instructions of a currently executing frame to unwind from that frame -- +// but on hand-written functions this profiling can fail. We should use the +// eh_frame instructions for these functions all the time. +// +// As an aside, it would be better if the eh_frame entries had a flag (or were +// extensible so they could have an Apple-specific flag) which indicates that +// the instructions are asynchronous -- accurate at every instruction, instead +// of our normal default assumption that they are not. +//---------------------------------------------------------------------- + +bool +DynamicLoaderMacOSXDYLD::AlwaysRelyOnEHUnwindInfo (SymbolContext &sym_ctx) +{ + ModuleSP module_sp; + if (sym_ctx.symbol) + { + module_sp = sym_ctx.symbol->GetAddressRef().GetModule(); + } + if (module_sp.get() == NULL && sym_ctx.function) + { + module_sp = sym_ctx.function->GetAddressRange().GetBaseAddress().GetModule(); + } + if (module_sp.get() == NULL) + return false; + + ObjCLanguageRuntime *objc_runtime = m_process->GetObjCLanguageRuntime(); + if (objc_runtime != NULL && objc_runtime->IsModuleObjCLibrary (module_sp)) + { + return true; + } + + return false; +} + + + +//---------------------------------------------------------------------- +// Dump a Segment to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Segment::PutToLog (Log *log, lldb::addr_t slide) const +{ + if (log) + { + if (slide == 0) + log->Printf ("\t\t%16s [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ")", + name.AsCString(""), + vmaddr + slide, + vmaddr + slide + vmsize); + else + log->Printf ("\t\t%16s [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") slide = 0x%" PRIx64, + name.AsCString(""), + vmaddr + slide, + vmaddr + slide + vmsize, + slide); + } +} + +const DynamicLoaderMacOSXDYLD::Segment * +DynamicLoaderMacOSXDYLD::DYLDImageInfo::FindSegment (const ConstString &name) const +{ + const size_t num_segments = segments.size(); + for (size_t i=0; i<num_segments; ++i) + { + if (segments[i].name == name) + return &segments[i]; + } + return NULL; +} + + +//---------------------------------------------------------------------- +// Dump an image info structure to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::DYLDImageInfo::PutToLog (Log *log) const +{ + if (log == NULL) + return; + const uint8_t *u = (const uint8_t *)uuid.GetBytes(); + + if (address == LLDB_INVALID_ADDRESS) + { + if (u) + { + log->Printf("\t modtime=0x%8.8" PRIx64 " uuid=%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X path='%s' (UNLOADED)", + mod_date, + u[ 0], u[ 1], u[ 2], u[ 3], + u[ 4], u[ 5], u[ 6], u[ 7], + u[ 8], u[ 9], u[10], u[11], + u[12], u[13], u[14], u[15], + file_spec.GetPath().c_str()); + } + else + log->Printf("\t modtime=0x%8.8" PRIx64 " path='%s' (UNLOADED)", + mod_date, + file_spec.GetPath().c_str()); + } + else + { + if (u) + { + log->Printf("\taddress=0x%16.16" PRIx64 " modtime=0x%8.8" PRIx64 " uuid=%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X path='%s'", + address, + mod_date, + u[ 0], u[ 1], u[ 2], u[ 3], + u[ 4], u[ 5], u[ 6], u[ 7], + u[ 8], u[ 9], u[10], u[11], + u[12], u[13], u[14], u[15], + file_spec.GetPath().c_str()); + } + else + { + log->Printf("\taddress=0x%16.16" PRIx64 " modtime=0x%8.8" PRIx64 " path='%s'", + address, + mod_date, + file_spec.GetPath().c_str()); + + } + for (uint32_t i=0; i<segments.size(); ++i) + segments[i].PutToLog(log, slide); + } +} + +//---------------------------------------------------------------------- +// Dump the _dyld_all_image_infos members and all current image infos +// that we have parsed to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::PutToLog(Log *log) const +{ + if (log == NULL) + return; + + Mutex::Locker locker(m_mutex); + log->Printf("dyld_all_image_infos = { version=%d, count=%d, addr=0x%8.8" PRIx64 ", notify=0x%8.8" PRIx64 " }", + m_dyld_all_image_infos.version, + m_dyld_all_image_infos.dylib_info_count, + (uint64_t)m_dyld_all_image_infos.dylib_info_addr, + (uint64_t)m_dyld_all_image_infos.notification); + size_t i; + const size_t count = m_dyld_image_infos.size(); + if (count > 0) + { + log->PutCString("Loaded:"); + for (i = 0; i<count; i++) + m_dyld_image_infos[i].PutToLog(log); + } +} + +void +DynamicLoaderMacOSXDYLD::PrivateInitialize(Process *process) +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s() process state = %s\n", __FUNCTION__, StateAsCString(m_process->GetState())); + Clear(true); + m_process = process; + m_process->GetTarget().ClearAllLoadedSections(); +} + +bool +DynamicLoaderMacOSXDYLD::SetNotificationBreakpoint () +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s() process state = %s\n", __FUNCTION__, StateAsCString(m_process->GetState())); + if (m_break_id == LLDB_INVALID_BREAK_ID) + { + if (m_dyld_all_image_infos.notification != LLDB_INVALID_ADDRESS) + { + Address so_addr; + // Set the notification breakpoint and install a breakpoint + // callback function that will get called each time the + // breakpoint gets hit. We will use this to track when shared + // libraries get loaded/unloaded. + bool resolved = m_process->GetTarget().ResolveLoadAddress(m_dyld_all_image_infos.notification, so_addr); + if (!resolved) + { + ModuleSP dyld_module_sp = m_dyld_module_wp.lock(); + if (dyld_module_sp) + { + UpdateImageLoadAddress (dyld_module_sp.get(), m_dyld); + resolved = m_process->GetTarget().ResolveLoadAddress(m_dyld_all_image_infos.notification, so_addr); + } + } + + if (resolved) + { + Breakpoint *dyld_break = m_process->GetTarget().CreateBreakpoint (so_addr, true, false).get(); + dyld_break->SetCallback (DynamicLoaderMacOSXDYLD::NotifyBreakpointHit, this, true); + dyld_break->SetBreakpointKind ("shared-library-event"); + m_break_id = dyld_break->GetID(); + } + } + } + return m_break_id != LLDB_INVALID_BREAK_ID; +} + +//---------------------------------------------------------------------- +// Member function that gets called when the process state changes. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::PrivateProcessStateChanged (Process *process, StateType state) +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s(%s)\n", __FUNCTION__, StateAsCString(state)); + switch (state) + { + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + case eStateDetached: + Clear(false); + break; + + case eStateStopped: + // Keep trying find dyld and set our notification breakpoint each time + // we stop until we succeed + if (!DidSetNotificationBreakpoint () && m_process->IsAlive()) + { + if (NeedToLocateDYLD ()) + LocateDYLD (); + + SetNotificationBreakpoint (); + } + break; + + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + break; + } +} + +ThreadPlanSP +DynamicLoaderMacOSXDYLD::GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) +{ + ThreadPlanSP thread_plan_sp; + StackFrame *current_frame = thread.GetStackFrameAtIndex(0).get(); + const SymbolContext ¤t_context = current_frame->GetSymbolContext(eSymbolContextSymbol); + Symbol *current_symbol = current_context.symbol; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + TargetSP target_sp (thread.CalculateTarget()); + + if (current_symbol != NULL) + { + std::vector<Address> addresses; + + if (current_symbol->IsTrampoline()) + { + const ConstString &trampoline_name = current_symbol->GetMangled().GetName(current_symbol->GetLanguage(), Mangled::ePreferMangled); + + if (trampoline_name) + { + const ModuleList &images = target_sp->GetImages(); + + SymbolContextList code_symbols; + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, code_symbols); + size_t num_code_symbols = code_symbols.GetSize(); + + if (num_code_symbols > 0) + { + for (uint32_t i = 0; i < num_code_symbols; i++) + { + SymbolContext context; + AddressRange addr_range; + if (code_symbols.GetContextAtIndex(i, context)) + { + context.GetAddressRange (eSymbolContextEverything, 0, false, addr_range); + addresses.push_back(addr_range.GetBaseAddress()); + if (log) + { + addr_t load_addr = addr_range.GetBaseAddress().GetLoadAddress(target_sp.get()); + + log->Printf ("Found a trampoline target symbol at 0x%" PRIx64 ".", load_addr); + } + } + } + } + + SymbolContextList reexported_symbols; + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeReExported, reexported_symbols); + size_t num_reexported_symbols = reexported_symbols.GetSize(); + if (num_reexported_symbols > 0) + { + for (uint32_t i = 0; i < num_reexported_symbols; i++) + { + SymbolContext context; + if (reexported_symbols.GetContextAtIndex(i, context)) + { + if (context.symbol) + { + Symbol *actual_symbol = context.symbol->ResolveReExportedSymbol(*target_sp.get()); + if (actual_symbol) + { + const Address actual_symbol_addr = actual_symbol->GetAddress(); + if (actual_symbol_addr.IsValid()) + { + addresses.push_back(actual_symbol_addr); + if (log) + { + lldb::addr_t load_addr = actual_symbol_addr.GetLoadAddress(target_sp.get()); + log->Printf ("Found a re-exported symbol: %s at 0x%" PRIx64 ".", + actual_symbol->GetName().GetCString(), load_addr); + } + } + } + } + } + } + } + + SymbolContextList indirect_symbols; + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, indirect_symbols); + size_t num_indirect_symbols = indirect_symbols.GetSize(); + if (num_indirect_symbols > 0) + { + for (uint32_t i = 0; i < num_indirect_symbols; i++) + { + SymbolContext context; + AddressRange addr_range; + if (indirect_symbols.GetContextAtIndex(i, context)) + { + context.GetAddressRange (eSymbolContextEverything, 0, false, addr_range); + addresses.push_back(addr_range.GetBaseAddress()); + if (log) + { + addr_t load_addr = addr_range.GetBaseAddress().GetLoadAddress(target_sp.get()); + + log->Printf ("Found an indirect target symbol at 0x%" PRIx64 ".", load_addr); + } + } + } + } + } + } + else if (current_symbol->GetType() == eSymbolTypeReExported) + { + // I am not sure we could ever end up stopped AT a re-exported symbol. But just in case: + + const Symbol *actual_symbol = current_symbol->ResolveReExportedSymbol(*(target_sp.get())); + if (actual_symbol) + { + Address target_addr(actual_symbol->GetAddress()); + if (target_addr.IsValid()) + { + if (log) + log->Printf ("Found a re-exported symbol: %s pointing to: %s at 0x%" PRIx64 ".", + current_symbol->GetName().GetCString(), + actual_symbol->GetName().GetCString(), + target_addr.GetLoadAddress(target_sp.get())); + addresses.push_back (target_addr.GetLoadAddress(target_sp.get())); + + } + } + } + + if (addresses.size() > 0) + { + // First check whether any of the addresses point to Indirect symbols, and if they do, resolve them: + std::vector<lldb::addr_t> load_addrs; + for (Address address : addresses) + { + Symbol *symbol = address.CalculateSymbolContextSymbol(); + if (symbol && symbol->IsIndirect()) + { + Error error; + Address symbol_address = symbol->GetAddress(); + addr_t resolved_addr = thread.GetProcess()->ResolveIndirectFunction(&symbol_address, error); + if (error.Success()) + { + load_addrs.push_back(resolved_addr); + if (log) + log->Printf("ResolveIndirectFunction found resolved target for %s at 0x%" PRIx64 ".", + symbol->GetName().GetCString(), resolved_addr); + } + } + else + { + load_addrs.push_back(address.GetLoadAddress(target_sp.get())); + } + + } + thread_plan_sp.reset (new ThreadPlanRunToAddress (thread, load_addrs, stop_others)); + } + } + else + { + if (log) + log->Printf ("Could not find symbol for step through."); + } + + return thread_plan_sp; +} + +size_t +DynamicLoaderMacOSXDYLD::FindEquivalentSymbols (lldb_private::Symbol *original_symbol, + lldb_private::ModuleList &images, + lldb_private::SymbolContextList &equivalent_symbols) +{ + const ConstString &trampoline_name = original_symbol->GetMangled().GetName(original_symbol->GetLanguage(), Mangled::ePreferMangled); + if (!trampoline_name) + return 0; + + size_t initial_size = equivalent_symbols.GetSize(); + + static const char *resolver_name_regex = "(_gc|_non_gc|\\$[A-Za-z0-9\\$]+)$"; + std::string equivalent_regex_buf("^"); + equivalent_regex_buf.append (trampoline_name.GetCString()); + equivalent_regex_buf.append (resolver_name_regex); + + RegularExpression equivalent_name_regex (equivalent_regex_buf.c_str()); + const bool append = true; + images.FindSymbolsMatchingRegExAndType (equivalent_name_regex, eSymbolTypeCode, equivalent_symbols, append); + + return equivalent_symbols.GetSize() - initial_size; +} + +Error +DynamicLoaderMacOSXDYLD::CanLoadImage () +{ + Error error; + // In order for us to tell if we can load a shared library we verify that + // the dylib_info_addr isn't zero (which means no shared libraries have + // been set yet, or dyld is currently mucking with the shared library list). + if (ReadAllImageInfosStructure ()) + { + // TODO: also check the _dyld_global_lock_held variable in libSystem.B.dylib? + // TODO: check the malloc lock? + // TODO: check the objective C lock? + if (m_dyld_all_image_infos.dylib_info_addr != 0) + return error; // Success + } + + error.SetErrorString("unsafe to load or unload shared libraries"); + return error; +} + +void +DynamicLoaderMacOSXDYLD::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderMacOSXDYLD::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +DynamicLoaderMacOSXDYLD::GetPluginNameStatic() +{ + static ConstString g_name("macosx-dyld"); + return g_name; +} + +const char * +DynamicLoaderMacOSXDYLD::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library loads/unloads in MacOSX user processes."; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +DynamicLoaderMacOSXDYLD::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DynamicLoaderMacOSXDYLD::GetPluginVersion() +{ + return 1; +} + +uint32_t +DynamicLoaderMacOSXDYLD::AddrByteSize() +{ + switch (m_dyld.header.magic) + { + case llvm::MachO::MH_MAGIC: + case llvm::MachO::MH_CIGAM: + return 4; + + case llvm::MachO::MH_MAGIC_64: + case llvm::MachO::MH_CIGAM_64: + return 8; + + default: + break; + } + return 0; +} + +lldb::ByteOrder +DynamicLoaderMacOSXDYLD::GetByteOrderFromMagic (uint32_t magic) +{ + switch (magic) + { + case llvm::MachO::MH_MAGIC: + case llvm::MachO::MH_MAGIC_64: + return endian::InlHostByteOrder(); + + case llvm::MachO::MH_CIGAM: + case llvm::MachO::MH_CIGAM_64: + if (endian::InlHostByteOrder() == lldb::eByteOrderBig) + return lldb::eByteOrderLittle; + else + return lldb::eByteOrderBig; + + default: + break; + } + return lldb::eByteOrderInvalid; +} + +lldb::ByteOrder +DynamicLoaderMacOSXDYLD::DYLDImageInfo::GetByteOrder() +{ + return DynamicLoaderMacOSXDYLD::GetByteOrderFromMagic(header.magic); +} + diff --git a/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h new file mode 100644 index 000000000000..8fd60d0b6ac7 --- /dev/null +++ b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h @@ -0,0 +1,385 @@ +//===-- DynamicLoaderMacOSXDYLD.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderMacOSXDYLD_h_ +#define liblldb_DynamicLoaderMacOSXDYLD_h_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/StructuredData.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/SafeMachO.h" + +class DynamicLoaderMacOSXDYLD : public lldb_private::DynamicLoader +{ +public: + DynamicLoaderMacOSXDYLD(lldb_private::Process *process); + + ~DynamicLoaderMacOSXDYLD() override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance (lldb_private::Process *process, bool force); + + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + void + DidAttach() override; + + void + DidLaunch() override; + + bool + ProcessDidExec() override; + + lldb::ThreadPlanSP + GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others) override; + + size_t + FindEquivalentSymbols(lldb_private::Symbol *original_symbol, + lldb_private::ModuleList &module_list, + lldb_private::SymbolContextList &equivalent_symbols) override; + + lldb_private::Error + CanLoadImage() override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + + bool + AlwaysRelyOnEHUnwindInfo(lldb_private::SymbolContext &sym_ctx) override; + +protected: + void + PrivateInitialize (lldb_private::Process *process); + + void + PrivateProcessStateChanged (lldb_private::Process *process, + lldb::StateType state); + + bool + LocateDYLD (); + + bool + DidSetNotificationBreakpoint () const; + + void + Clear (bool clear_process); + + void + PutToLog (lldb_private::Log *log) const; + + bool + ReadDYLDInfoFromMemoryAndSetNotificationCallback (lldb::addr_t addr); + + static bool + NotifyBreakpointHit (void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + uint32_t + AddrByteSize(); + + static lldb::ByteOrder + GetByteOrderFromMagic (uint32_t magic); + + bool + ReadMachHeader (lldb::addr_t addr, + llvm::MachO::mach_header *header, + lldb_private::DataExtractor *load_command_data); + + class Segment + { + public: + Segment() : + name(), + vmaddr(LLDB_INVALID_ADDRESS), + vmsize(0), + fileoff(0), + filesize(0), + maxprot(0), + initprot(0), + nsects(0), + flags(0) + { + } + + lldb_private::ConstString name; + lldb::addr_t vmaddr; + lldb::addr_t vmsize; + lldb::addr_t fileoff; + lldb::addr_t filesize; + uint32_t maxprot; + uint32_t initprot; + uint32_t nsects; + uint32_t flags; + + bool + operator==(const Segment& rhs) const + { + return name == rhs.name && vmaddr == rhs.vmaddr && vmsize == rhs.vmsize; + } + + void + PutToLog (lldb_private::Log *log, + lldb::addr_t slide) const; + + }; + + struct DYLDImageInfo + { + lldb::addr_t address; // Address of mach header for this dylib + lldb::addr_t slide; // The amount to slide all segments by if there is a global slide. + lldb::addr_t mod_date; // Modification date for this dylib + lldb_private::FileSpec file_spec; // Resolved path for this dylib + lldb_private::UUID uuid; // UUID for this dylib if it has one, else all zeros + llvm::MachO::mach_header header; // The mach header for this image + std::vector<Segment> segments; // All segment vmaddr and vmsize pairs for this executable (from memory of inferior) + uint32_t load_stop_id; // The process stop ID that the sections for this image were loaded + + DYLDImageInfo() : + address(LLDB_INVALID_ADDRESS), + slide(0), + mod_date(0), + file_spec(), + uuid(), + header(), + segments(), + load_stop_id(0) + { + } + + void + Clear(bool load_cmd_data_only) + { + if (!load_cmd_data_only) + { + address = LLDB_INVALID_ADDRESS; + slide = 0; + mod_date = 0; + file_spec.Clear(); + ::memset (&header, 0, sizeof(header)); + } + uuid.Clear(); + segments.clear(); + load_stop_id = 0; + } + + bool + operator == (const DYLDImageInfo& rhs) const + { + return address == rhs.address + && slide == rhs.slide + && mod_date == rhs.mod_date + && file_spec == rhs.file_spec + && uuid == rhs.uuid + && memcmp(&header, &rhs.header, sizeof(header)) == 0 + && segments == rhs.segments; + } + + bool + UUIDValid() const + { + return uuid.IsValid(); + } + + uint32_t + GetAddressByteSize () + { + if (header.cputype) + { + if (header.cputype & llvm::MachO::CPU_ARCH_ABI64) + return 8; + else + return 4; + } + return 0; + } + + lldb::ByteOrder + GetByteOrder(); + + lldb_private::ArchSpec + GetArchitecture () const + { + return lldb_private::ArchSpec (lldb_private::eArchTypeMachO, header.cputype, header.cpusubtype); + } + + const Segment * + FindSegment (const lldb_private::ConstString &name) const; + + void + PutToLog (lldb_private::Log *log) const; + + typedef std::vector<DYLDImageInfo> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + }; + + struct DYLDAllImageInfos + { + uint32_t version; + uint32_t dylib_info_count; // Version >= 1 + lldb::addr_t dylib_info_addr; // Version >= 1 + lldb::addr_t notification; // Version >= 1 + bool processDetachedFromSharedRegion; // Version >= 1 + bool libSystemInitialized; // Version >= 2 + lldb::addr_t dyldImageLoadAddress; // Version >= 2 + + DYLDAllImageInfos() : + version (0), + dylib_info_count (0), + dylib_info_addr (LLDB_INVALID_ADDRESS), + notification (LLDB_INVALID_ADDRESS), + processDetachedFromSharedRegion (false), + libSystemInitialized (false), + dyldImageLoadAddress (LLDB_INVALID_ADDRESS) + { + } + + void + Clear() + { + version = 0; + dylib_info_count = 0; + dylib_info_addr = LLDB_INVALID_ADDRESS; + notification = LLDB_INVALID_ADDRESS; + processDetachedFromSharedRegion = false; + libSystemInitialized = false; + dyldImageLoadAddress = LLDB_INVALID_ADDRESS; + } + + bool + IsValid() const + { + return version >= 1 || version <= 6; + } + }; + + void + RegisterNotificationCallbacks(); + + void + UnregisterNotificationCallbacks(); + + uint32_t + ParseLoadCommands (const lldb_private::DataExtractor& data, + DYLDImageInfo& dylib_info, + lldb_private::FileSpec *lc_id_dylinker); + + bool + UpdateImageLoadAddress(lldb_private::Module *module, + DYLDImageInfo& info); + + bool + UnloadImageLoadAddress (lldb_private::Module *module, + DYLDImageInfo& info); + + lldb::ModuleSP + FindTargetModuleForDYLDImageInfo (DYLDImageInfo &image_info, + bool can_create, + bool *did_create_ptr); + + DYLDImageInfo * + GetImageInfo (lldb_private::Module *module); + + bool + NeedToLocateDYLD () const; + + bool + SetNotificationBreakpoint (); + + // There is a little tricky bit where you might initially attach while dyld is updating + // the all_image_infos, and you can't read the infos, so you have to continue and pick it + // up when you hit the update breakpoint. At that point, you need to run this initialize + // function, but when you do it that way you DON'T need to do the extra work you would at + // the breakpoint. + // So this function will only do actual work if the image infos haven't been read yet. + // If it does do any work, then it will return true, and false otherwise. That way you can + // call it in the breakpoint action, and if it returns true you're done. + bool + InitializeFromAllImageInfos (); + + bool + ReadAllImageInfosStructure (); + + bool + AddModulesUsingInfosFromDebugserver (lldb_private::StructuredData::ObjectSP image_details, DYLDImageInfo::collection &image_infos); + + bool + AddModulesUsingImageInfosAddress (lldb::addr_t image_infos_addr, uint32_t image_infos_count); + + bool + AddModulesUsingImageInfos (DYLDImageInfo::collection &image_infos); + + bool + RemoveModulesUsingImageInfosAddress (lldb::addr_t image_infos_addr, uint32_t image_infos_count); + + void + UpdateImageInfosHeaderAndLoadCommands(DYLDImageInfo::collection &image_infos, + uint32_t infos_count, + bool update_executable); + + bool + ReadImageInfos (lldb::addr_t image_infos_addr, + uint32_t image_infos_count, + DYLDImageInfo::collection &image_infos); + + + DYLDImageInfo m_dyld; // Info about the current dyld being used + lldb::ModuleWP m_dyld_module_wp; + lldb::addr_t m_dyld_all_image_infos_addr; + DYLDAllImageInfos m_dyld_all_image_infos; + uint32_t m_dyld_all_image_infos_stop_id; + lldb::user_id_t m_break_id; + DYLDImageInfo::collection m_dyld_image_infos; // Current shared libraries information + uint32_t m_dyld_image_infos_stop_id; // The process stop ID that "m_dyld_image_infos" is valid for + mutable lldb_private::Mutex m_mutex; + lldb_private::Process::Notifications m_notification_callbacks; + bool m_process_image_addr_is_all_images_infos; + +private: + DISALLOW_COPY_AND_ASSIGN (DynamicLoaderMacOSXDYLD); +}; + +#endif // liblldb_DynamicLoaderMacOSXDYLD_h_ diff --git a/source/Plugins/DynamicLoader/MacOSX-DYLD/Makefile b/source/Plugins/DynamicLoader/MacOSX-DYLD/Makefile new file mode 100644 index 000000000000..ffac3b457179 --- /dev/null +++ b/source/Plugins/DynamicLoader/MacOSX-DYLD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Disassembler/llvm/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 := lldbPluginDynamicLoaderMacOSXDYLD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/CMakeLists.txt b/source/Plugins/DynamicLoader/POSIX-DYLD/CMakeLists.txt new file mode 100644 index 000000000000..b302997794a0 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_library(lldbPluginDynamicLoaderPosixDYLD + AuxVector.cpp + DYLDRendezvous.cpp + DynamicLoaderPOSIXDYLD.cpp + ) diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/Makefile b/source/Plugins/DynamicLoader/POSIX-DYLD/Makefile new file mode 100644 index 000000000000..1c56366015d6 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/DynamicLoader/POSIX-DYLD/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 := lldbPluginDynamicLoaderPosixDYLD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/Static/CMakeLists.txt b/source/Plugins/DynamicLoader/Static/CMakeLists.txt new file mode 100644 index 000000000000..274f6bac3697 --- /dev/null +++ b/source/Plugins/DynamicLoader/Static/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginDynamicLoaderStatic + DynamicLoaderStatic.cpp + ) diff --git a/source/Plugins/DynamicLoader/Static/Makefile b/source/Plugins/DynamicLoader/Static/Makefile new file mode 100644 index 000000000000..63972dfc5512 --- /dev/null +++ b/source/Plugins/DynamicLoader/Static/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/DynamicLoader/Static/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 := lldbPluginDynamicLoaderStatic +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/Windows-DYLD/CMakeLists.txt b/source/Plugins/DynamicLoader/Windows-DYLD/CMakeLists.txt new file mode 100644 index 000000000000..ee768057bcd5 --- /dev/null +++ b/source/Plugins/DynamicLoader/Windows-DYLD/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginDynamicLoaderWindowsDYLD + DynamicLoaderWindowsDYLD.cpp + ) diff --git a/source/Plugins/DynamicLoader/Windows-DYLD/Makefile b/source/Plugins/DynamicLoader/Windows-DYLD/Makefile new file mode 100644 index 000000000000..bf62aee30b2b --- /dev/null +++ b/source/Plugins/DynamicLoader/Windows-DYLD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/DynamicLoader/Windows-DYLD/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 := lldbPluginDynamicLoaderWindowsDYLD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ExpressionParser/CMakeLists.txt b/source/Plugins/ExpressionParser/CMakeLists.txt new file mode 100644 index 000000000000..dc0540ad30a1 --- /dev/null +++ b/source/Plugins/ExpressionParser/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(Clang) +add_subdirectory(Go) diff --git a/source/Plugins/ExpressionParser/Clang/CMakeLists.txt b/source/Plugins/ExpressionParser/Clang/CMakeLists.txt new file mode 100644 index 000000000000..e18dde6b700e --- /dev/null +++ b/source/Plugins/ExpressionParser/Clang/CMakeLists.txt @@ -0,0 +1,15 @@ +add_lldb_library(lldbPluginExpressionParserClang + ASTDumper.cpp + ASTResultSynthesizer.cpp + ASTStructExtractor.cpp + ClangASTSource.cpp + ClangExpressionDeclMap.cpp + ClangExpressionParser.cpp + ClangExpressionVariable.cpp + ClangFunctionCaller.cpp + ClangModulesDeclVendor.cpp + ClangPersistentVariables.cpp + ClangUserExpression.cpp + ClangUtilityFunction.cpp + IRForTarget.cpp + ) diff --git a/source/Plugins/ExpressionParser/Clang/Makefile b/source/Plugins/ExpressionParser/Clang/Makefile new file mode 100644 index 000000000000..eb592daabb48 --- /dev/null +++ b/source/Plugins/ExpressionParser/Clang/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ExpressionParser/Clang ---------------*- 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 := lldbPluginExpressionParserClang +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ExpressionParser/Go/CMakeLists.txt b/source/Plugins/ExpressionParser/Go/CMakeLists.txt new file mode 100644 index 000000000000..f59f51e76489 --- /dev/null +++ b/source/Plugins/ExpressionParser/Go/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_library(lldbPluginExpressionParserGo + GoLexer.cpp + GoParser.cpp + GoUserExpression.cpp + ) diff --git a/source/Plugins/ExpressionParser/Go/Makefile b/source/Plugins/ExpressionParser/Go/Makefile new file mode 100644 index 000000000000..c5bd7fb2857e --- /dev/null +++ b/source/Plugins/ExpressionParser/Go/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ExpressionParser/Clang ---------------*- 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 := lldbPluginExpressionParserGo +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Instruction/ARM/CMakeLists.txt b/source/Plugins/Instruction/ARM/CMakeLists.txt new file mode 100644 index 000000000000..dc547a57306b --- /dev/null +++ b/source/Plugins/Instruction/ARM/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lldb_library(lldbPluginInstructionARM + EmulateInstructionARM.cpp + EmulationStateARM.cpp + ) diff --git a/source/Plugins/Instruction/ARM/Makefile b/source/Plugins/Instruction/ARM/Makefile new file mode 100644 index 000000000000..31a233b0b374 --- /dev/null +++ b/source/Plugins/Instruction/ARM/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Instruction/ARM/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 := lldbPluginInstructionARM +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Instruction/ARM64/CMakeLists.txt b/source/Plugins/Instruction/ARM64/CMakeLists.txt new file mode 100644 index 000000000000..9f8ee0848984 --- /dev/null +++ b/source/Plugins/Instruction/ARM64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginInstructionARM64 + EmulateInstructionARM64.cpp + ) diff --git a/source/Plugins/Instruction/ARM64/Makefile b/source/Plugins/Instruction/ARM64/Makefile new file mode 100644 index 000000000000..8f60ce6dcd47 --- /dev/null +++ b/source/Plugins/Instruction/ARM64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Instruction/ARM/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 := lldbPluginInstructionARM64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Instruction/CMakeLists.txt b/source/Plugins/Instruction/CMakeLists.txt new file mode 100644 index 000000000000..78f2f64cf1ab --- /dev/null +++ b/source/Plugins/Instruction/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(ARM) +add_subdirectory(ARM64) +add_subdirectory(MIPS) +add_subdirectory(MIPS64) diff --git a/source/Plugins/Instruction/MIPS/CMakeLists.txt b/source/Plugins/Instruction/MIPS/CMakeLists.txt new file mode 100644 index 000000000000..dc6707336939 --- /dev/null +++ b/source/Plugins/Instruction/MIPS/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginInstructionMIPS + EmulateInstructionMIPS.cpp + ) diff --git a/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp b/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp index d6485f686e2c..a71fca7c5c3a 100644 --- a/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp +++ b/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp @@ -591,45 +591,45 @@ EmulateInstructionMIPS::GetOpcodeForInstruction (const char *op_name) //---------------------------------------------------------------------- // Branch instructions //---------------------------------------------------------------------- - { "BEQ", &EmulateInstructionMIPS::Emulate_BEQ, "BEQ rs,rt,offset" }, - { "BNE", &EmulateInstructionMIPS::Emulate_BNE, "BNE rs,rt,offset" }, - { "BEQL", &EmulateInstructionMIPS::Emulate_BEQL, "BEQL rs,rt,offset" }, - { "BNEL", &EmulateInstructionMIPS::Emulate_BNEL, "BNEL rs,rt,offset" }, - { "BGEZALL", &EmulateInstructionMIPS::Emulate_BGEZALL, "BGEZALL rt,offset" }, + { "BEQ", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQ rs,rt,offset" }, + { "BNE", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNE rs,rt,offset" }, + { "BEQL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQL rs,rt,offset" }, + { "BNEL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNEL rs,rt,offset" }, + { "BGEZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BGEZALL rt,offset" }, { "BAL", &EmulateInstructionMIPS::Emulate_BAL, "BAL offset" }, - { "BGEZAL", &EmulateInstructionMIPS::Emulate_BGEZAL, "BGEZAL rs,offset" }, + { "BGEZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BGEZAL rs,offset" }, { "BALC", &EmulateInstructionMIPS::Emulate_BALC, "BALC offset" }, { "BC", &EmulateInstructionMIPS::Emulate_BC, "BC offset" }, - { "BGEZ", &EmulateInstructionMIPS::Emulate_BGEZ, "BGEZ rs,offset" }, - { "BLEZALC", &EmulateInstructionMIPS::Emulate_BLEZALC, "BLEZALC rs,offset" }, - { "BGEZALC", &EmulateInstructionMIPS::Emulate_BGEZALC, "BGEZALC rs,offset" }, - { "BLTZALC", &EmulateInstructionMIPS::Emulate_BLTZALC, "BLTZALC rs,offset" }, - { "BGTZALC", &EmulateInstructionMIPS::Emulate_BGTZALC, "BGTZALC rs,offset" }, - { "BEQZALC", &EmulateInstructionMIPS::Emulate_BEQZALC, "BEQZALC rs,offset" }, - { "BNEZALC", &EmulateInstructionMIPS::Emulate_BNEZALC, "BNEZALC rs,offset" }, - { "BEQC", &EmulateInstructionMIPS::Emulate_BEQC, "BEQC rs,rt,offset" }, - { "BNEC", &EmulateInstructionMIPS::Emulate_BNEC, "BNEC rs,rt,offset" }, - { "BLTC", &EmulateInstructionMIPS::Emulate_BLTC, "BLTC rs,rt,offset" }, - { "BGEC", &EmulateInstructionMIPS::Emulate_BGEC, "BGEC rs,rt,offset" }, - { "BLTUC", &EmulateInstructionMIPS::Emulate_BLTUC, "BLTUC rs,rt,offset" }, - { "BGEUC", &EmulateInstructionMIPS::Emulate_BGEUC, "BGEUC rs,rt,offset" }, - { "BLTZC", &EmulateInstructionMIPS::Emulate_BLTZC, "BLTZC rt,offset" }, - { "BLEZC", &EmulateInstructionMIPS::Emulate_BLEZC, "BLEZC rt,offset" }, - { "BGEZC", &EmulateInstructionMIPS::Emulate_BGEZC, "BGEZC rt,offset" }, - { "BGTZC", &EmulateInstructionMIPS::Emulate_BGTZC, "BGTZC rt,offset" }, - { "BEQZC", &EmulateInstructionMIPS::Emulate_BEQZC, "BEQZC rt,offset" }, - { "BNEZC", &EmulateInstructionMIPS::Emulate_BNEZC, "BNEZC rt,offset" }, - { "BGEZL", &EmulateInstructionMIPS::Emulate_BGEZL, "BGEZL rt,offset" }, - { "BGTZ", &EmulateInstructionMIPS::Emulate_BGTZ, "BGTZ rt,offset" }, - { "BGTZL", &EmulateInstructionMIPS::Emulate_BGTZL, "BGTZL rt,offset" }, - { "BLEZ", &EmulateInstructionMIPS::Emulate_BLEZ, "BLEZ rt,offset" }, - { "BLEZL", &EmulateInstructionMIPS::Emulate_BLEZL, "BLEZL rt,offset" }, - { "BLTZ", &EmulateInstructionMIPS::Emulate_BLTZ, "BLTZ rt,offset" }, - { "BLTZAL", &EmulateInstructionMIPS::Emulate_BLTZAL, "BLTZAL rt,offset" }, - { "BLTZALL", &EmulateInstructionMIPS::Emulate_BLTZALL, "BLTZALL rt,offset" }, - { "BLTZL", &EmulateInstructionMIPS::Emulate_BLTZL, "BLTZL rt,offset" }, - { "BOVC", &EmulateInstructionMIPS::Emulate_BOVC, "BOVC rs,rt,offset" }, - { "BNVC", &EmulateInstructionMIPS::Emulate_BNVC, "BNVC rs,rt,offset" }, + { "BGEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZ rs,offset" }, + { "BLEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BLEZALC rs,offset" }, + { "BGEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BGEZALC rs,offset" }, + { "BLTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BLTZALC rs,offset" }, + { "BGTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BGTZALC rs,offset" }, + { "BEQZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BEQZALC rs,offset" }, + { "BNEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BNEZALC rs,offset" }, + { "BEQC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BEQC rs,rt,offset" }, + { "BNEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BNEC rs,rt,offset" }, + { "BLTC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BLTC rs,rt,offset" }, + { "BGEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BGEC rs,rt,offset" }, + { "BLTUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BLTUC rs,rt,offset" }, + { "BGEUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BGEUC rs,rt,offset" }, + { "BLTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLTZC rt,offset" }, + { "BLEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLEZC rt,offset" }, + { "BGEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGEZC rt,offset" }, + { "BGTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGTZC rt,offset" }, + { "BEQZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BEQZC rt,offset" }, + { "BNEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BNEZC rt,offset" }, + { "BGEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZL rt,offset" }, + { "BGTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZ rt,offset" }, + { "BGTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZL rt,offset" }, + { "BLEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZ rt,offset" }, + { "BLEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZL rt,offset" }, + { "BLTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZ rt,offset" }, + { "BLTZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BLTZAL rt,offset" }, + { "BLTZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BLTZALL rt,offset" }, + { "BLTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZL rt,offset" }, + { "BOVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BOVC rs,rt,offset" }, + { "BNVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BNVC rs,rt,offset" }, { "J", &EmulateInstructionMIPS::Emulate_J, "J target" }, { "JAL", &EmulateInstructionMIPS::Emulate_JAL, "JAL target" }, { "JALX", &EmulateInstructionMIPS::Emulate_JAL, "JALX target" }, @@ -639,16 +639,16 @@ EmulateInstructionMIPS::GetOpcodeForInstruction (const char *op_name) { "JIC", &EmulateInstructionMIPS::Emulate_JIC, "JIC rt,offset" }, { "JR", &EmulateInstructionMIPS::Emulate_JR, "JR target" }, { "JR_HB", &EmulateInstructionMIPS::Emulate_JR, "JR.HB target" }, - { "BC1F", &EmulateInstructionMIPS::Emulate_BC1F, "BC1F cc, offset" }, - { "BC1T", &EmulateInstructionMIPS::Emulate_BC1T, "BC1T cc, offset" }, - { "BC1FL", &EmulateInstructionMIPS::Emulate_BC1FL, "BC1FL cc, offset" }, - { "BC1TL", &EmulateInstructionMIPS::Emulate_BC1TL, "BC1TL cc, offset" }, + { "BC1F", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1F cc, offset" }, + { "BC1T", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1T cc, offset" }, + { "BC1FL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1FL cc, offset" }, + { "BC1TL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1TL cc, offset" }, { "BC1EQZ", &EmulateInstructionMIPS::Emulate_BC1EQZ, "BC1EQZ ft, offset" }, { "BC1NEZ", &EmulateInstructionMIPS::Emulate_BC1NEZ, "BC1NEZ ft, offset" }, - { "BC1ANY2F", &EmulateInstructionMIPS::Emulate_BC1ANY2F, "BC1ANY2F cc, offset" }, - { "BC1ANY2T", &EmulateInstructionMIPS::Emulate_BC1ANY2T, "BC1ANY2T cc, offset" }, - { "BC1ANY4F", &EmulateInstructionMIPS::Emulate_BC1ANY4F, "BC1ANY4F cc, offset" }, - { "BC1ANY4T", &EmulateInstructionMIPS::Emulate_BC1ANY4T, "BC1ANY4T cc, offset" }, + { "BC1ANY2F", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY2F cc, offset" }, + { "BC1ANY2T", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY2T cc, offset" }, + { "BC1ANY4F", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY4F cc, offset" }, + { "BC1ANY4T", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY4T cc, offset" }, { "BNZ_B", &EmulateInstructionMIPS::Emulate_BNZB, "BNZ.b wt,s16" }, { "BNZ_H", &EmulateInstructionMIPS::Emulate_BNZH, "BNZ.h wt,s16" }, { "BNZ_W", &EmulateInstructionMIPS::Emulate_BNZW, "BNZ.w wt,s16" }, @@ -1387,19 +1387,26 @@ EmulateInstructionMIPS::Emulate_JRADDIUSP (llvm::MCInst& insn) return true; } +static int +IsAdd64bitOverflow (int32_t a, int32_t b) +{ + int32_t r = (uint32_t) a + (uint32_t) b; + return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0); +} + +/* + Emulate below MIPS branch instructions. + BEQ, BNE : Branch on condition + BEQL, BNEL : Branch likely +*/ bool -EmulateInstructionMIPS::Emulate_BEQ (llvm::MCInst& insn) +EmulateInstructionMIPS::Emulate_BXX_3ops (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; + int32_t offset, pc, target = 0, rs_val, rt_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); - /* - * BEQ rs, rt, offset - * condition <- (GPR[rs] = GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); @@ -1416,13 +1423,399 @@ EmulateInstructionMIPS::Emulate_BEQ (llvm::MCInst& insn) if (!success) return false; - if (rs_val == rt_val) - target = pc + offset; - else - target = pc + 8; + if (!strcasecmp (op_name, "BEQ") || + !strcasecmp (op_name, "BEQL")) + { + if (rs_val == rt_val) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BNE") || + !strcasecmp (op_name, "BNEL")) + { + if (rs_val != rt_val) + target = pc + offset; + else + target = pc + 8; + } Context context; context.type = eContextRelativeBranchImmediate; + context.SetImmediate (offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + return true; +} + +/* + Emulate below MIPS branch instructions. + BEQC, BNEC, BLTC, BGEC, BLTUC, BGEUC, BOVC, BNVC: Compact branch instructions with no delay slot +*/ +bool +EmulateInstructionMIPS::Emulate_BXX_3ops_C (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs, rt; + int32_t offset, pc, target = 0, rs_val, rt_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); + offset = insn.getOperand(2).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BEQC")) + { + if (rs_val == rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNEC")) + { + if (rs_val != rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLTC")) + { + if (rs_val < rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEC")) + { + if (rs_val >= rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLTUC")) + { + if (rs_val < rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEUC")) + { + if ((uint32_t)rs_val >= (uint32_t)rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BOVC")) + { + if (IsAdd64bitOverflow (rs_val, rt_val)) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNVC")) + { + if (!IsAdd64bitOverflow (rs_val, rt_val)) + target = pc + 4 + offset; + else + target = pc + 4; + } + + Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate (current_inst_size + offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + return true; +} + +/* + Emulate below MIPS conditional branch and link instructions. + BLEZALC, BGEZALC, BLTZALC, BGTZALC, BEQZALC, BNEZALC : Compact branches +*/ +bool +EmulateInstructionMIPS::Emulate_Bcond_Link_C (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLEZALC")) + { + if (rs_val <= 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEZALC")) + { + if (rs_val >= 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLTZALC")) + { + if (rs_val < 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGTZALC")) + { + if (rs_val > 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BEQZALC")) + { + if (rs_val == 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNEZALC")) + { + if (rs_val != 0) + target = pc + offset; + else + target = pc + 4; + } + + Context context; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) + return false; + + return true; +} + +/* + Emulate below MIPS Non-Compact conditional branch and link instructions. + BLTZAL, BGEZAL : + BLTZALL, BGEZALL : Branch likely +*/ +bool +EmulateInstructionMIPS::Emulate_Bcond_Link (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLTZAL") || + !strcasecmp (op_name, "BLTZALL")) + { + if ((int32_t) rs_val < 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BGEZAL") || + !strcasecmp (op_name, "BGEZALL")) + { + if ((int32_t) rs_val >= 0) + target = pc + offset; + else + target = pc + 8; + } + + Context context; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) + return false; + + return true; +} + +/* + Emulate below MIPS branch instructions. + BLTZL, BGEZL, BGTZL, BLEZL : Branch likely + BLTZ, BGEZ, BGTZ, BLEZ : Non-compact branches +*/ +bool +EmulateInstructionMIPS::Emulate_BXX_2ops (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLTZL") || + !strcasecmp (op_name, "BLTZ")) + { + if (rs_val < 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BGEZL") || + !strcasecmp (op_name, "BGEZ")) + { + if (rs_val >= 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BGTZL") || + !strcasecmp (op_name, "BGTZ")) + { + if (rs_val > 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BLEZL") || + !strcasecmp (op_name, "BLEZ")) + { + if (rs_val <= 0) + target = pc + offset; + else + target = pc + 8; + } + + Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate (offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + return true; +} + +/* + Emulate below MIPS branch instructions. + BLTZC, BLEZC, BGEZC, BGTZC, BEQZC, BNEZC : Compact Branches +*/ +bool +EmulateInstructionMIPS::Emulate_BXX_2ops_C (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLTZC")) + { + if (rs_val < 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLEZC")) + { + if (rs_val <= 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEZC")) + { + if (rs_val >= 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGTZC")) + { + if (rs_val > 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BEQZC")) + { + if (rs_val == 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNEZC")) + { + if (rs_val != 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + + Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate (current_inst_size + offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; @@ -1688,450 +2081,6 @@ EmulateInstructionMIPS::Emulate_JALRS (llvm::MCInst& insn) } bool -EmulateInstructionMIPS::Emulate_BNE (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BNE rs, rt, offset - * condition <- (GPR[rs] != GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val != rt_val) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BEQL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BEQL rs, rt, offset - * condition <- (GPR[rs] = GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val == rt_val) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BNEL rs, rt, offset - * condition <- (GPR[rs] != GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val != rt_val) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZL rs, offset - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZL rs, offset - * condition <- (GPR[rs] < 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZL rs, offset - * condition <- (GPR[rs] > 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val > 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLEZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZL rs, offset - * condition <- (GPR[rs] <= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val <= 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZ rs, offset - * condition <- (GPR[rs] > 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val > 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLEZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZ rs, offset - * condition <- (GPR[rs] <= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val <= 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZ rs, offset - * condition <- (GPR[rs] < 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZALL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZALL rt, offset - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - -bool EmulateInstructionMIPS::Emulate_BAL (llvm::MCInst& insn) { bool success = false; @@ -2194,430 +2143,6 @@ EmulateInstructionMIPS::Emulate_BALC (llvm::MCInst& insn) } bool -EmulateInstructionMIPS::Emulate_BGEZAL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZAL rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] >= 0) - * if condition then - * RA = PC + 8 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if ((int32_t) rs_val >= 0) - target = pc + offset; - else - target = pc + 8; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZAL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZAL rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] < 0) - * if condition then - * RA = PC + 8 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if ((int32_t) rs_val < 0) - target = pc + offset; - else - target = pc + 8; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZALL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZALL rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] < 0) - * if condition then - * RA = PC + 8 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - - -bool -EmulateInstructionMIPS::Emulate_BLEZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] <= 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val <= 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] >= 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] < 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] > 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val > 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BEQZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target, rs_val; - - /* - * BEQZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] == 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val == 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target, rs_val; - - /* - * BNEZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] != 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val != 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target, rs_val; - - /* - * BGEZ rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 8; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool EmulateInstructionMIPS::Emulate_BC (llvm::MCInst& insn) { bool success = false; @@ -2645,597 +2170,6 @@ EmulateInstructionMIPS::Emulate_BC (llvm::MCInst& insn) } bool -EmulateInstructionMIPS::Emulate_BEQC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BEQC rs, rt, offset - * condition <- (GPR[rs] = GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val == rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BNEC rs, rt, offset - * condition <- (GPR[rs] != GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val != rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BLTC rs, rt, offset - * condition <- (GPR[rs] < GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val < rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BGEC rs, rt, offset - * condition <- (GPR[rs] > GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val > rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTUC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - uint32_t rs_val, rt_val; - - /* - * BLTUC rs, rt, offset - * condition <- (GPR[rs] < GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val < rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEUC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - uint32_t rs_val, rt_val; - - /* - * BGEUC rs, rt, offset - * condition <- (GPR[rs] > GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val > rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZC rs, offset - * condition <- (GPR[rs] < 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val < 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLEZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZC rs, offset - * condition <- (GPR[rs] <= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val <= 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZC rs, offset - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val >= 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZC rs, offset - * condition <- (GPR[rs] > 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val > 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BEQZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - uint32_t rs_val; - - /* - * BEQZC rs, offset - * condition <- (GPR[rs] = 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val == 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - uint32_t rs_val; - - /* - * BNEZC rs, offset - * condition <- (GPR[rs] != 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val != 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -static int -IsAdd64bitOverflow (int32_t a, int32_t b) -{ - int32_t r = (uint32_t) a + (uint32_t) b; - return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0); -} - -bool -EmulateInstructionMIPS::Emulate_BOVC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BOVC rs, rt, offset - * condition <- overflow(GPR[rs] + GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (IsAdd64bitOverflow (rs_val, rt_val)) - target = pc + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNVC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BNVC rs, rt, offset - * condition <- overflow(GPR[rs] + GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (! IsAdd64bitOverflow (rs_val, rt_val)) - target = pc + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool EmulateInstructionMIPS::Emulate_J (llvm::MCInst& insn) { bool success = false; @@ -3419,102 +2353,19 @@ EmulateInstructionMIPS::Emulate_JR (llvm::MCInst& insn) return true; } +/* + Emulate Branch on FP True/False + BC1F, BC1FL : Branch on FP False (L stands for branch likely) + BC1T, BC1TL : Branch on FP True (L stands for branch likely) +*/ bool -EmulateInstructionMIPS::Emulate_BC1F (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1F cc, offset - * condition <- (FPConditionCode(cc) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) == 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1T (llvm::MCInst& insn) +EmulateInstructionMIPS::Emulate_FP_branch (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1T cc, offset - * condition <- (FPConditionCode(cc) != 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) != 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} + int32_t pc, offset, target = 0; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); -bool -EmulateInstructionMIPS::Emulate_BC1FL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1F cc, offset - * condition <- (FPConditionCode(cc) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); @@ -3528,53 +2379,23 @@ EmulateInstructionMIPS::Emulate_BC1FL (llvm::MCInst& insn) /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) == 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} -bool -EmulateInstructionMIPS::Emulate_BC1TL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1T cc, offset - * condition <- (FPConditionCode(cc) != 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) != 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - + if (!strcasecmp (op_name, "BC1F") || + !strcasecmp (op_name, "BC1FL")) + { + if ((fcsr & (1 << cc)) == 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1T") || + !strcasecmp (op_name, "BC1TL")) + { + if ((fcsr & (1 << cc)) != 0) + target = pc + offset; + else + target = pc + 8; + } Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) @@ -3661,154 +2482,19 @@ EmulateInstructionMIPS::Emulate_BC1NEZ (llvm::MCInst& insn) return true; } +/* + Emulate MIPS-3D Branch instructions + BC1ANY2F, BC1ANY2T : Branch on Any of Two Floating Point Condition Codes False/True + BC1ANY4F, BC1ANY4T : Branch on Any of Four Floating Point Condition Codes False/True +*/ bool -EmulateInstructionMIPS::Emulate_BC1ANY2F (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY2F cc, offset - * condition <- (FPConditionCode(cc) == 0 - * || FPConditionCode(cc+1) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - /* if any one bit is 0 */ - if (((fcsr >> cc) & 3) != 3) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1ANY2T (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY2T cc, offset - * condition <- (FPConditionCode(cc) == 1 - * || FPConditionCode(cc+1) == 1) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - /* if any one bit is 1 */ - if (((fcsr >> cc) & 3) != 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1ANY4F (llvm::MCInst& insn) +EmulateInstructionMIPS::Emulate_3D_branch (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY4F cc, offset - * condition <- (FPConditionCode(cc) == 0 - * || FPConditionCode(cc+1) == 0) - * || FPConditionCode(cc+2) == 0) - * || FPConditionCode(cc+3) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - /* if any one bit is 0 */ - if (((fcsr >> cc) & 0xf) != 0xf) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} + int32_t pc, offset, target = 0; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); -bool -EmulateInstructionMIPS::Emulate_BC1ANY4T (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY4T cc, offset - * condition <- (FPConditionCode(cc) == 1 - * || FPConditionCode(cc+1) == 1) - * || FPConditionCode(cc+2) == 1) - * || FPConditionCode(cc+3) == 1) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); @@ -3823,12 +2509,38 @@ EmulateInstructionMIPS::Emulate_BC1ANY4T (llvm::MCInst& insn) /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - /* if any one bit is 1 */ - if (((fcsr >> cc) & 0xf) != 0) - target = pc + offset; - else - target = pc + 8; - + if (!strcasecmp (op_name, "BC1ANY2F")) + { + /* if any one bit is 0 */ + if (((fcsr >> cc) & 3) != 3) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1ANY2T")) + { + /* if any one bit is 1 */ + if (((fcsr >> cc) & 3) != 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1ANY4F")) + { + /* if any one bit is 0 */ + if (((fcsr >> cc) & 0xf) != 0xf) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1ANY4T")) + { + /* if any one bit is 1 */ + if (((fcsr >> cc) & 0xf) != 0) + target = pc + offset; + else + target = pc + 8; + } Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) diff --git a/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h b/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h index e1340f983278..892de054e2ae 100644 --- a/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h +++ b/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h @@ -160,121 +160,37 @@ protected: Emulate_LDST_Reg (llvm::MCInst& insn); bool - Emulate_BEQ (llvm::MCInst& insn); + Emulate_BXX_3ops (llvm::MCInst& insn); bool - Emulate_BNE (llvm::MCInst& insn); + Emulate_BXX_3ops_C (llvm::MCInst& insn); bool - Emulate_BEQL (llvm::MCInst& insn); + Emulate_BXX_2ops (llvm::MCInst& insn); bool - Emulate_BNEL (llvm::MCInst& insn); + Emulate_BXX_2ops_C (llvm::MCInst& insn); bool - Emulate_BGEZALL (llvm::MCInst& insn); + Emulate_Bcond_Link_C (llvm::MCInst& insn); bool - Emulate_BAL (llvm::MCInst& insn); - - bool - Emulate_BGEZAL (llvm::MCInst& insn); - - bool - Emulate_BALC (llvm::MCInst& insn); - - bool - Emulate_BC (llvm::MCInst& insn); - - bool - Emulate_BGEZ (llvm::MCInst& insn); - - bool - Emulate_BLEZALC (llvm::MCInst& insn); - - bool - Emulate_BGEZALC (llvm::MCInst& insn); - - bool - Emulate_BLTZALC (llvm::MCInst& insn); - - bool - Emulate_BGTZALC (llvm::MCInst& insn); - - bool - Emulate_BEQZALC (llvm::MCInst& insn); - - bool - Emulate_BNEZALC (llvm::MCInst& insn); - - bool - Emulate_BEQC (llvm::MCInst& insn); - - bool - Emulate_BNEC (llvm::MCInst& insn); - - bool - Emulate_BLTC (llvm::MCInst& insn); - - bool - Emulate_BGEC (llvm::MCInst& insn); - - bool - Emulate_BLTUC (llvm::MCInst& insn); - - bool - Emulate_BGEUC (llvm::MCInst& insn); - - bool - Emulate_BLTZC (llvm::MCInst& insn); - - bool - Emulate_BLEZC (llvm::MCInst& insn); + Emulate_Bcond_Link (llvm::MCInst& insn); bool - Emulate_BGEZC (llvm::MCInst& insn); + Emulate_FP_branch (llvm::MCInst& insn); bool - Emulate_BGTZC (llvm::MCInst& insn); + Emulate_3D_branch (llvm::MCInst& insn); bool - Emulate_BEQZC (llvm::MCInst& insn); - - bool - Emulate_BNEZC (llvm::MCInst& insn); - - bool - Emulate_BGEZL (llvm::MCInst& insn); - - bool - Emulate_BGTZ (llvm::MCInst& insn); - - bool - Emulate_BGTZL (llvm::MCInst& insn); - - bool - Emulate_BLEZ (llvm::MCInst& insn); - - bool - Emulate_BLEZL (llvm::MCInst& insn); - - bool - Emulate_BLTZ (llvm::MCInst& insn); - - bool - Emulate_BLTZAL (llvm::MCInst& insn); - - bool - Emulate_BLTZALL (llvm::MCInst& insn); - - bool - Emulate_BLTZL (llvm::MCInst& insn); + Emulate_BAL (llvm::MCInst& insn); bool - Emulate_BOVC (llvm::MCInst& insn); + Emulate_BALC (llvm::MCInst& insn); bool - Emulate_BNVC (llvm::MCInst& insn); + Emulate_BC (llvm::MCInst& insn); bool Emulate_J (llvm::MCInst& insn); @@ -295,36 +211,12 @@ protected: Emulate_JR (llvm::MCInst& insn); bool - Emulate_BC1F (llvm::MCInst& insn); - - bool - Emulate_BC1T (llvm::MCInst& insn); - - bool - Emulate_BC1FL (llvm::MCInst& insn); - - bool - Emulate_BC1TL (llvm::MCInst& insn); - - bool Emulate_BC1EQZ (llvm::MCInst& insn); bool Emulate_BC1NEZ (llvm::MCInst& insn); bool - Emulate_BC1ANY2F (llvm::MCInst& insn); - - bool - Emulate_BC1ANY2T (llvm::MCInst& insn); - - bool - Emulate_BC1ANY4F (llvm::MCInst& insn); - - bool - Emulate_BC1ANY4T (llvm::MCInst& insn); - - bool Emulate_BNZB (llvm::MCInst& insn); bool diff --git a/source/Plugins/Instruction/MIPS/Makefile b/source/Plugins/Instruction/MIPS/Makefile new file mode 100644 index 000000000000..e9cef4ba0cf7 --- /dev/null +++ b/source/Plugins/Instruction/MIPS/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Instruction/MIPS/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 := lldbPluginInstructionMIPS +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Instruction/MIPS64/CMakeLists.txt b/source/Plugins/Instruction/MIPS64/CMakeLists.txt new file mode 100644 index 000000000000..25c919c4edb6 --- /dev/null +++ b/source/Plugins/Instruction/MIPS64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginInstructionMIPS64 + EmulateInstructionMIPS64.cpp + ) diff --git a/source/Plugins/Instruction/MIPS64/Makefile b/source/Plugins/Instruction/MIPS64/Makefile new file mode 100644 index 000000000000..7e5b339a359d --- /dev/null +++ b/source/Plugins/Instruction/MIPS64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Instruction/MIPS64/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 := lldbPluginInstructionMIPS64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt b/source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt new file mode 100644 index 000000000000..77e7d15caa7c --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginInstrumentationRuntimeAddressSanitizer + AddressSanitizerRuntime.cpp + ) diff --git a/source/Plugins/InstrumentationRuntime/AddressSanitizer/Makefile b/source/Plugins/InstrumentationRuntime/AddressSanitizer/Makefile new file mode 100644 index 000000000000..030aec17b026 --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/AddressSanitizer/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/InstrumentationRuntime/AddressSanitizer 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 := lldbPluginInstrumentationRuntimeAddressSanitizer +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/InstrumentationRuntime/CMakeLists.txt b/source/Plugins/InstrumentationRuntime/CMakeLists.txt new file mode 100644 index 000000000000..8ee303b25359 --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(AddressSanitizer) diff --git a/source/Plugins/JITLoader/CMakeLists.txt b/source/Plugins/JITLoader/CMakeLists.txt new file mode 100644 index 000000000000..e52230199109 --- /dev/null +++ b/source/Plugins/JITLoader/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(GDB) diff --git a/source/Plugins/JITLoader/GDB/CMakeLists.txt b/source/Plugins/JITLoader/GDB/CMakeLists.txt new file mode 100644 index 000000000000..bcf714dca593 --- /dev/null +++ b/source/Plugins/JITLoader/GDB/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(.) + +add_lldb_library(lldbPluginJITLoaderGDB + JITLoaderGDB.cpp + ) + diff --git a/source/Plugins/JITLoader/GDB/Makefile b/source/Plugins/JITLoader/GDB/Makefile new file mode 100644 index 000000000000..cd5404ffca18 --- /dev/null +++ b/source/Plugins/JITLoader/GDB/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/JITLoader/GDB/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 := lldbPluginJITLoaderGDB +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Language/CMakeLists.txt b/source/Plugins/Language/CMakeLists.txt new file mode 100644 index 000000000000..60b3da2406b6 --- /dev/null +++ b/source/Plugins/Language/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(CPlusPlus) +add_subdirectory(Go) +add_subdirectory(ObjC) +add_subdirectory(ObjCPlusPlus) diff --git a/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/source/Plugins/Language/CPlusPlus/CMakeLists.txt new file mode 100644 index 000000000000..0b0f0f451a8d --- /dev/null +++ b/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_library(lldbPluginCPlusPlusLanguage + CPlusPlusLanguage.cpp + CxxStringTypes.cpp + LibCxx.cpp + LibCxxInitializerList.cpp + LibCxxList.cpp + LibCxxMap.cpp + LibCxxUnorderedMap.cpp + LibCxxVector.cpp + LibStdcpp.cpp +) diff --git a/source/Plugins/Language/CPlusPlus/Makefile b/source/Plugins/Language/CPlusPlus/Makefile new file mode 100644 index 000000000000..2cb0dcf638b3 --- /dev/null +++ b/source/Plugins/Language/CPlusPlus/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Language/CPlusPlus -------------------*- 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 := lldbPluginCPlusPlusLanguage +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Language/Go/CMakeLists.txt b/source/Plugins/Language/Go/CMakeLists.txt new file mode 100644 index 000000000000..f3a9c12b7523 --- /dev/null +++ b/source/Plugins/Language/Go/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lldb_library(lldbPluginGoLanguage + GoLanguage.cpp + GoFormatterFunctions.cpp +) diff --git a/source/Plugins/Language/Go/Makefile b/source/Plugins/Language/Go/Makefile new file mode 100644 index 000000000000..3ea09f6c538f --- /dev/null +++ b/source/Plugins/Language/Go/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Language/Go -------------------*- 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 := lldbPluginGoLanguage +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Language/ObjC/CMakeLists.txt b/source/Plugins/Language/ObjC/CMakeLists.txt new file mode 100644 index 000000000000..5c480a1aed11 --- /dev/null +++ b/source/Plugins/Language/ObjC/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginObjCLanguage + ObjCLanguage.cpp + CF.cpp + Cocoa.cpp + CoreMedia.cpp + NSArray.cpp + NSDictionary.cpp + NSError.cpp + NSException.cpp + NSIndexPath.cpp + NSSet.cpp + NSString.cpp +) diff --git a/source/Plugins/Language/ObjC/Makefile b/source/Plugins/Language/ObjC/Makefile new file mode 100644 index 000000000000..58c9e58f2bc6 --- /dev/null +++ b/source/Plugins/Language/ObjC/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Language/ObjC ------------------------*- 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 := lldbPluginObjCLanguage +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Language/ObjCPlusPlus/CMakeLists.txt b/source/Plugins/Language/ObjCPlusPlus/CMakeLists.txt new file mode 100644 index 000000000000..ef80af74107b --- /dev/null +++ b/source/Plugins/Language/ObjCPlusPlus/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginObjCPlusPlusLanguage + ObjCPlusPlusLanguage.cpp +) diff --git a/source/Plugins/Language/ObjCPlusPlus/Makefile b/source/Plugins/Language/ObjCPlusPlus/Makefile new file mode 100644 index 000000000000..74e1a14bcf4d --- /dev/null +++ b/source/Plugins/Language/ObjCPlusPlus/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Language/ObjCPlusPlus ----------------*- 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 := lldbPluginObjCPlusPlusLanguage +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/LanguageRuntime/CMakeLists.txt b/source/Plugins/LanguageRuntime/CMakeLists.txt new file mode 100644 index 000000000000..66b17a4af229 --- /dev/null +++ b/source/Plugins/LanguageRuntime/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(CPlusPlus) +add_subdirectory(ObjC) +add_subdirectory(Go) +add_subdirectory(RenderScript) diff --git a/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt b/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt new file mode 100644 index 000000000000..26c68c60b019 --- /dev/null +++ b/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(ItaniumABI) +#add_subdirectory(MicrosoftABI) diff --git a/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/CMakeLists.txt b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/CMakeLists.txt new file mode 100644 index 000000000000..d25215d0a613 --- /dev/null +++ b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginCXXItaniumABI + ItaniumABILanguageRuntime.cpp + ) diff --git a/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/Makefile b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/Makefile new file mode 100644 index 000000000000..ac87437f9d2a --- /dev/null +++ b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/LangRuntime/C++/ItaniumABI/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 := lldbPluginCXXItaniumABI +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/LanguageRuntime/Go/CMakeLists.txt b/source/Plugins/LanguageRuntime/Go/CMakeLists.txt new file mode 100644 index 000000000000..7c9166a94cbe --- /dev/null +++ b/source/Plugins/LanguageRuntime/Go/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_NO_RTTI 1) + +add_lldb_library(lldbPluginLanguageRuntimeGo + GoLanguageRuntime.cpp + ) diff --git a/source/Plugins/LanguageRuntime/Go/Makefile b/source/Plugins/LanguageRuntime/Go/Makefile new file mode 100644 index 000000000000..1c8114e421c1 --- /dev/null +++ b/source/Plugins/LanguageRuntime/Go/Makefile @@ -0,0 +1,14 @@ +##===- Source/Plugins/LangRuntime/Go/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 := lldbPluginLanguageRuntimeGo +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/CMakeLists.txt b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/CMakeLists.txt new file mode 100644 index 000000000000..13fde3d181fa --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/CMakeLists.txt @@ -0,0 +1,10 @@ +add_lldb_library(lldbPluginAppleObjCRuntime + AppleObjCRuntime.cpp + AppleObjCRuntimeV1.cpp + AppleObjCRuntimeV2.cpp + AppleObjCTrampolineHandler.cpp + AppleObjCDeclVendor.cpp + AppleThreadPlanStepThroughObjCTrampoline.cpp + AppleObjCClassDescriptorV2.cpp + AppleObjCTypeEncodingParser.cpp + ) diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/Makefile b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/Makefile new file mode 100644 index 000000000000..485fe75b3f88 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/LangRuntime/ObjC/AppleRT/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 := lldbPluginAppleObjCRuntime +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt b/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt new file mode 100644 index 000000000000..af13dc6a144d --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(AppleObjCRuntime) diff --git a/source/Plugins/LanguageRuntime/RenderScript/CMakeLists.txt b/source/Plugins/LanguageRuntime/RenderScript/CMakeLists.txt new file mode 100644 index 000000000000..d944d76c4957 --- /dev/null +++ b/source/Plugins/LanguageRuntime/RenderScript/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(RenderScriptRuntime) diff --git a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/CMakeLists.txt b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/CMakeLists.txt new file mode 100644 index 000000000000..d93e9fa2a291 --- /dev/null +++ b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginRenderScriptRuntime + RenderScriptRuntime.cpp + ) diff --git a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/Makefile b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/Makefile new file mode 100644 index 000000000000..eeb50ae3fcae --- /dev/null +++ b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/Makefile @@ -0,0 +1,14 @@ +##===- Source/Plugins/LangRuntime/RenderScript/RenderScriptRuntime/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 := lldbPluginRenderScriptRuntime +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp index 149244df30c2..5d82ded50882 100644 --- a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp +++ b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp @@ -452,7 +452,7 @@ RenderScriptRuntime::GetPluginNameStatic() return g_name; } -RenderScriptRuntime::ModuleKind +RenderScriptRuntime::ModuleKind RenderScriptRuntime::GetModuleKind(const lldb::ModuleSP &module_sp) { if (module_sp) @@ -493,7 +493,7 @@ RenderScriptRuntime::IsRenderScriptModule(const lldb::ModuleSP &module_sp) return GetModuleKind(module_sp) != eModuleKindIgnored; } -void +void RenderScriptRuntime::ModulesDidLoad(const ModuleList &module_list ) { Mutex::Locker locker (module_list.GetMutex ()); @@ -640,11 +640,11 @@ RenderScriptRuntime::HookCallback(void *baton, StoppointCallbackContext *ctx, ll RenderScriptRuntime *lang_rt = (RenderScriptRuntime *)context.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); lang_rt->HookCallback(hook_info, context); - + return false; } -void +void RenderScriptRuntime::HookCallback(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); @@ -652,7 +652,7 @@ RenderScriptRuntime::HookCallback(RuntimeHook* hook_info, ExecutionContext& cont if (log) log->Printf ("RenderScriptRuntime::HookCallback - '%s' .", hook_info->defn->name); - if (hook_info->defn->grabber) + if (hook_info->defn->grabber) { (this->*(hook_info->defn->grabber))(hook_info, context); } @@ -706,7 +706,6 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 *data = result; success = true; } - break; } case llvm::Triple::ArchType::x86_64: @@ -741,6 +740,7 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 case llvm::Triple::ArchType::arm: { // arm 32 bit + // first 4 arguments are passed via registers if (arg < 4) { const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg); @@ -760,18 +760,19 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 { uint64_t sp = reg_ctx->GetSP(); uint32_t offset = (arg-4) * sizeof(uint32_t); - process->ReadMemory(sp + offset, &data, sizeof(uint32_t), error); - if (error.Fail()) + uint32_t value = 0; + size_t bytes_read = process->ReadMemory(sp + offset, &value, sizeof(value), error); + if (error.Fail() || bytes_read != sizeof(value)) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - error reading ARM stack: %s.", error.AsCString()); } else { + *data = value; success = true; } } - break; } case llvm::Triple::ArchType::aarch64: @@ -803,8 +804,8 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 } case llvm::Triple::ArchType::mipsel: { - // read from the registers + // first 4 arguments are passed in registers if (arg < 4){ const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg + 4); RegisterValue rVal; @@ -818,26 +819,25 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 if (log) log->Printf("RenderScriptRuntime::GetArgSimple() - Mips - Error while reading the argument #%d", arg); } - } - - // read from the stack + // arguments > 4 are read from the stack else { uint64_t sp = reg_ctx->GetSP(); uint32_t offset = arg * sizeof(uint32_t); - process->ReadMemory(sp + offset, &data, sizeof(uint32_t), error); - if (error.Fail()) + uint32_t value = 0; + size_t bytes_read = process->ReadMemory(sp + offset, &value, sizeof(value), error); + if (error.Fail() || bytes_read != sizeof(value)) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - error reading Mips stack: %s.", error.AsCString()); } else { + *data = value; success = true; } } - break; } case llvm::Triple::ArchType::mips64el: @@ -858,24 +858,24 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 log->Printf("RenderScriptRuntime::GetArgSimple - Mips64 - Error reading the argument #%d", arg); } } - - // read from the stack + // arguments > 8 are read from the stack else { uint64_t sp = reg_ctx->GetSP(); uint32_t offset = (arg - 8) * sizeof(uint64_t); - process->ReadMemory(sp + offset, &data, sizeof(uint64_t), error); - if (error.Fail()) + uint64_t value = 0; + size_t bytes_read = process->ReadMemory(sp + offset, &value, sizeof(value), error); + if (error.Fail() || bytes_read != sizeof(value)) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - Mips64 - Error reading Mips64 stack: %s.", error.AsCString()); } else { + *data = value; success = true; } } - break; } default: @@ -883,7 +883,6 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 // invalid architecture if (log) log->Printf("RenderScriptRuntime::GetArgSimple - Architecture not supported"); - } } @@ -895,11 +894,11 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 return success; } -void +void RenderScriptRuntime::CaptureSetGlobalVar1(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); - + //Context, Script, int, data, length uint64_t rs_context_u64 = 0U; @@ -921,7 +920,7 @@ RenderScriptRuntime::CaptureSetGlobalVar1(RuntimeHook* hook_info, ExecutionConte log->Printf("RenderScriptRuntime::CaptureSetGlobalVar1 - Error while reading the function parameters"); return; } - + if (log) { log->Printf ("RenderScriptRuntime::CaptureSetGlobalVar1 - 0x%" PRIx64 ",0x%" PRIx64 " slot %" PRIu64 " = 0x%" PRIx64 ":%" PRIu64 "bytes.", @@ -934,18 +933,18 @@ RenderScriptRuntime::CaptureSetGlobalVar1(RuntimeHook* hook_info, ExecutionConte if (rs_id_u64 < rsm->m_globals.size()) { auto rsg = rsm->m_globals[rs_id_u64]; - log->Printf ("RenderScriptRuntime::CaptureSetGlobalVar1 - Setting of '%s' within '%s' inferred", rsg.m_name.AsCString(), + log->Printf ("RenderScriptRuntime::CaptureSetGlobalVar1 - Setting of '%s' within '%s' inferred", rsg.m_name.AsCString(), rsm->m_module->GetFileSpec().GetFilename().AsCString()); } } } } -void +void RenderScriptRuntime::CaptureAllocationInit1(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); - + //Context, Alloc, bool uint64_t rs_context_u64 = 0U; @@ -1009,7 +1008,7 @@ RenderScriptRuntime::CaptureAllocationDestroy(RuntimeHook* hook_info, ExecutionC log->Printf("RenderScriptRuntime::CaptureAllocationDestroy - Couldn't find destroyed allocation"); } -void +void RenderScriptRuntime::CaptureScriptInit1(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); @@ -1045,16 +1044,16 @@ RenderScriptRuntime::CaptureScriptInit1(RuntimeHook* hook_info, ExecutionContext { if (log) log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - error reading resname: %s.", error.AsCString()); - + } process->ReadCStringFromMemory((lldb::addr_t)rs_cachedirptr_u64, cachedir, error); if (error.Fail()) { if (log) - log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - error reading cachedir: %s.", error.AsCString()); + log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - error reading cachedir: %s.", error.AsCString()); } - + if (log) log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - 0x%" PRIx64 ",0x%" PRIx64 " => '%s' at '%s' .", rs_context_u64, rs_script_u64, resname.c_str(), cachedir.c_str()); @@ -1077,7 +1076,7 @@ RenderScriptRuntime::CaptureScriptInit1(RuntimeHook* hook_info, ExecutionContext if (log) log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - '%s' tagged with context 0x%" PRIx64 " and script 0x%" PRIx64 ".", strm.GetData(), rs_context_u64, rs_script_u64); - } + } else if (log) { log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - resource name invalid, Script not tagged"); @@ -1134,7 +1133,7 @@ RenderScriptRuntime::LoadRuntimeHooks(lldb::ModuleSP module, ModuleKind kind) if (addr == LLDB_INVALID_ADDRESS) { if (log) - log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Unable to resolve the address of hook function '%s' with symbol '%s'.", + log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Unable to resolve the address of hook function '%s' with symbol '%s'.", hook_defn->name, symbol_name); continue; } @@ -1152,7 +1151,7 @@ RenderScriptRuntime::LoadRuntimeHooks(lldb::ModuleSP module, ModuleKind kind) m_runtimeHooks[addr] = hook; if (log) { - log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Successfully hooked '%s' in '%s' version %" PRIu64 " at 0x%" PRIx64 ".", + log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Successfully hooked '%s' in '%s' version %" PRIu64 " at 0x%" PRIx64 ".", hook_defn->name, module->GetFileSpec().GetFilename().AsCString(), (uint64_t)hook_defn->version, (uint64_t)addr); } } @@ -2231,7 +2230,7 @@ RenderScriptRuntime::SaveAllocation(Stream &strm, const uint32_t alloc_id, const // Write allocation data to file num_bytes = static_cast<size_t>(*alloc->size.get()); if (log) - log->Printf("RenderScriptRuntime::SaveAllocation - Writing 0x%" PRIx64 " bytes from %p", (uint64_t) num_bytes, buffer.get()); + log->Printf("RenderScriptRuntime::SaveAllocation - Writing 0x%" PRIx64 " bytes from %p", (uint64_t) num_bytes, (void*) buffer.get()); err = file.Write(buffer.get(), num_bytes); if (!err.Success()) @@ -2299,7 +2298,7 @@ RenderScriptRuntime::LoadModule(const lldb::ModuleSP &module_sp) } case eModuleKindLibRS: { - if (!m_libRS) + if (!m_libRS) { m_libRS = module_sp; static ConstString gDbgPresentStr("gDebuggerPresent"); @@ -2334,7 +2333,7 @@ RenderScriptRuntime::LoadModule(const lldb::ModuleSP &module_sp) break; } if (module_loaded) - Update(); + Update(); return module_loaded; } return false; @@ -2408,7 +2407,7 @@ RSModuleDescriptor::ParseRSInfo() m_kernels.push_back(RSKernelDescriptor(this, name, slot)); } } - } + } else if (sscanf(line.c_str(), "pragmaCount: %u", &numDefns) == 1) { char name[MAXLINE]; @@ -2417,7 +2416,7 @@ RSModuleDescriptor::ParseRSInfo() { name[0] = '\0'; value[0] = '\0'; - if (sscanf(info_lines[++offset].c_str(), "%s - %s", &name[0], &value[0]) != 0 + if (sscanf(info_lines[++offset].c_str(), "%s - %s", &name[0], &value[0]) != 0 && (name[0] != '\0')) { m_pragmas[std::string(name)] = value; @@ -2466,7 +2465,7 @@ RenderScriptRuntime::Status(Stream &strm) const strm.Printf("CPU Reference Implementation discovered."); strm.EOL(); } - + if (m_runtimeHooks.size()) { strm.Printf("Runtime functions hooked:"); @@ -2476,7 +2475,7 @@ RenderScriptRuntime::Status(Stream &strm) const strm.Indent(b.second->defn->name); strm.EOL(); } - } + } else { strm.Printf("Runtime is not hooked."); @@ -2484,7 +2483,7 @@ RenderScriptRuntime::Status(Stream &strm) const } } -void +void RenderScriptRuntime::DumpContexts(Stream &strm) const { strm.Printf("Inferred RenderScript Contexts:"); @@ -2519,7 +2518,7 @@ RenderScriptRuntime::DumpContexts(Stream &strm) const strm.IndentLess(); } -void +void RenderScriptRuntime::DumpKernels(Stream &strm) const { strm.Printf("RenderScript Kernels:"); diff --git a/source/Plugins/Makefile b/source/Plugins/Makefile new file mode 100644 index 000000000000..931f459a26b7 --- /dev/null +++ b/source/Plugins/Makefile @@ -0,0 +1,67 @@ +##===- source/Plugins/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 := ../.. + +include $(LLDB_LEVEL)/../../Makefile.config + + +PARALLEL_DIRS := ABI/MacOSX-arm ABI/MacOSX-arm64 ABI/MacOSX-i386 ABI/SysV-i386 ABI/SysV-x86_64 \ + ABI/SysV-arm ABI/SysV-arm64 ABI/SysV-hexagon ABI/SysV-ppc ABI/SysV-ppc64 \ + ABI/SysV-mips ABI/SysV-mips64 Disassembler/llvm \ + ObjectContainer/BSD-Archive ObjectFile/ELF ObjectFile/PECOFF \ + ObjectContainer/Universal-Mach-O ObjectFile/Mach-O \ + ObjectFile/JIT SymbolFile/DWARF SymbolFile/Symtab Process/Utility \ + DynamicLoader/Static Platform Process/elf-core Process/gdb-remote \ + Instruction/ARM Instruction/ARM64 Instruction/MIPS Instruction/MIPS64 \ + UnwindAssembly/InstEmulation UnwindAssembly/x86 \ + LanguageRuntime/CPlusPlus/ItaniumABI \ + LanguageRuntime/ObjC/AppleObjCRuntime \ + LanguageRuntime/Go/ \ + LanguageRuntime/RenderScript/RenderScriptRuntime \ + Language/CPlusPlus \ + Language/Go \ + Language/ObjC \ + Language/ObjCPlusPlus \ + DynamicLoader/POSIX-DYLD \ + DynamicLoader/Hexagon-DYLD \ + DynamicLoader/MacOSX-DYLD \ + DynamicLoader/Windows-DYLD \ + JITLoader/GDB \ + ExpressionParser/Clang \ + ExpressionParser/Go \ + OperatingSystem/Go \ + OperatingSystem/Python \ + SystemRuntime/MacOSX \ + SymbolVendor/ELF \ + MemoryHistory/asan \ + InstrumentationRuntime/AddressSanitizer \ + ScriptInterpreter/Python ScriptInterpreter/None + +ifeq ($(HOST_OS),Darwin) +PARALLEL_DIRS += Process/MacOSX-Kernel +PARALLEL_DIRS += DynamicLoader/Darwin-Kernel +PARALLEL_DIRS += SymbolVendor/MacOSX +#PARALLEL_DIRS += Process/MacOSX-User +PARALLEL_DIRS += Process/mach-core +endif + +ifeq ($(HOST_OS),Linux) +PARALLEL_DIRS += Process/Linux Process/POSIX +endif + +ifneq (,$(filter $(HOST_OS), FreeBSD GNU/kFreeBSD)) +PARALLEL_DIRS += Process/FreeBSD Process/POSIX +endif + +ifeq ($(HOST_OS),NetBSD) +PARALLEL_DIRS += Process/POSIX +endif + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/MemoryHistory/CMakeLists.txt b/source/Plugins/MemoryHistory/CMakeLists.txt new file mode 100644 index 000000000000..113f06362578 --- /dev/null +++ b/source/Plugins/MemoryHistory/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(asan) diff --git a/source/Plugins/MemoryHistory/asan/CMakeLists.txt b/source/Plugins/MemoryHistory/asan/CMakeLists.txt new file mode 100644 index 000000000000..8bfe95e3680e --- /dev/null +++ b/source/Plugins/MemoryHistory/asan/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginMemoryHistoryASan + MemoryHistoryASan.cpp + ) diff --git a/source/Plugins/MemoryHistory/asan/Makefile b/source/Plugins/MemoryHistory/asan/Makefile new file mode 100644 index 000000000000..86de6aba3638 --- /dev/null +++ b/source/Plugins/MemoryHistory/asan/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/MemoryHistory/asan/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 := lldbPluginMemoryHistoryASan +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectContainer/BSD-Archive/CMakeLists.txt b/source/Plugins/ObjectContainer/BSD-Archive/CMakeLists.txt new file mode 100644 index 000000000000..68ebe885e3e9 --- /dev/null +++ b/source/Plugins/ObjectContainer/BSD-Archive/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginObjectContainerBSDArchive + ObjectContainerBSDArchive.cpp + ) diff --git a/source/Plugins/ObjectContainer/BSD-Archive/Makefile b/source/Plugins/ObjectContainer/BSD-Archive/Makefile new file mode 100644 index 000000000000..00c5911ea95f --- /dev/null +++ b/source/Plugins/ObjectContainer/BSD-Archive/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectContainer/BSD-Archive/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 := lldbPluginObjectContainerBSDArchive +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectContainer/CMakeLists.txt b/source/Plugins/ObjectContainer/CMakeLists.txt new file mode 100644 index 000000000000..5dcef91f07d2 --- /dev/null +++ b/source/Plugins/ObjectContainer/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(BSD-Archive) +add_subdirectory(Universal-Mach-O) diff --git a/source/Plugins/ObjectContainer/Universal-Mach-O/CMakeLists.txt b/source/Plugins/ObjectContainer/Universal-Mach-O/CMakeLists.txt new file mode 100644 index 000000000000..b4553868bf94 --- /dev/null +++ b/source/Plugins/ObjectContainer/Universal-Mach-O/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginObjectContainerMachOArchive + ObjectContainerUniversalMachO.cpp + ) diff --git a/source/Plugins/ObjectContainer/Universal-Mach-O/Makefile b/source/Plugins/ObjectContainer/Universal-Mach-O/Makefile new file mode 100644 index 000000000000..957753527d24 --- /dev/null +++ b/source/Plugins/ObjectContainer/Universal-Mach-O/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectContainer/Universal-Mach-O/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 := lldbPluginObjectContainerMachOArchive +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp b/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp new file mode 100644 index 000000000000..7497b987eba4 --- /dev/null +++ b/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp @@ -0,0 +1,310 @@ +//===-- ObjectContainerUniversalMachO.cpp -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectContainerUniversalMachO.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::MachO; + +void +ObjectContainerUniversalMachO::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + GetModuleSpecifications); +} + +void +ObjectContainerUniversalMachO::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +ObjectContainerUniversalMachO::GetPluginNameStatic() +{ + static ConstString g_name("mach-o"); + return g_name; +} + +const char * +ObjectContainerUniversalMachO::GetPluginDescriptionStatic() +{ + return "Universal mach-o object container reader."; +} + + +ObjectContainer * +ObjectContainerUniversalMachO::CreateInstance +( + const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length +) +{ + // We get data when we aren't trying to look for cached container information, + // so only try and look for an architecture slice if we get data + if (data_sp) + { + DataExtractor data; + data.SetData (data_sp, data_offset, length); + if (ObjectContainerUniversalMachO::MagicBytesMatch(data)) + { + std::unique_ptr<ObjectContainerUniversalMachO> container_ap(new ObjectContainerUniversalMachO (module_sp, data_sp, data_offset, file, file_offset, length)); + if (container_ap->ParseHeader()) + { + return container_ap.release(); + } + } + } + return NULL; +} + +bool +ObjectContainerUniversalMachO::MagicBytesMatch (const DataExtractor &data) +{ + lldb::offset_t offset = 0; + uint32_t magic = data.GetU32(&offset); + return magic == FAT_MAGIC || magic == FAT_CIGAM; +} + +ObjectContainerUniversalMachO::ObjectContainerUniversalMachO +( + const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length +) : + ObjectContainer (module_sp, file, file_offset, length, data_sp, data_offset), + m_header(), + m_fat_archs() +{ + memset(&m_header, 0, sizeof(m_header)); +} + + +ObjectContainerUniversalMachO::~ObjectContainerUniversalMachO() +{ +} + +bool +ObjectContainerUniversalMachO::ParseHeader () +{ + bool success = ParseHeader (m_data, m_header, m_fat_archs); + // We no longer need any data, we parsed all we needed to parse + // and cached it in m_header and m_fat_archs + m_data.Clear(); + return success; +} + +bool +ObjectContainerUniversalMachO::ParseHeader (lldb_private::DataExtractor &data, + llvm::MachO::fat_header &header, + std::vector<llvm::MachO::fat_arch> &fat_archs) +{ + bool success = false; + // Store the file offset for this universal file as we could have a universal .o file + // in a BSD archive, or be contained in another kind of object. + // Universal mach-o files always have their headers in big endian. + lldb::offset_t offset = 0; + data.SetByteOrder (eByteOrderBig); + header.magic = data.GetU32(&offset); + fat_archs.clear(); + + if (header.magic == FAT_MAGIC) + { + + data.SetAddressByteSize(4); + + header.nfat_arch = data.GetU32(&offset); + + // Now we should have enough data for all of the fat headers, so lets index + // them so we know how many architectures that this universal binary contains. + uint32_t arch_idx = 0; + for (arch_idx = 0; arch_idx < header.nfat_arch; ++arch_idx) + { + if (data.ValidOffsetForDataOfSize(offset, sizeof(fat_arch))) + { + fat_arch arch; + if (data.GetU32(&offset, &arch, sizeof(fat_arch)/sizeof(uint32_t))) + fat_archs.push_back(arch); + } + } + success = true; + } + else + { + memset(&header, 0, sizeof(header)); + } + return success; +} + +void +ObjectContainerUniversalMachO::Dump (Stream *s) const +{ + s->Printf("%p: ", static_cast<const void*>(this)); + s->Indent(); + const size_t num_archs = GetNumArchitectures(); + const size_t num_objects = GetNumObjects(); + s->Printf("ObjectContainerUniversalMachO, num_archs = %lu, num_objects = %lu", num_archs, num_objects); + uint32_t i; + ArchSpec arch; + s->IndentMore(); + for (i=0; i<num_archs; i++) + { + s->Indent(); + GetArchitectureAtIndex(i, arch); + s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName()); + } + for (i=0; i<num_objects; i++) + { + s->Indent(); + s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex (i)); + } + s->IndentLess(); + s->EOL(); +} + +size_t +ObjectContainerUniversalMachO::GetNumArchitectures () const +{ + return m_header.nfat_arch; +} + +bool +ObjectContainerUniversalMachO::GetArchitectureAtIndex (uint32_t idx, ArchSpec& arch) const +{ + if (idx < m_header.nfat_arch) + { + arch.SetArchitecture (eArchTypeMachO, m_fat_archs[idx].cputype, m_fat_archs[idx].cpusubtype); + return true; + } + return false; +} + +ObjectFileSP +ObjectContainerUniversalMachO::GetObjectFile (const FileSpec *file) +{ + uint32_t arch_idx = 0; + ArchSpec arch; + // If the module hasn't specified an architecture yet, set it to the default + // architecture: + ModuleSP module_sp (GetModule()); + if (module_sp) + { + if (!module_sp->GetArchitecture().IsValid()) + { + arch = Target::GetDefaultArchitecture (); + if (!arch.IsValid()) + arch.SetTriple (LLDB_ARCH_DEFAULT); + } + else + arch = module_sp->GetArchitecture(); + + ArchSpec curr_arch; + // First, try to find an exact match for the Arch of the Target. + for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) + { + if (GetArchitectureAtIndex (arch_idx, curr_arch) && arch.IsExactMatch(curr_arch)) + break; + } + + // Failing an exact match, try to find a compatible Arch of the Target. + if (arch_idx >= m_header.nfat_arch) + { + for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) + { + if (GetArchitectureAtIndex (arch_idx, curr_arch) && arch.IsCompatibleMatch(curr_arch)) + break; + } + } + + if (arch_idx < m_header.nfat_arch) + { + DataBufferSP data_sp; + lldb::offset_t data_offset = 0; + return ObjectFile::FindPlugin (module_sp, + file, + m_offset + m_fat_archs[arch_idx].offset, + m_fat_archs[arch_idx].size, + data_sp, + data_offset); + } + } + return ObjectFileSP(); +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ObjectContainerUniversalMachO::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectContainerUniversalMachO::GetPluginVersion() +{ + return 1; +} + + +size_t +ObjectContainerUniversalMachO::GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t file_size, + lldb_private::ModuleSpecList &specs) +{ + const size_t initial_count = specs.GetSize(); + + DataExtractor data; + data.SetData (data_sp, data_offset, data_sp->GetByteSize()); + + if (ObjectContainerUniversalMachO::MagicBytesMatch(data)) + { + llvm::MachO::fat_header header; + std::vector<llvm::MachO::fat_arch> fat_archs; + if (ParseHeader (data, header, fat_archs)) + { + for (const llvm::MachO::fat_arch &fat_arch : fat_archs) + { + const lldb::offset_t slice_file_offset = fat_arch.offset + file_offset; + if (fat_arch.offset < file_size && file_size > slice_file_offset) + { + ObjectFile::GetModuleSpecifications (file, + slice_file_offset, + file_size - slice_file_offset, + specs); + } + } + } + } + return specs.GetSize() - initial_count; +} + diff --git a/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h b/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h new file mode 100644 index 000000000000..162402e4b2ba --- /dev/null +++ b/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h @@ -0,0 +1,105 @@ +//===-- ObjectContainerUniversalMachO.h -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectContainerUniversalMachO_h_ +#define liblldb_ObjectContainerUniversalMachO_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Symbol/ObjectContainer.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Utility/SafeMachO.h" + +class ObjectContainerUniversalMachO : + public lldb_private::ObjectContainer +{ +public: + ObjectContainerUniversalMachO(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t offset, + lldb::offset_t length); + + ~ObjectContainerUniversalMachO() override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectContainer * + CreateInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t offset, + lldb::offset_t length); + + static size_t + GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool + MagicBytesMatch (const lldb_private::DataExtractor &data); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + bool + ParseHeader() override; + + void + Dump(lldb_private::Stream *s) const override; + + size_t + GetNumArchitectures() const override; + + bool + GetArchitectureAtIndex(uint32_t cpu_idx, lldb_private::ArchSpec& arch) const override; + + lldb::ObjectFileSP + GetObjectFile(const lldb_private::FileSpec *file) override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + +protected: + llvm::MachO::fat_header m_header; + std::vector<llvm::MachO::fat_arch> m_fat_archs; + + static bool + ParseHeader (lldb_private::DataExtractor &data, + llvm::MachO::fat_header &header, + std::vector<llvm::MachO::fat_arch> &fat_archs); +}; + +#endif // liblldb_ObjectContainerUniversalMachO_h_ diff --git a/source/Plugins/ObjectFile/CMakeLists.txt b/source/Plugins/ObjectFile/CMakeLists.txt new file mode 100644 index 000000000000..06aa01c4f004 --- /dev/null +++ b/source/Plugins/ObjectFile/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(ELF) +add_subdirectory(Mach-O) +add_subdirectory(PECOFF) +add_subdirectory(JIT)
\ No newline at end of file diff --git a/source/Plugins/ObjectFile/ELF/CMakeLists.txt b/source/Plugins/ObjectFile/ELF/CMakeLists.txt new file mode 100644 index 000000000000..69ec80c62bf0 --- /dev/null +++ b/source/Plugins/ObjectFile/ELF/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lldb_library(lldbPluginObjectFileELF + ELFHeader.cpp + ObjectFileELF.cpp + ) diff --git a/source/Plugins/ObjectFile/ELF/Makefile b/source/Plugins/ObjectFile/ELF/Makefile new file mode 100644 index 000000000000..470660bb7861 --- /dev/null +++ b/source/Plugins/ObjectFile/ELF/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectFile/ELF/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 := lldbPluginObjectFileELF +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index 44fe6615a361..1d63ced787e0 100644 --- a/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -119,7 +119,7 @@ public: /// /// @param type Either DT_REL or DT_RELA. Any other value is invalid. ELFRelocation(unsigned type); - + ~ELFRelocation(); bool @@ -156,7 +156,7 @@ private: }; ELFRelocation::ELFRelocation(unsigned type) -{ +{ if (type == DT_REL || type == SHT_REL) reloc = new ELFRel(); else if (type == DT_RELA || type == SHT_RELA) @@ -172,7 +172,7 @@ ELFRelocation::~ELFRelocation() if (reloc.is<ELFRel*>()) delete reloc.get<ELFRel*>(); else - delete reloc.get<ELFRela*>(); + delete reloc.get<ELFRela*>(); } bool @@ -315,7 +315,7 @@ kalimbaVariantFromElfFlags(const elf::elf_word e_flags) kal_arch_variant = llvm::Triple::KalimbaSubArch_v5; break; default: - break; + break; } return kal_arch_variant; } @@ -470,9 +470,9 @@ ObjectFileELF::CreateInstance (const lldb::ModuleSP &module_sp, ObjectFile* -ObjectFileELF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, - DataBufferSP& data_sp, - const lldb::ProcessSP &process_sp, +ObjectFileELF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT)) @@ -561,7 +561,7 @@ calc_crc32(uint32_t crc, const void *buf, size_t size) 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d - }; + }; const uint8_t *p = (const uint8_t *)buf; crc = crc ^ ~0U; @@ -826,12 +826,12 @@ ObjectFileELF::GetPluginVersion() // ObjectFile protocol //------------------------------------------------------------------ -ObjectFileELF::ObjectFileELF (const lldb::ModuleSP &module_sp, +ObjectFileELF::ObjectFileELF (const lldb::ModuleSP &module_sp, DataBufferSP& data_sp, lldb::offset_t data_offset, - const FileSpec* file, + const FileSpec* file, lldb::offset_t file_offset, - lldb::offset_t length) : + lldb::offset_t length) : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset), m_header(), m_uuid(), @@ -901,7 +901,7 @@ ObjectFileELF::SetLoadAddress (Target &target, if (header->p_type != PT_LOAD || header->p_offset != 0) continue; - + value = value - header->p_vaddr; found_offset = true; break; @@ -1176,7 +1176,7 @@ ObjectFileELF::GetImageInfoAddress(Target *target) } lldb_private::Address -ObjectFileELF::GetEntryPointAddress () +ObjectFileELF::GetEntryPointAddress () { if (m_entry_point_address.IsValid()) return m_entry_point_address; @@ -1187,7 +1187,7 @@ ObjectFileELF::GetEntryPointAddress () SectionList *section_list = GetSectionList(); addr_t offset = m_header.e_entry; - if (!section_list) + if (!section_list) m_entry_point_address.SetOffset(offset); else m_entry_point_address.ResolveAddressUsingFileSections(offset, section_list); @@ -1545,16 +1545,16 @@ ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, { switch (header.e_flags & llvm::ELF::EF_MIPS_ARCH_ASE) { - case llvm::ELF::EF_MIPS_MICROMIPS: - arch_spec.SetFlags (ArchSpec::eMIPSAse_micromips); + case llvm::ELF::EF_MIPS_MICROMIPS: + arch_spec.SetFlags (ArchSpec::eMIPSAse_micromips); break; - case llvm::ELF::EF_MIPS_ARCH_ASE_M16: - arch_spec.SetFlags (ArchSpec::eMIPSAse_mips16); + case llvm::ELF::EF_MIPS_ARCH_ASE_M16: + arch_spec.SetFlags (ArchSpec::eMIPSAse_mips16); break; - case llvm::ELF::EF_MIPS_ARCH_ASE_MDMX: - arch_spec.SetFlags (ArchSpec::eMIPSAse_mdmx); + case llvm::ELF::EF_MIPS_ARCH_ASE_MDMX: + arch_spec.SetFlags (ArchSpec::eMIPSAse_mdmx); break; - default: + default: break; } } @@ -1612,7 +1612,7 @@ ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, DataExtractor data; if (sheader.sh_type == SHT_MIPS_ABIFLAGS) { - + if (section_size && (data.SetData (object_data, sheader.sh_offset, section_size) == section_size)) { lldb::offset_t ase_offset = 12; // MIPS ABI Flags Version: 0 @@ -1621,12 +1621,12 @@ ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, } // Settings appropriate ArchSpec ABI Flags if (header.e_flags & llvm::ELF::EF_MIPS_ABI2) - { + { arch_flags |= lldb_private::ArchSpec::eMIPSABI_N32; } else if (header.e_flags & llvm::ELF::EF_MIPS_ABI_O32) { - arch_flags |= lldb_private::ArchSpec::eMIPSABI_O32; + arch_flags |= lldb_private::ArchSpec::eMIPSABI_O32; } arch_spec.SetFlags (arch_flags); } @@ -1700,7 +1700,7 @@ ObjectFileELF::GetProgramHeaderByIndex(lldb::user_id_t id) return NULL; } -DataExtractor +DataExtractor ObjectFileELF::GetSegmentDataByIndex(lldb::user_id_t id) { const elf::ELFProgramHeader *segment_header = GetProgramHeaderByIndex(id); @@ -1805,12 +1805,12 @@ ObjectFileELF::CreateSections(SectionList &unified_section_list) else if (name == g_sect_name_tdata) { sect_type = eSectionTypeData; - is_thread_specific = true; + is_thread_specific = true; } else if (name == g_sect_name_tbss) { - sect_type = eSectionTypeZeroFill; - is_thread_specific = true; + sect_type = eSectionTypeZeroFill; + is_thread_specific = true; } // .debug_abbrev – Abbreviations used in the .debug_info section // .debug_aranges – Lookup table for mapping addresses to compilation units @@ -1877,12 +1877,12 @@ ObjectFileELF::CreateSections(SectionList &unified_section_list) { // the kalimba toolchain assumes that ELF section names are free-form. It does // support linkscripts which (can) give rise to various arbitrarily named - // sections being "Code" or "Data". + // sections being "Code" or "Data". sect_type = kalimbaSectionType(m_header, header); } const uint32_t target_bytes_size = - (eSectionTypeData == sect_type || eSectionTypeZeroFill == sect_type) ? + (eSectionTypeData == sect_type || eSectionTypeZeroFill == sect_type) ? m_arch_spec.GetDataByteSize() : eSectionTypeCode == sect_type ? m_arch_spec.GetCodeByteSize() : 1; @@ -2022,7 +2022,7 @@ ObjectFileELF::ParseSymbols (Symtab *symtab, { if (symbol.Parse(symtab_data, &offset) == false) break; - + const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); // No need to add non-section symbols that have no names @@ -2325,7 +2325,7 @@ ObjectFileELF::ParseSymbolTable(Symtab *symbol_table, user_id_t start_id, lldb_p user_id_t symtab_id = symtab->GetID(); const ELFSectionHeaderInfo *symtab_hdr = GetSectionHeaderByIndex(symtab_id); - assert(symtab_hdr->sh_type == SHT_SYMTAB || + assert(symtab_hdr->sh_type == SHT_SYMTAB || symtab_hdr->sh_type == SHT_DYNSYM); // sh_link: section header index of associated string table. @@ -2601,16 +2601,16 @@ ObjectFileELF::ParseTrampolineSymbols(Symtab *symbol_table, if (!rel_type) return 0; - return ParsePLTRelocations (symbol_table, - start_id, + return ParsePLTRelocations (symbol_table, + start_id, rel_type, - &m_header, - rel_hdr, - plt_hdr, + &m_header, + rel_hdr, + plt_hdr, sym_hdr, - plt_section_sp, - rel_data, - symtab_data, + plt_section_sp, + rel_data, + symtab_data, strtab_data); } @@ -2797,24 +2797,24 @@ ObjectFileELF::GetSymtab() // Synthesize trampoline symbols to help navigate the PLT. addr_t addr = symbol->d_ptr; Section *reloc_section = section_list->FindSectionContainingFileAddress(addr).get(); - if (reloc_section) + if (reloc_section) { user_id_t reloc_id = reloc_section->GetID(); const ELFSectionHeaderInfo *reloc_header = GetSectionHeaderByIndex(reloc_id); assert(reloc_header); - + if (m_symtab_ap == nullptr) m_symtab_ap.reset(new Symtab(reloc_section->GetObjectFile())); ParseTrampolineSymbols (m_symtab_ap.get(), symbol_id, reloc_header, reloc_id); } } - + // If we still don't have any symtab then create an empty instance to avoid do the section // lookup next time. if (m_symtab_ap == nullptr) m_symtab_ap.reset(new Symtab(this)); - + m_symtab_ap->CalculateSymbolSizes(); } @@ -3274,7 +3274,7 @@ ObjectFileELF::CalculateStrata() { switch (m_header.e_type) { - case llvm::ELF::ET_NONE: + case llvm::ELF::ET_NONE: // 0 - No file type return eStrataUnknown; @@ -3285,21 +3285,21 @@ ObjectFileELF::CalculateStrata() case llvm::ELF::ET_EXEC: // 2 - Executable file // TODO: is there any way to detect that an executable is a kernel - // related executable by inspecting the program headers, section + // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUser; case llvm::ELF::ET_DYN: // 3 - Shared object file // TODO: is there any way to detect that an shared library is a kernel - // related executable by inspecting the program headers, section + // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUnknown; case ET_CORE: // 4 - Core file // TODO: is there any way to detect that an core file is a kernel - // related executable by inspecting the program headers, section + // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUnknown; diff --git a/source/Plugins/ObjectFile/JIT/CMakeLists.txt b/source/Plugins/ObjectFile/JIT/CMakeLists.txt new file mode 100644 index 000000000000..979724bac5ad --- /dev/null +++ b/source/Plugins/ObjectFile/JIT/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginObjectFileJIT + ObjectFileJIT.cpp + ) diff --git a/source/Plugins/ObjectFile/JIT/Makefile b/source/Plugins/ObjectFile/JIT/Makefile new file mode 100644 index 000000000000..2af3521777a1 --- /dev/null +++ b/source/Plugins/ObjectFile/JIT/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectFile/JIT/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 := lldbPluginObjectFileJIT +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectFile/Mach-O/CMakeLists.txt b/source/Plugins/ObjectFile/Mach-O/CMakeLists.txt new file mode 100644 index 000000000000..45d45860b9e5 --- /dev/null +++ b/source/Plugins/ObjectFile/Mach-O/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginObjectFileMachO + ObjectFileMachO.cpp + ) diff --git a/source/Plugins/ObjectFile/Mach-O/Makefile b/source/Plugins/ObjectFile/Mach-O/Makefile new file mode 100644 index 000000000000..2fab0238e411 --- /dev/null +++ b/source/Plugins/ObjectFile/Mach-O/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectFile/Mach-O/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 := lldbPluginObjectFileMachO +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp new file mode 100644 index 000000000000..9c1e17782508 --- /dev/null +++ b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -0,0 +1,6096 @@ +//===-- ObjectFileMachO.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/StringRef.h" + +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadList.h" +#include "Plugins/Process/Utility/RegisterContextDarwin_arm.h" +#include "Plugins/Process/Utility/RegisterContextDarwin_arm64.h" +#include "Plugins/Process/Utility/RegisterContextDarwin_i386.h" +#include "Plugins/Process/Utility/RegisterContextDarwin_x86_64.h" + +#include "lldb/Utility/SafeMachO.h" + +#include "ObjectFileMachO.h" + +#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) +// GetLLDBSharedCacheUUID() needs to call dlsym() +#include <dlfcn.h> +#endif + +#ifndef __APPLE__ +#include "Utility/UuidCompatibility.h" +#endif + +#define THUMB_ADDRESS_BIT_MASK 0xfffffffffffffffeull +using namespace lldb; +using namespace lldb_private; +using namespace llvm::MachO; + +// Some structure definitions needed for parsing the dyld shared cache files +// found on iOS devices. + +struct lldb_copy_dyld_cache_header_v1 +{ + char magic[16]; // e.g. "dyld_v0 i386", "dyld_v1 armv7", etc. + uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info + uint32_t mappingCount; // number of dyld_cache_mapping_info entries + uint32_t imagesOffset; + uint32_t imagesCount; + uint64_t dyldBaseAddress; + uint64_t codeSignatureOffset; + uint64_t codeSignatureSize; + uint64_t slideInfoOffset; + uint64_t slideInfoSize; + uint64_t localSymbolsOffset; + uint64_t localSymbolsSize; + uint8_t uuid[16]; // v1 and above, also recorded in dyld_all_image_infos v13 and later +}; + +struct lldb_copy_dyld_cache_mapping_info +{ + uint64_t address; + uint64_t size; + uint64_t fileOffset; + uint32_t maxProt; + uint32_t initProt; +}; + +struct lldb_copy_dyld_cache_local_symbols_info +{ + uint32_t nlistOffset; + uint32_t nlistCount; + uint32_t stringsOffset; + uint32_t stringsSize; + uint32_t entriesOffset; + uint32_t entriesCount; +}; +struct lldb_copy_dyld_cache_local_symbols_entry +{ + uint32_t dylibOffset; + uint32_t nlistStartIndex; + uint32_t nlistCount; +}; + + +class RegisterContextDarwin_x86_64_Mach : public RegisterContextDarwin_x86_64 +{ +public: + RegisterContextDarwin_x86_64_Mach (lldb_private::Thread &thread, const DataExtractor &data) : + RegisterContextDarwin_x86_64 (thread, 0) + { + SetRegisterDataFrom_LC_THREAD (data); + } + + void + InvalidateAllRegisters() override + { + // Do nothing... registers are always valid... + } + + void + SetRegisterDataFrom_LC_THREAD (const DataExtractor &data) + { + lldb::offset_t offset = 0; + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + bool done = false; + + while (!done) + { + int flavor = data.GetU32 (&offset); + if (flavor == 0) + done = true; + else + { + uint32_t i; + uint32_t count = data.GetU32 (&offset); + switch (flavor) + { + case GPRRegSet: + for (i=0; i<count; ++i) + (&gpr.rax)[i] = data.GetU64(&offset); + SetError (GPRRegSet, Read, 0); + done = true; + + break; + case FPURegSet: + // TODO: fill in FPU regs.... + //SetError (FPURegSet, Read, -1); + done = true; + + break; + case EXCRegSet: + exc.trapno = data.GetU32(&offset); + exc.err = data.GetU32(&offset); + exc.faultvaddr = data.GetU64(&offset); + SetError (EXCRegSet, Read, 0); + done = true; + break; + case 7: + case 8: + case 9: + // fancy flavors that encapsulate of the above + // flavors... + break; + + default: + done = true; + break; + } + } + } + } + + static size_t + WriteRegister (RegisterContext *reg_ctx, const char *name, const char *alt_name, size_t reg_byte_size, Stream &data) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name); + if (reg_info == NULL) + reg_info = reg_ctx->GetRegisterInfoByName(alt_name); + if (reg_info) + { + lldb_private::RegisterValue reg_value; + if (reg_ctx->ReadRegister(reg_info, reg_value)) + { + if (reg_info->byte_size >= reg_byte_size) + data.Write(reg_value.GetBytes(), reg_byte_size); + else + { + data.Write(reg_value.GetBytes(), reg_info->byte_size); + for (size_t i=0, n = reg_byte_size - reg_info->byte_size; i<n; ++ i) + data.PutChar(0); + } + return reg_byte_size; + } + } + // Just write zeros if all else fails + for (size_t i=0; i<reg_byte_size; ++ i) + data.PutChar(0); + return reg_byte_size; + } + + static bool + Create_LC_THREAD (Thread *thread, Stream &data) + { + RegisterContextSP reg_ctx_sp (thread->GetRegisterContext()); + if (reg_ctx_sp) + { + RegisterContext *reg_ctx = reg_ctx_sp.get(); + + data.PutHex32 (GPRRegSet); // Flavor + data.PutHex32 (GPRWordCount); + WriteRegister (reg_ctx, "rax", NULL, 8, data); + WriteRegister (reg_ctx, "rbx", NULL, 8, data); + WriteRegister (reg_ctx, "rcx", NULL, 8, data); + WriteRegister (reg_ctx, "rdx", NULL, 8, data); + WriteRegister (reg_ctx, "rdi", NULL, 8, data); + WriteRegister (reg_ctx, "rsi", NULL, 8, data); + WriteRegister (reg_ctx, "rbp", NULL, 8, data); + WriteRegister (reg_ctx, "rsp", NULL, 8, data); + WriteRegister (reg_ctx, "r8", NULL, 8, data); + WriteRegister (reg_ctx, "r9", NULL, 8, data); + WriteRegister (reg_ctx, "r10", NULL, 8, data); + WriteRegister (reg_ctx, "r11", NULL, 8, data); + WriteRegister (reg_ctx, "r12", NULL, 8, data); + WriteRegister (reg_ctx, "r13", NULL, 8, data); + WriteRegister (reg_ctx, "r14", NULL, 8, data); + WriteRegister (reg_ctx, "r15", NULL, 8, data); + WriteRegister (reg_ctx, "rip", NULL, 8, data); + WriteRegister (reg_ctx, "rflags", NULL, 8, data); + WriteRegister (reg_ctx, "cs", NULL, 8, data); + WriteRegister (reg_ctx, "fs", NULL, 8, data); + WriteRegister (reg_ctx, "gs", NULL, 8, data); + +// // Write out the FPU registers +// const size_t fpu_byte_size = sizeof(FPU); +// size_t bytes_written = 0; +// data.PutHex32 (FPURegSet); +// data.PutHex32 (fpu_byte_size/sizeof(uint64_t)); +// bytes_written += data.PutHex32(0); // uint32_t pad[0] +// bytes_written += data.PutHex32(0); // uint32_t pad[1] +// bytes_written += WriteRegister (reg_ctx, "fcw", "fctrl", 2, data); // uint16_t fcw; // "fctrl" +// bytes_written += WriteRegister (reg_ctx, "fsw" , "fstat", 2, data); // uint16_t fsw; // "fstat" +// bytes_written += WriteRegister (reg_ctx, "ftw" , "ftag", 1, data); // uint8_t ftw; // "ftag" +// bytes_written += data.PutHex8 (0); // uint8_t pad1; +// bytes_written += WriteRegister (reg_ctx, "fop" , NULL, 2, data); // uint16_t fop; // "fop" +// bytes_written += WriteRegister (reg_ctx, "fioff", "ip", 4, data); // uint32_t ip; // "fioff" +// bytes_written += WriteRegister (reg_ctx, "fiseg", NULL, 2, data); // uint16_t cs; // "fiseg" +// bytes_written += data.PutHex16 (0); // uint16_t pad2; +// bytes_written += WriteRegister (reg_ctx, "dp", "fooff" , 4, data); // uint32_t dp; // "fooff" +// bytes_written += WriteRegister (reg_ctx, "foseg", NULL, 2, data); // uint16_t ds; // "foseg" +// bytes_written += data.PutHex16 (0); // uint16_t pad3; +// bytes_written += WriteRegister (reg_ctx, "mxcsr", NULL, 4, data); // uint32_t mxcsr; +// bytes_written += WriteRegister (reg_ctx, "mxcsrmask", NULL, 4, data);// uint32_t mxcsrmask; +// bytes_written += WriteRegister (reg_ctx, "stmm0", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm1", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm2", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm3", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm4", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm5", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm6", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm7", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm0" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm1" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm2" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm3" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm4" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm5" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm6" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm7" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm8" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm9" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm10", NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm11", NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm12", NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm13", NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm14", NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm15", NULL, sizeof(XMMReg), data); +// +// // Fill rest with zeros +// for (size_t i=0, n = fpu_byte_size - bytes_written; i<n; ++ i) +// data.PutChar(0); + + // Write out the EXC registers + data.PutHex32 (EXCRegSet); + data.PutHex32 (EXCWordCount); + WriteRegister (reg_ctx, "trapno", NULL, 4, data); + WriteRegister (reg_ctx, "err", NULL, 4, data); + WriteRegister (reg_ctx, "faultvaddr", NULL, 8, data); + return true; + } + return false; + } + +protected: + int + DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override + { + return 0; + } + + int + DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override + { + return 0; + } + + int + DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override + { + return 0; + } + + int + DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override + { + return 0; + } + + int + DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override + { + return 0; + } + + int + DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override + { + return 0; + } +}; + +class RegisterContextDarwin_i386_Mach : public RegisterContextDarwin_i386 +{ +public: + RegisterContextDarwin_i386_Mach (lldb_private::Thread &thread, const DataExtractor &data) : + RegisterContextDarwin_i386 (thread, 0) + { + SetRegisterDataFrom_LC_THREAD (data); + } + + void + InvalidateAllRegisters() override + { + // Do nothing... registers are always valid... + } + + void + SetRegisterDataFrom_LC_THREAD (const DataExtractor &data) + { + lldb::offset_t offset = 0; + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + bool done = false; + + while (!done) + { + int flavor = data.GetU32 (&offset); + if (flavor == 0) + done = true; + else + { + uint32_t i; + uint32_t count = data.GetU32 (&offset); + switch (flavor) + { + case GPRRegSet: + for (i=0; i<count; ++i) + (&gpr.eax)[i] = data.GetU32(&offset); + SetError (GPRRegSet, Read, 0); + done = true; + + break; + case FPURegSet: + // TODO: fill in FPU regs.... + //SetError (FPURegSet, Read, -1); + done = true; + + break; + case EXCRegSet: + exc.trapno = data.GetU32(&offset); + exc.err = data.GetU32(&offset); + exc.faultvaddr = data.GetU32(&offset); + SetError (EXCRegSet, Read, 0); + done = true; + break; + case 7: + case 8: + case 9: + // fancy flavors that encapsulate of the above + // flavors... + break; + + default: + done = true; + break; + } + } + } + } + + static size_t + WriteRegister (RegisterContext *reg_ctx, const char *name, const char *alt_name, size_t reg_byte_size, Stream &data) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name); + if (reg_info == NULL) + reg_info = reg_ctx->GetRegisterInfoByName(alt_name); + if (reg_info) + { + lldb_private::RegisterValue reg_value; + if (reg_ctx->ReadRegister(reg_info, reg_value)) + { + if (reg_info->byte_size >= reg_byte_size) + data.Write(reg_value.GetBytes(), reg_byte_size); + else + { + data.Write(reg_value.GetBytes(), reg_info->byte_size); + for (size_t i=0, n = reg_byte_size - reg_info->byte_size; i<n; ++ i) + data.PutChar(0); + } + return reg_byte_size; + } + } + // Just write zeros if all else fails + for (size_t i=0; i<reg_byte_size; ++ i) + data.PutChar(0); + return reg_byte_size; + } + + static bool + Create_LC_THREAD (Thread *thread, Stream &data) + { + RegisterContextSP reg_ctx_sp (thread->GetRegisterContext()); + if (reg_ctx_sp) + { + RegisterContext *reg_ctx = reg_ctx_sp.get(); + + data.PutHex32 (GPRRegSet); // Flavor + data.PutHex32 (GPRWordCount); + WriteRegister (reg_ctx, "eax", NULL, 4, data); + WriteRegister (reg_ctx, "ebx", NULL, 4, data); + WriteRegister (reg_ctx, "ecx", NULL, 4, data); + WriteRegister (reg_ctx, "edx", NULL, 4, data); + WriteRegister (reg_ctx, "edi", NULL, 4, data); + WriteRegister (reg_ctx, "esi", NULL, 4, data); + WriteRegister (reg_ctx, "ebp", NULL, 4, data); + WriteRegister (reg_ctx, "esp", NULL, 4, data); + WriteRegister (reg_ctx, "ss", NULL, 4, data); + WriteRegister (reg_ctx, "eflags", NULL, 4, data); + WriteRegister (reg_ctx, "eip", NULL, 4, data); + WriteRegister (reg_ctx, "cs", NULL, 4, data); + WriteRegister (reg_ctx, "ds", NULL, 4, data); + WriteRegister (reg_ctx, "es", NULL, 4, data); + WriteRegister (reg_ctx, "fs", NULL, 4, data); + WriteRegister (reg_ctx, "gs", NULL, 4, data); + + // Write out the EXC registers + data.PutHex32 (EXCRegSet); + data.PutHex32 (EXCWordCount); + WriteRegister (reg_ctx, "trapno", NULL, 4, data); + WriteRegister (reg_ctx, "err", NULL, 4, data); + WriteRegister (reg_ctx, "faultvaddr", NULL, 4, data); + return true; + } + return false; + } + +protected: + int + DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override + { + return 0; + } + + int + DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override + { + return 0; + } + + int + DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override + { + return 0; + } + + int + DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override + { + return 0; + } + + int + DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override + { + return 0; + } + + int + DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override + { + return 0; + } +}; + +class RegisterContextDarwin_arm_Mach : public RegisterContextDarwin_arm +{ +public: + RegisterContextDarwin_arm_Mach (lldb_private::Thread &thread, const DataExtractor &data) : + RegisterContextDarwin_arm (thread, 0) + { + SetRegisterDataFrom_LC_THREAD (data); + } + + void + InvalidateAllRegisters() override + { + // Do nothing... registers are always valid... + } + + void + SetRegisterDataFrom_LC_THREAD (const DataExtractor &data) + { + lldb::offset_t offset = 0; + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + bool done = false; + + while (!done) + { + int flavor = data.GetU32 (&offset); + uint32_t count = data.GetU32 (&offset); + lldb::offset_t next_thread_state = offset + (count * 4); + switch (flavor) + { + case GPRRegSet: + for (uint32_t i=0; i<count; ++i) + { + gpr.r[i] = data.GetU32(&offset); + } + + // Note that gpr.cpsr is also copied by the above loop; this loop technically extends + // one element past the end of the gpr.r[] array. + + SetError (GPRRegSet, Read, 0); + offset = next_thread_state; + break; + + case FPURegSet: + { + uint8_t *fpu_reg_buf = (uint8_t*) &fpu.floats.s[0]; + const int fpu_reg_buf_size = sizeof (fpu.floats); + if (data.ExtractBytes (offset, fpu_reg_buf_size, eByteOrderLittle, fpu_reg_buf) == fpu_reg_buf_size) + { + offset += fpu_reg_buf_size; + fpu.fpscr = data.GetU32(&offset); + SetError (FPURegSet, Read, 0); + } + else + { + done = true; + } + } + offset = next_thread_state; + break; + + case EXCRegSet: + if (count == 3) + { + exc.exception = data.GetU32(&offset); + exc.fsr = data.GetU32(&offset); + exc.far = data.GetU32(&offset); + SetError (EXCRegSet, Read, 0); + } + done = true; + offset = next_thread_state; + break; + + // Unknown register set flavor, stop trying to parse. + default: + done = true; + } + } + } + + static size_t + WriteRegister (RegisterContext *reg_ctx, const char *name, const char *alt_name, size_t reg_byte_size, Stream &data) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name); + if (reg_info == NULL) + reg_info = reg_ctx->GetRegisterInfoByName(alt_name); + if (reg_info) + { + lldb_private::RegisterValue reg_value; + if (reg_ctx->ReadRegister(reg_info, reg_value)) + { + if (reg_info->byte_size >= reg_byte_size) + data.Write(reg_value.GetBytes(), reg_byte_size); + else + { + data.Write(reg_value.GetBytes(), reg_info->byte_size); + for (size_t i=0, n = reg_byte_size - reg_info->byte_size; i<n; ++ i) + data.PutChar(0); + } + return reg_byte_size; + } + } + // Just write zeros if all else fails + for (size_t i=0; i<reg_byte_size; ++ i) + data.PutChar(0); + return reg_byte_size; + } + + static bool + Create_LC_THREAD (Thread *thread, Stream &data) + { + RegisterContextSP reg_ctx_sp (thread->GetRegisterContext()); + if (reg_ctx_sp) + { + RegisterContext *reg_ctx = reg_ctx_sp.get(); + + data.PutHex32 (GPRRegSet); // Flavor + data.PutHex32 (GPRWordCount); + WriteRegister (reg_ctx, "r0", NULL, 4, data); + WriteRegister (reg_ctx, "r1", NULL, 4, data); + WriteRegister (reg_ctx, "r2", NULL, 4, data); + WriteRegister (reg_ctx, "r3", NULL, 4, data); + WriteRegister (reg_ctx, "r4", NULL, 4, data); + WriteRegister (reg_ctx, "r5", NULL, 4, data); + WriteRegister (reg_ctx, "r6", NULL, 4, data); + WriteRegister (reg_ctx, "r7", NULL, 4, data); + WriteRegister (reg_ctx, "r8", NULL, 4, data); + WriteRegister (reg_ctx, "r9", NULL, 4, data); + WriteRegister (reg_ctx, "r10", NULL, 4, data); + WriteRegister (reg_ctx, "r11", NULL, 4, data); + WriteRegister (reg_ctx, "r12", NULL, 4, data); + WriteRegister (reg_ctx, "sp", NULL, 4, data); + WriteRegister (reg_ctx, "lr", NULL, 4, data); + WriteRegister (reg_ctx, "pc", NULL, 4, data); + WriteRegister (reg_ctx, "cpsr", NULL, 4, data); + + // Write out the EXC registers +// data.PutHex32 (EXCRegSet); +// data.PutHex32 (EXCWordCount); +// WriteRegister (reg_ctx, "exception", NULL, 4, data); +// WriteRegister (reg_ctx, "fsr", NULL, 4, data); +// WriteRegister (reg_ctx, "far", NULL, 4, data); + return true; + } + return false; + } + +protected: + int + DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override + { + return -1; + } + + int + DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override + { + return -1; + } + + int + DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override + { + return -1; + } + + int + DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) override + { + return -1; + } + + int + DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override + { + return 0; + } + + int + DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override + { + return 0; + } + + int + DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override + { + return 0; + } + + int + DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) override + { + return -1; + } +}; + +class RegisterContextDarwin_arm64_Mach : public RegisterContextDarwin_arm64 +{ +public: + RegisterContextDarwin_arm64_Mach (lldb_private::Thread &thread, const DataExtractor &data) : + RegisterContextDarwin_arm64 (thread, 0) + { + SetRegisterDataFrom_LC_THREAD (data); + } + + void + InvalidateAllRegisters() override + { + // Do nothing... registers are always valid... + } + + void + SetRegisterDataFrom_LC_THREAD (const DataExtractor &data) + { + lldb::offset_t offset = 0; + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + bool done = false; + while (!done) + { + int flavor = data.GetU32 (&offset); + uint32_t count = data.GetU32 (&offset); + lldb::offset_t next_thread_state = offset + (count * 4); + switch (flavor) + { + case GPRRegSet: + // x0-x29 + fp + lr + sp + pc (== 33 64-bit registers) plus cpsr (1 32-bit register) + if (count >= (33 * 2) + 1) + { + for (uint32_t i=0; i<33; ++i) + gpr.x[i] = data.GetU64(&offset); + gpr.cpsr = data.GetU32(&offset); + SetError (GPRRegSet, Read, 0); + } + offset = next_thread_state; + break; + case FPURegSet: + { + uint8_t *fpu_reg_buf = (uint8_t*) &fpu.v[0]; + const int fpu_reg_buf_size = sizeof (fpu); + if (fpu_reg_buf_size == count + && data.ExtractBytes (offset, fpu_reg_buf_size, eByteOrderLittle, fpu_reg_buf) == fpu_reg_buf_size) + { + SetError (FPURegSet, Read, 0); + } + else + { + done = true; + } + } + offset = next_thread_state; + break; + case EXCRegSet: + if (count == 4) + { + exc.far = data.GetU64(&offset); + exc.esr = data.GetU32(&offset); + exc.exception = data.GetU32(&offset); + SetError (EXCRegSet, Read, 0); + } + offset = next_thread_state; + break; + default: + done = true; + break; + } + } + } + + static size_t + WriteRegister (RegisterContext *reg_ctx, const char *name, const char *alt_name, size_t reg_byte_size, Stream &data) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name); + if (reg_info == NULL) + reg_info = reg_ctx->GetRegisterInfoByName(alt_name); + if (reg_info) + { + lldb_private::RegisterValue reg_value; + if (reg_ctx->ReadRegister(reg_info, reg_value)) + { + if (reg_info->byte_size >= reg_byte_size) + data.Write(reg_value.GetBytes(), reg_byte_size); + else + { + data.Write(reg_value.GetBytes(), reg_info->byte_size); + for (size_t i=0, n = reg_byte_size - reg_info->byte_size; i<n; ++ i) + data.PutChar(0); + } + return reg_byte_size; + } + } + // Just write zeros if all else fails + for (size_t i=0; i<reg_byte_size; ++ i) + data.PutChar(0); + return reg_byte_size; + } + + static bool + Create_LC_THREAD (Thread *thread, Stream &data) + { + RegisterContextSP reg_ctx_sp (thread->GetRegisterContext()); + if (reg_ctx_sp) + { + RegisterContext *reg_ctx = reg_ctx_sp.get(); + + data.PutHex32 (GPRRegSet); // Flavor + data.PutHex32 (GPRWordCount); + WriteRegister (reg_ctx, "x0", NULL, 8, data); + WriteRegister (reg_ctx, "x1", NULL, 8, data); + WriteRegister (reg_ctx, "x2", NULL, 8, data); + WriteRegister (reg_ctx, "x3", NULL, 8, data); + WriteRegister (reg_ctx, "x4", NULL, 8, data); + WriteRegister (reg_ctx, "x5", NULL, 8, data); + WriteRegister (reg_ctx, "x6", NULL, 8, data); + WriteRegister (reg_ctx, "x7", NULL, 8, data); + WriteRegister (reg_ctx, "x8", NULL, 8, data); + WriteRegister (reg_ctx, "x9", NULL, 8, data); + WriteRegister (reg_ctx, "x10", NULL, 8, data); + WriteRegister (reg_ctx, "x11", NULL, 8, data); + WriteRegister (reg_ctx, "x12", NULL, 8, data); + WriteRegister (reg_ctx, "x13", NULL, 8, data); + WriteRegister (reg_ctx, "x14", NULL, 8, data); + WriteRegister (reg_ctx, "x15", NULL, 8, data); + WriteRegister (reg_ctx, "x16", NULL, 8, data); + WriteRegister (reg_ctx, "x17", NULL, 8, data); + WriteRegister (reg_ctx, "x18", NULL, 8, data); + WriteRegister (reg_ctx, "x19", NULL, 8, data); + WriteRegister (reg_ctx, "x20", NULL, 8, data); + WriteRegister (reg_ctx, "x21", NULL, 8, data); + WriteRegister (reg_ctx, "x22", NULL, 8, data); + WriteRegister (reg_ctx, "x23", NULL, 8, data); + WriteRegister (reg_ctx, "x24", NULL, 8, data); + WriteRegister (reg_ctx, "x25", NULL, 8, data); + WriteRegister (reg_ctx, "x26", NULL, 8, data); + WriteRegister (reg_ctx, "x27", NULL, 8, data); + WriteRegister (reg_ctx, "x28", NULL, 8, data); + WriteRegister (reg_ctx, "fp", NULL, 8, data); + WriteRegister (reg_ctx, "lr", NULL, 8, data); + WriteRegister (reg_ctx, "sp", NULL, 8, data); + WriteRegister (reg_ctx, "pc", NULL, 8, data); + WriteRegister (reg_ctx, "cpsr", NULL, 4, data); + + // Write out the EXC registers +// data.PutHex32 (EXCRegSet); +// data.PutHex32 (EXCWordCount); +// WriteRegister (reg_ctx, "far", NULL, 8, data); +// WriteRegister (reg_ctx, "esr", NULL, 4, data); +// WriteRegister (reg_ctx, "exception", NULL, 4, data); + return true; + } + return false; + } + +protected: + int + DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override + { + return -1; + } + + int + DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override + { + return -1; + } + + int + DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override + { + return -1; + } + + int + DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) override + { + return -1; + } + + int + DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override + { + return 0; + } + + int + DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override + { + return 0; + } + + int + DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override + { + return 0; + } + + int + DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) override + { + return -1; + } +}; + +static uint32_t +MachHeaderSizeFromMagic(uint32_t magic) +{ + switch (magic) + { + case MH_MAGIC: + case MH_CIGAM: + return sizeof(struct mach_header); + + case MH_MAGIC_64: + case MH_CIGAM_64: + return sizeof(struct mach_header_64); + break; + + default: + break; + } + return 0; +} + +#define MACHO_NLIST_ARM_SYMBOL_IS_THUMB 0x0008 + +void +ObjectFileMachO::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + CreateMemoryInstance, + GetModuleSpecifications, + SaveCore); +} + +void +ObjectFileMachO::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +ObjectFileMachO::GetPluginNameStatic() +{ + static ConstString g_name("mach-o"); + return g_name; +} + +const char * +ObjectFileMachO::GetPluginDescriptionStatic() +{ + return "Mach-o object file reader (32 and 64 bit)"; +} + +ObjectFile * +ObjectFileMachO::CreateInstance (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length) +{ + if (!data_sp) + { + data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); + data_offset = 0; + } + + if (ObjectFileMachO::MagicBytesMatch(data_sp, data_offset, length)) + { + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) + { + data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); + data_offset = 0; + } + std::unique_ptr<ObjectFile> objfile_ap(new ObjectFileMachO (module_sp, data_sp, data_offset, file, file_offset, length)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + +ObjectFile * +ObjectFileMachO::CreateMemoryInstance (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + const ProcessSP &process_sp, + lldb::addr_t header_addr) +{ + if (ObjectFileMachO::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) + { + std::unique_ptr<ObjectFile> objfile_ap(new ObjectFileMachO (module_sp, data_sp, process_sp, header_addr)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + +size_t +ObjectFileMachO::GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs) +{ + const size_t initial_count = specs.GetSize(); + + if (ObjectFileMachO::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) + { + DataExtractor data; + data.SetData(data_sp); + llvm::MachO::mach_header header; + if (ParseHeader (data, &data_offset, header)) + { + size_t header_and_load_cmds = header.sizeofcmds + MachHeaderSizeFromMagic(header.magic); + if (header_and_load_cmds >= data_sp->GetByteSize()) + { + data_sp = file.ReadFileContents(file_offset, header_and_load_cmds); + data.SetData(data_sp); + data_offset = MachHeaderSizeFromMagic(header.magic); + } + if (data_sp) + { + ModuleSpec spec; + spec.GetFileSpec() = file; + spec.SetObjectOffset(file_offset); + spec.SetObjectSize(length); + + if (GetArchitecture (header, data, data_offset, spec.GetArchitecture())) + { + if (spec.GetArchitecture().IsValid()) + { + GetUUID (header, data, data_offset, spec.GetUUID()); + specs.Append(spec); + } + } + } + } + } + return specs.GetSize() - initial_count; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameTEXT() +{ + static ConstString g_segment_name_TEXT ("__TEXT"); + return g_segment_name_TEXT; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameDATA() +{ + static ConstString g_segment_name_DATA ("__DATA"); + return g_segment_name_DATA; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameDATA_DIRTY() +{ + static ConstString g_segment_name ("__DATA_DIRTY"); + return g_segment_name; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameDATA_CONST() +{ + static ConstString g_segment_name ("__DATA_CONST"); + return g_segment_name; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameOBJC() +{ + static ConstString g_segment_name_OBJC ("__OBJC"); + return g_segment_name_OBJC; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameLINKEDIT() +{ + static ConstString g_section_name_LINKEDIT ("__LINKEDIT"); + return g_section_name_LINKEDIT; +} + +const ConstString & +ObjectFileMachO::GetSectionNameEHFrame() +{ + static ConstString g_section_name_eh_frame ("__eh_frame"); + return g_section_name_eh_frame; +} + +bool +ObjectFileMachO::MagicBytesMatch (DataBufferSP& data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length) +{ + DataExtractor data; + data.SetData (data_sp, data_offset, data_length); + lldb::offset_t offset = 0; + uint32_t magic = data.GetU32(&offset); + return MachHeaderSizeFromMagic(magic) != 0; +} + +ObjectFileMachO::ObjectFileMachO(const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length) : + ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset), + m_mach_segments(), + m_mach_sections(), + m_entry_point_address(), + m_thread_context_offsets(), + m_thread_context_offsets_valid(false) +{ + ::memset (&m_header, 0, sizeof(m_header)); + ::memset (&m_dysymtab, 0, sizeof(m_dysymtab)); +} + +ObjectFileMachO::ObjectFileMachO (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& header_data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr) : + ObjectFile(module_sp, process_sp, header_addr, header_data_sp), + m_mach_segments(), + m_mach_sections(), + m_entry_point_address(), + m_thread_context_offsets(), + m_thread_context_offsets_valid(false) +{ + ::memset (&m_header, 0, sizeof(m_header)); + ::memset (&m_dysymtab, 0, sizeof(m_dysymtab)); +} + +bool +ObjectFileMachO::ParseHeader (DataExtractor &data, + lldb::offset_t *data_offset_ptr, + llvm::MachO::mach_header &header) +{ + data.SetByteOrder (endian::InlHostByteOrder()); + // Leave magic in the original byte order + header.magic = data.GetU32(data_offset_ptr); + bool can_parse = false; + bool is_64_bit = false; + switch (header.magic) + { + case MH_MAGIC: + data.SetByteOrder (endian::InlHostByteOrder()); + data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_MAGIC_64: + data.SetByteOrder (endian::InlHostByteOrder()); + data.SetAddressByteSize(8); + can_parse = true; + is_64_bit = true; + break; + + case MH_CIGAM: + data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_CIGAM_64: + data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + data.SetAddressByteSize(8); + is_64_bit = true; + can_parse = true; + break; + + default: + break; + } + + if (can_parse) + { + data.GetU32(data_offset_ptr, &header.cputype, 6); + if (is_64_bit) + *data_offset_ptr += 4; + return true; + } + else + { + memset(&header, 0, sizeof(header)); + } + return false; +} + +bool +ObjectFileMachO::ParseHeader () +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + bool can_parse = false; + lldb::offset_t offset = 0; + m_data.SetByteOrder (endian::InlHostByteOrder()); + // Leave magic in the original byte order + m_header.magic = m_data.GetU32(&offset); + switch (m_header.magic) + { + case MH_MAGIC: + m_data.SetByteOrder (endian::InlHostByteOrder()); + m_data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_MAGIC_64: + m_data.SetByteOrder (endian::InlHostByteOrder()); + m_data.SetAddressByteSize(8); + can_parse = true; + break; + + case MH_CIGAM: + m_data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + m_data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_CIGAM_64: + m_data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + m_data.SetAddressByteSize(8); + can_parse = true; + break; + + default: + break; + } + + if (can_parse) + { + m_data.GetU32(&offset, &m_header.cputype, 6); + + + ArchSpec mach_arch; + + if (GetArchitecture (mach_arch)) + { + // Check if the module has a required architecture + const ArchSpec &module_arch = module_sp->GetArchitecture(); + if (module_arch.IsValid() && !module_arch.IsCompatibleMatch(mach_arch)) + return false; + + if (SetModulesArchitecture (mach_arch)) + { + const size_t header_and_lc_size = m_header.sizeofcmds + MachHeaderSizeFromMagic(m_header.magic); + if (m_data.GetByteSize() < header_and_lc_size) + { + DataBufferSP data_sp; + ProcessSP process_sp (m_process_wp.lock()); + if (process_sp) + { + data_sp = ReadMemory (process_sp, m_memory_addr, header_and_lc_size); + } + else + { + // Read in all only the load command data from the file on disk + data_sp = m_file.ReadFileContents(m_file_offset, header_and_lc_size); + if (data_sp->GetByteSize() != header_and_lc_size) + return false; + } + if (data_sp) + m_data.SetData (data_sp); + } + } + return true; + } + } + else + { + memset(&m_header, 0, sizeof(struct mach_header)); + } + } + return false; +} + +ByteOrder +ObjectFileMachO::GetByteOrder () const +{ + return m_data.GetByteOrder (); +} + +bool +ObjectFileMachO::IsExecutable() const +{ + return m_header.filetype == MH_EXECUTE; +} + +uint32_t +ObjectFileMachO::GetAddressByteSize () const +{ + return m_data.GetAddressByteSize (); +} + +AddressClass +ObjectFileMachO::GetAddressClass (lldb::addr_t file_addr) +{ + Symtab *symtab = GetSymtab(); + if (symtab) + { + Symbol *symbol = symtab->FindSymbolContainingFileAddress(file_addr); + if (symbol) + { + if (symbol->ValueIsAddress()) + { + SectionSP section_sp (symbol->GetAddressRef().GetSection()); + if (section_sp) + { + const lldb::SectionType section_type = section_sp->GetType(); + switch (section_type) + { + case eSectionTypeInvalid: + return eAddressClassUnknown; + + case eSectionTypeCode: + if (m_header.cputype == llvm::MachO::CPU_TYPE_ARM) + { + // For ARM we have a bit in the n_desc field of the symbol + // that tells us ARM/Thumb which is bit 0x0008. + if (symbol->GetFlags() & MACHO_NLIST_ARM_SYMBOL_IS_THUMB) + return eAddressClassCodeAlternateISA; + } + return eAddressClassCode; + + case eSectionTypeContainer: + return eAddressClassUnknown; + + case eSectionTypeData: + case eSectionTypeDataCString: + case eSectionTypeDataCStringPointers: + case eSectionTypeDataSymbolAddress: + case eSectionTypeData4: + case eSectionTypeData8: + case eSectionTypeData16: + case eSectionTypeDataPointers: + case eSectionTypeZeroFill: + case eSectionTypeDataObjCMessageRefs: + case eSectionTypeDataObjCCFStrings: + case eSectionTypeGoSymtab: + return eAddressClassData; + + case eSectionTypeDebug: + case eSectionTypeDWARFDebugAbbrev: + case eSectionTypeDWARFDebugAddr: + case eSectionTypeDWARFDebugAranges: + case eSectionTypeDWARFDebugFrame: + case eSectionTypeDWARFDebugInfo: + case eSectionTypeDWARFDebugLine: + case eSectionTypeDWARFDebugLoc: + case eSectionTypeDWARFDebugMacInfo: + case eSectionTypeDWARFDebugMacro: + case eSectionTypeDWARFDebugPubNames: + case eSectionTypeDWARFDebugPubTypes: + case eSectionTypeDWARFDebugRanges: + case eSectionTypeDWARFDebugStr: + case eSectionTypeDWARFDebugStrOffsets: + case eSectionTypeDWARFAppleNames: + case eSectionTypeDWARFAppleTypes: + case eSectionTypeDWARFAppleNamespaces: + case eSectionTypeDWARFAppleObjC: + return eAddressClassDebug; + + case eSectionTypeEHFrame: + case eSectionTypeARMexidx: + case eSectionTypeARMextab: + case eSectionTypeCompactUnwind: + return eAddressClassRuntime; + + case eSectionTypeELFSymbolTable: + case eSectionTypeELFDynamicSymbols: + case eSectionTypeELFRelocationEntries: + case eSectionTypeELFDynamicLinkInfo: + case eSectionTypeOther: + return eAddressClassUnknown; + } + } + } + + const SymbolType symbol_type = symbol->GetType(); + switch (symbol_type) + { + case eSymbolTypeAny: return eAddressClassUnknown; + case eSymbolTypeAbsolute: return eAddressClassUnknown; + + case eSymbolTypeCode: + case eSymbolTypeTrampoline: + case eSymbolTypeResolver: + if (m_header.cputype == llvm::MachO::CPU_TYPE_ARM) + { + // For ARM we have a bit in the n_desc field of the symbol + // that tells us ARM/Thumb which is bit 0x0008. + if (symbol->GetFlags() & MACHO_NLIST_ARM_SYMBOL_IS_THUMB) + return eAddressClassCodeAlternateISA; + } + return eAddressClassCode; + + case eSymbolTypeData: return eAddressClassData; + case eSymbolTypeRuntime: return eAddressClassRuntime; + case eSymbolTypeException: return eAddressClassRuntime; + case eSymbolTypeSourceFile: return eAddressClassDebug; + case eSymbolTypeHeaderFile: return eAddressClassDebug; + case eSymbolTypeObjectFile: return eAddressClassDebug; + case eSymbolTypeCommonBlock: return eAddressClassDebug; + case eSymbolTypeBlock: return eAddressClassDebug; + case eSymbolTypeLocal: return eAddressClassData; + case eSymbolTypeParam: return eAddressClassData; + case eSymbolTypeVariable: return eAddressClassData; + case eSymbolTypeVariableType: return eAddressClassDebug; + case eSymbolTypeLineEntry: return eAddressClassDebug; + case eSymbolTypeLineHeader: return eAddressClassDebug; + case eSymbolTypeScopeBegin: return eAddressClassDebug; + case eSymbolTypeScopeEnd: return eAddressClassDebug; + case eSymbolTypeAdditional: return eAddressClassUnknown; + case eSymbolTypeCompiler: return eAddressClassDebug; + case eSymbolTypeInstrumentation:return eAddressClassDebug; + case eSymbolTypeUndefined: return eAddressClassUnknown; + case eSymbolTypeObjCClass: return eAddressClassRuntime; + case eSymbolTypeObjCMetaClass: return eAddressClassRuntime; + case eSymbolTypeObjCIVar: return eAddressClassRuntime; + case eSymbolTypeReExported: return eAddressClassRuntime; + } + } + } + return eAddressClassUnknown; +} + +Symtab * +ObjectFileMachO::GetSymtab() +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_symtab_ap.get() == NULL) + { + m_symtab_ap.reset(new Symtab(this)); + Mutex::Locker symtab_locker (m_symtab_ap->GetMutex()); + ParseSymtab (); + m_symtab_ap->Finalize (); + } + } + return m_symtab_ap.get(); +} + +bool +ObjectFileMachO::IsStripped () +{ + if (m_dysymtab.cmd == 0) + { + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + for (uint32_t i=0; i<m_header.ncmds; ++i) + { + const lldb::offset_t load_cmd_offset = offset; + + load_command lc; + if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL) + break; + if (lc.cmd == LC_DYSYMTAB) + { + m_dysymtab.cmd = lc.cmd; + m_dysymtab.cmdsize = lc.cmdsize; + if (m_data.GetU32 (&offset, &m_dysymtab.ilocalsym, (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2) == NULL) + { + // Clear m_dysymtab if we were unable to read all items from the load command + ::memset (&m_dysymtab, 0, sizeof(m_dysymtab)); + } + } + offset = load_cmd_offset + lc.cmdsize; + } + } + } + if (m_dysymtab.cmd) + return m_dysymtab.nlocalsym <= 1; + return false; +} + +void +ObjectFileMachO::CreateSections (SectionList &unified_section_list) +{ + if (!m_sections_ap.get()) + { + m_sections_ap.reset(new SectionList()); + + const bool is_dsym = (m_header.filetype == MH_DSYM); + lldb::user_id_t segID = 0; + lldb::user_id_t sectID = 0; + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + const bool is_core = GetType() == eTypeCoreFile; + //bool dump_sections = false; + ModuleSP module_sp (GetModule()); + // First look up any LC_ENCRYPTION_INFO load commands + typedef RangeArray<uint32_t, uint32_t, 8> EncryptedFileRanges; + EncryptedFileRanges encrypted_file_ranges; + encryption_info_command encryption_cmd; + for (i=0; i<m_header.ncmds; ++i) + { + const lldb::offset_t load_cmd_offset = offset; + if (m_data.GetU32(&offset, &encryption_cmd, 2) == NULL) + break; + + // LC_ENCRYPTION_INFO and LC_ENCRYPTION_INFO_64 have the same sizes for + // the 3 fields we care about, so treat them the same. + if (encryption_cmd.cmd == LC_ENCRYPTION_INFO || encryption_cmd.cmd == LC_ENCRYPTION_INFO_64) + { + if (m_data.GetU32(&offset, &encryption_cmd.cryptoff, 3)) + { + if (encryption_cmd.cryptid != 0) + { + EncryptedFileRanges::Entry entry; + entry.SetRangeBase(encryption_cmd.cryptoff); + entry.SetByteSize(encryption_cmd.cryptsize); + encrypted_file_ranges.Append(entry); + } + } + } + offset = load_cmd_offset + encryption_cmd.cmdsize; + } + + bool section_file_addresses_changed = false; + + offset = MachHeaderSizeFromMagic(m_header.magic); + + struct segment_command_64 load_cmd; + for (i=0; i<m_header.ncmds; ++i) + { + const lldb::offset_t load_cmd_offset = offset; + if (m_data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + if (load_cmd.cmd == LC_SEGMENT || load_cmd.cmd == LC_SEGMENT_64) + { + if (m_data.GetU8(&offset, (uint8_t*)load_cmd.segname, 16)) + { + bool add_section = true; + bool add_to_unified = true; + ConstString const_segname (load_cmd.segname, std::min<size_t>(strlen(load_cmd.segname), sizeof(load_cmd.segname))); + + SectionSP unified_section_sp(unified_section_list.FindSectionByName(const_segname)); + if (is_dsym && unified_section_sp) + { + if (const_segname == GetSegmentNameLINKEDIT()) + { + // We need to keep the __LINKEDIT segment private to this object file only + add_to_unified = false; + } + else + { + // This is the dSYM file and this section has already been created by + // the object file, no need to create it. + add_section = false; + } + } + load_cmd.vmaddr = m_data.GetAddress(&offset); + load_cmd.vmsize = m_data.GetAddress(&offset); + load_cmd.fileoff = m_data.GetAddress(&offset); + load_cmd.filesize = m_data.GetAddress(&offset); + if (m_length != 0 && load_cmd.filesize != 0) + { + if (load_cmd.fileoff > m_length) + { + // We have a load command that says it extends past the end of the file. This is likely + // a corrupt file. We don't have any way to return an error condition here (this method + // was likely invoked from something like ObjectFile::GetSectionList()) -- all we can do + // is null out the SectionList vector and if a process has been set up, dump a message + // to stdout. The most common case here is core file debugging with a truncated file. + const char *lc_segment_name = load_cmd.cmd == LC_SEGMENT_64 ? "LC_SEGMENT_64" : "LC_SEGMENT"; + module_sp->ReportWarning("load command %u %s has a fileoff (0x%" PRIx64 ") that extends beyond the end of the file (0x%" PRIx64 "), ignoring this section", + i, + lc_segment_name, + load_cmd.fileoff, + m_length); + + load_cmd.fileoff = 0; + load_cmd.filesize = 0; + } + + if (load_cmd.fileoff + load_cmd.filesize > m_length) + { + // We have a load command that says it extends past the end of the file. This is likely + // a corrupt file. We don't have any way to return an error condition here (this method + // was likely invoked from something like ObjectFile::GetSectionList()) -- all we can do + // is null out the SectionList vector and if a process has been set up, dump a message + // to stdout. The most common case here is core file debugging with a truncated file. + const char *lc_segment_name = load_cmd.cmd == LC_SEGMENT_64 ? "LC_SEGMENT_64" : "LC_SEGMENT"; + GetModule()->ReportWarning("load command %u %s has a fileoff + filesize (0x%" PRIx64 ") that extends beyond the end of the file (0x%" PRIx64 "), the segment will be truncated to match", + i, + lc_segment_name, + load_cmd.fileoff + load_cmd.filesize, + m_length); + + // Tuncase the length + load_cmd.filesize = m_length - load_cmd.fileoff; + } + } + if (m_data.GetU32(&offset, &load_cmd.maxprot, 4)) + { + + const bool segment_is_encrypted = (load_cmd.flags & SG_PROTECTED_VERSION_1) != 0; + + // Keep a list of mach segments around in case we need to + // get at data that isn't stored in the abstracted Sections. + m_mach_segments.push_back (load_cmd); + + // Use a segment ID of the segment index shifted left by 8 so they + // never conflict with any of the sections. + SectionSP segment_sp; + if (add_section && (const_segname || is_core)) + { + segment_sp.reset(new Section (module_sp, // Module to which this section belongs + this, // Object file to which this sections belongs + ++segID << 8, // Section ID is the 1 based segment index shifted right by 8 bits as not to collide with any of the 256 section IDs that are possible + const_segname, // Name of this section + eSectionTypeContainer, // This section is a container of other sections. + load_cmd.vmaddr, // File VM address == addresses as they are found in the object file + load_cmd.vmsize, // VM size in bytes of this section + load_cmd.fileoff, // Offset to the data for this section in the file + load_cmd.filesize, // Size in bytes of this section as found in the file + 0, // Segments have no alignment information + load_cmd.flags)); // Flags for this section + + segment_sp->SetIsEncrypted (segment_is_encrypted); + m_sections_ap->AddSection(segment_sp); + if (add_to_unified) + unified_section_list.AddSection(segment_sp); + } + else if (unified_section_sp) + { + if (is_dsym && unified_section_sp->GetFileAddress() != load_cmd.vmaddr) + { + // Check to see if the module was read from memory? + if (module_sp->GetObjectFile()->GetHeaderAddress().IsValid()) + { + // We have a module that is in memory and needs to have its + // file address adjusted. We need to do this because when we + // load a file from memory, its addresses will be slid already, + // yet the addresses in the new symbol file will still be unslid. + // Since everything is stored as section offset, this shouldn't + // cause any problems. + + // Make sure we've parsed the symbol table from the + // ObjectFile before we go around changing its Sections. + module_sp->GetObjectFile()->GetSymtab(); + // eh_frame would present the same problems but we parse that on + // a per-function basis as-needed so it's more difficult to + // remove its use of the Sections. Realistically, the environments + // where this code path will be taken will not have eh_frame sections. + + unified_section_sp->SetFileAddress(load_cmd.vmaddr); + + // Notify the module that the section addresses have been changed once + // we're done so any file-address caches can be updated. + section_file_addresses_changed = true; + } + } + m_sections_ap->AddSection(unified_section_sp); + } + + struct section_64 sect64; + ::memset (§64, 0, sizeof(sect64)); + // Push a section into our mach sections for the section at + // index zero (NO_SECT) if we don't have any mach sections yet... + if (m_mach_sections.empty()) + m_mach_sections.push_back(sect64); + uint32_t segment_sect_idx; + const lldb::user_id_t first_segment_sectID = sectID + 1; + + + const uint32_t num_u32s = load_cmd.cmd == LC_SEGMENT ? 7 : 8; + for (segment_sect_idx=0; segment_sect_idx<load_cmd.nsects; ++segment_sect_idx) + { + if (m_data.GetU8(&offset, (uint8_t*)sect64.sectname, sizeof(sect64.sectname)) == NULL) + break; + if (m_data.GetU8(&offset, (uint8_t*)sect64.segname, sizeof(sect64.segname)) == NULL) + break; + sect64.addr = m_data.GetAddress(&offset); + sect64.size = m_data.GetAddress(&offset); + + if (m_data.GetU32(&offset, §64.offset, num_u32s) == NULL) + break; + + // Keep a list of mach sections around in case we need to + // get at data that isn't stored in the abstracted Sections. + m_mach_sections.push_back (sect64); + + if (add_section) + { + ConstString section_name (sect64.sectname, std::min<size_t>(strlen(sect64.sectname), sizeof(sect64.sectname))); + if (!const_segname) + { + // We have a segment with no name so we need to conjure up + // segments that correspond to the section's segname if there + // isn't already such a section. If there is such a section, + // we resize the section so that it spans all sections. + // We also mark these sections as fake so address matches don't + // hit if they land in the gaps between the child sections. + const_segname.SetTrimmedCStringWithLength(sect64.segname, sizeof(sect64.segname)); + segment_sp = unified_section_list.FindSectionByName (const_segname); + if (segment_sp.get()) + { + Section *segment = segment_sp.get(); + // Grow the section size as needed. + const lldb::addr_t sect64_min_addr = sect64.addr; + const lldb::addr_t sect64_max_addr = sect64_min_addr + sect64.size; + const lldb::addr_t curr_seg_byte_size = segment->GetByteSize(); + const lldb::addr_t curr_seg_min_addr = segment->GetFileAddress(); + const lldb::addr_t curr_seg_max_addr = curr_seg_min_addr + curr_seg_byte_size; + if (sect64_min_addr >= curr_seg_min_addr) + { + const lldb::addr_t new_seg_byte_size = sect64_max_addr - curr_seg_min_addr; + // Only grow the section size if needed + if (new_seg_byte_size > curr_seg_byte_size) + segment->SetByteSize (new_seg_byte_size); + } + else + { + // We need to change the base address of the segment and + // adjust the child section offsets for all existing children. + const lldb::addr_t slide_amount = sect64_min_addr - curr_seg_min_addr; + segment->Slide(slide_amount, false); + segment->GetChildren().Slide(-slide_amount, false); + segment->SetByteSize (curr_seg_max_addr - sect64_min_addr); + } + + // Grow the section size as needed. + if (sect64.offset) + { + const lldb::addr_t segment_min_file_offset = segment->GetFileOffset(); + const lldb::addr_t segment_max_file_offset = segment_min_file_offset + segment->GetFileSize(); + + const lldb::addr_t section_min_file_offset = sect64.offset; + const lldb::addr_t section_max_file_offset = section_min_file_offset + sect64.size; + const lldb::addr_t new_file_offset = std::min (section_min_file_offset, segment_min_file_offset); + const lldb::addr_t new_file_size = std::max (section_max_file_offset, segment_max_file_offset) - new_file_offset; + segment->SetFileOffset (new_file_offset); + segment->SetFileSize (new_file_size); + } + } + else + { + // Create a fake section for the section's named segment + segment_sp.reset(new Section (segment_sp, // Parent section + module_sp, // Module to which this section belongs + this, // Object file to which this section belongs + ++segID << 8, // Section ID is the 1 based segment index shifted right by 8 bits as not to collide with any of the 256 section IDs that are possible + const_segname, // Name of this section + eSectionTypeContainer, // This section is a container of other sections. + sect64.addr, // File VM address == addresses as they are found in the object file + sect64.size, // VM size in bytes of this section + sect64.offset, // Offset to the data for this section in the file + sect64.offset ? sect64.size : 0, // Size in bytes of this section as found in the file + sect64.align, + load_cmd.flags)); // Flags for this section + segment_sp->SetIsFake(true); + + m_sections_ap->AddSection(segment_sp); + if (add_to_unified) + unified_section_list.AddSection(segment_sp); + segment_sp->SetIsEncrypted (segment_is_encrypted); + } + } + assert (segment_sp.get()); + + lldb::SectionType sect_type = eSectionTypeOther; + + if (sect64.flags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS)) + sect_type = eSectionTypeCode; + else + { + uint32_t mach_sect_type = sect64.flags & SECTION_TYPE; + static ConstString g_sect_name_objc_data ("__objc_data"); + static ConstString g_sect_name_objc_msgrefs ("__objc_msgrefs"); + static ConstString g_sect_name_objc_selrefs ("__objc_selrefs"); + static ConstString g_sect_name_objc_classrefs ("__objc_classrefs"); + static ConstString g_sect_name_objc_superrefs ("__objc_superrefs"); + static ConstString g_sect_name_objc_const ("__objc_const"); + static ConstString g_sect_name_objc_classlist ("__objc_classlist"); + static ConstString g_sect_name_cfstring ("__cfstring"); + + static ConstString g_sect_name_dwarf_debug_abbrev ("__debug_abbrev"); + static ConstString g_sect_name_dwarf_debug_aranges ("__debug_aranges"); + static ConstString g_sect_name_dwarf_debug_frame ("__debug_frame"); + static ConstString g_sect_name_dwarf_debug_info ("__debug_info"); + static ConstString g_sect_name_dwarf_debug_line ("__debug_line"); + static ConstString g_sect_name_dwarf_debug_loc ("__debug_loc"); + static ConstString g_sect_name_dwarf_debug_macinfo ("__debug_macinfo"); + static ConstString g_sect_name_dwarf_debug_pubnames ("__debug_pubnames"); + static ConstString g_sect_name_dwarf_debug_pubtypes ("__debug_pubtypes"); + static ConstString g_sect_name_dwarf_debug_ranges ("__debug_ranges"); + static ConstString g_sect_name_dwarf_debug_str ("__debug_str"); + static ConstString g_sect_name_dwarf_apple_names ("__apple_names"); + static ConstString g_sect_name_dwarf_apple_types ("__apple_types"); + static ConstString g_sect_name_dwarf_apple_namespaces ("__apple_namespac"); + static ConstString g_sect_name_dwarf_apple_objc ("__apple_objc"); + static ConstString g_sect_name_eh_frame ("__eh_frame"); + static ConstString g_sect_name_compact_unwind ("__unwind_info"); + static ConstString g_sect_name_text ("__text"); + static ConstString g_sect_name_data ("__data"); + static ConstString g_sect_name_go_symtab ("__gosymtab"); + + if (section_name == g_sect_name_dwarf_debug_abbrev) + sect_type = eSectionTypeDWARFDebugAbbrev; + else if (section_name == g_sect_name_dwarf_debug_aranges) + sect_type = eSectionTypeDWARFDebugAranges; + else if (section_name == g_sect_name_dwarf_debug_frame) + sect_type = eSectionTypeDWARFDebugFrame; + else if (section_name == g_sect_name_dwarf_debug_info) + sect_type = eSectionTypeDWARFDebugInfo; + else if (section_name == g_sect_name_dwarf_debug_line) + sect_type = eSectionTypeDWARFDebugLine; + else if (section_name == g_sect_name_dwarf_debug_loc) + sect_type = eSectionTypeDWARFDebugLoc; + else if (section_name == g_sect_name_dwarf_debug_macinfo) + sect_type = eSectionTypeDWARFDebugMacInfo; + else if (section_name == g_sect_name_dwarf_debug_pubnames) + sect_type = eSectionTypeDWARFDebugPubNames; + else if (section_name == g_sect_name_dwarf_debug_pubtypes) + sect_type = eSectionTypeDWARFDebugPubTypes; + else if (section_name == g_sect_name_dwarf_debug_ranges) + sect_type = eSectionTypeDWARFDebugRanges; + else if (section_name == g_sect_name_dwarf_debug_str) + sect_type = eSectionTypeDWARFDebugStr; + else if (section_name == g_sect_name_dwarf_apple_names) + sect_type = eSectionTypeDWARFAppleNames; + else if (section_name == g_sect_name_dwarf_apple_types) + sect_type = eSectionTypeDWARFAppleTypes; + else if (section_name == g_sect_name_dwarf_apple_namespaces) + sect_type = eSectionTypeDWARFAppleNamespaces; + else if (section_name == g_sect_name_dwarf_apple_objc) + sect_type = eSectionTypeDWARFAppleObjC; + else if (section_name == g_sect_name_objc_selrefs) + sect_type = eSectionTypeDataCStringPointers; + else if (section_name == g_sect_name_objc_msgrefs) + sect_type = eSectionTypeDataObjCMessageRefs; + else if (section_name == g_sect_name_eh_frame) + sect_type = eSectionTypeEHFrame; + else if (section_name == g_sect_name_compact_unwind) + sect_type = eSectionTypeCompactUnwind; + else if (section_name == g_sect_name_cfstring) + sect_type = eSectionTypeDataObjCCFStrings; + else if (section_name == g_sect_name_go_symtab) + sect_type = eSectionTypeGoSymtab; + else if (section_name == g_sect_name_objc_data || + section_name == g_sect_name_objc_classrefs || + section_name == g_sect_name_objc_superrefs || + section_name == g_sect_name_objc_const || + section_name == g_sect_name_objc_classlist) + { + sect_type = eSectionTypeDataPointers; + } + + if (sect_type == eSectionTypeOther) + { + switch (mach_sect_type) + { + // TODO: categorize sections by other flags for regular sections + case S_REGULAR: + if (section_name == g_sect_name_text) + sect_type = eSectionTypeCode; + else if (section_name == g_sect_name_data) + sect_type = eSectionTypeData; + else + sect_type = eSectionTypeOther; + break; + case S_ZEROFILL: sect_type = eSectionTypeZeroFill; break; + case S_CSTRING_LITERALS: sect_type = eSectionTypeDataCString; break; // section with only literal C strings + case S_4BYTE_LITERALS: sect_type = eSectionTypeData4; break; // section with only 4 byte literals + case S_8BYTE_LITERALS: sect_type = eSectionTypeData8; break; // section with only 8 byte literals + case S_LITERAL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only pointers to literals + case S_NON_LAZY_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only non-lazy symbol pointers + case S_LAZY_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only lazy symbol pointers + case S_SYMBOL_STUBS: sect_type = eSectionTypeCode; break; // section with only symbol stubs, byte size of stub in the reserved2 field + case S_MOD_INIT_FUNC_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only function pointers for initialization + case S_MOD_TERM_FUNC_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only function pointers for termination + case S_COALESCED: sect_type = eSectionTypeOther; break; + case S_GB_ZEROFILL: sect_type = eSectionTypeZeroFill; break; + case S_INTERPOSING: sect_type = eSectionTypeCode; break; // section with only pairs of function pointers for interposing + case S_16BYTE_LITERALS: sect_type = eSectionTypeData16; break; // section with only 16 byte literals + case S_DTRACE_DOF: sect_type = eSectionTypeDebug; break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; + default: break; + } + } + } + + SectionSP section_sp(new Section (segment_sp, + module_sp, + this, + ++sectID, + section_name, + sect_type, + sect64.addr - segment_sp->GetFileAddress(), + sect64.size, + sect64.offset, + sect64.offset == 0 ? 0 : sect64.size, + sect64.align, + sect64.flags)); + // Set the section to be encrypted to match the segment + + bool section_is_encrypted = false; + if (!segment_is_encrypted && load_cmd.filesize != 0) + section_is_encrypted = encrypted_file_ranges.FindEntryThatContains(sect64.offset) != NULL; + + section_sp->SetIsEncrypted (segment_is_encrypted || section_is_encrypted); + segment_sp->GetChildren().AddSection(section_sp); + + if (segment_sp->IsFake()) + { + segment_sp.reset(); + const_segname.Clear(); + } + } + } + if (segment_sp && is_dsym) + { + if (first_segment_sectID <= sectID) + { + lldb::user_id_t sect_uid; + for (sect_uid = first_segment_sectID; sect_uid <= sectID; ++sect_uid) + { + SectionSP curr_section_sp(segment_sp->GetChildren().FindSectionByID (sect_uid)); + SectionSP next_section_sp; + if (sect_uid + 1 <= sectID) + next_section_sp = segment_sp->GetChildren().FindSectionByID (sect_uid+1); + + if (curr_section_sp.get()) + { + if (curr_section_sp->GetByteSize() == 0) + { + if (next_section_sp.get() != NULL) + curr_section_sp->SetByteSize ( next_section_sp->GetFileAddress() - curr_section_sp->GetFileAddress() ); + else + curr_section_sp->SetByteSize ( load_cmd.vmsize ); + } + } + } + } + } + } + } + } + else if (load_cmd.cmd == LC_DYSYMTAB) + { + m_dysymtab.cmd = load_cmd.cmd; + m_dysymtab.cmdsize = load_cmd.cmdsize; + m_data.GetU32 (&offset, &m_dysymtab.ilocalsym, (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2); + } + + offset = load_cmd_offset + load_cmd.cmdsize; + } + + + if (section_file_addresses_changed && module_sp.get()) + { + module_sp->SectionFileAddressesChanged(); + } + } +} + +class MachSymtabSectionInfo +{ +public: + MachSymtabSectionInfo (SectionList *section_list) : + m_section_list (section_list), + m_section_infos() + { + // Get the number of sections down to a depth of 1 to include + // all segments and their sections, but no other sections that + // may be added for debug map or + m_section_infos.resize(section_list->GetNumSections(1)); + } + + SectionSP + GetSection (uint8_t n_sect, addr_t file_addr) + { + if (n_sect == 0) + return SectionSP(); + if (n_sect < m_section_infos.size()) + { + if (!m_section_infos[n_sect].section_sp) + { + SectionSP section_sp (m_section_list->FindSectionByID (n_sect)); + m_section_infos[n_sect].section_sp = section_sp; + if (section_sp) + { + m_section_infos[n_sect].vm_range.SetBaseAddress (section_sp->GetFileAddress()); + m_section_infos[n_sect].vm_range.SetByteSize (section_sp->GetByteSize()); + } + else + { + Host::SystemLog (Host::eSystemLogError, "error: unable to find section for section %u\n", n_sect); + } + } + if (m_section_infos[n_sect].vm_range.Contains(file_addr)) + { + // Symbol is in section. + return m_section_infos[n_sect].section_sp; + } + else if (m_section_infos[n_sect].vm_range.GetByteSize () == 0 && + m_section_infos[n_sect].vm_range.GetBaseAddress() == file_addr) + { + // Symbol is in section with zero size, but has the same start + // address as the section. This can happen with linker symbols + // (symbols that start with the letter 'l' or 'L'. + return m_section_infos[n_sect].section_sp; + } + } + return m_section_list->FindSectionContainingFileAddress(file_addr); + } + +protected: + struct SectionInfo + { + SectionInfo () : + vm_range(), + section_sp () + { + } + + VMRange vm_range; + SectionSP section_sp; + }; + SectionList *m_section_list; + std::vector<SectionInfo> m_section_infos; +}; + +struct TrieEntry +{ + TrieEntry () : + name(), + address(LLDB_INVALID_ADDRESS), + flags (0), + other(0), + import_name() + { + } + + void + Clear () + { + name.Clear(); + address = LLDB_INVALID_ADDRESS; + flags = 0; + other = 0; + import_name.Clear(); + } + + void + Dump () const + { + printf ("0x%16.16llx 0x%16.16llx 0x%16.16llx \"%s\"", + static_cast<unsigned long long>(address), + static_cast<unsigned long long>(flags), + static_cast<unsigned long long>(other), name.GetCString()); + if (import_name) + printf (" -> \"%s\"\n", import_name.GetCString()); + else + printf ("\n"); + } + ConstString name; + uint64_t address; + uint64_t flags; + uint64_t other; + ConstString import_name; +}; + +struct TrieEntryWithOffset +{ + lldb::offset_t nodeOffset; + TrieEntry entry; + + TrieEntryWithOffset (lldb::offset_t offset) : + nodeOffset (offset), + entry() + { + } + + void + Dump (uint32_t idx) const + { + printf ("[%3u] 0x%16.16llx: ", idx, + static_cast<unsigned long long>(nodeOffset)); + entry.Dump(); + } + + bool + operator<(const TrieEntryWithOffset& other) const + { + return ( nodeOffset < other.nodeOffset ); + } +}; + +static bool +ParseTrieEntries (DataExtractor &data, + lldb::offset_t offset, + const bool is_arm, + std::vector<llvm::StringRef> &nameSlices, + std::set<lldb::addr_t> &resolver_addresses, + std::vector<TrieEntryWithOffset>& output) +{ + if (!data.ValidOffset(offset)) + return true; + + const uint64_t terminalSize = data.GetULEB128(&offset); + lldb::offset_t children_offset = offset + terminalSize; + if ( terminalSize != 0 ) { + TrieEntryWithOffset e (offset); + e.entry.flags = data.GetULEB128(&offset); + const char *import_name = NULL; + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + e.entry.address = 0; + e.entry.other = data.GetULEB128(&offset); // dylib ordinal + import_name = data.GetCStr(&offset); + } + else { + e.entry.address = data.GetULEB128(&offset); + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + { + e.entry.other = data.GetULEB128(&offset); + uint64_t resolver_addr = e.entry.other; + if (is_arm) + resolver_addr &= THUMB_ADDRESS_BIT_MASK; + resolver_addresses.insert(resolver_addr); + } + else + e.entry.other = 0; + } + // Only add symbols that are reexport symbols with a valid import name + if (EXPORT_SYMBOL_FLAGS_REEXPORT & e.entry.flags && import_name && import_name[0]) + { + std::string name; + if (!nameSlices.empty()) + { + for (auto name_slice: nameSlices) + name.append(name_slice.data(), name_slice.size()); + } + if (name.size() > 1) + { + // Skip the leading '_' + e.entry.name.SetCStringWithLength(name.c_str() + 1,name.size() - 1); + } + if (import_name) + { + // Skip the leading '_' + e.entry.import_name.SetCString(import_name+1); + } + output.push_back(e); + } + } + + const uint8_t childrenCount = data.GetU8(&children_offset); + for (uint8_t i=0; i < childrenCount; ++i) { + const char *cstr = data.GetCStr(&children_offset); + if (cstr) + nameSlices.push_back(llvm::StringRef(cstr)); + else + return false; // Corrupt data + lldb::offset_t childNodeOffset = data.GetULEB128(&children_offset); + if (childNodeOffset) + { + if (!ParseTrieEntries(data, + childNodeOffset, + is_arm, + nameSlices, + resolver_addresses, + output)) + { + return false; + } + } + nameSlices.pop_back(); + } + return true; +} + +// Read the UUID out of a dyld_shared_cache file on-disk. +UUID +ObjectFileMachO::GetSharedCacheUUID (FileSpec dyld_shared_cache, const ByteOrder byte_order, const uint32_t addr_byte_size) +{ + UUID dsc_uuid; + DataBufferSP dsc_data_sp = dyld_shared_cache.MemoryMapFileContentsIfLocal(0, sizeof(struct lldb_copy_dyld_cache_header_v1)); + if (dsc_data_sp) + { + DataExtractor dsc_header_data (dsc_data_sp, byte_order, addr_byte_size); + + char version_str[7]; + lldb::offset_t offset = 0; + memcpy (version_str, dsc_header_data.GetData (&offset, 6), 6); + version_str[6] = '\0'; + if (strcmp (version_str, "dyld_v") == 0) + { + offset = offsetof (struct lldb_copy_dyld_cache_header_v1, uuid); + uint8_t uuid_bytes[sizeof (uuid_t)]; + memcpy (uuid_bytes, dsc_header_data.GetData (&offset, sizeof (uuid_t)), sizeof (uuid_t)); + dsc_uuid.SetBytes (uuid_bytes); + } + } + return dsc_uuid; +} + +size_t +ObjectFileMachO::ParseSymtab () +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "ObjectFileMachO::ParseSymtab () module = %s", + m_file.GetFilename().AsCString("")); + ModuleSP module_sp (GetModule()); + if (!module_sp) + return 0; + + struct symtab_command symtab_load_command = { 0, 0, 0, 0, 0, 0 }; + struct linkedit_data_command function_starts_load_command = { 0, 0, 0, 0 }; + struct dyld_info_command dyld_info = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + typedef AddressDataArray<lldb::addr_t, bool, 100> FunctionStarts; + FunctionStarts function_starts; + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + FileSpecList dylib_files; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYMBOLS)); + static const llvm::StringRef g_objc_v2_prefix_class ("_OBJC_CLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_metaclass ("_OBJC_METACLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_ivar ("_OBJC_IVAR_$_"); + + for (i=0; i<m_header.ncmds; ++i) + { + const lldb::offset_t cmd_offset = offset; + // Read in the load command and load command size + struct load_command lc; + if (m_data.GetU32(&offset, &lc, 2) == NULL) + break; + // Watch for the symbol table load command + switch (lc.cmd) + { + case LC_SYMTAB: + symtab_load_command.cmd = lc.cmd; + symtab_load_command.cmdsize = lc.cmdsize; + // Read in the rest of the symtab load command + if (m_data.GetU32(&offset, &symtab_load_command.symoff, 4) == 0) // fill in symoff, nsyms, stroff, strsize fields + return 0; + if (symtab_load_command.symoff == 0) + { + if (log) + module_sp->LogMessage(log, "LC_SYMTAB.symoff == 0"); + return 0; + } + + if (symtab_load_command.stroff == 0) + { + if (log) + module_sp->LogMessage(log, "LC_SYMTAB.stroff == 0"); + return 0; + } + + if (symtab_load_command.nsyms == 0) + { + if (log) + module_sp->LogMessage(log, "LC_SYMTAB.nsyms == 0"); + return 0; + } + + if (symtab_load_command.strsize == 0) + { + if (log) + module_sp->LogMessage(log, "LC_SYMTAB.strsize == 0"); + return 0; + } + break; + + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + if (m_data.GetU32(&offset, &dyld_info.rebase_off, 10)) + { + dyld_info.cmd = lc.cmd; + dyld_info.cmdsize = lc.cmdsize; + } + else + { + memset (&dyld_info, 0, sizeof(dyld_info)); + } + break; + + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOADFVMLIB: + case LC_LOAD_UPWARD_DYLIB: + { + uint32_t name_offset = cmd_offset + m_data.GetU32(&offset); + const char *path = m_data.PeekCStr(name_offset); + if (path) + { + FileSpec file_spec(path, false); + // Strip the path if there is @rpath, @executable, etc so we just use the basename + if (path[0] == '@') + file_spec.GetDirectory().Clear(); + + if (lc.cmd == LC_REEXPORT_DYLIB) + { + m_reexported_dylibs.AppendIfUnique(file_spec); + } + + dylib_files.Append(file_spec); + } + } + break; + + case LC_FUNCTION_STARTS: + function_starts_load_command.cmd = lc.cmd; + function_starts_load_command.cmdsize = lc.cmdsize; + if (m_data.GetU32(&offset, &function_starts_load_command.dataoff, 2) == NULL) // fill in symoff, nsyms, stroff, strsize fields + memset (&function_starts_load_command, 0, sizeof(function_starts_load_command)); + break; + + default: + break; + } + offset = cmd_offset + lc.cmdsize; + } + + if (symtab_load_command.cmd) + { + Symtab *symtab = m_symtab_ap.get(); + SectionList *section_list = GetSectionList(); + if (section_list == NULL) + return 0; + + const uint32_t addr_byte_size = m_data.GetAddressByteSize(); + const ByteOrder byte_order = m_data.GetByteOrder(); + bool bit_width_32 = addr_byte_size == 4; + const size_t nlist_byte_size = bit_width_32 ? sizeof(struct nlist) : sizeof(struct nlist_64); + + DataExtractor nlist_data (NULL, 0, byte_order, addr_byte_size); + DataExtractor strtab_data (NULL, 0, byte_order, addr_byte_size); + DataExtractor function_starts_data (NULL, 0, byte_order, addr_byte_size); + DataExtractor indirect_symbol_index_data (NULL, 0, byte_order, addr_byte_size); + DataExtractor dyld_trie_data (NULL, 0, byte_order, addr_byte_size); + + const addr_t nlist_data_byte_size = symtab_load_command.nsyms * nlist_byte_size; + const addr_t strtab_data_byte_size = symtab_load_command.strsize; + addr_t strtab_addr = LLDB_INVALID_ADDRESS; + + ProcessSP process_sp (m_process_wp.lock()); + Process *process = process_sp.get(); + + uint32_t memory_module_load_level = eMemoryModuleLoadLevelComplete; + + if (process && m_header.filetype != llvm::MachO::MH_OBJECT) + { + Target &target = process->GetTarget(); + + memory_module_load_level = target.GetMemoryModuleLoadLevel(); + + SectionSP linkedit_section_sp(section_list->FindSectionByName(GetSegmentNameLINKEDIT())); + // Reading mach file from memory in a process or core file... + + if (linkedit_section_sp) + { + addr_t linkedit_load_addr = linkedit_section_sp->GetLoadBaseAddress(&target); + if (linkedit_load_addr == LLDB_INVALID_ADDRESS) + { + // We might be trying to access the symbol table before the __LINKEDIT's load + // address has been set in the target. We can't fail to read the symbol table, + // so calculate the right address manually + linkedit_load_addr = CalculateSectionLoadAddressForMemoryImage(m_memory_addr, GetMachHeaderSection(), linkedit_section_sp.get()); + } + + const addr_t linkedit_file_offset = linkedit_section_sp->GetFileOffset(); + const addr_t symoff_addr = linkedit_load_addr + symtab_load_command.symoff - linkedit_file_offset; + strtab_addr = linkedit_load_addr + symtab_load_command.stroff - linkedit_file_offset; + + bool data_was_read = false; + +#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) + if (m_header.flags & 0x80000000u && process->GetAddressByteSize() == sizeof (void*)) + { + // This mach-o memory file is in the dyld shared cache. If this + // program is not remote and this is iOS, then this process will + // share the same shared cache as the process we are debugging and + // we can read the entire __LINKEDIT from the address space in this + // process. This is a needed optimization that is used for local iOS + // debugging only since all shared libraries in the shared cache do + // not have corresponding files that exist in the file system of the + // device. They have been combined into a single file. This means we + // always have to load these files from memory. All of the symbol and + // string tables from all of the __LINKEDIT sections from the shared + // libraries in the shared cache have been merged into a single large + // symbol and string table. Reading all of this symbol and string table + // data across can slow down debug launch times, so we optimize this by + // reading the memory for the __LINKEDIT section from this process. + + UUID lldb_shared_cache(GetLLDBSharedCacheUUID()); + UUID process_shared_cache(GetProcessSharedCacheUUID(process)); + bool use_lldb_cache = true; + if (lldb_shared_cache.IsValid() && process_shared_cache.IsValid() && lldb_shared_cache != process_shared_cache) + { + use_lldb_cache = false; + ModuleSP module_sp (GetModule()); + if (module_sp) + module_sp->ReportWarning ("shared cache in process does not match lldb's own shared cache, startup will be slow."); + + } + + PlatformSP platform_sp (target.GetPlatform()); + if (platform_sp && platform_sp->IsHost() && use_lldb_cache) + { + data_was_read = true; + nlist_data.SetData((void *)symoff_addr, nlist_data_byte_size, eByteOrderLittle); + strtab_data.SetData((void *)strtab_addr, strtab_data_byte_size, eByteOrderLittle); + if (function_starts_load_command.cmd) + { + const addr_t func_start_addr = linkedit_load_addr + function_starts_load_command.dataoff - linkedit_file_offset; + function_starts_data.SetData ((void *)func_start_addr, function_starts_load_command.datasize, eByteOrderLittle); + } + } + } +#endif + + if (!data_was_read) + { + if (memory_module_load_level == eMemoryModuleLoadLevelComplete) + { + DataBufferSP nlist_data_sp (ReadMemory (process_sp, symoff_addr, nlist_data_byte_size)); + if (nlist_data_sp) + nlist_data.SetData (nlist_data_sp, 0, nlist_data_sp->GetByteSize()); + // Load strings individually from memory when loading from memory since shared cache + // string tables contain strings for all symbols from all shared cached libraries + //DataBufferSP strtab_data_sp (ReadMemory (process_sp, strtab_addr, strtab_data_byte_size)); + //if (strtab_data_sp) + // strtab_data.SetData (strtab_data_sp, 0, strtab_data_sp->GetByteSize()); + if (m_dysymtab.nindirectsyms != 0) + { + const addr_t indirect_syms_addr = linkedit_load_addr + m_dysymtab.indirectsymoff - linkedit_file_offset; + DataBufferSP indirect_syms_data_sp (ReadMemory (process_sp, indirect_syms_addr, m_dysymtab.nindirectsyms * 4)); + if (indirect_syms_data_sp) + indirect_symbol_index_data.SetData (indirect_syms_data_sp, 0, indirect_syms_data_sp->GetByteSize()); + } + } + + if (memory_module_load_level >= eMemoryModuleLoadLevelPartial) + { + if (function_starts_load_command.cmd) + { + const addr_t func_start_addr = linkedit_load_addr + function_starts_load_command.dataoff - linkedit_file_offset; + DataBufferSP func_start_data_sp (ReadMemory (process_sp, func_start_addr, function_starts_load_command.datasize)); + if (func_start_data_sp) + function_starts_data.SetData (func_start_data_sp, 0, func_start_data_sp->GetByteSize()); + } + } + } + } + } + else + { + nlist_data.SetData (m_data, + symtab_load_command.symoff, + nlist_data_byte_size); + strtab_data.SetData (m_data, + symtab_load_command.stroff, + strtab_data_byte_size); + + if (dyld_info.export_size > 0) + { + dyld_trie_data.SetData (m_data, + dyld_info.export_off, + dyld_info.export_size); + } + + if (m_dysymtab.nindirectsyms != 0) + { + indirect_symbol_index_data.SetData (m_data, + m_dysymtab.indirectsymoff, + m_dysymtab.nindirectsyms * 4); + } + if (function_starts_load_command.cmd) + { + function_starts_data.SetData (m_data, + function_starts_load_command.dataoff, + function_starts_load_command.datasize); + } + } + + if (nlist_data.GetByteSize() == 0 && memory_module_load_level == eMemoryModuleLoadLevelComplete) + { + if (log) + module_sp->LogMessage(log, "failed to read nlist data"); + return 0; + } + + const bool have_strtab_data = strtab_data.GetByteSize() > 0; + if (!have_strtab_data) + { + if (process) + { + if (strtab_addr == LLDB_INVALID_ADDRESS) + { + if (log) + module_sp->LogMessage(log, "failed to locate the strtab in memory"); + return 0; + } + } + else + { + if (log) + module_sp->LogMessage(log, "failed to read strtab data"); + return 0; + } + } + + const ConstString &g_segment_name_TEXT = GetSegmentNameTEXT(); + const ConstString &g_segment_name_DATA = GetSegmentNameDATA(); + const ConstString &g_segment_name_DATA_DIRTY = GetSegmentNameDATA_DIRTY(); + const ConstString &g_segment_name_DATA_CONST = GetSegmentNameDATA_CONST(); + const ConstString &g_segment_name_OBJC = GetSegmentNameOBJC(); + const ConstString &g_section_name_eh_frame = GetSectionNameEHFrame(); + SectionSP text_section_sp(section_list->FindSectionByName(g_segment_name_TEXT)); + SectionSP data_section_sp(section_list->FindSectionByName(g_segment_name_DATA)); + SectionSP data_dirty_section_sp(section_list->FindSectionByName(g_segment_name_DATA_DIRTY)); + SectionSP data_const_section_sp(section_list->FindSectionByName(g_segment_name_DATA_CONST)); + SectionSP objc_section_sp(section_list->FindSectionByName(g_segment_name_OBJC)); + SectionSP eh_frame_section_sp; + if (text_section_sp.get()) + eh_frame_section_sp = text_section_sp->GetChildren().FindSectionByName (g_section_name_eh_frame); + else + eh_frame_section_sp = section_list->FindSectionByName (g_section_name_eh_frame); + + const bool is_arm = (m_header.cputype == llvm::MachO::CPU_TYPE_ARM); + + // lldb works best if it knows the start address of all functions in a module. + // Linker symbols or debug info are normally the best source of information for start addr / size but + // they may be stripped in a released binary. + // Two additional sources of information exist in Mach-O binaries: + // LC_FUNCTION_STARTS - a list of ULEB128 encoded offsets of each function's start address in the + // binary, relative to the text section. + // eh_frame - the eh_frame FDEs have the start addr & size of each function + // LC_FUNCTION_STARTS is the fastest source to read in, and is present on all modern binaries. + // Binaries built to run on older releases may need to use eh_frame information. + + if (text_section_sp && function_starts_data.GetByteSize()) + { + FunctionStarts::Entry function_start_entry; + function_start_entry.data = false; + lldb::offset_t function_start_offset = 0; + function_start_entry.addr = text_section_sp->GetFileAddress(); + uint64_t delta; + while ((delta = function_starts_data.GetULEB128(&function_start_offset)) > 0) + { + // Now append the current entry + function_start_entry.addr += delta; + function_starts.Append(function_start_entry); + } + } + else + { + // If m_type is eTypeDebugInfo, then this is a dSYM - it will have the load command claiming an eh_frame + // but it doesn't actually have the eh_frame content. And if we have a dSYM, we don't need to do any + // of this fill-in-the-missing-symbols works anyway - the debug info should give us all the functions in + // the module. + if (text_section_sp.get() && eh_frame_section_sp.get() && m_type != eTypeDebugInfo) + { + DWARFCallFrameInfo eh_frame(*this, eh_frame_section_sp, eRegisterKindEHFrame, true); + DWARFCallFrameInfo::FunctionAddressAndSizeVector functions; + eh_frame.GetFunctionAddressAndSizeVector (functions); + addr_t text_base_addr = text_section_sp->GetFileAddress(); + size_t count = functions.GetSize(); + for (size_t i = 0; i < count; ++i) + { + const DWARFCallFrameInfo::FunctionAddressAndSizeVector::Entry *func = functions.GetEntryAtIndex (i); + if (func) + { + FunctionStarts::Entry function_start_entry; + function_start_entry.addr = func->base - text_base_addr; + function_starts.Append(function_start_entry); + } + } + } + } + + const size_t function_starts_count = function_starts.GetSize(); + + const user_id_t TEXT_eh_frame_sectID = + eh_frame_section_sp.get() ? eh_frame_section_sp->GetID() + : static_cast<user_id_t>(NO_SECT); + + lldb::offset_t nlist_data_offset = 0; + + uint32_t N_SO_index = UINT32_MAX; + + MachSymtabSectionInfo section_info (section_list); + std::vector<uint32_t> N_FUN_indexes; + std::vector<uint32_t> N_NSYM_indexes; + std::vector<uint32_t> N_INCL_indexes; + std::vector<uint32_t> N_BRAC_indexes; + std::vector<uint32_t> N_COMM_indexes; + typedef std::multimap <uint64_t, uint32_t> ValueToSymbolIndexMap; + typedef std::map <uint32_t, uint32_t> NListIndexToSymbolIndexMap; + typedef std::map <const char *, uint32_t> ConstNameToSymbolIndexMap; + ValueToSymbolIndexMap N_FUN_addr_to_sym_idx; + ValueToSymbolIndexMap N_STSYM_addr_to_sym_idx; + ConstNameToSymbolIndexMap N_GSYM_name_to_sym_idx; + // Any symbols that get merged into another will get an entry + // in this map so we know + NListIndexToSymbolIndexMap m_nlist_idx_to_sym_idx; + uint32_t nlist_idx = 0; + Symbol *symbol_ptr = NULL; + + uint32_t sym_idx = 0; + Symbol *sym = NULL; + size_t num_syms = 0; + std::string memory_symbol_name; + uint32_t unmapped_local_symbols_found = 0; + + std::vector<TrieEntryWithOffset> trie_entries; + std::set<lldb::addr_t> resolver_addresses; + + if (dyld_trie_data.GetByteSize() > 0) + { + std::vector<llvm::StringRef> nameSlices; + ParseTrieEntries (dyld_trie_data, + 0, + is_arm, + nameSlices, + resolver_addresses, + trie_entries); + + ConstString text_segment_name ("__TEXT"); + SectionSP text_segment_sp = GetSectionList()->FindSectionByName(text_segment_name); + if (text_segment_sp) + { + const lldb::addr_t text_segment_file_addr = text_segment_sp->GetFileAddress(); + if (text_segment_file_addr != LLDB_INVALID_ADDRESS) + { + for (auto &e : trie_entries) + e.entry.address += text_segment_file_addr; + } + } + } + + typedef std::set<ConstString> IndirectSymbols; + IndirectSymbols indirect_symbol_names; + +#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) + + // Some recent builds of the dyld_shared_cache (hereafter: DSC) have been optimized by moving LOCAL + // symbols out of the memory mapped portion of the DSC. The symbol information has all been retained, + // but it isn't available in the normal nlist data. However, there *are* duplicate entries of *some* + // LOCAL symbols in the normal nlist data. To handle this situation correctly, we must first attempt + // to parse any DSC unmapped symbol information. If we find any, we set a flag that tells the normal + // nlist parser to ignore all LOCAL symbols. + + if (m_header.flags & 0x80000000u) + { + // Before we can start mapping the DSC, we need to make certain the target process is actually + // using the cache we can find. + + // Next we need to determine the correct path for the dyld shared cache. + + ArchSpec header_arch; + GetArchitecture(header_arch); + char dsc_path[PATH_MAX]; + char dsc_path_development[PATH_MAX]; + + snprintf(dsc_path, sizeof(dsc_path), "%s%s%s", + "/System/Library/Caches/com.apple.dyld/", /* IPHONE_DYLD_SHARED_CACHE_DIR */ + "dyld_shared_cache_", /* DYLD_SHARED_CACHE_BASE_NAME */ + header_arch.GetArchitectureName()); + + snprintf(dsc_path_development, sizeof(dsc_path), "%s%s%s%s", + "/System/Library/Caches/com.apple.dyld/", /* IPHONE_DYLD_SHARED_CACHE_DIR */ + "dyld_shared_cache_", /* DYLD_SHARED_CACHE_BASE_NAME */ + header_arch.GetArchitectureName(), + ".development"); + + FileSpec dsc_nondevelopment_filespec(dsc_path, false); + FileSpec dsc_development_filespec(dsc_path_development, false); + FileSpec dsc_filespec; + + UUID dsc_uuid; + UUID process_shared_cache_uuid; + + if (process) + { + process_shared_cache_uuid = GetProcessSharedCacheUUID(process); + } + + // First see if we can find an exact match for the inferior process shared cache UUID in + // the development or non-development shared caches on disk. + if (process_shared_cache_uuid.IsValid()) + { + if (dsc_development_filespec.Exists()) + { + UUID dsc_development_uuid = GetSharedCacheUUID (dsc_development_filespec, byte_order, addr_byte_size); + if (dsc_development_uuid.IsValid() && dsc_development_uuid == process_shared_cache_uuid) + { + dsc_filespec = dsc_development_filespec; + dsc_uuid = dsc_development_uuid; + } + } + if (!dsc_uuid.IsValid() && dsc_nondevelopment_filespec.Exists()) + { + UUID dsc_nondevelopment_uuid = GetSharedCacheUUID (dsc_nondevelopment_filespec, byte_order, addr_byte_size); + if (dsc_nondevelopment_uuid.IsValid() && dsc_nondevelopment_uuid == process_shared_cache_uuid) + { + dsc_filespec = dsc_nondevelopment_filespec; + dsc_uuid = dsc_nondevelopment_uuid; + } + } + } + + // Failing a UUID match, prefer the development dyld_shared cache if both are present. + if (!dsc_filespec.Exists()) + { + if (dsc_development_filespec.Exists()) + { + dsc_filespec = dsc_development_filespec; + } + else + { + dsc_filespec = dsc_nondevelopment_filespec; + } + } + + /* The dyld_cache_header has a pointer to the dyld_cache_local_symbols_info structure (localSymbolsOffset). + The dyld_cache_local_symbols_info structure gives us three things: + 1. The start and count of the nlist records in the dyld_shared_cache file + 2. The start and size of the strings for these nlist records + 3. The start and count of dyld_cache_local_symbols_entry entries + + There is one dyld_cache_local_symbols_entry per dylib/framework in the dyld shared cache. + The "dylibOffset" field is the Mach-O header of this dylib/framework in the dyld shared cache. + The dyld_cache_local_symbols_entry also lists the start of this dylib/framework's nlist records + and the count of how many nlist records there are for this dylib/framework. + */ + + // Process the dyld shared cache header to find the unmapped symbols + + DataBufferSP dsc_data_sp = dsc_filespec.MemoryMapFileContentsIfLocal(0, sizeof(struct lldb_copy_dyld_cache_header_v1)); + if (!dsc_uuid.IsValid()) + { + dsc_uuid = GetSharedCacheUUID (dsc_filespec, byte_order, addr_byte_size); + } + if (dsc_data_sp) + { + DataExtractor dsc_header_data (dsc_data_sp, byte_order, addr_byte_size); + + bool uuid_match = true; + if (dsc_uuid.IsValid() && process) + { + if (process_shared_cache_uuid.IsValid() && dsc_uuid != process_shared_cache_uuid) + { + // The on-disk dyld_shared_cache file is not the same as the one in this + // process' memory, don't use it. + uuid_match = false; + ModuleSP module_sp (GetModule()); + if (module_sp) + module_sp->ReportWarning ("process shared cache does not match on-disk dyld_shared_cache file, some symbol names will be missing."); + } + } + + offset = offsetof (struct lldb_copy_dyld_cache_header_v1, mappingOffset); + + uint32_t mappingOffset = dsc_header_data.GetU32(&offset); + + // If the mappingOffset points to a location inside the header, we've + // opened an old dyld shared cache, and should not proceed further. + if (uuid_match && mappingOffset >= sizeof(struct lldb_copy_dyld_cache_header_v1)) + { + + DataBufferSP dsc_mapping_info_data_sp = dsc_filespec.MemoryMapFileContentsIfLocal(mappingOffset, sizeof (struct lldb_copy_dyld_cache_mapping_info)); + DataExtractor dsc_mapping_info_data(dsc_mapping_info_data_sp, byte_order, addr_byte_size); + offset = 0; + + // The File addresses (from the in-memory Mach-O load commands) for the shared libraries + // in the shared library cache need to be adjusted by an offset to match up with the + // dylibOffset identifying field in the dyld_cache_local_symbol_entry's. This offset is + // recorded in mapping_offset_value. + const uint64_t mapping_offset_value = dsc_mapping_info_data.GetU64(&offset); + + offset = offsetof (struct lldb_copy_dyld_cache_header_v1, localSymbolsOffset); + uint64_t localSymbolsOffset = dsc_header_data.GetU64(&offset); + uint64_t localSymbolsSize = dsc_header_data.GetU64(&offset); + + if (localSymbolsOffset && localSymbolsSize) + { + // Map the local symbols + if (DataBufferSP dsc_local_symbols_data_sp = dsc_filespec.MemoryMapFileContentsIfLocal(localSymbolsOffset, localSymbolsSize)) + { + DataExtractor dsc_local_symbols_data(dsc_local_symbols_data_sp, byte_order, addr_byte_size); + + offset = 0; + + typedef std::map<ConstString, uint16_t> UndefinedNameToDescMap; + typedef std::map<uint32_t, ConstString> SymbolIndexToName; + UndefinedNameToDescMap undefined_name_to_desc; + SymbolIndexToName reexport_shlib_needs_fixup; + + + // Read the local_symbols_infos struct in one shot + struct lldb_copy_dyld_cache_local_symbols_info local_symbols_info; + dsc_local_symbols_data.GetU32(&offset, &local_symbols_info.nlistOffset, 6); + + SectionSP text_section_sp(section_list->FindSectionByName(GetSegmentNameTEXT())); + + uint32_t header_file_offset = (text_section_sp->GetFileAddress() - mapping_offset_value); + + offset = local_symbols_info.entriesOffset; + for (uint32_t entry_index = 0; entry_index < local_symbols_info.entriesCount; entry_index++) + { + struct lldb_copy_dyld_cache_local_symbols_entry local_symbols_entry; + local_symbols_entry.dylibOffset = dsc_local_symbols_data.GetU32(&offset); + local_symbols_entry.nlistStartIndex = dsc_local_symbols_data.GetU32(&offset); + local_symbols_entry.nlistCount = dsc_local_symbols_data.GetU32(&offset); + + if (header_file_offset == local_symbols_entry.dylibOffset) + { + unmapped_local_symbols_found = local_symbols_entry.nlistCount; + + // The normal nlist code cannot correctly size the Symbols array, we need to allocate it here. + sym = symtab->Resize (symtab_load_command.nsyms + m_dysymtab.nindirectsyms + unmapped_local_symbols_found - m_dysymtab.nlocalsym); + num_syms = symtab->GetNumSymbols(); + + nlist_data_offset = local_symbols_info.nlistOffset + (nlist_byte_size * local_symbols_entry.nlistStartIndex); + uint32_t string_table_offset = local_symbols_info.stringsOffset; + + for (uint32_t nlist_index = 0; nlist_index < local_symbols_entry.nlistCount; nlist_index++) + { + ///////////////////////////// + { + struct nlist_64 nlist; + if (!dsc_local_symbols_data.ValidOffsetForDataOfSize(nlist_data_offset, nlist_byte_size)) + break; + + nlist.n_strx = dsc_local_symbols_data.GetU32_unchecked(&nlist_data_offset); + nlist.n_type = dsc_local_symbols_data.GetU8_unchecked (&nlist_data_offset); + nlist.n_sect = dsc_local_symbols_data.GetU8_unchecked (&nlist_data_offset); + nlist.n_desc = dsc_local_symbols_data.GetU16_unchecked (&nlist_data_offset); + nlist.n_value = dsc_local_symbols_data.GetAddress_unchecked (&nlist_data_offset); + + SymbolType type = eSymbolTypeInvalid; + const char *symbol_name = dsc_local_symbols_data.PeekCStr(string_table_offset + nlist.n_strx); + + if (symbol_name == NULL) + { + // No symbol should be NULL, even the symbols with no + // string values should have an offset zero which points + // to an empty C-string + Host::SystemLog (Host::eSystemLogError, + "error: DSC unmapped local symbol[%u] has invalid string table offset 0x%x in %s, ignoring symbol\n", + entry_index, + nlist.n_strx, + module_sp->GetFileSpec().GetPath().c_str()); + continue; + } + if (symbol_name[0] == '\0') + symbol_name = NULL; + + const char *symbol_name_non_abi_mangled = NULL; + + SectionSP symbol_section; + uint32_t symbol_byte_size = 0; + bool add_nlist = true; + bool is_debug = ((nlist.n_type & N_STAB) != 0); + bool demangled_is_synthesized = false; + bool is_gsym = false; + bool set_value = true; + + assert (sym_idx < num_syms); + + sym[sym_idx].SetDebug (is_debug); + + if (is_debug) + { + switch (nlist.n_type) + { + case N_GSYM: + // global symbol: name,,NO_SECT,type,0 + // Sometimes the N_GSYM value contains the address. + + // FIXME: In the .o files, we have a GSYM and a debug symbol for all the ObjC data. They + // have the same address, but we want to ensure that we always find only the real symbol, + // 'cause we don't currently correctly attribute the GSYM one to the ObjCClass/Ivar/MetaClass + // symbol type. This is a temporary hack to make sure the ObjectiveC symbols get treated + // correctly. To do this right, we should coalesce all the GSYM & global symbols that have the + // same address. + + is_gsym = true; + sym[sym_idx].SetExternal(true); + + if (symbol_name && symbol_name[0] == '_' && symbol_name[1] == 'O') + { + llvm::StringRef symbol_name_ref(symbol_name); + if (symbol_name_ref.startswith(g_objc_v2_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_metaclass)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size(); + type = eSymbolTypeObjCMetaClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_ivar.size(); + type = eSymbolTypeObjCIVar; + demangled_is_synthesized = true; + } + } + else + { + if (nlist.n_value != 0) + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeData; + } + break; + + case N_FNAME: + // procedure name (f77 kludge): name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_FUN: + // procedure: name,,n_sect,linenumber,address + if (symbol_name) + { + type = eSymbolTypeCode; + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + + N_FUN_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx)); + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_FUN_indexes.push_back(sym_idx); + } + else + { + type = eSymbolTypeCompiler; + + if ( !N_FUN_indexes.empty() ) + { + // Copy the size of the function into the original STAB entry so we don't have + // to hunt for it later + symtab->SymbolAtIndex(N_FUN_indexes.back())->SetByteSize(nlist.n_value); + N_FUN_indexes.pop_back(); + // We don't really need the end function STAB as it contains the size which + // we already placed with the original symbol, so don't add it if we want a + // minimal symbol table + add_nlist = false; + } + } + break; + + case N_STSYM: + // static symbol: name,,n_sect,type,address + N_STSYM_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx)); + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + if (symbol_name && symbol_name[0]) + { + type = ObjectFile::GetSymbolTypeFromName(symbol_name+1, eSymbolTypeData); + } + break; + + case N_LCSYM: + // .lcomm symbol: name,,n_sect,type,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeCommonBlock; + break; + + case N_BNSYM: + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + // Skip these if we want minimal symbol tables + add_nlist = false; + break; + + case N_ENSYM: + // Set the size of the N_BNSYM to the terminating index of this N_ENSYM + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + // Skip these if we want minimal symbol tables + add_nlist = false; + break; + + case N_OPT: + // emitted with gcc2_compiled and in gcc source + type = eSymbolTypeCompiler; + break; + + case N_RSYM: + // register sym: name,,NO_SECT,type,register + type = eSymbolTypeVariable; + break; + + case N_SLINE: + // src line: 0,,n_sect,linenumber,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeLineEntry; + break; + + case N_SSYM: + // structure elt: name,,NO_SECT,type,struct_offset + type = eSymbolTypeVariableType; + break; + + case N_SO: + // source file name + type = eSymbolTypeSourceFile; + if (symbol_name == NULL) + { + add_nlist = false; + if (N_SO_index != UINT32_MAX) + { + // Set the size of the N_SO to the terminating index of this N_SO + // so that we can always skip the entire N_SO if we need to navigate + // more quickly at the source level when parsing STABS + symbol_ptr = symtab->SymbolAtIndex(N_SO_index); + symbol_ptr->SetByteSize(sym_idx); + symbol_ptr->SetSizeIsSibling(true); + } + N_NSYM_indexes.clear(); + N_INCL_indexes.clear(); + N_BRAC_indexes.clear(); + N_COMM_indexes.clear(); + N_FUN_indexes.clear(); + N_SO_index = UINT32_MAX; + } + else + { + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + const bool N_SO_has_full_path = symbol_name[0] == '/'; + if (N_SO_has_full_path) + { + if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms)) + { + // We have two consecutive N_SO entries where the first contains a directory + // and the second contains a full path. + sym[sym_idx - 1].GetMangled().SetValue(ConstString(symbol_name), false); + m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1; + add_nlist = false; + } + else + { + // This is the first entry in a N_SO that contains a directory or + // a full path to the source file + N_SO_index = sym_idx; + } + } + else if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms)) + { + // This is usually the second N_SO entry that contains just the filename, + // so here we combine it with the first one if we are minimizing the symbol table + const char *so_path = sym[sym_idx - 1].GetMangled().GetDemangledName().AsCString(); + if (so_path && so_path[0]) + { + std::string full_so_path (so_path); + const size_t double_slash_pos = full_so_path.find("//"); + if (double_slash_pos != std::string::npos) + { + // The linker has been generating bad N_SO entries with doubled up paths + // in the format "%s%s" where the first string in the DW_AT_comp_dir, + // and the second is the directory for the source file so you end up with + // a path that looks like "/tmp/src//tmp/src/" + FileSpec so_dir(so_path, false); + if (!so_dir.Exists()) + { + so_dir.SetFile(&full_so_path[double_slash_pos + 1], false); + if (so_dir.Exists()) + { + // Trim off the incorrect path + full_so_path.erase(0, double_slash_pos + 1); + } + } + } + if (*full_so_path.rbegin() != '/') + full_so_path += '/'; + full_so_path += symbol_name; + sym[sym_idx - 1].GetMangled().SetValue(ConstString(full_so_path.c_str()), false); + add_nlist = false; + m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1; + } + } + else + { + // This could be a relative path to a N_SO + N_SO_index = sym_idx; + } + } + break; + + case N_OSO: + // object file name: name,,0,0,st_mtime + type = eSymbolTypeObjectFile; + break; + + case N_LSYM: + // local sym: name,,NO_SECT,type,offset + type = eSymbolTypeLocal; + break; + + //---------------------------------------------------------------------- + // INCL scopes + //---------------------------------------------------------------------- + case N_BINCL: + // include file beginning: name,,NO_SECT,0,sum + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_INCL_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + break; + + case N_EINCL: + // include file end: name,,NO_SECT,0,0 + // Set the size of the N_BINCL to the terminating index of this N_EINCL + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if ( !N_INCL_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_INCL_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_INCL_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_SOL: + // #included file name: name,,n_sect,0,address + type = eSymbolTypeHeaderFile; + + // We currently don't use the header files on darwin + add_nlist = false; + break; + + case N_PARAMS: + // compiler parameters: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_VERSION: + // compiler version: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_OLEVEL: + // compiler -O level: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_PSYM: + // parameter: name,,NO_SECT,type,offset + type = eSymbolTypeVariable; + break; + + case N_ENTRY: + // alternate entry: name,,n_sect,linenumber,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeLineEntry; + break; + + //---------------------------------------------------------------------- + // Left and Right Braces + //---------------------------------------------------------------------- + case N_LBRAC: + // left bracket: 0,,NO_SECT,nesting level,address + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + N_BRAC_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + break; + + case N_RBRAC: + // right bracket: 0,,NO_SECT,nesting level,address + // Set the size of the N_LBRAC to the terminating index of this N_RBRAC + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + if ( !N_BRAC_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_BRAC_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_BRAC_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_EXCL: + // deleted include file: name,,NO_SECT,0,sum + type = eSymbolTypeHeaderFile; + break; + + //---------------------------------------------------------------------- + // COMM scopes + //---------------------------------------------------------------------- + case N_BCOMM: + // begin common: name,,NO_SECT,0,0 + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + type = eSymbolTypeScopeBegin; + N_COMM_indexes.push_back(sym_idx); + break; + + case N_ECOML: + // end common (local name): 0,,n_sect,0,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + // Fall through + + case N_ECOMM: + // end common: name,,n_sect,0,0 + // Set the size of the N_BCOMM to the terminating index of this N_ECOMM/N_ECOML + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if ( !N_COMM_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_COMM_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_COMM_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_LENG: + // second stab entry with length information + type = eSymbolTypeAdditional; + break; + + default: break; + } + } + else + { + //uint8_t n_pext = N_PEXT & nlist.n_type; + uint8_t n_type = N_TYPE & nlist.n_type; + sym[sym_idx].SetExternal((N_EXT & nlist.n_type) != 0); + + switch (n_type) + { + case N_INDR: + { + const char *reexport_name_cstr = strtab_data.PeekCStr(nlist.n_value); + if (reexport_name_cstr && reexport_name_cstr[0]) + { + type = eSymbolTypeReExported; + ConstString reexport_name(reexport_name_cstr + ((reexport_name_cstr[0] == '_') ? 1 : 0)); + sym[sym_idx].SetReExportedSymbolName(reexport_name); + set_value = false; + reexport_shlib_needs_fixup[sym_idx] = reexport_name; + indirect_symbol_names.insert(ConstString(symbol_name + ((symbol_name[0] == '_') ? 1 : 0))); + } + else + type = eSymbolTypeUndefined; + } + break; + + case N_UNDF: + if (symbol_name && symbol_name[0]) + { + ConstString undefined_name(symbol_name + ((symbol_name[0] == '_') ? 1 : 0)); + undefined_name_to_desc[undefined_name] = nlist.n_desc; + } + // Fall through + case N_PBUD: + type = eSymbolTypeUndefined; + break; + + case N_ABS: + type = eSymbolTypeAbsolute; + break; + + case N_SECT: + { + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + + if (symbol_section == NULL) + { + // TODO: warn about this? + add_nlist = false; + break; + } + + if (TEXT_eh_frame_sectID == nlist.n_sect) + { + type = eSymbolTypeException; + } + else + { + uint32_t section_type = symbol_section->Get() & SECTION_TYPE; + + switch (section_type) + { + case S_CSTRING_LITERALS: type = eSymbolTypeData; break; // section with only literal C strings + case S_4BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 4 byte literals + case S_8BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 8 byte literals + case S_LITERAL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only pointers to literals + case S_NON_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only non-lazy symbol pointers + case S_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only lazy symbol pointers + case S_SYMBOL_STUBS: type = eSymbolTypeTrampoline; break; // section with only symbol stubs, byte size of stub in the reserved2 field + case S_MOD_INIT_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for initialization + case S_MOD_TERM_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for termination + case S_INTERPOSING: type = eSymbolTypeTrampoline; break; // section with only pairs of function pointers for interposing + case S_16BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 16 byte literals + case S_DTRACE_DOF: type = eSymbolTypeInstrumentation; break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; + default: + switch (symbol_section->GetType()) + { + case lldb::eSectionTypeCode: + type = eSymbolTypeCode; + break; + case eSectionTypeData: + case eSectionTypeDataCString: // Inlined C string data + case eSectionTypeDataCStringPointers: // Pointers to C string data + case eSectionTypeDataSymbolAddress: // Address of a symbol in the symbol table + case eSectionTypeData4: + case eSectionTypeData8: + case eSectionTypeData16: + type = eSymbolTypeData; + break; + default: + break; + } + break; + } + + if (type == eSymbolTypeInvalid) + { + const char *symbol_sect_name = symbol_section->GetName().AsCString(); + if (symbol_section->IsDescendant (text_section_sp.get())) + { + if (symbol_section->IsClear(S_ATTR_PURE_INSTRUCTIONS | + S_ATTR_SELF_MODIFYING_CODE | + S_ATTR_SOME_INSTRUCTIONS)) + type = eSymbolTypeData; + else + type = eSymbolTypeCode; + } + else if (symbol_section->IsDescendant(data_section_sp.get()) || + symbol_section->IsDescendant(data_dirty_section_sp.get()) || + symbol_section->IsDescendant(data_const_section_sp.get())) + { + if (symbol_sect_name && ::strstr (symbol_sect_name, "__objc") == symbol_sect_name) + { + type = eSymbolTypeRuntime; + + if (symbol_name) + { + llvm::StringRef symbol_name_ref(symbol_name); + if (symbol_name_ref.startswith("_OBJC_")) + { + static const llvm::StringRef g_objc_v2_prefix_class ("_OBJC_CLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_metaclass ("_OBJC_METACLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_ivar ("_OBJC_IVAR_$_"); + if (symbol_name_ref.startswith(g_objc_v2_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_metaclass)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size(); + type = eSymbolTypeObjCMetaClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_ivar.size(); + type = eSymbolTypeObjCIVar; + demangled_is_synthesized = true; + } + } + } + } + else if (symbol_sect_name && ::strstr (symbol_sect_name, "__gcc_except_tab") == symbol_sect_name) + { + type = eSymbolTypeException; + } + else + { + type = eSymbolTypeData; + } + } + else if (symbol_sect_name && ::strstr (symbol_sect_name, "__IMPORT") == symbol_sect_name) + { + type = eSymbolTypeTrampoline; + } + else if (symbol_section->IsDescendant(objc_section_sp.get())) + { + type = eSymbolTypeRuntime; + if (symbol_name && symbol_name[0] == '.') + { + llvm::StringRef symbol_name_ref(symbol_name); + static const llvm::StringRef g_objc_v1_prefix_class (".objc_class_name_"); + if (symbol_name_ref.startswith(g_objc_v1_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name; + symbol_name = symbol_name + g_objc_v1_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + } + } + } + } + } + } + break; + } + } + + if (add_nlist) + { + uint64_t symbol_value = nlist.n_value; + if (symbol_name_non_abi_mangled) + { + sym[sym_idx].GetMangled().SetMangledName (ConstString(symbol_name_non_abi_mangled)); + sym[sym_idx].GetMangled().SetDemangledName (ConstString(symbol_name)); + } + else + { + bool symbol_name_is_mangled = false; + + if (symbol_name && symbol_name[0] == '_') + { + symbol_name_is_mangled = symbol_name[1] == '_'; + symbol_name++; // Skip the leading underscore + } + + if (symbol_name) + { + ConstString const_symbol_name(symbol_name); + sym[sym_idx].GetMangled().SetValue(const_symbol_name, symbol_name_is_mangled); + if (is_gsym && is_debug) + { + const char *gsym_name = sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled).GetCString(); + if (gsym_name) + N_GSYM_name_to_sym_idx[gsym_name] = sym_idx; + } + } + } + if (symbol_section) + { + const addr_t section_file_addr = symbol_section->GetFileAddress(); + if (symbol_byte_size == 0 && function_starts_count > 0) + { + addr_t symbol_lookup_file_addr = nlist.n_value; + // Do an exact address match for non-ARM addresses, else get the closest since + // the symbol might be a thumb symbol which has an address with bit zero set + FunctionStarts::Entry *func_start_entry = function_starts.FindEntry (symbol_lookup_file_addr, !is_arm); + if (is_arm && func_start_entry) + { + // Verify that the function start address is the symbol address (ARM) + // or the symbol address + 1 (thumb) + if (func_start_entry->addr != symbol_lookup_file_addr && + func_start_entry->addr != (symbol_lookup_file_addr + 1)) + { + // Not the right entry, NULL it out... + func_start_entry = NULL; + } + } + if (func_start_entry) + { + func_start_entry->data = true; + + addr_t symbol_file_addr = func_start_entry->addr; + uint32_t symbol_flags = 0; + if (is_arm) + { + if (symbol_file_addr & 1) + symbol_flags = MACHO_NLIST_ARM_SYMBOL_IS_THUMB; + symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + } + + const FunctionStarts::Entry *next_func_start_entry = function_starts.FindNextEntry (func_start_entry); + const addr_t section_end_file_addr = section_file_addr + symbol_section->GetByteSize(); + if (next_func_start_entry) + { + addr_t next_symbol_file_addr = next_func_start_entry->addr; + // Be sure the clear the Thumb address bit when we calculate the size + // from the current and next address + if (is_arm) + next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + symbol_byte_size = std::min<lldb::addr_t>(next_symbol_file_addr - symbol_file_addr, section_end_file_addr - symbol_file_addr); + } + else + { + symbol_byte_size = section_end_file_addr - symbol_file_addr; + } + } + } + symbol_value -= section_file_addr; + } + + if (is_debug == false) + { + if (type == eSymbolTypeCode) + { + // See if we can find a N_FUN entry for any code symbols. + // If we do find a match, and the name matches, then we + // can merge the two into just the function symbol to avoid + // duplicate entries in the symbol table + std::pair<ValueToSymbolIndexMap::const_iterator, ValueToSymbolIndexMap::const_iterator> range; + range = N_FUN_addr_to_sym_idx.equal_range(nlist.n_value); + if (range.first != range.second) + { + bool found_it = false; + for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos) + { + if (sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(Mangled::ePreferMangled)) + { + m_nlist_idx_to_sym_idx[nlist_idx] = pos->second; + // We just need the flags from the linker symbol, so put these flags + // into the N_FUN flags to avoid duplicate symbols in the symbol table + sym[pos->second].SetExternal(sym[sym_idx].IsExternal()); + sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc); + if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end()) + sym[pos->second].SetType (eSymbolTypeResolver); + sym[sym_idx].Clear(); + found_it = true; + break; + } + } + if (found_it) + continue; + } + else + { + if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end()) + type = eSymbolTypeResolver; + } + } + else if (type == eSymbolTypeData || + type == eSymbolTypeObjCClass || + type == eSymbolTypeObjCMetaClass || + type == eSymbolTypeObjCIVar ) + { + // See if we can find a N_STSYM entry for any data symbols. + // If we do find a match, and the name matches, then we + // can merge the two into just the Static symbol to avoid + // duplicate entries in the symbol table + std::pair<ValueToSymbolIndexMap::const_iterator, ValueToSymbolIndexMap::const_iterator> range; + range = N_STSYM_addr_to_sym_idx.equal_range(nlist.n_value); + if (range.first != range.second) + { + bool found_it = false; + for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos) + { + if (sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(Mangled::ePreferMangled)) + { + m_nlist_idx_to_sym_idx[nlist_idx] = pos->second; + // We just need the flags from the linker symbol, so put these flags + // into the N_STSYM flags to avoid duplicate symbols in the symbol table + sym[pos->second].SetExternal(sym[sym_idx].IsExternal()); + sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc); + sym[sym_idx].Clear(); + found_it = true; + break; + } + } + if (found_it) + continue; + } + else + { + const char *gsym_name = sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled).GetCString(); + if (gsym_name) + { + // Combine N_GSYM stab entries with the non stab symbol + ConstNameToSymbolIndexMap::const_iterator pos = N_GSYM_name_to_sym_idx.find(gsym_name); + if (pos != N_GSYM_name_to_sym_idx.end()) + { + const uint32_t GSYM_sym_idx = pos->second; + m_nlist_idx_to_sym_idx[nlist_idx] = GSYM_sym_idx; + // Copy the address, because often the N_GSYM address has an invalid address of zero + // when the global is a common symbol + sym[GSYM_sym_idx].GetAddressRef().SetSection (symbol_section); + sym[GSYM_sym_idx].GetAddressRef().SetOffset (symbol_value); + // We just need the flags from the linker symbol, so put these flags + // into the N_GSYM flags to avoid duplicate symbols in the symbol table + sym[GSYM_sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc); + sym[sym_idx].Clear(); + continue; + } + } + } + } + } + + sym[sym_idx].SetID (nlist_idx); + sym[sym_idx].SetType (type); + if (set_value) + { + sym[sym_idx].GetAddressRef().SetSection (symbol_section); + sym[sym_idx].GetAddressRef().SetOffset (symbol_value); + } + sym[sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc); + + if (symbol_byte_size > 0) + sym[sym_idx].SetByteSize(symbol_byte_size); + + if (demangled_is_synthesized) + sym[sym_idx].SetDemangledNameIsSynthesized(true); + ++sym_idx; + } + else + { + sym[sym_idx].Clear(); + } + + } + ///////////////////////////// + } + break; // No more entries to consider + } + } + + for (const auto &pos :reexport_shlib_needs_fixup) + { + const auto undef_pos = undefined_name_to_desc.find(pos.second); + if (undef_pos != undefined_name_to_desc.end()) + { + const uint8_t dylib_ordinal = llvm::MachO::GET_LIBRARY_ORDINAL(undef_pos->second); + if (dylib_ordinal > 0 && dylib_ordinal < dylib_files.GetSize()) + sym[pos.first].SetReExportedSymbolSharedLibrary(dylib_files.GetFileSpecAtIndex(dylib_ordinal-1)); + } + } + } + } + } + } + } + + // Must reset this in case it was mutated above! + nlist_data_offset = 0; +#endif + + if (nlist_data.GetByteSize() > 0) + { + + // If the sym array was not created while parsing the DSC unmapped + // symbols, create it now. + if (sym == NULL) + { + sym = symtab->Resize (symtab_load_command.nsyms + m_dysymtab.nindirectsyms); + num_syms = symtab->GetNumSymbols(); + } + + if (unmapped_local_symbols_found) + { + assert(m_dysymtab.ilocalsym == 0); + nlist_data_offset += (m_dysymtab.nlocalsym * nlist_byte_size); + nlist_idx = m_dysymtab.nlocalsym; + } + else + { + nlist_idx = 0; + } + + typedef std::map<ConstString, uint16_t> UndefinedNameToDescMap; + typedef std::map<uint32_t, ConstString> SymbolIndexToName; + UndefinedNameToDescMap undefined_name_to_desc; + SymbolIndexToName reexport_shlib_needs_fixup; + for (; nlist_idx < symtab_load_command.nsyms; ++nlist_idx) + { + struct nlist_64 nlist; + if (!nlist_data.ValidOffsetForDataOfSize(nlist_data_offset, nlist_byte_size)) + break; + + nlist.n_strx = nlist_data.GetU32_unchecked(&nlist_data_offset); + nlist.n_type = nlist_data.GetU8_unchecked (&nlist_data_offset); + nlist.n_sect = nlist_data.GetU8_unchecked (&nlist_data_offset); + nlist.n_desc = nlist_data.GetU16_unchecked (&nlist_data_offset); + nlist.n_value = nlist_data.GetAddress_unchecked (&nlist_data_offset); + + SymbolType type = eSymbolTypeInvalid; + const char *symbol_name = NULL; + + if (have_strtab_data) + { + symbol_name = strtab_data.PeekCStr(nlist.n_strx); + + if (symbol_name == NULL) + { + // No symbol should be NULL, even the symbols with no + // string values should have an offset zero which points + // to an empty C-string + Host::SystemLog (Host::eSystemLogError, + "error: symbol[%u] has invalid string table offset 0x%x in %s, ignoring symbol\n", + nlist_idx, + nlist.n_strx, + module_sp->GetFileSpec().GetPath().c_str()); + continue; + } + if (symbol_name[0] == '\0') + symbol_name = NULL; + } + else + { + const addr_t str_addr = strtab_addr + nlist.n_strx; + Error str_error; + if (process->ReadCStringFromMemory(str_addr, memory_symbol_name, str_error)) + symbol_name = memory_symbol_name.c_str(); + } + const char *symbol_name_non_abi_mangled = NULL; + + SectionSP symbol_section; + lldb::addr_t symbol_byte_size = 0; + bool add_nlist = true; + bool is_gsym = false; + bool is_debug = ((nlist.n_type & N_STAB) != 0); + bool demangled_is_synthesized = false; + bool set_value = true; + assert (sym_idx < num_syms); + + sym[sym_idx].SetDebug (is_debug); + + if (is_debug) + { + switch (nlist.n_type) + { + case N_GSYM: + // global symbol: name,,NO_SECT,type,0 + // Sometimes the N_GSYM value contains the address. + + // FIXME: In the .o files, we have a GSYM and a debug symbol for all the ObjC data. They + // have the same address, but we want to ensure that we always find only the real symbol, + // 'cause we don't currently correctly attribute the GSYM one to the ObjCClass/Ivar/MetaClass + // symbol type. This is a temporary hack to make sure the ObjectiveC symbols get treated + // correctly. To do this right, we should coalesce all the GSYM & global symbols that have the + // same address. + is_gsym = true; + sym[sym_idx].SetExternal(true); + + if (symbol_name && symbol_name[0] == '_' && symbol_name[1] == 'O') + { + llvm::StringRef symbol_name_ref(symbol_name); + if (symbol_name_ref.startswith(g_objc_v2_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_metaclass)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size(); + type = eSymbolTypeObjCMetaClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_ivar.size(); + type = eSymbolTypeObjCIVar; + demangled_is_synthesized = true; + } + } + else + { + if (nlist.n_value != 0) + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeData; + } + break; + + case N_FNAME: + // procedure name (f77 kludge): name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_FUN: + // procedure: name,,n_sect,linenumber,address + if (symbol_name) + { + type = eSymbolTypeCode; + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + + N_FUN_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx)); + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_FUN_indexes.push_back(sym_idx); + } + else + { + type = eSymbolTypeCompiler; + + if ( !N_FUN_indexes.empty() ) + { + // Copy the size of the function into the original STAB entry so we don't have + // to hunt for it later + symtab->SymbolAtIndex(N_FUN_indexes.back())->SetByteSize(nlist.n_value); + N_FUN_indexes.pop_back(); + // We don't really need the end function STAB as it contains the size which + // we already placed with the original symbol, so don't add it if we want a + // minimal symbol table + add_nlist = false; + } + } + break; + + case N_STSYM: + // static symbol: name,,n_sect,type,address + N_STSYM_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx)); + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + if (symbol_name && symbol_name[0]) + { + type = ObjectFile::GetSymbolTypeFromName(symbol_name+1, eSymbolTypeData); + } + break; + + case N_LCSYM: + // .lcomm symbol: name,,n_sect,type,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeCommonBlock; + break; + + case N_BNSYM: + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + // Skip these if we want minimal symbol tables + add_nlist = false; + break; + + case N_ENSYM: + // Set the size of the N_BNSYM to the terminating index of this N_ENSYM + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + // Skip these if we want minimal symbol tables + add_nlist = false; + break; + + + case N_OPT: + // emitted with gcc2_compiled and in gcc source + type = eSymbolTypeCompiler; + break; + + case N_RSYM: + // register sym: name,,NO_SECT,type,register + type = eSymbolTypeVariable; + break; + + case N_SLINE: + // src line: 0,,n_sect,linenumber,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeLineEntry; + break; + + case N_SSYM: + // structure elt: name,,NO_SECT,type,struct_offset + type = eSymbolTypeVariableType; + break; + + case N_SO: + // source file name + type = eSymbolTypeSourceFile; + if (symbol_name == NULL) + { + add_nlist = false; + if (N_SO_index != UINT32_MAX) + { + // Set the size of the N_SO to the terminating index of this N_SO + // so that we can always skip the entire N_SO if we need to navigate + // more quickly at the source level when parsing STABS + symbol_ptr = symtab->SymbolAtIndex(N_SO_index); + symbol_ptr->SetByteSize(sym_idx); + symbol_ptr->SetSizeIsSibling(true); + } + N_NSYM_indexes.clear(); + N_INCL_indexes.clear(); + N_BRAC_indexes.clear(); + N_COMM_indexes.clear(); + N_FUN_indexes.clear(); + N_SO_index = UINT32_MAX; + } + else + { + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + const bool N_SO_has_full_path = symbol_name[0] == '/'; + if (N_SO_has_full_path) + { + if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms)) + { + // We have two consecutive N_SO entries where the first contains a directory + // and the second contains a full path. + sym[sym_idx - 1].GetMangled().SetValue(ConstString(symbol_name), false); + m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1; + add_nlist = false; + } + else + { + // This is the first entry in a N_SO that contains a directory or + // a full path to the source file + N_SO_index = sym_idx; + } + } + else if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms)) + { + // This is usually the second N_SO entry that contains just the filename, + // so here we combine it with the first one if we are minimizing the symbol table + const char *so_path = sym[sym_idx - 1].GetMangled().GetDemangledName(lldb::eLanguageTypeUnknown).AsCString(); + if (so_path && so_path[0]) + { + std::string full_so_path (so_path); + const size_t double_slash_pos = full_so_path.find("//"); + if (double_slash_pos != std::string::npos) + { + // The linker has been generating bad N_SO entries with doubled up paths + // in the format "%s%s" where the first string in the DW_AT_comp_dir, + // and the second is the directory for the source file so you end up with + // a path that looks like "/tmp/src//tmp/src/" + FileSpec so_dir(so_path, false); + if (!so_dir.Exists()) + { + so_dir.SetFile(&full_so_path[double_slash_pos + 1], false); + if (so_dir.Exists()) + { + // Trim off the incorrect path + full_so_path.erase(0, double_slash_pos + 1); + } + } + } + if (*full_so_path.rbegin() != '/') + full_so_path += '/'; + full_so_path += symbol_name; + sym[sym_idx - 1].GetMangled().SetValue(ConstString(full_so_path.c_str()), false); + add_nlist = false; + m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1; + } + } + else + { + // This could be a relative path to a N_SO + N_SO_index = sym_idx; + } + } + break; + + case N_OSO: + // object file name: name,,0,0,st_mtime + type = eSymbolTypeObjectFile; + break; + + case N_LSYM: + // local sym: name,,NO_SECT,type,offset + type = eSymbolTypeLocal; + break; + + //---------------------------------------------------------------------- + // INCL scopes + //---------------------------------------------------------------------- + case N_BINCL: + // include file beginning: name,,NO_SECT,0,sum + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_INCL_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + break; + + case N_EINCL: + // include file end: name,,NO_SECT,0,0 + // Set the size of the N_BINCL to the terminating index of this N_EINCL + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if ( !N_INCL_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_INCL_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_INCL_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_SOL: + // #included file name: name,,n_sect,0,address + type = eSymbolTypeHeaderFile; + + // We currently don't use the header files on darwin + add_nlist = false; + break; + + case N_PARAMS: + // compiler parameters: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_VERSION: + // compiler version: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_OLEVEL: + // compiler -O level: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_PSYM: + // parameter: name,,NO_SECT,type,offset + type = eSymbolTypeVariable; + break; + + case N_ENTRY: + // alternate entry: name,,n_sect,linenumber,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeLineEntry; + break; + + //---------------------------------------------------------------------- + // Left and Right Braces + //---------------------------------------------------------------------- + case N_LBRAC: + // left bracket: 0,,NO_SECT,nesting level,address + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + N_BRAC_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + break; + + case N_RBRAC: + // right bracket: 0,,NO_SECT,nesting level,address + // Set the size of the N_LBRAC to the terminating index of this N_RBRAC + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + if ( !N_BRAC_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_BRAC_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_BRAC_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_EXCL: + // deleted include file: name,,NO_SECT,0,sum + type = eSymbolTypeHeaderFile; + break; + + //---------------------------------------------------------------------- + // COMM scopes + //---------------------------------------------------------------------- + case N_BCOMM: + // begin common: name,,NO_SECT,0,0 + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + type = eSymbolTypeScopeBegin; + N_COMM_indexes.push_back(sym_idx); + break; + + case N_ECOML: + // end common (local name): 0,,n_sect,0,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + // Fall through + + case N_ECOMM: + // end common: name,,n_sect,0,0 + // Set the size of the N_BCOMM to the terminating index of this N_ECOMM/N_ECOML + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if ( !N_COMM_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_COMM_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_COMM_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_LENG: + // second stab entry with length information + type = eSymbolTypeAdditional; + break; + + default: break; + } + } + else + { + //uint8_t n_pext = N_PEXT & nlist.n_type; + uint8_t n_type = N_TYPE & nlist.n_type; + sym[sym_idx].SetExternal((N_EXT & nlist.n_type) != 0); + + switch (n_type) + { + case N_INDR: + { + const char *reexport_name_cstr = strtab_data.PeekCStr(nlist.n_value); + if (reexport_name_cstr && reexport_name_cstr[0]) + { + type = eSymbolTypeReExported; + ConstString reexport_name(reexport_name_cstr + ((reexport_name_cstr[0] == '_') ? 1 : 0)); + sym[sym_idx].SetReExportedSymbolName(reexport_name); + set_value = false; + reexport_shlib_needs_fixup[sym_idx] = reexport_name; + indirect_symbol_names.insert(ConstString(symbol_name + ((symbol_name[0] == '_') ? 1 : 0))); + } + else + type = eSymbolTypeUndefined; + } + break; + + case N_UNDF: + if (symbol_name && symbol_name[0]) + { + ConstString undefined_name(symbol_name + ((symbol_name[0] == '_') ? 1 : 0)); + undefined_name_to_desc[undefined_name] = nlist.n_desc; + } + // Fall through + case N_PBUD: + type = eSymbolTypeUndefined; + break; + + case N_ABS: + type = eSymbolTypeAbsolute; + break; + + case N_SECT: + { + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + + if (!symbol_section) + { + // TODO: warn about this? + add_nlist = false; + break; + } + + if (TEXT_eh_frame_sectID == nlist.n_sect) + { + type = eSymbolTypeException; + } + else + { + uint32_t section_type = symbol_section->Get() & SECTION_TYPE; + + switch (section_type) + { + case S_CSTRING_LITERALS: type = eSymbolTypeData; break; // section with only literal C strings + case S_4BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 4 byte literals + case S_8BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 8 byte literals + case S_LITERAL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only pointers to literals + case S_NON_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only non-lazy symbol pointers + case S_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only lazy symbol pointers + case S_SYMBOL_STUBS: type = eSymbolTypeTrampoline; break; // section with only symbol stubs, byte size of stub in the reserved2 field + case S_MOD_INIT_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for initialization + case S_MOD_TERM_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for termination + case S_INTERPOSING: type = eSymbolTypeTrampoline; break; // section with only pairs of function pointers for interposing + case S_16BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 16 byte literals + case S_DTRACE_DOF: type = eSymbolTypeInstrumentation; break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; + default: + switch (symbol_section->GetType()) + { + case lldb::eSectionTypeCode: + type = eSymbolTypeCode; + break; + case eSectionTypeData: + case eSectionTypeDataCString: // Inlined C string data + case eSectionTypeDataCStringPointers: // Pointers to C string data + case eSectionTypeDataSymbolAddress: // Address of a symbol in the symbol table + case eSectionTypeData4: + case eSectionTypeData8: + case eSectionTypeData16: + type = eSymbolTypeData; + break; + default: + break; + } + break; + } + + if (type == eSymbolTypeInvalid) + { + const char *symbol_sect_name = symbol_section->GetName().AsCString(); + if (symbol_section->IsDescendant (text_section_sp.get())) + { + if (symbol_section->IsClear(S_ATTR_PURE_INSTRUCTIONS | + S_ATTR_SELF_MODIFYING_CODE | + S_ATTR_SOME_INSTRUCTIONS)) + type = eSymbolTypeData; + else + type = eSymbolTypeCode; + } + else + if (symbol_section->IsDescendant(data_section_sp.get()) || + symbol_section->IsDescendant(data_dirty_section_sp.get()) || + symbol_section->IsDescendant(data_const_section_sp.get())) + { + if (symbol_sect_name && ::strstr (symbol_sect_name, "__objc") == symbol_sect_name) + { + type = eSymbolTypeRuntime; + + if (symbol_name) + { + llvm::StringRef symbol_name_ref(symbol_name); + if (symbol_name_ref.startswith("_OBJC_")) + { + static const llvm::StringRef g_objc_v2_prefix_class ("_OBJC_CLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_metaclass ("_OBJC_METACLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_ivar ("_OBJC_IVAR_$_"); + if (symbol_name_ref.startswith(g_objc_v2_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_metaclass)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size(); + type = eSymbolTypeObjCMetaClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_ivar.size(); + type = eSymbolTypeObjCIVar; + demangled_is_synthesized = true; + } + } + } + } + else + if (symbol_sect_name && ::strstr (symbol_sect_name, "__gcc_except_tab") == symbol_sect_name) + { + type = eSymbolTypeException; + } + else + { + type = eSymbolTypeData; + } + } + else + if (symbol_sect_name && ::strstr (symbol_sect_name, "__IMPORT") == symbol_sect_name) + { + type = eSymbolTypeTrampoline; + } + else + if (symbol_section->IsDescendant(objc_section_sp.get())) + { + type = eSymbolTypeRuntime; + if (symbol_name && symbol_name[0] == '.') + { + llvm::StringRef symbol_name_ref(symbol_name); + static const llvm::StringRef g_objc_v1_prefix_class (".objc_class_name_"); + if (symbol_name_ref.startswith(g_objc_v1_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name; + symbol_name = symbol_name + g_objc_v1_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + } + } + } + } + } + } + break; + } + } + + if (add_nlist) + { + uint64_t symbol_value = nlist.n_value; + + if (symbol_name_non_abi_mangled) + { + sym[sym_idx].GetMangled().SetMangledName (ConstString(symbol_name_non_abi_mangled)); + sym[sym_idx].GetMangled().SetDemangledName (ConstString(symbol_name)); + } + else + { + bool symbol_name_is_mangled = false; + + if (symbol_name && symbol_name[0] == '_') + { + symbol_name_is_mangled = symbol_name[1] == '_'; + symbol_name++; // Skip the leading underscore + } + + if (symbol_name) + { + ConstString const_symbol_name(symbol_name); + sym[sym_idx].GetMangled().SetValue(const_symbol_name, symbol_name_is_mangled); + } + } + + if (is_gsym) + { + const char *gsym_name = sym[sym_idx].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled).GetCString(); + if (gsym_name) + N_GSYM_name_to_sym_idx[gsym_name] = sym_idx; + } + + if (symbol_section) + { + const addr_t section_file_addr = symbol_section->GetFileAddress(); + if (symbol_byte_size == 0 && function_starts_count > 0) + { + addr_t symbol_lookup_file_addr = nlist.n_value; + // Do an exact address match for non-ARM addresses, else get the closest since + // the symbol might be a thumb symbol which has an address with bit zero set + FunctionStarts::Entry *func_start_entry = function_starts.FindEntry (symbol_lookup_file_addr, !is_arm); + if (is_arm && func_start_entry) + { + // Verify that the function start address is the symbol address (ARM) + // or the symbol address + 1 (thumb) + if (func_start_entry->addr != symbol_lookup_file_addr && + func_start_entry->addr != (symbol_lookup_file_addr + 1)) + { + // Not the right entry, NULL it out... + func_start_entry = NULL; + } + } + if (func_start_entry) + { + func_start_entry->data = true; + + addr_t symbol_file_addr = func_start_entry->addr; + if (is_arm) + symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + + const FunctionStarts::Entry *next_func_start_entry = function_starts.FindNextEntry (func_start_entry); + const addr_t section_end_file_addr = section_file_addr + symbol_section->GetByteSize(); + if (next_func_start_entry) + { + addr_t next_symbol_file_addr = next_func_start_entry->addr; + // Be sure the clear the Thumb address bit when we calculate the size + // from the current and next address + if (is_arm) + next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + symbol_byte_size = std::min<lldb::addr_t>(next_symbol_file_addr - symbol_file_addr, section_end_file_addr - symbol_file_addr); + } + else + { + symbol_byte_size = section_end_file_addr - symbol_file_addr; + } + } + } + symbol_value -= section_file_addr; + } + + if (is_debug == false) + { + if (type == eSymbolTypeCode) + { + // See if we can find a N_FUN entry for any code symbols. + // If we do find a match, and the name matches, then we + // can merge the two into just the function symbol to avoid + // duplicate entries in the symbol table + std::pair<ValueToSymbolIndexMap::const_iterator, ValueToSymbolIndexMap::const_iterator> range; + range = N_FUN_addr_to_sym_idx.equal_range(nlist.n_value); + if (range.first != range.second) + { + bool found_it = false; + for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos) + { + if (sym[sym_idx].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled)) + { + m_nlist_idx_to_sym_idx[nlist_idx] = pos->second; + // We just need the flags from the linker symbol, so put these flags + // into the N_FUN flags to avoid duplicate symbols in the symbol table + sym[pos->second].SetExternal(sym[sym_idx].IsExternal()); + sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc); + if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end()) + sym[pos->second].SetType (eSymbolTypeResolver); + sym[sym_idx].Clear(); + found_it = true; + break; + } + } + if (found_it) + continue; + } + else + { + if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end()) + type = eSymbolTypeResolver; + } + } + else if (type == eSymbolTypeData || + type == eSymbolTypeObjCClass || + type == eSymbolTypeObjCMetaClass || + type == eSymbolTypeObjCIVar ) + { + // See if we can find a N_STSYM entry for any data symbols. + // If we do find a match, and the name matches, then we + // can merge the two into just the Static symbol to avoid + // duplicate entries in the symbol table + std::pair<ValueToSymbolIndexMap::const_iterator, ValueToSymbolIndexMap::const_iterator> range; + range = N_STSYM_addr_to_sym_idx.equal_range(nlist.n_value); + if (range.first != range.second) + { + bool found_it = false; + for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos) + { + if (sym[sym_idx].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled)) + { + m_nlist_idx_to_sym_idx[nlist_idx] = pos->second; + // We just need the flags from the linker symbol, so put these flags + // into the N_STSYM flags to avoid duplicate symbols in the symbol table + sym[pos->second].SetExternal(sym[sym_idx].IsExternal()); + sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc); + sym[sym_idx].Clear(); + found_it = true; + break; + } + } + if (found_it) + continue; + } + else + { + // Combine N_GSYM stab entries with the non stab symbol + const char *gsym_name = sym[sym_idx].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled).GetCString(); + if (gsym_name) + { + ConstNameToSymbolIndexMap::const_iterator pos = N_GSYM_name_to_sym_idx.find(gsym_name); + if (pos != N_GSYM_name_to_sym_idx.end()) + { + const uint32_t GSYM_sym_idx = pos->second; + m_nlist_idx_to_sym_idx[nlist_idx] = GSYM_sym_idx; + // Copy the address, because often the N_GSYM address has an invalid address of zero + // when the global is a common symbol + sym[GSYM_sym_idx].GetAddressRef().SetSection (symbol_section); + sym[GSYM_sym_idx].GetAddressRef().SetOffset (symbol_value); + // We just need the flags from the linker symbol, so put these flags + // into the N_GSYM flags to avoid duplicate symbols in the symbol table + sym[GSYM_sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc); + sym[sym_idx].Clear(); + continue; + } + } + } + } + } + + sym[sym_idx].SetID (nlist_idx); + sym[sym_idx].SetType (type); + if (set_value) + { + sym[sym_idx].GetAddressRef().SetSection (symbol_section); + sym[sym_idx].GetAddressRef().SetOffset (symbol_value); + } + sym[sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc); + + if (symbol_byte_size > 0) + sym[sym_idx].SetByteSize(symbol_byte_size); + + if (demangled_is_synthesized) + sym[sym_idx].SetDemangledNameIsSynthesized(true); + + ++sym_idx; + } + else + { + sym[sym_idx].Clear(); + } + } + + for (const auto &pos :reexport_shlib_needs_fixup) + { + const auto undef_pos = undefined_name_to_desc.find(pos.second); + if (undef_pos != undefined_name_to_desc.end()) + { + const uint8_t dylib_ordinal = llvm::MachO::GET_LIBRARY_ORDINAL(undef_pos->second); + if (dylib_ordinal > 0 && dylib_ordinal < dylib_files.GetSize()) + sym[pos.first].SetReExportedSymbolSharedLibrary(dylib_files.GetFileSpecAtIndex(dylib_ordinal-1)); + } + } + } + + uint32_t synthetic_sym_id = symtab_load_command.nsyms; + + if (function_starts_count > 0) + { + char synthetic_function_symbol[PATH_MAX]; + uint32_t num_synthetic_function_symbols = 0; + for (i=0; i<function_starts_count; ++i) + { + if (function_starts.GetEntryRef (i).data == false) + ++num_synthetic_function_symbols; + } + + if (num_synthetic_function_symbols > 0) + { + if (num_syms < sym_idx + num_synthetic_function_symbols) + { + num_syms = sym_idx + num_synthetic_function_symbols; + sym = symtab->Resize (num_syms); + } + uint32_t synthetic_function_symbol_idx = 0; + for (i=0; i<function_starts_count; ++i) + { + const FunctionStarts::Entry *func_start_entry = function_starts.GetEntryAtIndex (i); + if (func_start_entry->data == false) + { + addr_t symbol_file_addr = func_start_entry->addr; + uint32_t symbol_flags = 0; + if (is_arm) + { + if (symbol_file_addr & 1) + symbol_flags = MACHO_NLIST_ARM_SYMBOL_IS_THUMB; + symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + } + Address symbol_addr; + if (module_sp->ResolveFileAddress (symbol_file_addr, symbol_addr)) + { + SectionSP symbol_section (symbol_addr.GetSection()); + uint32_t symbol_byte_size = 0; + if (symbol_section) + { + const addr_t section_file_addr = symbol_section->GetFileAddress(); + const FunctionStarts::Entry *next_func_start_entry = function_starts.FindNextEntry (func_start_entry); + const addr_t section_end_file_addr = section_file_addr + symbol_section->GetByteSize(); + if (next_func_start_entry) + { + addr_t next_symbol_file_addr = next_func_start_entry->addr; + if (is_arm) + next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + symbol_byte_size = std::min<lldb::addr_t>(next_symbol_file_addr - symbol_file_addr, section_end_file_addr - symbol_file_addr); + } + else + { + symbol_byte_size = section_end_file_addr - symbol_file_addr; + } + snprintf (synthetic_function_symbol, + sizeof(synthetic_function_symbol), + "___lldb_unnamed_function%u$$%s", + ++synthetic_function_symbol_idx, + module_sp->GetFileSpec().GetFilename().GetCString()); + sym[sym_idx].SetID (synthetic_sym_id++); + sym[sym_idx].GetMangled().SetDemangledName(ConstString(synthetic_function_symbol)); + sym[sym_idx].SetType (eSymbolTypeCode); + sym[sym_idx].SetIsSynthetic (true); + sym[sym_idx].GetAddressRef() = symbol_addr; + if (symbol_flags) + sym[sym_idx].SetFlags (symbol_flags); + if (symbol_byte_size) + sym[sym_idx].SetByteSize (symbol_byte_size); + ++sym_idx; + } + } + } + } + } + } + + // Trim our symbols down to just what we ended up with after + // removing any symbols. + if (sym_idx < num_syms) + { + num_syms = sym_idx; + sym = symtab->Resize (num_syms); + } + + // Now synthesize indirect symbols + if (m_dysymtab.nindirectsyms != 0) + { + if (indirect_symbol_index_data.GetByteSize()) + { + NListIndexToSymbolIndexMap::const_iterator end_index_pos = m_nlist_idx_to_sym_idx.end(); + + for (uint32_t sect_idx = 1; sect_idx < m_mach_sections.size(); ++sect_idx) + { + if ((m_mach_sections[sect_idx].flags & SECTION_TYPE) == S_SYMBOL_STUBS) + { + uint32_t symbol_stub_byte_size = m_mach_sections[sect_idx].reserved2; + if (symbol_stub_byte_size == 0) + continue; + + const uint32_t num_symbol_stubs = m_mach_sections[sect_idx].size / symbol_stub_byte_size; + + if (num_symbol_stubs == 0) + continue; + + const uint32_t symbol_stub_index_offset = m_mach_sections[sect_idx].reserved1; + for (uint32_t stub_idx = 0; stub_idx < num_symbol_stubs; ++stub_idx) + { + const uint32_t symbol_stub_index = symbol_stub_index_offset + stub_idx; + const lldb::addr_t symbol_stub_addr = m_mach_sections[sect_idx].addr + (stub_idx * symbol_stub_byte_size); + lldb::offset_t symbol_stub_offset = symbol_stub_index * 4; + if (indirect_symbol_index_data.ValidOffsetForDataOfSize(symbol_stub_offset, 4)) + { + const uint32_t stub_sym_id = indirect_symbol_index_data.GetU32 (&symbol_stub_offset); + if (stub_sym_id & (INDIRECT_SYMBOL_ABS | INDIRECT_SYMBOL_LOCAL)) + continue; + + NListIndexToSymbolIndexMap::const_iterator index_pos = m_nlist_idx_to_sym_idx.find (stub_sym_id); + Symbol *stub_symbol = NULL; + if (index_pos != end_index_pos) + { + // We have a remapping from the original nlist index to + // a current symbol index, so just look this up by index + stub_symbol = symtab->SymbolAtIndex (index_pos->second); + } + else + { + // We need to lookup a symbol using the original nlist + // symbol index since this index is coming from the + // S_SYMBOL_STUBS + stub_symbol = symtab->FindSymbolByID (stub_sym_id); + } + + if (stub_symbol) + { + Address so_addr(symbol_stub_addr, section_list); + + if (stub_symbol->GetType() == eSymbolTypeUndefined) + { + // Change the external symbol into a trampoline that makes sense + // These symbols were N_UNDF N_EXT, and are useless to us, so we + // can re-use them so we don't have to make up a synthetic symbol + // for no good reason. + if (resolver_addresses.find(symbol_stub_addr) == resolver_addresses.end()) + stub_symbol->SetType (eSymbolTypeTrampoline); + else + stub_symbol->SetType (eSymbolTypeResolver); + stub_symbol->SetExternal (false); + stub_symbol->GetAddressRef() = so_addr; + stub_symbol->SetByteSize (symbol_stub_byte_size); + } + else + { + // Make a synthetic symbol to describe the trampoline stub + Mangled stub_symbol_mangled_name(stub_symbol->GetMangled()); + if (sym_idx >= num_syms) + { + sym = symtab->Resize (++num_syms); + stub_symbol = NULL; // this pointer no longer valid + } + sym[sym_idx].SetID (synthetic_sym_id++); + sym[sym_idx].GetMangled() = stub_symbol_mangled_name; + if (resolver_addresses.find(symbol_stub_addr) == resolver_addresses.end()) + sym[sym_idx].SetType (eSymbolTypeTrampoline); + else + sym[sym_idx].SetType (eSymbolTypeResolver); + sym[sym_idx].SetIsSynthetic (true); + sym[sym_idx].GetAddressRef() = so_addr; + sym[sym_idx].SetByteSize (symbol_stub_byte_size); + ++sym_idx; + } + } + else + { + if (log) + log->Warning ("symbol stub referencing symbol table symbol %u that isn't in our minimal symbol table, fix this!!!", stub_sym_id); + } + } + } + } + } + } + } + + if (!trie_entries.empty()) + { + for (const auto &e : trie_entries) + { + if (e.entry.import_name) + { + // Only add indirect symbols from the Trie entries if we + // didn't have a N_INDR nlist entry for this already + if (indirect_symbol_names.find(e.entry.name) == indirect_symbol_names.end()) + { + // Make a synthetic symbol to describe re-exported symbol. + if (sym_idx >= num_syms) + sym = symtab->Resize (++num_syms); + sym[sym_idx].SetID (synthetic_sym_id++); + sym[sym_idx].GetMangled() = Mangled(e.entry.name); + sym[sym_idx].SetType (eSymbolTypeReExported); + sym[sym_idx].SetIsSynthetic (true); + sym[sym_idx].SetReExportedSymbolName(e.entry.import_name); + if (e.entry.other > 0 && e.entry.other <= dylib_files.GetSize()) + { + sym[sym_idx].SetReExportedSymbolSharedLibrary(dylib_files.GetFileSpecAtIndex(e.entry.other-1)); + } + ++sym_idx; + } + } + } + } + +// StreamFile s(stdout, false); +// s.Printf ("Symbol table before CalculateSymbolSizes():\n"); +// symtab->Dump(&s, NULL, eSortOrderNone); + // Set symbol byte sizes correctly since mach-o nlist entries don't have sizes + symtab->CalculateSymbolSizes(); + +// s.Printf ("Symbol table after CalculateSymbolSizes():\n"); +// symtab->Dump(&s, NULL, eSortOrderNone); + + return symtab->GetNumSymbols(); + } + return 0; +} + +void +ObjectFileMachO::Dump (Stream *s) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + s->Printf("%p: ", static_cast<void*>(this)); + s->Indent(); + if (m_header.magic == MH_MAGIC_64 || m_header.magic == MH_CIGAM_64) + s->PutCString("ObjectFileMachO64"); + else + s->PutCString("ObjectFileMachO32"); + + ArchSpec header_arch; + GetArchitecture(header_arch); + + *s << ", file = '" << m_file << "', arch = " << header_arch.GetArchitectureName() << "\n"; + + SectionList *sections = GetSectionList(); + if (sections) + sections->Dump(s, NULL, true, UINT32_MAX); + + if (m_symtab_ap.get()) + m_symtab_ap->Dump(s, NULL, eSortOrderNone); + } +} + +bool +ObjectFileMachO::GetUUID (const llvm::MachO::mach_header &header, + const lldb_private::DataExtractor &data, + lldb::offset_t lc_offset, + lldb_private::UUID& uuid) +{ + uint32_t i; + struct uuid_command load_cmd; + + lldb::offset_t offset = lc_offset; + for (i=0; i<header.ncmds; ++i) + { + const lldb::offset_t cmd_offset = offset; + if (data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + if (load_cmd.cmd == LC_UUID) + { + const uint8_t *uuid_bytes = data.PeekData(offset, 16); + + if (uuid_bytes) + { + // OpenCL on Mac OS X uses the same UUID for each of its object files. + // We pretend these object files have no UUID to prevent crashing. + + const uint8_t opencl_uuid[] = { 0x8c, 0x8e, 0xb3, 0x9b, + 0x3b, 0xa8, + 0x4b, 0x16, + 0xb6, 0xa4, + 0x27, 0x63, 0xbb, 0x14, 0xf0, 0x0d }; + + if (!memcmp(uuid_bytes, opencl_uuid, 16)) + return false; + + uuid.SetBytes (uuid_bytes); + return true; + } + return false; + } + offset = cmd_offset + load_cmd.cmdsize; + } + return false; +} + +bool +ObjectFileMachO::GetArchitecture (const llvm::MachO::mach_header &header, + const lldb_private::DataExtractor &data, + lldb::offset_t lc_offset, + ArchSpec &arch) +{ + arch.SetArchitecture (eArchTypeMachO, header.cputype, header.cpusubtype); + + if (arch.IsValid()) + { + llvm::Triple &triple = arch.GetTriple(); + + // Set OS to an unspecified unknown or a "*" so it can match any OS + triple.setOS(llvm::Triple::UnknownOS); + triple.setOSName(llvm::StringRef()); + + if (header.filetype == MH_PRELOAD) + { + // Set vendor to an unspecified unknown or a "*" so it can match any vendor + triple.setVendor(llvm::Triple::UnknownVendor); + triple.setVendorName(llvm::StringRef()); + return true; + } + else + { + struct load_command load_cmd; + + lldb::offset_t offset = lc_offset; + for (uint32_t i=0; i<header.ncmds; ++i) + { + const lldb::offset_t cmd_offset = offset; + if (data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + switch (load_cmd.cmd) + { + case llvm::MachO::LC_VERSION_MIN_IPHONEOS: + triple.setOS (llvm::Triple::IOS); + return true; + + case llvm::MachO::LC_VERSION_MIN_MACOSX: + triple.setOS (llvm::Triple::MacOSX); + return true; + + case llvm::MachO::LC_VERSION_MIN_TVOS: + triple.setOS (llvm::Triple::TvOS); + return true; + + case llvm::MachO::LC_VERSION_MIN_WATCHOS: + triple.setOS (llvm::Triple::WatchOS); + return true; + + default: + break; + } + + offset = cmd_offset + load_cmd.cmdsize; + } + + if (header.filetype != MH_KEXT_BUNDLE) + { + // We didn't find a LC_VERSION_MIN load command and this isn't a KEXT + // so lets not say our Vendor is Apple, leave it as an unspecified unknown + triple.setVendor(llvm::Triple::UnknownVendor); + triple.setVendorName(llvm::StringRef()); + } + } + } + return arch.IsValid(); +} + +bool +ObjectFileMachO::GetUUID (lldb_private::UUID* uuid) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + return GetUUID (m_header, m_data, offset, *uuid); + } + return false; +} + +uint32_t +ObjectFileMachO::GetDependentModules (FileSpecList& files) +{ + uint32_t count = 0; + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + struct load_command load_cmd; + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + std::vector<std::string> rpath_paths; + std::vector<std::string> rpath_relative_paths; + const bool resolve_path = false; // Don't resolve the dependent file paths since they may not reside on this system + uint32_t i; + for (i=0; i<m_header.ncmds; ++i) + { + const uint32_t cmd_offset = offset; + if (m_data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + switch (load_cmd.cmd) + { + case LC_RPATH: + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_DYLINKER: + case LC_LOADFVMLIB: + case LC_LOAD_UPWARD_DYLIB: + { + uint32_t name_offset = cmd_offset + m_data.GetU32(&offset); + const char *path = m_data.PeekCStr(name_offset); + if (path) + { + if (load_cmd.cmd == LC_RPATH) + rpath_paths.push_back(path); + else + { + if (path[0] == '@') + { + if (strncmp(path, "@rpath", strlen("@rpath")) == 0) + rpath_relative_paths.push_back(path + strlen("@rpath")); + } + else + { + FileSpec file_spec(path, resolve_path); + if (files.AppendIfUnique(file_spec)) + count++; + } + } + } + } + break; + + default: + break; + } + offset = cmd_offset + load_cmd.cmdsize; + } + + if (!rpath_paths.empty()) + { + // Fixup all LC_RPATH values to be absolute paths + FileSpec this_file_spec(m_file); + this_file_spec.ResolvePath(); + std::string loader_path("@loader_path"); + std::string executable_path("@executable_path"); + for (auto &rpath : rpath_paths) + { + if (rpath.find(loader_path) == 0) + { + rpath.erase(0, loader_path.size()); + rpath.insert(0, this_file_spec.GetDirectory().GetCString()); + } + else if (rpath.find(executable_path) == 0) + { + rpath.erase(0, executable_path.size()); + rpath.insert(0, this_file_spec.GetDirectory().GetCString()); + } + } + + for (const auto &rpath_relative_path : rpath_relative_paths) + { + for (const auto &rpath : rpath_paths) + { + std::string path = rpath; + path += rpath_relative_path; + // It is OK to resolve this path because we must find a file on + // disk for us to accept it anyway if it is rpath relative. + FileSpec file_spec(path, true); + // Remove any redundant parts of the path (like "../foo") since + // LC_RPATH values often contain "..". + file_spec.NormalizePath (); + if (file_spec.Exists() && files.AppendIfUnique(file_spec)) + { + count++; + break; + } + } + } + } + } + return count; +} + +lldb_private::Address +ObjectFileMachO::GetEntryPointAddress () +{ + // If the object file is not an executable it can't hold the entry point. m_entry_point_address + // is initialized to an invalid address, so we can just return that. + // If m_entry_point_address is valid it means we've found it already, so return the cached value. + + if (!IsExecutable() || m_entry_point_address.IsValid()) + return m_entry_point_address; + + // Otherwise, look for the UnixThread or Thread command. The data for the Thread command is given in + // /usr/include/mach-o.h, but it is basically: + // + // uint32_t flavor - this is the flavor argument you would pass to thread_get_state + // uint32_t count - this is the count of longs in the thread state data + // struct XXX_thread_state state - this is the structure from <machine/thread_status.h> corresponding to the flavor. + // <repeat this trio> + // + // So we just keep reading the various register flavors till we find the GPR one, then read the PC out of there. + // FIXME: We will need to have a "RegisterContext data provider" class at some point that can get all the registers + // out of data in this form & attach them to a given thread. That should underlie the MacOS X User process plugin, + // and we'll also need it for the MacOS X Core File process plugin. When we have that we can also use it here. + // + // For now we hard-code the offsets and flavors we need: + // + // + + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + struct load_command load_cmd; + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + lldb::addr_t start_address = LLDB_INVALID_ADDRESS; + bool done = false; + + for (i=0; i<m_header.ncmds; ++i) + { + const lldb::offset_t cmd_offset = offset; + if (m_data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + switch (load_cmd.cmd) + { + case LC_UNIXTHREAD: + case LC_THREAD: + { + while (offset < cmd_offset + load_cmd.cmdsize) + { + uint32_t flavor = m_data.GetU32(&offset); + uint32_t count = m_data.GetU32(&offset); + if (count == 0) + { + // We've gotten off somehow, log and exit; + return m_entry_point_address; + } + + switch (m_header.cputype) + { + case llvm::MachO::CPU_TYPE_ARM: + if (flavor == 1) // ARM_THREAD_STATE from mach/arm/thread_status.h + { + offset += 60; // This is the offset of pc in the GPR thread state data structure. + start_address = m_data.GetU32(&offset); + done = true; + } + break; + case llvm::MachO::CPU_TYPE_ARM64: + if (flavor == 6) // ARM_THREAD_STATE64 from mach/arm/thread_status.h + { + offset += 256; // This is the offset of pc in the GPR thread state data structure. + start_address = m_data.GetU64(&offset); + done = true; + } + break; + case llvm::MachO::CPU_TYPE_I386: + if (flavor == 1) // x86_THREAD_STATE32 from mach/i386/thread_status.h + { + offset += 40; // This is the offset of eip in the GPR thread state data structure. + start_address = m_data.GetU32(&offset); + done = true; + } + break; + case llvm::MachO::CPU_TYPE_X86_64: + if (flavor == 4) // x86_THREAD_STATE64 from mach/i386/thread_status.h + { + offset += 16 * 8; // This is the offset of rip in the GPR thread state data structure. + start_address = m_data.GetU64(&offset); + done = true; + } + break; + default: + return m_entry_point_address; + } + // Haven't found the GPR flavor yet, skip over the data for this flavor: + if (done) + break; + offset += count * 4; + } + } + break; + case LC_MAIN: + { + ConstString text_segment_name ("__TEXT"); + uint64_t entryoffset = m_data.GetU64(&offset); + SectionSP text_segment_sp = GetSectionList()->FindSectionByName(text_segment_name); + if (text_segment_sp) + { + done = true; + start_address = text_segment_sp->GetFileAddress() + entryoffset; + } + } + + default: + break; + } + if (done) + break; + + // Go to the next load command: + offset = cmd_offset + load_cmd.cmdsize; + } + + if (start_address != LLDB_INVALID_ADDRESS) + { + // We got the start address from the load commands, so now resolve that address in the sections + // of this ObjectFile: + if (!m_entry_point_address.ResolveAddressUsingFileSections (start_address, GetSectionList())) + { + m_entry_point_address.Clear(); + } + } + else + { + // We couldn't read the UnixThread load command - maybe it wasn't there. As a fallback look for the + // "start" symbol in the main executable. + + ModuleSP module_sp (GetModule()); + + if (module_sp) + { + SymbolContextList contexts; + SymbolContext context; + if (module_sp->FindSymbolsWithNameAndType(ConstString ("start"), eSymbolTypeCode, contexts)) + { + if (contexts.GetContextAtIndex(0, context)) + m_entry_point_address = context.symbol->GetAddress(); + } + } + } + } + + return m_entry_point_address; +} + +lldb_private::Address +ObjectFileMachO::GetHeaderAddress () +{ + lldb_private::Address header_addr; + SectionList *section_list = GetSectionList(); + if (section_list) + { + SectionSP text_segment_sp (section_list->FindSectionByName (GetSegmentNameTEXT())); + if (text_segment_sp) + { + header_addr.SetSection (text_segment_sp); + header_addr.SetOffset (0); + } + } + return header_addr; +} + +uint32_t +ObjectFileMachO::GetNumThreadContexts () +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (!m_thread_context_offsets_valid) + { + m_thread_context_offsets_valid = true; + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + FileRangeArray::Entry file_range; + thread_command thread_cmd; + for (uint32_t i=0; i<m_header.ncmds; ++i) + { + const uint32_t cmd_offset = offset; + if (m_data.GetU32(&offset, &thread_cmd, 2) == NULL) + break; + + if (thread_cmd.cmd == LC_THREAD) + { + file_range.SetRangeBase (offset); + file_range.SetByteSize (thread_cmd.cmdsize - 8); + m_thread_context_offsets.Append (file_range); + } + offset = cmd_offset + thread_cmd.cmdsize; + } + } + } + return m_thread_context_offsets.GetSize(); +} + +lldb::RegisterContextSP +ObjectFileMachO::GetThreadContextAtIndex (uint32_t idx, lldb_private::Thread &thread) +{ + lldb::RegisterContextSP reg_ctx_sp; + + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (!m_thread_context_offsets_valid) + GetNumThreadContexts (); + + const FileRangeArray::Entry *thread_context_file_range = m_thread_context_offsets.GetEntryAtIndex (idx); + if (thread_context_file_range) + { + + DataExtractor data (m_data, + thread_context_file_range->GetRangeBase(), + thread_context_file_range->GetByteSize()); + + switch (m_header.cputype) + { + case llvm::MachO::CPU_TYPE_ARM64: + reg_ctx_sp.reset (new RegisterContextDarwin_arm64_Mach (thread, data)); + break; + + case llvm::MachO::CPU_TYPE_ARM: + reg_ctx_sp.reset (new RegisterContextDarwin_arm_Mach (thread, data)); + break; + + case llvm::MachO::CPU_TYPE_I386: + reg_ctx_sp.reset (new RegisterContextDarwin_i386_Mach (thread, data)); + break; + + case llvm::MachO::CPU_TYPE_X86_64: + reg_ctx_sp.reset (new RegisterContextDarwin_x86_64_Mach (thread, data)); + break; + } + } + } + return reg_ctx_sp; +} + +ObjectFile::Type +ObjectFileMachO::CalculateType() +{ + switch (m_header.filetype) + { + case MH_OBJECT: // 0x1u + if (GetAddressByteSize () == 4) + { + // 32 bit kexts are just object files, but they do have a valid + // UUID load command. + UUID uuid; + if (GetUUID(&uuid)) + { + // this checking for the UUID load command is not enough + // we could eventually look for the symbol named + // "OSKextGetCurrentIdentifier" as this is required of kexts + if (m_strata == eStrataInvalid) + m_strata = eStrataKernel; + return eTypeSharedLibrary; + } + } + return eTypeObjectFile; + + case MH_EXECUTE: return eTypeExecutable; // 0x2u + case MH_FVMLIB: return eTypeSharedLibrary; // 0x3u + case MH_CORE: return eTypeCoreFile; // 0x4u + case MH_PRELOAD: return eTypeSharedLibrary; // 0x5u + case MH_DYLIB: return eTypeSharedLibrary; // 0x6u + case MH_DYLINKER: return eTypeDynamicLinker; // 0x7u + case MH_BUNDLE: return eTypeSharedLibrary; // 0x8u + case MH_DYLIB_STUB: return eTypeStubLibrary; // 0x9u + case MH_DSYM: return eTypeDebugInfo; // 0xAu + case MH_KEXT_BUNDLE: return eTypeSharedLibrary; // 0xBu + default: + break; + } + return eTypeUnknown; +} + +ObjectFile::Strata +ObjectFileMachO::CalculateStrata() +{ + switch (m_header.filetype) + { + case MH_OBJECT: // 0x1u + { + // 32 bit kexts are just object files, but they do have a valid + // UUID load command. + UUID uuid; + if (GetUUID(&uuid)) + { + // this checking for the UUID load command is not enough + // we could eventually look for the symbol named + // "OSKextGetCurrentIdentifier" as this is required of kexts + if (m_type == eTypeInvalid) + m_type = eTypeSharedLibrary; + + return eStrataKernel; + } + } + return eStrataUnknown; + + case MH_EXECUTE: // 0x2u + // Check for the MH_DYLDLINK bit in the flags + if (m_header.flags & MH_DYLDLINK) + { + return eStrataUser; + } + else + { + SectionList *section_list = GetSectionList(); + if (section_list) + { + static ConstString g_kld_section_name ("__KLD"); + if (section_list->FindSectionByName(g_kld_section_name)) + return eStrataKernel; + } + } + return eStrataRawImage; + + case MH_FVMLIB: return eStrataUser; // 0x3u + case MH_CORE: return eStrataUnknown; // 0x4u + case MH_PRELOAD: return eStrataRawImage; // 0x5u + case MH_DYLIB: return eStrataUser; // 0x6u + case MH_DYLINKER: return eStrataUser; // 0x7u + case MH_BUNDLE: return eStrataUser; // 0x8u + case MH_DYLIB_STUB: return eStrataUser; // 0x9u + case MH_DSYM: return eStrataUnknown; // 0xAu + case MH_KEXT_BUNDLE: return eStrataKernel; // 0xBu + default: + break; + } + return eStrataUnknown; +} + +uint32_t +ObjectFileMachO::GetVersion (uint32_t *versions, uint32_t num_versions) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + struct dylib_command load_cmd; + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t version_cmd = 0; + uint64_t version = 0; + uint32_t i; + for (i=0; i<m_header.ncmds; ++i) + { + const lldb::offset_t cmd_offset = offset; + if (m_data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + if (load_cmd.cmd == LC_ID_DYLIB) + { + if (version_cmd == 0) + { + version_cmd = load_cmd.cmd; + if (m_data.GetU32(&offset, &load_cmd.dylib, 4) == NULL) + break; + version = load_cmd.dylib.current_version; + } + break; // Break for now unless there is another more complete version + // number load command in the future. + } + offset = cmd_offset + load_cmd.cmdsize; + } + + if (version_cmd == LC_ID_DYLIB) + { + if (versions != NULL && num_versions > 0) + { + if (num_versions > 0) + versions[0] = (version & 0xFFFF0000ull) >> 16; + if (num_versions > 1) + versions[1] = (version & 0x0000FF00ull) >> 8; + if (num_versions > 2) + versions[2] = (version & 0x000000FFull); + // Fill in an remaining version numbers with invalid values + for (i=3; i<num_versions; ++i) + versions[i] = UINT32_MAX; + } + // The LC_ID_DYLIB load command has a version with 3 version numbers + // in it, so always return 3 + return 3; + } + } + return false; +} + +bool +ObjectFileMachO::GetArchitecture (ArchSpec &arch) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + return GetArchitecture (m_header, m_data, MachHeaderSizeFromMagic(m_header.magic), arch); + } + return false; +} + +UUID +ObjectFileMachO::GetProcessSharedCacheUUID (Process *process) +{ + UUID uuid; + if (process) + { + addr_t all_image_infos = process->GetImageInfoAddress(); + + // The address returned by GetImageInfoAddress may be the address of dyld (don't want) + // or it may be the address of the dyld_all_image_infos structure (want). The first four + // bytes will be either the version field (all_image_infos) or a Mach-O file magic constant. + // Version 13 and higher of dyld_all_image_infos is required to get the sharedCacheUUID field. + + Error err; + uint32_t version_or_magic = process->ReadUnsignedIntegerFromMemory (all_image_infos, 4, -1, err); + if (version_or_magic != static_cast<uint32_t>(-1) + && version_or_magic != MH_MAGIC + && version_or_magic != MH_CIGAM + && version_or_magic != MH_MAGIC_64 + && version_or_magic != MH_CIGAM_64 + && version_or_magic >= 13) + { + addr_t sharedCacheUUID_address = LLDB_INVALID_ADDRESS; + int wordsize = process->GetAddressByteSize(); + if (wordsize == 8) + { + sharedCacheUUID_address = all_image_infos + 160; // sharedCacheUUID <mach-o/dyld_images.h> + } + if (wordsize == 4) + { + sharedCacheUUID_address = all_image_infos + 84; // sharedCacheUUID <mach-o/dyld_images.h> + } + if (sharedCacheUUID_address != LLDB_INVALID_ADDRESS) + { + uuid_t shared_cache_uuid; + if (process->ReadMemory (sharedCacheUUID_address, shared_cache_uuid, sizeof (uuid_t), err) == sizeof (uuid_t)) + { + uuid.SetBytes (shared_cache_uuid); + } + } + } + } + return uuid; +} + +UUID +ObjectFileMachO::GetLLDBSharedCacheUUID () +{ + UUID uuid; +#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) + uint8_t *(*dyld_get_all_image_infos)(void); + dyld_get_all_image_infos = (uint8_t*(*)()) dlsym (RTLD_DEFAULT, "_dyld_get_all_image_infos"); + if (dyld_get_all_image_infos) + { + uint8_t *dyld_all_image_infos_address = dyld_get_all_image_infos(); + if (dyld_all_image_infos_address) + { + uint32_t *version = (uint32_t*) dyld_all_image_infos_address; // version <mach-o/dyld_images.h> + if (*version >= 13) + { + uuid_t *sharedCacheUUID_address = 0; + int wordsize = sizeof (uint8_t *); + if (wordsize == 8) + { + sharedCacheUUID_address = (uuid_t*) ((uint8_t*) dyld_all_image_infos_address + 160); // sharedCacheUUID <mach-o/dyld_images.h> + } + else + { + sharedCacheUUID_address = (uuid_t*) ((uint8_t*) dyld_all_image_infos_address + 84); // sharedCacheUUID <mach-o/dyld_images.h> + } + uuid.SetBytes (sharedCacheUUID_address); + } + } + } +#endif + return uuid; +} + +uint32_t +ObjectFileMachO::GetMinimumOSVersion (uint32_t *versions, uint32_t num_versions) +{ + if (m_min_os_versions.empty()) + { + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + bool success = false; + for (uint32_t i=0; success == false && i < m_header.ncmds; ++i) + { + const lldb::offset_t load_cmd_offset = offset; + + version_min_command lc; + if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL) + break; + if (lc.cmd == llvm::MachO::LC_VERSION_MIN_MACOSX + || lc.cmd == llvm::MachO::LC_VERSION_MIN_IPHONEOS + || lc.cmd == llvm::MachO::LC_VERSION_MIN_TVOS + || lc.cmd == llvm::MachO::LC_VERSION_MIN_WATCHOS) + { + if (m_data.GetU32 (&offset, &lc.version, (sizeof(lc) / sizeof(uint32_t)) - 2)) + { + const uint32_t xxxx = lc.version >> 16; + const uint32_t yy = (lc.version >> 8) & 0xffu; + const uint32_t zz = lc.version & 0xffu; + if (xxxx) + { + m_min_os_versions.push_back(xxxx); + m_min_os_versions.push_back(yy); + m_min_os_versions.push_back(zz); + } + success = true; + } + } + offset = load_cmd_offset + lc.cmdsize; + } + + if (success == false) + { + // Push an invalid value so we don't keep trying to + m_min_os_versions.push_back(UINT32_MAX); + } + } + + if (m_min_os_versions.size() > 1 || m_min_os_versions[0] != UINT32_MAX) + { + if (versions != NULL && num_versions > 0) + { + for (size_t i=0; i<num_versions; ++i) + { + if (i < m_min_os_versions.size()) + versions[i] = m_min_os_versions[i]; + else + versions[i] = 0; + } + } + return m_min_os_versions.size(); + } + // Call the superclasses version that will empty out the data + return ObjectFile::GetMinimumOSVersion (versions, num_versions); +} + +uint32_t +ObjectFileMachO::GetSDKVersion(uint32_t *versions, uint32_t num_versions) +{ + if (m_sdk_versions.empty()) + { + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + bool success = false; + for (uint32_t i=0; success == false && i < m_header.ncmds; ++i) + { + const lldb::offset_t load_cmd_offset = offset; + + version_min_command lc; + if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL) + break; + if (lc.cmd == llvm::MachO::LC_VERSION_MIN_MACOSX + || lc.cmd == llvm::MachO::LC_VERSION_MIN_IPHONEOS + || lc.cmd == llvm::MachO::LC_VERSION_MIN_TVOS + || lc.cmd == llvm::MachO::LC_VERSION_MIN_WATCHOS) + { + if (m_data.GetU32 (&offset, &lc.version, (sizeof(lc) / sizeof(uint32_t)) - 2)) + { + const uint32_t xxxx = lc.sdk >> 16; + const uint32_t yy = (lc.sdk >> 8) & 0xffu; + const uint32_t zz = lc.sdk & 0xffu; + if (xxxx) + { + m_sdk_versions.push_back(xxxx); + m_sdk_versions.push_back(yy); + m_sdk_versions.push_back(zz); + } + success = true; + } + } + offset = load_cmd_offset + lc.cmdsize; + } + + if (success == false) + { + // Push an invalid value so we don't keep trying to + m_sdk_versions.push_back(UINT32_MAX); + } + } + + if (m_sdk_versions.size() > 1 || m_sdk_versions[0] != UINT32_MAX) + { + if (versions != NULL && num_versions > 0) + { + for (size_t i=0; i<num_versions; ++i) + { + if (i < m_sdk_versions.size()) + versions[i] = m_sdk_versions[i]; + else + versions[i] = 0; + } + } + return m_sdk_versions.size(); + } + // Call the superclasses version that will empty out the data + return ObjectFile::GetSDKVersion (versions, num_versions); +} + +bool +ObjectFileMachO::GetIsDynamicLinkEditor() +{ + return m_header.filetype == llvm::MachO::MH_DYLINKER; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ObjectFileMachO::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectFileMachO::GetPluginVersion() +{ + return 1; +} + +Section * +ObjectFileMachO::GetMachHeaderSection() +{ + // Find the first address of the mach header which is the first non-zero + // file sized section whose file offset is zero. This is the base file address + // of the mach-o file which can be subtracted from the vmaddr of the other + // segments found in memory and added to the load address + ModuleSP module_sp = GetModule(); + if (module_sp) + { + SectionList *section_list = GetSectionList (); + if (section_list) + { + lldb::addr_t mach_base_file_addr = LLDB_INVALID_ADDRESS; + const size_t num_sections = section_list->GetSize(); + + for (size_t sect_idx = 0; + sect_idx < num_sections && mach_base_file_addr == LLDB_INVALID_ADDRESS; + ++sect_idx) + { + Section *section = section_list->GetSectionAtIndex (sect_idx).get(); + if (section && + section->GetFileSize() > 0 && + section->GetFileOffset() == 0 && + section->IsThreadSpecific() == false && + module_sp.get() == section->GetModule().get()) + { + return section; + } + } + } + } + return nullptr; +} + +lldb::addr_t +ObjectFileMachO::CalculateSectionLoadAddressForMemoryImage(lldb::addr_t mach_header_load_address, const Section *mach_header_section, const Section *section) +{ + ModuleSP module_sp = GetModule(); + if (module_sp && mach_header_section && section && mach_header_load_address != LLDB_INVALID_ADDRESS) + { + lldb::addr_t mach_header_file_addr = mach_header_section->GetFileAddress(); + if (mach_header_file_addr != LLDB_INVALID_ADDRESS) + { + if (section && + section->GetFileSize() > 0 && + section->IsThreadSpecific() == false && + module_sp.get() == section->GetModule().get()) + { + // Ignore __LINKEDIT and __DWARF segments + if (section->GetName() == GetSegmentNameLINKEDIT()) + { + // Only map __LINKEDIT if we have an in memory image and this isn't + // a kernel binary like a kext or mach_kernel. + const bool is_memory_image = (bool)m_process_wp.lock(); + const Strata strata = GetStrata(); + if (is_memory_image == false || strata == eStrataKernel) + return LLDB_INVALID_ADDRESS; + } + return section->GetFileAddress() - mach_header_file_addr + mach_header_load_address; + } + } + } + return LLDB_INVALID_ADDRESS; +} + +bool +ObjectFileMachO::SetLoadAddress (Target &target, + lldb::addr_t value, + bool value_is_offset) +{ + ModuleSP module_sp = GetModule(); + if (module_sp) + { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList (); + if (section_list) + { + const size_t num_sections = section_list->GetSize(); + + if (value_is_offset) + { + // "value" is an offset to apply to each top level segment + for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + // Iterate through the object file sections to find all + // of the sections that size on disk (to avoid __PAGEZERO) + // and load them + SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); + if (section_sp && + section_sp->GetFileSize() > 0 && + section_sp->IsThreadSpecific() == false && + module_sp.get() == section_sp->GetModule().get()) + { + // Ignore __LINKEDIT and __DWARF segments + if (section_sp->GetName() == GetSegmentNameLINKEDIT()) + { + // Only map __LINKEDIT if we have an in memory image and this isn't + // a kernel binary like a kext or mach_kernel. + const bool is_memory_image = (bool)m_process_wp.lock(); + const Strata strata = GetStrata(); + if (is_memory_image == false || strata == eStrataKernel) + continue; + } + if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + } + else + { + // "value" is the new base address of the mach_header, adjust each + // section accordingly + + Section *mach_header_section = GetMachHeaderSection(); + if (mach_header_section) + { + for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); + + lldb::addr_t section_load_addr = CalculateSectionLoadAddressForMemoryImage(value, mach_header_section, section_sp.get()); + if (section_load_addr != LLDB_INVALID_ADDRESS) + { + if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_load_addr)) + ++num_loaded_sections; + } + } + } + } + } + return num_loaded_sections > 0; + } + return false; +} + +bool +ObjectFileMachO::SaveCore (const lldb::ProcessSP &process_sp, + const FileSpec &outfile, + Error &error) +{ + if (process_sp) + { + Target &target = process_sp->GetTarget(); + const ArchSpec target_arch = target.GetArchitecture(); + const llvm::Triple &target_triple = target_arch.GetTriple(); + if (target_triple.getVendor() == llvm::Triple::Apple && + (target_triple.getOS() == llvm::Triple::MacOSX + || target_triple.getOS() == llvm::Triple::IOS + || target_triple.getOS() == llvm::Triple::WatchOS + || target_triple.getOS() == llvm::Triple::TvOS)) + { + bool make_core = false; + switch (target_arch.GetMachine()) + { + case llvm::Triple::aarch64: + case llvm::Triple::arm: + case llvm::Triple::thumb: + case llvm::Triple::x86: + case llvm::Triple::x86_64: + make_core = true; + break; + default: + error.SetErrorStringWithFormat ("unsupported core architecture: %s", target_triple.str().c_str()); + break; + } + + if (make_core) + { + std::vector<segment_command_64> segment_load_commands; +// uint32_t range_info_idx = 0; + MemoryRegionInfo range_info; + Error range_error = process_sp->GetMemoryRegionInfo(0, range_info); + const uint32_t addr_byte_size = target_arch.GetAddressByteSize(); + const ByteOrder byte_order = target_arch.GetByteOrder(); + if (range_error.Success()) + { + while (range_info.GetRange().GetRangeBase() != LLDB_INVALID_ADDRESS) + { + const addr_t addr = range_info.GetRange().GetRangeBase(); + const addr_t size = range_info.GetRange().GetByteSize(); + + if (size == 0) + break; + + // Calculate correct protections + uint32_t prot = 0; + if (range_info.GetReadable() == MemoryRegionInfo::eYes) + prot |= VM_PROT_READ; + if (range_info.GetWritable() == MemoryRegionInfo::eYes) + prot |= VM_PROT_WRITE; + if (range_info.GetExecutable() == MemoryRegionInfo::eYes) + prot |= VM_PROT_EXECUTE; + +// printf ("[%3u] [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") %c%c%c\n", +// range_info_idx, +// addr, +// size, +// (prot & VM_PROT_READ ) ? 'r' : '-', +// (prot & VM_PROT_WRITE ) ? 'w' : '-', +// (prot & VM_PROT_EXECUTE) ? 'x' : '-'); + + if (prot != 0) + { + uint32_t cmd_type = LC_SEGMENT_64; + uint32_t segment_size = sizeof (segment_command_64); + if (addr_byte_size == 4) + { + cmd_type = LC_SEGMENT; + segment_size = sizeof (segment_command); + } + segment_command_64 segment = { + cmd_type, // uint32_t cmd; + segment_size, // uint32_t cmdsize; + {0}, // char segname[16]; + addr, // uint64_t vmaddr; // uint32_t for 32-bit Mach-O + size, // uint64_t vmsize; // uint32_t for 32-bit Mach-O + 0, // uint64_t fileoff; // uint32_t for 32-bit Mach-O + size, // uint64_t filesize; // uint32_t for 32-bit Mach-O + prot, // uint32_t maxprot; + prot, // uint32_t initprot; + 0, // uint32_t nsects; + 0 }; // uint32_t flags; + segment_load_commands.push_back(segment); + } + else + { + // No protections and a size of 1 used to be returned from old + // debugservers when we asked about a region that was past the + // last memory region and it indicates the end... + if (size == 1) + break; + } + + range_error = process_sp->GetMemoryRegionInfo(range_info.GetRange().GetRangeEnd(), range_info); + if (range_error.Fail()) + break; + } + + StreamString buffer (Stream::eBinary, + addr_byte_size, + byte_order); + + mach_header_64 mach_header; + if (addr_byte_size == 8) + { + mach_header.magic = MH_MAGIC_64; + } + else + { + mach_header.magic = MH_MAGIC; + } + mach_header.cputype = target_arch.GetMachOCPUType(); + mach_header.cpusubtype = target_arch.GetMachOCPUSubType(); + mach_header.filetype = MH_CORE; + mach_header.ncmds = segment_load_commands.size(); + mach_header.flags = 0; + mach_header.reserved = 0; + ThreadList &thread_list = process_sp->GetThreadList(); + const uint32_t num_threads = thread_list.GetSize(); + + // Make an array of LC_THREAD data items. Each one contains + // the contents of the LC_THREAD load command. The data doesn't + // contain the load command + load command size, we will + // add the load command and load command size as we emit the data. + std::vector<StreamString> LC_THREAD_datas(num_threads); + for (auto &LC_THREAD_data : LC_THREAD_datas) + { + LC_THREAD_data.GetFlags().Set(Stream::eBinary); + LC_THREAD_data.SetAddressByteSize(addr_byte_size); + LC_THREAD_data.SetByteOrder(byte_order); + } + for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) + { + ThreadSP thread_sp (thread_list.GetThreadAtIndex(thread_idx)); + if (thread_sp) + { + switch (mach_header.cputype) + { + case llvm::MachO::CPU_TYPE_ARM64: + RegisterContextDarwin_arm64_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]); + break; + + case llvm::MachO::CPU_TYPE_ARM: + RegisterContextDarwin_arm_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]); + break; + + case llvm::MachO::CPU_TYPE_I386: + RegisterContextDarwin_i386_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]); + break; + + case llvm::MachO::CPU_TYPE_X86_64: + RegisterContextDarwin_x86_64_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]); + break; + } + + } + } + + // The size of the load command is the size of the segments... + if (addr_byte_size == 8) + { + mach_header.sizeofcmds = segment_load_commands.size() * sizeof (struct segment_command_64); + } + else + { + mach_header.sizeofcmds = segment_load_commands.size() * sizeof (struct segment_command); + } + + // and the size of all LC_THREAD load command + for (const auto &LC_THREAD_data : LC_THREAD_datas) + { + ++mach_header.ncmds; + mach_header.sizeofcmds += 8 + LC_THREAD_data.GetSize(); + } + + printf ("mach_header: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", + mach_header.magic, + mach_header.cputype, + mach_header.cpusubtype, + mach_header.filetype, + mach_header.ncmds, + mach_header.sizeofcmds, + mach_header.flags, + mach_header.reserved); + + // Write the mach header + buffer.PutHex32(mach_header.magic); + buffer.PutHex32(mach_header.cputype); + buffer.PutHex32(mach_header.cpusubtype); + buffer.PutHex32(mach_header.filetype); + buffer.PutHex32(mach_header.ncmds); + buffer.PutHex32(mach_header.sizeofcmds); + buffer.PutHex32(mach_header.flags); + if (addr_byte_size == 8) + { + buffer.PutHex32(mach_header.reserved); + } + + // Skip the mach header and all load commands and align to the next + // 0x1000 byte boundary + addr_t file_offset = buffer.GetSize() + mach_header.sizeofcmds; + if (file_offset & 0x00000fff) + { + file_offset += 0x00001000ull; + file_offset &= (~0x00001000ull + 1); + } + + for (auto &segment : segment_load_commands) + { + segment.fileoff = file_offset; + file_offset += segment.filesize; + } + + // Write out all of the LC_THREAD load commands + for (const auto &LC_THREAD_data : LC_THREAD_datas) + { + const size_t LC_THREAD_data_size = LC_THREAD_data.GetSize(); + buffer.PutHex32(LC_THREAD); + buffer.PutHex32(8 + LC_THREAD_data_size); // cmd + cmdsize + data + buffer.Write(LC_THREAD_data.GetData(), LC_THREAD_data_size); + } + + // Write out all of the segment load commands + for (const auto &segment : segment_load_commands) + { + printf ("0x%8.8x 0x%8.8x [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") [0x%16.16" PRIx64 " 0x%16.16" PRIx64 ") 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x]\n", + segment.cmd, + segment.cmdsize, + segment.vmaddr, + segment.vmaddr + segment.vmsize, + segment.fileoff, + segment.filesize, + segment.maxprot, + segment.initprot, + segment.nsects, + segment.flags); + + buffer.PutHex32(segment.cmd); + buffer.PutHex32(segment.cmdsize); + buffer.PutRawBytes(segment.segname, sizeof(segment.segname)); + if (addr_byte_size == 8) + { + buffer.PutHex64(segment.vmaddr); + buffer.PutHex64(segment.vmsize); + buffer.PutHex64(segment.fileoff); + buffer.PutHex64(segment.filesize); + } + else + { + buffer.PutHex32(static_cast<uint32_t>(segment.vmaddr)); + buffer.PutHex32(static_cast<uint32_t>(segment.vmsize)); + buffer.PutHex32(static_cast<uint32_t>(segment.fileoff)); + buffer.PutHex32(static_cast<uint32_t>(segment.filesize)); + } + buffer.PutHex32(segment.maxprot); + buffer.PutHex32(segment.initprot); + buffer.PutHex32(segment.nsects); + buffer.PutHex32(segment.flags); + } + + File core_file; + std::string core_file_path(outfile.GetPath()); + error = core_file.Open(core_file_path.c_str(), + File::eOpenOptionWrite | + File::eOpenOptionTruncate | + File::eOpenOptionCanCreate); + if (error.Success()) + { + // Read 1 page at a time + uint8_t bytes[0x1000]; + // Write the mach header and load commands out to the core file + size_t bytes_written = buffer.GetString().size(); + error = core_file.Write(buffer.GetString().data(), bytes_written); + if (error.Success()) + { + // Now write the file data for all memory segments in the process + for (const auto &segment : segment_load_commands) + { + if (core_file.SeekFromStart(segment.fileoff) == -1) + { + error.SetErrorStringWithFormat("unable to seek to offset 0x%" PRIx64 " in '%s'", segment.fileoff, core_file_path.c_str()); + break; + } + + printf ("Saving %" PRId64 " bytes of data for memory region at 0x%" PRIx64 "\n", segment.vmsize, segment.vmaddr); + addr_t bytes_left = segment.vmsize; + addr_t addr = segment.vmaddr; + Error memory_read_error; + while (bytes_left > 0 && error.Success()) + { + const size_t bytes_to_read = bytes_left > sizeof(bytes) ? sizeof(bytes) : bytes_left; + const size_t bytes_read = process_sp->ReadMemory(addr, bytes, bytes_to_read, memory_read_error); + if (bytes_read == bytes_to_read) + { + size_t bytes_written = bytes_read; + error = core_file.Write(bytes, bytes_written); + bytes_left -= bytes_read; + addr += bytes_read; + } + else + { + // Some pages within regions are not readable, those + // should be zero filled + memset (bytes, 0, bytes_to_read); + size_t bytes_written = bytes_to_read; + error = core_file.Write(bytes, bytes_written); + bytes_left -= bytes_to_read; + addr += bytes_to_read; + } + } + } + } + } + } + else + { + error.SetErrorString("process doesn't support getting memory region info"); + } + } + return true; // This is the right plug to handle saving core files for this process + } + } + return false; +} diff --git a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h new file mode 100644 index 000000000000..6b7ad531b83b --- /dev/null +++ b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -0,0 +1,252 @@ +//===-- ObjectFileMachO.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectFileMachO_h_ +#define liblldb_ObjectFileMachO_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Utility/SafeMachO.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/ObjectFile.h" + +//---------------------------------------------------------------------- +// This class needs to be hidden as eventually belongs in a plugin that +// will export the ObjectFile protocol +//---------------------------------------------------------------------- +class ObjectFileMachO : + public lldb_private::ObjectFile +{ +public: + ObjectFileMachO(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t offset, + lldb::offset_t length); + + ObjectFileMachO(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr); + + ~ObjectFileMachO() override = default; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectFile * + CreateInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length); + + static lldb_private::ObjectFile * + CreateMemoryInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr); + + static size_t + GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool + SaveCore (const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error); + + static bool + MagicBytesMatch (lldb::DataBufferSP& data_sp, + lldb::addr_t offset, + lldb::addr_t length); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + bool + ParseHeader() override; + + bool + SetLoadAddress(lldb_private::Target &target, + lldb::addr_t value, + bool value_is_offset) override; + + lldb::ByteOrder + GetByteOrder() const override; + + bool + IsExecutable() const override; + + uint32_t + GetAddressByteSize() const override; + + lldb::AddressClass + GetAddressClass(lldb::addr_t file_addr) override; + + lldb_private::Symtab * + GetSymtab() override; + + bool + IsStripped() override; + + void + CreateSections(lldb_private::SectionList &unified_section_list) override; + + void + Dump(lldb_private::Stream *s) override; + + bool + GetArchitecture(lldb_private::ArchSpec &arch) override; + + bool + GetUUID(lldb_private::UUID* uuid) override; + + uint32_t + GetDependentModules(lldb_private::FileSpecList& files) override; + + lldb_private::FileSpecList + GetReExportedLibraries() override + { + return m_reexported_dylibs; + } + + lldb_private::Address + GetEntryPointAddress() override; + + lldb_private::Address + GetHeaderAddress() override; + + uint32_t + GetNumThreadContexts() override; + + lldb::RegisterContextSP + GetThreadContextAtIndex(uint32_t idx, lldb_private::Thread &thread) override; + + ObjectFile::Type + CalculateType() override; + + ObjectFile::Strata + CalculateStrata() override; + + uint32_t + GetVersion(uint32_t *versions, uint32_t num_versions) override; + + uint32_t + GetMinimumOSVersion(uint32_t *versions, uint32_t num_versions) override; + + uint32_t + GetSDKVersion(uint32_t *versions, uint32_t num_versions) override; + + bool + GetIsDynamicLinkEditor() override; + + static bool + ParseHeader (lldb_private::DataExtractor &data, + lldb::offset_t *data_offset_ptr, + llvm::MachO::mach_header &header); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + +protected: + static bool + GetUUID (const llvm::MachO::mach_header &header, + const lldb_private::DataExtractor &data, + lldb::offset_t lc_offset, // Offset to the first load command + lldb_private::UUID& uuid); + + static bool + GetArchitecture (const llvm::MachO::mach_header &header, + const lldb_private::DataExtractor &data, + lldb::offset_t lc_offset, + lldb_private::ArchSpec &arch); + + // Intended for same-host arm device debugging where lldb needs to + // detect libraries in the shared cache and augment the nlist entries + // with an on-disk dyld_shared_cache file. The process will record + // the shared cache UUID so the on-disk cache can be matched or rejected + // correctly. + lldb_private::UUID + GetProcessSharedCacheUUID (lldb_private::Process *); + + // Intended for same-host arm device debugging where lldb will read + // shared cache libraries out of its own memory instead of the remote + // process' memory as an optimization. If lldb's shared cache UUID + // does not match the process' shared cache UUID, this optimization + // should not be used. + lldb_private::UUID + GetLLDBSharedCacheUUID (); + + lldb_private::Section * + GetMachHeaderSection(); + + lldb::addr_t + CalculateSectionLoadAddressForMemoryImage(lldb::addr_t mach_header_load_address, + const lldb_private::Section *mach_header_section, + const lldb_private::Section *section); + + lldb_private::UUID + GetSharedCacheUUID (lldb_private::FileSpec dyld_shared_cache, const lldb::ByteOrder byte_order, const uint32_t addr_byte_size); + + size_t + ParseSymtab(); + + llvm::MachO::mach_header m_header; + static const lldb_private::ConstString &GetSegmentNameTEXT(); + static const lldb_private::ConstString &GetSegmentNameDATA(); + static const lldb_private::ConstString &GetSegmentNameDATA_DIRTY(); + static const lldb_private::ConstString &GetSegmentNameDATA_CONST(); + static const lldb_private::ConstString &GetSegmentNameOBJC(); + static const lldb_private::ConstString &GetSegmentNameLINKEDIT(); + static const lldb_private::ConstString &GetSectionNameEHFrame(); + + llvm::MachO::dysymtab_command m_dysymtab; + std::vector<llvm::MachO::segment_command_64> m_mach_segments; + std::vector<llvm::MachO::section_64> m_mach_sections; + std::vector<uint32_t> m_min_os_versions; + std::vector<uint32_t> m_sdk_versions; + typedef lldb_private::RangeVector<uint32_t, uint32_t> FileRangeArray; + lldb_private::Address m_entry_point_address; + FileRangeArray m_thread_context_offsets; + bool m_thread_context_offsets_valid; + lldb_private::FileSpecList m_reexported_dylibs; +}; + +#endif // liblldb_ObjectFileMachO_h_ diff --git a/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt b/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt new file mode 100644 index 000000000000..5c7c488f362f --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lldb_library(lldbPluginObjectFilePECOFF + ObjectFilePECOFF.cpp + WindowsMiniDump.cpp + ) diff --git a/source/Plugins/ObjectFile/PECOFF/Makefile b/source/Plugins/ObjectFile/PECOFF/Makefile new file mode 100644 index 000000000000..1b5abc461e82 --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectFile/PECOFF/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 := lldbPluginObjectFilePECOFF +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp new file mode 100644 index 000000000000..b66244813240 --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -0,0 +1,1056 @@ +//===-- ObjectFilePECOFF.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectFilePECOFF.h" +#include "WindowsMiniDump.h" + +#include "llvm/Support/COFF.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" + +#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ +#define IMAGE_NT_SIGNATURE 0x00004550 // PE00 +#define OPT_HEADER_MAGIC_PE32 0x010b +#define OPT_HEADER_MAGIC_PE32_PLUS 0x020b + +using namespace lldb; +using namespace lldb_private; + +void +ObjectFilePECOFF::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + CreateMemoryInstance, + GetModuleSpecifications, + SaveCore); +} + +void +ObjectFilePECOFF::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +ObjectFilePECOFF::GetPluginNameStatic() +{ + static ConstString g_name("pe-coff"); + return g_name; +} + +const char * +ObjectFilePECOFF::GetPluginDescriptionStatic() +{ + return "Portable Executable and Common Object File Format object file reader (32 and 64 bit)"; +} + + +ObjectFile * +ObjectFilePECOFF::CreateInstance (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length) +{ + if (!data_sp) + { + data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); + data_offset = 0; + } + + if (ObjectFilePECOFF::MagicBytesMatch(data_sp)) + { + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) + data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); + std::unique_ptr<ObjectFile> objfile_ap(new ObjectFilePECOFF (module_sp, data_sp, data_offset, file, file_offset, length)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + +ObjectFile * +ObjectFilePECOFF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr) +{ + return NULL; +} + +size_t +ObjectFilePECOFF::GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs) +{ + const size_t initial_count = specs.GetSize(); + + if (ObjectFilePECOFF::MagicBytesMatch(data_sp)) + { + DataExtractor data; + data.SetData(data_sp, data_offset, length); + data.SetByteOrder(eByteOrderLittle); + + dos_header_t dos_header; + coff_header_t coff_header; + + if (ParseDOSHeader(data, dos_header)) + { + lldb::offset_t offset = dos_header.e_lfanew; + uint32_t pe_signature = data.GetU32(&offset); + if (pe_signature != IMAGE_NT_SIGNATURE) + return false; + if (ParseCOFFHeader(data, &offset, coff_header)) + { + ArchSpec spec; + if (coff_header.machine == MachineAmd64) + { + spec.SetTriple("x86_64-pc-windows"); + specs.Append(ModuleSpec(file, spec)); + } + else if (coff_header.machine == MachineX86) + { + spec.SetTriple("i386-pc-windows"); + specs.Append(ModuleSpec(file, spec)); + spec.SetTriple("i686-pc-windows"); + specs.Append(ModuleSpec(file, spec)); + } + } + } + } + + return specs.GetSize() - initial_count; +} + +bool +ObjectFilePECOFF::SaveCore(const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error) +{ + return SaveMiniDump(process_sp, outfile, error); +} + + +bool +ObjectFilePECOFF::MagicBytesMatch (DataBufferSP& data_sp) +{ + DataExtractor data(data_sp, eByteOrderLittle, 4); + lldb::offset_t offset = 0; + uint16_t magic = data.GetU16 (&offset); + return magic == IMAGE_DOS_SIGNATURE; +} + + +ObjectFilePECOFF::ObjectFilePECOFF (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length) : + ObjectFile (module_sp, file, file_offset, length, data_sp, data_offset), + m_dos_header (), + m_coff_header (), + m_coff_header_opt (), + m_sect_headers () +{ + ::memset (&m_dos_header, 0, sizeof(m_dos_header)); + ::memset (&m_coff_header, 0, sizeof(m_coff_header)); + ::memset (&m_coff_header_opt, 0, sizeof(m_coff_header_opt)); +} + + +ObjectFilePECOFF::~ObjectFilePECOFF() +{ +} + + +bool +ObjectFilePECOFF::ParseHeader () +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + m_sect_headers.clear(); + m_data.SetByteOrder (eByteOrderLittle); + lldb::offset_t offset = 0; + + if (ParseDOSHeader(m_data, m_dos_header)) + { + offset = m_dos_header.e_lfanew; + uint32_t pe_signature = m_data.GetU32 (&offset); + if (pe_signature != IMAGE_NT_SIGNATURE) + return false; + if (ParseCOFFHeader(m_data, &offset, m_coff_header)) + { + if (m_coff_header.hdrsize > 0) + ParseCOFFOptionalHeader(&offset); + ParseSectionHeaders (offset); + } + return true; + } + } + return false; +} + +bool +ObjectFilePECOFF::SetLoadAddress(Target &target, addr_t value, bool value_is_offset) +{ + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) + { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList (); + if (section_list) + { + if (!value_is_offset) + { + value -= m_image_base; + } + + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + // Iterate through the object file sections to find all + // of the sections that have SHF_ALLOC in their flag bits. + SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); + if (section_sp && !section_sp->IsThreadSpecific()) + { + if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + + +ByteOrder +ObjectFilePECOFF::GetByteOrder () const +{ + return eByteOrderLittle; +} + +bool +ObjectFilePECOFF::IsExecutable() const +{ + return (m_coff_header.flags & llvm::COFF::IMAGE_FILE_DLL) == 0; +} + +uint32_t +ObjectFilePECOFF::GetAddressByteSize () const +{ + if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32_PLUS) + return 8; + else if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32) + return 4; + return 4; +} + +//---------------------------------------------------------------------- +// NeedsEndianSwap +// +// Return true if an endian swap needs to occur when extracting data +// from this file. +//---------------------------------------------------------------------- +bool +ObjectFilePECOFF::NeedsEndianSwap() const +{ +#if defined(__LITTLE_ENDIAN__) + return false; +#else + return true; +#endif +} +//---------------------------------------------------------------------- +// ParseDOSHeader +//---------------------------------------------------------------------- +bool +ObjectFilePECOFF::ParseDOSHeader (DataExtractor &data, dos_header_t &dos_header) +{ + bool success = false; + lldb::offset_t offset = 0; + success = data.ValidOffsetForDataOfSize(0, sizeof(dos_header)); + + if (success) + { + dos_header.e_magic = data.GetU16(&offset); // Magic number + success = dos_header.e_magic == IMAGE_DOS_SIGNATURE; + + if (success) + { + dos_header.e_cblp = data.GetU16(&offset); // Bytes on last page of file + dos_header.e_cp = data.GetU16(&offset); // Pages in file + dos_header.e_crlc = data.GetU16(&offset); // Relocations + dos_header.e_cparhdr = data.GetU16(&offset); // Size of header in paragraphs + dos_header.e_minalloc = data.GetU16(&offset); // Minimum extra paragraphs needed + dos_header.e_maxalloc = data.GetU16(&offset); // Maximum extra paragraphs needed + dos_header.e_ss = data.GetU16(&offset); // Initial (relative) SS value + dos_header.e_sp = data.GetU16(&offset); // Initial SP value + dos_header.e_csum = data.GetU16(&offset); // Checksum + dos_header.e_ip = data.GetU16(&offset); // Initial IP value + dos_header.e_cs = data.GetU16(&offset); // Initial (relative) CS value + dos_header.e_lfarlc = data.GetU16(&offset); // File address of relocation table + dos_header.e_ovno = data.GetU16(&offset); // Overlay number + + dos_header.e_res[0] = data.GetU16(&offset); // Reserved words + dos_header.e_res[1] = data.GetU16(&offset); // Reserved words + dos_header.e_res[2] = data.GetU16(&offset); // Reserved words + dos_header.e_res[3] = data.GetU16(&offset); // Reserved words + + dos_header.e_oemid = data.GetU16(&offset); // OEM identifier (for e_oeminfo) + dos_header.e_oeminfo = data.GetU16(&offset); // OEM information; e_oemid specific + dos_header.e_res2[0] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[1] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[2] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[3] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[4] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[5] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[6] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[7] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[8] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[9] = data.GetU16(&offset); // Reserved words + + dos_header.e_lfanew = data.GetU32(&offset); // File address of new exe header + } + } + if (!success) + memset(&dos_header, 0, sizeof(dos_header)); + return success; +} + + +//---------------------------------------------------------------------- +// ParserCOFFHeader +//---------------------------------------------------------------------- +bool +ObjectFilePECOFF::ParseCOFFHeader(DataExtractor &data, lldb::offset_t *offset_ptr, coff_header_t &coff_header) +{ + bool success = data.ValidOffsetForDataOfSize (*offset_ptr, sizeof(coff_header)); + if (success) + { + coff_header.machine = data.GetU16(offset_ptr); + coff_header.nsects = data.GetU16(offset_ptr); + coff_header.modtime = data.GetU32(offset_ptr); + coff_header.symoff = data.GetU32(offset_ptr); + coff_header.nsyms = data.GetU32(offset_ptr); + coff_header.hdrsize = data.GetU16(offset_ptr); + coff_header.flags = data.GetU16(offset_ptr); + } + if (!success) + memset(&coff_header, 0, sizeof(coff_header)); + return success; +} + +bool +ObjectFilePECOFF::ParseCOFFOptionalHeader(lldb::offset_t *offset_ptr) +{ + bool success = false; + const lldb::offset_t end_offset = *offset_ptr + m_coff_header.hdrsize; + if (*offset_ptr < end_offset) + { + success = true; + m_coff_header_opt.magic = m_data.GetU16(offset_ptr); + m_coff_header_opt.major_linker_version = m_data.GetU8 (offset_ptr); + m_coff_header_opt.minor_linker_version = m_data.GetU8 (offset_ptr); + m_coff_header_opt.code_size = m_data.GetU32(offset_ptr); + m_coff_header_opt.data_size = m_data.GetU32(offset_ptr); + m_coff_header_opt.bss_size = m_data.GetU32(offset_ptr); + m_coff_header_opt.entry = m_data.GetU32(offset_ptr); + m_coff_header_opt.code_offset = m_data.GetU32(offset_ptr); + + const uint32_t addr_byte_size = GetAddressByteSize (); + + if (*offset_ptr < end_offset) + { + if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32) + { + // PE32 only + m_coff_header_opt.data_offset = m_data.GetU32(offset_ptr); + } + else + m_coff_header_opt.data_offset = 0; + + if (*offset_ptr < end_offset) + { + m_coff_header_opt.image_base = m_data.GetMaxU64 (offset_ptr, addr_byte_size); + m_coff_header_opt.sect_alignment = m_data.GetU32(offset_ptr); + m_coff_header_opt.file_alignment = m_data.GetU32(offset_ptr); + m_coff_header_opt.major_os_system_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.minor_os_system_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.major_image_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.minor_image_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.major_subsystem_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.minor_subsystem_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.reserved1 = m_data.GetU32(offset_ptr); + m_coff_header_opt.image_size = m_data.GetU32(offset_ptr); + m_coff_header_opt.header_size = m_data.GetU32(offset_ptr); + m_coff_header_opt.checksum = m_data.GetU32(offset_ptr); + m_coff_header_opt.subsystem = m_data.GetU16(offset_ptr); + m_coff_header_opt.dll_flags = m_data.GetU16(offset_ptr); + m_coff_header_opt.stack_reserve_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); + m_coff_header_opt.stack_commit_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); + m_coff_header_opt.heap_reserve_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); + m_coff_header_opt.heap_commit_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); + m_coff_header_opt.loader_flags = m_data.GetU32(offset_ptr); + uint32_t num_data_dir_entries = m_data.GetU32(offset_ptr); + m_coff_header_opt.data_dirs.clear(); + m_coff_header_opt.data_dirs.resize(num_data_dir_entries); + uint32_t i; + for (i=0; i<num_data_dir_entries; i++) + { + m_coff_header_opt.data_dirs[i].vmaddr = m_data.GetU32(offset_ptr); + m_coff_header_opt.data_dirs[i].vmsize = m_data.GetU32(offset_ptr); + } + + m_file_offset = m_coff_header_opt.image_base; + m_image_base = m_coff_header_opt.image_base; + } + } + } + // Make sure we are on track for section data which follows + *offset_ptr = end_offset; + return success; +} + + +//---------------------------------------------------------------------- +// ParseSectionHeaders +//---------------------------------------------------------------------- +bool +ObjectFilePECOFF::ParseSectionHeaders (uint32_t section_header_data_offset) +{ + const uint32_t nsects = m_coff_header.nsects; + m_sect_headers.clear(); + + if (nsects > 0) + { + const uint32_t addr_byte_size = GetAddressByteSize (); + const size_t section_header_byte_size = nsects * sizeof(section_header_t); + DataBufferSP section_header_data_sp(m_file.ReadFileContents (section_header_data_offset, section_header_byte_size)); + DataExtractor section_header_data (section_header_data_sp, GetByteOrder(), addr_byte_size); + + lldb::offset_t offset = 0; + if (section_header_data.ValidOffsetForDataOfSize (offset, section_header_byte_size)) + { + m_sect_headers.resize(nsects); + + for (uint32_t idx = 0; idx<nsects; ++idx) + { + const void *name_data = section_header_data.GetData(&offset, 8); + if (name_data) + { + memcpy(m_sect_headers[idx].name, name_data, 8); + m_sect_headers[idx].vmsize = section_header_data.GetU32(&offset); + m_sect_headers[idx].vmaddr = section_header_data.GetU32(&offset); + m_sect_headers[idx].size = section_header_data.GetU32(&offset); + m_sect_headers[idx].offset = section_header_data.GetU32(&offset); + m_sect_headers[idx].reloff = section_header_data.GetU32(&offset); + m_sect_headers[idx].lineoff = section_header_data.GetU32(&offset); + m_sect_headers[idx].nreloc = section_header_data.GetU16(&offset); + m_sect_headers[idx].nline = section_header_data.GetU16(&offset); + m_sect_headers[idx].flags = section_header_data.GetU32(&offset); + } + } + } + } + + return m_sect_headers.empty() == false; +} + +bool +ObjectFilePECOFF::GetSectionName(std::string& sect_name, const section_header_t& sect) +{ + if (sect.name[0] == '/') + { + lldb::offset_t stroff = strtoul(§.name[1], NULL, 10); + lldb::offset_t string_file_offset = m_coff_header.symoff + (m_coff_header.nsyms * 18) + stroff; + const char *name = m_data.GetCStr (&string_file_offset); + if (name) + { + sect_name = name; + return true; + } + + return false; + } + sect_name = sect.name; + return true; +} + +//---------------------------------------------------------------------- +// GetNListSymtab +//---------------------------------------------------------------------- +Symtab * +ObjectFilePECOFF::GetSymtab() +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_symtab_ap.get() == NULL) + { + SectionList *sect_list = GetSectionList(); + m_symtab_ap.reset(new Symtab(this)); + Mutex::Locker symtab_locker (m_symtab_ap->GetMutex()); + + const uint32_t num_syms = m_coff_header.nsyms; + + if (num_syms > 0 && m_coff_header.symoff > 0) + { + const uint32_t symbol_size = 18; + const uint32_t addr_byte_size = GetAddressByteSize (); + const size_t symbol_data_size = num_syms * symbol_size; + // Include the 4 bytes string table size at the end of the symbols + DataBufferSP symtab_data_sp(m_file.ReadFileContents (m_coff_header.symoff, symbol_data_size + 4)); + DataExtractor symtab_data (symtab_data_sp, GetByteOrder(), addr_byte_size); + lldb::offset_t offset = symbol_data_size; + const uint32_t strtab_size = symtab_data.GetU32 (&offset); + DataBufferSP strtab_data_sp(m_file.ReadFileContents (m_coff_header.symoff + symbol_data_size, strtab_size)); + DataExtractor strtab_data (strtab_data_sp, GetByteOrder(), addr_byte_size); + + // First 4 bytes should be zeroed after strtab_size has been read, + // because it is used as offset 0 to encode a NULL string. + uint32_t* strtab_data_start = (uint32_t*)strtab_data_sp->GetBytes(); + strtab_data_start[0] = 0; + + offset = 0; + std::string symbol_name; + Symbol *symbols = m_symtab_ap->Resize (num_syms); + for (uint32_t i=0; i<num_syms; ++i) + { + coff_symbol_t symbol; + const uint32_t symbol_offset = offset; + const char *symbol_name_cstr = NULL; + // If the first 4 bytes of the symbol string are zero, then we + // it is followed by a 4 byte string table offset. Else these + // 8 bytes contain the symbol name + if (symtab_data.GetU32 (&offset) == 0) + { + // Long string that doesn't fit into the symbol table name, + // so now we must read the 4 byte string table offset + uint32_t strtab_offset = symtab_data.GetU32 (&offset); + symbol_name_cstr = strtab_data.PeekCStr (strtab_offset); + symbol_name.assign (symbol_name_cstr); + } + else + { + // Short string that fits into the symbol table name which is 8 bytes + offset += sizeof(symbol.name) - 4; // Skip remaining + symbol_name_cstr = symtab_data.PeekCStr (symbol_offset); + if (symbol_name_cstr == NULL) + break; + symbol_name.assign (symbol_name_cstr, sizeof(symbol.name)); + } + symbol.value = symtab_data.GetU32 (&offset); + symbol.sect = symtab_data.GetU16 (&offset); + symbol.type = symtab_data.GetU16 (&offset); + symbol.storage = symtab_data.GetU8 (&offset); + symbol.naux = symtab_data.GetU8 (&offset); + symbols[i].GetMangled ().SetValue (ConstString(symbol_name.c_str())); + if ((int16_t)symbol.sect >= 1) + { + Address symbol_addr(sect_list->GetSectionAtIndex(symbol.sect-1), symbol.value); + symbols[i].GetAddressRef() = symbol_addr; + } + + if (symbol.naux > 0) + { + i += symbol.naux; + offset += symbol_size; + } + } + + } + + // Read export header + if (coff_data_dir_export_table < m_coff_header_opt.data_dirs.size() + && m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmsize > 0 && m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr > 0) + { + export_directory_entry export_table; + uint32_t data_start = m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr; + Address address(m_coff_header_opt.image_base + data_start, sect_list); + DataBufferSP symtab_data_sp(m_file.ReadFileContents(address.GetSection()->GetFileOffset() + address.GetOffset(), m_coff_header_opt.data_dirs[0].vmsize)); + DataExtractor symtab_data (symtab_data_sp, GetByteOrder(), GetAddressByteSize()); + lldb::offset_t offset = 0; + + // Read export_table header + export_table.characteristics = symtab_data.GetU32(&offset); + export_table.time_date_stamp = symtab_data.GetU32(&offset); + export_table.major_version = symtab_data.GetU16(&offset); + export_table.minor_version = symtab_data.GetU16(&offset); + export_table.name = symtab_data.GetU32(&offset); + export_table.base = symtab_data.GetU32(&offset); + export_table.number_of_functions = symtab_data.GetU32(&offset); + export_table.number_of_names = symtab_data.GetU32(&offset); + export_table.address_of_functions = symtab_data.GetU32(&offset); + export_table.address_of_names = symtab_data.GetU32(&offset); + export_table.address_of_name_ordinals = symtab_data.GetU32(&offset); + + bool has_ordinal = export_table.address_of_name_ordinals != 0; + + lldb::offset_t name_offset = export_table.address_of_names - data_start; + lldb::offset_t name_ordinal_offset = export_table.address_of_name_ordinals - data_start; + + Symbol *symbols = m_symtab_ap->Resize(export_table.number_of_names); + + std::string symbol_name; + + // Read each export table entry + for (size_t i = 0; i < export_table.number_of_names; ++i) + { + uint32_t name_ordinal = has_ordinal ? symtab_data.GetU16(&name_ordinal_offset) : i; + uint32_t name_address = symtab_data.GetU32(&name_offset); + + const char* symbol_name_cstr = symtab_data.PeekCStr(name_address - data_start); + symbol_name.assign(symbol_name_cstr); + + lldb::offset_t function_offset = export_table.address_of_functions - data_start + sizeof(uint32_t) * name_ordinal; + uint32_t function_rva = symtab_data.GetU32(&function_offset); + + Address symbol_addr(m_coff_header_opt.image_base + function_rva, sect_list); + symbols[i].GetMangled().SetValue(ConstString(symbol_name.c_str())); + symbols[i].GetAddressRef() = symbol_addr; + symbols[i].SetType(lldb::eSymbolTypeCode); + symbols[i].SetDebug(true); + } + } + } + } + return m_symtab_ap.get(); + +} + +bool +ObjectFilePECOFF::IsStripped () +{ + // TODO: determine this for COFF + return false; +} + + + +void +ObjectFilePECOFF::CreateSections (SectionList &unified_section_list) +{ + if (!m_sections_ap.get()) + { + m_sections_ap.reset(new SectionList()); + + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + const uint32_t nsects = m_sect_headers.size(); + ModuleSP module_sp (GetModule()); + for (uint32_t idx = 0; idx<nsects; ++idx) + { + std::string sect_name; + GetSectionName (sect_name, m_sect_headers[idx]); + ConstString const_sect_name (sect_name.c_str()); + static ConstString g_code_sect_name (".code"); + static ConstString g_CODE_sect_name ("CODE"); + static ConstString g_data_sect_name (".data"); + static ConstString g_DATA_sect_name ("DATA"); + static ConstString g_bss_sect_name (".bss"); + static ConstString g_BSS_sect_name ("BSS"); + static ConstString g_debug_sect_name (".debug"); + static ConstString g_reloc_sect_name (".reloc"); + static ConstString g_stab_sect_name (".stab"); + static ConstString g_stabstr_sect_name (".stabstr"); + static ConstString g_sect_name_dwarf_debug_abbrev (".debug_abbrev"); + static ConstString g_sect_name_dwarf_debug_aranges (".debug_aranges"); + static ConstString g_sect_name_dwarf_debug_frame (".debug_frame"); + static ConstString g_sect_name_dwarf_debug_info (".debug_info"); + static ConstString g_sect_name_dwarf_debug_line (".debug_line"); + static ConstString g_sect_name_dwarf_debug_loc (".debug_loc"); + static ConstString g_sect_name_dwarf_debug_macinfo (".debug_macinfo"); + static ConstString g_sect_name_dwarf_debug_pubnames (".debug_pubnames"); + static ConstString g_sect_name_dwarf_debug_pubtypes (".debug_pubtypes"); + static ConstString g_sect_name_dwarf_debug_ranges (".debug_ranges"); + static ConstString g_sect_name_dwarf_debug_str (".debug_str"); + static ConstString g_sect_name_eh_frame (".eh_frame"); + static ConstString g_sect_name_go_symtab (".gosymtab"); + SectionType section_type = eSectionTypeOther; + if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_CODE && + ((const_sect_name == g_code_sect_name) || (const_sect_name == g_CODE_sect_name))) + { + section_type = eSectionTypeCode; + } + else if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA && + ((const_sect_name == g_data_sect_name) || (const_sect_name == g_DATA_sect_name))) + { + section_type = eSectionTypeData; + } + else if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA && + ((const_sect_name == g_bss_sect_name) || (const_sect_name == g_BSS_sect_name))) + { + if (m_sect_headers[idx].size == 0) + section_type = eSectionTypeZeroFill; + else + section_type = eSectionTypeData; + } + else if (const_sect_name == g_debug_sect_name) + { + section_type = eSectionTypeDebug; + } + else if (const_sect_name == g_stabstr_sect_name) + { + section_type = eSectionTypeDataCString; + } + else if (const_sect_name == g_reloc_sect_name) + { + section_type = eSectionTypeOther; + } + else if (const_sect_name == g_sect_name_dwarf_debug_abbrev) section_type = eSectionTypeDWARFDebugAbbrev; + else if (const_sect_name == g_sect_name_dwarf_debug_aranges) section_type = eSectionTypeDWARFDebugAranges; + else if (const_sect_name == g_sect_name_dwarf_debug_frame) section_type = eSectionTypeDWARFDebugFrame; + else if (const_sect_name == g_sect_name_dwarf_debug_info) section_type = eSectionTypeDWARFDebugInfo; + else if (const_sect_name == g_sect_name_dwarf_debug_line) section_type = eSectionTypeDWARFDebugLine; + else if (const_sect_name == g_sect_name_dwarf_debug_loc) section_type = eSectionTypeDWARFDebugLoc; + else if (const_sect_name == g_sect_name_dwarf_debug_macinfo) section_type = eSectionTypeDWARFDebugMacInfo; + else if (const_sect_name == g_sect_name_dwarf_debug_pubnames) section_type = eSectionTypeDWARFDebugPubNames; + else if (const_sect_name == g_sect_name_dwarf_debug_pubtypes) section_type = eSectionTypeDWARFDebugPubTypes; + else if (const_sect_name == g_sect_name_dwarf_debug_ranges) section_type = eSectionTypeDWARFDebugRanges; + else if (const_sect_name == g_sect_name_dwarf_debug_str) section_type = eSectionTypeDWARFDebugStr; + else if (const_sect_name == g_sect_name_eh_frame) section_type = eSectionTypeEHFrame; + else if (const_sect_name == g_sect_name_go_symtab) section_type = eSectionTypeGoSymtab; + else if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_CODE) + { + section_type = eSectionTypeCode; + } + else if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) + { + section_type = eSectionTypeData; + } + else if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) + { + if (m_sect_headers[idx].size == 0) + section_type = eSectionTypeZeroFill; + else + section_type = eSectionTypeData; + } + + // Use a segment ID of the segment index shifted left by 8 so they + // never conflict with any of the sections. + SectionSP section_sp (new Section (module_sp, // Module to which this section belongs + this, // Object file to which this section belongs + idx + 1, // Section ID is the 1 based segment index shifted right by 8 bits as not to collide with any of the 256 section IDs that are possible + const_sect_name, // Name of this section + section_type, // This section is a container of other sections. + m_coff_header_opt.image_base + m_sect_headers[idx].vmaddr, // File VM address == addresses as they are found in the object file + m_sect_headers[idx].vmsize, // VM size in bytes of this section + m_sect_headers[idx].offset, // Offset to the data for this section in the file + m_sect_headers[idx].size, // Size in bytes of this section as found in the file + m_coff_header_opt.sect_alignment, // Section alignment + m_sect_headers[idx].flags)); // Flags for this section + + //section_sp->SetIsEncrypted (segment_is_encrypted); + + unified_section_list.AddSection(section_sp); + m_sections_ap->AddSection (section_sp); + } + } + } +} + +bool +ObjectFilePECOFF::GetUUID (UUID* uuid) +{ + return false; +} + +uint32_t +ObjectFilePECOFF::GetDependentModules (FileSpecList& files) +{ + return 0; +} + + +//---------------------------------------------------------------------- +// Dump +// +// Dump the specifics of the runtime file container (such as any headers +// segments, sections, etc). +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::Dump(Stream *s) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + s->Printf("%p: ", static_cast<void*>(this)); + s->Indent(); + s->PutCString("ObjectFilePECOFF"); + + ArchSpec header_arch; + GetArchitecture (header_arch); + + *s << ", file = '" << m_file << "', arch = " << header_arch.GetArchitectureName() << "\n"; + + SectionList *sections = GetSectionList(); + if (sections) + sections->Dump(s, NULL, true, UINT32_MAX); + + if (m_symtab_ap.get()) + m_symtab_ap->Dump(s, NULL, eSortOrderNone); + + if (m_dos_header.e_magic) + DumpDOSHeader (s, m_dos_header); + if (m_coff_header.machine) + { + DumpCOFFHeader (s, m_coff_header); + if (m_coff_header.hdrsize) + DumpOptCOFFHeader (s, m_coff_header_opt); + } + s->EOL(); + DumpSectionHeaders(s); + s->EOL(); + } +} + +//---------------------------------------------------------------------- +// DumpDOSHeader +// +// Dump the MS-DOS header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::DumpDOSHeader(Stream *s, const dos_header_t& header) +{ + s->PutCString ("MSDOS Header\n"); + s->Printf (" e_magic = 0x%4.4x\n", header.e_magic); + s->Printf (" e_cblp = 0x%4.4x\n", header.e_cblp); + s->Printf (" e_cp = 0x%4.4x\n", header.e_cp); + s->Printf (" e_crlc = 0x%4.4x\n", header.e_crlc); + s->Printf (" e_cparhdr = 0x%4.4x\n", header.e_cparhdr); + s->Printf (" e_minalloc = 0x%4.4x\n", header.e_minalloc); + s->Printf (" e_maxalloc = 0x%4.4x\n", header.e_maxalloc); + s->Printf (" e_ss = 0x%4.4x\n", header.e_ss); + s->Printf (" e_sp = 0x%4.4x\n", header.e_sp); + s->Printf (" e_csum = 0x%4.4x\n", header.e_csum); + s->Printf (" e_ip = 0x%4.4x\n", header.e_ip); + s->Printf (" e_cs = 0x%4.4x\n", header.e_cs); + s->Printf (" e_lfarlc = 0x%4.4x\n", header.e_lfarlc); + s->Printf (" e_ovno = 0x%4.4x\n", header.e_ovno); + s->Printf (" e_res[4] = { 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x }\n", + header.e_res[0], + header.e_res[1], + header.e_res[2], + header.e_res[3]); + s->Printf (" e_oemid = 0x%4.4x\n", header.e_oemid); + s->Printf (" e_oeminfo = 0x%4.4x\n", header.e_oeminfo); + s->Printf (" e_res2[10] = { 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x }\n", + header.e_res2[0], + header.e_res2[1], + header.e_res2[2], + header.e_res2[3], + header.e_res2[4], + header.e_res2[5], + header.e_res2[6], + header.e_res2[7], + header.e_res2[8], + header.e_res2[9]); + s->Printf (" e_lfanew = 0x%8.8x\n", header.e_lfanew); +} + +//---------------------------------------------------------------------- +// DumpCOFFHeader +// +// Dump the COFF header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::DumpCOFFHeader(Stream *s, const coff_header_t& header) +{ + s->PutCString ("COFF Header\n"); + s->Printf (" machine = 0x%4.4x\n", header.machine); + s->Printf (" nsects = 0x%4.4x\n", header.nsects); + s->Printf (" modtime = 0x%8.8x\n", header.modtime); + s->Printf (" symoff = 0x%8.8x\n", header.symoff); + s->Printf (" nsyms = 0x%8.8x\n", header.nsyms); + s->Printf (" hdrsize = 0x%4.4x\n", header.hdrsize); +} + +//---------------------------------------------------------------------- +// DumpOptCOFFHeader +// +// Dump the optional COFF header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::DumpOptCOFFHeader(Stream *s, const coff_opt_header_t& header) +{ + s->PutCString ("Optional COFF Header\n"); + s->Printf (" magic = 0x%4.4x\n", header.magic); + s->Printf (" major_linker_version = 0x%2.2x\n", header.major_linker_version); + s->Printf (" minor_linker_version = 0x%2.2x\n", header.minor_linker_version); + s->Printf (" code_size = 0x%8.8x\n", header.code_size); + s->Printf (" data_size = 0x%8.8x\n", header.data_size); + s->Printf (" bss_size = 0x%8.8x\n", header.bss_size); + s->Printf (" entry = 0x%8.8x\n", header.entry); + s->Printf (" code_offset = 0x%8.8x\n", header.code_offset); + s->Printf (" data_offset = 0x%8.8x\n", header.data_offset); + s->Printf (" image_base = 0x%16.16" PRIx64 "\n", header.image_base); + s->Printf (" sect_alignment = 0x%8.8x\n", header.sect_alignment); + s->Printf (" file_alignment = 0x%8.8x\n", header.file_alignment); + s->Printf (" major_os_system_version = 0x%4.4x\n", header.major_os_system_version); + s->Printf (" minor_os_system_version = 0x%4.4x\n", header.minor_os_system_version); + s->Printf (" major_image_version = 0x%4.4x\n", header.major_image_version); + s->Printf (" minor_image_version = 0x%4.4x\n", header.minor_image_version); + s->Printf (" major_subsystem_version = 0x%4.4x\n", header.major_subsystem_version); + s->Printf (" minor_subsystem_version = 0x%4.4x\n", header.minor_subsystem_version); + s->Printf (" reserved1 = 0x%8.8x\n", header.reserved1); + s->Printf (" image_size = 0x%8.8x\n", header.image_size); + s->Printf (" header_size = 0x%8.8x\n", header.header_size); + s->Printf (" checksum = 0x%8.8x\n", header.checksum); + s->Printf (" subsystem = 0x%4.4x\n", header.subsystem); + s->Printf (" dll_flags = 0x%4.4x\n", header.dll_flags); + s->Printf (" stack_reserve_size = 0x%16.16" PRIx64 "\n", header.stack_reserve_size); + s->Printf (" stack_commit_size = 0x%16.16" PRIx64 "\n", header.stack_commit_size); + s->Printf (" heap_reserve_size = 0x%16.16" PRIx64 "\n", header.heap_reserve_size); + s->Printf (" heap_commit_size = 0x%16.16" PRIx64 "\n", header.heap_commit_size); + s->Printf (" loader_flags = 0x%8.8x\n", header.loader_flags); + s->Printf (" num_data_dir_entries = 0x%8.8x\n", (uint32_t)header.data_dirs.size()); + uint32_t i; + for (i=0; i<header.data_dirs.size(); i++) + { + s->Printf (" data_dirs[%2u] vmaddr = 0x%8.8x, vmsize = 0x%8.8x\n", + i, + header.data_dirs[i].vmaddr, + header.data_dirs[i].vmsize); + } +} +//---------------------------------------------------------------------- +// DumpSectionHeader +// +// Dump a single ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::DumpSectionHeader(Stream *s, const section_header_t& sh) +{ + std::string name; + GetSectionName(name, sh); + s->Printf ("%-16s 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%4.4x 0x%4.4x 0x%8.8x\n", + name.c_str(), + sh.vmaddr, + sh.vmsize, + sh.offset, + sh.size, + sh.reloff, + sh.lineoff, + sh.nreloc, + sh.nline, + sh.flags); +} + + +//---------------------------------------------------------------------- +// DumpSectionHeaders +// +// Dump all of the ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::DumpSectionHeaders(Stream *s) +{ + + s->PutCString ("Section Headers\n"); + s->PutCString ("IDX name vm addr vm size file off file size reloc off line off nreloc nline flags\n"); + s->PutCString ("==== ---------------- ---------- ---------- ---------- ---------- ---------- ---------- ------ ------ ----------\n"); + + uint32_t idx = 0; + SectionHeaderCollIter pos, end = m_sect_headers.end(); + + for (pos = m_sect_headers.begin(); pos != end; ++pos, ++idx) + { + s->Printf ("[%2u] ", idx); + ObjectFilePECOFF::DumpSectionHeader(s, *pos); + } +} + +bool +ObjectFilePECOFF::GetArchitecture (ArchSpec &arch) +{ + uint16_t machine = m_coff_header.machine; + switch (machine) + { + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + case llvm::COFF::IMAGE_FILE_MACHINE_POWERPC: + case llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP: + case llvm::COFF::IMAGE_FILE_MACHINE_ARM: + case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: + case llvm::COFF::IMAGE_FILE_MACHINE_THUMB: + arch.SetArchitecture (eArchTypeCOFF, machine, LLDB_INVALID_CPUTYPE); + return true; + default: + break; + } + return false; +} + +ObjectFile::Type +ObjectFilePECOFF::CalculateType() +{ + if (m_coff_header.machine != 0) + { + if ((m_coff_header.flags & llvm::COFF::IMAGE_FILE_DLL) == 0) + return eTypeExecutable; + else + return eTypeSharedLibrary; + } + return eTypeExecutable; +} + +ObjectFile::Strata +ObjectFilePECOFF::CalculateStrata() +{ + return eStrataUser; +} +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString +ObjectFilePECOFF::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectFilePECOFF::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h b/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h new file mode 100644 index 000000000000..fd33cd3d32f8 --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h @@ -0,0 +1,303 @@ +//===-- ObjectFilePECOFF.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectFilePECOFF_h_ +#define liblldb_ObjectFilePECOFF_h_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "lldb/Symbol/ObjectFile.h" + +class ObjectFilePECOFF : + public lldb_private::ObjectFile +{ +public: + typedef enum MachineType + { + MachineUnknown = 0x0, + MachineAm33 = 0x1d3, + MachineAmd64 = 0x8664, + MachineArm = 0x1c0, + MachineArmNt = 0x1c4, + MachineArm64 = 0xaa64, + MachineEbc = 0xebc, + MachineX86 = 0x14c, + MachineIA64 = 0x200, + MachineM32R = 0x9041, + MachineMips16 = 0x266, + MachineMipsFpu = 0x366, + MachineMipsFpu16 = 0x466, + MachinePowerPc = 0x1f0, + MachinePowerPcfp = 0x1f1, + MachineR4000 = 0x166, + MachineSh3 = 0x1a2, + MachineSh3dsp = 0x1a3, + MachineSh4 = 0x1a6, + MachineSh5 = 0x1a8, + MachineThumb = 0x1c2, + MachineWcemIpsv2 = 0x169 + } MachineType; + + ObjectFilePECOFF(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length); + + ~ObjectFilePECOFF() override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static ObjectFile * + CreateInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t offset, + lldb::offset_t length); + + static lldb_private::ObjectFile * + CreateMemoryInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr); + + static size_t + GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool + SaveCore (const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error); + + static bool + MagicBytesMatch (lldb::DataBufferSP& data_sp); + + bool + ParseHeader() override; + + bool + SetLoadAddress(lldb_private::Target &target, lldb::addr_t value, bool value_is_offset) override; + + lldb::ByteOrder + GetByteOrder() const override; + + bool + IsExecutable() const override; + + uint32_t + GetAddressByteSize() const override; + +// virtual lldb_private::AddressClass +// GetAddressClass (lldb::addr_t file_addr); + + lldb_private::Symtab * + GetSymtab() override; + + bool + IsStripped() override; + + void + CreateSections(lldb_private::SectionList &unified_section_list) override; + + void + Dump(lldb_private::Stream *s) override; + + bool + GetArchitecture(lldb_private::ArchSpec &arch) override; + + bool + GetUUID(lldb_private::UUID* uuid) override; + + uint32_t + GetDependentModules(lldb_private::FileSpecList& files) override; + +// virtual lldb_private::Address +// GetEntryPointAddress (); + + ObjectFile::Type + CalculateType() override; + + ObjectFile::Strata + CalculateStrata() override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + +protected: + bool NeedsEndianSwap() const; + + typedef struct dos_header { // DOS .EXE header + uint16_t e_magic; // Magic number + uint16_t e_cblp; // Bytes on last page of file + uint16_t e_cp; // Pages in file + uint16_t e_crlc; // Relocations + uint16_t e_cparhdr; // Size of header in paragraphs + uint16_t e_minalloc; // Minimum extra paragraphs needed + uint16_t e_maxalloc; // Maximum extra paragraphs needed + uint16_t e_ss; // Initial (relative) SS value + uint16_t e_sp; // Initial SP value + uint16_t e_csum; // Checksum + uint16_t e_ip; // Initial IP value + uint16_t e_cs; // Initial (relative) CS value + uint16_t e_lfarlc; // File address of relocation table + uint16_t e_ovno; // Overlay number + uint16_t e_res[4]; // Reserved words + uint16_t e_oemid; // OEM identifier (for e_oeminfo) + uint16_t e_oeminfo; // OEM information; e_oemid specific + uint16_t e_res2[10]; // Reserved words + uint32_t e_lfanew; // File address of new exe header + } dos_header_t; + + typedef struct coff_header { + uint16_t machine; + uint16_t nsects; + uint32_t modtime; + uint32_t symoff; + uint32_t nsyms; + uint16_t hdrsize; + uint16_t flags; + } coff_header_t; + + typedef struct data_directory { + uint32_t vmaddr; + uint32_t vmsize; + } data_directory_t; + + typedef struct coff_opt_header + { + uint16_t magic; + uint8_t major_linker_version; + uint8_t minor_linker_version; + uint32_t code_size; + uint32_t data_size; + uint32_t bss_size; + uint32_t entry; + uint32_t code_offset; + uint32_t data_offset; + + uint64_t image_base; + uint32_t sect_alignment; + uint32_t file_alignment; + uint16_t major_os_system_version; + uint16_t minor_os_system_version; + uint16_t major_image_version; + uint16_t minor_image_version; + uint16_t major_subsystem_version; + uint16_t minor_subsystem_version; + uint32_t reserved1; + uint32_t image_size; + uint32_t header_size; + uint32_t checksum; + uint16_t subsystem; + uint16_t dll_flags; + uint64_t stack_reserve_size; + uint64_t stack_commit_size; + uint64_t heap_reserve_size; + uint64_t heap_commit_size; + uint32_t loader_flags; + // uint32_t num_data_dir_entries; + std::vector<data_directory> data_dirs; // will contain num_data_dir_entries entries + } coff_opt_header_t; + + typedef enum coff_data_dir_type + { + coff_data_dir_export_table = 0, + coff_data_dir_import_table = 1, + } coff_data_dir_type; + + typedef struct section_header { + char name[8]; + uint32_t vmsize; // Virtual Size + uint32_t vmaddr; // Virtual Addr + uint32_t size; // File size + uint32_t offset; // File offset + uint32_t reloff; // Offset to relocations + uint32_t lineoff;// Offset to line table entries + uint16_t nreloc; // Number of relocation entries + uint16_t nline; // Number of line table entries + uint32_t flags; + } section_header_t; + + typedef struct coff_symbol { + char name[8]; + uint32_t value; + uint16_t sect; + uint16_t type; + uint8_t storage; + uint8_t naux; + } coff_symbol_t; + + typedef struct export_directory_entry { + uint32_t characteristics; + uint32_t time_date_stamp; + uint16_t major_version; + uint16_t minor_version; + uint32_t name; + uint32_t base; + uint32_t number_of_functions; + uint32_t number_of_names; + uint32_t address_of_functions; + uint32_t address_of_names; + uint32_t address_of_name_ordinals; + } export_directory_entry; + + static bool ParseDOSHeader (lldb_private::DataExtractor &data, dos_header_t &dos_header); + static bool ParseCOFFHeader (lldb_private::DataExtractor &data, lldb::offset_t *offset_ptr, coff_header_t &coff_header); + bool ParseCOFFOptionalHeader (lldb::offset_t *offset_ptr); + bool ParseSectionHeaders (uint32_t offset); + + static void DumpDOSHeader(lldb_private::Stream *s, const dos_header_t& header); + static void DumpCOFFHeader(lldb_private::Stream *s, const coff_header_t& header); + static void DumpOptCOFFHeader(lldb_private::Stream *s, const coff_opt_header_t& header); + void DumpSectionHeaders(lldb_private::Stream *s); + void DumpSectionHeader(lldb_private::Stream *s, const section_header_t& sh); + bool GetSectionName(std::string& sect_name, const section_header_t& sect); + + typedef std::vector<section_header_t> SectionHeaderColl; + typedef SectionHeaderColl::iterator SectionHeaderCollIter; + typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter; +private: + dos_header_t m_dos_header; + coff_header_t m_coff_header; + coff_opt_header_t m_coff_header_opt; + SectionHeaderColl m_sect_headers; + lldb::addr_t m_image_base; +}; + +#endif // liblldb_ObjectFilePECOFF_h_ diff --git a/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.cpp b/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.cpp new file mode 100644 index 000000000000..14c73eff2388 --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.cpp @@ -0,0 +1,56 @@ +//===-- WindowsMiniDump.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This function is separated out from ObjectFilePECOFF.cpp to name avoid name +// collisions with WinAPI preprocessor macros. + +#include "WindowsMiniDump.h" +#include "lldb/Host/FileSpec.h" +#include "llvm/Support/ConvertUTF.h" + +#ifdef _WIN32 +#include "lldb/Host/windows/windows.h" +#include <DbgHelp.h> // for MiniDumpWriteDump +#endif + +namespace lldb_private { + +bool +SaveMiniDump(const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error) +{ + if (!process_sp) return false; +#ifdef _WIN32 + HANDLE process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_sp->GetID()); + const std::string file_name = outfile.GetCString(); + std::wstring wide_name; + wide_name.resize(file_name.size() + 1); + char * result_ptr = reinterpret_cast<char *>(&wide_name[0]); + const UTF8 *error_ptr = nullptr; + if (!llvm::ConvertUTF8toWide(sizeof(wchar_t), file_name, result_ptr, error_ptr)) { + error.SetErrorString("cannot convert file name"); + return false; + } + HANDLE file_handle = ::CreateFileW(wide_name.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + const auto result = ::MiniDumpWriteDump(process_handle, process_sp->GetID(), file_handle, + MiniDumpWithFullMemoryInfo, NULL, NULL, NULL); + ::CloseHandle(file_handle); + ::CloseHandle(process_handle); + if (!result) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return false; + } + return true; +#endif + return false; +} + +} // namesapce lldb_private diff --git a/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.h b/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.h new file mode 100644 index 000000000000..cbea88af1fb0 --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.h @@ -0,0 +1,24 @@ +//===-- WindowsMiniDump.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_WindowsMiniDump_h_ +#define liblldb_WindowsMiniDump_h_ + +#include "lldb/Target/Process.h" + +namespace lldb_private { + +bool +SaveMiniDump(const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error); + +} // namespace lldb_private + +#endif diff --git a/source/Plugins/OperatingSystem/CMakeLists.txt b/source/Plugins/OperatingSystem/CMakeLists.txt new file mode 100644 index 000000000000..1f017adcd02b --- /dev/null +++ b/source/Plugins/OperatingSystem/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(Go) +add_subdirectory(Python) diff --git a/source/Plugins/OperatingSystem/Go/CMakeLists.txt b/source/Plugins/OperatingSystem/Go/CMakeLists.txt new file mode 100644 index 000000000000..1ca82c462069 --- /dev/null +++ b/source/Plugins/OperatingSystem/Go/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginOSGo + OperatingSystemGo.cpp + ) diff --git a/source/Plugins/OperatingSystem/Go/Makefile b/source/Plugins/OperatingSystem/Go/Makefile new file mode 100644 index 000000000000..7d06d483d3ae --- /dev/null +++ b/source/Plugins/OperatingSystem/Go/Makefile @@ -0,0 +1,14 @@ +##==- source/Plugins/OperatingSystem/Go/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 := lldbPluginOSGo +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/OperatingSystem/Python/CMakeLists.txt b/source/Plugins/OperatingSystem/Python/CMakeLists.txt new file mode 100644 index 000000000000..7188e6f67ba0 --- /dev/null +++ b/source/Plugins/OperatingSystem/Python/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginOSPython + OperatingSystemPython.cpp + ) diff --git a/source/Plugins/OperatingSystem/Python/Makefile b/source/Plugins/OperatingSystem/Python/Makefile new file mode 100644 index 000000000000..67cd0acd7038 --- /dev/null +++ b/source/Plugins/OperatingSystem/Python/Makefile @@ -0,0 +1,14 @@ +##==- source/Plugins/OperatingSystem/Python/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 := lldbPluginOSPython +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Android/AdbClient.cpp b/source/Plugins/Platform/Android/AdbClient.cpp new file mode 100644 index 000000000000..736447fd22d2 --- /dev/null +++ b/source/Plugins/Platform/Android/AdbClient.cpp @@ -0,0 +1,569 @@ +//===-- AdbClient.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Other libraries and framework includes +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataEncoder.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FileUtilities.h" + +// Project includes +#include "AdbClient.h" + +#include <limits.h> + +#include <algorithm> +#include <fstream> +#include <sstream> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_android; + +namespace { + +const uint32_t kReadTimeout = 1000000; // 1 second +const char * kOKAY = "OKAY"; +const char * kFAIL = "FAIL"; +const char * kDATA = "DATA"; +const char * kDONE = "DONE"; + +const char * kSEND = "SEND"; +const char * kRECV = "RECV"; +const char * kSTAT = "STAT"; + +const size_t kSyncPacketLen = 8; +// Maximum size of a filesync DATA packet. +const size_t kMaxPushData = 2*1024; +// Default mode for pushed files. +const uint32_t kDefaultMode = 0100770; // S_IFREG | S_IRWXU | S_IRWXG + +const char * kSocketNamespaceAbstract = "localabstract"; +const char * kSocketNamespaceFileSystem = "localfilesystem"; + +} // namespace + +Error +AdbClient::CreateByDeviceID(const std::string &device_id, AdbClient &adb) +{ + DeviceIDList connect_devices; + auto error = adb.GetDevices(connect_devices); + if (error.Fail()) + return error; + + if (device_id.empty()) + { + if (connect_devices.size() != 1) + return Error("Expected a single connected device, got instead %" PRIu64, + static_cast<uint64_t>(connect_devices.size())); + + adb.SetDeviceID(connect_devices.front()); + } + else + { + auto find_it = std::find(connect_devices.begin(), connect_devices.end(), device_id); + if (find_it == connect_devices.end()) + return Error("Device \"%s\" not found", device_id.c_str()); + + adb.SetDeviceID(*find_it); + } + return error; +} + +AdbClient::AdbClient (const std::string &device_id) + : m_device_id (device_id) +{ +} + +void +AdbClient::SetDeviceID (const std::string &device_id) +{ + m_device_id = device_id; +} + +const std::string& +AdbClient::GetDeviceID() const +{ + return m_device_id; +} + +Error +AdbClient::Connect () +{ + Error error; + m_conn.Connect ("connect://localhost:5037", &error); + + return error; +} + +Error +AdbClient::GetDevices (DeviceIDList &device_list) +{ + device_list.clear (); + + auto error = SendMessage ("host:devices"); + if (error.Fail ()) + return error; + + error = ReadResponseStatus (); + if (error.Fail ()) + return error; + + std::vector<char> in_buffer; + error = ReadMessage (in_buffer); + + llvm::StringRef response (&in_buffer[0], in_buffer.size ()); + llvm::SmallVector<llvm::StringRef, 4> devices; + response.split (devices, "\n", -1, false); + + for (const auto device: devices) + device_list.push_back (device.split ('\t').first); + + return error; +} + +Error +AdbClient::SetPortForwarding (const uint16_t local_port, const uint16_t remote_port) +{ + char message[48]; + snprintf (message, sizeof (message), "forward:tcp:%d;tcp:%d", local_port, remote_port); + + const auto error = SendDeviceMessage (message); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::SetPortForwarding (const uint16_t local_port, + const char* remote_socket_name, + const UnixSocketNamespace socket_namespace) +{ + char message[PATH_MAX]; + const char * sock_namespace_str = (socket_namespace == UnixSocketNamespaceAbstract) ? + kSocketNamespaceAbstract : kSocketNamespaceFileSystem; + snprintf (message, sizeof (message), "forward:tcp:%d;%s:%s", + local_port, + sock_namespace_str, + remote_socket_name); + + const auto error = SendDeviceMessage (message); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::DeletePortForwarding (const uint16_t local_port) +{ + char message[32]; + snprintf (message, sizeof (message), "killforward:tcp:%d", local_port); + + const auto error = SendDeviceMessage (message); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::SendMessage (const std::string &packet, const bool reconnect) +{ + Error error; + if (reconnect) + { + error = Connect (); + if (error.Fail ()) + return error; + } + + char length_buffer[5]; + snprintf (length_buffer, sizeof (length_buffer), "%04x", static_cast<int>(packet.size ())); + + ConnectionStatus status; + + m_conn.Write (length_buffer, 4, status, &error); + if (error.Fail ()) + return error; + + m_conn.Write (packet.c_str (), packet.size (), status, &error); + return error; +} + +Error +AdbClient::SendDeviceMessage (const std::string &packet) +{ + std::ostringstream msg; + msg << "host-serial:" << m_device_id << ":" << packet; + return SendMessage (msg.str ()); +} + +Error +AdbClient::ReadMessage (std::vector<char> &message) +{ + message.clear (); + + char buffer[5]; + buffer[4] = 0; + + auto error = ReadAllBytes (buffer, 4); + if (error.Fail ()) + return error; + + unsigned int packet_len = 0; + sscanf (buffer, "%x", &packet_len); + + message.resize (packet_len, 0); + error = ReadAllBytes (&message[0], packet_len); + if (error.Fail ()) + message.clear (); + + return error; +} + +Error +AdbClient::ReadMessageStream (std::vector<char>& message, uint32_t timeout_ms) +{ + auto start = std::chrono::steady_clock::now(); + message.clear(); + + Error error; + lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; + char buffer[1024]; + while (error.Success() && status == lldb::eConnectionStatusSuccess) + { + auto end = std::chrono::steady_clock::now(); + uint32_t elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); + if (elapsed_time >= timeout_ms) + return Error("Timed out"); + + size_t n = m_conn.Read(buffer, sizeof(buffer), 1000 * (timeout_ms - elapsed_time), status, &error); + if (n > 0) + message.insert(message.end(), &buffer[0], &buffer[n]); + } + return error; +} + +Error +AdbClient::ReadResponseStatus() +{ + char response_id[5]; + + static const size_t packet_len = 4; + response_id[packet_len] = 0; + + auto error = ReadAllBytes (response_id, packet_len); + if (error.Fail ()) + return error; + + if (strncmp (response_id, kOKAY, packet_len) != 0) + return GetResponseError (response_id); + + return error; +} + +Error +AdbClient::GetResponseError (const char *response_id) +{ + if (strcmp (response_id, kFAIL) != 0) + return Error ("Got unexpected response id from adb: \"%s\"", response_id); + + std::vector<char> error_message; + auto error = ReadMessage (error_message); + if (error.Success ()) + error.SetErrorString (std::string (&error_message[0], error_message.size ()).c_str ()); + + return error; +} + +Error +AdbClient::SwitchDeviceTransport () +{ + std::ostringstream msg; + msg << "host:transport:" << m_device_id; + + auto error = SendMessage (msg.str ()); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::PullFile (const FileSpec &remote_file, const FileSpec &local_file) +{ + auto error = StartSync (); + if (error.Fail ()) + return error; + + const auto local_file_path = local_file.GetPath (); + llvm::FileRemover local_file_remover (local_file_path.c_str ()); + + std::ofstream dst (local_file_path, std::ios::out | std::ios::binary); + if (!dst.is_open ()) + return Error ("Unable to open local file %s", local_file_path.c_str()); + + const auto remote_file_path = remote_file.GetPath (false); + error = SendSyncRequest (kRECV, remote_file_path.length (), remote_file_path.c_str ()); + if (error.Fail ()) + return error; + + std::vector<char> chunk; + bool eof = false; + while (!eof) + { + error = PullFileChunk (chunk, eof); + if (error.Fail ()) + return error; + if (!eof) + dst.write (&chunk[0], chunk.size ()); + } + + local_file_remover.releaseFile (); + return error; +} + +Error +AdbClient::PushFile (const FileSpec &local_file, const FileSpec &remote_file) +{ + auto error = StartSync (); + if (error.Fail ()) + return error; + + const auto local_file_path (local_file.GetPath ()); + std::ifstream src (local_file_path.c_str(), std::ios::in | std::ios::binary); + if (!src.is_open ()) + return Error ("Unable to open local file %s", local_file_path.c_str()); + + std::stringstream file_description; + file_description << remote_file.GetPath(false).c_str() << "," << kDefaultMode; + std::string file_description_str = file_description.str(); + error = SendSyncRequest (kSEND, file_description_str.length(), file_description_str.c_str()); + if (error.Fail ()) + return error; + + char chunk[kMaxPushData]; + while (!src.eof() && !src.read(chunk, kMaxPushData).bad()) + { + size_t chunk_size = src.gcount(); + error = SendSyncRequest(kDATA, chunk_size, chunk); + if (error.Fail ()) + return Error ("Failed to send file chunk: %s", error.AsCString ()); + } + error = SendSyncRequest(kDONE, local_file.GetModificationTime().seconds(), nullptr); + if (error.Fail ()) + return error; + + std::string response_id; + uint32_t data_len; + error = ReadSyncHeader (response_id, data_len); + if (error.Fail ()) + return Error ("Failed to read DONE response: %s", error.AsCString ()); + if (response_id == kFAIL) + { + std::string error_message (data_len, 0); + error = ReadAllBytes (&error_message[0], data_len); + if (error.Fail ()) + return Error ("Failed to read DONE error message: %s", error.AsCString ()); + return Error ("Failed to push file: %s", error_message.c_str ()); + } + else if (response_id != kOKAY) + return Error ("Got unexpected DONE response: %s", response_id.c_str ()); + + // If there was an error reading the source file, finish the adb file + // transfer first so that adb isn't expecting any more data. + if (src.bad()) + return Error ("Failed read on %s", local_file_path.c_str()); + return error; +} + +Error +AdbClient::StartSync () +{ + auto error = SwitchDeviceTransport (); + if (error.Fail ()) + return Error ("Failed to switch to device transport: %s", error.AsCString ()); + + error = Sync (); + if (error.Fail ()) + return Error ("Sync failed: %s", error.AsCString ()); + + return error; +} + +Error +AdbClient::Sync () +{ + auto error = SendMessage ("sync:", false); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::PullFileChunk (std::vector<char> &buffer, bool &eof) +{ + buffer.clear (); + + std::string response_id; + uint32_t data_len; + auto error = ReadSyncHeader (response_id, data_len); + if (error.Fail ()) + return error; + + if (response_id == kDATA) + { + buffer.resize (data_len, 0); + error = ReadAllBytes (&buffer[0], data_len); + if (error.Fail ()) + buffer.clear (); + } + else if (response_id == kDONE) + { + eof = true; + } + else if (response_id == kFAIL) + { + std::string error_message (data_len, 0); + error = ReadAllBytes (&error_message[0], data_len); + if (error.Fail ()) + return Error ("Failed to read pull error message: %s", error.AsCString ()); + return Error ("Failed to pull file: %s", error_message.c_str ()); + } + else + return Error ("Pull failed with unknown response: %s", response_id.c_str ()); + + return Error (); +} + +Error +AdbClient::SendSyncRequest (const char *request_id, const uint32_t data_len, const void *data) +{ + const DataBufferSP data_sp (new DataBufferHeap (kSyncPacketLen, 0)); + DataEncoder encoder (data_sp, eByteOrderLittle, sizeof (void*)); + auto offset = encoder.PutData (0, request_id, strlen(request_id)); + encoder.PutU32 (offset, data_len); + + Error error; + ConnectionStatus status; + m_conn.Write (data_sp->GetBytes (), kSyncPacketLen, status, &error); + if (error.Fail ()) + return error; + + if (data) + m_conn.Write (data, data_len, status, &error); + return error; +} + +Error +AdbClient::ReadSyncHeader (std::string &response_id, uint32_t &data_len) +{ + char buffer[kSyncPacketLen]; + + auto error = ReadAllBytes (buffer, kSyncPacketLen); + if (error.Success ()) + { + response_id.assign (&buffer[0], 4); + DataExtractor extractor (&buffer[4], 4, eByteOrderLittle, sizeof (void*)); + offset_t offset = 0; + data_len = extractor.GetU32 (&offset); + } + + return error; +} + +Error +AdbClient::ReadAllBytes (void *buffer, size_t size) +{ + Error error; + ConnectionStatus status; + char *read_buffer = static_cast<char*>(buffer); + + size_t tota_read_bytes = 0; + while (tota_read_bytes < size) + { + auto read_bytes = m_conn.Read (read_buffer + tota_read_bytes, size - tota_read_bytes, kReadTimeout, status, &error); + if (error.Fail ()) + return error; + tota_read_bytes += read_bytes; + } + return error; +} + +Error +AdbClient::Stat (const FileSpec &remote_file, uint32_t &mode, uint32_t &size, uint32_t &mtime) +{ + auto error = StartSync (); + if (error.Fail ()) + return error; + + const std::string remote_file_path (remote_file.GetPath (false)); + error = SendSyncRequest (kSTAT, remote_file_path.length (), remote_file_path.c_str ()); + if (error.Fail ()) + return Error ("Failed to send request: %s", error.AsCString ()); + + static const size_t stat_len = strlen (kSTAT); + static const size_t response_len = stat_len + (sizeof (uint32_t) * 3); + + std::vector<char> buffer (response_len); + error = ReadAllBytes (&buffer[0], buffer.size ()); + if (error.Fail ()) + return Error ("Failed to read response: %s", error.AsCString ()); + + DataExtractor extractor (&buffer[0], buffer.size (), eByteOrderLittle, sizeof (void*)); + offset_t offset = 0; + + const void* command = extractor.GetData (&offset, stat_len); + if (!command) + return Error ("Failed to get response command"); + const char* command_str = static_cast<const char*> (command); + if (strncmp (command_str, kSTAT, stat_len)) + return Error ("Got invalid stat command: %s", command_str); + + mode = extractor.GetU32 (&offset); + size = extractor.GetU32 (&offset); + mtime = extractor.GetU32 (&offset); + return Error (); +} + +Error +AdbClient::Shell (const char* command, uint32_t timeout_ms, std::string* output) +{ + auto error = SwitchDeviceTransport (); + if (error.Fail ()) + return Error ("Failed to switch to device transport: %s", error.AsCString ()); + + StreamString adb_command; + adb_command.Printf("shell:%s", command); + error = SendMessage (adb_command.GetData(), false); + if (error.Fail ()) + return error; + + error = ReadResponseStatus (); + if (error.Fail ()) + return error; + + std::vector<char> in_buffer; + error = ReadMessageStream (in_buffer, timeout_ms); + if (error.Fail()) + return error; + + if (output) + output->assign(in_buffer.begin(), in_buffer.end()); + return error; +} diff --git a/source/Plugins/Platform/Android/AdbClient.h b/source/Plugins/Platform/Android/AdbClient.h new file mode 100644 index 000000000000..4ec411d1411d --- /dev/null +++ b/source/Plugins/Platform/Android/AdbClient.h @@ -0,0 +1,132 @@ +//===-- AdbClient.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AdbClient_h_ +#define liblldb_AdbClient_h_ + +// C Includes + +// C++ Includes + +#include <list> +#include <string> +#include <vector> + +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Error.h" +#include "lldb/Host/ConnectionFileDescriptor.h" + +namespace lldb_private { + +class FileSpec; + +namespace platform_android { + +class AdbClient +{ +public: + enum UnixSocketNamespace + { + UnixSocketNamespaceAbstract, + UnixSocketNamespaceFileSystem, + }; + + using DeviceIDList = std::list<std::string>; + + static Error + CreateByDeviceID(const std::string &device_id, AdbClient &adb); + + AdbClient () = default; + explicit AdbClient (const std::string &device_id); + + const std::string& + GetDeviceID() const; + + Error + GetDevices (DeviceIDList &device_list); + + Error + SetPortForwarding (const uint16_t local_port, const uint16_t remote_port); + + Error + SetPortForwarding (const uint16_t local_port, + const char* remote_socket_name, + const UnixSocketNamespace socket_namespace); + + Error + DeletePortForwarding (const uint16_t local_port); + + Error + PullFile (const FileSpec &remote_file, const FileSpec &local_file); + + Error + PushFile (const FileSpec &local_file, const FileSpec &remote_file); + + Error + Stat (const FileSpec &remote_file, uint32_t &mode, uint32_t &size, uint32_t &mtime); + + Error + Shell (const char* command, uint32_t timeout_ms, std::string* output); + +private: + Error + Connect (); + + void + SetDeviceID (const std::string &device_id); + + Error + SendMessage (const std::string &packet, const bool reconnect = true); + + Error + SendDeviceMessage (const std::string &packet); + + Error + SendSyncRequest (const char *request_id, const uint32_t data_len, const void *data); + + Error + ReadSyncHeader (std::string &response_id, uint32_t &data_len); + + Error + ReadMessage (std::vector<char> &message); + + Error + ReadMessageStream (std::vector<char> &message, uint32_t timeout_ms); + + Error + GetResponseError (const char *response_id); + + Error + ReadResponseStatus (); + + Error + SwitchDeviceTransport (); + + Error + Sync (); + + Error + StartSync (); + + Error + PullFileChunk (std::vector<char> &buffer, bool &eof); + + Error + ReadAllBytes (void *buffer, size_t size); + + std::string m_device_id; + ConnectionFileDescriptor m_conn; +}; + +} // namespace platform_android +} // namespace lldb_private + +#endif // liblldb_AdbClient_h_ diff --git a/source/Plugins/Platform/Android/CMakeLists.txt b/source/Plugins/Platform/Android/CMakeLists.txt new file mode 100644 index 000000000000..e831a33a4b6d --- /dev/null +++ b/source/Plugins/Platform/Android/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_library(lldbPluginPlatformAndroid + AdbClient.cpp + PlatformAndroid.cpp + PlatformAndroidRemoteGDBServer.cpp + ) diff --git a/source/Plugins/Platform/Android/Makefile b/source/Plugins/Platform/Android/Makefile new file mode 100644 index 000000000000..aa186f924e66 --- /dev/null +++ b/source/Plugins/Platform/Android/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/Android/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 := lldbPluginPlatformAndroid +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Android/PlatformAndroid.cpp b/source/Plugins/Platform/Android/PlatformAndroid.cpp new file mode 100644 index 000000000000..e842884c046a --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroid.cpp @@ -0,0 +1,389 @@ +//===-- PlatformAndroid.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/StringConvert.h" +#include "Utility/UriParser.h" + +// Project includes +#include "AdbClient.h" +#include "PlatformAndroid.h" +#include "PlatformAndroidRemoteGDBServer.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_android; + +static uint32_t g_initialize_count = 0; +static const unsigned int g_android_default_cache_size = 2048; // Fits inside 4k adb packet. + +void +PlatformAndroid::Initialize () +{ + PlatformLinux::Initialize (); + + if (g_initialize_count++ == 0) + { +#if defined(__ANDROID__) + PlatformSP default_platform_sp (new PlatformAndroid(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform (default_platform_sp); +#endif + PluginManager::RegisterPlugin (PlatformAndroid::GetPluginNameStatic(false), + PlatformAndroid::GetPluginDescriptionStatic(false), + PlatformAndroid::CreateInstance); + } +} + +void +PlatformAndroid::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformAndroid::CreateInstance); + } + } + + PlatformLinux::Terminate (); +} + +PlatformSP +PlatformAndroid::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = "<null>"; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; + + log->Printf ("PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::PC: + create = true; + break; + +#if defined(__ANDROID__) + // Only accept "unknown" for the vendor if the host is android and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified_ + case llvm::Triple::VendorType::UnknownVendor: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Android: + break; + +#if defined(__ANDROID__) + // Only accept "unknown" for the OS if the host is android and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::OSType::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + + if (create) + { + if (log) + log->Printf ("PlatformAndroid::%s() creating remote-android platform", __FUNCTION__); + return PlatformSP(new PlatformAndroid(false)); + } + + if (log) + log->Printf ("PlatformAndroid::%s() aborting creation of remote-android platform", __FUNCTION__); + + return PlatformSP(); +} + +PlatformAndroid::PlatformAndroid (bool is_host) : + PlatformLinux(is_host), + m_sdk_version(0) +{ +} + +PlatformAndroid::~PlatformAndroid() +{ +} + +ConstString +PlatformAndroid::GetPluginNameStatic (bool is_host) +{ + if (is_host) + { + static ConstString g_host_name(Platform::GetHostPlatformName ()); + return g_host_name; + } + else + { + static ConstString g_remote_name("remote-android"); + return g_remote_name; + } +} + +const char * +PlatformAndroid::GetPluginDescriptionStatic (bool is_host) +{ + if (is_host) + return "Local Android user platform plug-in."; + else + return "Remote Android user platform plug-in."; +} + +ConstString +PlatformAndroid::GetPluginName() +{ + return GetPluginNameStatic(IsHost()); +} + +Error +PlatformAndroid::ConnectRemote(Args& args) +{ + m_device_id.clear(); + + if (IsHost()) + { + return Error ("can't connect to the host platform '%s', always connected", GetPluginName().GetCString()); + } + + if (!m_remote_platform_sp) + m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer()); + + int port; + std::string scheme, host, path; + const char *url = args.GetArgumentAtIndex(0); + if (!url) + return Error("URL is null."); + if (!UriParser::Parse(url, scheme, host, port, path)) + return Error("Invalid URL: %s", url); + if (host != "localhost") + m_device_id = host; + + auto error = PlatformLinux::ConnectRemote(args); + if (error.Success()) + { + AdbClient adb; + error = AdbClient::CreateByDeviceID(m_device_id, adb); + if (error.Fail()) + return error; + + m_device_id = adb.GetDeviceID(); + } + return error; +} + +Error +PlatformAndroid::GetFile (const FileSpec& source, + const FileSpec& destination) +{ + if (IsHost() || !m_remote_platform_sp) + return PlatformLinux::GetFile(source, destination); + + FileSpec source_spec (source.GetPath (false), false, FileSpec::ePathSyntaxPosix); + if (source_spec.IsRelative()) + source_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (source_spec.GetCString (false)); + + AdbClient adb (m_device_id); + return adb.PullFile (source_spec, destination); +} + +Error +PlatformAndroid::PutFile (const FileSpec& source, + const FileSpec& destination, + uint32_t uid, + uint32_t gid) +{ + if (IsHost() || !m_remote_platform_sp) + return PlatformLinux::PutFile (source, destination, uid, gid); + + FileSpec destination_spec (destination.GetPath (false), false, FileSpec::ePathSyntaxPosix); + if (destination_spec.IsRelative()) + destination_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (destination_spec.GetCString (false)); + + AdbClient adb (m_device_id); + // TODO: Set correct uid and gid on remote file. + return adb.PushFile(source, destination_spec); +} + +const char * +PlatformAndroid::GetCacheHostname () +{ + return m_device_id.c_str (); +} + +Error +PlatformAndroid::DownloadModuleSlice (const FileSpec &src_file_spec, + const uint64_t src_offset, + const uint64_t src_size, + const FileSpec &dst_file_spec) +{ + if (src_offset != 0) + return Error ("Invalid offset - %" PRIu64, src_offset); + + return GetFile (src_file_spec, dst_file_spec); +} + +Error +PlatformAndroid::DisconnectRemote() +{ + Error error = PlatformLinux::DisconnectRemote(); + if (error.Success()) + { + m_device_id.clear(); + m_sdk_version = 0; + } + return error; +} + +uint32_t +PlatformAndroid::GetDefaultMemoryCacheLineSize() +{ + return g_android_default_cache_size; +} + +uint32_t +PlatformAndroid::GetSdkVersion() +{ + if (!IsConnected()) + return 0; + + if (m_sdk_version != 0) + return m_sdk_version; + + std::string version_string; + AdbClient adb(m_device_id); + Error error = adb.Shell("getprop ro.build.version.sdk", 5000 /* ms */, &version_string); + version_string = llvm::StringRef(version_string).trim().str(); + + if (error.Fail() || version_string.empty()) + { + Log* log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM); + if (log) + log->Printf("Get SDK version failed. (error: %s, output: %s)", + error.AsCString(), version_string.c_str()); + return 0; + } + + m_sdk_version = StringConvert::ToUInt32(version_string.c_str()); + return m_sdk_version; +} + +Error +PlatformAndroid::DownloadSymbolFile (const lldb::ModuleSP& module_sp, + const FileSpec& dst_file_spec) +{ + // For oat file we can try to fetch additional debug info from the device + if (module_sp->GetFileSpec().GetFileNameExtension() != ConstString("oat")) + return Error("Symbol file downloading only supported for oat files"); + + // If we have no information about the platform file we can't execute oatdump + if (!module_sp->GetPlatformFileSpec()) + return Error("No platform file specified"); + + // Symbolizer isn't available before SDK version 23 + if (GetSdkVersion() < 23) + return Error("Symbol file generation only supported on SDK 23+"); + + // If we already have symtab then we don't have to try and generate one + if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) != nullptr) + return Error("Symtab already available in the module"); + + AdbClient adb(m_device_id); + + std::string tmpdir; + Error error = adb.Shell("mktemp --directory --tmpdir /data/local/tmp", 5000 /* ms */, &tmpdir); + if (error.Fail() || tmpdir.empty()) + return Error("Failed to generate temporary directory on the device (%s)", error.AsCString()); + tmpdir = llvm::StringRef(tmpdir).trim().str(); + + // Create file remover for the temporary directory created on the device + std::unique_ptr<std::string, std::function<void(std::string*)>> tmpdir_remover( + &tmpdir, + [this, &adb](std::string* s) { + StreamString command; + command.Printf("rm -rf %s", s->c_str()); + Error error = adb.Shell(command.GetData(), 5000 /* ms */, nullptr); + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (error.Fail()) + log->Printf("Failed to remove temp directory: %s", error.AsCString()); + } + ); + + FileSpec symfile_platform_filespec(tmpdir.c_str(), false); + symfile_platform_filespec.AppendPathComponent("symbolized.oat"); + + // Execute oatdump on the remote device to generate a file with symtab + StreamString command; + command.Printf("oatdump --symbolize=%s --output=%s", + module_sp->GetPlatformFileSpec().GetCString(false), + symfile_platform_filespec.GetCString(false)); + error = adb.Shell(command.GetData(), 60000 /* ms */, nullptr); + if (error.Fail()) + return Error("Oatdump failed: %s", error.AsCString()); + + // Download the symbolfile from the remote device + return GetFile(symfile_platform_filespec, dst_file_spec); +} + +bool +PlatformAndroid::GetRemoteOSVersion () +{ + m_major_os_version = GetSdkVersion(); + m_minor_os_version = 0; + m_update_os_version = 0; + return m_major_os_version != 0; +} + +const char* +PlatformAndroid::GetLibdlFunctionDeclarations() const +{ + return R"( + extern "C" void* dlopen(const char*, int) asm("__dl_dlopen"); + extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym"); + extern "C" int dlclose(void*) asm("__dl_dlclose"); + extern "C" char* dlerror(void) asm("__dl_dlerror"); + )"; +} diff --git a/source/Plugins/Platform/Android/PlatformAndroid.h b/source/Plugins/Platform/Android/PlatformAndroid.h new file mode 100644 index 000000000000..119d0a0bdf04 --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroid.h @@ -0,0 +1,114 @@ +//===-- PlatformAndroid.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformAndroid_h_ +#define liblldb_PlatformAndroid_h_ + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes +#include "Plugins/Platform/Linux/PlatformLinux.h" + +namespace lldb_private { +namespace platform_android { + + class PlatformAndroid : public platform_linux::PlatformLinux + { + public: + PlatformAndroid(bool is_host); + + ~PlatformAndroid() override; + + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const ArchSpec *arch); + + static ConstString + GetPluginNameStatic (bool is_host); + + static const char * + GetPluginDescriptionStatic (bool is_host); + + ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + + Error + ConnectRemote (Args& args) override; + + Error + GetFile (const FileSpec& source, + const FileSpec& destination) override; + + Error + PutFile (const FileSpec& source, + const FileSpec& destination, + uint32_t uid = UINT32_MAX, + uint32_t gid = UINT32_MAX) override; + + uint32_t + GetSdkVersion(); + + bool + GetRemoteOSVersion() override; + + Error + DisconnectRemote () override; + + uint32_t + GetDefaultMemoryCacheLineSize() override; + + protected: + const char * + GetCacheHostname () override; + + Error + DownloadModuleSlice (const FileSpec &src_file_spec, + const uint64_t src_offset, + const uint64_t src_size, + const FileSpec &dst_file_spec) override; + + Error + DownloadSymbolFile (const lldb::ModuleSP& module_sp, + const FileSpec& dst_file_spec) override; + + const char* + GetLibdlFunctionDeclarations() const override; + + private: + std::string m_device_id; + uint32_t m_sdk_version; + + DISALLOW_COPY_AND_ASSIGN (PlatformAndroid); + }; + +} // namespace platofor_android +} // namespace lldb_private + +#endif // liblldb_PlatformAndroid_h_ diff --git a/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp new file mode 100644 index 000000000000..3d91dd6b7a32 --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp @@ -0,0 +1,275 @@ +//===-- PlatformAndroidRemoteGDBServer.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Other libraries and framework includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/common/TCPSocket.h" +#include "PlatformAndroidRemoteGDBServer.h" +#include "Utility/UriParser.h" + +#include <sstream> + +using namespace lldb; +using namespace lldb_private; +using namespace platform_android; + +static const lldb::pid_t g_remote_platform_pid = 0; // Alias for the process id of lldb-platform + +static Error +ForwardPortWithAdb (const uint16_t local_port, + const uint16_t remote_port, + const char* remote_socket_name, + const llvm::Optional<AdbClient::UnixSocketNamespace>& socket_namespace, + std::string& device_id) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + + AdbClient adb; + auto error = AdbClient::CreateByDeviceID(device_id, adb); + if (error.Fail ()) + return error; + + device_id = adb.GetDeviceID(); + if (log) + log->Printf("Connected to Android device \"%s\"", device_id.c_str ()); + + if (remote_port != 0) + { + if (log) + log->Printf("Forwarding remote TCP port %d to local TCP port %d", remote_port, local_port); + return adb.SetPortForwarding(local_port, remote_port); + } + + if (log) + log->Printf("Forwarding remote socket \"%s\" to local TCP port %d", remote_socket_name, local_port); + + if (!socket_namespace) + return Error("Invalid socket namespace"); + + return adb.SetPortForwarding(local_port, remote_socket_name, *socket_namespace); +} + +static Error +DeleteForwardPortWithAdb (uint16_t local_port, const std::string& device_id) +{ + AdbClient adb (device_id); + return adb.DeletePortForwarding (local_port); +} + +static Error +FindUnusedPort (uint16_t& port) +{ + Error error; + std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(false, error)); + if (error.Fail()) + return error; + + error = tcp_socket->Listen("127.0.0.1:0", 1); + if (error.Success()) + port = tcp_socket->GetLocalPortNumber(); + + return error; +} + +PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer () +{ +} + +PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer () +{ + for (const auto& it : m_port_forwards) + DeleteForwardPortWithAdb(it.second, m_device_id); +} + +bool +PlatformAndroidRemoteGDBServer::LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url) +{ + uint16_t remote_port = 0; + std::string socket_name; + if (!m_gdb_client.LaunchGDBServer ("127.0.0.1", pid, remote_port, socket_name)) + return false; + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + + auto error = MakeConnectURL (pid, + remote_port, + socket_name.c_str (), + connect_url); + if (error.Success() && log) + log->Printf("gdbserver connect URL: %s", connect_url.c_str()); + + return error.Success(); +} + +bool +PlatformAndroidRemoteGDBServer::KillSpawnedProcess (lldb::pid_t pid) +{ + DeleteForwardPort (pid); + return m_gdb_client.KillSpawnedProcess (pid); +} + +Error +PlatformAndroidRemoteGDBServer::ConnectRemote (Args& args) +{ + m_device_id.clear(); + + if (args.GetArgumentCount() != 1) + return Error("\"platform connect\" takes a single argument: <connect-url>"); + + int remote_port; + std::string scheme, host, path; + const char *url = args.GetArgumentAtIndex (0); + if (!url) + return Error("URL is null."); + if (!UriParser::Parse (url, scheme, host, remote_port, path)) + return Error("Invalid URL: %s", url); + if (host != "localhost") + m_device_id = host; + + m_socket_namespace.reset(); + if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME) + m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem; + else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME) + m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract; + + std::string connect_url; + auto error = MakeConnectURL (g_remote_platform_pid, + (remote_port < 0) ? 0 : remote_port, + path.c_str (), + connect_url); + + if (error.Fail ()) + return error; + + args.ReplaceArgumentAtIndex (0, connect_url.c_str ()); + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("Rewritten platform connect URL: %s", connect_url.c_str()); + + error = PlatformRemoteGDBServer::ConnectRemote(args); + if (error.Fail ()) + DeleteForwardPort (g_remote_platform_pid); + + return error; +} + +Error +PlatformAndroidRemoteGDBServer::DisconnectRemote () +{ + DeleteForwardPort (g_remote_platform_pid); + return PlatformRemoteGDBServer::DisconnectRemote (); +} + +void +PlatformAndroidRemoteGDBServer::DeleteForwardPort (lldb::pid_t pid) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + + auto it = m_port_forwards.find(pid); + if (it == m_port_forwards.end()) + return; + + const auto port = it->second; + const auto error = DeleteForwardPortWithAdb(port, m_device_id); + if (error.Fail()) { + if (log) + log->Printf("Failed to delete port forwarding (pid=%" PRIu64 ", port=%d, device=%s): %s", + pid, port, m_device_id.c_str(), error.AsCString()); + } + m_port_forwards.erase(it); +} + +Error +PlatformAndroidRemoteGDBServer::MakeConnectURL(const lldb::pid_t pid, + const uint16_t remote_port, + const char* remote_socket_name, + std::string& connect_url) +{ + static const int kAttempsNum = 5; + + Error error; + // There is a race possibility that somebody will occupy + // a port while we're in between FindUnusedPort and ForwardPortWithAdb - + // adding the loop to mitigate such problem. + for (auto i = 0; i < kAttempsNum; ++i) + { + uint16_t local_port = 0; + error = FindUnusedPort(local_port); + if (error.Fail()) + return error; + + error = ForwardPortWithAdb(local_port, + remote_port, + remote_socket_name, + m_socket_namespace, + m_device_id); + if (error.Success()) + { + m_port_forwards[pid] = local_port; + std::ostringstream url_str; + url_str << "connect://localhost:" << local_port; + connect_url = url_str.str(); + break; + } + } + + return error; +} + +lldb::ProcessSP +PlatformAndroidRemoteGDBServer::ConnectProcess(const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) +{ + // We don't have the pid of the remote gdbserver when it isn't started by us but we still want + // to store the list of port forwards we set up in our port forward map. Generate a fake pid for + // these cases what won't collide with any other valid pid on android. + static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; + + int remote_port; + std::string scheme, host, path; + if (!UriParser::Parse(connect_url, scheme, host, remote_port, path)) + { + error.SetErrorStringWithFormat("Invalid URL: %s", connect_url); + return nullptr; + } + + std::string new_connect_url; + error = MakeConnectURL(s_remote_gdbserver_fake_pid--, + (remote_port < 0) ? 0 : remote_port, + path.c_str(), + new_connect_url); + if (error.Fail()) + return nullptr; + + return PlatformRemoteGDBServer::ConnectProcess(new_connect_url.c_str(), + plugin_name, + debugger, + target, + error); +} + +size_t +PlatformAndroidRemoteGDBServer::ConnectToWaitingProcesses(Debugger& debugger, Error& error) +{ + std::vector<std::string> connection_urls; + GetPendingGdbServerList(connection_urls); + + for (size_t i = 0; i < connection_urls.size(); ++i) + { + ConnectProcess(connection_urls[i].c_str(), nullptr, debugger, nullptr, error); + if (error.Fail()) + return i; // We already connected to i process succsessfully + } + return connection_urls.size(); +} diff --git a/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h new file mode 100644 index 000000000000..3d2653812ded --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h @@ -0,0 +1,79 @@ +//===-- PlatformAndroidRemoteGDBServer.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformAndroidRemoteGDBServer_h_ +#define liblldb_PlatformAndroidRemoteGDBServer_h_ + +// C Includes +// C++ Includes +#include <map> +#include <utility> + +// Other libraries and framework includes +// Project includes +#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" + +#include "llvm/ADT/Optional.h" + +#include "AdbClient.h" + +namespace lldb_private { +namespace platform_android { + +class PlatformAndroidRemoteGDBServer : public platform_gdb_server::PlatformRemoteGDBServer +{ +public: + PlatformAndroidRemoteGDBServer(); + + ~PlatformAndroidRemoteGDBServer() override; + + Error + ConnectRemote (Args& args) override; + + Error + DisconnectRemote () override; + + lldb::ProcessSP + ConnectProcess (const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) override; + + size_t + ConnectToWaitingProcesses(lldb_private::Debugger& debugger, lldb_private::Error& error) override; + +protected: + std::string m_device_id; + std::map<lldb::pid_t, uint16_t> m_port_forwards; + llvm::Optional<AdbClient::UnixSocketNamespace> m_socket_namespace; + + bool + LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url) override; + + bool + KillSpawnedProcess (lldb::pid_t pid) override; + + void + DeleteForwardPort (lldb::pid_t pid); + + Error + MakeConnectURL(const lldb::pid_t pid, + const uint16_t remote_port, + const char* remote_socket_name, + std::string& connect_url); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformAndroidRemoteGDBServer); +}; + +} // namespace platform_android +} // namespace lldb_private + +#endif // liblldb_PlatformAndroidRemoteGDBServer_h_ diff --git a/source/Plugins/Platform/CMakeLists.txt b/source/Plugins/Platform/CMakeLists.txt new file mode 100644 index 000000000000..2e3a3f7c1b2e --- /dev/null +++ b/source/Plugins/Platform/CMakeLists.txt @@ -0,0 +1,16 @@ +#if (CMAKE_SYSTEM_NAME MATCHES "Linux") + add_subdirectory(Linux) +#elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + add_subdirectory(FreeBSD) +#elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + add_subdirectory(NetBSD) +#elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_subdirectory(MacOSX) +#elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") + add_subdirectory(Windows) +#endif() + +add_subdirectory(POSIX) +add_subdirectory(gdb-server) +add_subdirectory(Kalimba) +add_subdirectory(Android) diff --git a/source/Plugins/Platform/FreeBSD/CMakeLists.txt b/source/Plugins/Platform/FreeBSD/CMakeLists.txt new file mode 100644 index 000000000000..57153969c3b4 --- /dev/null +++ b/source/Plugins/Platform/FreeBSD/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformFreeBSD + PlatformFreeBSD.cpp + ) diff --git a/source/Plugins/Platform/FreeBSD/Makefile b/source/Plugins/Platform/FreeBSD/Makefile new file mode 100644 index 000000000000..e5c25d8504df --- /dev/null +++ b/source/Plugins/Platform/FreeBSD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/FreeBSD/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 := lldbPluginPlatformFreeBSD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Kalimba/CMakeLists.txt b/source/Plugins/Platform/Kalimba/CMakeLists.txt new file mode 100644 index 000000000000..df0bf9761a00 --- /dev/null +++ b/source/Plugins/Platform/Kalimba/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformKalimba + PlatformKalimba.cpp + ) diff --git a/source/Plugins/Platform/Kalimba/Makefile b/source/Plugins/Platform/Kalimba/Makefile new file mode 100644 index 000000000000..c22b7d21c13d --- /dev/null +++ b/source/Plugins/Platform/Kalimba/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/Kalimba/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 := lldbPluginPlatformKalimba +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Kalimba/PlatformKalimba.cpp b/source/Plugins/Platform/Kalimba/PlatformKalimba.cpp new file mode 100644 index 000000000000..2f1e4d554320 --- /dev/null +++ b/source/Plugins/Platform/Kalimba/PlatformKalimba.cpp @@ -0,0 +1,324 @@ +//===-- PlatformKalimba.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformKalimba.h" +#include "lldb/Host/Config.h" + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +static uint32_t g_initialize_count = 0; + +PlatformSP +PlatformKalimba::CreateInstance (bool force, const ArchSpec *arch) +{ + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::CSR: + create = true; + break; + + default: + break; + } + } + if (create) + return PlatformSP(new PlatformKalimba(false)); + return PlatformSP(); +} + +lldb_private::ConstString +PlatformKalimba::GetPluginNameStatic (bool /*is_host*/) +{ + static ConstString g_remote_name("kalimba"); + return g_remote_name; +} + +const char * +PlatformKalimba::GetPluginDescriptionStatic (bool /*is_host*/) +{ + return "Kalimba user platform plug-in."; +} + +lldb_private::ConstString +PlatformKalimba::GetPluginName() +{ + return GetPluginNameStatic(false); +} + +void +PlatformKalimba::Initialize () +{ + Platform::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin(PlatformKalimba::GetPluginNameStatic(false), + PlatformKalimba::GetPluginDescriptionStatic(false), + PlatformKalimba::CreateInstance); + } +} + +void +PlatformKalimba::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformKalimba::CreateInstance); + } + } + + Platform::Terminate (); +} + +Error +PlatformKalimba::ResolveExecutable (const ModuleSpec &ms, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + char exe_path[PATH_MAX]; + ModuleSpec resolved_module_spec(ms); + + if (!resolved_module_spec.GetFileSpec().Exists()) + { + resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat("unable to find executable for '%s'", exe_path); + } + + if (error.Success()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + if (error.Fail()) + { + // If we failed, it may be because the vendor and os aren't known. If that is the + // case, try setting them to the host architecture and give it another try. + llvm::Triple &module_triple = resolved_module_spec.GetArchitecture().GetTriple(); + bool is_vendor_specified = (module_triple.getVendor() != llvm::Triple::UnknownVendor); + bool is_os_specified = (module_triple.getOS() != llvm::Triple::UnknownOS); + if (!is_vendor_specified || !is_os_specified) + { + const llvm::Triple &host_triple = HostInfo::GetArchitecture(HostInfo::eArchKindDefault).GetTriple(); + + if (!is_vendor_specified) + module_triple.setVendorName (host_triple.getVendorName()); + if (!is_os_specified) + module_triple.setOSName (host_triple.getOSName()); + + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + } + } + + // TODO find out why exe_module_sp might be NULL + if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL) + { + exe_module_sp.reset(); + error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + } + else + { + // No valid architecture was specified, ask the platform for + // the architectures that we should be using (in the correct order) + // and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + } + + return error; +} + +Error +PlatformKalimba::GetFileWithUUID (const FileSpec & /*platform_file*/, + const UUID * /*uuid_ptr*/, FileSpec & /*local_file*/) +{ + return Error(); +} + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformKalimba::PlatformKalimba (bool is_host) : + Platform(is_host), // This is the local host platform + m_remote_platform_sp () +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformKalimba::~PlatformKalimba() +{ +} + +bool +PlatformKalimba::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + bool success = false; + if (IsHost()) + { + success = false; + } + else + { + if (m_remote_platform_sp) + success = m_remote_platform_sp->GetProcessInfo (pid, process_info); + } + return success; +} + +bool +PlatformKalimba::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + if (idx == 0) + { + arch = ArchSpec("kalimba3-csr-unknown"); + return true; + } + if (idx == 1) + { + arch = ArchSpec("kalimba4-csr-unknown"); + return true; + } + if (idx == 2) + { + arch = ArchSpec("kalimba5-csr-unknown"); + return true; + } + return false; +} + +void +PlatformKalimba::GetStatus (Stream &strm) +{ + Platform::GetStatus(strm); +} + +size_t +PlatformKalimba::GetSoftwareBreakpointTrapOpcode (Target & /*target*/, + BreakpointSite * /*bp_site*/) +{ + // the target hardware does not support software breakpoints + return 0; +} + +Error +PlatformKalimba::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + + if (IsHost()) + { + error.SetErrorString ("native execution is not possible"); + } + else + { + error.SetErrorString ("the platform is not currently connected"); + } + return error; +} + +lldb::ProcessSP +PlatformKalimba::Attach(ProcessAttachInfo &attach_info, + Debugger &debugger, + Target *target, + Error &error) +{ + lldb::ProcessSP process_sp; + if (IsHost()) + { + error.SetErrorString ("native execution is not possible"); + } + else + { + if (m_remote_platform_sp) + process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, error); + else + error.SetErrorString ("the platform is not currently connected"); + } + return process_sp; +} + +void +PlatformKalimba::CalculateTrapHandlerSymbolNames () +{ + // TODO Research this sometime. +} diff --git a/source/Plugins/Platform/Kalimba/PlatformKalimba.h b/source/Plugins/Platform/Kalimba/PlatformKalimba.h new file mode 100644 index 000000000000..dd68415838f0 --- /dev/null +++ b/source/Plugins/Platform/Kalimba/PlatformKalimba.h @@ -0,0 +1,99 @@ +//===-- PlatformKalimba.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformKalimba_h_ +#define liblldb_PlatformKalimba_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Platform.h" + +namespace lldb_private { + + class PlatformKalimba : public Platform + { + public: + PlatformKalimba(bool is_host); + + ~PlatformKalimba() override; + + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static lldb_private::ConstString + GetPluginNameStatic (bool is_host); + + static const char * + GetPluginDescriptionStatic (bool is_host); + + lldb_private::ConstString GetPluginName() override; + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + Error ResolveExecutable(const lldb_private::ModuleSpec &module_spec, lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription() override + { + return GetPluginDescriptionStatic(IsHost()); + } + + void GetStatus(Stream &strm) override; + + Error GetFileWithUUID(const FileSpec &platform_file, const UUID *uuid, FileSpec &local_file) override; + + bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &proc_info) override; + + bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override; + + size_t GetSoftwareBreakpointTrapOpcode(Target &target, BreakpointSite *bp_site) override; + + lldb_private::Error LaunchProcess(lldb_private::ProcessLaunchInfo &launch_info) override; + + lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger, Target *target, + Error &error) override; + + // Kalimba processes can not be launched by spawning and attaching. + bool + CanDebugProcess() override + { + return false; + } + + void CalculateTrapHandlerSymbolNames() override; + + protected: + lldb::PlatformSP m_remote_platform_sp; + + private: + DISALLOW_COPY_AND_ASSIGN (PlatformKalimba); + }; + +} // namespace lldb_private + +#endif // liblldb_PlatformKalimba_h_ diff --git a/source/Plugins/Platform/Linux/CMakeLists.txt b/source/Plugins/Platform/Linux/CMakeLists.txt new file mode 100644 index 000000000000..4a9eb1460e38 --- /dev/null +++ b/source/Plugins/Platform/Linux/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformLinux + PlatformLinux.cpp + ) diff --git a/source/Plugins/Platform/Linux/Makefile b/source/Plugins/Platform/Linux/Makefile new file mode 100644 index 000000000000..2877fddf0bcd --- /dev/null +++ b/source/Plugins/Platform/Linux/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/Linux/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 := lldbPluginPlatformLinux +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Linux/PlatformLinux.cpp b/source/Plugins/Platform/Linux/PlatformLinux.cpp new file mode 100644 index 000000000000..8dc9769844c7 --- /dev/null +++ b/source/Plugins/Platform/Linux/PlatformLinux.cpp @@ -0,0 +1,868 @@ +//===-- PlatformLinux.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformLinux.h" +#include "lldb/Host/Config.h" + +// C Includes +#include <stdio.h> +#ifndef LLDB_DISABLE_POSIX +#include <sys/utsname.h> +#endif + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/Property.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" + +// Define these constants from Linux mman.h for use when targeting +// remote linux systems even when host has different values. +#define MAP_PRIVATE 2 +#define MAP_ANON 0x20 + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_linux; + +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +/// Code to handle the PlatformLinux settings +//------------------------------------------------------------------ + +namespace +{ + class PlatformLinuxProperties : public Properties + { + public: + PlatformLinuxProperties(); + + ~PlatformLinuxProperties() override = default; + + static ConstString& + GetSettingName (); + + private: + static const PropertyDefinition* + GetStaticPropertyDefinitions(); + }; + + typedef std::shared_ptr<PlatformLinuxProperties> PlatformLinuxPropertiesSP; + +} // anonymous namespace + +PlatformLinuxProperties::PlatformLinuxProperties() : + Properties () +{ + m_collection_sp.reset (new OptionValueProperties(GetSettingName ())); + m_collection_sp->Initialize (GetStaticPropertyDefinitions ()); +} + +ConstString& +PlatformLinuxProperties::GetSettingName () +{ + static ConstString g_setting_name("linux"); + return g_setting_name; +} + +const PropertyDefinition* +PlatformLinuxProperties::GetStaticPropertyDefinitions() +{ + static PropertyDefinition + g_properties[] = + { + { NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL } + }; + + return g_properties; +} + +static const PlatformLinuxPropertiesSP & +GetGlobalProperties() +{ + static PlatformLinuxPropertiesSP g_settings_sp; + if (!g_settings_sp) + g_settings_sp.reset (new PlatformLinuxProperties ()); + return g_settings_sp; +} + +void +PlatformLinux::DebuggerInitialize (Debugger &debugger) +{ + if (!PluginManager::GetSettingForPlatformPlugin (debugger, PlatformLinuxProperties::GetSettingName())) + { + const bool is_global_setting = true; + PluginManager::CreateSettingForPlatformPlugin (debugger, + GetGlobalProperties()->GetValueProperties(), + ConstString ("Properties for the PlatformLinux plug-in."), + is_global_setting); + } +} + +//------------------------------------------------------------------ + +PlatformSP +PlatformLinux::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = "<null>"; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; + + log->Printf ("PlatformLinux::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getOS()) + { + case llvm::Triple::Linux: + create = true; + break; + +#if defined(__linux__) + // Only accept "unknown" for the OS if the host is linux and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::OSType::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + break; + } + } + + if (create) + { + if (log) + log->Printf ("PlatformLinux::%s() creating remote-linux platform", __FUNCTION__); + return PlatformSP(new PlatformLinux(false)); + } + + if (log) + log->Printf ("PlatformLinux::%s() aborting creation of remote-linux platform", __FUNCTION__); + + return PlatformSP(); +} + +ConstString +PlatformLinux::GetPluginNameStatic (bool is_host) +{ + if (is_host) + { + static ConstString g_host_name(Platform::GetHostPlatformName ()); + return g_host_name; + } + else + { + static ConstString g_remote_name("remote-linux"); + return g_remote_name; + } +} + +const char * +PlatformLinux::GetPluginDescriptionStatic (bool is_host) +{ + if (is_host) + return "Local Linux user platform plug-in."; + else + return "Remote Linux user platform plug-in."; +} + +ConstString +PlatformLinux::GetPluginName() +{ + return GetPluginNameStatic(IsHost()); +} + +void +PlatformLinux::Initialize () +{ + PlatformPOSIX::Initialize (); + + if (g_initialize_count++ == 0) + { +#if defined(__linux__) && !defined(__ANDROID__) + PlatformSP default_platform_sp (new PlatformLinux(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform (default_platform_sp); +#endif + PluginManager::RegisterPlugin(PlatformLinux::GetPluginNameStatic(false), + PlatformLinux::GetPluginDescriptionStatic(false), + PlatformLinux::CreateInstance, + PlatformLinux::DebuggerInitialize); + } +} + +void +PlatformLinux::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformLinux::CreateInstance); + } + } + + PlatformPOSIX::Terminate (); +} + +Error +PlatformLinux::ResolveExecutable (const ModuleSpec &ms, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + char exe_path[PATH_MAX]; + ModuleSpec resolved_module_spec (ms); + + if (IsHost()) + { + // If we have "ls" as the exe_file, resolve the executable location based on + // the current path variables + if (!resolved_module_spec.GetFileSpec().Exists()) + { + resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); + resolved_module_spec.GetFileSpec().SetFile(exe_path, true); + } + + if (!resolved_module_spec.GetFileSpec().Exists()) + resolved_module_spec.GetFileSpec().ResolveExecutableLocation (); + + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + { + error.SetErrorStringWithFormat("unable to find executable for '%s'", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + else + { + if (m_remote_platform_sp) + { + error = GetCachedExecutable (resolved_module_spec, exe_module_sp, module_search_paths_ptr, *m_remote_platform_sp); + } + else + { + // We may connect to a process and use the provided executable (Don't use local $PATH). + + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", exe_path); + } + } + + if (error.Success()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + if (error.Fail()) + { + // If we failed, it may be because the vendor and os aren't known. If that is the + // case, try setting them to the host architecture and give it another try. + llvm::Triple &module_triple = resolved_module_spec.GetArchitecture().GetTriple(); + bool is_vendor_specified = (module_triple.getVendor() != llvm::Triple::UnknownVendor); + bool is_os_specified = (module_triple.getOS() != llvm::Triple::UnknownOS); + if (!is_vendor_specified || !is_os_specified) + { + const llvm::Triple &host_triple = HostInfo::GetArchitecture(HostInfo::eArchKindDefault).GetTriple(); + + if (!is_vendor_specified) + module_triple.setVendorName (host_triple.getVendorName()); + if (!is_os_specified) + module_triple.setOSName (host_triple.getOSName()); + + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + } + } + + // TODO find out why exe_module_sp might be NULL + if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL) + { + exe_module_sp.reset(); + error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + } + else + { + // No valid architecture was specified, ask the platform for + // the architectures that we should be using (in the correct order) + // and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + } + + return error; +} + +Error +PlatformLinux::GetFileWithUUID (const FileSpec &platform_file, + const UUID *uuid_ptr, FileSpec &local_file) +{ + if (IsRemote()) + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetFileWithUUID (platform_file, uuid_ptr, local_file); + } + + // Default to the local case + local_file = platform_file; + return Error(); +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformLinux::PlatformLinux (bool is_host) : + PlatformPOSIX(is_host) // This is the local host platform +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformLinux::~PlatformLinux() = default; + +bool +PlatformLinux::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + bool success = false; + if (IsHost()) + { + success = Platform::GetProcessInfo (pid, process_info); + } + else + { + if (m_remote_platform_sp) + success = m_remote_platform_sp->GetProcessInfo (pid, process_info); + } + return success; +} + +uint32_t +PlatformLinux::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + uint32_t match_count = 0; + if (IsHost()) + { + // Let the base class figure out the host details + match_count = Platform::FindProcesses (match_info, process_infos); + } + else + { + // If we are remote, we can only return results if we are connected + if (m_remote_platform_sp) + match_count = m_remote_platform_sp->FindProcesses (match_info, process_infos); + } + return match_count; +} + +bool +PlatformLinux::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + if (IsHost()) + { + ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + if (hostArch.GetTriple().isOSLinux()) + { + if (idx == 0) + { + arch = hostArch; + return arch.IsValid(); + } + else if (idx == 1) + { + // If the default host architecture is 64-bit, look for a 32-bit variant + if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit()) + { + arch = HostInfo::GetArchitecture(HostInfo::eArchKind32); + return arch.IsValid(); + } + } + } + } + else + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch); + + llvm::Triple triple; + // Set the OS to linux + triple.setOS(llvm::Triple::Linux); + // Set the architecture + switch (idx) + { + case 0: triple.setArchName("x86_64"); break; + case 1: triple.setArchName("i386"); break; + case 2: triple.setArchName("arm"); break; + case 3: triple.setArchName("aarch64"); break; + case 4: triple.setArchName("mips64"); break; + case 5: triple.setArchName("hexagon"); break; + case 6: triple.setArchName("mips"); break; + case 7: triple.setArchName("mips64el"); break; + case 8: triple.setArchName("mipsel"); break; + default: return false; + } + // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the vendor by + // calling triple.SetVendorName("unknown") so that it is a "unspecified unknown". + // This means when someone calls triple.GetVendorName() it will return an empty string + // which indicates that the vendor can be set when two architectures are merged + + // Now set the triple into "arch" and return true + arch.SetTriple(triple); + return true; + } + return false; +} + +void +PlatformLinux::GetStatus (Stream &strm) +{ + Platform::GetStatus(strm); + +#ifndef LLDB_DISABLE_POSIX + // Display local kernel information only when we are running in host mode. + // Otherwise, we would end up printing non-Linux information (when running + // on Mac OS for example). + if (IsHost()) + { + struct utsname un; + + if (uname(&un)) + return; + + strm.Printf (" Kernel: %s\n", un.sysname); + strm.Printf (" Release: %s\n", un.release); + strm.Printf (" Version: %s\n", un.version); + } +#endif +} + +size_t +PlatformLinux::GetSoftwareBreakpointTrapOpcode (Target &target, + BreakpointSite *bp_site) +{ + ArchSpec arch = target.GetArchitecture(); + const uint8_t *trap_opcode = NULL; + size_t trap_opcode_size = 0; + + switch (arch.GetMachine()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case llvm::Triple::aarch64: + { + static const uint8_t g_aarch64_opcode[] = { 0x00, 0x00, 0x20, 0xd4 }; + trap_opcode = g_aarch64_opcode; + trap_opcode_size = sizeof(g_aarch64_opcode); + } + break; + case llvm::Triple::x86: + case llvm::Triple::x86_64: + { + static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC }; + trap_opcode = g_i386_breakpoint_opcode; + trap_opcode_size = sizeof(g_i386_breakpoint_opcode); + } + break; + case llvm::Triple::hexagon: + { + static const uint8_t g_hex_opcode[] = { 0x0c, 0xdb, 0x00, 0x54 }; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } + break; + case llvm::Triple::arm: + { + // The ARM reference recommends the use of 0xe7fddefe and 0xdefe + // but the linux kernel does otherwise. + static const uint8_t g_arm_breakpoint_opcode[] = { 0xf0, 0x01, 0xf0, 0xe7 }; + static const uint8_t g_thumb_breakpoint_opcode[] = { 0x01, 0xde }; + + lldb::BreakpointLocationSP bp_loc_sp (bp_site->GetOwnerAtIndex (0)); + AddressClass addr_class = eAddressClassUnknown; + + if (bp_loc_sp) + { + addr_class = bp_loc_sp->GetAddress ().GetAddressClass (); + + if (addr_class == eAddressClassUnknown && + (bp_loc_sp->GetAddress ().GetFileAddress () & 1)) + { + addr_class = eAddressClassCodeAlternateISA; + } + } + + if (addr_class == eAddressClassCodeAlternateISA) + { + trap_opcode = g_thumb_breakpoint_opcode; + trap_opcode_size = sizeof(g_thumb_breakpoint_opcode); + } + else + { + trap_opcode = g_arm_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm_breakpoint_opcode); + } + } + break; + case llvm::Triple::mips: + case llvm::Triple::mips64: + { + static const uint8_t g_hex_opcode[] = { 0x00, 0x00, 0x00, 0x0d }; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } + break; + case llvm::Triple::mipsel: + case llvm::Triple::mips64el: + { + static const uint8_t g_hex_opcode[] = { 0x0d, 0x00, 0x00, 0x00 }; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } + break; + } + + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + return 0; +} + +int32_t +PlatformLinux::GetResumeCountForLaunchInfo (ProcessLaunchInfo &launch_info) +{ + int32_t resume_count = 0; + + // Always resume past the initial stop when we use eLaunchFlagDebug + if (launch_info.GetFlags ().Test (eLaunchFlagDebug)) + { + // Resume past the stop for the final exec into the true inferior. + ++resume_count; + } + + // If we're not launching a shell, we're done. + const FileSpec &shell = launch_info.GetShell(); + if (!shell) + return resume_count; + + std::string shell_string = shell.GetPath(); + // We're in a shell, so for sure we have to resume past the shell exec. + ++resume_count; + + // Figure out what shell we're planning on using. + const char *shell_name = strrchr (shell_string.c_str(), '/'); + if (shell_name == NULL) + shell_name = shell_string.c_str(); + else + shell_name++; + + if (strcmp (shell_name, "csh") == 0 + || strcmp (shell_name, "tcsh") == 0 + || strcmp (shell_name, "zsh") == 0 + || strcmp (shell_name, "sh") == 0) + { + // These shells seem to re-exec themselves. Add another resume. + ++resume_count; + } + + return resume_count; +} + +bool +PlatformLinux::CanDebugProcess () +{ + if (IsHost ()) + { + return true; + } + else + { + // If we're connected, we can debug. + return IsConnected (); + } +} + +// For local debugging, Linux will override the debug logic to use llgs-launch rather than +// lldb-launch, llgs-attach. This differs from current lldb-launch, debugserver-attach +// approach on MacOSX. +lldb::ProcessSP +PlatformLinux::DebugProcess (ProcessLaunchInfo &launch_info, + Debugger &debugger, + Target *target, // Can be NULL, if NULL create a new target, else use existing one + Error &error) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf ("PlatformLinux::%s entered (target %p)", __FUNCTION__, static_cast<void*>(target)); + + // If we're a remote host, use standard behavior from parent class. + if (!IsHost ()) + return PlatformPOSIX::DebugProcess (launch_info, debugger, target, error); + + // + // For local debugging, we'll insist on having ProcessGDBRemote create the process. + // + + ProcessSP process_sp; + + // Make sure we stop at the entry point + launch_info.GetFlags ().Set (eLaunchFlagDebug); + + // We always launch the process we are going to debug in a separate process + // group, since then we can handle ^C interrupts ourselves w/o having to worry + // about the target getting them as well. + launch_info.SetLaunchInSeparateProcessGroup(true); + + // Ensure we have a target. + if (target == nullptr) + { + if (log) + log->Printf ("PlatformLinux::%s creating new target", __FUNCTION__); + + TargetSP new_target_sp; + error = debugger.GetTargetList().CreateTarget (debugger, + nullptr, + nullptr, + false, + nullptr, + new_target_sp); + if (error.Fail ()) + { + if (log) + log->Printf ("PlatformLinux::%s failed to create new target: %s", __FUNCTION__, error.AsCString ()); + return process_sp; + } + + target = new_target_sp.get(); + if (!target) + { + error.SetErrorString ("CreateTarget() returned nullptr"); + if (log) + log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ()); + return process_sp; + } + } + else + { + if (log) + log->Printf ("PlatformLinux::%s using provided target", __FUNCTION__); + } + + // Mark target as currently selected target. + debugger.GetTargetList().SetSelectedTarget(target); + + // Now create the gdb-remote process. + if (log) + log->Printf ("PlatformLinux::%s having target create process with gdb-remote plugin", __FUNCTION__); + process_sp = target->CreateProcess (launch_info.GetListenerForProcess(debugger), "gdb-remote", nullptr); + + if (!process_sp) + { + error.SetErrorString ("CreateProcess() failed for gdb-remote process"); + if (log) + log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ()); + return process_sp; + } + else + { + if (log) + log->Printf ("PlatformLinux::%s successfully created process", __FUNCTION__); + } + + // Adjust launch for a hijacker. + ListenerSP listener_sp; + if (!launch_info.GetHijackListener ()) + { + if (log) + log->Printf ("PlatformLinux::%s setting up hijacker", __FUNCTION__); + + listener_sp.reset (new Listener("lldb.PlatformLinux.DebugProcess.hijack")); + launch_info.SetHijackListener (listener_sp); + process_sp->HijackProcessEvents (listener_sp.get ()); + } + + // Log file actions. + if (log) + { + log->Printf ("PlatformLinux::%s launching process with the following file actions:", __FUNCTION__); + + StreamString stream; + size_t i = 0; + const FileAction *file_action; + while ((file_action = launch_info.GetFileActionAtIndex (i++)) != nullptr) + { + file_action->Dump (stream); + log->PutCString (stream.GetString().c_str ()); + stream.Clear(); + } + } + + // Do the launch. + error = process_sp->Launch(launch_info); + if (error.Success ()) + { + // Handle the hijacking of process events. + if (listener_sp) + { + const StateType state = process_sp->WaitForProcessToStop (NULL, NULL, false, listener_sp.get()); + + if (state == eStateStopped) + { + if (log) + log->Printf ("PlatformLinux::%s pid %" PRIu64 " state %s\n", + __FUNCTION__, process_sp->GetID (), StateAsCString (state)); + } + else + { + if (log) + log->Printf ("PlatformLinux::%s pid %" PRIu64 " state is not stopped - %s\n", + __FUNCTION__, process_sp->GetID (), StateAsCString (state)); + } + } + + // Hook up process PTY if we have one (which we should for local debugging with llgs). + int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); + if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd) + { + process_sp->SetSTDIOFileDescriptor(pty_fd); + if (log) + log->Printf ("PlatformLinux::%s pid %" PRIu64 " hooked up STDIO pty to process", __FUNCTION__, process_sp->GetID ()); + } + else + { + if (log) + log->Printf ("PlatformLinux::%s pid %" PRIu64 " not using process STDIO pty", __FUNCTION__, process_sp->GetID ()); + } + } + else + { + if (log) + log->Printf ("PlatformLinux::%s process launch failed: %s", __FUNCTION__, error.AsCString ()); + // FIXME figure out appropriate cleanup here. Do we delete the target? Do we delete the process? Does our caller do that? + } + + return process_sp; +} + +void +PlatformLinux::CalculateTrapHandlerSymbolNames () +{ + m_trap_handlers.push_back (ConstString ("_sigtramp")); +} + +uint64_t +PlatformLinux::ConvertMmapFlagsToPlatform(const ArchSpec &arch, unsigned flags) +{ + uint64_t flags_platform = 0; + uint64_t map_anon = MAP_ANON; + + // To get correct flags for MIPS Architecture + if (arch.GetTriple ().getArch () == llvm::Triple::mips64 + || arch.GetTriple ().getArch () == llvm::Triple::mips64el + || arch.GetTriple ().getArch () == llvm::Triple::mips + || arch.GetTriple ().getArch () == llvm::Triple::mipsel) + map_anon = 0x800; + + if (flags & eMmapFlagsPrivate) + flags_platform |= MAP_PRIVATE; + if (flags & eMmapFlagsAnon) + flags_platform |= map_anon; + return flags_platform; +} + +ConstString +PlatformLinux::GetFullNameForDylib (ConstString basename) +{ + if (basename.IsEmpty()) + return basename; + + StreamString stream; + stream.Printf("lib%s.so", basename.GetCString()); + return ConstString(stream.GetData()); +} diff --git a/source/Plugins/Platform/Linux/PlatformLinux.h b/source/Plugins/Platform/Linux/PlatformLinux.h new file mode 100644 index 000000000000..770a20c90cce --- /dev/null +++ b/source/Plugins/Platform/Linux/PlatformLinux.h @@ -0,0 +1,122 @@ +//===-- PlatformLinux.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformLinux_h_ +#define liblldb_PlatformLinux_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Platform/POSIX/PlatformPOSIX.h" + +namespace lldb_private { +namespace platform_linux { + + class PlatformLinux : public PlatformPOSIX + { + public: + PlatformLinux(bool is_host); + + ~PlatformLinux() override; + + static void + DebuggerInitialize (Debugger &debugger); + + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const ArchSpec *arch); + + static ConstString + GetPluginNameStatic (bool is_host); + + static const char * + GetPluginDescriptionStatic (bool is_host); + + ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + Error + ResolveExecutable (const ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetPluginDescriptionStatic(IsHost()); + } + + void + GetStatus (Stream &strm) override; + + Error + GetFileWithUUID (const FileSpec &platform_file, + const UUID* uuid, FileSpec &local_file) override; + + bool + GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &proc_info) override; + + uint32_t + FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) override; + + size_t + GetSoftwareBreakpointTrapOpcode (Target &target, + BreakpointSite *bp_site) override; + + int32_t + GetResumeCountForLaunchInfo (ProcessLaunchInfo &launch_info) override; + + bool + CanDebugProcess () override; + + lldb::ProcessSP + DebugProcess (ProcessLaunchInfo &launch_info, + Debugger &debugger, + Target *target, + Error &error) override; + + void + CalculateTrapHandlerSymbolNames () override; + + uint64_t + ConvertMmapFlagsToPlatform(const ArchSpec &arch, unsigned flags) override; + + ConstString + GetFullNameForDylib (ConstString basename) override; + + private: + DISALLOW_COPY_AND_ASSIGN (PlatformLinux); + }; + +} // namespace platform_linux +} // namespace lldb_private + +#endif // liblldb_PlatformLinux_h_ diff --git a/source/Plugins/Platform/MacOSX/CMakeLists.txt b/source/Plugins/Platform/MacOSX/CMakeLists.txt new file mode 100644 index 000000000000..02566ab3db06 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/CMakeLists.txt @@ -0,0 +1,27 @@ +list(APPEND PLUGIN_PLATFORM_MACOSX_SOURCES + PlatformDarwin.cpp + PlatformDarwinKernel.cpp + PlatformMacOSX.cpp + PlatformRemoteiOS.cpp + PlatformRemoteAppleTV.cpp + PlatformRemoteAppleWatch.cpp + ) + +list(APPEND PLUGIN_PLATFORM_MACOSX_DARWIN_ONLY_SOURCES + PlatformAppleSimulator.cpp + PlatformiOSSimulator.cpp + PlatformiOSSimulatorCoreSimulatorSupport.mm + PlatformAppleTVSimulator.cpp + PlatformAppleWatchSimulator.cpp + ) + +if(CMAKE_SYSTEM_NAME MATCHES "Darwin") + include_directories(${LIBXML2_INCLUDE_DIR}) + list(APPEND PLUGIN_PLATFORM_MACOSX_SOURCES + ${PLUGIN_PLATFORM_MACOSX_DARWIN_ONLY_SOURCES}) +else() + list(APPEND LLVM_OPTIONAL_SOURCES + ${PLUGIN_PLATFORM_MACOSX_DARWIN_ONLY_SOURCES}) +endif() + +add_lldb_library(lldbPluginPlatformMacOSX ${PLUGIN_PLATFORM_MACOSX_SOURCES}) diff --git a/source/Plugins/Platform/MacOSX/Makefile b/source/Plugins/Platform/MacOSX/Makefile new file mode 100644 index 000000000000..3e61dc982bd6 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/Makefile @@ -0,0 +1,15 @@ +##===- source/Plugins/Platform/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 := ../../../.. +LIBRARYNAME := lldbPluginPlatformMacOSX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile + diff --git a/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp b/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp new file mode 100644 index 000000000000..eea2844d5649 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp @@ -0,0 +1,303 @@ +//===-- PlatformAppleSimulator.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformAppleSimulator.h" + +// C Includes +#if defined(__APPLE__) +#include <dlfcn.h> +#endif + +// C++ Includes +#include <mutex> +#include <thread> +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/PseudoTerminal.h" + +using namespace lldb; +using namespace lldb_private; + +#if !defined(__APPLE__) +#define UNSUPPORTED_ERROR ("Apple simulators aren't supported on this platform") +#endif + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformAppleSimulator::Initialize () +{ + PlatformDarwin::Initialize (); +} + +void +PlatformAppleSimulator::Terminate () +{ + PlatformDarwin::Terminate (); +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformAppleSimulator::PlatformAppleSimulator () : + PlatformDarwin (true), + m_core_simulator_framework_path() +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformAppleSimulator::~PlatformAppleSimulator() +{ +} + +lldb_private::Error +PlatformAppleSimulator::LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info) +{ +#if defined(__APPLE__) + LoadCoreSimulator(); + CoreSimulatorSupport::Device device(GetSimulatorDevice()); + + if (device.GetState() != CoreSimulatorSupport::Device::State::Booted) + { + Error boot_err; + device.Boot(boot_err); + if (boot_err.Fail()) + return boot_err; + } + + auto spawned = device.Spawn(launch_info); + + if (spawned) + { + launch_info.SetProcessID(spawned.GetPID()); + return Error(); + } + else + return spawned.GetError(); +#else + Error err; + err.SetErrorString(UNSUPPORTED_ERROR); + return err; +#endif +} + +void +PlatformAppleSimulator::GetStatus (Stream &strm) +{ +#if defined(__APPLE__) + // This will get called by subclasses, so just output status on the + // current simulator + PlatformAppleSimulator::LoadCoreSimulator(); + + CoreSimulatorSupport::DeviceSet devices = CoreSimulatorSupport::DeviceSet::GetAvailableDevices(); + const size_t num_devices = devices.GetNumDevices(); + if (num_devices) + { + strm.Printf("Available devices:\n"); + for (size_t i=0; i<num_devices; ++i) + { + CoreSimulatorSupport::Device device = devices.GetDeviceAtIndex(i); + strm.Printf(" %s: %s\n", device.GetUDID().c_str(), device.GetName().c_str()); + } + + if (m_device.hasValue() && m_device->operator bool()) + { + strm.Printf("Current device: %s: %s", m_device->GetUDID().c_str(), m_device->GetName().c_str()); + if (m_device->GetState() == CoreSimulatorSupport::Device::State::Booted) + { + strm.Printf(" state = booted"); + } + strm.Printf("\nType \"platform connect <ARG>\" where <ARG> is a device UDID or a device name to disconnect and connect to a different device.\n"); + + } + else + { + strm.Printf("No current device is selected, \"platform connect <ARG>\" where <ARG> is a device UDID or a device name to connect to a specific device.\n"); + } + + } + else + { + strm.Printf("No devices are available.\n"); + } +#else + strm.Printf(UNSUPPORTED_ERROR); +#endif +} + +Error +PlatformAppleSimulator::ConnectRemote (Args& args) +{ +#if defined(__APPLE__) + Error error; + if (args.GetArgumentCount() == 1) + { + if (m_device) + DisconnectRemote (); + PlatformAppleSimulator::LoadCoreSimulator(); + const char *arg_cstr = args.GetArgumentAtIndex(0); + if (arg_cstr) + { + std::string arg_str(arg_cstr); + CoreSimulatorSupport::DeviceSet devices = CoreSimulatorSupport::DeviceSet::GetAvailableDevices(); + devices.ForEach([this, &arg_str](const CoreSimulatorSupport::Device &device) -> bool { + if (arg_str == device.GetUDID() || arg_str == device.GetName()) + { + m_device = device; + return false; // Stop iterating + } + else + { + return true; // Keep iterating + } + }); + if (!m_device) + error.SetErrorStringWithFormat("no device with UDID or name '%s' was found", arg_cstr); + } + } + else + { + error.SetErrorString("this command take a single UDID argument of the device you want to connect to."); + } + return error; +#else + Error err; + err.SetErrorString(UNSUPPORTED_ERROR); + return err; +#endif +} + +Error +PlatformAppleSimulator::DisconnectRemote () +{ +#if defined(__APPLE__) + m_device.reset(); + return Error(); +#else + Error err; + err.SetErrorString(UNSUPPORTED_ERROR); + return err; +#endif +} + + +lldb::ProcessSP +PlatformAppleSimulator::DebugProcess (ProcessLaunchInfo &launch_info, + Debugger &debugger, + Target *target, // Can be NULL, if NULL create a new target, else use existing one + Error &error) +{ +#if defined(__APPLE__) + ProcessSP process_sp; + // Make sure we stop at the entry point + launch_info.GetFlags ().Set (eLaunchFlagDebug); + // We always launch the process we are going to debug in a separate process + // group, since then we can handle ^C interrupts ourselves w/o having to worry + // about the target getting them as well. + launch_info.SetLaunchInSeparateProcessGroup(true); + + error = LaunchProcess (launch_info); + if (error.Success()) + { + if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) + { + ProcessAttachInfo attach_info (launch_info); + process_sp = Attach (attach_info, debugger, target, error); + if (process_sp) + { + launch_info.SetHijackListener(attach_info.GetHijackListener()); + + // Since we attached to the process, it will think it needs to detach + // if the process object just goes away without an explicit call to + // Process::Kill() or Process::Detach(), so let it know to kill the + // process if this happens. + process_sp->SetShouldDetach (false); + + // If we didn't have any file actions, the pseudo terminal might + // have been used where the slave side was given as the file to + // open for stdin/out/err after we have already opened the master + // so we can read/write stdin/out/err. + int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); + if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd) + { + process_sp->SetSTDIOFileDescriptor(pty_fd); + } + } + } + } + + return process_sp; +#else + return ProcessSP(); +#endif +} + +FileSpec +PlatformAppleSimulator::GetCoreSimulatorPath() +{ +#if defined(__APPLE__) + Mutex::Locker locker (m_mutex); + if (!m_core_simulator_framework_path.hasValue()) + { + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir) + { + StreamString cs_path; + cs_path.Printf("%s/Library/PrivateFrameworks/CoreSimulator.framework/CoreSimulator", developer_dir); + const bool resolve_path = true; + m_core_simulator_framework_path = FileSpec(cs_path.GetData(), resolve_path); + } + } + + return m_core_simulator_framework_path.getValue(); +#else + return FileSpec(); +#endif +} + +void +PlatformAppleSimulator::LoadCoreSimulator () +{ +#if defined(__APPLE__) + static std::once_flag g_load_core_sim_flag; + std::call_once(g_load_core_sim_flag, [this] { + const std::string core_sim_path(GetCoreSimulatorPath().GetPath()); + if (core_sim_path.size()) + dlopen(core_sim_path.c_str(), RTLD_LAZY); + }); +#endif +} + +#if defined(__APPLE__) +CoreSimulatorSupport::Device +PlatformAppleSimulator::GetSimulatorDevice () +{ + if (!m_device.hasValue()) + { + const CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id = CoreSimulatorSupport::DeviceType::ProductFamilyID::iPhone; + m_device = CoreSimulatorSupport::DeviceSet::GetAvailableDevices().GetFanciest(dev_id); + } + + if (m_device.hasValue()) + return m_device.getValue(); + else + return CoreSimulatorSupport::Device(); +} +#endif + diff --git a/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h b/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h new file mode 100644 index 000000000000..de8673b2a2af --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h @@ -0,0 +1,81 @@ +//===-- PlatformAppleSimulator.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformAppleSimulator_h_ +#define liblldb_PlatformAppleSimulator_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" +#include "PlatformDarwin.h" +#include "PlatformiOSSimulatorCoreSimulatorSupport.h" + +#include "llvm/ADT/Optional.h" + +class PlatformAppleSimulator : public PlatformDarwin +{ +public: + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------ + PlatformAppleSimulator (); + + virtual + ~PlatformAppleSimulator(); + + lldb_private::Error + LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info) override; + + void + GetStatus (lldb_private::Stream &strm) override; + + lldb_private::Error + ConnectRemote (lldb_private::Args& args) override; + + lldb_private::Error + DisconnectRemote () override; + + lldb::ProcessSP + DebugProcess (lldb_private::ProcessLaunchInfo &launch_info, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) override; + +protected: + llvm::Optional<lldb_private::FileSpec> m_core_simulator_framework_path; + llvm::Optional<CoreSimulatorSupport::Device> m_device; + + lldb_private::FileSpec + GetCoreSimulatorPath(); + + void + LoadCoreSimulator (); + +#if defined(__APPLE__) + CoreSimulatorSupport::Device + GetSimulatorDevice (); +#endif + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformAppleSimulator); + +}; + +#endif // liblldb_PlatformAppleSimulator_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.cpp b/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.cpp new file mode 100644 index 000000000000..f537934a9172 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.cpp @@ -0,0 +1,466 @@ +//===-- PlatformAppleTVSimulator.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformAppleTVSimulator.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformAppleTVSimulator::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformAppleTVSimulator::GetPluginNameStatic(), + PlatformAppleTVSimulator::GetDescriptionStatic(), + PlatformAppleTVSimulator::CreateInstance); + } +} + +void +PlatformAppleTVSimulator::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformAppleTVSimulator::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformAppleTVSimulator::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = "<null>"; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; + + log->Printf ("PlatformAppleTVSimulator::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::x86_64: + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::TvOS: + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + if (create) + { + if (log) + log->Printf ("PlatformAppleTVSimulator::%s() creating platform", __FUNCTION__); + + return PlatformSP(new PlatformAppleTVSimulator ()); + } + + if (log) + log->Printf ("PlatformAppleTVSimulator::%s() aborting creation of platform", __FUNCTION__); + + return PlatformSP(); +} + + +lldb_private::ConstString +PlatformAppleTVSimulator::GetPluginNameStatic () +{ + static ConstString g_name("tvos-simulator"); + return g_name; +} + +const char * +PlatformAppleTVSimulator::GetDescriptionStatic() +{ + return "Apple TV simulator platform plug-in."; +} + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformAppleTVSimulator::PlatformAppleTVSimulator () : + PlatformDarwin (true), + m_sdk_directory () +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformAppleTVSimulator::~PlatformAppleTVSimulator() +{ +} + + +void +PlatformAppleTVSimulator::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetSDKDirectoryAsCString(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); +} + + +Error +PlatformAppleTVSimulator::ResolveExecutable (const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + ModuleSpec resolved_module_spec(module_spec); + + // If we have "ls" as the exe_file, resolve the executable loation based on + // the current path variables + // TODO: resolve bare executables in the Platform SDK +// if (!resolved_exe_file.Exists()) +// resolved_exe_file.ResolveExecutableLocation (); + + // Resolve any executable within a bundle on MacOSX + // TODO: verify that this handles shallow bundles, if not then implement one ourselves + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + + if (exe_module_sp && exe_module_sp->GetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + ArchSpec platform_arch; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + // Only match x86 with x86 and x86_64 with x86_64... + if (!module_spec.GetArchitecture().IsValid() || module_spec.GetArchitecture().GetCore() == resolved_module_spec.GetArchitecture().GetCore()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (platform_arch.GetArchitectureName()); + } + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +static FileSpec::EnumerateDirectoryResult +EnumerateDirectoryCallback (void *baton, FileSpec::FileType file_type, const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeDirectory) + { + const char *filename = file_spec.GetFilename().GetCString(); + if (filename && strncmp(filename, "AppleTVSimulator", strlen ("AppleTVSimulator")) == 0) + { + ::snprintf ((char *)baton, PATH_MAX, "%s", filename); + return FileSpec::eEnumerateDirectoryResultQuit; + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + + + +const char * +PlatformAppleTVSimulator::GetSDKDirectoryAsCString() +{ + Mutex::Locker locker (m_mutex); + if (m_sdk_directory.empty()) + { + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir) + { + char sdks_directory[PATH_MAX]; + char sdk_dirname[PATH_MAX]; + sdk_dirname[0] = '\0'; + snprintf (sdks_directory, + sizeof(sdks_directory), + "%s/Platforms/AppleTVSimulator.platform/Developer/SDKs", + developer_dir); + FileSpec simulator_sdk_spec; + bool find_directories = true; + bool find_files = false; + bool find_other = false; + FileSpec::EnumerateDirectory (sdks_directory, + find_directories, + find_files, + find_other, + EnumerateDirectoryCallback, + sdk_dirname); + + if (sdk_dirname[0]) + { + m_sdk_directory = sdks_directory; + m_sdk_directory.append (1, '/'); + m_sdk_directory.append (sdk_dirname); + return m_sdk_directory.c_str(); + } + } + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_sdk_directory.assign (1, '\0'); + } + + // We should have put a single NULL character into m_sdk_directory + // or it should have a valid path if the code gets here + assert (m_sdk_directory.empty() == false); + if (m_sdk_directory[0]) + return m_sdk_directory.c_str(); + return NULL; +} + +Error +PlatformAppleTVSimulator::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + Error error; + char platform_file_path[PATH_MAX]; + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + char resolved_path[PATH_MAX]; + + const char * sdk_dir = GetSDKDirectoryAsCString(); + if (sdk_dir) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/%s", + sdk_dir, + platform_file_path); + + // First try in the SDK and see if the file is in there + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + + // Else fall back to the actual path itself + local_file.SetFile(platform_file_path, true); + if (local_file.Exists()) + return error; + + } + error.SetErrorStringWithFormat ("unable to locate a platform file for '%s' in platform '%s'", + platform_file_path, + GetPluginName().GetCString()); + } + else + { + error.SetErrorString ("invalid platform file argument"); + } + return error; +} + +Error +PlatformAppleTVSimulator::GetSharedModule (const ModuleSpec &module_spec, + lldb_private::Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + // For AppleTV, the SDK files are all cached locally on the host + // system. So first we ask for the file in the cached SDK, + // then we attempt to get a shared module for the right architecture + // with the right UUID. + Error error; + ModuleSpec platform_module_spec (module_spec); + const FileSpec &platform_file = module_spec.GetFileSpec(); + error = GetSymbolFile (platform_file, module_spec.GetUUIDPtr(), platform_module_spec.GetFileSpec()); + if (error.Success()) + { + error = ResolveExecutable (platform_module_spec, module_sp, module_search_paths_ptr); + } + else + { + const bool always_create = false; + error = ModuleList::GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr, + always_create); + + } + if (module_sp) + module_sp->SetPlatformFileSpec(platform_file); + + return error; +} + + +uint32_t +PlatformAppleTVSimulator::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + ProcessInstanceInfoList all_osx_process_infos; + // First we get all OSX processes + const uint32_t n = Host::FindProcesses (match_info, all_osx_process_infos); + + // Now we filter them down to only the TvOS triples + for (uint32_t i=0; i<n; ++i) + { + const ProcessInstanceInfo &proc_info = all_osx_process_infos.GetProcessInfoAtIndex(i); + if (proc_info.GetArchitecture().GetTriple().getOS() == llvm::Triple::TvOS) { + process_infos.Append(proc_info); + } + } + return process_infos.GetSize(); +} + +bool +PlatformAppleTVSimulator::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + static const ArchSpec platform_arch(HostInfo::GetArchitecture(HostInfo::eArchKind64)); + + if (idx == 0) + { + arch = platform_arch; + if (arch.IsValid()) + { + arch.GetTriple().setOS (llvm::Triple::TvOS); + return true; + } + } + return false; +} diff --git a/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.h b/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.h new file mode 100644 index 000000000000..0990f0729203 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.h @@ -0,0 +1,121 @@ +//===-- PlatformAppleTVSimulator.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformAppleTVSimulator_h_ +#define liblldb_PlatformAppleTVSimulator_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "PlatformDarwin.h" + +class PlatformAppleTVSimulator : public PlatformDarwin +{ +public: + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------ + PlatformAppleTVSimulator (); + + virtual + ~PlatformAppleTVSimulator(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + virtual lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + uint32_t + FindProcesses (const lldb_private::ProcessInstanceInfoMatch &match_info, + lldb_private::ProcessInstanceInfoList &process_infos) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector<std::string> &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneSimulator); + } + +protected: + std::string m_sdk_directory; + std::string m_build_update; + + const char * + GetSDKDirectoryAsCString(); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformAppleTVSimulator); + +}; + + +#endif // liblldb_PlatformAppleTVSimulator_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.cpp b/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.cpp new file mode 100644 index 000000000000..ea8e789b2920 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.cpp @@ -0,0 +1,466 @@ +//===-- PlatformAppleWatchSimulator.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformAppleWatchSimulator.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformAppleWatchSimulator::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformAppleWatchSimulator::GetPluginNameStatic(), + PlatformAppleWatchSimulator::GetDescriptionStatic(), + PlatformAppleWatchSimulator::CreateInstance); + } +} + +void +PlatformAppleWatchSimulator::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformAppleWatchSimulator::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformAppleWatchSimulator::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = "<null>"; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; + + log->Printf ("PlatformAppleWatchSimulator::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::x86_64: + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::WatchOS: + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + if (create) + { + if (log) + log->Printf ("PlatformAppleWatchSimulator::%s() creating platform", __FUNCTION__); + + return PlatformSP(new PlatformAppleWatchSimulator ()); + } + + if (log) + log->Printf ("PlatformAppleWatchSimulator::%s() aborting creation of platform", __FUNCTION__); + + return PlatformSP(); +} + + +lldb_private::ConstString +PlatformAppleWatchSimulator::GetPluginNameStatic () +{ + static ConstString g_name("watchos-simulator"); + return g_name; +} + +const char * +PlatformAppleWatchSimulator::GetDescriptionStatic() +{ + return "Apple Watch simulator platform plug-in."; +} + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformAppleWatchSimulator::PlatformAppleWatchSimulator () : + PlatformDarwin (true), + m_sdk_directory () +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformAppleWatchSimulator::~PlatformAppleWatchSimulator() +{ +} + + +void +PlatformAppleWatchSimulator::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetSDKDirectoryAsCString(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); +} + + +Error +PlatformAppleWatchSimulator::ResolveExecutable (const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + ModuleSpec resolved_module_spec(module_spec); + + // If we have "ls" as the exe_file, resolve the executable loation based on + // the current path variables + // TODO: resolve bare executables in the Platform SDK +// if (!resolved_exe_file.Exists()) +// resolved_exe_file.ResolveExecutableLocation (); + + // Resolve any executable within a bundle on MacOSX + // TODO: verify that this handles shallow bundles, if not then implement one ourselves + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + + if (exe_module_sp && exe_module_sp->GetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + ArchSpec platform_arch; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + // Only match x86 with x86 and x86_64 with x86_64... + if (!module_spec.GetArchitecture().IsValid() || module_spec.GetArchitecture().GetCore() == resolved_module_spec.GetArchitecture().GetCore()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (platform_arch.GetArchitectureName()); + } + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +static FileSpec::EnumerateDirectoryResult +EnumerateDirectoryCallback (void *baton, FileSpec::FileType file_type, const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeDirectory) + { + const char *filename = file_spec.GetFilename().GetCString(); + if (filename && strncmp(filename, "AppleWatchSimulator", strlen ("AppleWatchSimulator")) == 0) + { + ::snprintf ((char *)baton, PATH_MAX, "%s", filename); + return FileSpec::eEnumerateDirectoryResultQuit; + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + + + +const char * +PlatformAppleWatchSimulator::GetSDKDirectoryAsCString() +{ + Mutex::Locker locker (m_mutex); + if (m_sdk_directory.empty()) + { + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir) + { + char sdks_directory[PATH_MAX]; + char sdk_dirname[PATH_MAX]; + sdk_dirname[0] = '\0'; + snprintf (sdks_directory, + sizeof(sdks_directory), + "%s/Platforms/AppleWatchSimulator.platform/Developer/SDKs", + developer_dir); + FileSpec simulator_sdk_spec; + bool find_directories = true; + bool find_files = false; + bool find_other = false; + FileSpec::EnumerateDirectory (sdks_directory, + find_directories, + find_files, + find_other, + EnumerateDirectoryCallback, + sdk_dirname); + + if (sdk_dirname[0]) + { + m_sdk_directory = sdks_directory; + m_sdk_directory.append (1, '/'); + m_sdk_directory.append (sdk_dirname); + return m_sdk_directory.c_str(); + } + } + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_sdk_directory.assign (1, '\0'); + } + + // We should have put a single NULL character into m_sdk_directory + // or it should have a valid path if the code gets here + assert (m_sdk_directory.empty() == false); + if (m_sdk_directory[0]) + return m_sdk_directory.c_str(); + return NULL; +} + +Error +PlatformAppleWatchSimulator::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + Error error; + char platform_file_path[PATH_MAX]; + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + char resolved_path[PATH_MAX]; + + const char * sdk_dir = GetSDKDirectoryAsCString(); + if (sdk_dir) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/%s", + sdk_dir, + platform_file_path); + + // First try in the SDK and see if the file is in there + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + + // Else fall back to the actual path itself + local_file.SetFile(platform_file_path, true); + if (local_file.Exists()) + return error; + + } + error.SetErrorStringWithFormat ("unable to locate a platform file for '%s' in platform '%s'", + platform_file_path, + GetPluginName().GetCString()); + } + else + { + error.SetErrorString ("invalid platform file argument"); + } + return error; +} + +Error +PlatformAppleWatchSimulator::GetSharedModule (const ModuleSpec &module_spec, + lldb_private::Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + // For AppleWatch, the SDK files are all cached locally on the host + // system. So first we ask for the file in the cached SDK, + // then we attempt to get a shared module for the right architecture + // with the right UUID. + Error error; + ModuleSpec platform_module_spec (module_spec); + const FileSpec &platform_file = module_spec.GetFileSpec(); + error = GetSymbolFile (platform_file, module_spec.GetUUIDPtr(), platform_module_spec.GetFileSpec()); + if (error.Success()) + { + error = ResolveExecutable (platform_module_spec, module_sp, module_search_paths_ptr); + } + else + { + const bool always_create = false; + error = ModuleList::GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr, + always_create); + + } + if (module_sp) + module_sp->SetPlatformFileSpec(platform_file); + + return error; +} + + +uint32_t +PlatformAppleWatchSimulator::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + ProcessInstanceInfoList all_osx_process_infos; + // First we get all OSX processes + const uint32_t n = Host::FindProcesses (match_info, all_osx_process_infos); + + // Now we filter them down to only the WatchOS triples + for (uint32_t i=0; i<n; ++i) + { + const ProcessInstanceInfo &proc_info = all_osx_process_infos.GetProcessInfoAtIndex(i); + if (proc_info.GetArchitecture().GetTriple().getOS() == llvm::Triple::WatchOS) { + process_infos.Append(proc_info); + } + } + return process_infos.GetSize(); +} + +bool +PlatformAppleWatchSimulator::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + static const ArchSpec platform_arch(HostInfo::GetArchitecture(HostInfo::eArchKind64)); + + if (idx == 0) + { + arch = platform_arch; + if (arch.IsValid()) + { + arch.GetTriple().setOS (llvm::Triple::WatchOS); + return true; + } + } + return false; +} diff --git a/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.h b/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.h new file mode 100644 index 000000000000..8bcc0d4784fc --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.h @@ -0,0 +1,120 @@ +//===-- PlatformAppleWatchSimulator.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformAppleWatchSimulator_h_ +#define liblldb_PlatformAppleWatchSimulator_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "PlatformDarwin.h" + +class PlatformAppleWatchSimulator : public PlatformDarwin +{ +public: + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------ + PlatformAppleWatchSimulator (); + + virtual + ~PlatformAppleWatchSimulator(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + virtual lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + uint32_t + FindProcesses (const lldb_private::ProcessInstanceInfoMatch &match_info, + lldb_private::ProcessInstanceInfoList &process_infos) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector<std::string> &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneSimulator); + } + +protected: + std::string m_sdk_directory; + std::string m_build_update; + + const char * + GetSDKDirectoryAsCString(); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformAppleWatchSimulator); + +}; + +#endif // liblldb_PlatformAppleWatchSimulator_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp new file mode 100644 index 000000000000..fb38630710a1 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -0,0 +1,1705 @@ +//===-- PlatformDarwin.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformDarwin.h" + +// C Includes +#include <string.h> + +// C++ Includes +#include <algorithm> +#include <mutex> + +// Other libraries and framework includes +#include "clang/Basic/VersionTuple.h" +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Host/XML.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "llvm/ADT/STLExtras.h" + +#if defined (__APPLE__) +#include <TargetConditionals.h> // for TARGET_OS_TV, TARGET_OS_WATCH +#endif + +using namespace lldb; +using namespace lldb_private; + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformDarwin::PlatformDarwin (bool is_host) : + PlatformPOSIX(is_host), // This is the local host platform + m_developer_directory () +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformDarwin::~PlatformDarwin() +{ +} + +FileSpecList +PlatformDarwin::LocateExecutableScriptingResources (Target *target, + Module &module, + Stream* feedback_stream) +{ + FileSpecList file_list; + if (target && target->GetDebugger().GetScriptLanguage() == eScriptLanguagePython) + { + // NB some extensions might be meaningful and should not be stripped - "this.binary.file" + // should not lose ".file" but GetFileNameStrippingExtension() will do precisely that. + // Ideally, we should have a per-platform list of extensions (".exe", ".app", ".dSYM", ".framework") + // which should be stripped while leaving "this.binary.file" as-is. + ScriptInterpreter *script_interpreter = target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + + FileSpec module_spec = module.GetFileSpec(); + + if (module_spec) + { + SymbolVendor *symbols = module.GetSymbolVendor (); + if (symbols) + { + SymbolFile *symfile = symbols->GetSymbolFile(); + if (symfile) + { + ObjectFile *objfile = symfile->GetObjectFile(); + if (objfile) + { + FileSpec symfile_spec (objfile->GetFileSpec()); + if (symfile_spec && symfile_spec.Exists()) + { + while (module_spec.GetFilename()) + { + std::string module_basename (module_spec.GetFilename().GetCString()); + std::string original_module_basename (module_basename); + + bool was_keyword = false; + + // FIXME: for Python, we cannot allow certain characters in module + // filenames we import. Theoretically, different scripting languages may + // have different sets of forbidden tokens in filenames, and that should + // be dealt with by each ScriptInterpreter. For now, we just replace dots + // with underscores, but if we ever support anything other than Python + // we will need to rework this + std::replace(module_basename.begin(), module_basename.end(), '.', '_'); + std::replace(module_basename.begin(), module_basename.end(), ' ', '_'); + std::replace(module_basename.begin(), module_basename.end(), '-', '_'); + if (script_interpreter && script_interpreter->IsReservedWord(module_basename.c_str())) + { + module_basename.insert(module_basename.begin(), '_'); + was_keyword = true; + } + + StreamString path_string; + StreamString original_path_string; + // for OSX we are going to be in .dSYM/Contents/Resources/DWARF/<basename> + // let us go to .dSYM/Contents/Resources/Python/<basename>.py and see if the file exists + path_string.Printf("%s/../Python/%s.py",symfile_spec.GetDirectory().GetCString(), module_basename.c_str()); + original_path_string.Printf("%s/../Python/%s.py",symfile_spec.GetDirectory().GetCString(), original_module_basename.c_str()); + FileSpec script_fspec(path_string.GetData(), true); + FileSpec orig_script_fspec(original_path_string.GetData(), true); + + // if we did some replacements of reserved characters, and a file with the untampered name + // exists, then warn the user that the file as-is shall not be loaded + if (feedback_stream) + { + if (module_basename != original_module_basename + && orig_script_fspec.Exists()) + { + const char* reason_for_complaint = was_keyword ? "conflicts with a keyword" : "contains reserved characters"; + if (script_fspec.Exists()) + feedback_stream->Printf("warning: the symbol file '%s' contains a debug script. However, its name" + " '%s' %s and as such cannot be loaded. LLDB will" + " load '%s' instead. Consider removing the file with the malformed name to" + " eliminate this warning.\n", + symfile_spec.GetPath().c_str(), + original_path_string.GetData(), + reason_for_complaint, + path_string.GetData()); + else + feedback_stream->Printf("warning: the symbol file '%s' contains a debug script. However, its name" + " %s and as such cannot be loaded. If you intend" + " to have this script loaded, please rename '%s' to '%s' and retry.\n", + symfile_spec.GetPath().c_str(), + reason_for_complaint, + original_path_string.GetData(), + path_string.GetData()); + } + } + + if (script_fspec.Exists()) + { + file_list.Append (script_fspec); + break; + } + + // If we didn't find the python file, then keep + // stripping the extensions and try again + ConstString filename_no_extension (module_spec.GetFileNameStrippingExtension()); + if (module_spec.GetFilename() == filename_no_extension) + break; + + module_spec.GetFilename() = filename_no_extension; + } + } + } + } + } + } + } + return file_list; +} + +Error +PlatformDarwin::ResolveExecutable (const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + char exe_path[PATH_MAX]; + ModuleSpec resolved_module_spec(module_spec); + + if (IsHost()) + { + // If we have "ls" as the exe_file, resolve the executable loation based on + // the current path variables + if (!resolved_module_spec.GetFileSpec().Exists()) + { + module_spec.GetFileSpec().GetPath (exe_path, sizeof(exe_path)); + resolved_module_spec.GetFileSpec().SetFile(exe_path, true); + } + + if (!resolved_module_spec.GetFileSpec().Exists()) + resolved_module_spec.GetFileSpec().ResolveExecutableLocation (); + + // Resolve any executable within a bundle on MacOSX + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + { + const uint32_t permissions = resolved_module_spec.GetFileSpec().GetPermissions(); + if (permissions && (permissions & eFilePermissionsEveryoneR) == 0) + error.SetErrorStringWithFormat ("executable '%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + else + error.SetErrorStringWithFormat ("unable to find executable for '%s'", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + else + { + if (m_remote_platform_sp) + { + error = GetCachedExecutable (resolved_module_spec, exe_module_sp, module_search_paths_ptr, *m_remote_platform_sp); + } + else + { + // We may connect to a process and use the provided executable (Don't use local $PATH). + + // Resolve any executable within a bundle on MacOSX + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", resolved_module_spec.GetFileSpec().GetFilename().AsCString("")); + } + } + + + if (error.Success()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + module_search_paths_ptr, + NULL, + NULL); + + if (error.Fail() || exe_module_sp.get() == NULL || exe_module_sp->GetObjectFile() == NULL) + { + exe_module_sp.reset(); + error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + } + else + { + // No valid architecture was specified, ask the platform for + // the architectures that we should be using (in the correct order) + // and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = GetSharedModule (resolved_module_spec, + NULL, + exe_module_sp, + module_search_paths_ptr, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + } + + return error; +} + +Error +PlatformDarwin::ResolveSymbolFile (Target &target, + const ModuleSpec &sym_spec, + FileSpec &sym_file) +{ + Error error; + sym_file = sym_spec.GetSymbolFileSpec(); + if (sym_file.Exists()) + { + if (sym_file.GetFileType() == FileSpec::eFileTypeDirectory) + { + sym_file = Symbols::FindSymbolFileInBundle (sym_file, + sym_spec.GetUUIDPtr(), + sym_spec.GetArchitecturePtr()); + } + } + else + { + if (sym_spec.GetUUID().IsValid()) + { + + } + } + return error; + +} + +static lldb_private::Error +MakeCacheFolderForFile (const FileSpec& module_cache_spec) +{ + FileSpec module_cache_folder = module_cache_spec.CopyByRemovingLastPathComponent(); + return FileSystem::MakeDirectory(module_cache_folder, eFilePermissionsDirectoryDefault); +} + +static lldb_private::Error +BringInRemoteFile (Platform* platform, + const lldb_private::ModuleSpec &module_spec, + const FileSpec& module_cache_spec) +{ + MakeCacheFolderForFile(module_cache_spec); + Error err = platform->GetFile(module_spec.GetFileSpec(), module_cache_spec); + return err; +} + +lldb_private::Error +PlatformDarwin::GetSharedModuleWithLocalCache (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] Trying to find module %s/%s - platform path %s/%s symbol path %s/%s", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString(), + module_spec.GetPlatformFileSpec().GetDirectory().AsCString(), + module_spec.GetPlatformFileSpec().GetFilename().AsCString(), + module_spec.GetSymbolFileSpec().GetDirectory().AsCString(), + module_spec.GetSymbolFileSpec().GetFilename().AsCString()); + + Error err; + + err = ModuleList::GetSharedModule(module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); + if (module_sp) + return err; + + if (!IsHost()) + { + std::string cache_path(GetLocalCacheDirectory()); + // Only search for a locally cached file if we have a valid cache path + if (!cache_path.empty()) + { + std::string module_path (module_spec.GetFileSpec().GetPath()); + cache_path.append(module_path); + FileSpec module_cache_spec(cache_path.c_str(),false); + + // if rsync is supported, always bring in the file - rsync will be very efficient + // when files are the same on the local and remote end of the connection + if (this->GetSupportsRSync()) + { + err = BringInRemoteFile (this, module_spec, module_cache_spec); + if (err.Fail()) + return err; + if (module_cache_spec.Exists()) + { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s was rsynced and is now there", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture()); + module_sp.reset(new Module(local_spec)); + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + return Error(); + } + } + + // try to find the module in the cache + if (module_cache_spec.Exists()) + { + // get the local and remote MD5 and compare + if (m_remote_platform_sp) + { + // when going over the *slow* GDB remote transfer mechanism we first check + // the hashes of the files - and only do the actual transfer if they differ + uint64_t high_local,high_remote,low_local,low_remote; + FileSystem::CalculateMD5(module_cache_spec, low_local, high_local); + m_remote_platform_sp->CalculateMD5(module_spec.GetFileSpec(), low_remote, high_remote); + if (low_local != low_remote || high_local != high_remote) + { + // bring in the remote file + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s needs to be replaced from remote copy", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + Error err = BringInRemoteFile (this, module_spec, module_cache_spec); + if (err.Fail()) + return err; + } + } + + ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture()); + module_sp.reset(new Module(local_spec)); + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s was found in the cache", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + return Error(); + } + + // bring in the remote module file + if (log) + log->Printf("[%s] module %s/%s needs to come in remotely", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + Error err = BringInRemoteFile (this, module_spec, module_cache_spec); + if (err.Fail()) + return err; + if (module_cache_spec.Exists()) + { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s is now cached and fine", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture()); + module_sp.reset(new Module(local_spec)); + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + return Error(); + } + else + return Error("unable to obtain valid module file"); + } + else + return Error("no cache path"); + } + else + return Error ("unable to resolve module"); +} + +Error +PlatformDarwin::GetSharedModule (const ModuleSpec &module_spec, + Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + Error error; + module_sp.reset(); + + if (IsRemote()) + { + // If we have a remote platform always, let it try and locate + // the shared module first. + if (m_remote_platform_sp) + { + error = m_remote_platform_sp->GetSharedModule (module_spec, + process, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr); + } + } + + if (!module_sp) + { + // Fall back to the local platform and find the file locally + error = Platform::GetSharedModule (module_spec, + process, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr); + + const FileSpec &platform_file = module_spec.GetFileSpec(); + if (!module_sp && module_search_paths_ptr && platform_file) + { + // We can try to pull off part of the file path up to the bundle + // directory level and try any module search paths... + FileSpec bundle_directory; + if (Host::GetBundleDirectory (platform_file, bundle_directory)) + { + if (platform_file == bundle_directory) + { + ModuleSpec new_module_spec (module_spec); + new_module_spec.GetFileSpec() = bundle_directory; + if (Host::ResolveExecutableInBundle (new_module_spec.GetFileSpec())) + { + Error new_error (Platform::GetSharedModule (new_module_spec, + process, + module_sp, + NULL, + old_module_sp_ptr, + did_create_ptr)); + + if (module_sp) + return new_error; + } + } + else + { + char platform_path[PATH_MAX]; + char bundle_dir[PATH_MAX]; + platform_file.GetPath (platform_path, sizeof(platform_path)); + const size_t bundle_directory_len = bundle_directory.GetPath (bundle_dir, sizeof(bundle_dir)); + char new_path[PATH_MAX]; + size_t num_module_search_paths = module_search_paths_ptr->GetSize(); + for (size_t i=0; i<num_module_search_paths; ++i) + { + const size_t search_path_len = module_search_paths_ptr->GetFileSpecAtIndex(i).GetPath(new_path, sizeof(new_path)); + if (search_path_len < sizeof(new_path)) + { + snprintf (new_path + search_path_len, sizeof(new_path) - search_path_len, "/%s", platform_path + bundle_directory_len); + FileSpec new_file_spec (new_path, false); + if (new_file_spec.Exists()) + { + ModuleSpec new_module_spec (module_spec); + new_module_spec.GetFileSpec() = new_file_spec; + Error new_error (Platform::GetSharedModule (new_module_spec, + process, + module_sp, + NULL, + old_module_sp_ptr, + did_create_ptr)); + + if (module_sp) + { + module_sp->SetPlatformFileSpec(new_file_spec); + return new_error; + } + } + } + } + } + } + } + } + if (module_sp) + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + return error; +} + +size_t +PlatformDarwin::GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite *bp_site) +{ + const uint8_t *trap_opcode = NULL; + uint32_t trap_opcode_size = 0; + bool bp_is_thumb = false; + + llvm::Triple::ArchType machine = target.GetArchitecture().GetMachine(); + switch (machine) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + { + static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC }; + trap_opcode = g_i386_breakpoint_opcode; + trap_opcode_size = sizeof(g_i386_breakpoint_opcode); + } + break; + + case llvm::Triple::aarch64: + { + // TODO: fix this with actual darwin breakpoint opcode for arm64. + // right now debugging uses the Z packets with GDB remote so this + // is not needed, but the size needs to be correct... + static const uint8_t g_arm64_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; + trap_opcode = g_arm64_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm64_breakpoint_opcode); + } + break; + + case llvm::Triple::thumb: + bp_is_thumb = true; // Fall through... + case llvm::Triple::arm: + { + static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; + static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE }; + + // Auto detect arm/thumb if it wasn't explicitly specified + if (!bp_is_thumb) + { + lldb::BreakpointLocationSP bp_loc_sp (bp_site->GetOwnerAtIndex (0)); + if (bp_loc_sp) + bp_is_thumb = bp_loc_sp->GetAddress().GetAddressClass () == eAddressClassCodeAlternateISA; + } + if (bp_is_thumb) + { + trap_opcode = g_thumb_breakpooint_opcode; + trap_opcode_size = sizeof(g_thumb_breakpooint_opcode); + break; + } + trap_opcode = g_arm_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm_breakpoint_opcode); + } + break; + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + { + static const uint8_t g_ppc_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 }; + trap_opcode = g_ppc_breakpoint_opcode; + trap_opcode_size = sizeof(g_ppc_breakpoint_opcode); + } + break; + + default: + assert(!"Unhandled architecture in PlatformDarwin::GetSoftwareBreakpointTrapOpcode()"); + break; + } + + if (trap_opcode && trap_opcode_size) + { + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + } + return 0; + +} + +bool +PlatformDarwin::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + bool success = false; + if (IsHost()) + { + success = Platform::GetProcessInfo (pid, process_info); + } + else + { + if (m_remote_platform_sp) + success = m_remote_platform_sp->GetProcessInfo (pid, process_info); + } + return success; +} + +uint32_t +PlatformDarwin::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + uint32_t match_count = 0; + if (IsHost()) + { + // Let the base class figure out the host details + match_count = Platform::FindProcesses (match_info, process_infos); + } + else + { + // If we are remote, we can only return results if we are connected + if (m_remote_platform_sp) + match_count = m_remote_platform_sp->FindProcesses (match_info, process_infos); + } + return match_count; +} + +bool +PlatformDarwin::ModuleIsExcludedForUnconstrainedSearches (lldb_private::Target &target, const lldb::ModuleSP &module_sp) +{ + if (!module_sp) + return false; + + ObjectFile *obj_file = module_sp->GetObjectFile(); + if (!obj_file) + return false; + + ObjectFile::Type obj_type = obj_file->GetType(); + if (obj_type == ObjectFile::eTypeDynamicLinker) + return true; + else + return false; +} + +bool +PlatformDarwin::x86GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + ArchSpec host_arch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + if (host_arch.GetCore() == ArchSpec::eCore_x86_64_x86_64h) + { + switch (idx) + { + case 0: + arch = host_arch; + return true; + + case 1: + arch.SetTriple("x86_64-apple-macosx"); + return true; + + case 2: + arch = HostInfo::GetArchitecture(HostInfo::eArchKind32); + return true; + + default: return false; + } + } + else + { + if (idx == 0) + { + arch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + return arch.IsValid(); + } + else if (idx == 1) + { + ArchSpec platform_arch(HostInfo::GetArchitecture(HostInfo::eArchKindDefault)); + ArchSpec platform_arch64(HostInfo::GetArchitecture(HostInfo::eArchKind64)); + if (platform_arch.IsExactMatch(platform_arch64)) + { + // This macosx platform supports both 32 and 64 bit. Since we already + // returned the 64 bit arch for idx == 0, return the 32 bit arch + // for idx == 1 + arch = HostInfo::GetArchitecture(HostInfo::eArchKind32); + return arch.IsValid(); + } + } + } + return false; +} + +// The architecture selection rules for arm processors +// These cpu subtypes have distinct names (e.g. armv7f) but armv7 binaries run fine on an armv7f processor. + +bool +PlatformDarwin::ARMGetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + ArchSpec system_arch (GetSystemArchitecture()); + + // When lldb is running on a watch or tv, set the arch OS name appropriately. +#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1 +#define OSNAME "tvos" +#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 +#define OSNAME "watchos" +#else +#define OSNAME "ios" +#endif + + const ArchSpec::Core system_core = system_arch.GetCore(); + switch (system_core) + { + default: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv7f-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv7k-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv7s-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv7m-apple-" OSNAME); return true; + case 6: arch.SetTriple ("armv7em-apple-" OSNAME); return true; + case 7: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 8: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 9: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 10: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 11: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumbv7f-apple-" OSNAME); return true; + case 14: arch.SetTriple ("thumbv7k-apple-" OSNAME); return true; + case 15: arch.SetTriple ("thumbv7s-apple-" OSNAME); return true; + case 16: arch.SetTriple ("thumbv7m-apple-" OSNAME); return true; + case 17: arch.SetTriple ("thumbv7em-apple-" OSNAME); return true; + case 18: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 19: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 20: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 21: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 22: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_arm64: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7s-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv7f-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv7m-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv7em-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 6: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 7: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 8: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 9: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 10: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv7f-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumbv7k-apple-" OSNAME); return true; + case 14: arch.SetTriple ("thumbv7s-apple-" OSNAME); return true; + case 15: arch.SetTriple ("thumbv7m-apple-" OSNAME); return true; + case 16: arch.SetTriple ("thumbv7em-apple-" OSNAME); return true; + case 17: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 18: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 19: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 20: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 21: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7f: + switch (idx) + { + case 0: arch.SetTriple ("armv7f-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 6: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv7f-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7k: + switch (idx) + { + case 0: arch.SetTriple ("armv7k-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 6: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv7k-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7s: + switch (idx) + { + case 0: arch.SetTriple ("armv7s-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 6: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv7s-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7m: + switch (idx) + { + case 0: arch.SetTriple ("armv7m-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 6: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv7m-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7em: + switch (idx) + { + case 0: arch.SetTriple ("armv7em-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 6: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv7em-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7: + switch (idx) + { + case 0: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 5: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 6: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv6m: + switch (idx) + { + case 0: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 4: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 5: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 6: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv6: + switch (idx) + { + case 0: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 3: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 4: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 5: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 6: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv5: + switch (idx) + { + case 0: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 2: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 3: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 4: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 5: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv4: + switch (idx) + { + case 0: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 1: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 2: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 3: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + } + arch.Clear(); + return false; +} + + +const char * +PlatformDarwin::GetDeveloperDirectory() +{ + Mutex::Locker locker (m_mutex); + if (m_developer_directory.empty()) + { + bool developer_dir_path_valid = false; + char developer_dir_path[PATH_MAX]; + FileSpec temp_file_spec; + if (HostInfo::GetLLDBPath(ePathTypeLLDBShlibDir, temp_file_spec)) + { + if (temp_file_spec.GetPath (developer_dir_path, sizeof(developer_dir_path))) + { + char *shared_frameworks = strstr (developer_dir_path, "/SharedFrameworks/LLDB.framework"); + if (shared_frameworks) + { + ::snprintf (shared_frameworks, + sizeof(developer_dir_path) - (shared_frameworks - developer_dir_path), + "/Developer"); + developer_dir_path_valid = true; + } + else + { + char *lib_priv_frameworks = strstr (developer_dir_path, "/Library/PrivateFrameworks/LLDB.framework"); + if (lib_priv_frameworks) + { + *lib_priv_frameworks = '\0'; + developer_dir_path_valid = true; + } + } + } + } + + if (!developer_dir_path_valid) + { + std::string xcode_dir_path; + const char *xcode_select_prefix_dir = getenv ("XCODE_SELECT_PREFIX_DIR"); + if (xcode_select_prefix_dir) + xcode_dir_path.append (xcode_select_prefix_dir); + xcode_dir_path.append ("/usr/share/xcode-select/xcode_dir_path"); + temp_file_spec.SetFile(xcode_dir_path.c_str(), false); + size_t bytes_read = temp_file_spec.ReadFileContents(0, developer_dir_path, sizeof(developer_dir_path), NULL); + if (bytes_read > 0) + { + developer_dir_path[bytes_read] = '\0'; + while (developer_dir_path[bytes_read-1] == '\r' || + developer_dir_path[bytes_read-1] == '\n') + developer_dir_path[--bytes_read] = '\0'; + developer_dir_path_valid = true; + } + } + + if (!developer_dir_path_valid) + { + FileSpec xcode_select_cmd ("/usr/bin/xcode-select", false); + if (xcode_select_cmd.Exists()) + { + int exit_status = -1; + int signo = -1; + std::string command_output; + Error error = Host::RunShellCommand ("/usr/bin/xcode-select --print-path", + NULL, // current working directory + &exit_status, + &signo, + &command_output, + 2, // short timeout + false); // don't run in a shell + if (error.Success() && exit_status == 0 && !command_output.empty()) + { + const char *cmd_output_ptr = command_output.c_str(); + developer_dir_path[sizeof (developer_dir_path) - 1] = '\0'; + size_t i; + for (i = 0; i < sizeof (developer_dir_path) - 1; i++) + { + if (cmd_output_ptr[i] == '\r' || cmd_output_ptr[i] == '\n' || cmd_output_ptr[i] == '\0') + break; + developer_dir_path[i] = cmd_output_ptr[i]; + } + developer_dir_path[i] = '\0'; + + FileSpec devel_dir (developer_dir_path, false); + if (devel_dir.Exists() && devel_dir.IsDirectory()) + { + developer_dir_path_valid = true; + } + } + } + } + + if (developer_dir_path_valid) + { + temp_file_spec.SetFile (developer_dir_path, false); + if (temp_file_spec.Exists()) + { + m_developer_directory.assign (developer_dir_path); + return m_developer_directory.c_str(); + } + } + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_developer_directory.assign (1, '\0'); + } + + // We should have put a single NULL character into m_developer_directory + // or it should have a valid path if the code gets here + assert (m_developer_directory.empty() == false); + if (m_developer_directory[0]) + return m_developer_directory.c_str(); + return NULL; +} + + +BreakpointSP +PlatformDarwin::SetThreadCreationBreakpoint (Target &target) +{ + BreakpointSP bp_sp; + static const char *g_bp_names[] = + { + "start_wqthread", + "_pthread_wqthread", + "_pthread_start", + }; + + static const char *g_bp_modules[] = + { + "libsystem_c.dylib", + "libSystem.B.dylib" + }; + + FileSpecList bp_modules; + for (size_t i = 0; i < llvm::array_lengthof(g_bp_modules); i++) + { + const char *bp_module = g_bp_modules[i]; + bp_modules.Append(FileSpec(bp_module, false)); + } + + bool internal = true; + bool hardware = false; + LazyBool skip_prologue = eLazyBoolNo; + bp_sp = target.CreateBreakpoint (&bp_modules, + NULL, + g_bp_names, + llvm::array_lengthof(g_bp_names), + eFunctionNameTypeFull, + eLanguageTypeUnknown, + skip_prologue, + internal, + hardware); + bp_sp->SetBreakpointKind("thread-creation"); + + return bp_sp; +} + + +int32_t +PlatformDarwin::GetResumeCountForLaunchInfo (ProcessLaunchInfo &launch_info) +{ + const FileSpec &shell = launch_info.GetShell(); + if (!shell) + return 1; + + std::string shell_string = shell.GetPath(); + const char *shell_name = strrchr (shell_string.c_str(), '/'); + if (shell_name == NULL) + shell_name = shell_string.c_str(); + else + shell_name++; + + if (strcmp (shell_name, "sh") == 0) + { + // /bin/sh re-exec's itself as /bin/bash requiring another resume. + // But it only does this if the COMMAND_MODE environment variable + // is set to "legacy". + const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector(); + if (envp != NULL) + { + for (int i = 0; envp[i] != NULL; i++) + { + if (strcmp (envp[i], "COMMAND_MODE=legacy" ) == 0) + return 2; + } + } + return 1; + } + else if (strcmp (shell_name, "csh") == 0 + || strcmp (shell_name, "tcsh") == 0 + || strcmp (shell_name, "zsh") == 0) + { + // csh and tcsh always seem to re-exec themselves. + return 2; + } + else + return 1; +} + +void +PlatformDarwin::CalculateTrapHandlerSymbolNames () +{ + m_trap_handlers.push_back (ConstString ("_sigtramp")); +} + + +static const char *const sdk_strings[] = { + "MacOSX", + "iPhoneSimulator", + "iPhoneOS", +}; + +static FileSpec +CheckPathForXcode(const FileSpec &fspec) +{ + if (fspec.Exists()) + { + const char substr[] = ".app/Contents/"; + + std::string path_to_shlib = fspec.GetPath(); + size_t pos = path_to_shlib.rfind(substr); + if (pos != std::string::npos) + { + path_to_shlib.erase(pos + strlen(substr)); + FileSpec ret (path_to_shlib.c_str(), false); + + FileSpec xcode_binary_path = ret; + xcode_binary_path.AppendPathComponent("MacOS"); + xcode_binary_path.AppendPathComponent("Xcode"); + + if (xcode_binary_path.Exists()) + { + return ret; + } + } + } + return FileSpec(); +} + +static FileSpec +GetXcodeContentsPath () +{ + static FileSpec g_xcode_filespec; + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + + + FileSpec fspec; + + // First get the program file spec. If lldb.so or LLDB.framework is running + // in a program and that program is Xcode, the path returned with be the path + // to Xcode.app/Contents/MacOS/Xcode, so this will be the correct Xcode to use. + fspec = HostInfo::GetProgramFileSpec(); + + if (fspec) + { + // Ignore the current binary if it is python. + std::string basename_lower = fspec.GetFilename ().GetCString (); + std::transform(basename_lower.begin (), basename_lower.end (), basename_lower.begin (), tolower); + if (basename_lower != "python") + { + g_xcode_filespec = CheckPathForXcode(fspec); + } + } + + // Next check DEVELOPER_DIR environment variable + if (!g_xcode_filespec) + { + const char *developer_dir_env_var = getenv("DEVELOPER_DIR"); + if (developer_dir_env_var && developer_dir_env_var[0]) + { + g_xcode_filespec = CheckPathForXcode(FileSpec(developer_dir_env_var, true)); + } + + // Fall back to using "xcrun" to find the selected Xcode + if (!g_xcode_filespec) + { + int status = 0; + int signo = 0; + std::string output; + const char *command = "/usr/bin/xcode-select -p"; + lldb_private::Error error = Host::RunShellCommand (command, // shell command to run + NULL, // current working directory + &status, // Put the exit status of the process in here + &signo, // Put the signal that caused the process to exit in here + &output, // Get the output from the command and place it in this string + 3); // Timeout in seconds to wait for shell program to finish + if (status == 0 && !output.empty()) + { + size_t first_non_newline = output.find_last_not_of("\r\n"); + if (first_non_newline != std::string::npos) + { + output.erase(first_non_newline+1); + } + output.append("/.."); + + g_xcode_filespec = CheckPathForXcode(FileSpec(output.c_str(), false)); + } + } + } + }); + + return g_xcode_filespec; +} + +bool +PlatformDarwin::SDKSupportsModules (SDKType sdk_type, uint32_t major, uint32_t minor, uint32_t micro) +{ + switch (sdk_type) + { + case SDKType::MacOSX: + if (major > 10 || (major == 10 && minor >= 10)) + return true; + break; + case SDKType::iPhoneOS: + case SDKType::iPhoneSimulator: + if (major >= 8) + return true; + break; + } + + return false; +} + +bool +PlatformDarwin::SDKSupportsModules (SDKType desired_type, const FileSpec &sdk_path) +{ + ConstString last_path_component = sdk_path.GetLastPathComponent(); + + if (last_path_component) + { + const llvm::StringRef sdk_name = last_path_component.GetStringRef(); + + llvm::StringRef version_part; + + if (sdk_name.startswith(sdk_strings[(int)desired_type])) + { + version_part = sdk_name.drop_front(strlen(sdk_strings[(int)desired_type])); + } + else + { + return false; + } + + const size_t major_dot_offset = version_part.find('.'); + if (major_dot_offset == llvm::StringRef::npos) + return false; + + const llvm::StringRef major_version = version_part.slice(0, major_dot_offset); + const llvm::StringRef minor_part = version_part.drop_front(major_dot_offset + 1); + + const size_t minor_dot_offset = minor_part.find('.'); + if (minor_dot_offset == llvm::StringRef::npos) + return false; + + const llvm::StringRef minor_version = minor_part.slice(0, minor_dot_offset); + + unsigned int major = 0; + unsigned int minor = 0; + unsigned int micro = 0; + + if (major_version.getAsInteger(10, major)) + return false; + + if (minor_version.getAsInteger(10, minor)) + return false; + + return SDKSupportsModules(desired_type, major, minor, micro); + } + + return false; +} + +FileSpec::EnumerateDirectoryResult +PlatformDarwin::DirectoryEnumerator(void *baton, + FileSpec::FileType file_type, + const FileSpec &spec) +{ + SDKEnumeratorInfo *enumerator_info = static_cast<SDKEnumeratorInfo*>(baton); + + if (SDKSupportsModules(enumerator_info->sdk_type, spec)) + { + enumerator_info->found_path = spec; + return FileSpec::EnumerateDirectoryResult::eEnumerateDirectoryResultNext; + } + + return FileSpec::EnumerateDirectoryResult::eEnumerateDirectoryResultNext; +} + +FileSpec +PlatformDarwin::FindSDKInXcodeForModules (SDKType sdk_type, + const FileSpec &sdks_spec) +{ + // Look inside Xcode for the required installed iOS SDK version + + if (!sdks_spec.IsDirectory()) + return FileSpec(); + + const bool find_directories = true; + const bool find_files = false; + const bool find_other = true; // include symlinks + + SDKEnumeratorInfo enumerator_info; + + enumerator_info.sdk_type = sdk_type; + + FileSpec::EnumerateDirectory(sdks_spec.GetPath().c_str(), + find_directories, + find_files, + find_other, + DirectoryEnumerator, + &enumerator_info); + + if (enumerator_info.found_path.IsDirectory()) + return enumerator_info.found_path; + else + return FileSpec(); +} + +FileSpec +PlatformDarwin::GetSDKDirectoryForModules (SDKType sdk_type) +{ + switch (sdk_type) + { + case SDKType::MacOSX: + case SDKType::iPhoneSimulator: + case SDKType::iPhoneOS: + break; + } + + FileSpec sdks_spec = GetXcodeContentsPath(); + sdks_spec.AppendPathComponent("Developer"); + sdks_spec.AppendPathComponent("Platforms"); + + switch (sdk_type) + { + case SDKType::MacOSX: + sdks_spec.AppendPathComponent("MacOSX.platform"); + break; + case SDKType::iPhoneSimulator: + sdks_spec.AppendPathComponent("iPhoneSimulator.platform"); + break; + case SDKType::iPhoneOS: + sdks_spec.AppendPathComponent("iPhoneOS.platform"); + break; + } + + sdks_spec.AppendPathComponent("Developer"); + sdks_spec.AppendPathComponent("SDKs"); + + if (sdk_type == SDKType::MacOSX) + { + uint32_t major = 0; + uint32_t minor = 0; + uint32_t micro = 0; + + if (HostInfo::GetOSVersion(major, minor, micro)) + { + if (SDKSupportsModules(SDKType::MacOSX, major, minor, micro)) + { + // We slightly prefer the exact SDK for this machine. See if it is there. + + FileSpec native_sdk_spec = sdks_spec; + StreamString native_sdk_name; + native_sdk_name.Printf("MacOSX%u.%u.sdk", major, minor); + native_sdk_spec.AppendPathComponent(native_sdk_name.GetString().c_str()); + + if (native_sdk_spec.Exists()) + { + return native_sdk_spec; + } + } + } + } + + return FindSDKInXcodeForModules(sdk_type, sdks_spec); +} + +void +PlatformDarwin::AddClangModuleCompilationOptionsForSDKType (Target *target, std::vector<std::string> &options, SDKType sdk_type) +{ + const std::vector<std::string> apple_arguments = + { + "-x", "objective-c++", + "-fobjc-arc", + "-fblocks", + "-D_ISO646_H", + "-D__ISO646_H" + }; + + options.insert(options.end(), + apple_arguments.begin(), + apple_arguments.end()); + + StreamString minimum_version_option; + uint32_t versions[3] = { 0, 0, 0 }; + bool use_current_os_version = false; + switch (sdk_type) + { + case SDKType::iPhoneOS: +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + use_current_os_version = true; +#else + use_current_os_version = false; +#endif + break; + + case SDKType::iPhoneSimulator: + use_current_os_version = false; + break; + + case SDKType::MacOSX: +#if defined (__i386__) || defined (__x86_64__) + use_current_os_version = true; +#else + use_current_os_version = false; +#endif + break; + } + + bool versions_valid = false; + if (use_current_os_version) + versions_valid = GetOSVersion(versions[0], versions[1], versions[2]); + else if (target) + { + // Our OS doesn't match our executable so we need to get the min OS version from the object file + ModuleSP exe_module_sp = target->GetExecutableModule(); + if (exe_module_sp) + { + ObjectFile *object_file = exe_module_sp->GetObjectFile(); + if (object_file) + versions_valid = object_file->GetMinimumOSVersion(versions, 3) > 0; + } + } + // Only add the version-min options if we got a version from somewhere + if (versions_valid && versions[0] != UINT32_MAX) + { + // Make any invalid versions be zero if needed + if (versions[1] == UINT32_MAX) + versions[1] = 0; + if (versions[2] == UINT32_MAX) + versions[2] = 0; + + switch (sdk_type) + { + case SDKType::iPhoneOS: + minimum_version_option.PutCString("-mios-version-min="); + minimum_version_option.PutCString(clang::VersionTuple(versions[0], versions[1], versions[2]).getAsString().c_str()); + break; + case SDKType::iPhoneSimulator: + minimum_version_option.PutCString("-mios-simulator-version-min="); + minimum_version_option.PutCString(clang::VersionTuple(versions[0], versions[1], versions[2]).getAsString().c_str()); + break; + case SDKType::MacOSX: + minimum_version_option.PutCString("-mmacosx-version-min="); + minimum_version_option.PutCString(clang::VersionTuple(versions[0], versions[1], versions[2]).getAsString().c_str()); + } + options.push_back(minimum_version_option.GetString()); + } + + FileSpec sysroot_spec; + // Scope for mutex locker below + { + Mutex::Locker locker (m_mutex); + sysroot_spec = GetSDKDirectoryForModules(sdk_type); + } + + if (sysroot_spec.IsDirectory()) + { + options.push_back("-isysroot"); + options.push_back(sysroot_spec.GetPath()); + } +} + +ConstString +PlatformDarwin::GetFullNameForDylib (ConstString basename) +{ + if (basename.IsEmpty()) + return basename; + + StreamString stream; + stream.Printf("lib%s.dylib", basename.GetCString()); + return ConstString(stream.GetData()); +} + +bool +PlatformDarwin::GetOSVersion (uint32_t &major, + uint32_t &minor, + uint32_t &update, + Process *process) +{ + if (process && strstr(GetPluginName().GetCString(), "-simulator")) + { + lldb_private::ProcessInstanceInfo proc_info; + if (Host::GetProcessInfo(process->GetID(), proc_info)) + { + Args &env = proc_info.GetEnvironmentEntries(); + const size_t n = env.GetArgumentCount(); + const llvm::StringRef k_runtime_version("SIMULATOR_RUNTIME_VERSION="); + const llvm::StringRef k_dyld_root_path("DYLD_ROOT_PATH="); + std::string dyld_root_path; + + for (size_t i=0; i<n; ++i) + { + const char *env_cstr = env.GetArgumentAtIndex(i); + if (env_cstr) + { + llvm::StringRef env_str(env_cstr); + if (env_str.startswith(k_runtime_version)) + { + llvm::StringRef version_str(env_str.substr(k_runtime_version.size())); + Args::StringToVersion (version_str.data(), major, minor, update); + if (major != UINT32_MAX) + return true; + } + else if (env_str.startswith(k_dyld_root_path)) + { + dyld_root_path = env_str.substr(k_dyld_root_path.size()).str(); + } + } + } + + if (!dyld_root_path.empty()) + { + dyld_root_path += "/System/Library/CoreServices/SystemVersion.plist"; + ApplePropertyList system_version_plist(dyld_root_path.c_str()); + std::string product_version; + if (system_version_plist.GetValueAsString("ProductVersion", product_version)) + { + Args::StringToVersion (product_version.c_str(), major, minor, update); + return major != UINT32_MAX; + } + } + + } + // For simulator platforms, do NOT call back through Platform::GetOSVersion() + // as it might call Process::GetHostOSVersion() which we don't want as it will be + // incorrect + return false; + } + + return Platform::GetOSVersion(major, minor, update, process); +} + +lldb_private::FileSpec +PlatformDarwin::LocateExecutable (const char *basename) +{ + // A collection of SBFileSpec whose SBFileSpec.m_directory members are filled in with + // any executable directories that should be searched. + static std::vector<FileSpec> g_executable_dirs; + + // Find the global list of directories that we will search for + // executables once so we don't keep doing the work over and over. + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + + // When locating executables, trust the DEVELOPER_DIR first if it is set + FileSpec xcode_contents_dir = GetXcodeContentsPath(); + if (xcode_contents_dir) + { + FileSpec xcode_lldb_resources = xcode_contents_dir; + xcode_lldb_resources.AppendPathComponent("SharedFrameworks"); + xcode_lldb_resources.AppendPathComponent("LLDB.framework"); + xcode_lldb_resources.AppendPathComponent("Resources"); + if (xcode_lldb_resources.Exists()) + { + FileSpec dir; + dir.GetDirectory().SetCString(xcode_lldb_resources.GetPath().c_str()); + g_executable_dirs.push_back(dir); + } + } + }); + + // Now search the global list of executable directories for the executable we + // are looking for + for (const auto &executable_dir : g_executable_dirs) + { + FileSpec executable_file; + executable_file.GetDirectory() = executable_dir.GetDirectory(); + executable_file.GetFilename().SetCString(basename); + if (executable_file.Exists()) + return executable_file; + } + + return FileSpec(); +} diff --git a/source/Plugins/Platform/MacOSX/PlatformDarwin.h b/source/Plugins/Platform/MacOSX/PlatformDarwin.h new file mode 100644 index 000000000000..b280b35da655 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformDarwin.h @@ -0,0 +1,156 @@ +//===-- PlatformDarwin.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformDarwin_h_ +#define liblldb_PlatformDarwin_h_ + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" +#include "Plugins/Platform/POSIX/PlatformPOSIX.h" + +class PlatformDarwin : public PlatformPOSIX +{ +public: + PlatformDarwin(bool is_host); + + ~PlatformDarwin() override; + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + lldb_private::Error + ResolveSymbolFile (lldb_private::Target &target, + const lldb_private::ModuleSpec &sym_spec, + lldb_private::FileSpec &sym_file) override; + + lldb_private::FileSpecList + LocateExecutableScriptingResources (lldb_private::Target *target, + lldb_private::Module &module, + lldb_private::Stream* feedback_stream) override; + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + size_t + GetSoftwareBreakpointTrapOpcode (lldb_private::Target &target, + lldb_private::BreakpointSite *bp_site) override; + + bool + GetProcessInfo (lldb::pid_t pid, + lldb_private::ProcessInstanceInfo &proc_info) override; + + lldb::BreakpointSP + SetThreadCreationBreakpoint (lldb_private::Target &target) override; + + uint32_t + FindProcesses (const lldb_private::ProcessInstanceInfoMatch &match_info, + lldb_private::ProcessInstanceInfoList &process_infos) override; + + bool + ModuleIsExcludedForUnconstrainedSearches(lldb_private::Target &target, + const lldb::ModuleSP &module_sp) override; + + bool + ARMGetSupportedArchitectureAtIndex (uint32_t idx, lldb_private::ArchSpec &arch); + + bool + x86GetSupportedArchitectureAtIndex (uint32_t idx, lldb_private::ArchSpec &arch); + + int32_t + GetResumeCountForLaunchInfo (lldb_private::ProcessLaunchInfo &launch_info) override; + + void + CalculateTrapHandlerSymbolNames () override; + + bool + GetOSVersion (uint32_t &major, + uint32_t &minor, + uint32_t &update, + lldb_private::Process *process = nullptr) override; + + bool + SupportsModules () override { return true; } + + lldb_private::ConstString + GetFullNameForDylib (lldb_private::ConstString basename) override; + + lldb_private::FileSpec + LocateExecutable (const char *basename) override; + +protected: + void + ReadLibdispatchOffsetsAddress (lldb_private::Process *process); + + void + ReadLibdispatchOffsets (lldb_private::Process *process); + + virtual lldb_private::Error + GetSharedModuleWithLocalCache (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr); + + enum class SDKType { + MacOSX = 0, + iPhoneSimulator, + iPhoneOS, + }; + + static bool + SDKSupportsModules (SDKType sdk_type, uint32_t major, uint32_t minor, uint32_t micro); + + static bool + SDKSupportsModules (SDKType desired_type, const lldb_private::FileSpec &sdk_path); + + struct SDKEnumeratorInfo { + lldb_private::FileSpec found_path; + SDKType sdk_type; + }; + + static lldb_private::FileSpec::EnumerateDirectoryResult + DirectoryEnumerator(void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &spec); + + static lldb_private::FileSpec + FindSDKInXcodeForModules (SDKType sdk_type, + const lldb_private::FileSpec &sdks_spec); + + static lldb_private::FileSpec + GetSDKDirectoryForModules (PlatformDarwin::SDKType sdk_type); + + void + AddClangModuleCompilationOptionsForSDKType (lldb_private::Target *target, std::vector<std::string> &options, SDKType sdk_type); + + std::string m_developer_directory; + + const char * + GetDeveloperDirectory(); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformDarwin); +}; + +#endif // liblldb_PlatformDarwin_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp b/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp new file mode 100644 index 000000000000..a502aa03eb26 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp @@ -0,0 +1,979 @@ +//===-- PlatformDarwinKernel.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformDarwinKernel.h" + +#if defined (__APPLE__) // This Plugin uses the Mac-specific source/Host/macosx/cfcpp utilities + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/OptionValueFileSpecList.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/Property.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include <CoreFoundation/CoreFoundation.h> + +#include "Host/macosx/cfcpp/CFCBundle.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformDarwinKernel::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformDarwinKernel::GetPluginNameStatic(), + PlatformDarwinKernel::GetDescriptionStatic(), + PlatformDarwinKernel::CreateInstance, + PlatformDarwinKernel::DebuggerInitialize); + } +} + +void +PlatformDarwinKernel::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformDarwinKernel::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformDarwinKernel::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = "<null>"; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; + + log->Printf ("PlatformDarwinKernel::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + // This is a special plugin that we don't want to activate just based on an ArchSpec for normal + // userland debugging. It is only useful in kernel debug sessions and the DynamicLoaderDarwinPlugin + // (or a user doing 'platform select') will force the creation of this Platform plugin. + if (force == false) + { + if (log) + log->Printf ("PlatformDarwinKernel::%s() aborting creation of platform because force == false", __FUNCTION__); + return PlatformSP(); + } + + bool create = force; + LazyBool is_ios_debug_session = eLazyBoolCalculate; + + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::Apple: + create = true; + break; + + // Only accept "unknown" for vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + case llvm::Triple::WatchOS: + case llvm::Triple::TvOS: + break; + // Only accept "vendor" for vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; + default: + create = false; + break; + } + } + } + if (arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + is_ios_debug_session = eLazyBoolNo; + break; + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::thumb: + is_ios_debug_session = eLazyBoolYes; + break; + default: + is_ios_debug_session = eLazyBoolCalculate; + break; + } + } + if (create) + { + if (log) + log->Printf ("PlatformDarwinKernel::%s() creating platform", __FUNCTION__); + + return PlatformSP(new PlatformDarwinKernel (is_ios_debug_session)); + } + + if (log) + log->Printf ("PlatformDarwinKernel::%s() aborting creation of platform", __FUNCTION__); + + return PlatformSP(); +} + + +lldb_private::ConstString +PlatformDarwinKernel::GetPluginNameStatic () +{ + static ConstString g_name("darwin-kernel"); + return g_name; +} + +const char * +PlatformDarwinKernel::GetDescriptionStatic() +{ + return "Darwin Kernel platform plug-in."; +} + +//------------------------------------------------------------------ +/// Code to handle the PlatformDarwinKernel settings +//------------------------------------------------------------------ + +static PropertyDefinition +g_properties[] = +{ + { "search-locally-for-kexts" , OptionValue::eTypeBoolean, true, true, NULL, NULL, "Automatically search for kexts on the local system when doing kernel debugging." }, + { "kext-directories", OptionValue::eTypeFileSpecList, false, 0, NULL, NULL, "Directories/KDKs to search for kexts in when starting a kernel debug session." }, + { NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL } +}; + +enum { + ePropertySearchForKexts = 0, + ePropertyKextDirectories +}; + + + +class PlatformDarwinKernelProperties : public Properties +{ +public: + + static ConstString & + GetSettingName () + { + static ConstString g_setting_name("darwin-kernel"); + return g_setting_name; + } + + PlatformDarwinKernelProperties() : + Properties () + { + m_collection_sp.reset (new OptionValueProperties(GetSettingName())); + m_collection_sp->Initialize(g_properties); + } + + virtual + ~PlatformDarwinKernelProperties() + { + } + + bool + GetSearchForKexts() const + { + const uint32_t idx = ePropertySearchForKexts; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); + } + + FileSpecList & + GetKextDirectories() const + { + const uint32_t idx = ePropertyKextDirectories; + OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList (NULL, false, idx); + assert(option_value); + return option_value->GetCurrentValue(); + } +}; + +typedef std::shared_ptr<PlatformDarwinKernelProperties> PlatformDarwinKernelPropertiesSP; + +static const PlatformDarwinKernelPropertiesSP & +GetGlobalProperties() +{ + static PlatformDarwinKernelPropertiesSP g_settings_sp; + if (!g_settings_sp) + g_settings_sp.reset (new PlatformDarwinKernelProperties ()); + return g_settings_sp; +} + +void +PlatformDarwinKernel::DebuggerInitialize (lldb_private::Debugger &debugger) +{ + if (!PluginManager::GetSettingForPlatformPlugin (debugger, PlatformDarwinKernelProperties::GetSettingName())) + { + const bool is_global_setting = true; + PluginManager::CreateSettingForPlatformPlugin (debugger, + GetGlobalProperties()->GetValueProperties(), + ConstString ("Properties for the PlatformDarwinKernel plug-in."), + is_global_setting); + } +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformDarwinKernel::PlatformDarwinKernel (lldb_private::LazyBool is_ios_debug_session) : + PlatformDarwin (false), // This is a remote platform + m_name_to_kext_path_map(), + m_search_directories(), + m_kernel_binaries(), + m_ios_debug_session(is_ios_debug_session) + +{ + if (GetGlobalProperties()->GetSearchForKexts()) + { + CollectKextAndKernelDirectories (); + IndexKextsInDirectories (); + IndexKernelsInDirectories (); + } +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformDarwinKernel::~PlatformDarwinKernel() +{ +} + + +void +PlatformDarwinKernel::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + strm.Printf (" Debug session type: "); + if (m_ios_debug_session == eLazyBoolYes) + strm.Printf ("iOS kernel debugging\n"); + else if (m_ios_debug_session == eLazyBoolNo) + strm.Printf ("Mac OS X kernel debugging\n"); + else + strm.Printf ("unknown kernel debugging\n"); + const uint32_t num_kext_dirs = m_search_directories.size(); + for (uint32_t i=0; i<num_kext_dirs; ++i) + { + const FileSpec &kext_dir = m_search_directories[i]; + strm.Printf (" Kext directories: [%2u] \"%s\"\n", i, kext_dir.GetPath().c_str()); + } + strm.Printf (" Total number of kexts indexed: %d\n", (int) m_name_to_kext_path_map.size()); +} + +// Populate the m_search_directories vector with directories we should search +// for kernel & kext binaries. + +void +PlatformDarwinKernel::CollectKextAndKernelDirectories () +{ + // Differentiate between "ios debug session" and "mac debug session" so we don't index + // kext bundles that won't be used in this debug session. If this is an ios kext debug + // session, looking in /System/Library/Extensions is a waste of stat()s, for example. + + // Build up a list of all SDKs we'll be searching for directories of kexts/kernels + // e.g. /Applications/Xcode.app//Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.Internal.sdk + std::vector<FileSpec> sdk_dirs; + if (m_ios_debug_session != eLazyBoolNo) + { + GetiOSSDKDirectoriesToSearch (sdk_dirs); + GetAppleTVOSSDKDirectoriesToSearch (sdk_dirs); + GetWatchOSSDKDirectoriesToSearch (sdk_dirs); + } + if (m_ios_debug_session != eLazyBoolYes) + GetMacSDKDirectoriesToSearch (sdk_dirs); + + GetGenericSDKDirectoriesToSearch (sdk_dirs); + + // Build up a list of directories that hold may kext bundles & kernels + // + // e.g. given /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ + // find + // /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.Internal.sdk/ + // and + // /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.Internal.sdk/System/Library/Extensions + + std::vector<FileSpec> kext_dirs; + SearchSDKsForKextDirectories (sdk_dirs, kext_dirs); + + if (m_ios_debug_session != eLazyBoolNo) + GetiOSDirectoriesToSearch (kext_dirs); + if (m_ios_debug_session != eLazyBoolYes) + GetMacDirectoriesToSearch (kext_dirs); + + GetGenericDirectoriesToSearch (kext_dirs); + + GetUserSpecifiedDirectoriesToSearch (kext_dirs); + + GetKernelDirectoriesToSearch (kext_dirs); + + GetCurrentDirectoryToSearch (kext_dirs); + + // We now have a complete list of directories that we will search for kext bundles + m_search_directories = kext_dirs; +} + +void +PlatformDarwinKernel::GetiOSSDKDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories) +{ + // DeveloperDirectory is something like "/Applications/Xcode.app/Contents/Developer" + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir == NULL) + developer_dir = "/Applications/Xcode.app/Contents/Developer"; + + char pathbuf[PATH_MAX]; + ::snprintf (pathbuf, sizeof (pathbuf), "%s/Platforms/iPhoneOS.platform/Developer/SDKs", developer_dir); + FileSpec ios_sdk(pathbuf, true); + if (ios_sdk.Exists() && ios_sdk.IsDirectory()) + { + directories.push_back (ios_sdk); + } +} + +void +PlatformDarwinKernel::GetAppleTVOSSDKDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories) +{ + // DeveloperDirectory is something like "/Applications/Xcode.app/Contents/Developer" + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir == NULL) + developer_dir = "/Applications/Xcode.app/Contents/Developer"; + + char pathbuf[PATH_MAX]; + ::snprintf (pathbuf, sizeof (pathbuf), "%s/Platforms/AppleTVOS.platform/Developer/SDKs", developer_dir); + FileSpec ios_sdk(pathbuf, true); + if (ios_sdk.Exists() && ios_sdk.IsDirectory()) + { + directories.push_back (ios_sdk); + } +} + +void +PlatformDarwinKernel::GetWatchOSSDKDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories) +{ + // DeveloperDirectory is something like "/Applications/Xcode.app/Contents/Developer" + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir == NULL) + developer_dir = "/Applications/Xcode.app/Contents/Developer"; + + char pathbuf[PATH_MAX]; + ::snprintf (pathbuf, sizeof (pathbuf), "%s/Platforms/watchOS.platform/Developer/SDKs", developer_dir); + FileSpec ios_sdk(pathbuf, true); + if (ios_sdk.Exists() && ios_sdk.IsDirectory()) + { + directories.push_back (ios_sdk); + } + else + { + ::snprintf (pathbuf, sizeof (pathbuf), "%s/Platforms/WatchOS.platform/Developer/SDKs", developer_dir); + FileSpec alt_watch_sdk(pathbuf, true); + if (ios_sdk.Exists() && ios_sdk.IsDirectory()) + { + directories.push_back (ios_sdk); + } + } +} + + +void +PlatformDarwinKernel::GetMacSDKDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories) +{ + // DeveloperDirectory is something like "/Applications/Xcode.app/Contents/Developer" + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir == NULL) + developer_dir = "/Applications/Xcode.app/Contents/Developer"; + + char pathbuf[PATH_MAX]; + ::snprintf (pathbuf, sizeof (pathbuf), "%s/Platforms/MacOSX.platform/Developer/SDKs", developer_dir); + FileSpec mac_sdk(pathbuf, true); + if (mac_sdk.Exists() && mac_sdk.IsDirectory()) + { + directories.push_back (mac_sdk); + } +} + +void +PlatformDarwinKernel::GetGenericSDKDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories) +{ + FileSpec generic_sdk("/AppleInternal/Developer/KDKs", true); + if (generic_sdk.Exists() && generic_sdk.IsDirectory()) + { + directories.push_back (generic_sdk); + } + + // The KDKs distributed from Apple installed on external + // developer systems may be in directories like + // /Library/Developer/KDKs/KDK_10.10_14A298i.kdk + FileSpec installed_kdks("/Library/Developer/KDKs", true); + if (installed_kdks.Exists() && installed_kdks.IsDirectory()) + { + directories.push_back (installed_kdks); + } +} + +void +PlatformDarwinKernel::GetiOSDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories) +{ +} + +void +PlatformDarwinKernel::GetMacDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories) +{ + FileSpec sle("/System/Library/Extensions", true); + if (sle.Exists() && sle.IsDirectory()) + { + directories.push_back(sle); + } + + FileSpec le("/Library/Extensions", true); + if (le.Exists() && le.IsDirectory()) + { + directories.push_back(le); + } + + FileSpec kdk("/Volumes/KernelDebugKit", true); + if (kdk.Exists() && kdk.IsDirectory()) + { + directories.push_back(kdk); + } +} + +void +PlatformDarwinKernel::GetGenericDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories) +{ + // DeveloperDirectory is something like "/Applications/Xcode.app/Contents/Developer" + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir == NULL) + developer_dir = "/Applications/Xcode.app/Contents/Developer"; + + char pathbuf[PATH_MAX]; + ::snprintf (pathbuf, sizeof (pathbuf), "%s/../Symbols", developer_dir); + FileSpec symbols_dir (pathbuf, true); + if (symbols_dir.Exists() && symbols_dir.IsDirectory()) + { + directories.push_back (symbols_dir); + } +} + +void +PlatformDarwinKernel::GetKernelDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories) +{ + FileSpec system_library_kernels ("/System/Library/Kernels", true); + if (system_library_kernels.Exists() && system_library_kernels.IsDirectory()) + { + directories.push_back (system_library_kernels); + } + FileSpec slek("/System/Library/Extensions/KDK", true); + if (slek.Exists() && slek.IsDirectory()) + { + directories.push_back(slek); + } +} + +void +PlatformDarwinKernel::GetCurrentDirectoryToSearch (std::vector<lldb_private::FileSpec> &directories) +{ + directories.push_back (FileSpec (".", true)); + + FileSpec sle_directory ("System/Library/Extensions", true); + if (sle_directory.Exists() && sle_directory.IsDirectory()) + { + directories.push_back (sle_directory); + } + + FileSpec le_directory ("Library/Extensions", true); + if (le_directory.Exists() && le_directory.IsDirectory()) + { + directories.push_back (le_directory); + } + + FileSpec slk_directory ("System/Library/Kernels", true); + if (slk_directory.Exists() && slk_directory.IsDirectory()) + { + directories.push_back (slk_directory); + } + FileSpec slek("System/Library/Extensions/KDK", true); + if (slek.Exists() && slek.IsDirectory()) + { + directories.push_back(slek); + } +} + +void +PlatformDarwinKernel::GetUserSpecifiedDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories) +{ + FileSpecList user_dirs(GetGlobalProperties()->GetKextDirectories()); + std::vector<FileSpec> possible_sdk_dirs; + + const uint32_t user_dirs_count = user_dirs.GetSize(); + for (uint32_t i = 0; i < user_dirs_count; i++) + { + FileSpec dir = user_dirs.GetFileSpecAtIndex (i); + dir.ResolvePath(); + if (dir.Exists() && dir.IsDirectory()) + { + directories.push_back (dir); + possible_sdk_dirs.push_back (dir); // does this directory have a *.sdk or *.kdk that we should look in? + + // Is there a "System/Library/Extensions" subdir of this directory? + std::string dir_sle_path = dir.GetPath(); + dir_sle_path.append ("/System/Library/Extensions"); + FileSpec dir_sle(dir_sle_path.c_str(), true); + if (dir_sle.Exists() && dir_sle.IsDirectory()) + { + directories.push_back (dir_sle); + } + + // Is there a "System/Library/Kernels" subdir of this directory? + std::string dir_slk_path = dir.GetPath(); + dir_slk_path.append ("/System/Library/Kernels"); + FileSpec dir_slk(dir_slk_path.c_str(), true); + if (dir_slk.Exists() && dir_slk.IsDirectory()) + { + directories.push_back (dir_slk); + } + + // Is there a "System/Library/Extensions/KDK" subdir of this directory? + std::string dir_slek_path = dir.GetPath(); + dir_slek_path.append ("/System/Library/Kernels"); + FileSpec dir_slek(dir_slek_path.c_str(), true); + if (dir_slek.Exists() && dir_slek.IsDirectory()) + { + directories.push_back (dir_slek); + } + } + } + + SearchSDKsForKextDirectories (possible_sdk_dirs, directories); +} + +// Scan through the SDK directories, looking for directories where kexts are likely. +// Add those directories to kext_dirs. +void +PlatformDarwinKernel::SearchSDKsForKextDirectories (std::vector<lldb_private::FileSpec> sdk_dirs, std::vector<lldb_private::FileSpec> &kext_dirs) +{ + const uint32_t num_sdks = sdk_dirs.size(); + for (uint32_t i = 0; i < num_sdks; i++) + { + const FileSpec &sdk_dir = sdk_dirs[i]; + std::string sdk_dir_path = sdk_dir.GetPath(); + if (!sdk_dir_path.empty()) + { + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + FileSpec::EnumerateDirectory (sdk_dir_path.c_str(), + find_directories, + find_files, + find_other, + GetKextDirectoriesInSDK, + &kext_dirs); + } + } +} + +// Callback for FileSpec::EnumerateDirectory(). +// Step through the entries in a directory like +// /Applications/Xcode.app//Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs +// looking for any subdirectories of the form MacOSX10.8.Internal.sdk/System/Library/Extensions +// Adds these to the vector of FileSpec's. + +FileSpec::EnumerateDirectoryResult +PlatformDarwinKernel::GetKextDirectoriesInSDK (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeDirectory + && (file_spec.GetFileNameExtension() == ConstString("sdk") + || file_spec.GetFileNameExtension() == ConstString("kdk"))) + { + std::string kext_directory_path = file_spec.GetPath(); + + // Append the raw directory path, e.g. /Library/Developer/KDKs/KDK_10.10_14A298i.kdk + // to the directory search list -- there may be kexts sitting directly + // in that directory instead of being in a System/Library/Extensions subdir. + ((std::vector<lldb_private::FileSpec> *)baton)->push_back(file_spec); + + // Check to see if there is a System/Library/Extensions subdir & add it if it exists + + std::string sle_kext_directory_path (kext_directory_path); + sle_kext_directory_path.append ("/System/Library/Extensions"); + FileSpec sle_kext_directory (sle_kext_directory_path.c_str(), true); + if (sle_kext_directory.Exists() && sle_kext_directory.IsDirectory()) + { + ((std::vector<lldb_private::FileSpec> *)baton)->push_back(sle_kext_directory); + } + + // Check to see if there is a Library/Extensions subdir & add it if it exists + + std::string le_kext_directory_path (kext_directory_path); + le_kext_directory_path.append ("/Library/Extensions"); + FileSpec le_kext_directory (le_kext_directory_path.c_str(), true); + if (le_kext_directory.Exists() && le_kext_directory.IsDirectory()) + { + ((std::vector<lldb_private::FileSpec> *)baton)->push_back(le_kext_directory); + } + + // Check to see if there is a System/Library/Kernels subdir & add it if it exists + std::string slk_kernel_path (kext_directory_path); + slk_kernel_path.append ("/System/Library/Kernels"); + FileSpec slk_kernel_directory (slk_kernel_path.c_str(), true); + if (slk_kernel_directory.Exists() && slk_kernel_directory.IsDirectory()) + { + ((std::vector<lldb_private::FileSpec> *)baton)->push_back(slk_kernel_directory); + } + + // Check to see if there is a System/Library/Extensions/KDK subdir & add it if it exists + std::string slek_kernel_path (kext_directory_path); + slek_kernel_path.append ("/System/Library/Extensions/KDK"); + FileSpec slek_kernel_directory (slek_kernel_path.c_str(), true); + if (slek_kernel_directory.Exists() && slek_kernel_directory.IsDirectory()) + { + ((std::vector<lldb_private::FileSpec> *)baton)->push_back(slek_kernel_directory); + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + +void +PlatformDarwinKernel::IndexKextsInDirectories () +{ + std::vector<FileSpec> kext_bundles; + + const uint32_t num_dirs = m_search_directories.size(); + for (uint32_t i = 0; i < num_dirs; i++) + { + const FileSpec &dir = m_search_directories[i]; + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + FileSpec::EnumerateDirectory (dir.GetPath().c_str(), + find_directories, + find_files, + find_other, + GetKextsInDirectory, + &kext_bundles); + } + + const uint32_t num_kexts = kext_bundles.size(); + for (uint32_t i = 0; i < num_kexts; i++) + { + const FileSpec &kext = kext_bundles[i]; + CFCBundle bundle (kext.GetPath().c_str()); + CFStringRef bundle_id (bundle.GetIdentifier()); + if (bundle_id && CFGetTypeID (bundle_id) == CFStringGetTypeID ()) + { + char bundle_id_buf[PATH_MAX]; + if (CFStringGetCString (bundle_id, bundle_id_buf, sizeof (bundle_id_buf), kCFStringEncodingUTF8)) + { + ConstString bundle_conststr(bundle_id_buf); + m_name_to_kext_path_map.insert(std::pair<ConstString, FileSpec>(bundle_conststr, kext)); + } + } + } +} + +// Callback for FileSpec::EnumerateDirectory(). +// Step through the entries in a directory like /System/Library/Extensions, find .kext bundles, add them +// to the vector of FileSpecs. +// If a .kext bundle has a Contents/PlugIns or PlugIns subdir, search for kexts in there too. + +FileSpec::EnumerateDirectoryResult +PlatformDarwinKernel::GetKextsInDirectory (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeDirectory && file_spec.GetFileNameExtension() == ConstString("kext")) + { + ((std::vector<lldb_private::FileSpec> *)baton)->push_back(file_spec); + std::string kext_bundle_path = file_spec.GetPath(); + std::string search_here_too; + std::string contents_plugins_path = kext_bundle_path + "/Contents/PlugIns"; + FileSpec contents_plugins (contents_plugins_path.c_str(), false); + if (contents_plugins.Exists() && contents_plugins.IsDirectory()) + { + search_here_too = contents_plugins_path; + } + else + { + std::string plugins_path = kext_bundle_path + "/PlugIns"; + FileSpec plugins (plugins_path.c_str(), false); + if (plugins.Exists() && plugins.IsDirectory()) + { + search_here_too = plugins_path; + } + } + + if (!search_here_too.empty()) + { + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + FileSpec::EnumerateDirectory (search_here_too.c_str(), + find_directories, + find_files, + find_other, + GetKextsInDirectory, + baton); + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + +void +PlatformDarwinKernel::IndexKernelsInDirectories () +{ + std::vector<FileSpec> kernels; + + + const uint32_t num_dirs = m_search_directories.size(); + for (uint32_t i = 0; i < num_dirs; i++) + { + const FileSpec &dir = m_search_directories[i]; + const bool find_directories = false; + const bool find_files = true; + const bool find_other = true; // I think eFileTypeSymbolicLink are "other"s. + FileSpec::EnumerateDirectory (dir.GetPath().c_str(), + find_directories, + find_files, + find_other, + GetKernelsInDirectory, + &m_kernel_binaries); + } +} + +// Callback for FileSpec::EnumerateDirectory(). +// Step through the entries in a directory like /System/Library/Kernels/, find kernel binaries, +// add them to m_kernel_binaries. + +// We're only doing a filename match here. We won't try opening the file to see if it's really +// a kernel or not until we need to find a kernel of a given UUID. There's no cheap way to find +// the UUID of a file (or if it's a Mach-O binary at all) without creating a whole Module for +// the file and throwing it away if it's not wanted. + +FileSpec::EnumerateDirectoryResult +PlatformDarwinKernel::GetKernelsInDirectory (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeRegular || file_type == FileSpec::eFileTypeSymbolicLink) + { + ConstString filename = file_spec.GetFilename(); + if (strncmp (filename.GetCString(), "kernel", 6) == 0 + || strncmp (filename.GetCString(), "mach", 4) == 0) + { + // This is m_kernel_binaries but we're in a class method here + ((std::vector<lldb_private::FileSpec> *)baton)->push_back(file_spec); + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + + +Error +PlatformDarwinKernel::GetSharedModule (const ModuleSpec &module_spec, + Process *process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + Error error; + module_sp.reset(); + const FileSpec &platform_file = module_spec.GetFileSpec(); + + // Treat the file's path as a kext bundle ID (e.g. "com.apple.driver.AppleIRController") and search our kext index. + std::string kext_bundle_id = platform_file.GetPath(); + if (!kext_bundle_id.empty()) + { + ConstString kext_bundle_cs(kext_bundle_id.c_str()); + if (m_name_to_kext_path_map.count(kext_bundle_cs) > 0) + { + for (BundleIDToKextIterator it = m_name_to_kext_path_map.begin (); it != m_name_to_kext_path_map.end (); ++it) + { + if (it->first == kext_bundle_cs) + { + error = ExamineKextForMatchingUUID (it->second, module_spec.GetUUID(), module_spec.GetArchitecture(), module_sp); + if (module_sp.get()) + { + return error; + } + } + } + } + } + + if (kext_bundle_id.compare("mach_kernel") == 0 && module_spec.GetUUID().IsValid()) + { + for (auto possible_kernel : m_kernel_binaries) + { + if (possible_kernel.Exists()) + { + ModuleSpec kern_spec (possible_kernel); + kern_spec.GetUUID() = module_spec.GetUUID(); + ModuleSP module_sp (new Module (kern_spec)); + if (module_sp && module_sp->GetObjectFile() && module_sp->MatchesModuleSpec (kern_spec)) + { + Error error; + error = ModuleList::GetSharedModule (kern_spec, module_sp, NULL, NULL, NULL); + if (module_sp && module_sp->GetObjectFile()) + { + return error; + } + } + } + } + } + + // Else fall back to treating the file's path as an actual file path - defer to PlatformDarwin's GetSharedModule. + return PlatformDarwin::GetSharedModule (module_spec, process, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); +} + +Error +PlatformDarwinKernel::ExamineKextForMatchingUUID (const FileSpec &kext_bundle_path, const lldb_private::UUID &uuid, const ArchSpec &arch, ModuleSP &exe_module_sp) +{ + Error error; + FileSpec exe_file = kext_bundle_path; + Host::ResolveExecutableInBundle (exe_file); + if (exe_file.Exists()) + { + ModuleSpec exe_spec (exe_file); + exe_spec.GetUUID() = uuid; + if (!uuid.IsValid()) + { + exe_spec.GetArchitecture() = arch; + } + + // First try to create a ModuleSP with the file / arch and see if the UUID matches. + // If that fails (this exec file doesn't have the correct uuid), don't call GetSharedModule + // (which may call in to the DebugSymbols framework and therefore can be slow.) + ModuleSP module_sp (new Module (exe_spec)); + if (module_sp && module_sp->GetObjectFile() && module_sp->MatchesModuleSpec (exe_spec)) + { + error = ModuleList::GetSharedModule (exe_spec, exe_module_sp, NULL, NULL, NULL); + if (exe_module_sp && exe_module_sp->GetObjectFile()) + { + return error; + } + } + exe_module_sp.reset(); + } + return error; +} + +bool +PlatformDarwinKernel::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + return ARMGetSupportedArchitectureAtIndex (idx, arch); +#else + return x86GetSupportedArchitectureAtIndex (idx, arch); +#endif +} + +void +PlatformDarwinKernel::CalculateTrapHandlerSymbolNames () +{ + m_trap_handlers.push_back(ConstString ("trap_from_kernel")); + m_trap_handlers.push_back(ConstString ("hndl_machine_check")); + m_trap_handlers.push_back(ConstString ("hndl_double_fault")); + m_trap_handlers.push_back(ConstString ("hndl_allintrs")); + m_trap_handlers.push_back(ConstString ("hndl_alltraps")); + m_trap_handlers.push_back(ConstString ("interrupt")); + m_trap_handlers.push_back(ConstString ("fleh_prefabt")); + m_trap_handlers.push_back(ConstString ("ExceptionVectorsBase")); + m_trap_handlers.push_back(ConstString ("ExceptionVectorsTable")); + m_trap_handlers.push_back(ConstString ("fleh_undef")); + m_trap_handlers.push_back(ConstString ("fleh_dataabt")); + m_trap_handlers.push_back(ConstString ("fleh_irq")); + m_trap_handlers.push_back(ConstString ("fleh_decirq")); + m_trap_handlers.push_back(ConstString ("fleh_fiq_generic")); + m_trap_handlers.push_back(ConstString ("fleh_dec")); + +} + +#else // __APPLE__ + +// Since DynamicLoaderDarwinKernel is compiled in for all systems, and relies on +// PlatformDarwinKernel for the plug-in name, we compile just the plug-in name in +// here to avoid issues. We are tracking an internal bug to resolve this issue by +// either not compiling in DynamicLoaderDarwinKernel for non-apple builds, or to make +// PlatformDarwinKernel build on all systems. PlatformDarwinKernel is currently not +// compiled on other platforms due to the use of the Mac-specific +// source/Host/macosx/cfcpp utilities. + +lldb_private::ConstString +PlatformDarwinKernel::GetPluginNameStatic () +{ + static lldb_private::ConstString g_name("darwin-kernel"); + return g_name; +} + +#endif // __APPLE__ diff --git a/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h b/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h new file mode 100644 index 000000000000..c1fe23178bf4 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h @@ -0,0 +1,228 @@ +//===-- PlatformDarwinKernel.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformDarwinKernel_h_ +#define liblldb_PlatformDarwinKernel_h_ + +#include "lldb/Core/ConstString.h" + +#if defined (__APPLE__) // This Plugin uses the Mac-specific source/Host/macosx/cfcpp utilities + + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Host/FileSpec.h" + +// Project includes +#include "PlatformDarwin.h" + +class PlatformDarwinKernel : public PlatformDarwin +{ +public: + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + DebuggerInitialize (lldb_private::Debugger &debugger); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------ + PlatformDarwinKernel (lldb_private::LazyBool is_ios_debug_session); + + virtual + ~PlatformDarwinKernel(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process *process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + bool + SupportsModules() override { return false; } + + void + CalculateTrapHandlerSymbolNames () override; + +protected: + + // Map from kext bundle ID ("com.apple.filesystems.exfat") to FileSpec for the kext bundle on + // the host ("/System/Library/Extensions/exfat.kext/Contents/Info.plist"). + typedef std::multimap<lldb_private::ConstString, lldb_private::FileSpec> BundleIDToKextMap; + typedef BundleIDToKextMap::iterator BundleIDToKextIterator; + + typedef std::vector<lldb_private::FileSpec> KernelBinaryCollection; + + // Array of directories that were searched for kext bundles (used only for reporting to user) + typedef std::vector<lldb_private::FileSpec> DirectoriesSearchedCollection; + typedef DirectoriesSearchedCollection::iterator DirectoriesSearchedIterator; + + + static lldb_private::FileSpec::EnumerateDirectoryResult + GetKextDirectoriesInSDK (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + static lldb_private::FileSpec::EnumerateDirectoryResult + GetKextsInDirectory (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + // Populate m_search_directories vector of directories + void + CollectKextAndKernelDirectories (); + + // Directories where we may find iOS SDKs with kext bundles in them + void + GetiOSSDKDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories); + + // Directories where we may find AppleTVOS SDKs with kext bundles in them + void + GetAppleTVOSSDKDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories); + + // Directories where we may find WatchOS SDKs with kext bundles in them + void + GetWatchOSSDKDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories); + + // Directories where we may find Mac OS X SDKs with kext bundles in them + void + GetMacSDKDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories); + + // Directories where we may find Mac OS X or iOS SDKs with kext bundles in them + void + GetGenericSDKDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories); + + // Directories where we may find iOS kext bundles + void + GetiOSDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories); + + // Directories where we may find MacOSX kext bundles + void + GetMacDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories); + + // Directories where we may find iOS or MacOSX kext bundles + void + GetGenericDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories); + + // Directories specified via the "kext-directories" setting - maybe KDK/SDKs, may be plain directories + void + GetUserSpecifiedDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories); + + void + GetCurrentDirectoryToSearch (std::vector<lldb_private::FileSpec> &directories); + + // Directories where we may find kernels exclusively + void + GetKernelDirectoriesToSearch (std::vector<lldb_private::FileSpec> &directories); + + // Search through a vector of SDK FileSpecs, add any directories that may contain kexts + // to the vector of kext dir FileSpecs + void + SearchSDKsForKextDirectories (std::vector<lldb_private::FileSpec> sdk_dirs, std::vector<lldb_private::FileSpec> &kext_dirs); + + // Search through all of the directories passed in, find all .kext bundles in those directories, + // get the CFBundleIDs out of the Info.plists and add the bundle ID and kext path to m_name_to_kext_path_map. + void + IndexKextsInDirectories (); + + // Search through all of the directories passed in, find all kernel binaries in those directories + // (look for "kernel*", "mach.*", assume those are kernels. False positives aren't a huge problem.) + void + IndexKernelsInDirectories (); + + // Callback which iterates over all the files in a given directory, looking for kernel binaries + static lldb_private::FileSpec::EnumerateDirectoryResult + GetKernelsInDirectory (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + lldb_private::Error + ExamineKextForMatchingUUID (const lldb_private::FileSpec &kext_bundle_path, const lldb_private::UUID &uuid, const lldb_private::ArchSpec &arch, lldb::ModuleSP &exe_module_sp); + +private: + + BundleIDToKextMap m_name_to_kext_path_map; // multimap of CFBundleID to FileSpec on local filesystem + DirectoriesSearchedCollection m_search_directories; // list of directories we search for kexts/kernels + KernelBinaryCollection m_kernel_binaries; // list of kernel binaries we found on local filesystem + lldb_private::LazyBool m_ios_debug_session; + + DISALLOW_COPY_AND_ASSIGN (PlatformDarwinKernel); + +}; + +#else // __APPLE__ + +// Since DynamicLoaderDarwinKernel is compiled in for all systems, and relies on +// PlatformDarwinKernel for the plug-in name, we compile just the plug-in name in +// here to avoid issues. We are tracking an internal bug to resolve this issue by +// either not compiling in DynamicLoaderDarwinKernel for non-apple builds, or to make +// PlatformDarwinKernel build on all systems. PlatformDarwinKernel is currently not +// compiled on other platforms due to the use of the Mac-specific +// source/Host/macosx/cfcpp utilities. + +class PlatformDarwinKernel +{ + static lldb_private::ConstString + GetPluginNameStatic (); +}; + +#endif // __APPLE__ + +#endif // liblldb_PlatformDarwinKernel_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp b/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp new file mode 100644 index 000000000000..7e15facc1b03 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp @@ -0,0 +1,386 @@ +//===-- PlatformMacOSX.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformMacOSX.h" +#include "lldb/Host/Config.h" + +// C++ Includes + +#include <sstream> + +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +static uint32_t g_initialize_count = 0; + +void +PlatformMacOSX::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { +#if defined (__APPLE__) + PlatformSP default_platform_sp (new PlatformMacOSX(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform (default_platform_sp); +#endif + PluginManager::RegisterPlugin (PlatformMacOSX::GetPluginNameStatic(false), + PlatformMacOSX::GetDescriptionStatic(false), + PlatformMacOSX::CreateInstance); + } + +} + +void +PlatformMacOSX::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformMacOSX::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformMacOSX::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = "<null>"; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; + + log->Printf ("PlatformMacOSX::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + // The only time we create an instance is when we are creating a remote + // macosx platform + const bool is_host = false; + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Darwin: // Deprecated, but still support Darwin for historical reasons + case llvm::Triple::MacOSX: + break; +#if defined(__APPLE__) + // Only accept "vendor" for vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + if (create) + { + if (log) + log->Printf ("PlatformMacOSX::%s() creating platform", __FUNCTION__); + return PlatformSP(new PlatformMacOSX (is_host)); + } + + if (log) + log->Printf ("PlatformMacOSX::%s() aborting creation of platform", __FUNCTION__); + + return PlatformSP(); +} + +lldb_private::ConstString +PlatformMacOSX::GetPluginNameStatic (bool is_host) +{ + if (is_host) + { + static ConstString g_host_name(Platform::GetHostPlatformName ()); + return g_host_name; + } + else + { + static ConstString g_remote_name("remote-macosx"); + return g_remote_name; + } +} + +const char * +PlatformMacOSX::GetDescriptionStatic (bool is_host) +{ + if (is_host) + return "Local Mac OS X user platform plug-in."; + else + return "Remote Mac OS X user platform plug-in."; +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformMacOSX::PlatformMacOSX (bool is_host) : + PlatformDarwin (is_host) +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformMacOSX::~PlatformMacOSX() +{ +} + +ConstString +PlatformMacOSX::GetSDKDirectory (lldb_private::Target &target) +{ + ModuleSP exe_module_sp (target.GetExecutableModule()); + if (exe_module_sp) + { + ObjectFile *objfile = exe_module_sp->GetObjectFile(); + if (objfile) + { + std::string xcode_contents_path; + std::string default_xcode_sdk; + FileSpec fspec; + uint32_t versions[2]; + if (objfile->GetSDKVersion(versions, sizeof(versions))) + { + if (HostInfo::GetLLDBPath(ePathTypeLLDBShlibDir, fspec)) + { + std::string path; + xcode_contents_path = fspec.GetPath(); + size_t pos = xcode_contents_path.find("/Xcode.app/Contents/"); + if (pos != std::string::npos) + { + // LLDB.framework is inside an Xcode app bundle, we can locate the SDK from here + xcode_contents_path.erase(pos + strlen("/Xcode.app/Contents/")); + } + else + { + xcode_contents_path.clear(); + // Use the selected Xcode + int status = 0; + int signo = 0; + std::string output; + const char *command = "xcrun -sdk macosx --show-sdk-path"; + lldb_private::Error error = RunShellCommand (command, // shell command to run + NULL, // current working directory + &status, // Put the exit status of the process in here + &signo, // Put the signal that caused the process to exit in here + &output, // Get the output from the command and place it in this string + 3); // Timeout in seconds to wait for shell program to finish + if (status == 0 && !output.empty()) + { + size_t first_non_newline = output.find_last_not_of("\r\n"); + if (first_non_newline != std::string::npos) + output.erase(first_non_newline+1); + default_xcode_sdk = output; + + pos = default_xcode_sdk.find("/Xcode.app/Contents/"); + if (pos != std::string::npos) + xcode_contents_path = default_xcode_sdk.substr(0, pos + strlen("/Xcode.app/Contents/")); + } + } + } + + if (!xcode_contents_path.empty()) + { + StreamString sdk_path; + sdk_path.Printf("%sDeveloper/Platforms/MacOSX.platform/Developer/SDKs/MacOSX%u.%u.sdk", xcode_contents_path.c_str(), versions[0], versions[1]); + fspec.SetFile(sdk_path.GetString().c_str(), false); + if (fspec.Exists()) + return ConstString(sdk_path.GetString().c_str()); + } + + if (!default_xcode_sdk.empty()) + { + fspec.SetFile(default_xcode_sdk.c_str(), false); + if (fspec.Exists()) + return ConstString(default_xcode_sdk.c_str()); + } + } + } + } + return ConstString(); +} + + +Error +PlatformMacOSX::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + if (IsRemote()) + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetFileWithUUID (platform_file, uuid_ptr, local_file); + } + + // Default to the local case + local_file = platform_file; + return Error(); +} + +lldb_private::Error +PlatformMacOSX::GetFileWithUUID (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file) +{ + if (IsRemote() && m_remote_platform_sp) + { + std::string local_os_build; +#if !defined(__linux__) + HostInfo::GetOSBuildString(local_os_build); +#endif + std::string remote_os_build; + m_remote_platform_sp->GetOSBuildString(remote_os_build); + if (local_os_build.compare(remote_os_build) == 0) + { + // same OS version: the local file is good enough + local_file = platform_file; + return Error(); + } + else + { + // try to find the file in the cache + std::string cache_path(GetLocalCacheDirectory()); + std::string module_path (platform_file.GetPath()); + cache_path.append(module_path); + FileSpec module_cache_spec(cache_path.c_str(),false); + if (module_cache_spec.Exists()) + { + local_file = module_cache_spec; + return Error(); + } + // bring in the remote module file + FileSpec module_cache_folder = module_cache_spec.CopyByRemovingLastPathComponent(); + // try to make the local directory first + Error err = + FileSystem::MakeDirectory(module_cache_folder, eFilePermissionsDirectoryDefault); + if (err.Fail()) + return err; + err = GetFile(platform_file, module_cache_spec); + if (err.Fail()) + return err; + if (module_cache_spec.Exists()) + { + local_file = module_cache_spec; + return Error(); + } + else + return Error("unable to obtain valid module file"); + } + } + local_file = platform_file; + return Error(); +} + +bool +PlatformMacOSX::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + return ARMGetSupportedArchitectureAtIndex (idx, arch); +#else + return x86GetSupportedArchitectureAtIndex (idx, arch); +#endif +} + +lldb_private::Error +PlatformMacOSX::GetSharedModule (const lldb_private::ModuleSpec &module_spec, + Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + Error error = GetSharedModuleWithLocalCache(module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); + + if (module_sp) + { + if (module_spec.GetArchitecture().GetCore() == ArchSpec::eCore_x86_64_x86_64h) + { + ObjectFile *objfile = module_sp->GetObjectFile(); + if (objfile == NULL) + { + // We didn't find an x86_64h slice, fall back to a x86_64 slice + ModuleSpec module_spec_x86_64 (module_spec); + module_spec_x86_64.GetArchitecture() = ArchSpec("x86_64-apple-macosx"); + lldb::ModuleSP x86_64_module_sp; + lldb::ModuleSP old_x86_64_module_sp; + bool did_create = false; + Error x86_64_error = GetSharedModuleWithLocalCache(module_spec_x86_64, x86_64_module_sp, module_search_paths_ptr, &old_x86_64_module_sp, &did_create); + if (x86_64_module_sp && x86_64_module_sp->GetObjectFile()) + { + module_sp = x86_64_module_sp; + if (old_module_sp_ptr) + *old_module_sp_ptr = old_x86_64_module_sp; + if (did_create_ptr) + *did_create_ptr = did_create; + return x86_64_error; + } + } + } + } + return error; +} + diff --git a/source/Plugins/Platform/MacOSX/PlatformMacOSX.h b/source/Plugins/Platform/MacOSX/PlatformMacOSX.h new file mode 100644 index 000000000000..10e177ea6362 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformMacOSX.h @@ -0,0 +1,104 @@ +//===-- PlatformMacOSX.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformMacOSX_h_ +#define liblldb_PlatformMacOSX_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "PlatformDarwin.h" + +class PlatformMacOSX : public PlatformDarwin +{ +public: + PlatformMacOSX(bool is_host); + + ~PlatformMacOSX() override; + + //------------------------------------------------------------ + // Class functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (bool is_host); + + static const char * + GetDescriptionStatic(bool is_host); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic (IsHost()); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic (IsHost()); + } + + lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetFile (const lldb_private::FileSpec& source, + const lldb_private::FileSpec& destination) override + { + return PlatformDarwin::GetFile (source,destination); + } + + lldb_private::Error + GetFileWithUUID (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file) override; + + bool GetSupportedArchitectureAtIndex(uint32_t idx, lldb_private::ArchSpec &arch) override; + + lldb_private::ConstString GetSDKDirectory(lldb_private::Target &target) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector<std::string> &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::MacOSX); + } + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformMacOSX); +}; + +#endif // liblldb_PlatformMacOSX_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformRemoteAppleTV.cpp b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleTV.cpp new file mode 100644 index 000000000000..04231f27ff9b --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleTV.cpp @@ -0,0 +1,912 @@ +//===-- PlatformRemoteAppleTV.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +#include <string> +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "PlatformRemoteAppleTV.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformRemoteAppleTV::PlatformRemoteAppleTV () : + PlatformDarwin (false), // This is a remote platform + m_sdk_directory_infos(), + m_device_support_directory(), + m_device_support_directory_for_os_version (), + m_build_update(), + m_last_module_sdk_idx (UINT32_MAX), + m_connected_module_sdk_idx (UINT32_MAX) +{ +} + +PlatformRemoteAppleTV::SDKDirectoryInfo::SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir) : + directory(sdk_dir), + build(), + version_major(0), + version_minor(0), + version_update(0), + user_cached(false) +{ + const char *dirname_cstr = sdk_dir.GetFilename().GetCString(); + const char *pos = Args::StringToVersion (dirname_cstr, + version_major, + version_minor, + version_update); + + if (pos && pos[0] == ' ' && pos[1] == '(') + { + const char *build_start = pos + 2; + const char *end_paren = strchr (build_start, ')'); + if (end_paren && build_start < end_paren) + build.SetCStringWithLength(build_start, end_paren - build_start); + } +} + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformRemoteAppleTV::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformRemoteAppleTV::GetPluginNameStatic(), + PlatformRemoteAppleTV::GetDescriptionStatic(), + PlatformRemoteAppleTV::CreateInstance); + } +} + +void +PlatformRemoteAppleTV::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformRemoteAppleTV::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformRemoteAppleTV::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = "<null>"; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; + + log->Printf ("PlatformRemoteAppleTV::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (!create && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::thumb: + { + const llvm::Triple &triple = arch->GetTriple(); + llvm::Triple::VendorType vendor = triple.getVendor(); + switch (vendor) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; + +#endif + default: + break; + } + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::TvOS: // This is the right triple value for Apple TV debugging + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + + if (create) + { + if (log) + log->Printf ("PlatformRemoteAppleTV::%s() creating platform", __FUNCTION__); + + return lldb::PlatformSP(new PlatformRemoteAppleTV ()); + } + + if (log) + log->Printf ("PlatformRemoteAppleTV::%s() aborting creation of platform", __FUNCTION__); + + return lldb::PlatformSP(); +} + +lldb_private::ConstString +PlatformRemoteAppleTV::GetPluginNameStatic () +{ + static ConstString g_name("remote-tvos"); + return g_name; +} + +const char * +PlatformRemoteAppleTV::GetDescriptionStatic() +{ + return "Remote Apple TV platform plug-in."; +} + +void +PlatformRemoteAppleTV::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetDeviceSupportDirectoryForOSVersion(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); + + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; i<num_sdk_infos; ++i) + { + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i]; + strm.Printf (" SDK Roots: [%2u] \"%s\"\n", + i, + sdk_dir_info.directory.GetPath().c_str()); + } +} + +Error +PlatformRemoteAppleTV::ResolveExecutable (const ModuleSpec &ms, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + ModuleSpec resolved_module_spec(ms); + + // Resolve any executable within a bundle on MacOSX + // TODO: verify that this handles shallow bundles, if not then implement one ourselves + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + { + if (resolved_module_spec.GetArchitecture().IsValid() || resolved_module_spec.GetUUID().IsValid()) + { + error = ModuleList::GetSharedModule(resolved_module_spec, + exe_module_sp, + nullptr, + nullptr, + nullptr); + + if (exe_module_sp && exe_module_sp->GetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule(resolved_module_spec, + exe_module_sp, + nullptr, + nullptr, + nullptr); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +FileSpec::EnumerateDirectoryResult +PlatformRemoteAppleTV::GetContainedFilesIntoVectorOfStringsCallback (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + ((PlatformRemoteAppleTV::SDKDirectoryInfoCollection *)baton)->push_back(PlatformRemoteAppleTV::SDKDirectoryInfo(file_spec)); + return FileSpec::eEnumerateDirectoryResultNext; +} + +bool +PlatformRemoteAppleTV::UpdateSDKDirectoryInfosIfNeeded() +{ + if (m_sdk_directory_infos.empty()) + { + const char *device_support_dir = GetDeviceSupportDirectory(); + if (device_support_dir) + { + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + + SDKDirectoryInfoCollection builtin_sdk_directory_infos; + FileSpec::EnumerateDirectory (m_device_support_directory.c_str(), + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &builtin_sdk_directory_infos); + + // Only add SDK directories that have symbols in them, some SDKs only contain + // developer disk images and no symbols, so they aren't useful to us. + FileSpec sdk_symbols_symlink_fspec; + for (const auto &sdk_directory_info : builtin_sdk_directory_infos) + { + sdk_symbols_symlink_fspec = sdk_directory_info.directory; + sdk_symbols_symlink_fspec.AppendPathComponent("Symbols.Internal"); + if (sdk_symbols_symlink_fspec.Exists()) + { + m_sdk_directory_infos.push_back(sdk_directory_info); + } + else + { + sdk_symbols_symlink_fspec.GetFilename().SetCString("Symbols"); + if (sdk_symbols_symlink_fspec.Exists()) + m_sdk_directory_infos.push_back(sdk_directory_info); + } + } + + const uint32_t num_installed = m_sdk_directory_infos.size(); + FileSpec local_sdk_cache("~/Library/Developer/Xcode/tvOS DeviceSupport", true); + if (!local_sdk_cache.Exists()) + { + // Try looking for another possible name + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/Apple TVOS DeviceSupport", true); + } + if (!local_sdk_cache.Exists()) + { + // Try looking for another possible name + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/AppleTVOS DeviceSupport", true); + } + if (!local_sdk_cache.Exists()) + { + // Try looking for another possible name + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/AppleTV OS DeviceSupport", true); + } + if (!local_sdk_cache.Exists()) + { + // Try looking for another possible name + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/Apple TV OS DeviceSupport", true); + } + if (local_sdk_cache.Exists()) + { + char path[PATH_MAX]; + if (local_sdk_cache.GetPath(path, sizeof(path))) + { + FileSpec::EnumerateDirectory (path, + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &m_sdk_directory_infos); + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + // First try for an exact match of major, minor and update + for (uint32_t i=num_installed; i<num_sdk_infos; ++i) + { + m_sdk_directory_infos[i].user_cached = true; + } + } + } + } + } + return !m_sdk_directory_infos.empty(); +} + +const PlatformRemoteAppleTV::SDKDirectoryInfo * +PlatformRemoteAppleTV::GetSDKDirectoryForCurrentOSVersion () +{ + uint32_t i; + if (UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + + // Check to see if the user specified a build string. If they did, then + // be sure to match it. + std::vector<bool> check_sdk_info(num_sdk_infos, true); + ConstString build(m_sdk_build); + if (build) + { + for (i=0; i<num_sdk_infos; ++i) + check_sdk_info[i] = m_sdk_directory_infos[i].build == build; + } + + // If we are connected we can find the version of the OS the platform + // us running on and select the right SDK + uint32_t major, minor, update; + if (GetOSVersion(major, minor, update)) + { + if (UpdateSDKDirectoryInfosIfNeeded()) + { + // First try for an exact match of major, minor and update + for (i=0; i<num_sdk_infos; ++i) + { + if (check_sdk_info[i]) + { + if (m_sdk_directory_infos[i].version_major == major && + m_sdk_directory_infos[i].version_minor == minor && + m_sdk_directory_infos[i].version_update == update) + { + return &m_sdk_directory_infos[i]; + } + } + } + // First try for an exact match of major and minor + for (i=0; i<num_sdk_infos; ++i) + { + if (check_sdk_info[i]) + { + if (m_sdk_directory_infos[i].version_major == major && + m_sdk_directory_infos[i].version_minor == minor) + { + return &m_sdk_directory_infos[i]; + } + } + } + // Lastly try to match of major version only.. + for (i=0; i<num_sdk_infos; ++i) + { + if (check_sdk_info[i]) + { + if (m_sdk_directory_infos[i].version_major == major) + { + return &m_sdk_directory_infos[i]; + } + } + } + } + } + else if (build) + { + // No version, just a build number, search for the first one that matches + for (i=0; i<num_sdk_infos; ++i) + if (check_sdk_info[i]) + return &m_sdk_directory_infos[i]; + } + } + return nullptr; +} + +const PlatformRemoteAppleTV::SDKDirectoryInfo * +PlatformRemoteAppleTV::GetSDKDirectoryForLatestOSVersion () +{ + const PlatformRemoteAppleTV::SDKDirectoryInfo *result = nullptr; + if (UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + // First try for an exact match of major, minor and update + for (uint32_t i=0; i<num_sdk_infos; ++i) + { + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i]; + if (sdk_dir_info.version_major != UINT32_MAX) + { + if (result == nullptr || sdk_dir_info.version_major > result->version_major) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_major == result->version_major) + { + if (sdk_dir_info.version_minor > result->version_minor) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_minor == result->version_minor) + { + if (sdk_dir_info.version_update > result->version_update) + { + result = &sdk_dir_info; + } + } + } + } + } + } + return result; +} + +const char * +PlatformRemoteAppleTV::GetDeviceSupportDirectory() +{ + if (m_device_support_directory.empty()) + { + const char *device_support_dir = GetDeveloperDirectory(); + if (device_support_dir) + { + m_device_support_directory.assign (device_support_dir); + m_device_support_directory.append ("/Platforms/AppleTVOS.platform/DeviceSupport"); + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory + // or it should have a valid path if the code gets here + assert (m_device_support_directory.empty() == false); + if (m_device_support_directory[0]) + return m_device_support_directory.c_str(); + return nullptr; +} + +const char * +PlatformRemoteAppleTV::GetDeviceSupportDirectoryForOSVersion() +{ + if (m_sdk_sysroot) + return m_sdk_sysroot.GetCString(); + + if (m_device_support_directory_for_os_version.empty()) + { + const PlatformRemoteAppleTV::SDKDirectoryInfo *sdk_dir_info = GetSDKDirectoryForCurrentOSVersion (); + if (sdk_dir_info == nullptr) + sdk_dir_info = GetSDKDirectoryForLatestOSVersion (); + if (sdk_dir_info) + { + char path[PATH_MAX]; + if (sdk_dir_info->directory.GetPath(path, sizeof(path))) + { + m_device_support_directory_for_os_version = path; + return m_device_support_directory_for_os_version.c_str(); + } + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory_for_os_version.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory_for_os_version + // or it should have a valid path if the code gets here + assert (m_device_support_directory_for_os_version.empty() == false); + if (m_device_support_directory_for_os_version[0]) + return m_device_support_directory_for_os_version.c_str(); + return nullptr; +} + +uint32_t +PlatformRemoteAppleTV::FindFileInAllSDKs (const char *platform_file_path, + FileSpecList &file_list) +{ + if (platform_file_path && platform_file_path[0] && UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + lldb_private::FileSpec local_file; + // First try for an exact match of major, minor and update + for (uint32_t sdk_idx=0; sdk_idx<num_sdk_infos; ++sdk_idx) + { + if (GetFileInSDK (platform_file_path, + sdk_idx, + local_file)) + { + file_list.Append(local_file); + } + } + } + return file_list.GetSize(); +} + +bool +PlatformRemoteAppleTV::GetFileInSDK (const char *platform_file_path, + uint32_t sdk_idx, + lldb_private::FileSpec &local_file) +{ + if (sdk_idx < m_sdk_directory_infos.size()) + { + char sdkroot_path[PATH_MAX]; + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[sdk_idx]; + if (sdk_dir_info.directory.GetPath(sdkroot_path, sizeof(sdkroot_path))) + { + const bool symbols_dirs_only = true; + + return GetFileInSDKRoot (platform_file_path, + sdkroot_path, + symbols_dirs_only, + local_file); + } + } + return false; +} + +bool +PlatformRemoteAppleTV::GetFileInSDKRoot (const char *platform_file_path, + const char *sdkroot_path, + bool symbols_dirs_only, + lldb_private::FileSpec &local_file) +{ + if (sdkroot_path && sdkroot_path[0] && platform_file_path && platform_file_path[0]) + { + char resolved_path[PATH_MAX]; + + if (!symbols_dirs_only) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return true; + } + + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols.Internal%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return true; + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return true; + } + return false; +} + +Error +PlatformRemoteAppleTV::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + Error error; + char platform_file_path[PATH_MAX]; + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + char resolved_path[PATH_MAX]; + + const char * os_version_dir = GetDeviceSupportDirectoryForOSVersion(); + if (os_version_dir) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols.Internal/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + + } + local_file = platform_file; + if (local_file.Exists()) + return error; + + error.SetErrorStringWithFormat ("unable to locate a platform file for '%s' in platform '%s'", + platform_file_path, + GetPluginName().GetCString()); + } + else + { + error.SetErrorString ("invalid platform file argument"); + } + return error; +} + +Error +PlatformRemoteAppleTV::GetSharedModule (const ModuleSpec &module_spec, + lldb_private::Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + // For Apple TV, the SDK files are all cached locally on the host + // system. So first we ask for the file in the cached SDK, + // then we attempt to get a shared module for the right architecture + // with the right UUID. + const FileSpec &platform_file = module_spec.GetFileSpec(); + + Error error; + char platform_file_path[PATH_MAX]; + + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + ModuleSpec platform_module_spec(module_spec); + + UpdateSDKDirectoryInfosIfNeeded(); + + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + + // If we are connected we migth be able to correctly deduce the SDK directory + // using the OS build. + const uint32_t connected_sdk_idx = GetConnectedSDKIndex (); + if (connected_sdk_idx < num_sdk_infos) + { + if (GetFileInSDK (platform_file_path, connected_sdk_idx, platform_module_spec.GetFileSpec())) + { + module_sp.reset(); + error = ResolveExecutable(platform_module_spec, + module_sp, + nullptr); + if (module_sp) + { + m_last_module_sdk_idx = connected_sdk_idx; + error.Clear(); + return error; + } + } + } + + // Try the last SDK index if it is set as most files from an SDK + // will tend to be valid in that same SDK. + if (m_last_module_sdk_idx < num_sdk_infos) + { + if (GetFileInSDK (platform_file_path, m_last_module_sdk_idx, platform_module_spec.GetFileSpec())) + { + module_sp.reset(); + error = ResolveExecutable(platform_module_spec, + module_sp, + nullptr); + if (module_sp) + { + error.Clear(); + return error; + } + } + } + + // First try for an exact match of major, minor and update + for (uint32_t sdk_idx=0; sdk_idx<num_sdk_infos; ++sdk_idx) + { + if (m_last_module_sdk_idx == sdk_idx) + { + // Skip the last module SDK index if we already searched + // it above + continue; + } + if (GetFileInSDK (platform_file_path, sdk_idx, platform_module_spec.GetFileSpec())) + { + //printf ("sdk[%u]: '%s'\n", sdk_idx, local_file.GetPath().c_str()); + + error = ResolveExecutable(platform_module_spec, module_sp, nullptr); + if (module_sp) + { + // Remember the index of the last SDK that we found a file + // in in case the wrong SDK was selected. + m_last_module_sdk_idx = sdk_idx; + error.Clear(); + return error; + } + } + } + } + // Not the module we are looking for... Nothing to see here... + module_sp.reset(); + + // This may not be an SDK-related module. Try whether we can bring in the thing to our local cache. + error = GetSharedModuleWithLocalCache(module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); + if (error.Success()) + return error; + + const bool always_create = false; + error = ModuleList::GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr, + always_create); + + if (module_sp) + module_sp->SetPlatformFileSpec(platform_file); + + return error; +} + +bool +PlatformRemoteAppleTV::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + ArchSpec system_arch (GetSystemArchitecture()); + + const ArchSpec::Core system_core = system_arch.GetCore(); + switch (system_core) + { + default: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-tvos"); return true; + case 1: arch.SetTriple ("armv7s-apple-tvos"); return true; + case 2: arch.SetTriple ("armv7-apple-tvos"); return true; + case 3: arch.SetTriple ("thumbv7s-apple-tvos"); return true; + case 4: arch.SetTriple ("thumbv7-apple-tvos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_arm64: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-tvos"); return true; + case 1: arch.SetTriple ("armv7s-apple-tvos"); return true; + case 2: arch.SetTriple ("armv7-apple-tvos"); return true; + case 3: arch.SetTriple ("thumbv7s-apple-tvos"); return true; + case 4: arch.SetTriple ("thumbv7-apple-tvos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7s: + switch (idx) + { + case 0: arch.SetTriple ("armv7s-apple-tvos"); return true; + case 1: arch.SetTriple ("armv7-apple-tvos"); return true; + case 2: arch.SetTriple ("thumbv7s-apple-tvos"); return true; + case 3: arch.SetTriple ("thumbv7-apple-tvos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7: + switch (idx) + { + case 0: arch.SetTriple ("armv7-apple-tvos"); return true; + case 1: arch.SetTriple ("thumbv7-apple-tvos"); return true; + default: break; + } + break; + } + arch.Clear(); + return false; +} + +uint32_t +PlatformRemoteAppleTV::GetConnectedSDKIndex () +{ + if (IsConnected()) + { + if (m_connected_module_sdk_idx == UINT32_MAX) + { + std::string build; + if (GetRemoteOSBuildString(build)) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; i<num_sdk_infos; ++i) + { + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i]; + if (strstr(sdk_dir_info.directory.GetFilename().AsCString(""), build.c_str())) + { + m_connected_module_sdk_idx = i; + } + } + } + } + } + else + { + m_connected_module_sdk_idx = UINT32_MAX; + } + return m_connected_module_sdk_idx; +} diff --git a/source/Plugins/Platform/MacOSX/PlatformRemoteAppleTV.h b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleTV.h new file mode 100644 index 000000000000..28bd9df0fad7 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleTV.h @@ -0,0 +1,171 @@ +//===-- PlatformRemoteAppleTV.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformRemoteAppleTV_h_ +#define liblldb_PlatformRemoteAppleTV_h_ + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" + +#include "PlatformDarwin.h" + +class PlatformRemoteAppleTV : public PlatformDarwin +{ +public: + PlatformRemoteAppleTV(); + + ~PlatformRemoteAppleTV() override = default; + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------ + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + virtual lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector<std::string> &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneOS); + } + +protected: + struct SDKDirectoryInfo + { + SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir_spec); + lldb_private::FileSpec directory; + lldb_private::ConstString build; + uint32_t version_major; + uint32_t version_minor; + uint32_t version_update; + bool user_cached; + }; + typedef std::vector<SDKDirectoryInfo> SDKDirectoryInfoCollection; + SDKDirectoryInfoCollection m_sdk_directory_infos; + std::string m_device_support_directory; + std::string m_device_support_directory_for_os_version; + std::string m_build_update; + uint32_t m_last_module_sdk_idx; + uint32_t m_connected_module_sdk_idx; + + bool + UpdateSDKDirectoryInfosIfNeeded(); + + const char * + GetDeviceSupportDirectory(); + + const char * + GetDeviceSupportDirectoryForOSVersion(); + + const SDKDirectoryInfo * + GetSDKDirectoryForLatestOSVersion (); + + const SDKDirectoryInfo * + GetSDKDirectoryForCurrentOSVersion (); + + static lldb_private::FileSpec::EnumerateDirectoryResult + GetContainedFilesIntoVectorOfStringsCallback (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + uint32_t + FindFileInAllSDKs (const char *platform_file_path, + lldb_private::FileSpecList &file_list); + + bool + GetFileInSDK (const char *platform_file_path, + uint32_t sdk_idx, + lldb_private::FileSpec &local_file); + + bool + GetFileInSDKRoot (const char *platform_file_path, + const char *sdkroot_path, + bool symbols_dirs_only, + lldb_private::FileSpec &local_file); + + uint32_t + FindFileInAllSDKs (const lldb_private::FileSpec &platform_file, + lldb_private::FileSpecList &file_list); + + uint32_t + GetConnectedSDKIndex (); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformRemoteAppleTV); +}; + +#endif // liblldb_PlatformRemoteAppleTV_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.cpp b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.cpp new file mode 100644 index 000000000000..808fd96a5284 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.cpp @@ -0,0 +1,944 @@ +//===-- PlatformRemoteAppleWatch.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +#include <string> +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "PlatformRemoteAppleWatch.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformRemoteAppleWatch::PlatformRemoteAppleWatch () : + PlatformDarwin (false), // This is a remote platform + m_sdk_directory_infos(), + m_device_support_directory(), + m_device_support_directory_for_os_version (), + m_build_update(), + m_last_module_sdk_idx (UINT32_MAX), + m_connected_module_sdk_idx (UINT32_MAX) +{ +} + +PlatformRemoteAppleWatch::SDKDirectoryInfo::SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir) : + directory(sdk_dir), + build(), + version_major(0), + version_minor(0), + version_update(0), + user_cached(false) +{ + const char *dirname_cstr = sdk_dir.GetFilename().GetCString(); + const char *pos = Args::StringToVersion (dirname_cstr, + version_major, + version_minor, + version_update); + + if (pos && pos[0] == ' ' && pos[1] == '(') + { + const char *build_start = pos + 2; + const char *end_paren = strchr (build_start, ')'); + if (end_paren && build_start < end_paren) + build.SetCStringWithLength(build_start, end_paren - build_start); + } +} + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformRemoteAppleWatch::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformRemoteAppleWatch::GetPluginNameStatic(), + PlatformRemoteAppleWatch::GetDescriptionStatic(), + PlatformRemoteAppleWatch::CreateInstance); + } +} + +void +PlatformRemoteAppleWatch::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformRemoteAppleWatch::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformRemoteAppleWatch::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = "<null>"; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; + + log->Printf ("PlatformRemoteAppleWatch::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (!create && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::thumb: + { + const llvm::Triple &triple = arch->GetTriple(); + llvm::Triple::VendorType vendor = triple.getVendor(); + switch (vendor) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; + +#endif + default: + break; + } + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::WatchOS: // This is the right triple value for Apple Watch debugging + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + +#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__) || defined(__aarch64__)) + // If lldb is running on a watch, this isn't a RemoteWatch environment; it's a local system environment. + if (force == false) + { + create = false; + } +#endif + + if (create) + { + if (log) + log->Printf ("PlatformRemoteAppleWatch::%s() creating platform", __FUNCTION__); + + return lldb::PlatformSP(new PlatformRemoteAppleWatch ()); + } + + if (log) + log->Printf ("PlatformRemoteAppleWatch::%s() aborting creation of platform", __FUNCTION__); + + return lldb::PlatformSP(); +} + +lldb_private::ConstString +PlatformRemoteAppleWatch::GetPluginNameStatic () +{ + static ConstString g_name("remote-watchos"); + return g_name; +} + +const char * +PlatformRemoteAppleWatch::GetDescriptionStatic() +{ + return "Remote Apple Watch platform plug-in."; +} + +void +PlatformRemoteAppleWatch::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetDeviceSupportDirectoryForOSVersion(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); + + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; i<num_sdk_infos; ++i) + { + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i]; + strm.Printf (" SDK Roots: [%2u] \"%s\"\n", + i, + sdk_dir_info.directory.GetPath().c_str()); + } +} + +Error +PlatformRemoteAppleWatch::ResolveExecutable (const ModuleSpec &ms, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + ModuleSpec resolved_module_spec(ms); + + // Resolve any executable within a bundle on MacOSX + // TODO: verify that this handles shallow bundles, if not then implement one ourselves + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + { + if (resolved_module_spec.GetArchitecture().IsValid() || resolved_module_spec.GetUUID().IsValid()) + { + error = ModuleList::GetSharedModule(resolved_module_spec, + exe_module_sp, + nullptr, + nullptr, + nullptr); + + if (exe_module_sp && exe_module_sp->GetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule(resolved_module_spec, + exe_module_sp, + nullptr, + nullptr, + nullptr); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +FileSpec::EnumerateDirectoryResult +PlatformRemoteAppleWatch::GetContainedFilesIntoVectorOfStringsCallback (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + ((PlatformRemoteAppleWatch::SDKDirectoryInfoCollection *)baton)->push_back(PlatformRemoteAppleWatch::SDKDirectoryInfo(file_spec)); + return FileSpec::eEnumerateDirectoryResultNext; +} + +bool +PlatformRemoteAppleWatch::UpdateSDKDirectoryInfosIfNeeded() +{ + if (m_sdk_directory_infos.empty()) + { + const char *device_support_dir = GetDeviceSupportDirectory(); + if (device_support_dir) + { + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + + SDKDirectoryInfoCollection builtin_sdk_directory_infos; + FileSpec::EnumerateDirectory (m_device_support_directory.c_str(), + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &builtin_sdk_directory_infos); + + // Only add SDK directories that have symbols in them, some SDKs only contain + // developer disk images and no symbols, so they aren't useful to us. + FileSpec sdk_symbols_symlink_fspec; + for (const auto &sdk_directory_info : builtin_sdk_directory_infos) + { + sdk_symbols_symlink_fspec = sdk_directory_info.directory; + sdk_symbols_symlink_fspec.AppendPathComponent("Symbols.Internal"); + if (sdk_symbols_symlink_fspec.Exists()) + { + m_sdk_directory_infos.push_back(sdk_directory_info); + } + else + { + sdk_symbols_symlink_fspec.GetFilename().SetCString("Symbols"); + if (sdk_symbols_symlink_fspec.Exists()) + m_sdk_directory_infos.push_back(sdk_directory_info); + } + } + + const uint32_t num_installed = m_sdk_directory_infos.size(); + FileSpec local_sdk_cache("~/Library/Developer/Xcode/watchOS DeviceSupport", true); + if (!local_sdk_cache.Exists()) + { + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/watch OS DeviceSupport", true); + } + if (!local_sdk_cache.Exists()) + { + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/WatchOS DeviceSupport", true); + } + if (!local_sdk_cache.Exists()) + { + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/Watch OS DeviceSupport", true); + } + if (local_sdk_cache.Exists()) + { + char path[PATH_MAX]; + if (local_sdk_cache.GetPath(path, sizeof(path))) + { + FileSpec::EnumerateDirectory (path, + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &m_sdk_directory_infos); + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + // First try for an exact match of major, minor and update + for (uint32_t i=num_installed; i<num_sdk_infos; ++i) + { + m_sdk_directory_infos[i].user_cached = true; + } + } + } + } + } + return !m_sdk_directory_infos.empty(); +} + +const PlatformRemoteAppleWatch::SDKDirectoryInfo * +PlatformRemoteAppleWatch::GetSDKDirectoryForCurrentOSVersion () +{ + uint32_t i; + if (UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + + // Check to see if the user specified a build string. If they did, then + // be sure to match it. + std::vector<bool> check_sdk_info(num_sdk_infos, true); + ConstString build(m_sdk_build); + if (build) + { + for (i=0; i<num_sdk_infos; ++i) + check_sdk_info[i] = m_sdk_directory_infos[i].build == build; + } + + // If we are connected we can find the version of the OS the platform + // us running on and select the right SDK + uint32_t major, minor, update; + if (GetOSVersion(major, minor, update)) + { + if (UpdateSDKDirectoryInfosIfNeeded()) + { + // First try for an exact match of major, minor and update + for (i=0; i<num_sdk_infos; ++i) + { + if (check_sdk_info[i]) + { + if (m_sdk_directory_infos[i].version_major == major && + m_sdk_directory_infos[i].version_minor == minor && + m_sdk_directory_infos[i].version_update == update) + { + return &m_sdk_directory_infos[i]; + } + } + } + // First try for an exact match of major and minor + for (i=0; i<num_sdk_infos; ++i) + { + if (check_sdk_info[i]) + { + if (m_sdk_directory_infos[i].version_major == major && + m_sdk_directory_infos[i].version_minor == minor) + { + return &m_sdk_directory_infos[i]; + } + } + } + // Lastly try to match of major version only.. + for (i=0; i<num_sdk_infos; ++i) + { + if (check_sdk_info[i]) + { + if (m_sdk_directory_infos[i].version_major == major) + { + return &m_sdk_directory_infos[i]; + } + } + } + } + } + else if (build) + { + // No version, just a build number, search for the first one that matches + for (i=0; i<num_sdk_infos; ++i) + if (check_sdk_info[i]) + return &m_sdk_directory_infos[i]; + } + } + return nullptr; +} + +const PlatformRemoteAppleWatch::SDKDirectoryInfo * +PlatformRemoteAppleWatch::GetSDKDirectoryForLatestOSVersion () +{ + const PlatformRemoteAppleWatch::SDKDirectoryInfo *result = nullptr; + if (UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + // First try for an exact match of major, minor and update + for (uint32_t i=0; i<num_sdk_infos; ++i) + { + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i]; + if (sdk_dir_info.version_major != UINT32_MAX) + { + if (result == nullptr || sdk_dir_info.version_major > result->version_major) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_major == result->version_major) + { + if (sdk_dir_info.version_minor > result->version_minor) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_minor == result->version_minor) + { + if (sdk_dir_info.version_update > result->version_update) + { + result = &sdk_dir_info; + } + } + } + } + } + } + return result; +} + +const char * +PlatformRemoteAppleWatch::GetDeviceSupportDirectory() +{ + if (m_device_support_directory.empty()) + { + const char *device_support_dir = GetDeveloperDirectory(); + if (device_support_dir) + { + m_device_support_directory.assign (device_support_dir); + m_device_support_directory.append ("/Platforms/watchOS.platform/DeviceSupport"); + FileSpec platform_device_support_dir (m_device_support_directory.c_str(), true); + if (!platform_device_support_dir.Exists()) + { + std::string alt_platform_dirname = device_support_dir; + alt_platform_dirname.append ("/Platforms/WatchOS.platform/DeviceSupport"); + FileSpec alt_platform_device_support_dir (m_device_support_directory.c_str(), true); + if (alt_platform_device_support_dir.Exists()) + { + m_device_support_directory = alt_platform_dirname; + } + } + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory + // or it should have a valid path if the code gets here + assert (m_device_support_directory.empty() == false); + if (m_device_support_directory[0]) + return m_device_support_directory.c_str(); + return nullptr; +} + +const char * +PlatformRemoteAppleWatch::GetDeviceSupportDirectoryForOSVersion() +{ + if (m_sdk_sysroot) + return m_sdk_sysroot.GetCString(); + + if (m_device_support_directory_for_os_version.empty()) + { + const PlatformRemoteAppleWatch::SDKDirectoryInfo *sdk_dir_info = GetSDKDirectoryForCurrentOSVersion (); + if (sdk_dir_info == nullptr) + sdk_dir_info = GetSDKDirectoryForLatestOSVersion (); + if (sdk_dir_info) + { + char path[PATH_MAX]; + if (sdk_dir_info->directory.GetPath(path, sizeof(path))) + { + m_device_support_directory_for_os_version = path; + return m_device_support_directory_for_os_version.c_str(); + } + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory_for_os_version.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory_for_os_version + // or it should have a valid path if the code gets here + assert (m_device_support_directory_for_os_version.empty() == false); + if (m_device_support_directory_for_os_version[0]) + return m_device_support_directory_for_os_version.c_str(); + return nullptr; +} + +uint32_t +PlatformRemoteAppleWatch::FindFileInAllSDKs (const char *platform_file_path, + FileSpecList &file_list) +{ + if (platform_file_path && platform_file_path[0] && UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + lldb_private::FileSpec local_file; + // First try for an exact match of major, minor and update + for (uint32_t sdk_idx=0; sdk_idx<num_sdk_infos; ++sdk_idx) + { + if (GetFileInSDK (platform_file_path, + sdk_idx, + local_file)) + { + file_list.Append(local_file); + } + } + } + return file_list.GetSize(); +} + +bool +PlatformRemoteAppleWatch::GetFileInSDK (const char *platform_file_path, + uint32_t sdk_idx, + lldb_private::FileSpec &local_file) +{ + if (sdk_idx < m_sdk_directory_infos.size()) + { + char sdkroot_path[PATH_MAX]; + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[sdk_idx]; + if (sdk_dir_info.directory.GetPath(sdkroot_path, sizeof(sdkroot_path))) + { + const bool symbols_dirs_only = true; + + return GetFileInSDKRoot (platform_file_path, + sdkroot_path, + symbols_dirs_only, + local_file); + } + } + return false; +} + +bool +PlatformRemoteAppleWatch::GetFileInSDKRoot (const char *platform_file_path, + const char *sdkroot_path, + bool symbols_dirs_only, + lldb_private::FileSpec &local_file) +{ + if (sdkroot_path && sdkroot_path[0] && platform_file_path && platform_file_path[0]) + { + char resolved_path[PATH_MAX]; + + if (!symbols_dirs_only) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return true; + } + + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols.Internal%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return true; + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return true; + } + return false; +} + +Error +PlatformRemoteAppleWatch::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + Error error; + char platform_file_path[PATH_MAX]; + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + char resolved_path[PATH_MAX]; + + const char * os_version_dir = GetDeviceSupportDirectoryForOSVersion(); + if (os_version_dir) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols.Internal/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + + } + local_file = platform_file; + if (local_file.Exists()) + return error; + + error.SetErrorStringWithFormat ("unable to locate a platform file for '%s' in platform '%s'", + platform_file_path, + GetPluginName().GetCString()); + } + else + { + error.SetErrorString ("invalid platform file argument"); + } + return error; +} + +Error +PlatformRemoteAppleWatch::GetSharedModule (const ModuleSpec &module_spec, + lldb_private::Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + // For Apple Watch, the SDK files are all cached locally on the host + // system. So first we ask for the file in the cached SDK, + // then we attempt to get a shared module for the right architecture + // with the right UUID. + const FileSpec &platform_file = module_spec.GetFileSpec(); + + Error error; + char platform_file_path[PATH_MAX]; + + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + ModuleSpec platform_module_spec(module_spec); + + UpdateSDKDirectoryInfosIfNeeded(); + + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + + // If we are connected we migth be able to correctly deduce the SDK directory + // using the OS build. + const uint32_t connected_sdk_idx = GetConnectedSDKIndex (); + if (connected_sdk_idx < num_sdk_infos) + { + if (GetFileInSDK (platform_file_path, connected_sdk_idx, platform_module_spec.GetFileSpec())) + { + module_sp.reset(); + error = ResolveExecutable(platform_module_spec, + module_sp, + nullptr); + if (module_sp) + { + m_last_module_sdk_idx = connected_sdk_idx; + error.Clear(); + return error; + } + } + } + + // Try the last SDK index if it is set as most files from an SDK + // will tend to be valid in that same SDK. + if (m_last_module_sdk_idx < num_sdk_infos) + { + if (GetFileInSDK (platform_file_path, m_last_module_sdk_idx, platform_module_spec.GetFileSpec())) + { + module_sp.reset(); + error = ResolveExecutable(platform_module_spec, + module_sp, + nullptr); + if (module_sp) + { + error.Clear(); + return error; + } + } + } + + // First try for an exact match of major, minor and update + for (uint32_t sdk_idx=0; sdk_idx<num_sdk_infos; ++sdk_idx) + { + if (m_last_module_sdk_idx == sdk_idx) + { + // Skip the last module SDK index if we already searched + // it above + continue; + } + if (GetFileInSDK (platform_file_path, sdk_idx, platform_module_spec.GetFileSpec())) + { + //printf ("sdk[%u]: '%s'\n", sdk_idx, local_file.GetPath().c_str()); + + error = ResolveExecutable(platform_module_spec, module_sp, nullptr); + if (module_sp) + { + // Remember the index of the last SDK that we found a file + // in in case the wrong SDK was selected. + m_last_module_sdk_idx = sdk_idx; + error.Clear(); + return error; + } + } + } + } + // Not the module we are looking for... Nothing to see here... + module_sp.reset(); + + // This may not be an SDK-related module. Try whether we can bring in the thing to our local cache. + error = GetSharedModuleWithLocalCache(module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); + if (error.Success()) + return error; + + const bool always_create = false; + error = ModuleList::GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr, + always_create); + + if (module_sp) + module_sp->SetPlatformFileSpec(platform_file); + + return error; +} + +bool +PlatformRemoteAppleWatch::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + ArchSpec system_arch (GetSystemArchitecture()); + + const ArchSpec::Core system_core = system_arch.GetCore(); + switch (system_core) + { + default: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-watchos"); return true; + case 1: arch.SetTriple ("armv7k-apple-watchos"); return true; + case 2: arch.SetTriple ("armv7s-apple-watchos"); return true; + case 3: arch.SetTriple ("armv7-apple-watchos"); return true; + case 4: arch.SetTriple ("thumbv7k-apple-watchos"); return true; + case 5: arch.SetTriple ("thumbv7-apple-watchos"); return true; + case 6: arch.SetTriple ("thumbv7s-apple-watchos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_arm64: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-watchos"); return true; + case 1: arch.SetTriple ("armv7k-apple-watchos"); return true; + case 2: arch.SetTriple ("armv7s-apple-watchos"); return true; + case 3: arch.SetTriple ("armv7-apple-watchos"); return true; + case 4: arch.SetTriple ("thumbv7k-apple-watchos"); return true; + case 5: arch.SetTriple ("thumbv7-apple-watchos"); return true; + case 6: arch.SetTriple ("thumbv7s-apple-watchos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7k: + switch (idx) + { + case 0: arch.SetTriple ("armv7k-apple-watchos"); return true; + case 1: arch.SetTriple ("armv7s-apple-watchos"); return true; + case 2: arch.SetTriple ("armv7-apple-watchos"); return true; + case 3: arch.SetTriple ("thumbv7k-apple-watchos"); return true; + case 4: arch.SetTriple ("thumbv7-apple-watchos"); return true; + case 5: arch.SetTriple ("thumbv7s-apple-watchos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7s: + switch (idx) + { + case 0: arch.SetTriple ("armv7s-apple-watchos"); return true; + case 1: arch.SetTriple ("armv7k-apple-watchos"); return true; + case 2: arch.SetTriple ("armv7-apple-watchos"); return true; + case 3: arch.SetTriple ("thumbv7k-apple-watchos"); return true; + case 4: arch.SetTriple ("thumbv7-apple-watchos"); return true; + case 5: arch.SetTriple ("thumbv7s-apple-watchos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7: + switch (idx) + { + case 0: arch.SetTriple ("armv7-apple-watchos"); return true; + case 1: arch.SetTriple ("armv7k-apple-watchos"); return true; + case 2: arch.SetTriple ("thumbv7k-apple-watchos"); return true; + case 3: arch.SetTriple ("thumbv7-apple-watchos"); return true; + default: break; + } + break; + } + arch.Clear(); + return false; +} + +uint32_t +PlatformRemoteAppleWatch::GetConnectedSDKIndex () +{ + if (IsConnected()) + { + if (m_connected_module_sdk_idx == UINT32_MAX) + { + std::string build; + if (GetRemoteOSBuildString(build)) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; i<num_sdk_infos; ++i) + { + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i]; + if (strstr(sdk_dir_info.directory.GetFilename().AsCString(""), build.c_str())) + { + m_connected_module_sdk_idx = i; + } + } + } + } + } + else + { + m_connected_module_sdk_idx = UINT32_MAX; + } + return m_connected_module_sdk_idx; +} diff --git a/source/Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.h b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.h new file mode 100644 index 000000000000..891bc5d1c6ef --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.h @@ -0,0 +1,173 @@ +//===-- PlatformRemoteAppleWatch.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformRemoteAppleWatch_h_ +#define liblldb_PlatformRemoteAppleWatch_h_ + +// C Includes +// C++ Includes +#include <string> +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" + +#include "PlatformDarwin.h" + +class PlatformRemoteAppleWatch : public PlatformDarwin +{ +public: + PlatformRemoteAppleWatch(); + + ~PlatformRemoteAppleWatch() override = default; + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------ + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + virtual lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector<std::string> &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneOS); + } + +protected: + struct SDKDirectoryInfo + { + SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir_spec); + lldb_private::FileSpec directory; + lldb_private::ConstString build; + uint32_t version_major; + uint32_t version_minor; + uint32_t version_update; + bool user_cached; + }; + typedef std::vector<SDKDirectoryInfo> SDKDirectoryInfoCollection; + SDKDirectoryInfoCollection m_sdk_directory_infos; + std::string m_device_support_directory; + std::string m_device_support_directory_for_os_version; + std::string m_build_update; + uint32_t m_last_module_sdk_idx; + uint32_t m_connected_module_sdk_idx; + + bool + UpdateSDKDirectoryInfosIfNeeded(); + + const char * + GetDeviceSupportDirectory(); + + const char * + GetDeviceSupportDirectoryForOSVersion(); + + const SDKDirectoryInfo * + GetSDKDirectoryForLatestOSVersion (); + + const SDKDirectoryInfo * + GetSDKDirectoryForCurrentOSVersion (); + + static lldb_private::FileSpec::EnumerateDirectoryResult + GetContainedFilesIntoVectorOfStringsCallback (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + uint32_t + FindFileInAllSDKs (const char *platform_file_path, + lldb_private::FileSpecList &file_list); + + bool + GetFileInSDK (const char *platform_file_path, + uint32_t sdk_idx, + lldb_private::FileSpec &local_file); + + bool + GetFileInSDKRoot (const char *platform_file_path, + const char *sdkroot_path, + bool symbols_dirs_only, + lldb_private::FileSpec &local_file); + + uint32_t + FindFileInAllSDKs (const lldb_private::FileSpec &platform_file, + lldb_private::FileSpecList &file_list); + + uint32_t + GetConnectedSDKIndex (); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformRemoteAppleWatch); +}; + +#endif // liblldb_PlatformRemoteAppleWatch_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.cpp b/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.cpp new file mode 100644 index 000000000000..75afa9019dcd --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.cpp @@ -0,0 +1,974 @@ +//===-- PlatformRemoteiOS.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformRemoteiOS.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +PlatformRemoteiOS::SDKDirectoryInfo::SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir) : + directory(sdk_dir), + build(), + version_major(0), + version_minor(0), + version_update(0), + user_cached(false) +{ + const char *dirname_cstr = sdk_dir.GetFilename().GetCString(); + const char *pos = Args::StringToVersion (dirname_cstr, + version_major, + version_minor, + version_update); + + if (pos && pos[0] == ' ' && pos[1] == '(') + { + const char *build_start = pos + 2; + const char *end_paren = strchr (build_start, ')'); + if (end_paren && build_start < end_paren) + build.SetCStringWithLength(build_start, end_paren - build_start); + } +} + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformRemoteiOS::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformRemoteiOS::GetPluginNameStatic(), + PlatformRemoteiOS::GetDescriptionStatic(), + PlatformRemoteiOS::CreateInstance); + } +} + +void +PlatformRemoteiOS::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformRemoteiOS::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformRemoteiOS::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = "<null>"; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; + + log->Printf ("PlatformRemoteiOS::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::thumb: + { + const llvm::Triple &triple = arch->GetTriple(); + llvm::Triple::VendorType vendor = triple.getVendor(); + switch (vendor) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; + +#endif + default: + break; + } + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Darwin: // Deprecated, but still support Darwin for historical reasons + case llvm::Triple::IOS: // This is the right triple value for iOS debugging + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + + if (create) + { + if (log) + log->Printf ("PlatformRemoteiOS::%s() creating platform", __FUNCTION__); + + return lldb::PlatformSP(new PlatformRemoteiOS ()); + } + + if (log) + log->Printf ("PlatformRemoteiOS::%s() aborting creation of platform", __FUNCTION__); + + return lldb::PlatformSP(); +} + + +lldb_private::ConstString +PlatformRemoteiOS::GetPluginNameStatic () +{ + static ConstString g_name("remote-ios"); + return g_name; +} + +const char * +PlatformRemoteiOS::GetDescriptionStatic() +{ + return "Remote iOS platform plug-in."; +} + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformRemoteiOS::PlatformRemoteiOS () : + PlatformDarwin (false), // This is a remote platform + m_sdk_directory_infos(), + m_device_support_directory(), + m_device_support_directory_for_os_version (), + m_build_update(), + m_last_module_sdk_idx (UINT32_MAX), + m_connected_module_sdk_idx (UINT32_MAX) +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformRemoteiOS::~PlatformRemoteiOS() +{ +} + + +void +PlatformRemoteiOS::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetDeviceSupportDirectoryForOSVersion(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); + + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; i<num_sdk_infos; ++i) + { + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i]; + strm.Printf (" SDK Roots: [%2u] \"%s\"\n", + i, + sdk_dir_info.directory.GetPath().c_str()); + } +} + + +Error +PlatformRemoteiOS::ResolveExecutable (const ModuleSpec &ms, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + ModuleSpec resolved_module_spec(ms); + + // Resolve any executable within a bundle on MacOSX + // TODO: verify that this handles shallow bundles, if not then implement one ourselves + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + { + if (resolved_module_spec.GetArchitecture().IsValid() || resolved_module_spec.GetUUID().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + + if (exe_module_sp && exe_module_sp->GetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +FileSpec::EnumerateDirectoryResult +PlatformRemoteiOS::GetContainedFilesIntoVectorOfStringsCallback (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + ((PlatformRemoteiOS::SDKDirectoryInfoCollection *)baton)->push_back(PlatformRemoteiOS::SDKDirectoryInfo(file_spec)); + return FileSpec::eEnumerateDirectoryResultNext; +} + +bool +PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded() +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (m_sdk_directory_infos.empty()) + { + // A --sysroot option was supplied - add it to our list of SDKs to check + if (m_sdk_sysroot) + { + FileSpec sdk_sysroot_fspec(m_sdk_sysroot.GetCString(), true); + const SDKDirectoryInfo sdk_sysroot_directory_info(sdk_sysroot_fspec); + m_sdk_directory_infos.push_back(sdk_sysroot_directory_info); + if (log) + { + log->Printf ("PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded added --sysroot SDK directory %s", m_sdk_sysroot.GetCString()); + } + return true; + } + const char *device_support_dir = GetDeviceSupportDirectory(); + if (log) + { + log->Printf ("PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded Got DeviceSupport directory %s", device_support_dir); + } + if (device_support_dir) + { + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + + SDKDirectoryInfoCollection builtin_sdk_directory_infos; + FileSpec::EnumerateDirectory (m_device_support_directory.c_str(), + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &builtin_sdk_directory_infos); + + // Only add SDK directories that have symbols in them, some SDKs only contain + // developer disk images and no symbols, so they aren't useful to us. + FileSpec sdk_symbols_symlink_fspec; + for (const auto &sdk_directory_info : builtin_sdk_directory_infos) + { + sdk_symbols_symlink_fspec = sdk_directory_info.directory; + sdk_symbols_symlink_fspec.AppendPathComponent("Symbols"); + if (sdk_symbols_symlink_fspec.Exists()) + { + m_sdk_directory_infos.push_back(sdk_directory_info); + if (log) + { + log->Printf ("PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded added builtin SDK directory %s", sdk_symbols_symlink_fspec.GetPath().c_str()); + } + } + } + + const uint32_t num_installed = m_sdk_directory_infos.size(); + FileSpec local_sdk_cache("~/Library/Developer/Xcode/iOS DeviceSupport", true); + if (local_sdk_cache.Exists()) + { + if (log) + { + log->Printf ("PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded searching %s for additional SDKs", local_sdk_cache.GetPath().c_str()); + } + char path[PATH_MAX]; + if (local_sdk_cache.GetPath(path, sizeof(path))) + { + FileSpec::EnumerateDirectory (path, + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &m_sdk_directory_infos); + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + // First try for an exact match of major, minor and update + for (uint32_t i=num_installed; i<num_sdk_infos; ++i) + { + m_sdk_directory_infos[i].user_cached = true; + if (log) + { + log->Printf ("PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded user SDK directory %s", m_sdk_directory_infos[i].directory.GetPath().c_str()); + } + } + } + } + } + } + return !m_sdk_directory_infos.empty(); +} + +const PlatformRemoteiOS::SDKDirectoryInfo * +PlatformRemoteiOS::GetSDKDirectoryForCurrentOSVersion () +{ + uint32_t i; + if (UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + + // Check to see if the user specified a build string. If they did, then + // be sure to match it. + std::vector<bool> check_sdk_info(num_sdk_infos, true); + ConstString build(m_sdk_build); + if (build) + { + for (i=0; i<num_sdk_infos; ++i) + check_sdk_info[i] = m_sdk_directory_infos[i].build == build; + } + + // If we are connected we can find the version of the OS the platform + // us running on and select the right SDK + uint32_t major, minor, update; + if (GetOSVersion(major, minor, update)) + { + if (UpdateSDKDirectoryInfosIfNeeded()) + { + // First try for an exact match of major, minor and update + for (i=0; i<num_sdk_infos; ++i) + { + if (check_sdk_info[i]) + { + if (m_sdk_directory_infos[i].version_major == major && + m_sdk_directory_infos[i].version_minor == minor && + m_sdk_directory_infos[i].version_update == update) + { + return &m_sdk_directory_infos[i]; + } + } + } + // First try for an exact match of major and minor + for (i=0; i<num_sdk_infos; ++i) + { + if (check_sdk_info[i]) + { + if (m_sdk_directory_infos[i].version_major == major && + m_sdk_directory_infos[i].version_minor == minor) + { + return &m_sdk_directory_infos[i]; + } + } + } + // Lastly try to match of major version only.. + for (i=0; i<num_sdk_infos; ++i) + { + if (check_sdk_info[i]) + { + if (m_sdk_directory_infos[i].version_major == major) + { + return &m_sdk_directory_infos[i]; + } + } + } + } + } + else if (build) + { + // No version, just a build number, search for the first one that matches + for (i=0; i<num_sdk_infos; ++i) + if (check_sdk_info[i]) + return &m_sdk_directory_infos[i]; + } + } + return NULL; +} + +const PlatformRemoteiOS::SDKDirectoryInfo * +PlatformRemoteiOS::GetSDKDirectoryForLatestOSVersion () +{ + const PlatformRemoteiOS::SDKDirectoryInfo *result = NULL; + if (UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + // First try for an exact match of major, minor and update + for (uint32_t i=0; i<num_sdk_infos; ++i) + { + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i]; + if (sdk_dir_info.version_major != UINT32_MAX) + { + if (result == NULL || sdk_dir_info.version_major > result->version_major) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_major == result->version_major) + { + if (sdk_dir_info.version_minor > result->version_minor) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_minor == result->version_minor) + { + if (sdk_dir_info.version_update > result->version_update) + { + result = &sdk_dir_info; + } + } + } + } + } + } + return result; +} + + + +const char * +PlatformRemoteiOS::GetDeviceSupportDirectory() +{ + if (m_device_support_directory.empty()) + { + const char *device_support_dir = GetDeveloperDirectory(); + if (device_support_dir) + { + m_device_support_directory.assign (device_support_dir); + m_device_support_directory.append ("/Platforms/iPhoneOS.platform/DeviceSupport"); + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory + // or it should have a valid path if the code gets here + assert (m_device_support_directory.empty() == false); + if (m_device_support_directory[0]) + return m_device_support_directory.c_str(); + return NULL; +} + + +const char * +PlatformRemoteiOS::GetDeviceSupportDirectoryForOSVersion() +{ + if (m_sdk_sysroot) + return m_sdk_sysroot.GetCString(); + + if (m_device_support_directory_for_os_version.empty()) + { + const PlatformRemoteiOS::SDKDirectoryInfo *sdk_dir_info = GetSDKDirectoryForCurrentOSVersion (); + if (sdk_dir_info == NULL) + sdk_dir_info = GetSDKDirectoryForLatestOSVersion (); + if (sdk_dir_info) + { + char path[PATH_MAX]; + if (sdk_dir_info->directory.GetPath(path, sizeof(path))) + { + m_device_support_directory_for_os_version = path; + return m_device_support_directory_for_os_version.c_str(); + } + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory_for_os_version.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory_for_os_version + // or it should have a valid path if the code gets here + assert (m_device_support_directory_for_os_version.empty() == false); + if (m_device_support_directory_for_os_version[0]) + return m_device_support_directory_for_os_version.c_str(); + return NULL; +} + +uint32_t +PlatformRemoteiOS::FindFileInAllSDKs (const char *platform_file_path, + FileSpecList &file_list) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST | LIBLLDB_LOG_VERBOSE); + if (platform_file_path && platform_file_path[0] && UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + lldb_private::FileSpec local_file; + // First try for an exact match of major, minor and update + for (uint32_t sdk_idx=0; sdk_idx<num_sdk_infos; ++sdk_idx) + { + if (log) + { + log->Printf ("Searching for %s in sdk path %s", platform_file_path, m_sdk_directory_infos[sdk_idx].directory.GetPath().c_str()); + } + if (GetFileInSDK (platform_file_path, + sdk_idx, + local_file)) + { + file_list.Append(local_file); + } + } + } + return file_list.GetSize(); +} + +bool +PlatformRemoteiOS::GetFileInSDK (const char *platform_file_path, + uint32_t sdk_idx, + lldb_private::FileSpec &local_file) +{ + if (sdk_idx < m_sdk_directory_infos.size()) + { + char sdkroot_path[PATH_MAX]; + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[sdk_idx]; + if (sdk_dir_info.directory.GetPath(sdkroot_path, sizeof(sdkroot_path))) + { + const bool symbols_dirs_only = true; + + return GetFileInSDKRoot (platform_file_path, + sdkroot_path, + symbols_dirs_only, + local_file); + } + } + return false; +} + + +bool +PlatformRemoteiOS::GetFileInSDKRoot (const char *platform_file_path, + const char *sdkroot_path, + bool symbols_dirs_only, + lldb_private::FileSpec &local_file) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (sdkroot_path && sdkroot_path[0] && platform_file_path && platform_file_path[0]) + { + char resolved_path[PATH_MAX]; + + if (!symbols_dirs_only) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the SDK dir %s", platform_file_path, sdkroot_path); + } + return true; + } + } + + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols.Internal%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the SDK dir %s/Symbols.Internal", platform_file_path, sdkroot_path); + } + return true; + } + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the SDK dir %s/Symbols", platform_file_path, sdkroot_path); + } + return true; + } + } + return false; +} + + +Error +PlatformRemoteiOS::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + Error error; + char platform_file_path[PATH_MAX]; + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + char resolved_path[PATH_MAX]; + + const char * os_version_dir = GetDeviceSupportDirectoryForOSVersion(); + if (os_version_dir) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the DeviceSupport dir %s", platform_file_path, os_version_dir); + } + return error; + } + + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols.Internal/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the DeviceSupport dir %s/Symbols.Internal", platform_file_path, os_version_dir); + } + return error; + } + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the DeviceSupport dir %s/Symbols", platform_file_path, os_version_dir); + } + return error; + } + + } + local_file = platform_file; + if (local_file.Exists()) + return error; + + error.SetErrorStringWithFormat ("unable to locate a platform file for '%s' in platform '%s'", + platform_file_path, + GetPluginName().GetCString()); + } + else + { + error.SetErrorString ("invalid platform file argument"); + } + return error; +} + +Error +PlatformRemoteiOS::GetSharedModule (const ModuleSpec &module_spec, + Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + // For iOS, the SDK files are all cached locally on the host + // system. So first we ask for the file in the cached SDK, + // then we attempt to get a shared module for the right architecture + // with the right UUID. + const FileSpec &platform_file = module_spec.GetFileSpec(); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST | LIBLLDB_LOG_VERBOSE); + + Error error; + char platform_file_path[PATH_MAX]; + + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + ModuleSpec platform_module_spec(module_spec); + + UpdateSDKDirectoryInfosIfNeeded(); + + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + + // If we are connected we migth be able to correctly deduce the SDK directory + // using the OS build. + const uint32_t connected_sdk_idx = GetConnectedSDKIndex (); + if (connected_sdk_idx < num_sdk_infos) + { + if (log) + { + log->Printf ("Searching for %s in sdk path %s", platform_file_path, m_sdk_directory_infos[connected_sdk_idx].directory.GetPath().c_str()); + } + if (GetFileInSDK (platform_file_path, connected_sdk_idx, platform_module_spec.GetFileSpec())) + { + module_sp.reset(); + error = ResolveExecutable (platform_module_spec, + module_sp, + NULL); + if (module_sp) + { + m_last_module_sdk_idx = connected_sdk_idx; + error.Clear(); + return error; + } + } + } + + // Try the last SDK index if it is set as most files from an SDK + // will tend to be valid in that same SDK. + if (m_last_module_sdk_idx < num_sdk_infos) + { + if (log) + { + log->Printf ("Searching for %s in sdk path %s", platform_file_path, m_sdk_directory_infos[m_last_module_sdk_idx].directory.GetPath().c_str()); + } + if (GetFileInSDK (platform_file_path, m_last_module_sdk_idx, platform_module_spec.GetFileSpec())) + { + module_sp.reset(); + error = ResolveExecutable (platform_module_spec, + module_sp, + NULL); + if (module_sp) + { + error.Clear(); + return error; + } + } + } + + // First try for an exact match of major, minor and update: + // If a particalar SDK version was specified via --version or --build, look for a match on disk. + const SDKDirectoryInfo *current_sdk_info = GetSDKDirectoryForCurrentOSVersion(); + const uint32_t current_sdk_idx = GetSDKIndexBySDKDirectoryInfo(current_sdk_info); + if (current_sdk_idx < num_sdk_infos && current_sdk_idx != m_last_module_sdk_idx) + { + if (log) + { + log->Printf ("Searching for %s in sdk path %s", platform_file_path, m_sdk_directory_infos[current_sdk_idx].directory.GetPath().c_str()); + } + if (GetFileInSDK (platform_file_path, current_sdk_idx, platform_module_spec.GetFileSpec())) + { + module_sp.reset(); + error = ResolveExecutable (platform_module_spec, + module_sp, + NULL); + if (module_sp) + { + m_last_module_sdk_idx = current_sdk_idx; + error.Clear(); + return error; + } + } + } + + // Second try all SDKs that were found. + for (uint32_t sdk_idx=0; sdk_idx<num_sdk_infos; ++sdk_idx) + { + if (m_last_module_sdk_idx == sdk_idx) + { + // Skip the last module SDK index if we already searched + // it above + continue; + } + if (log) + { + log->Printf ("Searching for %s in sdk path %s", platform_file_path, m_sdk_directory_infos[sdk_idx].directory.GetPath().c_str()); + } + if (GetFileInSDK (platform_file_path, sdk_idx, platform_module_spec.GetFileSpec())) + { + //printf ("sdk[%u]: '%s'\n", sdk_idx, local_file.GetPath().c_str()); + + error = ResolveExecutable (platform_module_spec, module_sp, NULL); + if (module_sp) + { + // Remember the index of the last SDK that we found a file + // in in case the wrong SDK was selected. + m_last_module_sdk_idx = sdk_idx; + error.Clear(); + return error; + } + } + } + } + // Not the module we are looking for... Nothing to see here... + module_sp.reset(); + + // This may not be an SDK-related module. Try whether we can bring in the thing to our local cache. + error = GetSharedModuleWithLocalCache(module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); + if (error.Success()) + return error; + + const bool always_create = false; + error = ModuleList::GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr, + always_create); + + if (module_sp) + module_sp->SetPlatformFileSpec(platform_file); + + return error; +} + +bool +PlatformRemoteiOS::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + return ARMGetSupportedArchitectureAtIndex (idx, arch); +} + +uint32_t +PlatformRemoteiOS::GetConnectedSDKIndex () +{ + if (IsConnected()) + { + if (m_connected_module_sdk_idx == UINT32_MAX) + { + std::string build; + if (GetRemoteOSBuildString(build)) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; i<num_sdk_infos; ++i) + { + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i]; + if (strstr(sdk_dir_info.directory.GetFilename().AsCString(""), build.c_str())) + { + m_connected_module_sdk_idx = i; + } + } + } + } + } + else + { + m_connected_module_sdk_idx = UINT32_MAX; + } + return m_connected_module_sdk_idx; +} + +uint32_t +PlatformRemoteiOS::GetSDKIndexBySDKDirectoryInfo (const SDKDirectoryInfo *sdk_info) +{ + if (sdk_info == NULL) + { + return UINT32_MAX; + } + + return sdk_info - &m_sdk_directory_infos[0]; +} diff --git a/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.h b/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.h new file mode 100644 index 000000000000..9726d8238e13 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.h @@ -0,0 +1,173 @@ +//===-- PlatformRemoteiOS.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformRemoteiOS_h_ +#define liblldb_PlatformRemoteiOS_h_ + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" +#include "PlatformDarwin.h" + +class PlatformRemoteiOS : public PlatformDarwin +{ +public: + PlatformRemoteiOS (); + + ~PlatformRemoteiOS() override; + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + virtual lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector<std::string> &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneOS); + } + +protected: + struct SDKDirectoryInfo + { + SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir_spec); + lldb_private::FileSpec directory; + lldb_private::ConstString build; + uint32_t version_major; + uint32_t version_minor; + uint32_t version_update; + bool user_cached; + }; + + typedef std::vector<SDKDirectoryInfo> SDKDirectoryInfoCollection; + + SDKDirectoryInfoCollection m_sdk_directory_infos; + std::string m_device_support_directory; + std::string m_device_support_directory_for_os_version; + std::string m_build_update; + uint32_t m_last_module_sdk_idx; + uint32_t m_connected_module_sdk_idx; + + bool + UpdateSDKDirectoryInfosIfNeeded(); + + const char * + GetDeviceSupportDirectory(); + + const char * + GetDeviceSupportDirectoryForOSVersion(); + + const SDKDirectoryInfo * + GetSDKDirectoryForLatestOSVersion (); + + const SDKDirectoryInfo * + GetSDKDirectoryForCurrentOSVersion (); + + static lldb_private::FileSpec::EnumerateDirectoryResult + GetContainedFilesIntoVectorOfStringsCallback (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + uint32_t + FindFileInAllSDKs (const char *platform_file_path, + lldb_private::FileSpecList &file_list); + + bool + GetFileInSDK (const char *platform_file_path, + uint32_t sdk_idx, + lldb_private::FileSpec &local_file); + + bool + GetFileInSDKRoot (const char *platform_file_path, + const char *sdkroot_path, + bool symbols_dirs_only, + lldb_private::FileSpec &local_file); + + uint32_t + FindFileInAllSDKs (const lldb_private::FileSpec &platform_file, + lldb_private::FileSpecList &file_list); + + uint32_t + GetConnectedSDKIndex (); + + // Get index of SDK in SDKDirectoryInfoCollection by its pointer and return UINT32_MAX if that SDK not found. + uint32_t + GetSDKIndexBySDKDirectoryInfo (const SDKDirectoryInfo *sdk_info); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformRemoteiOS); +}; + +#endif // liblldb_PlatformRemoteiOS_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp b/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp new file mode 100644 index 000000000000..cbe9c7949a4a --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp @@ -0,0 +1,502 @@ +//===-- PlatformiOSSimulator.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformiOSSimulator.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformiOSSimulator::Initialize () +{ + PlatformAppleSimulator::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformiOSSimulator::GetPluginNameStatic(), + PlatformiOSSimulator::GetDescriptionStatic(), + PlatformiOSSimulator::CreateInstance); + } +} + +void +PlatformiOSSimulator::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformiOSSimulator::CreateInstance); + } + } + + PlatformAppleSimulator::Terminate (); +} + +PlatformSP +PlatformiOSSimulator::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = "<null>"; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; + + log->Printf ("PlatformiOSSimulator::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::x86_64: + case llvm::Triple::x86: + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Darwin: // Deprecated, but still support Darwin for historical reasons + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: // IOS is not used for simulator triples, but accept it just in case + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + if (create) + { + if (log) + log->Printf ("PlatformiOSSimulator::%s() creating platform", __FUNCTION__); + + return PlatformSP(new PlatformiOSSimulator ()); + } + + if (log) + log->Printf ("PlatformiOSSimulator::%s() aborting creation of platform", __FUNCTION__); + + return PlatformSP(); +} + + +lldb_private::ConstString +PlatformiOSSimulator::GetPluginNameStatic () +{ + static ConstString g_name("ios-simulator"); + return g_name; +} + +const char * +PlatformiOSSimulator::GetDescriptionStatic() +{ + return "iOS simulator platform plug-in."; +} + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformiOSSimulator::PlatformiOSSimulator () : +PlatformAppleSimulator (), +m_sdk_directory () +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformiOSSimulator::~PlatformiOSSimulator() +{ +} + + +void +PlatformiOSSimulator::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetSDKDirectoryAsCString(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); + PlatformAppleSimulator::GetStatus(strm); +} + + +Error +PlatformiOSSimulator::ResolveExecutable (const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + ModuleSpec resolved_module_spec(module_spec); + + // If we have "ls" as the exe_file, resolve the executable loation based on + // the current path variables + // TODO: resolve bare executables in the Platform SDK + // if (!resolved_exe_file.Exists()) + // resolved_exe_file.ResolveExecutableLocation (); + + // Resolve any executable within a bundle on MacOSX + // TODO: verify that this handles shallow bundles, if not then implement one ourselves + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + + if (exe_module_sp && exe_module_sp->GetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + ArchSpec platform_arch; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + // Only match x86 with x86 and x86_64 with x86_64... + if (!module_spec.GetArchitecture().IsValid() || module_spec.GetArchitecture().GetCore() == resolved_module_spec.GetArchitecture().GetCore()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (platform_arch.GetArchitectureName()); + } + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +static FileSpec::EnumerateDirectoryResult +EnumerateDirectoryCallback (void *baton, FileSpec::FileType file_type, const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeDirectory) + { + const char *filename = file_spec.GetFilename().GetCString(); + if (filename && strncmp(filename, "iPhoneSimulator", strlen ("iPhoneSimulator")) == 0) + { + ::snprintf ((char *)baton, PATH_MAX, "%s", filename); + return FileSpec::eEnumerateDirectoryResultQuit; + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + + + +const char * +PlatformiOSSimulator::GetSDKDirectoryAsCString() +{ + Mutex::Locker locker (m_mutex); + if (m_sdk_directory.empty()) + { + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir) + { + char sdks_directory[PATH_MAX]; + char sdk_dirname[PATH_MAX]; + sdk_dirname[0] = '\0'; + snprintf (sdks_directory, + sizeof(sdks_directory), + "%s/Platforms/iPhoneSimulator.platform/Developer/SDKs", + developer_dir); + FileSpec simulator_sdk_spec; + bool find_directories = true; + bool find_files = false; + bool find_other = false; + FileSpec::EnumerateDirectory (sdks_directory, + find_directories, + find_files, + find_other, + EnumerateDirectoryCallback, + sdk_dirname); + + if (sdk_dirname[0]) + { + m_sdk_directory = sdks_directory; + m_sdk_directory.append (1, '/'); + m_sdk_directory.append (sdk_dirname); + return m_sdk_directory.c_str(); + } + } + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_sdk_directory.assign (1, '\0'); + } + + // We should have put a single NULL character into m_sdk_directory + // or it should have a valid path if the code gets here + assert (m_sdk_directory.empty() == false); + if (m_sdk_directory[0]) + return m_sdk_directory.c_str(); + return NULL; +} + +Error +PlatformiOSSimulator::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + Error error; + char platform_file_path[PATH_MAX]; + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + char resolved_path[PATH_MAX]; + + const char * sdk_dir = GetSDKDirectoryAsCString(); + if (sdk_dir) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/%s", + sdk_dir, + platform_file_path); + + // First try in the SDK and see if the file is in there + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + + // Else fall back to the actual path itself + local_file.SetFile(platform_file_path, true); + if (local_file.Exists()) + return error; + + } + error.SetErrorStringWithFormat ("unable to locate a platform file for '%s' in platform '%s'", + platform_file_path, + GetPluginName().GetCString()); + } + else + { + error.SetErrorString ("invalid platform file argument"); + } + return error; +} + +Error +PlatformiOSSimulator::GetSharedModule (const ModuleSpec &module_spec, + Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + // For iOS, the SDK files are all cached locally on the host + // system. So first we ask for the file in the cached SDK, + // then we attempt to get a shared module for the right architecture + // with the right UUID. + Error error; + ModuleSpec platform_module_spec (module_spec); + const FileSpec &platform_file = module_spec.GetFileSpec(); + error = GetSymbolFile (platform_file, module_spec.GetUUIDPtr(), platform_module_spec.GetFileSpec()); + if (error.Success()) + { + error = ResolveExecutable (platform_module_spec, module_sp, module_search_paths_ptr); + } + else + { + const bool always_create = false; + error = ModuleList::GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr, + always_create); + + } + if (module_sp) + module_sp->SetPlatformFileSpec(platform_file); + + return error; +} + + +uint32_t +PlatformiOSSimulator::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + ProcessInstanceInfoList all_osx_process_infos; + // First we get all OSX processes + const uint32_t n = Host::FindProcesses (match_info, all_osx_process_infos); + + // Now we filter them down to only the iOS triples + for (uint32_t i=0; i<n; ++i) + { + const ProcessInstanceInfo &proc_info = all_osx_process_infos.GetProcessInfoAtIndex(i); + if (proc_info.GetArchitecture().GetTriple().getOS() == llvm::Triple::IOS) { + process_infos.Append(proc_info); + } + } + return process_infos.GetSize(); +} + +bool +PlatformiOSSimulator::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + static const ArchSpec platform_arch(HostInfo::GetArchitecture(HostInfo::eArchKindDefault)); + static const ArchSpec platform_arch64(HostInfo::GetArchitecture(HostInfo::eArchKind64)); + + if (idx == 0) + { + arch = platform_arch; + if (arch.IsValid()) + { + arch.GetTriple().setOS (llvm::Triple::IOS); + return true; + } + } + else + { + if (platform_arch.IsExactMatch(platform_arch64)) + { + // This macosx platform supports both 32 and 64 bit. + if (idx == 1) + { + // 32/64: return "x86_64-apple-macosx" for architecture 1 + arch = platform_arch64; + return true; + } + else if (idx == 2 || idx == 3) + { + arch = HostInfo::GetArchitecture(HostInfo::eArchKind32); + if (arch.IsValid()) + { + if (idx == 2) + arch.GetTriple().setOS (llvm::Triple::IOS); + // 32/64: return "i386-apple-ios" for architecture 2 + // 32/64: return "i386-apple-macosx" for architecture 3 + return true; + } + } + } + else if (idx == 1) + { + // This macosx platform supports only 32 bit, so return the *-apple-macosx version + arch = platform_arch; + return true; + } + } + return false; +} diff --git a/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.h b/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.h new file mode 100644 index 000000000000..f84d04b9c485 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.h @@ -0,0 +1,116 @@ +//===-- PlatformiOSSimulator.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformiOSSimulator_h_ +#define liblldb_PlatformiOSSimulator_h_ + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes +#include "PlatformAppleSimulator.h" + +class PlatformiOSSimulator : public PlatformAppleSimulator +{ +public: + PlatformiOSSimulator (); + + ~PlatformiOSSimulator() override; + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + virtual lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + uint32_t + FindProcesses (const lldb_private::ProcessInstanceInfoMatch &match_info, + lldb_private::ProcessInstanceInfoList &process_infos) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector<std::string> &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneSimulator); + } + +protected: + std::string m_sdk_directory; + std::string m_build_update; + + const char * + GetSDKDirectoryAsCString(); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformiOSSimulator); +}; + +#endif // liblldb_PlatformiOSSimulator_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.h b/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.h new file mode 100644 index 000000000000..8393ea304906 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.h @@ -0,0 +1,315 @@ +//===-- PlatformiOSSimulatorCoreSimulatorSupport.h ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformiOSSimulatorCoreSimulatorSupport_h_ +#define liblldb_PlatformiOSSimulatorCoreSimulatorSupport_h_ + +// C Includes +// C++ Includes +#include <functional> +#include <string> +#include <ostream> +#include <vector> +// Other libraries and framework includes +#ifdef __APPLE__ +#include <objc/objc.h> +#else +typedef void *id; +#endif +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Target/ProcessLaunchInfo.h" + +#include "llvm/ADT/Optional.h" + +// And now the actual magic +namespace CoreSimulatorSupport +{ + class Process + { + public: + lldb::pid_t + GetPID () + { + return m_pid; + } + + explicit operator bool () + { + return m_pid != LLDB_INVALID_PROCESS_ID; + } + + lldb_private::Error + GetError () + { + return m_error; + } + + private: + Process (lldb::pid_t p); + + Process(lldb_private::Error error); + + Process (lldb::pid_t p, lldb_private::Error error); + + lldb::pid_t m_pid; + lldb_private::Error m_error; + + friend class Device; + }; + + class ModelIdentifier { + public: + ModelIdentifier (const std::string& mi); + ModelIdentifier (); + + explicit operator bool () const + { + return !m_versions.empty(); + } + + size_t + GetNumVersions () const + { + return m_versions.size(); + } + + unsigned int + GetVersionAtIndex (size_t idx) const + { + return m_versions[idx]; + } + + std::string + GetFamily () const + { + return m_family.c_str(); + } + + private: + std::string m_family; + std::vector<unsigned int> m_versions; + }; + + class DeviceType + { + public: + enum class ProductFamilyID : int32_t + { + iPhone = 1, + iPad = 2, + appleTV = 3, + appleWatch = 4 + }; + + DeviceType (); + + DeviceType (id d); + + explicit operator bool (); + + std::string + GetName (); + + lldb_private::ConstString + GetIdentifier (); + + ModelIdentifier + GetModelIdentifier (); + + lldb_private::ConstString + GetProductFamily (); + + ProductFamilyID + GetProductFamilyID (); + + private: + id m_dev; + llvm::Optional<ModelIdentifier> m_model_identifier; + }; + + class OSVersion { + public: + OSVersion (const std::string& ver, + const std::string& build); + + OSVersion (); + + explicit operator bool () const + { + return !m_versions.empty(); + } + + size_t + GetNumVersions () const + { + return m_versions.size(); + } + + unsigned int + GetVersionAtIndex (size_t idx) const + { + return m_versions[idx]; + } + + const char* + GetBuild () const + { + return m_build.c_str(); + } + + private: + std::vector<unsigned int> m_versions; + std::string m_build; + }; + + class DeviceRuntime + { + public: + DeviceRuntime (); + + DeviceRuntime (id d); + + explicit operator bool (); + + OSVersion + GetVersion (); + + bool + IsAvailable (); + + private: + id m_dev; + llvm::Optional<OSVersion> m_os_version; + }; + + class Device + { + private: + typedef unsigned long int NSUInteger; + + public: + enum class State : NSUInteger + { + Creating, + Shutdown, + Booting, + Booted, + ShuttingDown + }; + + Device (); + + Device (id d); + + explicit operator bool (); + + std::string + GetName () const; + + DeviceType + GetDeviceType (); + + DeviceRuntime + GetDeviceRuntime (); + + State + GetState (); + + bool + Boot (lldb_private::Error &err); + + bool + Shutdown (lldb_private::Error &err); + + std::string + GetUDID () const; + + Process + Spawn (lldb_private::ProcessLaunchInfo& launch_info); + + private: + id m_dev; + llvm::Optional<DeviceType> m_dev_type; + llvm::Optional<DeviceRuntime> m_dev_runtime; + + friend class DeviceSet; + }; + + bool + operator > (const OSVersion& lhs, + const OSVersion& rhs); + + bool + operator > (const ModelIdentifier& lhs, + const ModelIdentifier& rhs); + + bool + operator < (const OSVersion& lhs, + const OSVersion& rhs); + + bool + operator < (const ModelIdentifier& lhs, + const ModelIdentifier& rhs); + + bool + operator == (const OSVersion& lhs, + const OSVersion& rhs); + + bool + operator == (const ModelIdentifier& lhs, + const ModelIdentifier& rhs); + + bool + operator != (const OSVersion& lhs, + const OSVersion& rhs); + + bool + operator != (const ModelIdentifier& lhs, + const ModelIdentifier& rhs); + + class DeviceSet + { + public: + static DeviceSet + GetAllDevices (); + + static DeviceSet + GetAvailableDevices (); + + size_t + GetNumDevices (); + + Device + GetDeviceAtIndex (size_t idx); + + void + ForEach (std::function<bool(const Device &)> f); + + DeviceSet + GetDevicesIf (std::function<bool(Device)> f); + + DeviceSet + GetDevices (DeviceType::ProductFamilyID dev_id); + + Device + GetFanciest (DeviceType::ProductFamilyID dev_id); + + private: + DeviceSet (id arr) : m_dev(arr) + { + } + + id m_dev; + }; +} + +#endif // liblldb_PlatformiOSSimulatorCoreSimulatorSupport_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm b/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm new file mode 100644 index 000000000000..7075de552529 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm @@ -0,0 +1,773 @@ +//===-- PlatformiOSSimulatorCoreSimulatorSupport.cpp ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformiOSSimulatorCoreSimulatorSupport.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include <CoreFoundation/CoreFoundation.h> +#include <Foundation/Foundation.h> +// Project includes +#include "lldb/Target/FileAction.h" +#include "lldb/Utility/PseudoTerminal.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb_private; +using namespace lldb_utility; +// CoreSimulator lives as part of Xcode, which means we can't really link against it, so we dlopen() +// it at runtime, and error out nicely if that fails +@interface SimDeviceSet +{} ++ (id) defaultSet; +@end +// However, the drawback is that the compiler will not know about the selectors we're trying to use +// until runtime; to appease clang in this regard, define a fake protocol on NSObject that exposes +// the needed interface names for us +@protocol LLDBCoreSimulatorSupport <NSObject> +- (NSArray *) devices; +- (id) deviceType; +- (NSString *) name; +- (NSString *) identifier; +- (NSString *) modelIdentifier; +- (NSString *) productFamily; +- (int32_t) productFamilyID; +- (id) runtime; +- (BOOL) available; +- (NSString *) versionString; +- (NSString *) buildVersionString; +- (BOOL) bootWithOptions:(NSDictionary *)options error:(NSError**)error; +- (NSUInteger) state; +- (BOOL) shutdownWithError:(NSError **)error; +- (NSUUID *) UDID; +- (pid_t) spawnWithPath:(NSString *)path options:(NSDictionary *)options terminationHandler:(void (^)(int status)) terminationHandler error:(NSError **)error; +@end + +CoreSimulatorSupport::Process::Process (lldb::pid_t p) : + m_pid (p), + m_error () +{ +} + +CoreSimulatorSupport::Process::Process(Error error) : + m_pid (LLDB_INVALID_PROCESS_ID), + m_error (error) +{ +} + +CoreSimulatorSupport::Process::Process (lldb::pid_t p, Error error) : + m_pid (p), + m_error (error) +{ +} + + +CoreSimulatorSupport::DeviceType::DeviceType () : + m_dev (nil), + m_model_identifier () +{ +} + +CoreSimulatorSupport::DeviceType::DeviceType (id d) : + m_dev (d), + m_model_identifier () +{ +} + +CoreSimulatorSupport::DeviceType::operator bool () +{ + return m_dev != nil; +} + +ConstString +CoreSimulatorSupport::DeviceType::GetIdentifier () +{ + return ConstString( [[m_dev identifier] UTF8String] ); +} + +ConstString +CoreSimulatorSupport::DeviceType::GetProductFamily () +{ + return ConstString( [[m_dev productFamily] UTF8String] ); +} + +CoreSimulatorSupport::DeviceType::ProductFamilyID +CoreSimulatorSupport::DeviceType::GetProductFamilyID () +{ + return ProductFamilyID([m_dev productFamilyID]); +} + +CoreSimulatorSupport::DeviceRuntime::DeviceRuntime () : + m_dev (nil), + m_os_version () +{ +} + +CoreSimulatorSupport::DeviceRuntime::DeviceRuntime (id d) : + m_dev (d), + m_os_version () +{ +} + +CoreSimulatorSupport::DeviceRuntime::operator bool () +{ + return m_dev != nil; +} + +bool +CoreSimulatorSupport::DeviceRuntime::IsAvailable () +{ + return [m_dev available]; +} + +CoreSimulatorSupport::Device::Device () : + m_dev (nil), + m_dev_type (), + m_dev_runtime () +{ +} + +CoreSimulatorSupport::Device::Device (id d) : + m_dev (d), + m_dev_type (), + m_dev_runtime () +{ +} + +CoreSimulatorSupport::Device::operator bool () +{ + return m_dev != nil; +} + +CoreSimulatorSupport::Device::State +CoreSimulatorSupport::Device::GetState () +{ + return (State)([m_dev state]); +} + +CoreSimulatorSupport::ModelIdentifier::ModelIdentifier (const std::string& mi) : + m_family (), + m_versions () +{ + bool any = false; + bool first_digit = false; + unsigned int val = 0; + + for (char c : mi) + { + any = true; + if (::isdigit(c)) + { + if (!first_digit) + first_digit = true; + val = 10*val + (c - '0'); + } + else if (c == ',') + { + if (first_digit) + { + m_versions.push_back(val); + val = 0; + } + else + m_family.push_back(c); + } + else + { + if (first_digit) + { + m_family.clear(); + m_versions.clear(); + return; + } + else + { + m_family.push_back(c); + } + } + } + + if (first_digit) + m_versions.push_back(val); +} + +CoreSimulatorSupport::ModelIdentifier::ModelIdentifier () : +ModelIdentifier("") +{ +} + +CoreSimulatorSupport::OSVersion::OSVersion (const std::string& ver, + const std::string& build) : + m_versions (), + m_build (build) +{ + bool any = false; + unsigned int val = 0; + for (char c : ver) + { + if (c == '.') + { + m_versions.push_back(val); + val = 0; + } + else if (::isdigit(c)) + { + val = 10*val + (c - '0'); + any = true; + } + else + { + m_versions.clear(); + return; + } + } + if (any) + m_versions.push_back(val); +} + +CoreSimulatorSupport::OSVersion::OSVersion () : + OSVersion("","") +{ +} + +CoreSimulatorSupport::ModelIdentifier +CoreSimulatorSupport::DeviceType::GetModelIdentifier () +{ + if (!m_model_identifier.hasValue()) + { + auto utf8_model_id = [[m_dev modelIdentifier] UTF8String]; + if (utf8_model_id && *utf8_model_id) + m_model_identifier = ModelIdentifier (utf8_model_id); + } + + if (m_model_identifier.hasValue()) + return m_model_identifier.getValue(); + else + return ModelIdentifier(); +} + +CoreSimulatorSupport::OSVersion +CoreSimulatorSupport::DeviceRuntime::GetVersion () +{ + if (!m_os_version.hasValue()) + { + auto utf8_ver_string = [[m_dev versionString] UTF8String]; + auto utf8_build_ver = [[m_dev buildVersionString] UTF8String]; + if (utf8_ver_string && *utf8_ver_string && + utf8_build_ver && *utf8_build_ver) + { + m_os_version = OSVersion(utf8_ver_string, utf8_build_ver); + } + } + + if (m_os_version.hasValue()) + return m_os_version.getValue(); + return OSVersion(); +} + +std::string +CoreSimulatorSupport::DeviceType::GetName () +{ + auto utf8_name = [[m_dev name] UTF8String]; + if (utf8_name) + return std::string(utf8_name); + return ""; +} + +std::string +CoreSimulatorSupport::Device::GetName () const +{ + auto utf8_name = [[m_dev name] UTF8String]; + if (utf8_name) + return std::string(utf8_name); + return ""; +} + +std::string +CoreSimulatorSupport::Device::GetUDID () const +{ + auto utf8_udid = [ [[m_dev UDID] UUIDString] UTF8String]; + if (utf8_udid) + return std::string(utf8_udid); + else + return std::string(); +} + +CoreSimulatorSupport::DeviceType +CoreSimulatorSupport::Device::GetDeviceType () +{ + if (!m_dev_type.hasValue()) + m_dev_type = DeviceType([m_dev deviceType]); + + return m_dev_type.getValue(); +} + +CoreSimulatorSupport::DeviceRuntime +CoreSimulatorSupport::Device::GetDeviceRuntime () +{ + if (!m_dev_runtime.hasValue()) + m_dev_runtime = DeviceRuntime([m_dev runtime]); + + return m_dev_runtime.getValue(); +} + +bool +CoreSimulatorSupport::operator > (const CoreSimulatorSupport::OSVersion& lhs, + const CoreSimulatorSupport::OSVersion& rhs) +{ + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l > r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::operator > (const CoreSimulatorSupport::ModelIdentifier& lhs, + const CoreSimulatorSupport::ModelIdentifier& rhs) +{ + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l > r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::operator < (const CoreSimulatorSupport::OSVersion& lhs, + const CoreSimulatorSupport::OSVersion& rhs) +{ + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l < r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::operator < (const CoreSimulatorSupport::ModelIdentifier& lhs, + const CoreSimulatorSupport::ModelIdentifier& rhs) +{ + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l < r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::operator == (const CoreSimulatorSupport::OSVersion& lhs, + const CoreSimulatorSupport::OSVersion& rhs) +{ + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return false; + } + return true; +} + +bool +CoreSimulatorSupport::operator == (const CoreSimulatorSupport::ModelIdentifier& lhs, + const CoreSimulatorSupport::ModelIdentifier& rhs) +{ + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return false; + } + return true; +} + +bool +CoreSimulatorSupport::operator != (const CoreSimulatorSupport::OSVersion& lhs, + const CoreSimulatorSupport::OSVersion& rhs) +{ + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::operator != (const CoreSimulatorSupport::ModelIdentifier& lhs, + const CoreSimulatorSupport::ModelIdentifier& rhs) +{ + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::Device::Boot (Error &err) +{ + if (m_dev == nil) + { + err.SetErrorString("no valid simulator instance"); + return false; + } + +#define kSimDeviceBootEnv @"env" /* An NSDictionary of "extra" environment key/values */ +#define kSimDeviceBootPersist @"persist" /* An NSNumber (boolean) indicating whether or not the session should outlive the calling process (default false) */ +#define kSimDeviceBootDisabledJobs @"disabled_jobs" /* An NSDictionary of NSStrings -> NSNumbers, each string is the name of a job, and the value is the corresponding state (true if disabled) */ + + NSDictionary *options = @{ + kSimDeviceBootPersist : @NO, + kSimDeviceBootDisabledJobs : @{@"com.apple.backboardd" : @YES} + }; + +#undef kSimDeviceBootEnv +#undef kSimDeviceBootPersist +#undef kSimDeviceBootDisabledJobs + + NSError* nserror; + if ([m_dev bootWithOptions:options error:&nserror]) + { + err.Clear(); + return true; + } + else + { + err.SetErrorString([[nserror description] UTF8String]); + return false; + } +} + +bool +CoreSimulatorSupport::Device::Shutdown (Error &err) +{ + NSError* nserror; + if ([m_dev shutdownWithError:&nserror]) + { + err.Clear(); + return true; + } + else + { + err.SetErrorString([[nserror description] UTF8String]); + return false; + } +} + + +static Error +HandleFileAction(ProcessLaunchInfo& launch_info, + NSMutableDictionary *options, + NSString *key, + const int fd, + File &file) +{ + Error error; + const FileAction *file_action = launch_info.GetFileActionForFD (fd); + if (file_action) + { + switch (file_action->GetAction()) + { + case FileAction::eFileActionNone: + break; + + case FileAction::eFileActionClose: + error.SetErrorStringWithFormat ("close file action for %i not supported", fd); + break; + + case FileAction::eFileActionDuplicate: + error.SetErrorStringWithFormat ("duplication file action for %i not supported", fd); + break; + + case FileAction::eFileActionOpen: + { + FileSpec file_spec = file_action->GetFileSpec(); + if (file_spec) + { + const int master_fd = launch_info.GetPTY().GetMasterFileDescriptor(); + if (master_fd != PseudoTerminal::invalid_fd) + { + // Check in case our file action open wants to open the slave + const char *slave_path = launch_info.GetPTY().GetSlaveName(NULL, 0); + if (slave_path) + { + FileSpec slave_spec(slave_path, false); + if (file_spec == slave_spec) + { + int slave_fd = launch_info.GetPTY().GetSlaveFileDescriptor(); + if (slave_fd == PseudoTerminal::invalid_fd) + slave_fd = launch_info.GetPTY().OpenSlave(O_RDWR, nullptr, 0); + if (slave_fd == PseudoTerminal::invalid_fd) + { + error.SetErrorStringWithFormat("unable to open slave pty '%s'", slave_path); + return error; // Failure + } + [options setValue:[NSNumber numberWithInteger:slave_fd] forKey:key]; + return error; // Success + } + } + } + Error posix_error; + int created_fd = open(file_spec.GetPath().c_str(), file_action->GetActionArgument(), S_IRUSR | S_IWUSR); + if (created_fd >= 0) + { + file.SetDescriptor(created_fd, true); + [options setValue:[NSNumber numberWithInteger:created_fd] forKey:key]; + return error; // Success + } + else + { + posix_error.SetErrorToErrno(); + error.SetErrorStringWithFormat("unable to open file '%s': %s", file_spec.GetPath().c_str(), posix_error.AsCString()); + } + } + } + break; + } + } + return error; // Success, no file action, nothing to do +} + +CoreSimulatorSupport::Process +CoreSimulatorSupport::Device::Spawn (ProcessLaunchInfo& launch_info) +{ +#define kSimDeviceSpawnEnvironment @"environment" /* An NSDictionary (NSStrings -> NSStrings) of environment key/values */ +#define kSimDeviceSpawnStdin @"stdin" /* An NSNumber corresponding to a fd */ +#define kSimDeviceSpawnStdout @"stdout" /* An NSNumber corresponding to a fd */ +#define kSimDeviceSpawnStderr @"stderr" /* An NSNumber corresponding to a fd */ +#define kSimDeviceSpawnArguments @"arguments" /* An NSArray of strings to use as the argv array. If not provided, path will be argv[0] */ +#define kSimDeviceSpawnWaitForDebugger @"wait_for_debugger" /* An NSNumber (bool) */ + + NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; + + if (launch_info.GetFlags().Test(lldb::eLaunchFlagDebug)) + [options setObject:@YES forKey:kSimDeviceSpawnWaitForDebugger]; + + if (launch_info.GetArguments().GetArgumentCount()) + { + const Args& args(launch_info.GetArguments()); + NSMutableArray *args_array = [[NSMutableArray alloc] init]; + for (size_t idx = 0; + idx < args.GetArgumentCount(); + idx++) + [args_array addObject:[NSString stringWithUTF8String:args.GetArgumentAtIndex(idx)]]; + + [options setObject:args_array forKey:kSimDeviceSpawnArguments]; + } + + if (launch_info.GetEnvironmentEntries().GetArgumentCount()) + { + const Args& envs(launch_info.GetEnvironmentEntries()); + NSMutableDictionary *env_dict = [[NSMutableDictionary alloc] init]; + for (size_t idx = 0; + idx < envs.GetArgumentCount(); + idx++) + { + llvm::StringRef arg_sr(envs.GetArgumentAtIndex(idx)); + auto first_eq = arg_sr.find('='); + if (first_eq == llvm::StringRef::npos) + continue; + llvm::StringRef key = arg_sr.substr(0, first_eq); + llvm::StringRef value = arg_sr.substr(first_eq+1); + + NSString *key_ns = [NSString stringWithUTF8String:key.str().c_str()]; + NSString *value_ns = [NSString stringWithUTF8String:value.str().c_str()]; + + [env_dict setValue:value_ns forKey:key_ns]; + } + + [options setObject:env_dict forKey:kSimDeviceSpawnEnvironment]; + } + + Error error; + File stdin_file; + File stdout_file; + File stderr_file; + error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdin, STDIN_FILENO, stdin_file); + + if (error.Fail()) + return CoreSimulatorSupport::Process(error); + + error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdout, STDOUT_FILENO, stdout_file); + + if (error.Fail()) + return CoreSimulatorSupport::Process(error); + + error = HandleFileAction(launch_info, options, kSimDeviceSpawnStderr, STDERR_FILENO, stderr_file); + + if (error.Fail()) + return CoreSimulatorSupport::Process(error); + +#undef kSimDeviceSpawnEnvironment +#undef kSimDeviceSpawnStdin +#undef kSimDeviceSpawnStdout +#undef kSimDeviceSpawnStderr +#undef kSimDeviceSpawnWaitForDebugger +#undef kSimDeviceSpawnArguments + + NSError* nserror; + + pid_t pid = [m_dev spawnWithPath: [NSString stringWithUTF8String: launch_info.GetExecutableFile().GetPath().c_str()] + options: options + terminationHandler: nil + error: &nserror]; + + + if (pid < 0) + { + const char* nserror_string = [[nserror description] UTF8String]; + error.SetErrorString(nserror_string ? nserror_string : "unable to launch"); + } + + return CoreSimulatorSupport::Process (pid, error); +} + +CoreSimulatorSupport::DeviceSet +CoreSimulatorSupport::DeviceSet::GetAllDevices () +{ + return DeviceSet([[NSClassFromString(@"SimDeviceSet") defaultSet] devices]); +} + +CoreSimulatorSupport::DeviceSet +CoreSimulatorSupport::DeviceSet::GetAvailableDevices () +{ + return GetAllDevices().GetDevicesIf( [] (Device d) -> bool { + return (d && d.GetDeviceType() && d.GetDeviceRuntime() && d.GetDeviceRuntime().IsAvailable()); + }); +} + +size_t +CoreSimulatorSupport::DeviceSet::GetNumDevices () +{ + return [m_dev count]; +} + +CoreSimulatorSupport::Device +CoreSimulatorSupport::DeviceSet::GetDeviceAtIndex (size_t idx) +{ + if (idx < GetNumDevices()) + return Device([m_dev objectAtIndex:idx]); + return Device(); +} + +CoreSimulatorSupport::DeviceSet +CoreSimulatorSupport::DeviceSet::GetDevicesIf (std::function<bool(CoreSimulatorSupport::Device)> f) +{ + NSMutableArray *array = [[NSMutableArray alloc] init]; + for (NSUInteger i = 0; + i < GetNumDevices(); + i++) + { + Device d(GetDeviceAtIndex(i)); + if (f(d)) + [array addObject:(id)d.m_dev]; + } + + return DeviceSet(array); +} + +void +CoreSimulatorSupport::DeviceSet::ForEach (std::function<bool(const Device &)> f) +{ + const size_t n = GetNumDevices(); + for (NSUInteger i = 0; i < n; ++i) + { + if (f(GetDeviceAtIndex(i)) == false) + break; + } +} + +CoreSimulatorSupport::DeviceSet +CoreSimulatorSupport::DeviceSet::GetDevices (CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) +{ + NSMutableArray *array = [[NSMutableArray alloc] init]; + const size_t n = GetNumDevices(); + for (NSUInteger i = 0; i < n; ++i) + { + Device d(GetDeviceAtIndex(i)); + if (d && d.GetDeviceType() && d.GetDeviceType().GetProductFamilyID() == dev_id) + [array addObject:(id)d.m_dev]; + } + + return DeviceSet(array); +} + +CoreSimulatorSupport::Device +CoreSimulatorSupport::DeviceSet::GetFanciest (CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) +{ + Device dev; + + for (NSUInteger i = 0; + i < GetNumDevices(); + i++) + { + Device d(GetDeviceAtIndex(i)); + if (d && d.GetDeviceType() && d.GetDeviceType().GetProductFamilyID() == dev_id) + { + if (!dev) + dev = d; + else + { + if ((d.GetDeviceType().GetModelIdentifier() > dev.GetDeviceType().GetModelIdentifier()) || + d.GetDeviceRuntime().GetVersion() > dev.GetDeviceRuntime().GetVersion()) + dev = d; + } + } + } + + return dev; +} diff --git a/source/Plugins/Platform/Makefile b/source/Plugins/Platform/Makefile new file mode 100644 index 000000000000..572b08644074 --- /dev/null +++ b/source/Plugins/Platform/Makefile @@ -0,0 +1,36 @@ +##===- source/Plugins/Platform/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 := ../../.. + +include $(LLDB_LEVEL)/../../Makefile.config + +PARALLEL_DIRS := gdb-server MacOSX Linux FreeBSD NetBSD POSIX Windows Kalimba Android + +# ifeq ($(HOST_OS),Darwin) +# DIRS += MacOSX +# endif +# +# ifeq ($(HOST_OS),Linux) +# DIRS += Linux +# endif +# +# ifeq ($(HOST_OS),FreeBSD) +# DIRS += FreeBSD +# endif +# +# ifeq ($(HOST_OS),GNU/kFreeBSD) +# DIRS += FreeBSD +# endif +# +# ifeq ($(HOST_OS),NetBSD) +# DIRS += NetBSD +# endif + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/NetBSD/CMakeLists.txt b/source/Plugins/Platform/NetBSD/CMakeLists.txt new file mode 100644 index 000000000000..c70b419b98b4 --- /dev/null +++ b/source/Plugins/Platform/NetBSD/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformNetBSD + PlatformNetBSD.cpp + ) diff --git a/source/Plugins/Platform/NetBSD/Makefile b/source/Plugins/Platform/NetBSD/Makefile new file mode 100644 index 000000000000..2c480bdbe8da --- /dev/null +++ b/source/Plugins/Platform/NetBSD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/NetBSD/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 := lldbPluginPlatformNetBSD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/POSIX/CMakeLists.txt b/source/Plugins/Platform/POSIX/CMakeLists.txt new file mode 100644 index 000000000000..c23e68155c2b --- /dev/null +++ b/source/Plugins/Platform/POSIX/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformPOSIX + PlatformPOSIX.cpp + ) diff --git a/source/Plugins/Platform/POSIX/Makefile b/source/Plugins/Platform/POSIX/Makefile new file mode 100644 index 000000000000..eca927720ba8 --- /dev/null +++ b/source/Plugins/Platform/POSIX/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/POSIX/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 := lldbPluginPlatformPOSIX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Windows/CMakeLists.txt b/source/Plugins/Platform/Windows/CMakeLists.txt new file mode 100644 index 000000000000..09fbc11d33f2 --- /dev/null +++ b/source/Plugins/Platform/Windows/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformWindows + PlatformWindows.cpp + ) diff --git a/source/Plugins/Platform/Windows/Makefile b/source/Plugins/Platform/Windows/Makefile new file mode 100644 index 000000000000..b78cd7bfd080 --- /dev/null +++ b/source/Plugins/Platform/Windows/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/Windows/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 := lldbPluginPlatformWindows +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Windows/PlatformWindows.cpp b/source/Plugins/Platform/Windows/PlatformWindows.cpp new file mode 100644 index 000000000000..4172f80176d3 --- /dev/null +++ b/source/Plugins/Platform/Windows/PlatformWindows.cpp @@ -0,0 +1,753 @@ +//===-- PlatformWindows.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformWindows.h" + +// C Includes +#include <stdio.h> +#if defined (_WIN32) +#include "lldb/Host/windows/windows.h" +#include <winsock2.h> +#endif + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +static uint32_t g_initialize_count = 0; + +namespace +{ + class SupportedArchList + { + public: + SupportedArchList() + { + AddArch(ArchSpec("i686-pc-windows")); + AddArch(HostInfo::GetArchitecture(HostInfo::eArchKindDefault)); + AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind32)); + AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind64)); + AddArch(ArchSpec("i386-pc-windows")); + } + + size_t Count() const { return m_archs.size(); } + + const ArchSpec& operator[](int idx) { return m_archs[idx]; } + + private: + void AddArch(const ArchSpec& spec) + { + auto iter = std::find_if( + m_archs.begin(), m_archs.end(), + [spec](const ArchSpec& rhs) { return spec.IsExactMatch(rhs); }); + if (iter != m_archs.end()) + return; + if (spec.IsValid()) + m_archs.push_back(spec); + } + + std::vector<ArchSpec> m_archs; + }; +} // anonymous namespace + +PlatformSP +PlatformWindows::CreateInstance (bool force, const lldb_private::ArchSpec *arch) +{ + // The only time we create an instance is when we are creating a remote + // windows platform + const bool is_host = false; + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::PC: + create = true; + break; + + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; + + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Win32: + break; + + case llvm::Triple::UnknownOS: + create = arch->TripleOSWasSpecified(); + break; + + default: + create = false; + break; + } + } + } + if (create) + return PlatformSP(new PlatformWindows (is_host)); + return PlatformSP(); +} + +lldb_private::ConstString +PlatformWindows::GetPluginNameStatic(bool is_host) +{ + if (is_host) + { + static ConstString g_host_name(Platform::GetHostPlatformName ()); + return g_host_name; + } + else + { + static ConstString g_remote_name("remote-windows"); + return g_remote_name; + } +} + +const char * +PlatformWindows::GetPluginDescriptionStatic(bool is_host) +{ + return is_host ? + "Local Windows user platform plug-in." : + "Remote Windows user platform plug-in."; +} + +lldb_private::ConstString +PlatformWindows::GetPluginName() +{ + return GetPluginNameStatic(IsHost()); +} + +void +PlatformWindows::Initialize() +{ + Platform::Initialize (); + + if (g_initialize_count++ == 0) + { +#if defined (_WIN32) + WSADATA dummy; + WSAStartup(MAKEWORD(2,2), &dummy); + // Force a host flag to true for the default platform object. + PlatformSP default_platform_sp (new PlatformWindows(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform (default_platform_sp); +#endif + PluginManager::RegisterPlugin(PlatformWindows::GetPluginNameStatic(false), + PlatformWindows::GetPluginDescriptionStatic(false), + PlatformWindows::CreateInstance); + } +} + +void +PlatformWindows::Terminate( void ) +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { +#ifdef _WIN32 + WSACleanup(); +#endif + PluginManager::UnregisterPlugin (PlatformWindows::CreateInstance); + } + } + + Platform::Terminate (); +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformWindows::PlatformWindows (bool is_host) : + Platform(is_host) +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformWindows::~PlatformWindows() = default; + +bool +PlatformWindows::GetModuleSpec (const FileSpec& module_file_spec, + const ArchSpec& arch, + ModuleSpec &module_spec) +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetModuleSpec (module_file_spec, arch, module_spec); + + return Platform::GetModuleSpec (module_file_spec, arch, module_spec); +} + +Error +PlatformWindows::ResolveExecutable (const ModuleSpec &ms, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + char exe_path[PATH_MAX]; + ModuleSpec resolved_module_spec(ms); + + if (IsHost()) + { + // if we cant resolve the executable loation based on the current path variables + if (!resolved_module_spec.GetFileSpec().Exists()) + { + resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); + resolved_module_spec.GetFileSpec().SetFile(exe_path, true); + } + + if (!resolved_module_spec.GetFileSpec().Exists()) + resolved_module_spec.GetFileSpec().ResolveExecutableLocation (); + + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + { + ms.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat("unable to find executable for '%s'", exe_path); + } + } + else + { + if (m_remote_platform_sp) + { + error = GetCachedExecutable (resolved_module_spec, exe_module_sp, nullptr, *m_remote_platform_sp); + } + else + { + // We may connect to a process and use the provided executable (Don't use local $PATH). + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", exe_path); + } + } + + if (error.Success()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule(resolved_module_spec, + exe_module_sp, + nullptr, + nullptr, + nullptr); + + if (!exe_module_sp || exe_module_sp->GetObjectFile() == nullptr) + { + exe_module_sp.reset(); + error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + } + else + { + // No valid architecture was specified, ask the platform for + // the architectures that we should be using (in the correct order) + // and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule(resolved_module_spec, + exe_module_sp, + nullptr, + nullptr, + nullptr); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + } + + return error; +} + +size_t +PlatformWindows::GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite *bp_site) +{ + ArchSpec arch = target.GetArchitecture(); + const uint8_t *trap_opcode = nullptr; + size_t trap_opcode_size = 0; + + switch (arch.GetMachine()) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + { + static const uint8_t g_i386_opcode[] = { 0xCC }; + trap_opcode = g_i386_opcode; + trap_opcode_size = sizeof(g_i386_opcode); + } + break; + + case llvm::Triple::hexagon: + { + static const uint8_t g_hex_opcode[] = { 0x0c, 0xdb, 0x00, 0x54 }; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } + break; + default: + llvm_unreachable("Unhandled architecture in PlatformWindows::GetSoftwareBreakpointTrapOpcode()"); + break; + } + + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + + return 0; +} + +bool +PlatformWindows::GetRemoteOSVersion () +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetOSVersion (m_major_os_version, + m_minor_os_version, + m_update_os_version); + return false; +} + +bool +PlatformWindows::GetRemoteOSBuildString (std::string &s) +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteOSBuildString (s); + s.clear(); + return false; +} + +bool +PlatformWindows::GetRemoteOSKernelDescription (std::string &s) +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteOSKernelDescription (s); + s.clear(); + return false; +} + +// Remote Platform subclasses need to override this function +ArchSpec +PlatformWindows::GetRemoteSystemArchitecture () +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteSystemArchitecture (); + return ArchSpec(); +} + +const char * +PlatformWindows::GetHostname () +{ + if (IsHost()) + return Platform::GetHostname(); + + if (m_remote_platform_sp) + return m_remote_platform_sp->GetHostname (); + return nullptr; +} + +bool +PlatformWindows::IsConnected () const +{ + if (IsHost()) + return true; + else if (m_remote_platform_sp) + return m_remote_platform_sp->IsConnected(); + return false; +} + +Error +PlatformWindows::ConnectRemote (Args& args) +{ + Error error; + if (IsHost()) + { + error.SetErrorStringWithFormat ("can't connect to the host platform '%s', always connected", GetPluginName().AsCString() ); + } + else + { + if (!m_remote_platform_sp) + m_remote_platform_sp = Platform::Create (ConstString("remote-gdb-server"), error); + + if (m_remote_platform_sp) + { + if (error.Success()) + { + if (m_remote_platform_sp) + { + error = m_remote_platform_sp->ConnectRemote (args); + } + else + { + error.SetErrorString ("\"platform connect\" takes a single argument: <connect-url>"); + } + } + } + else + error.SetErrorString ("failed to create a 'remote-gdb-server' platform"); + + if (error.Fail()) + m_remote_platform_sp.reset(); + } + + return error; +} + +Error +PlatformWindows::DisconnectRemote () +{ + Error error; + + if (IsHost()) + { + error.SetErrorStringWithFormat ("can't disconnect from the host platform '%s', always connected", GetPluginName().AsCString() ); + } + else + { + if (m_remote_platform_sp) + error = m_remote_platform_sp->DisconnectRemote (); + else + error.SetErrorString ("the platform is not currently connected"); + } + return error; +} + +bool +PlatformWindows::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + bool success = false; + if (IsHost()) + { + success = Platform::GetProcessInfo (pid, process_info); + } + else if (m_remote_platform_sp) + { + success = m_remote_platform_sp->GetProcessInfo (pid, process_info); + } + return success; +} + +uint32_t +PlatformWindows::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + uint32_t match_count = 0; + if (IsHost()) + { + // Let the base class figure out the host details + match_count = Platform::FindProcesses (match_info, process_infos); + } + else + { + // If we are remote, we can only return results if we are connected + if (m_remote_platform_sp) + match_count = m_remote_platform_sp->FindProcesses (match_info, process_infos); + } + return match_count; +} + +Error +PlatformWindows::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + if (IsHost()) + { + error = Platform::LaunchProcess (launch_info); + } + else + { + if (m_remote_platform_sp) + error = m_remote_platform_sp->LaunchProcess (launch_info); + else + error.SetErrorString ("the platform is not currently connected"); + } + return error; +} + +ProcessSP +PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, Error &error) +{ + // Windows has special considerations that must be followed when launching or attaching to a process. The + // key requirement is that when launching or attaching to a process, you must do it from the same the thread + // that will go into a permanent loop which will then receive debug events from the process. In particular, + // this means we can't use any of LLDB's generic mechanisms to do it for us, because it doesn't have the + // special knowledge required for setting up the background thread or passing the right flags. + // + // Another problem is that that LLDB's standard model for debugging a process is to first launch it, have + // it stop at the entry point, and then attach to it. In Windows this doesn't quite work, you have to + // specify as an argument to CreateProcess() that you're going to debug the process. So we override DebugProcess + // here to handle this. Launch operations go directly to the process plugin, and attach operations almost go + // directly to the process plugin (but we hijack the events first). In essence, we encapsulate all the logic + // of Launching and Attaching in the process plugin, and PlatformWindows::DebugProcess is just a pass-through + // to get to the process plugin. + + if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) + { + // This is a process attach. Don't need to launch anything. + ProcessAttachInfo attach_info(launch_info); + return Attach(attach_info, debugger, target, error); + } + else + { + ProcessSP process_sp = target->CreateProcess(launch_info.GetListenerForProcess(debugger), + launch_info.GetProcessPluginName(), + nullptr); + + // We need to launch and attach to the process. + launch_info.GetFlags().Set(eLaunchFlagDebug); + if (process_sp) + error = process_sp->Launch(launch_info); + + return process_sp; + } +} + +lldb::ProcessSP +PlatformWindows::Attach(ProcessAttachInfo &attach_info, + Debugger &debugger, + Target *target, + Error &error) +{ + error.Clear(); + lldb::ProcessSP process_sp; + if (!IsHost()) + { + if (m_remote_platform_sp) + process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, error); + else + error.SetErrorString ("the platform is not currently connected"); + return process_sp; + } + + if (target == nullptr) + { + TargetSP new_target_sp; + FileSpec emptyFileSpec; + ArchSpec emptyArchSpec; + + error = debugger.GetTargetList().CreateTarget(debugger, + nullptr, + nullptr, + false, + nullptr, + new_target_sp); + target = new_target_sp.get(); + } + + if (!target || error.Fail()) + return process_sp; + + debugger.GetTargetList().SetSelectedTarget(target); + + const char *plugin_name = attach_info.GetProcessPluginName(); + process_sp = target->CreateProcess(attach_info.GetListenerForProcess(debugger), plugin_name, nullptr); + + process_sp->HijackProcessEvents(attach_info.GetHijackListener().get()); + if (process_sp) + error = process_sp->Attach (attach_info); + + return process_sp; +} + +const char * +PlatformWindows::GetUserName (uint32_t uid) +{ + // Check the cache in Platform in case we have already looked this uid up + const char *user_name = Platform::GetUserName(uid); + if (user_name) + return user_name; + + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetUserName(uid); + return nullptr; +} + +const char * +PlatformWindows::GetGroupName (uint32_t gid) +{ + const char *group_name = Platform::GetGroupName(gid); + if (group_name) + return group_name; + + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetGroupName(gid); + return nullptr; +} + +Error +PlatformWindows::GetFileWithUUID (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + if (IsRemote()) + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetFileWithUUID (platform_file, uuid_ptr, local_file); + } + + // Default to the local case + local_file = platform_file; + return Error(); +} + +Error +PlatformWindows::GetSharedModule (const ModuleSpec &module_spec, + Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + Error error; + module_sp.reset(); + + if (IsRemote()) + { + // If we have a remote platform always, let it try and locate + // the shared module first. + if (m_remote_platform_sp) + { + error = m_remote_platform_sp->GetSharedModule (module_spec, + process, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr); + } + } + + if (!module_sp) + { + // Fall back to the local platform and find the file locally + error = Platform::GetSharedModule (module_spec, + process, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr); + } + if (module_sp) + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + return error; +} + +bool +PlatformWindows::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + static SupportedArchList architectures; + + if (idx >= architectures.Count()) + return false; + arch = architectures[idx]; + return true; +} + +void +PlatformWindows::GetStatus (Stream &strm) +{ + Platform::GetStatus(strm); + +#ifdef _WIN32 + uint32_t major; + uint32_t minor; + uint32_t update; + if (!HostInfo::GetOSVersion(major, minor, update)) + { + strm << "Windows"; + return; + } + + strm << "Host: Windows " << major + << '.' << minor + << " Build: " << update << '\n'; +#endif +} + +bool +PlatformWindows::CanDebugProcess() +{ + return true; +} + +size_t +PlatformWindows::GetEnvironment(StringList &env) +{ + if (IsRemote()) + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetEnvironment(env); + return 0; + } + + return Host::GetEnvironment(env); +} + +ConstString +PlatformWindows::GetFullNameForDylib (ConstString basename) +{ + if (basename.IsEmpty()) + return basename; + + StreamString stream; + stream.Printf("%s.dll", basename.GetCString()); + return ConstString(stream.GetData()); +} diff --git a/source/Plugins/Platform/Windows/PlatformWindows.h b/source/Plugins/Platform/Windows/PlatformWindows.h new file mode 100644 index 000000000000..e9a04b4cc33d --- /dev/null +++ b/source/Plugins/Platform/Windows/PlatformWindows.h @@ -0,0 +1,169 @@ +//===-- PlatformWindows.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformWindows_h_ +#define liblldb_PlatformWindows_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Platform.h" + +namespace lldb_private +{ + +class PlatformWindows : public Platform +{ +public: + PlatformWindows(bool is_host); + + ~PlatformWindows() override; + + static void + Initialize(); + + static void + Terminate(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static lldb_private::ConstString + GetPluginNameStatic(bool is_host); + + static const char * + GetPluginDescriptionStatic(bool is_host); + + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + bool + GetModuleSpec (const lldb_private::FileSpec& module_file_spec, + const lldb_private::ArchSpec& arch, + lldb_private::ModuleSpec &module_spec) override; + + Error + ResolveExecutable(const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription() override + { + return GetPluginDescriptionStatic(IsHost()); + } + + size_t + GetSoftwareBreakpointTrapOpcode(lldb_private::Target &target, + lldb_private::BreakpointSite *bp_site) override; + + bool + GetRemoteOSVersion() override; + + bool + GetRemoteOSBuildString(std::string &s) override; + + bool + GetRemoteOSKernelDescription(std::string &s) override; + + // Remote Platform subclasses need to override this function + lldb_private::ArchSpec + GetRemoteSystemArchitecture() override; + + bool + IsConnected() const override; + + lldb_private::Error + ConnectRemote(lldb_private::Args& args) override; + + lldb_private::Error + DisconnectRemote() override; + + const char * + GetHostname() override; + + const char * + GetUserName(uint32_t uid) override; + + const char * + GetGroupName(uint32_t gid) override; + + bool + GetProcessInfo(lldb::pid_t pid, + lldb_private::ProcessInstanceInfo &proc_info) override; + + uint32_t + FindProcesses(const lldb_private::ProcessInstanceInfoMatch &match_info, + lldb_private::ProcessInstanceInfoList &process_infos) override; + + lldb_private::Error + LaunchProcess(lldb_private::ProcessLaunchInfo &launch_info) override; + + lldb::ProcessSP + DebugProcess(lldb_private::ProcessLaunchInfo &launch_info, lldb_private::Debugger &debugger, + lldb_private::Target *target, lldb_private::Error &error) override; + + lldb::ProcessSP + Attach(lldb_private::ProcessAttachInfo &attach_info, lldb_private::Debugger &debugger, + lldb_private::Target *target, lldb_private::Error &error) override; + + lldb_private::Error + GetFileWithUUID(const lldb_private::FileSpec &platform_file, + const lldb_private::UUID* uuid, lldb_private::FileSpec &local_file) override; + + lldb_private::Error + GetSharedModule(const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + bool + GetSupportedArchitectureAtIndex(uint32_t idx, lldb_private::ArchSpec &arch) override; + + void + GetStatus(lldb_private::Stream &strm) override; + + bool CanDebugProcess() override; + + size_t GetEnvironment(StringList &env) override; + + // FIXME not sure what the _sigtramp equivalent would be on this platform + void + CalculateTrapHandlerSymbolNames () override + { + } + + ConstString + GetFullNameForDylib (ConstString basename) override; + +protected: + lldb::PlatformSP m_remote_platform_sp; + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformWindows); +}; + +} // namespace lldb_private + +#endif // liblldb_PlatformWindows_h_ diff --git a/source/Plugins/Platform/gdb-server/CMakeLists.txt b/source/Plugins/Platform/gdb-server/CMakeLists.txt new file mode 100644 index 000000000000..b0b669e5b8b7 --- /dev/null +++ b/source/Plugins/Platform/gdb-server/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformGDB + PlatformRemoteGDBServer.cpp + ) diff --git a/source/Plugins/Platform/gdb-server/Makefile b/source/Plugins/Platform/gdb-server/Makefile new file mode 100644 index 000000000000..c56613b2a653 --- /dev/null +++ b/source/Plugins/Platform/gdb-server/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/gdb-server/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 := lldbPluginPlatformGDB +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/CMakeLists.txt b/source/Plugins/Process/CMakeLists.txt new file mode 100644 index 000000000000..d15df94414b3 --- /dev/null +++ b/source/Plugins/Process/CMakeLists.txt @@ -0,0 +1,19 @@ +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + add_subdirectory(Linux) + add_subdirectory(POSIX) +elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + add_subdirectory(FreeBSD) + add_subdirectory(POSIX) +elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + add_subdirectory(POSIX) +elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") + add_subdirectory(Windows/Live) + add_subdirectory(Windows/MiniDump) + add_subdirectory(Windows/Common) +elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_subdirectory(MacOSX-Kernel) +endif() +add_subdirectory(gdb-remote) +add_subdirectory(Utility) +add_subdirectory(mach-core) +add_subdirectory(elf-core) diff --git a/source/Plugins/Process/FreeBSD/CMakeLists.txt b/source/Plugins/Process/FreeBSD/CMakeLists.txt new file mode 100644 index 000000000000..c0e3374fef89 --- /dev/null +++ b/source/Plugins/Process/FreeBSD/CMakeLists.txt @@ -0,0 +1,16 @@ +include_directories(.) +include_directories(../POSIX) +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessFreeBSD + ProcessFreeBSD.cpp + FreeBSDThread.cpp + ProcessMonitor.cpp + + POSIXStopInfo.cpp + RegisterContextPOSIXProcessMonitor_arm.cpp + RegisterContextPOSIXProcessMonitor_arm64.cpp + RegisterContextPOSIXProcessMonitor_powerpc.cpp + RegisterContextPOSIXProcessMonitor_x86.cpp + RegisterContextPOSIXProcessMonitor_mips64.cpp + ) diff --git a/source/Plugins/Process/FreeBSD/Makefile b/source/Plugins/Process/FreeBSD/Makefile new file mode 100644 index 000000000000..7f546540e556 --- /dev/null +++ b/source/Plugins/Process/FreeBSD/Makefile @@ -0,0 +1,17 @@ +##===- source/Plugins/Process/FreeBSD/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 := lldbPluginProcessFreeBSD +BUILD_ARCHIVE = 1 + +# Extend the include path so we may locate UnwindLLDB.h +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h index 3cc46f489875..5f9365418d7a 100644 --- a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h +++ b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h @@ -88,7 +88,7 @@ public: DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info) override; lldb_private::Error - DoLaunch (lldb_private::Module *exe_module, + DoLaunch (lldb_private::Module *exe_module, lldb_private::ProcessLaunchInfo &launch_info) override; void @@ -160,7 +160,7 @@ public: UpdateThreadListIfNeeded(); bool - UpdateThreadList(lldb_private::ThreadList &old_thread_list, + UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override; virtual lldb::ByteOrder diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp index ceb527b61d80..cd016fbd4b8c 100644 --- a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp +++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp @@ -317,7 +317,7 @@ ReadRegOperation::Execute(ProcessMonitor *monitor) else if (m_size == sizeof(uint64_t)) m_value = *(uint64_t *)(((caddr_t)®s) + m_offset); else - memcpy(&m_value, (((caddr_t)®s) + m_offset), m_size); + memcpy((void *)&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } } @@ -393,7 +393,7 @@ ReadDebugRegOperation::Execute(ProcessMonitor *monitor) if (m_size == sizeof(uintptr_t)) m_value = *(uintptr_t *)(((caddr_t)®s) + m_offset); else - memcpy(&m_value, (((caddr_t)®s) + m_offset), m_size); + memcpy((void *)&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } } diff --git a/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp index 012f4b6e1555..a1a0cab82a15 100644 --- a/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp @@ -260,9 +260,7 @@ RegisterContextPOSIXProcessMonitor_arm64::HardwareSingleStep(bool enable) bool RegisterContextPOSIXProcessMonitor_arm64::UpdateAfterBreakpoint() { - lldb::addr_t pc; - - if ((pc = GetPC()) == LLDB_INVALID_ADDRESS) + if (GetPC() == LLDB_INVALID_ADDRESS) return false; return true; diff --git a/source/Plugins/Process/Linux/CMakeLists.txt b/source/Plugins/Process/Linux/CMakeLists.txt new file mode 100644 index 000000000000..80de8413d209 --- /dev/null +++ b/source/Plugins/Process/Linux/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories(.) +include_directories(../POSIX) +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessLinux + NativeProcessLinux.cpp + NativeRegisterContextLinux.cpp + NativeRegisterContextLinux_arm.cpp + NativeRegisterContextLinux_arm64.cpp + NativeRegisterContextLinux_x86_64.cpp + NativeRegisterContextLinux_mips64.cpp + NativeThreadLinux.cpp + ProcFileReader.cpp + ) diff --git a/source/Plugins/Process/Linux/Makefile b/source/Plugins/Process/Linux/Makefile new file mode 100644 index 000000000000..239e94d608e9 --- /dev/null +++ b/source/Plugins/Process/Linux/Makefile @@ -0,0 +1,17 @@ +##===- source/Plugins/Process/Linux/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 := lldbPluginProcessLinux +BUILD_ARCHIVE = 1 + +# Extend the include path so we may locate UnwindLLDB.h +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/source/Plugins/Process/Linux/NativeProcessLinux.cpp new file mode 100644 index 000000000000..87c76f57830c --- /dev/null +++ b/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -0,0 +1,3189 @@ +//===-- NativeProcessLinux.cpp -------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NativeProcessLinux.h" + +// C Includes +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <unistd.h> + +// C++ Includes +#include <fstream> +#include <mutex> +#include <sstream> +#include <string> +#include <unordered_map> + +// Other libraries and framework includes +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/State.h" +#include "lldb/Host/common/NativeBreakpoint.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/PseudoTerminal.h" +#include "lldb/Utility/StringExtractor.h" + +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "NativeThreadLinux.h" +#include "ProcFileReader.h" +#include "Procfs.h" + +// System includes - They have to be included after framework includes because they define some +// macros which collide with variable names in other modules +#include <linux/unistd.h> +#include <sys/socket.h> + +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/user.h> +#include <sys/wait.h> + +#include "lldb/Host/linux/Personality.h" +#include "lldb/Host/linux/Ptrace.h" +#include "lldb/Host/linux/Signalfd.h" +#include "lldb/Host/linux/Uio.h" +#include "lldb/Host/android/Android.h" + +#define LLDB_PERSONALITY_GET_CURRENT_SETTINGS 0xffffffff + +// Support hardware breakpoints in case it has not been defined +#ifndef TRAP_HWBKPT + #define TRAP_HWBKPT 4 +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_linux; +using namespace llvm; + +// Private bits we only need internally. + +static bool ProcessVmReadvSupported() +{ + static bool is_supported; + static std::once_flag flag; + + std::call_once(flag, [] { + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + uint32_t source = 0x47424742; + uint32_t dest = 0; + + struct iovec local, remote; + remote.iov_base = &source; + local.iov_base = &dest; + remote.iov_len = local.iov_len = sizeof source; + + // We shall try if cross-process-memory reads work by attempting to read a value from our own process. + ssize_t res = process_vm_readv(getpid(), &local, 1, &remote, 1, 0); + is_supported = (res == sizeof(source) && source == dest); + if (log) + { + if (is_supported) + log->Printf("%s: Detected kernel support for process_vm_readv syscall. Fast memory reads enabled.", + __FUNCTION__); + else + log->Printf("%s: syscall process_vm_readv failed (error: %s). Fast memory reads disabled.", + __FUNCTION__, strerror(errno)); + } + }); + + return is_supported; +} + +namespace +{ + Error + ResolveProcessArchitecture (lldb::pid_t pid, Platform &platform, ArchSpec &arch) + { + // Grab process info for the running process. + ProcessInstanceInfo process_info; + if (!platform.GetProcessInfo (pid, process_info)) + return Error("failed to get process info"); + + // Resolve the executable module. + ModuleSP exe_module_sp; + ModuleSpec exe_module_spec(process_info.GetExecutableFile(), process_info.GetArchitecture()); + FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths ()); + Error error = platform.ResolveExecutable( + exe_module_spec, + exe_module_sp, + executable_search_paths.GetSize () ? &executable_search_paths : NULL); + + if (!error.Success ()) + return error; + + // Check if we've got our architecture from the exe_module. + arch = exe_module_sp->GetArchitecture (); + if (arch.IsValid ()) + return Error(); + else + return Error("failed to retrieve a valid architecture from the exe module"); + } + + void + DisplayBytes (StreamString &s, void *bytes, uint32_t count) + { + uint8_t *ptr = (uint8_t *)bytes; + const uint32_t loop_count = std::min<uint32_t>(DEBUG_PTRACE_MAXBYTES, count); + for(uint32_t i=0; i<loop_count; i++) + { + s.Printf ("[%x]", *ptr); + ptr++; + } + } + + void + PtraceDisplayBytes(int &req, void *data, size_t data_size) + { + StreamString buf; + Log *verbose_log (ProcessPOSIXLog::GetLogIfAllCategoriesSet ( + POSIX_LOG_PTRACE | POSIX_LOG_VERBOSE)); + + if (verbose_log) + { + switch(req) + { + case PTRACE_POKETEXT: + { + DisplayBytes(buf, &data, 8); + verbose_log->Printf("PTRACE_POKETEXT %s", buf.GetData()); + break; + } + case PTRACE_POKEDATA: + { + DisplayBytes(buf, &data, 8); + verbose_log->Printf("PTRACE_POKEDATA %s", buf.GetData()); + break; + } + case PTRACE_POKEUSER: + { + DisplayBytes(buf, &data, 8); + verbose_log->Printf("PTRACE_POKEUSER %s", buf.GetData()); + break; + } + case PTRACE_SETREGS: + { + DisplayBytes(buf, data, data_size); + verbose_log->Printf("PTRACE_SETREGS %s", buf.GetData()); + break; + } + case PTRACE_SETFPREGS: + { + DisplayBytes(buf, data, data_size); + verbose_log->Printf("PTRACE_SETFPREGS %s", buf.GetData()); + break; + } + case PTRACE_SETSIGINFO: + { + DisplayBytes(buf, data, sizeof(siginfo_t)); + verbose_log->Printf("PTRACE_SETSIGINFO %s", buf.GetData()); + break; + } + case PTRACE_SETREGSET: + { + // Extract iov_base from data, which is a pointer to the struct IOVEC + DisplayBytes(buf, *(void **)data, data_size); + verbose_log->Printf("PTRACE_SETREGSET %s", buf.GetData()); + break; + } + default: + { + } + } + } + } + + static constexpr unsigned k_ptrace_word_size = sizeof(void*); + static_assert(sizeof(long) >= k_ptrace_word_size, "Size of long must be larger than ptrace word size"); +} // end of anonymous namespace + +// Simple helper function to ensure flags are enabled on the given file +// descriptor. +static Error +EnsureFDFlags(int fd, int flags) +{ + Error error; + + int status = fcntl(fd, F_GETFL); + if (status == -1) + { + error.SetErrorToErrno(); + return error; + } + + if (fcntl(fd, F_SETFL, status | flags) == -1) + { + error.SetErrorToErrno(); + return error; + } + + return error; +} + +NativeProcessLinux::LaunchArgs::LaunchArgs(Module *module, + char const **argv, + char const **envp, + const FileSpec &stdin_file_spec, + const FileSpec &stdout_file_spec, + const FileSpec &stderr_file_spec, + const FileSpec &working_dir, + const ProcessLaunchInfo &launch_info) + : m_module(module), + m_argv(argv), + m_envp(envp), + m_stdin_file_spec(stdin_file_spec), + m_stdout_file_spec(stdout_file_spec), + m_stderr_file_spec(stderr_file_spec), + m_working_dir(working_dir), + m_launch_info(launch_info) +{ +} + +NativeProcessLinux::LaunchArgs::~LaunchArgs() +{ } + +// ----------------------------------------------------------------------------- +// Public Static Methods +// ----------------------------------------------------------------------------- + +Error +NativeProcessProtocol::Launch ( + ProcessLaunchInfo &launch_info, + NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &native_process_sp) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + lldb::ModuleSP exe_module_sp; + PlatformSP platform_sp (Platform::GetHostPlatform ()); + Error error = platform_sp->ResolveExecutable( + ModuleSpec(launch_info.GetExecutableFile(), launch_info.GetArchitecture()), + exe_module_sp, + nullptr); + + if (! error.Success()) + return error; + + // Verify the working directory is valid if one was specified. + FileSpec working_dir{launch_info.GetWorkingDirectory()}; + if (working_dir && + (!working_dir.ResolvePath() || + working_dir.GetFileType() != FileSpec::eFileTypeDirectory)) + { + error.SetErrorStringWithFormat ("No such file or directory: %s", + working_dir.GetCString()); + return error; + } + + const FileAction *file_action; + + // Default of empty will mean to use existing open file descriptors. + FileSpec stdin_file_spec{}; + FileSpec stdout_file_spec{}; + FileSpec stderr_file_spec{}; + + file_action = launch_info.GetFileActionForFD (STDIN_FILENO); + if (file_action) + stdin_file_spec = file_action->GetFileSpec(); + + file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); + if (file_action) + stdout_file_spec = file_action->GetFileSpec(); + + file_action = launch_info.GetFileActionForFD (STDERR_FILENO); + if (file_action) + stderr_file_spec = file_action->GetFileSpec(); + + if (log) + { + if (stdin_file_spec) + log->Printf ("NativeProcessLinux::%s setting STDIN to '%s'", + __FUNCTION__, stdin_file_spec.GetCString()); + else + log->Printf ("NativeProcessLinux::%s leaving STDIN as is", __FUNCTION__); + + if (stdout_file_spec) + log->Printf ("NativeProcessLinux::%s setting STDOUT to '%s'", + __FUNCTION__, stdout_file_spec.GetCString()); + else + log->Printf ("NativeProcessLinux::%s leaving STDOUT as is", __FUNCTION__); + + if (stderr_file_spec) + log->Printf ("NativeProcessLinux::%s setting STDERR to '%s'", + __FUNCTION__, stderr_file_spec.GetCString()); + else + log->Printf ("NativeProcessLinux::%s leaving STDERR as is", __FUNCTION__); + } + + // Create the NativeProcessLinux in launch mode. + native_process_sp.reset (new NativeProcessLinux ()); + + if (log) + { + int i = 0; + for (const char **args = launch_info.GetArguments ().GetConstArgumentVector (); *args; ++args, ++i) + { + log->Printf ("NativeProcessLinux::%s arg %d: \"%s\"", __FUNCTION__, i, *args ? *args : "nullptr"); + ++i; + } + } + + if (!native_process_sp->RegisterNativeDelegate (native_delegate)) + { + native_process_sp.reset (); + error.SetErrorStringWithFormat ("failed to register the native delegate"); + return error; + } + + std::static_pointer_cast<NativeProcessLinux> (native_process_sp)->LaunchInferior ( + mainloop, + exe_module_sp.get(), + launch_info.GetArguments ().GetConstArgumentVector (), + launch_info.GetEnvironmentEntries ().GetConstArgumentVector (), + stdin_file_spec, + stdout_file_spec, + stderr_file_spec, + working_dir, + launch_info, + error); + + if (error.Fail ()) + { + native_process_sp.reset (); + if (log) + log->Printf ("NativeProcessLinux::%s failed to launch process: %s", __FUNCTION__, error.AsCString ()); + return error; + } + + launch_info.SetProcessID (native_process_sp->GetID ()); + + return error; +} + +Error +NativeProcessProtocol::Attach ( + lldb::pid_t pid, + NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &native_process_sp) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log && log->GetMask ().Test (POSIX_LOG_VERBOSE)) + log->Printf ("NativeProcessLinux::%s(pid = %" PRIi64 ")", __FUNCTION__, pid); + + // Grab the current platform architecture. This should be Linux, + // since this code is only intended to run on a Linux host. + PlatformSP platform_sp (Platform::GetHostPlatform ()); + if (!platform_sp) + return Error("failed to get a valid default platform"); + + // Retrieve the architecture for the running process. + ArchSpec process_arch; + Error error = ResolveProcessArchitecture (pid, *platform_sp.get (), process_arch); + if (!error.Success ()) + return error; + + std::shared_ptr<NativeProcessLinux> native_process_linux_sp (new NativeProcessLinux ()); + + if (!native_process_linux_sp->RegisterNativeDelegate (native_delegate)) + { + error.SetErrorStringWithFormat ("failed to register the native delegate"); + return error; + } + + native_process_linux_sp->AttachToInferior (mainloop, pid, error); + if (!error.Success ()) + return error; + + native_process_sp = native_process_linux_sp; + return error; +} + +// ----------------------------------------------------------------------------- +// Public Instance Methods +// ----------------------------------------------------------------------------- + +NativeProcessLinux::NativeProcessLinux () : + NativeProcessProtocol (LLDB_INVALID_PROCESS_ID), + m_arch (), + m_supports_mem_region (eLazyBoolCalculate), + m_mem_region_cache (), + m_mem_region_cache_mutex(), + m_pending_notification_tid(LLDB_INVALID_THREAD_ID) +{ +} + +void +NativeProcessLinux::LaunchInferior ( + MainLoop &mainloop, + Module *module, + const char *argv[], + const char *envp[], + const FileSpec &stdin_file_spec, + const FileSpec &stdout_file_spec, + const FileSpec &stderr_file_spec, + const FileSpec &working_dir, + const ProcessLaunchInfo &launch_info, + Error &error) +{ + m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD, + [this] (MainLoopBase &) { SigchldHandler(); }, error); + if (! m_sigchld_handle) + return; + + if (module) + m_arch = module->GetArchitecture (); + + SetState (eStateLaunching); + + std::unique_ptr<LaunchArgs> args( + new LaunchArgs(module, argv, envp, + stdin_file_spec, + stdout_file_spec, + stderr_file_spec, + working_dir, + launch_info)); + + Launch(args.get(), error); +} + +void +NativeProcessLinux::AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ")", __FUNCTION__, pid); + + m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD, + [this] (MainLoopBase &) { SigchldHandler(); }, error); + if (! m_sigchld_handle) + return; + + // We can use the Host for everything except the ResolveExecutable portion. + PlatformSP platform_sp = Platform::GetHostPlatform (); + if (!platform_sp) + { + if (log) + log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 "): no default platform set", __FUNCTION__, pid); + error.SetErrorString ("no default platform available"); + return; + } + + // Gather info about the process. + ProcessInstanceInfo process_info; + if (!platform_sp->GetProcessInfo (pid, process_info)) + { + if (log) + log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 "): failed to get process info", __FUNCTION__, pid); + error.SetErrorString ("failed to get process info"); + return; + } + + // Resolve the executable module + ModuleSP exe_module_sp; + FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths()); + ModuleSpec exe_module_spec(process_info.GetExecutableFile(), process_info.GetArchitecture()); + error = platform_sp->ResolveExecutable(exe_module_spec, exe_module_sp, + executable_search_paths.GetSize() ? &executable_search_paths : NULL); + if (!error.Success()) + return; + + // Set the architecture to the exe architecture. + m_arch = exe_module_sp->GetArchitecture(); + if (log) + log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ") detected architecture %s", __FUNCTION__, pid, m_arch.GetArchitectureName ()); + + m_pid = pid; + SetState(eStateAttaching); + + Attach(pid, error); +} + +::pid_t +NativeProcessLinux::Launch(LaunchArgs *args, Error &error) +{ + assert (args && "null args"); + + const char **argv = args->m_argv; + const char **envp = args->m_envp; + const FileSpec working_dir = args->m_working_dir; + + lldb_utility::PseudoTerminal terminal; + const size_t err_len = 1024; + char err_str[err_len]; + lldb::pid_t pid; + + // Propagate the environment if one is not supplied. + if (envp == NULL || envp[0] == NULL) + envp = const_cast<const char **>(environ); + + if ((pid = terminal.Fork(err_str, err_len)) == static_cast<lldb::pid_t> (-1)) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Process fork failed: %s", err_str); + return -1; + } + + // Recognized child exit status codes. + enum { + ePtraceFailed = 1, + eDupStdinFailed, + eDupStdoutFailed, + eDupStderrFailed, + eChdirFailed, + eExecFailed, + eSetGidFailed, + eSetSigMaskFailed + }; + + // Child process. + if (pid == 0) + { + // First, make sure we disable all logging. If we are logging to stdout, our logs can be + // mistaken for inferior output. + Log::DisableAllLogChannels(nullptr); + // FIXME consider opening a pipe between parent/child and have this forked child + // send log info to parent re: launch status. + + // Start tracing this child that is about to exec. + error = PtraceWrapper(PTRACE_TRACEME, 0); + if (error.Fail()) + exit(ePtraceFailed); + + // terminal has already dupped the tty descriptors to stdin/out/err. + // This closes original fd from which they were copied (and avoids + // leaking descriptors to the debugged process. + terminal.CloseSlaveFileDescriptor(); + + // Do not inherit setgid powers. + if (setgid(getgid()) != 0) + exit(eSetGidFailed); + + // Attempt to have our own process group. + if (setpgid(0, 0) != 0) + { + // FIXME log that this failed. This is common. + // Don't allow this to prevent an inferior exec. + } + + // Dup file descriptors if needed. + if (args->m_stdin_file_spec) + if (!DupDescriptor(args->m_stdin_file_spec, STDIN_FILENO, O_RDONLY)) + exit(eDupStdinFailed); + + if (args->m_stdout_file_spec) + if (!DupDescriptor(args->m_stdout_file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) + exit(eDupStdoutFailed); + + if (args->m_stderr_file_spec) + if (!DupDescriptor(args->m_stderr_file_spec, STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) + exit(eDupStderrFailed); + + // Close everything besides stdin, stdout, and stderr that has no file + // action to avoid leaking + for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd) + if (!args->m_launch_info.GetFileActionForFD(fd)) + close(fd); + + // Change working directory + if (working_dir && 0 != ::chdir(working_dir.GetCString())) + exit(eChdirFailed); + + // Disable ASLR if requested. + if (args->m_launch_info.GetFlags ().Test (lldb::eLaunchFlagDisableASLR)) + { + const int old_personality = personality (LLDB_PERSONALITY_GET_CURRENT_SETTINGS); + if (old_personality == -1) + { + // Can't retrieve Linux personality. Cannot disable ASLR. + } + else + { + const int new_personality = personality (ADDR_NO_RANDOMIZE | old_personality); + if (new_personality == -1) + { + // Disabling ASLR failed. + } + else + { + // Disabling ASLR succeeded. + } + } + } + + // Clear the signal mask to prevent the child from being affected by + // any masking done by the parent. + sigset_t set; + if (sigemptyset(&set) != 0 || pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0) + exit(eSetSigMaskFailed); + + // Execute. We should never return... + execve(argv[0], + const_cast<char *const *>(argv), + const_cast<char *const *>(envp)); + + // ...unless exec fails. In which case we definitely need to end the child here. + exit(eExecFailed); + } + + // + // This is the parent code here. + // + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // Wait for the child process to trap on its call to execve. + ::pid_t wpid; + int status; + if ((wpid = waitpid(pid, &status, 0)) < 0) + { + error.SetErrorToErrno(); + if (log) + log->Printf ("NativeProcessLinux::%s waitpid for inferior failed with %s", + __FUNCTION__, error.AsCString ()); + + // Mark the inferior as invalid. + // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. + SetState (StateType::eStateInvalid); + + return -1; + } + else if (WIFEXITED(status)) + { + // open, dup or execve likely failed for some reason. + error.SetErrorToGenericError(); + switch (WEXITSTATUS(status)) + { + case ePtraceFailed: + error.SetErrorString("Child ptrace failed."); + break; + case eDupStdinFailed: + error.SetErrorString("Child open stdin failed."); + break; + case eDupStdoutFailed: + error.SetErrorString("Child open stdout failed."); + break; + case eDupStderrFailed: + error.SetErrorString("Child open stderr failed."); + break; + case eChdirFailed: + error.SetErrorString("Child failed to set working directory."); + break; + case eExecFailed: + error.SetErrorString("Child exec failed."); + break; + case eSetGidFailed: + error.SetErrorString("Child setgid failed."); + break; + case eSetSigMaskFailed: + error.SetErrorString("Child failed to set signal mask."); + break; + default: + error.SetErrorString("Child returned unknown exit status."); + break; + } + + if (log) + { + log->Printf ("NativeProcessLinux::%s inferior exited with status %d before issuing a STOP", + __FUNCTION__, + WEXITSTATUS(status)); + } + + // Mark the inferior as invalid. + // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. + SetState (StateType::eStateInvalid); + + return -1; + } + assert(WIFSTOPPED(status) && (wpid == static_cast< ::pid_t> (pid)) && + "Could not sync with inferior process."); + + if (log) + log->Printf ("NativeProcessLinux::%s inferior started, now in stopped state", __FUNCTION__); + + error = SetDefaultPtraceOpts(pid); + if (error.Fail()) + { + if (log) + log->Printf ("NativeProcessLinux::%s inferior failed to set default ptrace options: %s", + __FUNCTION__, error.AsCString ()); + + // Mark the inferior as invalid. + // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. + SetState (StateType::eStateInvalid); + + return -1; + } + + // Release the master terminal descriptor and pass it off to the + // NativeProcessLinux instance. Similarly stash the inferior pid. + m_terminal_fd = terminal.ReleaseMasterFileDescriptor(); + m_pid = pid; + + // Set the terminal fd to be in non blocking mode (it simplifies the + // implementation of ProcessLinux::GetSTDOUT to have a non-blocking + // descriptor to read from). + error = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); + if (error.Fail()) + { + if (log) + log->Printf ("NativeProcessLinux::%s inferior EnsureFDFlags failed for ensuring terminal O_NONBLOCK setting: %s", + __FUNCTION__, error.AsCString ()); + + // Mark the inferior as invalid. + // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. + SetState (StateType::eStateInvalid); + + return -1; + } + + if (log) + log->Printf ("NativeProcessLinux::%s() adding pid = %" PRIu64, __FUNCTION__, pid); + + NativeThreadLinuxSP thread_sp = AddThread(pid); + assert (thread_sp && "AddThread() returned a nullptr thread"); + thread_sp->SetStoppedBySignal(SIGSTOP); + ThreadWasCreated(*thread_sp); + + // Let our process instance know the thread has stopped. + SetCurrentThreadID (thread_sp->GetID ()); + SetState (StateType::eStateStopped); + + if (log) + { + if (error.Success ()) + { + log->Printf ("NativeProcessLinux::%s inferior launching succeeded", __FUNCTION__); + } + else + { + log->Printf ("NativeProcessLinux::%s inferior launching failed: %s", + __FUNCTION__, error.AsCString ()); + return -1; + } + } + return pid; +} + +::pid_t +NativeProcessLinux::Attach(lldb::pid_t pid, Error &error) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // Use a map to keep track of the threads which we have attached/need to attach. + Host::TidMap tids_to_attach; + if (pid <= 1) + { + error.SetErrorToGenericError(); + error.SetErrorString("Attaching to process 1 is not allowed."); + return -1; + } + + while (Host::FindProcessThreads(pid, tids_to_attach)) + { + for (Host::TidMap::iterator it = tids_to_attach.begin(); + it != tids_to_attach.end();) + { + if (it->second == false) + { + lldb::tid_t tid = it->first; + + // Attach to the requested process. + // An attach will cause the thread to stop with a SIGSTOP. + error = PtraceWrapper(PTRACE_ATTACH, tid); + if (error.Fail()) + { + // No such thread. The thread may have exited. + // More error handling may be needed. + if (error.GetError() == ESRCH) + { + it = tids_to_attach.erase(it); + continue; + } + else + return -1; + } + + int status; + // Need to use __WALL otherwise we receive an error with errno=ECHLD + // At this point we should have a thread stopped if waitpid succeeds. + if ((status = waitpid(tid, NULL, __WALL)) < 0) + { + // No such thread. The thread may have exited. + // More error handling may be needed. + if (errno == ESRCH) + { + it = tids_to_attach.erase(it); + continue; + } + else + { + error.SetErrorToErrno(); + return -1; + } + } + + error = SetDefaultPtraceOpts(tid); + if (error.Fail()) + return -1; + + if (log) + log->Printf ("NativeProcessLinux::%s() adding tid = %" PRIu64, __FUNCTION__, tid); + + it->second = true; + + // Create the thread, mark it as stopped. + NativeThreadLinuxSP thread_sp (AddThread(static_cast<lldb::tid_t>(tid))); + assert (thread_sp && "AddThread() returned a nullptr"); + + // This will notify this is a new thread and tell the system it is stopped. + thread_sp->SetStoppedBySignal(SIGSTOP); + ThreadWasCreated(*thread_sp); + SetCurrentThreadID (thread_sp->GetID ()); + } + + // move the loop forward + ++it; + } + } + + if (tids_to_attach.size() > 0) + { + m_pid = pid; + // Let our process instance know the thread has stopped. + SetState (StateType::eStateStopped); + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString("No such process."); + return -1; + } + + return pid; +} + +Error +NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) +{ + long ptrace_opts = 0; + + // Have the child raise an event on exit. This is used to keep the child in + // limbo until it is destroyed. + ptrace_opts |= PTRACE_O_TRACEEXIT; + + // Have the tracer trace threads which spawn in the inferior process. + // TODO: if we want to support tracing the inferiors' child, add the + // appropriate ptrace flags here (PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK) + ptrace_opts |= PTRACE_O_TRACECLONE; + + // Have the tracer notify us before execve returns + // (needed to disable legacy SIGTRAP generation) + ptrace_opts |= PTRACE_O_TRACEEXEC; + + return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void*)ptrace_opts); +} + +static ExitType convert_pid_status_to_exit_type (int status) +{ + if (WIFEXITED (status)) + return ExitType::eExitTypeExit; + else if (WIFSIGNALED (status)) + return ExitType::eExitTypeSignal; + else if (WIFSTOPPED (status)) + return ExitType::eExitTypeStop; + else + { + // We don't know what this is. + return ExitType::eExitTypeInvalid; + } +} + +static int convert_pid_status_to_return_code (int status) +{ + if (WIFEXITED (status)) + return WEXITSTATUS (status); + else if (WIFSIGNALED (status)) + return WTERMSIG (status); + else if (WIFSTOPPED (status)) + return WSTOPSIG (status); + else + { + // We don't know what this is. + return ExitType::eExitTypeInvalid; + } +} + +// Handles all waitpid events from the inferior process. +void +NativeProcessLinux::MonitorCallback(lldb::pid_t pid, + bool exited, + int signal, + int status) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // Certain activities differ based on whether the pid is the tid of the main thread. + const bool is_main_thread = (pid == GetID ()); + + // Handle when the thread exits. + if (exited) + { + if (log) + log->Printf ("NativeProcessLinux::%s() got exit signal(%d) , tid = %" PRIu64 " (%s main thread)", __FUNCTION__, signal, pid, is_main_thread ? "is" : "is not"); + + // This is a thread that exited. Ensure we're not tracking it anymore. + const bool thread_found = StopTrackingThread (pid); + + if (is_main_thread) + { + // We only set the exit status and notify the delegate if we haven't already set the process + // state to an exited state. We normally should have received a SIGTRAP | (PTRACE_EVENT_EXIT << 8) + // for the main thread. + const bool already_notified = (GetState() == StateType::eStateExited) || (GetState () == StateType::eStateCrashed); + if (!already_notified) + { + if (log) + log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " handling main thread exit (%s), expected exit state already set but state was %s instead, setting exit state now", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found", StateAsCString (GetState ())); + // The main thread exited. We're done monitoring. Report to delegate. + SetExitStatus (convert_pid_status_to_exit_type (status), convert_pid_status_to_return_code (status), nullptr, true); + + // Notify delegate that our process has exited. + SetState (StateType::eStateExited, true); + } + else + { + if (log) + log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " main thread now exited (%s)", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found"); + } + } + else + { + // Do we want to report to the delegate in this case? I think not. If this was an orderly + // thread exit, we would already have received the SIGTRAP | (PTRACE_EVENT_EXIT << 8) signal, + // and we would have done an all-stop then. + if (log) + log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " handling non-main thread exit (%s)", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found"); + } + return; + } + + siginfo_t info; + const auto info_err = GetSignalInfo(pid, &info); + auto thread_sp = GetThreadByID(pid); + + if (! thread_sp) + { + // Normally, the only situation when we cannot find the thread is if we have just + // received a new thread notification. This is indicated by GetSignalInfo() returning + // si_code == SI_USER and si_pid == 0 + if (log) + log->Printf("NativeProcessLinux::%s received notification about an unknown tid %" PRIu64 ".", __FUNCTION__, pid); + + if (info_err.Fail()) + { + if (log) + log->Printf("NativeProcessLinux::%s (tid %" PRIu64 ") GetSignalInfo failed (%s). Ingoring this notification.", __FUNCTION__, pid, info_err.AsCString()); + return; + } + + if (log && (info.si_code != SI_USER || info.si_pid != 0)) + log->Printf("NativeProcessLinux::%s (tid %" PRIu64 ") unexpected signal info (si_code: %d, si_pid: %d). Treating as a new thread notification anyway.", __FUNCTION__, pid, info.si_code, info.si_pid); + + auto thread_sp = AddThread(pid); + // Resume the newly created thread. + ResumeThread(*thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); + ThreadWasCreated(*thread_sp); + return; + } + + // Get details on the signal raised. + if (info_err.Success()) + { + // We have retrieved the signal info. Dispatch appropriately. + if (info.si_signo == SIGTRAP) + MonitorSIGTRAP(info, *thread_sp); + else + MonitorSignal(info, *thread_sp, exited); + } + else + { + if (info_err.GetError() == EINVAL) + { + // This is a group stop reception for this tid. + // We can reach here if we reinject SIGSTOP, SIGSTP, SIGTTIN or SIGTTOU into the + // tracee, triggering the group-stop mechanism. Normally receiving these would stop + // the process, pending a SIGCONT. Simulating this state in a debugger is hard and is + // generally not needed (one use case is debugging background task being managed by a + // shell). For general use, it is sufficient to stop the process in a signal-delivery + // stop which happens before the group stop. This done by MonitorSignal and works + // correctly for all signals. + if (log) + log->Printf("NativeProcessLinux::%s received a group stop for pid %" PRIu64 " tid %" PRIu64 ". Transparent handling of group stops not supported, resuming the thread.", __FUNCTION__, GetID (), pid); + ResumeThread(*thread_sp, thread_sp->GetState(), LLDB_INVALID_SIGNAL_NUMBER); + } + else + { + // ptrace(GETSIGINFO) failed (but not due to group-stop). + + // A return value of ESRCH means the thread/process is no longer on the system, + // so it was killed somehow outside of our control. Either way, we can't do anything + // with it anymore. + + // Stop tracking the metadata for the thread since it's entirely off the system now. + const bool thread_found = StopTrackingThread (pid); + + if (log) + log->Printf ("NativeProcessLinux::%s GetSignalInfo failed: %s, tid = %" PRIu64 ", signal = %d, status = %d (%s, %s, %s)", + __FUNCTION__, info_err.AsCString(), pid, signal, status, info_err.GetError() == ESRCH ? "thread/process killed" : "unknown reason", is_main_thread ? "is main thread" : "is not main thread", thread_found ? "thread metadata removed" : "thread metadata not found"); + + if (is_main_thread) + { + // Notify the delegate - our process is not available but appears to have been killed outside + // our control. Is eStateExited the right exit state in this case? + SetExitStatus (convert_pid_status_to_exit_type (status), convert_pid_status_to_return_code (status), nullptr, true); + SetState (StateType::eStateExited, true); + } + else + { + // This thread was pulled out from underneath us. Anything to do here? Do we want to do an all stop? + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 " non-main thread exit occurred, didn't tell delegate anything since thread disappeared out from underneath us", __FUNCTION__, GetID (), pid); + } + } + } +} + +void +NativeProcessLinux::WaitForNewThread(::pid_t tid) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + NativeThreadLinuxSP new_thread_sp = GetThreadByID(tid); + + if (new_thread_sp) + { + // We are already tracking the thread - we got the event on the new thread (see + // MonitorSignal) before this one. We are done. + return; + } + + // The thread is not tracked yet, let's wait for it to appear. + int status = -1; + ::pid_t wait_pid; + do + { + if (log) + log->Printf ("NativeProcessLinux::%s() received thread creation event for tid %" PRIu32 ". tid not tracked yet, waiting for thread to appear...", __FUNCTION__, tid); + wait_pid = waitpid(tid, &status, __WALL); + } + while (wait_pid == -1 && errno == EINTR); + // Since we are waiting on a specific tid, this must be the creation event. But let's do + // some checks just in case. + if (wait_pid != tid) { + if (log) + log->Printf ("NativeProcessLinux::%s() waiting for tid %" PRIu32 " failed. Assuming the thread has disappeared in the meantime", __FUNCTION__, tid); + // The only way I know of this could happen is if the whole process was + // SIGKILLed in the mean time. In any case, we can't do anything about that now. + return; + } + if (WIFEXITED(status)) + { + if (log) + log->Printf ("NativeProcessLinux::%s() waiting for tid %" PRIu32 " returned an 'exited' event. Not tracking the thread.", __FUNCTION__, tid); + // Also a very improbable event. + return; + } + + siginfo_t info; + Error error = GetSignalInfo(tid, &info); + if (error.Fail()) + { + if (log) + log->Printf ("NativeProcessLinux::%s() GetSignalInfo for tid %" PRIu32 " failed. Assuming the thread has disappeared in the meantime.", __FUNCTION__, tid); + return; + } + + if (((info.si_pid != 0) || (info.si_code != SI_USER)) && log) + { + // We should be getting a thread creation signal here, but we received something + // else. There isn't much we can do about it now, so we will just log that. Since the + // thread is alive and we are receiving events from it, we shall pretend that it was + // created properly. + log->Printf ("NativeProcessLinux::%s() GetSignalInfo for tid %" PRIu32 " received unexpected signal with code %d from pid %d.", __FUNCTION__, tid, info.si_code, info.si_pid); + } + + if (log) + log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 ": tracking new thread tid %" PRIu32, + __FUNCTION__, GetID (), tid); + + new_thread_sp = AddThread(tid); + ResumeThread(*new_thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); + ThreadWasCreated(*new_thread_sp); +} + +void +NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + const bool is_main_thread = (thread.GetID() == GetID ()); + + assert(info.si_signo == SIGTRAP && "Unexpected child signal!"); + + Mutex::Locker locker (m_threads_mutex); + + switch (info.si_code) + { + // TODO: these two cases are required if we want to support tracing of the inferiors' children. We'd need this to debug a monitor. + // case (SIGTRAP | (PTRACE_EVENT_FORK << 8)): + // case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)): + + case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)): + { + // This is the notification on the parent thread which informs us of new thread + // creation. + // We don't want to do anything with the parent thread so we just resume it. In case we + // want to implement "break on thread creation" functionality, we would need to stop + // here. + + unsigned long event_message = 0; + if (GetEventMessage(thread.GetID(), &event_message).Fail()) + { + if (log) + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " received thread creation event but GetEventMessage failed so we don't know the new tid", __FUNCTION__, thread.GetID()); + } else + WaitForNewThread(event_message); + + ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER); + break; + } + + case (SIGTRAP | (PTRACE_EVENT_EXEC << 8)): + { + NativeThreadLinuxSP main_thread_sp; + if (log) + log->Printf ("NativeProcessLinux::%s() received exec event, code = %d", __FUNCTION__, info.si_code ^ SIGTRAP); + + // Exec clears any pending notifications. + m_pending_notification_tid = LLDB_INVALID_THREAD_ID; + + // Remove all but the main thread here. Linux fork creates a new process which only copies the main thread. Mutexes are in undefined state. + if (log) + log->Printf ("NativeProcessLinux::%s exec received, stop tracking all but main thread", __FUNCTION__); + + for (auto thread_sp : m_threads) + { + const bool is_main_thread = thread_sp && thread_sp->GetID () == GetID (); + if (is_main_thread) + { + main_thread_sp = std::static_pointer_cast<NativeThreadLinux>(thread_sp); + if (log) + log->Printf ("NativeProcessLinux::%s found main thread with tid %" PRIu64 ", keeping", __FUNCTION__, main_thread_sp->GetID ()); + } + else + { + if (log) + log->Printf ("NativeProcessLinux::%s discarding non-main-thread tid %" PRIu64 " due to exec", __FUNCTION__, thread_sp->GetID ()); + } + } + + m_threads.clear (); + + if (main_thread_sp) + { + m_threads.push_back (main_thread_sp); + SetCurrentThreadID (main_thread_sp->GetID ()); + main_thread_sp->SetStoppedByExec(); + } + else + { + SetCurrentThreadID (LLDB_INVALID_THREAD_ID); + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 "no main thread found, discarded all threads, we're in a no-thread state!", __FUNCTION__, GetID ()); + } + + // Tell coordinator about about the "new" (since exec) stopped main thread. + ThreadWasCreated(*main_thread_sp); + + // Let our delegate know we have just exec'd. + NotifyDidExec (); + + // If we have a main thread, indicate we are stopped. + assert (main_thread_sp && "exec called during ptraced process but no main thread metadata tracked"); + + // Let the process know we're stopped. + StopRunningThreads(main_thread_sp->GetID()); + + break; + } + + case (SIGTRAP | (PTRACE_EVENT_EXIT << 8)): + { + // The inferior process or one of its threads is about to exit. + // We don't want to do anything with the thread so we just resume it. In case we + // want to implement "break on thread exit" functionality, we would need to stop + // here. + + unsigned long data = 0; + if (GetEventMessage(thread.GetID(), &data).Fail()) + data = -1; + + if (log) + { + log->Printf ("NativeProcessLinux::%s() received PTRACE_EVENT_EXIT, data = %lx (WIFEXITED=%s,WIFSIGNALED=%s), pid = %" PRIu64 " (%s)", + __FUNCTION__, + data, WIFEXITED (data) ? "true" : "false", WIFSIGNALED (data) ? "true" : "false", + thread.GetID(), + is_main_thread ? "is main thread" : "not main thread"); + } + + if (is_main_thread) + { + SetExitStatus (convert_pid_status_to_exit_type (data), convert_pid_status_to_return_code (data), nullptr, true); + } + + StateType state = thread.GetState(); + if (! StateIsRunningState(state)) + { + // Due to a kernel bug, we may sometimes get this stop after the inferior gets a + // SIGKILL. This confuses our state tracking logic in ResumeThread(), since normally, + // we should not be receiving any ptrace events while the inferior is stopped. This + // makes sure that the inferior is resumed and exits normally. + state = eStateRunning; + } + ResumeThread(thread, state, LLDB_INVALID_SIGNAL_NUMBER); + + break; + } + + case 0: + case TRAP_TRACE: // We receive this on single stepping. + case TRAP_HWBKPT: // We receive this on watchpoint hit + { + // If a watchpoint was hit, report it + uint32_t wp_index; + Error error = thread.GetRegisterContext()->GetWatchpointHitIndex(wp_index, (uintptr_t)info.si_addr); + if (error.Fail() && log) + log->Printf("NativeProcessLinux::%s() " + "received error while checking for watchpoint hits, " + "pid = %" PRIu64 " error = %s", + __FUNCTION__, thread.GetID(), error.AsCString()); + if (wp_index != LLDB_INVALID_INDEX32) + { + MonitorWatchpoint(thread, wp_index); + break; + } + + // Otherwise, report step over + MonitorTrace(thread); + break; + } + + case SI_KERNEL: +#if defined __mips__ + // For mips there is no special signal for watchpoint + // So we check for watchpoint in kernel trap + { + // If a watchpoint was hit, report it + uint32_t wp_index; + Error error = thread.GetRegisterContext()->GetWatchpointHitIndex(wp_index, LLDB_INVALID_ADDRESS); + if (error.Fail() && log) + log->Printf("NativeProcessLinux::%s() " + "received error while checking for watchpoint hits, " + "pid = %" PRIu64 " error = %s", + __FUNCTION__, thread.GetID(), error.AsCString()); + if (wp_index != LLDB_INVALID_INDEX32) + { + MonitorWatchpoint(thread, wp_index); + break; + } + } + // NO BREAK +#endif + case TRAP_BRKPT: + MonitorBreakpoint(thread); + break; + + case SIGTRAP: + case (SIGTRAP | 0x80): + if (log) + log->Printf ("NativeProcessLinux::%s() received unknown SIGTRAP system call stop event, pid %" PRIu64 "tid %" PRIu64 ", resuming", __FUNCTION__, GetID (), thread.GetID()); + + // Ignore these signals until we know more about them. + ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER); + break; + + default: + assert(false && "Unexpected SIGTRAP code!"); + if (log) + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 "tid %" PRIu64 " received unhandled SIGTRAP code: 0x%d", + __FUNCTION__, GetID(), thread.GetID(), info.si_code); + break; + + } +} + +void +NativeProcessLinux::MonitorTrace(NativeThreadLinux &thread) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("NativeProcessLinux::%s() received trace event, pid = %" PRIu64 " (single stepping)", + __FUNCTION__, thread.GetID()); + + // This thread is currently stopped. + thread.SetStoppedByTrace(); + + StopRunningThreads(thread.GetID()); +} + +void +NativeProcessLinux::MonitorBreakpoint(NativeThreadLinux &thread) +{ + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf("NativeProcessLinux::%s() received breakpoint event, pid = %" PRIu64, + __FUNCTION__, thread.GetID()); + + // Mark the thread as stopped at breakpoint. + thread.SetStoppedByBreakpoint(); + Error error = FixupBreakpointPCAsNeeded(thread); + if (error.Fail()) + if (log) + log->Printf("NativeProcessLinux::%s() pid = %" PRIu64 " fixup: %s", + __FUNCTION__, thread.GetID(), error.AsCString()); + + if (m_threads_stepping_with_breakpoint.find(thread.GetID()) != m_threads_stepping_with_breakpoint.end()) + thread.SetStoppedByTrace(); + + StopRunningThreads(thread.GetID()); +} + +void +NativeProcessLinux::MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index) +{ + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_WATCHPOINTS)); + if (log) + log->Printf("NativeProcessLinux::%s() received watchpoint event, " + "pid = %" PRIu64 ", wp_index = %" PRIu32, + __FUNCTION__, thread.GetID(), wp_index); + + // Mark the thread as stopped at watchpoint. + // The address is at (lldb::addr_t)info->si_addr if we need it. + thread.SetStoppedByWatchpoint(wp_index); + + // We need to tell all other running threads before we notify the delegate about this stop. + StopRunningThreads(thread.GetID()); +} + +void +NativeProcessLinux::MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited) +{ + const int signo = info.si_signo; + const bool is_from_llgs = info.si_pid == getpid (); + + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // POSIX says that process behaviour is undefined after it ignores a SIGFPE, + // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a + // kill(2) or raise(3). Similarly for tgkill(2) on Linux. + // + // IOW, user generated signals never generate what we consider to be a + // "crash". + // + // Similarly, ACK signals generated by this monitor. + + Mutex::Locker locker (m_threads_mutex); + + // Handle the signal. + if (info.si_code == SI_TKILL || info.si_code == SI_USER) + { + if (log) + log->Printf ("NativeProcessLinux::%s() received signal %s (%d) with code %s, (siginfo pid = %d (%s), waitpid pid = %" PRIu64 ")", + __FUNCTION__, + Host::GetSignalAsCString(signo), + signo, + (info.si_code == SI_TKILL ? "SI_TKILL" : "SI_USER"), + info.si_pid, + is_from_llgs ? "from llgs" : "not from llgs", + thread.GetID()); + } + + // Check for thread stop notification. + if (is_from_llgs && (info.si_code == SI_TKILL) && (signo == SIGSTOP)) + { + // This is a tgkill()-based stop. + if (log) + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", thread stopped", + __FUNCTION__, + GetID (), + thread.GetID()); + + // Check that we're not already marked with a stop reason. + // Note this thread really shouldn't already be marked as stopped - if we were, that would imply that + // the kernel signaled us with the thread stopping which we handled and marked as stopped, + // and that, without an intervening resume, we received another stop. It is more likely + // that we are missing the marking of a run state somewhere if we find that the thread was + // marked as stopped. + const StateType thread_state = thread.GetState(); + if (!StateIsStoppedState (thread_state, false)) + { + // An inferior thread has stopped because of a SIGSTOP we have sent it. + // Generally, these are not important stops and we don't want to report them as + // they are just used to stop other threads when one thread (the one with the + // *real* stop reason) hits a breakpoint (watchpoint, etc...). However, in the + // case of an asynchronous Interrupt(), this *is* the real stop reason, so we + // leave the signal intact if this is the thread that was chosen as the + // triggering thread. + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) + { + if (m_pending_notification_tid == thread.GetID()) + thread.SetStoppedBySignal(SIGSTOP, &info); + else + thread.SetStoppedWithNoReason(); + + SetCurrentThreadID (thread.GetID ()); + SignalIfAllThreadsStopped(); + } + else + { + // We can end up here if stop was initiated by LLGS but by this time a + // thread stop has occurred - maybe initiated by another event. + Error error = ResumeThread(thread, thread.GetState(), 0); + if (error.Fail() && log) + { + log->Printf("NativeProcessLinux::%s failed to resume thread tid %" PRIu64 ": %s", + __FUNCTION__, thread.GetID(), error.AsCString()); + } + } + } + else + { + if (log) + { + // Retrieve the signal name if the thread was stopped by a signal. + int stop_signo = 0; + const bool stopped_by_signal = thread.IsStopped(&stop_signo); + const char *signal_name = stopped_by_signal ? Host::GetSignalAsCString(stop_signo) : "<not stopped by signal>"; + if (!signal_name) + signal_name = "<no-signal-name>"; + + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", thread was already marked as a stopped state (state=%s, signal=%d (%s)), leaving stop signal as is", + __FUNCTION__, + GetID (), + thread.GetID(), + StateAsCString (thread_state), + stop_signo, + signal_name); + } + SignalIfAllThreadsStopped(); + } + + // Done handling. + return; + } + + if (log) + log->Printf ("NativeProcessLinux::%s() received signal %s", __FUNCTION__, Host::GetSignalAsCString(signo)); + + // This thread is stopped. + thread.SetStoppedBySignal(signo, &info); + + // Send a stop to the debugger after we get all other threads to stop. + StopRunningThreads(thread.GetID()); +} + +namespace { + +struct EmulatorBaton +{ + NativeProcessLinux* m_process; + NativeRegisterContext* m_reg_context; + + // eRegisterKindDWARF -> RegsiterValue + std::unordered_map<uint32_t, RegisterValue> m_register_values; + + EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) : + m_process(process), m_reg_context(reg_context) {} +}; + +} // anonymous namespace + +static size_t +ReadMemoryCallback (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, + void *dst, + size_t length) +{ + EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton); + + size_t bytes_read; + emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read); + return bytes_read; +} + +static bool +ReadRegisterCallback (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value) +{ + EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton); + + auto it = emulator_baton->m_register_values.find(reg_info->kinds[eRegisterKindDWARF]); + if (it != emulator_baton->m_register_values.end()) + { + reg_value = it->second; + return true; + } + + // The emulator only fill in the dwarf regsiter numbers (and in some case + // the generic register numbers). Get the full register info from the + // register context based on the dwarf register numbers. + const RegisterInfo* full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo( + eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]); + + Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); + if (error.Success()) + return true; + + return false; +} + +static bool +WriteRegisterCallback (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value) +{ + EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton); + emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] = reg_value; + return true; +} + +static size_t +WriteMemoryCallback (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, + const void *dst, + size_t length) +{ + return length; +} + +static lldb::addr_t +ReadFlags (NativeRegisterContext* regsiter_context) +{ + const RegisterInfo* flags_info = regsiter_context->GetRegisterInfo( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + return regsiter_context->ReadRegisterAsUnsigned(flags_info, LLDB_INVALID_ADDRESS); +} + +Error +NativeProcessLinux::SetupSoftwareSingleStepping(NativeThreadLinux &thread) +{ + Error error; + NativeRegisterContextSP register_context_sp = thread.GetRegisterContext(); + + std::unique_ptr<EmulateInstruction> emulator_ap( + EmulateInstruction::FindPlugin(m_arch, eInstructionTypePCModifying, nullptr)); + + if (emulator_ap == nullptr) + return Error("Instruction emulator not found!"); + + EmulatorBaton baton(this, register_context_sp.get()); + emulator_ap->SetBaton(&baton); + emulator_ap->SetReadMemCallback(&ReadMemoryCallback); + emulator_ap->SetReadRegCallback(&ReadRegisterCallback); + emulator_ap->SetWriteMemCallback(&WriteMemoryCallback); + emulator_ap->SetWriteRegCallback(&WriteRegisterCallback); + + if (!emulator_ap->ReadInstruction()) + return Error("Read instruction failed!"); + + bool emulation_result = emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC); + + const RegisterInfo* reg_info_pc = register_context_sp->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + const RegisterInfo* reg_info_flags = register_context_sp->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + + auto pc_it = baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]); + auto flags_it = baton.m_register_values.find(reg_info_flags->kinds[eRegisterKindDWARF]); + + lldb::addr_t next_pc; + lldb::addr_t next_flags; + if (emulation_result) + { + assert(pc_it != baton.m_register_values.end() && "Emulation was successfull but PC wasn't updated"); + next_pc = pc_it->second.GetAsUInt64(); + + if (flags_it != baton.m_register_values.end()) + next_flags = flags_it->second.GetAsUInt64(); + else + next_flags = ReadFlags (register_context_sp.get()); + } + else if (pc_it == baton.m_register_values.end()) + { + // Emulate instruction failed and it haven't changed PC. Advance PC + // with the size of the current opcode because the emulation of all + // PC modifying instruction should be successful. The failure most + // likely caused by a not supported instruction which don't modify PC. + next_pc = register_context_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize(); + next_flags = ReadFlags (register_context_sp.get()); + } + else + { + // The instruction emulation failed after it modified the PC. It is an + // unknown error where we can't continue because the next instruction is + // modifying the PC but we don't know how. + return Error ("Instruction emulation failed unexpectedly."); + } + + if (m_arch.GetMachine() == llvm::Triple::arm) + { + if (next_flags & 0x20) + { + // Thumb mode + error = SetSoftwareBreakpoint(next_pc, 2); + } + else + { + // Arm mode + error = SetSoftwareBreakpoint(next_pc, 4); + } + } + else if (m_arch.GetMachine() == llvm::Triple::mips64 + || m_arch.GetMachine() == llvm::Triple::mips64el + || m_arch.GetMachine() == llvm::Triple::mips + || m_arch.GetMachine() == llvm::Triple::mipsel) + error = SetSoftwareBreakpoint(next_pc, 4); + else + { + // No size hint is given for the next breakpoint + error = SetSoftwareBreakpoint(next_pc, 0); + } + + if (error.Fail()) + return error; + + m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc}); + + return Error(); +} + +bool +NativeProcessLinux::SupportHardwareSingleStepping() const +{ + if (m_arch.GetMachine() == llvm::Triple::arm + || m_arch.GetMachine() == llvm::Triple::mips64 || m_arch.GetMachine() == llvm::Triple::mips64el + || m_arch.GetMachine() == llvm::Triple::mips || m_arch.GetMachine() == llvm::Triple::mipsel) + return false; + return true; +} + +Error +NativeProcessLinux::Resume (const ResumeActionList &resume_actions) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("NativeProcessLinux::%s called: pid %" PRIu64, __FUNCTION__, GetID ()); + + bool software_single_step = !SupportHardwareSingleStepping(); + + Mutex::Locker locker (m_threads_mutex); + + if (software_single_step) + { + for (auto thread_sp : m_threads) + { + assert (thread_sp && "thread list should not contain NULL threads"); + + const ResumeAction *const action = resume_actions.GetActionForThread (thread_sp->GetID (), true); + if (action == nullptr) + continue; + + if (action->state == eStateStepping) + { + Error error = SetupSoftwareSingleStepping(static_cast<NativeThreadLinux &>(*thread_sp)); + if (error.Fail()) + return error; + } + } + } + + for (auto thread_sp : m_threads) + { + assert (thread_sp && "thread list should not contain NULL threads"); + + const ResumeAction *const action = resume_actions.GetActionForThread (thread_sp->GetID (), true); + + if (action == nullptr) + { + if (log) + log->Printf ("NativeProcessLinux::%s no action specified for pid %" PRIu64 " tid %" PRIu64, + __FUNCTION__, GetID (), thread_sp->GetID ()); + continue; + } + + if (log) + { + log->Printf ("NativeProcessLinux::%s processing resume action state %s for pid %" PRIu64 " tid %" PRIu64, + __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ()); + } + + switch (action->state) + { + case eStateRunning: + case eStateStepping: + { + // Run the thread, possibly feeding it the signal. + const int signo = action->signal; + ResumeThread(static_cast<NativeThreadLinux &>(*thread_sp), action->state, signo); + break; + } + + case eStateSuspended: + case eStateStopped: + lldbassert(0 && "Unexpected state"); + + default: + return Error ("NativeProcessLinux::%s (): unexpected state %s specified for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ()); + } + } + + return Error(); +} + +Error +NativeProcessLinux::Halt () +{ + Error error; + + if (kill (GetID (), SIGSTOP) != 0) + error.SetErrorToErrno (); + + return error; +} + +Error +NativeProcessLinux::Detach () +{ + Error error; + + // Stop monitoring the inferior. + m_sigchld_handle.reset(); + + // Tell ptrace to detach from the process. + if (GetID () == LLDB_INVALID_PROCESS_ID) + return error; + + for (auto thread_sp : m_threads) + { + Error e = Detach(thread_sp->GetID()); + if (e.Fail()) + error = e; // Save the error, but still attempt to detach from other threads. + } + + return error; +} + +Error +NativeProcessLinux::Signal (int signo) +{ + Error error; + + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessLinux::%s: sending signal %d (%s) to pid %" PRIu64, + __FUNCTION__, signo, Host::GetSignalAsCString(signo), GetID()); + + if (kill(GetID(), signo)) + error.SetErrorToErrno(); + + return error; +} + +Error +NativeProcessLinux::Interrupt () +{ + // Pick a running thread (or if none, a not-dead stopped thread) as + // the chosen thread that will be the stop-reason thread. + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + NativeThreadProtocolSP running_thread_sp; + NativeThreadProtocolSP stopped_thread_sp; + + if (log) + log->Printf ("NativeProcessLinux::%s selecting running thread for interrupt target", __FUNCTION__); + + Mutex::Locker locker (m_threads_mutex); + + for (auto thread_sp : m_threads) + { + // The thread shouldn't be null but lets just cover that here. + if (!thread_sp) + continue; + + // If we have a running or stepping thread, we'll call that the + // target of the interrupt. + const auto thread_state = thread_sp->GetState (); + if (thread_state == eStateRunning || + thread_state == eStateStepping) + { + running_thread_sp = thread_sp; + break; + } + else if (!stopped_thread_sp && StateIsStoppedState (thread_state, true)) + { + // Remember the first non-dead stopped thread. We'll use that as a backup if there are no running threads. + stopped_thread_sp = thread_sp; + } + } + + if (!running_thread_sp && !stopped_thread_sp) + { + Error error("found no running/stepping or live stopped threads as target for interrupt"); + if (log) + log->Printf ("NativeProcessLinux::%s skipping due to error: %s", __FUNCTION__, error.AsCString ()); + + return error; + } + + NativeThreadProtocolSP deferred_signal_thread_sp = running_thread_sp ? running_thread_sp : stopped_thread_sp; + + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " %s tid %" PRIu64 " chosen for interrupt target", + __FUNCTION__, + GetID (), + running_thread_sp ? "running" : "stopped", + deferred_signal_thread_sp->GetID ()); + + StopRunningThreads(deferred_signal_thread_sp->GetID()); + + return Error(); +} + +Error +NativeProcessLinux::Kill () +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessLinux::%s called for PID %" PRIu64, __FUNCTION__, GetID ()); + + Error error; + + switch (m_state) + { + case StateType::eStateInvalid: + case StateType::eStateExited: + case StateType::eStateCrashed: + case StateType::eStateDetached: + case StateType::eStateUnloaded: + // Nothing to do - the process is already dead. + if (log) + log->Printf ("NativeProcessLinux::%s ignored for PID %" PRIu64 " due to current state: %s", __FUNCTION__, GetID (), StateAsCString (m_state)); + return error; + + case StateType::eStateConnected: + case StateType::eStateAttaching: + case StateType::eStateLaunching: + case StateType::eStateStopped: + case StateType::eStateRunning: + case StateType::eStateStepping: + case StateType::eStateSuspended: + // We can try to kill a process in these states. + break; + } + + if (kill (GetID (), SIGKILL) != 0) + { + error.SetErrorToErrno (); + return error; + } + + return error; +} + +static Error +ParseMemoryRegionInfoFromProcMapsLine (const std::string &maps_line, MemoryRegionInfo &memory_region_info) +{ + memory_region_info.Clear(); + + StringExtractor line_extractor (maps_line.c_str ()); + + // Format: {address_start_hex}-{address_end_hex} perms offset dev inode pathname + // perms: rwxp (letter is present if set, '-' if not, final character is p=private, s=shared). + + // Parse out the starting address + lldb::addr_t start_address = line_extractor.GetHexMaxU64 (false, 0); + + // Parse out hyphen separating start and end address from range. + if (!line_extractor.GetBytesLeft () || (line_extractor.GetChar () != '-')) + return Error ("malformed /proc/{pid}/maps entry, missing dash between address range"); + + // Parse out the ending address + lldb::addr_t end_address = line_extractor.GetHexMaxU64 (false, start_address); + + // Parse out the space after the address. + if (!line_extractor.GetBytesLeft () || (line_extractor.GetChar () != ' ')) + return Error ("malformed /proc/{pid}/maps entry, missing space after range"); + + // Save the range. + memory_region_info.GetRange ().SetRangeBase (start_address); + memory_region_info.GetRange ().SetRangeEnd (end_address); + + // Parse out each permission entry. + if (line_extractor.GetBytesLeft () < 4) + return Error ("malformed /proc/{pid}/maps entry, missing some portion of permissions"); + + // Handle read permission. + const char read_perm_char = line_extractor.GetChar (); + if (read_perm_char == 'r') + memory_region_info.SetReadable (MemoryRegionInfo::OptionalBool::eYes); + else + { + assert ( (read_perm_char == '-') && "unexpected /proc/{pid}/maps read permission char" ); + memory_region_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo); + } + + // Handle write permission. + const char write_perm_char = line_extractor.GetChar (); + if (write_perm_char == 'w') + memory_region_info.SetWritable (MemoryRegionInfo::OptionalBool::eYes); + else + { + assert ( (write_perm_char == '-') && "unexpected /proc/{pid}/maps write permission char" ); + memory_region_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo); + } + + // Handle execute permission. + const char exec_perm_char = line_extractor.GetChar (); + if (exec_perm_char == 'x') + memory_region_info.SetExecutable (MemoryRegionInfo::OptionalBool::eYes); + else + { + assert ( (exec_perm_char == '-') && "unexpected /proc/{pid}/maps exec permission char" ); + memory_region_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo); + } + + return Error (); +} + +Error +NativeProcessLinux::GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInfo &range_info) +{ + // FIXME review that the final memory region returned extends to the end of the virtual address space, + // with no perms if it is not mapped. + + // Use an approach that reads memory regions from /proc/{pid}/maps. + // Assume proc maps entries are in ascending order. + // FIXME assert if we find differently. + Mutex::Locker locker (m_mem_region_cache_mutex); + + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + Error error; + + if (m_supports_mem_region == LazyBool::eLazyBoolNo) + { + // We're done. + error.SetErrorString ("unsupported"); + return error; + } + + // If our cache is empty, pull the latest. There should always be at least one memory region + // if memory region handling is supported. + if (m_mem_region_cache.empty ()) + { + error = ProcFileReader::ProcessLineByLine (GetID (), "maps", + [&] (const std::string &line) -> bool + { + MemoryRegionInfo info; + const Error parse_error = ParseMemoryRegionInfoFromProcMapsLine (line, info); + if (parse_error.Success ()) + { + m_mem_region_cache.push_back (info); + return true; + } + else + { + if (log) + log->Printf ("NativeProcessLinux::%s failed to parse proc maps line '%s': %s", __FUNCTION__, line.c_str (), error.AsCString ()); + return false; + } + }); + + // If we had an error, we'll mark unsupported. + if (error.Fail ()) + { + m_supports_mem_region = LazyBool::eLazyBoolNo; + return error; + } + else if (m_mem_region_cache.empty ()) + { + // No entries after attempting to read them. This shouldn't happen if /proc/{pid}/maps + // is supported. Assume we don't support map entries via procfs. + if (log) + log->Printf ("NativeProcessLinux::%s failed to find any procfs maps entries, assuming no support for memory region metadata retrieval", __FUNCTION__); + m_supports_mem_region = LazyBool::eLazyBoolNo; + error.SetErrorString ("not supported"); + return error; + } + + if (log) + log->Printf ("NativeProcessLinux::%s read %" PRIu64 " memory region entries from /proc/%" PRIu64 "/maps", __FUNCTION__, static_cast<uint64_t> (m_mem_region_cache.size ()), GetID ()); + + // We support memory retrieval, remember that. + m_supports_mem_region = LazyBool::eLazyBoolYes; + } + else + { + if (log) + log->Printf ("NativeProcessLinux::%s reusing %" PRIu64 " cached memory region entries", __FUNCTION__, static_cast<uint64_t> (m_mem_region_cache.size ())); + } + + lldb::addr_t prev_base_address = 0; + + // FIXME start by finding the last region that is <= target address using binary search. Data is sorted. + // There can be a ton of regions on pthreads apps with lots of threads. + for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end (); ++it) + { + MemoryRegionInfo &proc_entry_info = *it; + + // Sanity check assumption that /proc/{pid}/maps entries are ascending. + assert ((proc_entry_info.GetRange ().GetRangeBase () >= prev_base_address) && "descending /proc/pid/maps entries detected, unexpected"); + prev_base_address = proc_entry_info.GetRange ().GetRangeBase (); + + // If the target address comes before this entry, indicate distance to next region. + if (load_addr < proc_entry_info.GetRange ().GetRangeBase ()) + { + range_info.GetRange ().SetRangeBase (load_addr); + range_info.GetRange ().SetByteSize (proc_entry_info.GetRange ().GetRangeBase () - load_addr); + range_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo); + + return error; + } + else if (proc_entry_info.GetRange ().Contains (load_addr)) + { + // The target address is within the memory region we're processing here. + range_info = proc_entry_info; + return error; + } + + // The target memory address comes somewhere after the region we just parsed. + } + + // If we made it here, we didn't find an entry that contained the given address. Return the + // load_addr as start and the amount of bytes betwwen load address and the end of the memory as + // size. + range_info.GetRange ().SetRangeBase (load_addr); + switch (m_arch.GetAddressByteSize()) + { + case 4: + range_info.GetRange ().SetByteSize (0x100000000ull - load_addr); + break; + case 8: + range_info.GetRange ().SetByteSize (0ull - load_addr); + break; + default: + assert(false && "Unrecognized data byte size"); + break; + } + range_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo); + return error; +} + +void +NativeProcessLinux::DoStopIDBumped (uint32_t newBumpId) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessLinux::%s(newBumpId=%" PRIu32 ") called", __FUNCTION__, newBumpId); + + { + Mutex::Locker locker (m_mem_region_cache_mutex); + if (log) + log->Printf ("NativeProcessLinux::%s clearing %" PRIu64 " entries from the cache", __FUNCTION__, static_cast<uint64_t> (m_mem_region_cache.size ())); + m_mem_region_cache.clear (); + } +} + +Error +NativeProcessLinux::AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) +{ + // FIXME implementing this requires the equivalent of + // InferiorCallPOSIX::InferiorCallMmap, which depends on + // functional ThreadPlans working with Native*Protocol. +#if 1 + return Error ("not implemented yet"); +#else + addr = LLDB_INVALID_ADDRESS; + + unsigned prot = 0; + if (permissions & lldb::ePermissionsReadable) + prot |= eMmapProtRead; + if (permissions & lldb::ePermissionsWritable) + prot |= eMmapProtWrite; + if (permissions & lldb::ePermissionsExecutable) + prot |= eMmapProtExec; + + // TODO implement this directly in NativeProcessLinux + // (and lift to NativeProcessPOSIX if/when that class is + // refactored out). + if (InferiorCallMmap(this, addr, 0, size, prot, + eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) { + m_addr_to_mmap_size[addr] = size; + return Error (); + } else { + addr = LLDB_INVALID_ADDRESS; + return Error("unable to allocate %" PRIu64 " bytes of memory with permissions %s", size, GetPermissionsAsCString (permissions)); + } +#endif +} + +Error +NativeProcessLinux::DeallocateMemory (lldb::addr_t addr) +{ + // FIXME see comments in AllocateMemory - required lower-level + // bits not in place yet (ThreadPlans) + return Error ("not implemented"); +} + +lldb::addr_t +NativeProcessLinux::GetSharedLibraryInfoAddress () +{ +#if 1 + // punt on this for now + return LLDB_INVALID_ADDRESS; +#else + // Return the image info address for the exe module +#if 1 + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + ModuleSP module_sp; + Error error = GetExeModuleSP (module_sp); + if (error.Fail ()) + { + if (log) + log->Warning ("NativeProcessLinux::%s failed to retrieve exe module: %s", __FUNCTION__, error.AsCString ()); + return LLDB_INVALID_ADDRESS; + } + + if (module_sp == nullptr) + { + if (log) + log->Warning ("NativeProcessLinux::%s exe module returned was NULL", __FUNCTION__); + return LLDB_INVALID_ADDRESS; + } + + ObjectFileSP object_file_sp = module_sp->GetObjectFile (); + if (object_file_sp == nullptr) + { + if (log) + log->Warning ("NativeProcessLinux::%s exe module returned a NULL object file", __FUNCTION__); + return LLDB_INVALID_ADDRESS; + } + + return obj_file_sp->GetImageInfoAddress(); +#else + Target *target = &GetTarget(); + ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(target); + + if (addr.IsValid()) + return addr.GetLoadAddress(target); + return LLDB_INVALID_ADDRESS; +#endif +#endif // punt on this for now +} + +size_t +NativeProcessLinux::UpdateThreads () +{ + // The NativeProcessLinux monitoring threads are always up to date + // with respect to thread state and they keep the thread list + // populated properly. All this method needs to do is return the + // thread count. + Mutex::Locker locker (m_threads_mutex); + return m_threads.size (); +} + +bool +NativeProcessLinux::GetArchitecture (ArchSpec &arch) const +{ + arch = m_arch; + return true; +} + +Error +NativeProcessLinux::GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size) +{ + // FIXME put this behind a breakpoint protocol class that can be + // set per architecture. Need ARM, MIPS support here. + static const uint8_t g_i386_opcode [] = { 0xCC }; + + switch (m_arch.GetMachine ()) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + actual_opcode_size = static_cast<uint32_t> (sizeof(g_i386_opcode)); + return Error (); + + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + case llvm::Triple::mips: + case llvm::Triple::mipsel: + // On these architectures the PC don't get updated for breakpoint hits + actual_opcode_size = 0; + return Error (); + + default: + assert(false && "CPU type not supported!"); + return Error ("CPU type not supported"); + } +} + +Error +NativeProcessLinux::SetBreakpoint (lldb::addr_t addr, uint32_t size, bool hardware) +{ + if (hardware) + return Error ("NativeProcessLinux does not support hardware breakpoints"); + else + return SetSoftwareBreakpoint (addr, size); +} + +Error +NativeProcessLinux::GetSoftwareBreakpointTrapOpcode (size_t trap_opcode_size_hint, + size_t &actual_opcode_size, + const uint8_t *&trap_opcode_bytes) +{ + // FIXME put this behind a breakpoint protocol class that can be set per + // architecture. Need MIPS support here. + static const uint8_t g_aarch64_opcode[] = { 0x00, 0x00, 0x20, 0xd4 }; + // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the + // linux kernel does otherwise. + static const uint8_t g_arm_breakpoint_opcode[] = { 0xf0, 0x01, 0xf0, 0xe7 }; + static const uint8_t g_i386_opcode [] = { 0xCC }; + static const uint8_t g_mips64_opcode[] = { 0x00, 0x00, 0x00, 0x0d }; + static const uint8_t g_mips64el_opcode[] = { 0x0d, 0x00, 0x00, 0x00 }; + static const uint8_t g_thumb_breakpoint_opcode[] = { 0x01, 0xde }; + + switch (m_arch.GetMachine ()) + { + case llvm::Triple::aarch64: + trap_opcode_bytes = g_aarch64_opcode; + actual_opcode_size = sizeof(g_aarch64_opcode); + return Error (); + + case llvm::Triple::arm: + switch (trap_opcode_size_hint) + { + case 2: + trap_opcode_bytes = g_thumb_breakpoint_opcode; + actual_opcode_size = sizeof(g_thumb_breakpoint_opcode); + return Error (); + case 4: + trap_opcode_bytes = g_arm_breakpoint_opcode; + actual_opcode_size = sizeof(g_arm_breakpoint_opcode); + return Error (); + default: + assert(false && "Unrecognised trap opcode size hint!"); + return Error ("Unrecognised trap opcode size hint!"); + } + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + trap_opcode_bytes = g_i386_opcode; + actual_opcode_size = sizeof(g_i386_opcode); + return Error (); + + case llvm::Triple::mips: + case llvm::Triple::mips64: + trap_opcode_bytes = g_mips64_opcode; + actual_opcode_size = sizeof(g_mips64_opcode); + return Error (); + + case llvm::Triple::mipsel: + case llvm::Triple::mips64el: + trap_opcode_bytes = g_mips64el_opcode; + actual_opcode_size = sizeof(g_mips64el_opcode); + return Error (); + + default: + assert(false && "CPU type not supported!"); + return Error ("CPU type not supported"); + } +} + +#if 0 +ProcessMessage::CrashReason +NativeProcessLinux::GetCrashReasonForSIGSEGV(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGSEGV); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGSEGV"); + break; + case SI_KERNEL: + // Linux will occasionally send spurious SI_KERNEL codes. + // (this is poorly documented in sigaction) + // One way to get this is via unaligned SIMD loads. + reason = ProcessMessage::eInvalidAddress; // for lack of anything better + break; + case SEGV_MAPERR: + reason = ProcessMessage::eInvalidAddress; + break; + case SEGV_ACCERR: + reason = ProcessMessage::ePrivilegedAddress; + break; + } + + return reason; +} +#endif + + +#if 0 +ProcessMessage::CrashReason +NativeProcessLinux::GetCrashReasonForSIGILL(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGILL); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGILL"); + break; + case ILL_ILLOPC: + reason = ProcessMessage::eIllegalOpcode; + break; + case ILL_ILLOPN: + reason = ProcessMessage::eIllegalOperand; + break; + case ILL_ILLADR: + reason = ProcessMessage::eIllegalAddressingMode; + break; + case ILL_ILLTRP: + reason = ProcessMessage::eIllegalTrap; + break; + case ILL_PRVOPC: + reason = ProcessMessage::ePrivilegedOpcode; + break; + case ILL_PRVREG: + reason = ProcessMessage::ePrivilegedRegister; + break; + case ILL_COPROC: + reason = ProcessMessage::eCoprocessorError; + break; + case ILL_BADSTK: + reason = ProcessMessage::eInternalStackError; + break; + } + + return reason; +} +#endif + +#if 0 +ProcessMessage::CrashReason +NativeProcessLinux::GetCrashReasonForSIGFPE(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGFPE); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGFPE"); + break; + case FPE_INTDIV: + reason = ProcessMessage::eIntegerDivideByZero; + break; + case FPE_INTOVF: + reason = ProcessMessage::eIntegerOverflow; + break; + case FPE_FLTDIV: + reason = ProcessMessage::eFloatDivideByZero; + break; + case FPE_FLTOVF: + reason = ProcessMessage::eFloatOverflow; + break; + case FPE_FLTUND: + reason = ProcessMessage::eFloatUnderflow; + break; + case FPE_FLTRES: + reason = ProcessMessage::eFloatInexactResult; + break; + case FPE_FLTINV: + reason = ProcessMessage::eFloatInvalidOperation; + break; + case FPE_FLTSUB: + reason = ProcessMessage::eFloatSubscriptRange; + break; + } + + return reason; +} +#endif + +#if 0 +ProcessMessage::CrashReason +NativeProcessLinux::GetCrashReasonForSIGBUS(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGBUS); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGBUS"); + break; + case BUS_ADRALN: + reason = ProcessMessage::eIllegalAlignment; + break; + case BUS_ADRERR: + reason = ProcessMessage::eIllegalAddress; + break; + case BUS_OBJERR: + reason = ProcessMessage::eHardwareError; + break; + } + + return reason; +} +#endif + +Error +NativeProcessLinux::ReadMemory (lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) +{ + if (ProcessVmReadvSupported()) { + // The process_vm_readv path is about 50 times faster than ptrace api. We want to use + // this syscall if it is supported. + + const ::pid_t pid = GetID(); + + struct iovec local_iov, remote_iov; + local_iov.iov_base = buf; + local_iov.iov_len = size; + remote_iov.iov_base = reinterpret_cast<void *>(addr); + remote_iov.iov_len = size; + + bytes_read = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0); + const bool success = bytes_read == size; + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessLinux::%s using process_vm_readv to read %zd bytes from inferior address 0x%" PRIx64": %s", + __FUNCTION__, size, addr, success ? "Success" : strerror(errno)); + + if (success) + return Error(); + // else + // the call failed for some reason, let's retry the read using ptrace api. + } + + unsigned char *dst = static_cast<unsigned char*>(buf); + size_t remainder; + long data; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL)); + if (log) + ProcessPOSIXLog::IncNestLevel(); + if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY)) + log->Printf ("NativeProcessLinux::%s(%p, %p, %zd, _)", __FUNCTION__, (void*)addr, buf, size); + + for (bytes_read = 0; bytes_read < size; bytes_read += remainder) + { + Error error = NativeProcessLinux::PtraceWrapper(PTRACE_PEEKDATA, GetID(), (void*)addr, nullptr, 0, &data); + if (error.Fail()) + { + if (log) + ProcessPOSIXLog::DecNestLevel(); + return error; + } + + remainder = size - bytes_read; + remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; + + // Copy the data into our buffer + memcpy(dst, &data, remainder); + + if (log && ProcessPOSIXLog::AtTopNestLevel() && + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) || + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) && + size <= POSIX_LOG_MEMORY_SHORT_BYTES))) + { + uintptr_t print_dst = 0; + // Format bytes from data by moving into print_dst for log output + for (unsigned i = 0; i < remainder; ++i) + print_dst |= (((data >> i*8) & 0xFF) << i*8); + log->Printf ("NativeProcessLinux::%s() [0x%" PRIx64 "]:0x%" PRIx64 " (0x%" PRIx64 ")", + __FUNCTION__, addr, uint64_t(print_dst), uint64_t(data)); + } + addr += k_ptrace_word_size; + dst += k_ptrace_word_size; + } + + if (log) + ProcessPOSIXLog::DecNestLevel(); + return Error(); +} + +Error +NativeProcessLinux::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) +{ + Error error = ReadMemory(addr, buf, size, bytes_read); + if (error.Fail()) return error; + return m_breakpoint_list.RemoveTrapsFromBuffer(addr, buf, size); +} + +Error +NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) +{ + const unsigned char *src = static_cast<const unsigned char*>(buf); + size_t remainder; + Error error; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL)); + if (log) + ProcessPOSIXLog::IncNestLevel(); + if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY)) + log->Printf ("NativeProcessLinux::%s(0x%" PRIx64 ", %p, %zu)", __FUNCTION__, addr, buf, size); + + for (bytes_written = 0; bytes_written < size; bytes_written += remainder) + { + remainder = size - bytes_written; + remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; + + if (remainder == k_ptrace_word_size) + { + unsigned long data = 0; + memcpy(&data, src, k_ptrace_word_size); + + if (log && ProcessPOSIXLog::AtTopNestLevel() && + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) || + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) && + size <= POSIX_LOG_MEMORY_SHORT_BYTES))) + log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__, + (void*)addr, *(const unsigned long*)src, data); + + error = NativeProcessLinux::PtraceWrapper(PTRACE_POKEDATA, GetID(), (void*)addr, (void*)data); + if (error.Fail()) + { + if (log) + ProcessPOSIXLog::DecNestLevel(); + return error; + } + } + else + { + unsigned char buff[8]; + size_t bytes_read; + error = ReadMemory(addr, buff, k_ptrace_word_size, bytes_read); + if (error.Fail()) + { + if (log) + ProcessPOSIXLog::DecNestLevel(); + return error; + } + + memcpy(buff, src, remainder); + + size_t bytes_written_rec; + error = WriteMemory(addr, buff, k_ptrace_word_size, bytes_written_rec); + if (error.Fail()) + { + if (log) + ProcessPOSIXLog::DecNestLevel(); + return error; + } + + if (log && ProcessPOSIXLog::AtTopNestLevel() && + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) || + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) && + size <= POSIX_LOG_MEMORY_SHORT_BYTES))) + log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__, + (void*)addr, *(const unsigned long*)src, *(unsigned long*)buff); + } + + addr += k_ptrace_word_size; + src += k_ptrace_word_size; + } + if (log) + ProcessPOSIXLog::DecNestLevel(); + return error; +} + +Error +NativeProcessLinux::Resume (lldb::tid_t tid, uint32_t signo) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + if (log) + log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " with signal %s", __FUNCTION__, tid, + Host::GetSignalAsCString(signo)); + + + + intptr_t data = 0; + + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + Error error = PtraceWrapper(PTRACE_CONT, tid, nullptr, (void*)data); + + if (log) + log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " result = %s", __FUNCTION__, tid, error.Success() ? "true" : "false"); + return error; +} + +Error +NativeProcessLinux::SingleStep(lldb::tid_t tid, uint32_t signo) +{ + intptr_t data = 0; + + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + // If hardware single-stepping is not supported, we just do a continue. The breakpoint on the + // next instruction has been setup in NativeProcessLinux::Resume. + return PtraceWrapper(SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP : PTRACE_CONT, + tid, nullptr, (void*)data); +} + +Error +NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) +{ + return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo); +} + +Error +NativeProcessLinux::GetEventMessage(lldb::tid_t tid, unsigned long *message) +{ + return PtraceWrapper(PTRACE_GETEVENTMSG, tid, nullptr, message); +} + +Error +NativeProcessLinux::Detach(lldb::tid_t tid) +{ + if (tid == LLDB_INVALID_THREAD_ID) + return Error(); + + return PtraceWrapper(PTRACE_DETACH, tid); +} + +bool +NativeProcessLinux::DupDescriptor(const FileSpec &file_spec, int fd, int flags) +{ + int target_fd = open(file_spec.GetCString(), flags, 0666); + + if (target_fd == -1) + return false; + + if (dup2(target_fd, fd) == -1) + return false; + + return (close(target_fd) == -1) ? false : true; +} + +bool +NativeProcessLinux::HasThreadNoLock (lldb::tid_t thread_id) +{ + for (auto thread_sp : m_threads) + { + assert (thread_sp && "thread list should not contain NULL threads"); + if (thread_sp->GetID () == thread_id) + { + // We have this thread. + return true; + } + } + + // We don't have this thread. + return false; +} + +bool +NativeProcessLinux::StopTrackingThread (lldb::tid_t thread_id) +{ + Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD); + + if (log) + log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", __FUNCTION__, thread_id); + + bool found = false; + + Mutex::Locker locker (m_threads_mutex); + for (auto it = m_threads.begin (); it != m_threads.end (); ++it) + { + if (*it && ((*it)->GetID () == thread_id)) + { + m_threads.erase (it); + found = true; + break; + } + } + + SignalIfAllThreadsStopped(); + + return found; +} + +NativeThreadLinuxSP +NativeProcessLinux::AddThread (lldb::tid_t thread_id) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + Mutex::Locker locker (m_threads_mutex); + + if (log) + { + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " adding thread with tid %" PRIu64, + __FUNCTION__, + GetID (), + thread_id); + } + + assert (!HasThreadNoLock (thread_id) && "attempted to add a thread by id that already exists"); + + // If this is the first thread, save it as the current thread + if (m_threads.empty ()) + SetCurrentThreadID (thread_id); + + auto thread_sp = std::make_shared<NativeThreadLinux>(this, thread_id); + m_threads.push_back (thread_sp); + return thread_sp; +} + +Error +NativeProcessLinux::FixupBreakpointPCAsNeeded(NativeThreadLinux &thread) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + Error error; + + // Find out the size of a breakpoint (might depend on where we are in the code). + NativeRegisterContextSP context_sp = thread.GetRegisterContext(); + if (!context_sp) + { + error.SetErrorString ("cannot get a NativeRegisterContext for the thread"); + if (log) + log->Printf ("NativeProcessLinux::%s failed: %s", __FUNCTION__, error.AsCString ()); + return error; + } + + uint32_t breakpoint_size = 0; + error = GetSoftwareBreakpointPCOffset(breakpoint_size); + if (error.Fail ()) + { + if (log) + log->Printf ("NativeProcessLinux::%s GetBreakpointSize() failed: %s", __FUNCTION__, error.AsCString ()); + return error; + } + else + { + if (log) + log->Printf ("NativeProcessLinux::%s breakpoint size: %" PRIu32, __FUNCTION__, breakpoint_size); + } + + // First try probing for a breakpoint at a software breakpoint location: PC - breakpoint size. + const lldb::addr_t initial_pc_addr = context_sp->GetPCfromBreakpointLocation (); + lldb::addr_t breakpoint_addr = initial_pc_addr; + if (breakpoint_size > 0) + { + // Do not allow breakpoint probe to wrap around. + if (breakpoint_addr >= breakpoint_size) + breakpoint_addr -= breakpoint_size; + } + + // Check if we stopped because of a breakpoint. + NativeBreakpointSP breakpoint_sp; + error = m_breakpoint_list.GetBreakpoint (breakpoint_addr, breakpoint_sp); + if (!error.Success () || !breakpoint_sp) + { + // We didn't find one at a software probe location. Nothing to do. + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " no lldb breakpoint found at current pc with adjustment: 0x%" PRIx64, __FUNCTION__, GetID (), breakpoint_addr); + return Error (); + } + + // If the breakpoint is not a software breakpoint, nothing to do. + if (!breakpoint_sp->IsSoftwareBreakpoint ()) + { + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " breakpoint found at 0x%" PRIx64 ", not software, nothing to adjust", __FUNCTION__, GetID (), breakpoint_addr); + return Error (); + } + + // + // We have a software breakpoint and need to adjust the PC. + // + + // Sanity check. + if (breakpoint_size == 0) + { + // Nothing to do! How did we get here? + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " breakpoint found at 0x%" PRIx64 ", it is software, but the size is zero, nothing to do (unexpected)", __FUNCTION__, GetID (), breakpoint_addr); + return Error (); + } + + // Change the program counter. + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 ": changing PC from 0x%" PRIx64 " to 0x%" PRIx64, __FUNCTION__, GetID(), thread.GetID(), initial_pc_addr, breakpoint_addr); + + error = context_sp->SetPC (breakpoint_addr); + if (error.Fail ()) + { + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 ": failed to set PC: %s", __FUNCTION__, GetID(), thread.GetID(), error.AsCString ()); + return error; + } + + return error; +} + +Error +NativeProcessLinux::GetLoadedModuleFileSpec(const char* module_path, FileSpec& file_spec) +{ + FileSpec module_file_spec(module_path, true); + + bool found = false; + file_spec.Clear(); + ProcFileReader::ProcessLineByLine(GetID(), "maps", + [&] (const std::string &line) + { + SmallVector<StringRef, 16> columns; + StringRef(line).split(columns, " ", -1, false); + if (columns.size() < 6) + return true; // continue searching + + FileSpec this_file_spec(columns[5].str().c_str(), false); + if (this_file_spec.GetFilename() != module_file_spec.GetFilename()) + return true; // continue searching + + file_spec = this_file_spec; + found = true; + return false; // we are done + }); + + if (! found) + return Error("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", + module_file_spec.GetFilename().AsCString(), GetID()); + + return Error(); +} + +Error +NativeProcessLinux::GetFileLoadAddress(const llvm::StringRef& file_name, lldb::addr_t& load_addr) +{ + load_addr = LLDB_INVALID_ADDRESS; + Error error = ProcFileReader::ProcessLineByLine (GetID (), "maps", + [&] (const std::string &line) -> bool + { + StringRef maps_row(line); + + SmallVector<StringRef, 16> maps_columns; + maps_row.split(maps_columns, StringRef(" "), -1, false); + + if (maps_columns.size() < 6) + { + // Return true to continue reading the proc file + return true; + } + + if (maps_columns[5] == file_name) + { + StringExtractor addr_extractor(maps_columns[0].str().c_str()); + load_addr = addr_extractor.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + + // Return false to stop reading the proc file further + return false; + } + + // Return true to continue reading the proc file + return true; + }); + return error; +} + +NativeThreadLinuxSP +NativeProcessLinux::GetThreadByID(lldb::tid_t tid) +{ + return std::static_pointer_cast<NativeThreadLinux>(NativeProcessProtocol::GetThreadByID(tid)); +} + +Error +NativeProcessLinux::ResumeThread(NativeThreadLinux &thread, lldb::StateType state, int signo) +{ + Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD); + + if (log) + log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", + __FUNCTION__, thread.GetID()); + + // Before we do the resume below, first check if we have a pending + // stop notification that is currently waiting for + // all threads to stop. This is potentially a buggy situation since + // we're ostensibly waiting for threads to stop before we send out the + // pending notification, and here we are resuming one before we send + // out the pending stop notification. + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && log) + { + log->Printf("NativeProcessLinux::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that is actively waiting for this thread to stop. Valid sequence of events?", __FUNCTION__, thread.GetID(), m_pending_notification_tid); + } + + // Request a resume. We expect this to be synchronous and the system + // to reflect it is running after this completes. + switch (state) + { + case eStateRunning: + { + thread.SetRunning(); + const auto resume_result = Resume(thread.GetID(), signo); + if (resume_result.Success()) + SetState(eStateRunning, true); + return resume_result; + } + case eStateStepping: + { + thread.SetStepping(); + const auto step_result = SingleStep(thread.GetID(), signo); + if (step_result.Success()) + SetState(eStateRunning, true); + return step_result; + } + default: + if (log) + log->Printf("NativeProcessLinux::%s Unhandled state %s.", + __FUNCTION__, StateAsCString(state)); + llvm_unreachable("Unhandled state for resume"); + } +} + +//===----------------------------------------------------------------------===// + +void +NativeProcessLinux::StopRunningThreads(const lldb::tid_t triggering_tid) +{ + Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD); + + if (log) + { + log->Printf("NativeProcessLinux::%s about to process event: (triggering_tid: %" PRIu64 ")", + __FUNCTION__, triggering_tid); + } + + m_pending_notification_tid = triggering_tid; + + // Request a stop for all the thread stops that need to be stopped + // and are not already known to be stopped. + for (const auto &thread_sp: m_threads) + { + if (StateIsRunningState(thread_sp->GetState())) + static_pointer_cast<NativeThreadLinux>(thread_sp)->RequestStop(); + } + + SignalIfAllThreadsStopped(); + + if (log) + { + log->Printf("NativeProcessLinux::%s event processing done", __FUNCTION__); + } +} + +void +NativeProcessLinux::SignalIfAllThreadsStopped() +{ + if (m_pending_notification_tid == LLDB_INVALID_THREAD_ID) + return; // No pending notification. Nothing to do. + + for (const auto &thread_sp: m_threads) + { + if (StateIsRunningState(thread_sp->GetState())) + return; // Some threads are still running. Don't signal yet. + } + + // We have a pending notification and all threads have stopped. + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS)); + + // Clear any temporary breakpoints we used to implement software single stepping. + for (const auto &thread_info: m_threads_stepping_with_breakpoint) + { + Error error = RemoveBreakpoint (thread_info.second); + if (error.Fail()) + if (log) + log->Printf("NativeProcessLinux::%s() pid = %" PRIu64 " remove stepping breakpoint: %s", + __FUNCTION__, thread_info.first, error.AsCString()); + } + m_threads_stepping_with_breakpoint.clear(); + + // Notify the delegate about the stop + SetCurrentThreadID(m_pending_notification_tid); + SetState(StateType::eStateStopped, true); + m_pending_notification_tid = LLDB_INVALID_THREAD_ID; +} + +void +NativeProcessLinux::ThreadWasCreated(NativeThreadLinux &thread) +{ + Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD); + + if (log) + log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", __FUNCTION__, thread.GetID()); + + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && StateIsRunningState(thread.GetState())) + { + // We will need to wait for this new thread to stop as well before firing the + // notification. + thread.RequestStop(); + } +} + +void +NativeProcessLinux::SigchldHandler() +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + // Process all pending waitpid notifications. + while (true) + { + int status = -1; + ::pid_t wait_pid = waitpid(-1, &status, __WALL | __WNOTHREAD | WNOHANG); + + if (wait_pid == 0) + break; // We are done. + + if (wait_pid == -1) + { + if (errno == EINTR) + continue; + + Error error(errno, eErrorTypePOSIX); + if (log) + log->Printf("NativeProcessLinux::%s waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG) failed: %s", + __FUNCTION__, error.AsCString()); + break; + } + + bool exited = false; + int signal = 0; + int exit_status = 0; + const char *status_cstr = nullptr; + if (WIFSTOPPED(status)) + { + signal = WSTOPSIG(status); + status_cstr = "STOPPED"; + } + else if (WIFEXITED(status)) + { + exit_status = WEXITSTATUS(status); + status_cstr = "EXITED"; + exited = true; + } + else if (WIFSIGNALED(status)) + { + signal = WTERMSIG(status); + status_cstr = "SIGNALED"; + if (wait_pid == static_cast< ::pid_t>(GetID())) { + exited = true; + exit_status = -1; + } + } + else + status_cstr = "(\?\?\?)"; + + if (log) + log->Printf("NativeProcessLinux::%s: waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG)" + "=> pid = %" PRIi32 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i", + __FUNCTION__, wait_pid, status, status_cstr, signal, exit_status); + + MonitorCallback (wait_pid, exited, signal, exit_status); + } +} + +// Wrapper for ptrace to catch errors and log calls. +// Note that ptrace sets errno on error because -1 can be a valid result (i.e. for PTRACE_PEEK*) +Error +NativeProcessLinux::PtraceWrapper(int req, lldb::pid_t pid, void *addr, void *data, size_t data_size, long *result) +{ + Error error; + long int ret; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PTRACE)); + + PtraceDisplayBytes(req, data, data_size); + + errno = 0; + if (req == PTRACE_GETREGSET || req == PTRACE_SETREGSET) + ret = ptrace(static_cast<__ptrace_request>(req), static_cast< ::pid_t>(pid), *(unsigned int *)addr, data); + else + ret = ptrace(static_cast<__ptrace_request>(req), static_cast< ::pid_t>(pid), addr, data); + + if (ret == -1) + error.SetErrorToErrno(); + + if (result) + *result = ret; + + if (log) + log->Printf("ptrace(%d, %" PRIu64 ", %p, %p, %zu)=%lX", req, pid, addr, data, data_size, ret); + + PtraceDisplayBytes(req, data, data_size); + + if (log && error.GetError() != 0) + { + const char* str; + switch (error.GetError()) + { + case ESRCH: str = "ESRCH"; break; + case EINVAL: str = "EINVAL"; break; + case EBUSY: str = "EBUSY"; break; + case EPERM: str = "EPERM"; break; + default: str = error.AsCString(); + } + log->Printf("ptrace() failed; errno=%d (%s)", error.GetError(), str); + } + + return error; +} diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.h b/source/Plugins/Process/Linux/NativeProcessLinux.h new file mode 100644 index 000000000000..7bac1dc8dbb7 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -0,0 +1,329 @@ +//===-- NativeProcessLinux.h ---------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeProcessLinux_H_ +#define liblldb_NativeProcessLinux_H_ + +// C++ Includes +#include <unordered_set> + +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/lldb-types.h" +#include "lldb/Host/Debug.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/MemoryRegionInfo.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "NativeThreadLinux.h" + +namespace lldb_private { + class Error; + class Module; + class Scalar; + +namespace process_linux { + /// @class NativeProcessLinux + /// @brief Manages communication with the inferior (debugee) process. + /// + /// Upon construction, this class prepares and launches an inferior process for + /// debugging. + /// + /// Changes in the inferior process state are broadcasted. + class NativeProcessLinux: public NativeProcessProtocol + { + friend Error + NativeProcessProtocol::Launch (ProcessLaunchInfo &launch_info, + NativeDelegate &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &process_sp); + + friend Error + NativeProcessProtocol::Attach (lldb::pid_t pid, + NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &process_sp); + + public: + // --------------------------------------------------------------------- + // NativeProcessProtocol Interface + // --------------------------------------------------------------------- + Error + Resume (const ResumeActionList &resume_actions) override; + + Error + Halt () override; + + Error + Detach () override; + + Error + Signal (int signo) override; + + Error + Interrupt () override; + + Error + Kill () override; + + Error + GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInfo &range_info) override; + + Error + ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override; + + Error + ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override; + + Error + WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override; + + Error + AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) override; + + Error + DeallocateMemory (lldb::addr_t addr) override; + + lldb::addr_t + GetSharedLibraryInfoAddress () override; + + size_t + UpdateThreads () override; + + bool + GetArchitecture (ArchSpec &arch) const override; + + Error + SetBreakpoint (lldb::addr_t addr, uint32_t size, bool hardware) override; + + void + DoStopIDBumped (uint32_t newBumpId) override; + + Error + GetLoadedModuleFileSpec(const char* module_path, FileSpec& file_spec) override; + + Error + GetFileLoadAddress(const llvm::StringRef& file_name, lldb::addr_t& load_addr) override; + + NativeThreadLinuxSP + GetThreadByID(lldb::tid_t id); + + // --------------------------------------------------------------------- + // Interface used by NativeRegisterContext-derived classes. + // --------------------------------------------------------------------- + static Error + PtraceWrapper(int req, + lldb::pid_t pid, + void *addr = nullptr, + void *data = nullptr, + size_t data_size = 0, + long *result = nullptr); + + protected: + // --------------------------------------------------------------------- + // NativeProcessProtocol protected interface + // --------------------------------------------------------------------- + Error + GetSoftwareBreakpointTrapOpcode (size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) override; + + private: + + MainLoop::SignalHandleUP m_sigchld_handle; + ArchSpec m_arch; + + LazyBool m_supports_mem_region; + std::vector<MemoryRegionInfo> m_mem_region_cache; + Mutex m_mem_region_cache_mutex; + + lldb::tid_t m_pending_notification_tid; + + // List of thread ids stepping with a breakpoint with the address of + // the relevan breakpoint + std::map<lldb::tid_t, lldb::addr_t> m_threads_stepping_with_breakpoint; + + /// @class LauchArgs + /// + /// @brief Simple structure to pass data to the thread responsible for + /// launching a child process. + struct LaunchArgs + { + LaunchArgs(Module *module, + char const **argv, + char const **envp, + const FileSpec &stdin_file_spec, + const FileSpec &stdout_file_spec, + const FileSpec &stderr_file_spec, + const FileSpec &working_dir, + const ProcessLaunchInfo &launch_info); + + ~LaunchArgs(); + + Module *m_module; // The executable image to launch. + char const **m_argv; // Process arguments. + char const **m_envp; // Process environment. + const FileSpec m_stdin_file_spec; // Redirect stdin if not empty. + const FileSpec m_stdout_file_spec; // Redirect stdout if not empty. + const FileSpec m_stderr_file_spec; // Redirect stderr if not empty. + const FileSpec m_working_dir; // Working directory or empty. + const ProcessLaunchInfo &m_launch_info; + }; + + typedef std::function< ::pid_t(Error &)> InitialOperation; + + // --------------------------------------------------------------------- + // Private Instance Methods + // --------------------------------------------------------------------- + NativeProcessLinux (); + + /// Launches an inferior process ready for debugging. Forms the + /// implementation of Process::DoLaunch. + void + LaunchInferior ( + MainLoop &mainloop, + Module *module, + char const *argv[], + char const *envp[], + const FileSpec &stdin_file_spec, + const FileSpec &stdout_file_spec, + const FileSpec &stderr_file_spec, + const FileSpec &working_dir, + const ProcessLaunchInfo &launch_info, + Error &error); + + /// Attaches to an existing process. Forms the + /// implementation of Process::DoAttach + void + AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error); + + ::pid_t + Launch(LaunchArgs *args, Error &error); + + ::pid_t + Attach(lldb::pid_t pid, Error &error); + + static Error + SetDefaultPtraceOpts(const lldb::pid_t); + + static bool + DupDescriptor(const FileSpec &file_spec, int fd, int flags); + + static void * + MonitorThread(void *baton); + + void + MonitorCallback(lldb::pid_t pid, bool exited, int signal, int status); + + void + WaitForNewThread(::pid_t tid); + + void + MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread); + + void + MonitorTrace(NativeThreadLinux &thread); + + void + MonitorBreakpoint(NativeThreadLinux &thread); + + void + MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index); + + void + MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited); + + bool + SupportHardwareSingleStepping() const; + + Error + SetupSoftwareSingleStepping(NativeThreadLinux &thread); + +#if 0 + static ::ProcessMessage::CrashReason + GetCrashReasonForSIGSEGV(const siginfo_t *info); + + static ::ProcessMessage::CrashReason + GetCrashReasonForSIGILL(const siginfo_t *info); + + static ::ProcessMessage::CrashReason + GetCrashReasonForSIGFPE(const siginfo_t *info); + + static ::ProcessMessage::CrashReason + GetCrashReasonForSIGBUS(const siginfo_t *info); +#endif + + bool + HasThreadNoLock (lldb::tid_t thread_id); + + bool + StopTrackingThread (lldb::tid_t thread_id); + + NativeThreadLinuxSP + AddThread (lldb::tid_t thread_id); + + Error + GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size); + + Error + FixupBreakpointPCAsNeeded(NativeThreadLinux &thread); + + /// Writes a siginfo_t structure corresponding to the given thread ID to the + /// memory region pointed to by @p siginfo. + Error + GetSignalInfo(lldb::tid_t tid, void *siginfo); + + /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG) + /// corresponding to the given thread ID to the memory pointed to by @p + /// message. + Error + GetEventMessage(lldb::tid_t tid, unsigned long *message); + + /// Resumes the given thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Error + Resume(lldb::tid_t tid, uint32_t signo); + + /// Single steps the given thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Error + SingleStep(lldb::tid_t tid, uint32_t signo); + + void + NotifyThreadDeath (lldb::tid_t tid); + + Error + Detach(lldb::tid_t tid); + + + // This method is requests a stop on all threads which are still running. It sets up a + // deferred delegate notification, which will fire once threads report as stopped. The + // triggerring_tid will be set as the current thread (main stop reason). + void + StopRunningThreads(lldb::tid_t triggering_tid); + + // Notify the delegate if all threads have stopped. + void SignalIfAllThreadsStopped(); + + // Resume the given thread, optionally passing it the given signal. The type of resume + // operation (continue, single-step) depends on the state parameter. + Error + ResumeThread(NativeThreadLinux &thread, lldb::StateType state, int signo); + + void + ThreadWasCreated(NativeThreadLinux &thread); + + void + SigchldHandler(); + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeProcessLinux_H_ diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp new file mode 100644 index 000000000000..df0a008ff5f7 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp @@ -0,0 +1,230 @@ +//===-- NativeRegisterContextLinux.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NativeRegisterContextLinux.h" + +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Host/linux/Ptrace.h" + +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" + +using namespace lldb_private; +using namespace lldb_private::process_linux; + +NativeRegisterContextLinux::NativeRegisterContextLinux(NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx, + RegisterInfoInterface *reg_info_interface_p) : + NativeRegisterContextRegisterInfo(native_thread, concrete_frame_idx, reg_info_interface_p) +{} + +lldb::ByteOrder +NativeRegisterContextLinux::GetByteOrder() const +{ + // Get the target process whose privileged thread was used for the register read. + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid; + + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + if (!process_sp) + return byte_order; + + if (!process_sp->GetByteOrder (byte_order)) + { + // FIXME log here + } + + return byte_order; +} + +Error +NativeRegisterContextLinux::ReadRegisterRaw(uint32_t reg_index, RegisterValue ®_value) +{ + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); + if (!reg_info) + return Error("register %" PRIu32 " not found", reg_index); + + return DoReadRegisterValue(reg_info->byte_offset, reg_info->name, reg_info->byte_size, reg_value); +} + +Error +NativeRegisterContextLinux::WriteRegisterRaw(uint32_t reg_index, const RegisterValue ®_value) +{ + uint32_t reg_to_write = reg_index; + RegisterValue value_to_write = reg_value; + + // Check if this is a subregister of a full register. + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_index); + if (reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) + { + Error error; + + RegisterValue full_value; + uint32_t full_reg = reg_info->invalidate_regs[0]; + const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg); + + // Read the full register. + error = ReadRegister(full_reg_info, full_value); + if (error.Fail ()) + return error; + + lldb::ByteOrder byte_order = GetByteOrder(); + uint8_t dst[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the full register. + const uint32_t dest_size = full_value.GetAsMemoryData (full_reg_info, + dst, + sizeof(dst), + byte_order, + error); + if (error.Success() && dest_size) + { + uint8_t src[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the source data. + const uint32_t src_size = reg_value.GetAsMemoryData (reg_info, src, sizeof(src), byte_order, error); + if (error.Success() && src_size && (src_size < dest_size)) + { + // Copy the src bytes to the destination. + memcpy (dst + (reg_info->byte_offset & 0x1), src, src_size); + // Set this full register as the value to write. + value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order); + value_to_write.SetType(full_reg_info); + reg_to_write = full_reg; + } + } + } + + const RegisterInfo *const register_to_write_info_p = GetRegisterInfoAtIndex (reg_to_write); + assert (register_to_write_info_p && "register to write does not have valid RegisterInfo"); + if (!register_to_write_info_p) + return Error("NativeRegisterContextLinux::%s failed to get RegisterInfo for write register index %" PRIu32, __FUNCTION__, reg_to_write); + + return DoWriteRegisterValue(reg_info->byte_offset, reg_info->name, reg_value); +} + +Error +NativeRegisterContextLinux::ReadGPR() +{ + void* buf = GetGPRBuffer(); + if (!buf) + return Error("GPR buffer is NULL"); + size_t buf_size = GetGPRSize(); + + return DoReadGPR(buf, buf_size); +} + +Error +NativeRegisterContextLinux::WriteGPR() +{ + void* buf = GetGPRBuffer(); + if (!buf) + return Error("GPR buffer is NULL"); + size_t buf_size = GetGPRSize(); + + return DoWriteGPR(buf, buf_size); +} + +Error +NativeRegisterContextLinux::ReadFPR() +{ + void* buf = GetFPRBuffer(); + if (!buf) + return Error("FPR buffer is NULL"); + size_t buf_size = GetFPRSize(); + + return DoReadFPR(buf, buf_size); +} + +Error +NativeRegisterContextLinux::WriteFPR() +{ + void* buf = GetFPRBuffer(); + if (!buf) + return Error("FPR buffer is NULL"); + size_t buf_size = GetFPRSize(); + + return DoWriteFPR(buf, buf_size); +} + +Error +NativeRegisterContextLinux::ReadRegisterSet(void *buf, size_t buf_size, unsigned int regset) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), + static_cast<void *>(®set), buf, buf_size); +} + +Error +NativeRegisterContextLinux::WriteRegisterSet(void *buf, size_t buf_size, unsigned int regset) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), + static_cast<void *>(®set), buf, buf_size); +} + +Error +NativeRegisterContextLinux::DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_REGISTERS)); + + long data; + Error error = NativeProcessLinux::PtraceWrapper( + PTRACE_PEEKUSER, m_thread.GetID(), reinterpret_cast<void *>(offset), nullptr, 0, &data); + + if (error.Success()) + // First cast to an unsigned of the same size to avoid sign extension. + value.SetUInt64(static_cast<unsigned long>(data)); + + if (log) + log->Printf ("NativeRegisterContextLinux::%s() reg %s: 0x%lx", __FUNCTION__, reg_name, data); + + return error; +} + +Error +NativeRegisterContextLinux::DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_REGISTERS)); + + void* buf = reinterpret_cast<void *>(value.GetAsUInt64()); + + if (log) + log->Printf ("NativeRegisterContextLinux::%s() reg %s: %p", __FUNCTION__, reg_name, buf); + + return NativeProcessLinux::PtraceWrapper( + PTRACE_POKEUSER, m_thread.GetID(), reinterpret_cast<void *>(offset), buf); +} + +Error +NativeRegisterContextLinux::DoReadGPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), nullptr, buf, buf_size); +} + +Error +NativeRegisterContextLinux::DoWriteGPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGS, m_thread.GetID(), nullptr, buf, buf_size); +} + +Error +NativeRegisterContextLinux::DoReadFPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_GETFPREGS, m_thread.GetID(), nullptr, buf, buf_size); +} + +Error +NativeRegisterContextLinux::DoWriteFPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_SETFPREGS, m_thread.GetID(), nullptr, buf, buf_size); +} diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux.h new file mode 100644 index 000000000000..0b0b747984b4 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux.h @@ -0,0 +1,107 @@ +//===-- NativeRegisterContextLinux.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_NativeRegisterContextLinux_h +#define lldb_NativeRegisterContextLinux_h + +#include "lldb/Host/common/NativeRegisterContextRegisterInfo.h" +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include "Plugins/Process/Linux/NativeProcessLinux.h" + +namespace lldb_private { +namespace process_linux { + +class NativeRegisterContextLinux : public NativeRegisterContextRegisterInfo +{ +public: + NativeRegisterContextLinux(NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx, + RegisterInfoInterface *reg_info_interface_p); + + // This function is implemented in the NativeRegisterContextLinux_* subclasses to create a new + // instance of the host specific NativeRegisterContextLinux. The implementations can't collide + // as only one NativeRegisterContextLinux_* variant should be compiled into the final + // executable. + static NativeRegisterContextLinux* + CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + +protected: + lldb::ByteOrder + GetByteOrder() const; + + virtual Error + ReadRegisterRaw(uint32_t reg_index, RegisterValue& reg_value); + + virtual Error + WriteRegisterRaw(uint32_t reg_index, const RegisterValue& reg_value); + + virtual Error + ReadRegisterSet(void *buf, size_t buf_size, unsigned int regset); + + virtual Error + WriteRegisterSet(void *buf, size_t buf_size, unsigned int regset); + + virtual Error + ReadGPR(); + + virtual Error + WriteGPR(); + + virtual Error + ReadFPR(); + + virtual Error + WriteFPR(); + + virtual void* + GetGPRBuffer() { return nullptr; } + + virtual size_t + GetGPRSize() { return GetRegisterInfoInterface().GetGPRSize(); } + + virtual void* + GetFPRBuffer() { return nullptr; } + + virtual size_t + GetFPRSize() { return 0; } + + + // The Do*** functions are executed on the privileged thread and can perform ptrace + // operations directly. + virtual Error + DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value); + + virtual Error + DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value); + + virtual Error + DoReadGPR(void *buf, size_t buf_size); + + virtual Error + DoWriteGPR(void *buf, size_t buf_size); + + virtual Error + DoReadFPR(void *buf, size_t buf_size); + + virtual Error + DoWriteFPR(void *buf, size_t buf_size); +}; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_h diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp new file mode 100644 index 000000000000..31752fed5b42 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -0,0 +1,1017 @@ +//===-- NativeRegisterContextLinux_arm.cpp --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__arm__) + +#include "NativeRegisterContextLinux_arm.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" + +#include "Plugins/Process/Utility/RegisterContextLinux_arm.h" + +#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof (m_fpr)) + +#ifndef PTRACE_GETVFPREGS + #define PTRACE_GETVFPREGS 27 + #define PTRACE_SETVFPREGS 28 +#endif +#ifndef PTRACE_GETHBPREGS + #define PTRACE_GETHBPREGS 29 + #define PTRACE_SETHBPREGS 30 +#endif +#if !defined(PTRACE_TYPE_ARG3) + #define PTRACE_TYPE_ARG3 void * +#endif +#if !defined(PTRACE_TYPE_ARG4) + #define PTRACE_TYPE_ARG4 void * +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_linux; + +// arm general purpose registers. +static const uint32_t g_gpr_regnums_arm[] = +{ + gpr_r0_arm, + gpr_r1_arm, + gpr_r2_arm, + gpr_r3_arm, + gpr_r4_arm, + gpr_r5_arm, + gpr_r6_arm, + gpr_r7_arm, + gpr_r8_arm, + gpr_r9_arm, + gpr_r10_arm, + gpr_r11_arm, + gpr_r12_arm, + gpr_sp_arm, + gpr_lr_arm, + gpr_pc_arm, + gpr_cpsr_arm, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert(((sizeof g_gpr_regnums_arm / sizeof g_gpr_regnums_arm[0]) - 1) == k_num_gpr_registers_arm, \ + "g_gpr_regnums_arm has wrong number of register infos"); + +// arm floating point registers. +static const uint32_t g_fpu_regnums_arm[] = +{ + fpu_s0_arm, + fpu_s1_arm, + fpu_s2_arm, + fpu_s3_arm, + fpu_s4_arm, + fpu_s5_arm, + fpu_s6_arm, + fpu_s7_arm, + fpu_s8_arm, + fpu_s9_arm, + fpu_s10_arm, + fpu_s11_arm, + fpu_s12_arm, + fpu_s13_arm, + fpu_s14_arm, + fpu_s15_arm, + fpu_s16_arm, + fpu_s17_arm, + fpu_s18_arm, + fpu_s19_arm, + fpu_s20_arm, + fpu_s21_arm, + fpu_s22_arm, + fpu_s23_arm, + fpu_s24_arm, + fpu_s25_arm, + fpu_s26_arm, + fpu_s27_arm, + fpu_s28_arm, + fpu_s29_arm, + fpu_s30_arm, + fpu_s31_arm, + fpu_fpscr_arm, + fpu_d0_arm, + fpu_d1_arm, + fpu_d2_arm, + fpu_d3_arm, + fpu_d4_arm, + fpu_d5_arm, + fpu_d6_arm, + fpu_d7_arm, + fpu_d8_arm, + fpu_d9_arm, + fpu_d10_arm, + fpu_d11_arm, + fpu_d12_arm, + fpu_d13_arm, + fpu_d14_arm, + fpu_d15_arm, + fpu_d16_arm, + fpu_d17_arm, + fpu_d18_arm, + fpu_d19_arm, + fpu_d20_arm, + fpu_d21_arm, + fpu_d22_arm, + fpu_d23_arm, + fpu_d24_arm, + fpu_d25_arm, + fpu_d26_arm, + fpu_d27_arm, + fpu_d28_arm, + fpu_d29_arm, + fpu_d30_arm, + fpu_d31_arm, + fpu_q0_arm, + fpu_q1_arm, + fpu_q2_arm, + fpu_q3_arm, + fpu_q4_arm, + fpu_q5_arm, + fpu_q6_arm, + fpu_q7_arm, + fpu_q8_arm, + fpu_q9_arm, + fpu_q10_arm, + fpu_q11_arm, + fpu_q12_arm, + fpu_q13_arm, + fpu_q14_arm, + fpu_q15_arm, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert(((sizeof g_fpu_regnums_arm / sizeof g_fpu_regnums_arm[0]) - 1) == k_num_fpr_registers_arm, \ + "g_fpu_regnums_arm has wrong number of register infos"); + +namespace { + // Number of register sets provided by this context. + enum + { + k_num_register_sets = 2 + }; +} + +// Register sets for arm. +static const RegisterSet +g_reg_sets_arm[k_num_register_sets] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers_arm, g_gpr_regnums_arm }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_arm, g_fpu_regnums_arm } +}; + +NativeRegisterContextLinux* +NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) +{ + return new NativeRegisterContextLinux_arm(target_arch, native_thread, concrete_frame_idx); +} + +NativeRegisterContextLinux_arm::NativeRegisterContextLinux_arm (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) : + NativeRegisterContextLinux (native_thread, concrete_frame_idx, new RegisterContextLinux_arm(target_arch)) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::arm: + m_reg_info.num_registers = k_num_registers_arm; + m_reg_info.num_gpr_registers = k_num_gpr_registers_arm; + m_reg_info.num_fpr_registers = k_num_fpr_registers_arm; + m_reg_info.last_gpr = k_last_gpr_arm; + m_reg_info.first_fpr = k_first_fpr_arm; + m_reg_info.last_fpr = k_last_fpr_arm; + m_reg_info.first_fpr_v = fpu_s0_arm; + m_reg_info.last_fpr_v = fpu_s31_arm; + m_reg_info.gpr_flags = gpr_cpsr_arm; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } + + ::memset(&m_fpr, 0, sizeof (m_fpr)); + ::memset(&m_gpr_arm, 0, sizeof (m_gpr_arm)); + ::memset(&m_hwp_regs, 0, sizeof (m_hwp_regs)); + + // 16 is just a maximum value, query hardware for actual watchpoint count + m_max_hwp_supported = 16; + m_max_hbp_supported = 16; + m_refresh_hwdebug_info = true; +} + +uint32_t +NativeRegisterContextLinux_arm::GetRegisterSetCount () const +{ + return k_num_register_sets; +} + +uint32_t +NativeRegisterContextLinux_arm::GetUserRegisterCount() const +{ + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + count += g_reg_sets_arm[set_index].num_registers; + return count; +} + +const RegisterSet * +NativeRegisterContextLinux_arm::GetRegisterSet (uint32_t set_index) const +{ + if (set_index < k_num_register_sets) + return &g_reg_sets_arm[set_index]; + + return nullptr; +} + +Error +NativeRegisterContextLinux_arm::ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) +{ + Error error; + + if (!reg_info) + { + error.SetErrorString ("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (IsFPR(reg)) + { + error = ReadFPR(); + if (error.Fail()) + return error; + } + else + { + uint32_t full_reg = reg; + bool is_subreg = reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM); + + if (is_subreg) + { + // Read the full aligned 64-bit register. + full_reg = reg_info->invalidate_regs[0]; + } + + error = ReadRegisterRaw(full_reg, reg_value); + + if (error.Success ()) + { + // If our read was not aligned (for ah,bh,ch,dh), shift our returned value one byte to the right. + if (is_subreg && (reg_info->byte_offset & 0x1)) + reg_value.SetUInt64(reg_value.GetAsUInt64() >> 8); + + // If our return byte size was greater than the return value reg size, then + // use the type specified by reg_info rather than the uint64_t default + if (reg_value.GetByteSize() > reg_info->byte_size) + reg_value.SetType(reg_info); + } + return error; + } + + // Get pointer to m_fpr variable and set the data from it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert (fpr_offset < sizeof m_fpr); + uint8_t *src = (uint8_t *)&m_fpr + fpr_offset; + switch (reg_info->byte_size) + { + case 2: + reg_value.SetUInt16(*(uint16_t *)src); + break; + case 4: + reg_value.SetUInt32(*(uint32_t *)src); + break; + case 8: + reg_value.SetUInt64(*(uint64_t *)src); + break; + case 16: + reg_value.SetBytes(src, 16, GetByteOrder()); + break; + default: + assert(false && "Unhandled data size."); + error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size); + break; + } + + return error; +} + +Error +NativeRegisterContextLinux_arm::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + if (!reg_info) + return Error ("reg_info NULL"); + + const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg_index == LLDB_INVALID_REGNUM) + return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : "<unknown register>"); + + if (IsGPR(reg_index)) + return WriteRegisterRaw(reg_index, reg_value); + + if (IsFPR(reg_index)) + { + // Get pointer to m_fpr variable and set the data to it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert (fpr_offset < sizeof m_fpr); + uint8_t *dst = (uint8_t *)&m_fpr + fpr_offset; + switch (reg_info->byte_size) + { + case 2: + *(uint16_t *)dst = reg_value.GetAsUInt16(); + break; + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + default: + assert(false && "Unhandled data size."); + return Error ("unhandled register data size %" PRIu32, reg_info->byte_size); + } + + Error error = WriteFPR(); + if (error.Fail()) + return error; + + return Error (); + } + + return Error ("failed - register wasn't recognized to be a GPR or an FPR, write strategy unknown"); +} + +Error +NativeRegisterContextLinux_arm::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + Error error; + + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (!data_sp) + return Error ("failed to allocate DataBufferHeap instance of size %" PRIu64, (uint64_t)REG_CONTEXT_SIZE); + + error = ReadGPR(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes (); + if (dst == nullptr) + { + error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", (uint64_t)REG_CONTEXT_SIZE); + return error; + } + + ::memcpy (dst, &m_gpr_arm, GetGPRSize()); + dst += GetGPRSize(); + ::memcpy (dst, &m_fpr, sizeof(m_fpr)); + + return error; +} + +Error +NativeRegisterContextLinux_arm::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + Error error; + + if (!data_sp) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s invalid data_sp provided", __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize () != REG_CONTEXT_SIZE) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, (uint64_t)REG_CONTEXT_SIZE, data_sp->GetByteSize ()); + return error; + } + + + uint8_t *src = data_sp->GetBytes (); + if (src == nullptr) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__); + return error; + } + ::memcpy (&m_gpr_arm, src, GetRegisterInfoInterface ().GetGPRSize ()); + + error = WriteGPR(); + if (error.Fail()) + return error; + + src += GetRegisterInfoInterface ().GetGPRSize (); + ::memcpy (&m_fpr, src, sizeof(m_fpr)); + + error = WriteFPR(); + if (error.Fail()) + return error; + + return error; +} + +bool +NativeRegisterContextLinux_arm::IsGPR(unsigned reg) const +{ + return reg <= m_reg_info.last_gpr; // GPR's come first. +} + +bool +NativeRegisterContextLinux_arm::IsFPR(unsigned reg) const +{ + return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr); +} + +uint32_t +NativeRegisterContextLinux_arm::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, bp_index = 0; + + // Check if size has a valid hardware breakpoint length. + // Thumb instructions are 2-bytes but we have no way here to determine + // if target address is a thumb or arm instruction. + // TODO: Add support for setting thumb mode hardware breakpoints + if (size != 4 && size != 2) + return LLDB_INVALID_INDEX32; + + // Setup control value + // Make the byte_mask into a valid Byte Address Select mask + control_value = 0xfu << 5; + + // Enable this breakpoint and make it stop in privileged or user mode; + control_value |= 7; + + // Make sure bits 1:0 are clear in our address + // This should be different once we support thumb here. + addr &= ~((lldb::addr_t)3); + + // Iterate over stored hardware breakpoints + // Find a free bp_index or update reference count if duplicate. + bp_index = LLDB_INVALID_INDEX32; + + for (uint32_t i = 0; i < m_max_hbp_supported; i++) + { + if ((m_hbr_regs[i].control & 1) == 0) + { + bp_index = i; // Mark last free slot + } + else if (m_hbr_regs[i].address == addr && m_hbr_regs[i].control == control_value) + { + bp_index = i; // Mark duplicate index + break; // Stop searching here + } + } + + if (bp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Add new or update existing breakpoint + if ((m_hbr_regs[bp_index].control & 1) == 0) + { + m_hbr_regs[bp_index].address = addr; + m_hbr_regs[bp_index].control = control_value; + m_hbr_regs[bp_index].refcount = 1; + + // PTRACE call to set corresponding hardware breakpoint register. + error = WriteHardwareDebugRegs(eDREGTypeBREAK, bp_index); + + if (error.Fail()) + { + m_hbr_regs[bp_index].address = 0; + m_hbr_regs[bp_index].control &= ~1; + m_hbr_regs[bp_index].refcount = 0; + + return LLDB_INVALID_INDEX32; + } + } + else + m_hbr_regs[bp_index].refcount++; + + return bp_index; +} + +bool +NativeRegisterContextLinux_arm::ClearHardwareBreakpoint (uint32_t hw_idx) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return false; + + if (hw_idx >= m_max_hbp_supported) + return false; + + // Update reference count if multiple references. + if (m_hbr_regs[hw_idx].refcount > 1) + { + m_hbr_regs[hw_idx].refcount--; + return true; + } + else if (m_hbr_regs[hw_idx].refcount == 1) + { + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address; + uint32_t tempControl = m_hbr_regs[hw_idx].control; + uint32_t tempRefCount = m_hbr_regs[hw_idx].refcount; + + m_hbr_regs[hw_idx].control &= ~1; + m_hbr_regs[hw_idx].address = 0; + m_hbr_regs[hw_idx].refcount = 0; + + // PTRACE call to clear corresponding hardware breakpoint register. + WriteHardwareDebugRegs(eDREGTypeBREAK, hw_idx); + + if (error.Fail()) + { + m_hbr_regs[hw_idx].control = tempControl; + m_hbr_regs[hw_idx].address = tempAddr; + m_hbr_regs[hw_idx].refcount = tempRefCount; + + return false; + } + + return true; + } + + return false; +} + +uint32_t +NativeRegisterContextLinux_arm::NumSupportedHardwareWatchpoints () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + return m_max_hwp_supported; +} + +uint32_t +NativeRegisterContextLinux_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, wp_index = 0, addr_word_offset = 0, byte_mask = 0; + + // Check if we are setting watchpoint other than read/write/access + // Also update watchpoint flag to match Arm write-read bit configuration. + switch (watch_flags) + { + case 1: + watch_flags = 2; + break; + case 2: + watch_flags = 1; + break; + case 3: + break; + default: + return LLDB_INVALID_INDEX32; + } + + // Can't watch zero bytes + // Can't watch more than 4 bytes per WVR/WCR pair + + if (size == 0 || size > 4) + return LLDB_INVALID_INDEX32; + + // We can only watch up to four bytes that follow a 4 byte aligned address + // per watchpoint register pair, so make sure we can properly encode this. + addr_word_offset = addr % 4; + byte_mask = ((1u << size) - 1u) << addr_word_offset; + + // Check if we need multiple watchpoint register + if (byte_mask > 0xfu) + return LLDB_INVALID_INDEX32; + + // Setup control value + // Make the byte_mask into a valid Byte Address Select mask + control_value = byte_mask << 5; + + //Turn on appropriate watchpoint flags read or write + control_value |= (watch_flags << 3); + + // Enable this watchpoint and make it stop in privileged or user mode; + control_value |= 7; + + // Make sure bits 1:0 are clear in our address + addr &= ~((lldb::addr_t)3); + + // Iterate over stored watchpoints + // Find a free wp_index or update reference count if duplicate. + wp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hwp_supported; i++) + { + if ((m_hwp_regs[i].control & 1) == 0) + { + wp_index = i; // Mark last free slot + } + else if (m_hwp_regs[i].address == addr && m_hwp_regs[i].control == control_value) + { + wp_index = i; // Mark duplicate index + break; // Stop searching here + } + } + + if (wp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Add new or update existing watchpoint + if ((m_hwp_regs[wp_index].control & 1) == 0) + { + // Update watchpoint in local cache + m_hwp_regs[wp_index].address = addr; + m_hwp_regs[wp_index].control = control_value; + m_hwp_regs[wp_index].refcount = 1; + + // PTRACE call to set corresponding watchpoint register. + error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index); + + if (error.Fail()) + { + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].control &= ~1; + m_hwp_regs[wp_index].refcount = 0; + + return LLDB_INVALID_INDEX32; + } + } + else + m_hwp_regs[wp_index].refcount++; + + return wp_index; +} + +bool +NativeRegisterContextLinux_arm::ClearHardwareWatchpoint (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return false; + + if (wp_index >= m_max_hwp_supported) + return false; + + // Update reference count if multiple references. + if (m_hwp_regs[wp_index].refcount > 1) + { + m_hwp_regs[wp_index].refcount--; + return true; + } + else if (m_hwp_regs[wp_index].refcount == 1) + { + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; + uint32_t tempControl = m_hwp_regs[wp_index].control; + uint32_t tempRefCount = m_hwp_regs[wp_index].refcount; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].control &= ~1; + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].refcount = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index); + + if (error.Fail()) + { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; + m_hwp_regs[wp_index].refcount = tempRefCount; + + return false; + } + + return true; + } + + return false; +} + +Error +NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return error; + + lldb::addr_t tempAddr = 0; + uint32_t tempControl = 0, tempRefCount = 0; + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) + { + if (m_hwp_regs[i].control & 0x01) + { + // Create a backup we can revert to in case of failure. + tempAddr = m_hwp_regs[i].address; + tempControl = m_hwp_regs[i].control; + tempRefCount = m_hwp_regs[i].refcount; + + // Clear watchpoints in local cache + m_hwp_regs[i].control &= ~1; + m_hwp_regs[i].address = 0; + m_hwp_regs[i].refcount = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH, i); + + if (error.Fail()) + { + m_hwp_regs[i].control = tempControl; + m_hwp_regs[i].address = tempAddr; + m_hwp_regs[i].refcount = tempRefCount; + + return error; + } + } + } + + return Error(); +} + +uint32_t +NativeRegisterContextLinux_arm::GetWatchpointSize(uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + switch ((m_hwp_regs[wp_index].control >> 5) & 0x0f) + { + case 0x01: + return 1; + case 0x03: + return 2; + case 0x07: + return 3; + case 0x0f: + return 4; + default: + return 0; + } +} +bool +NativeRegisterContextLinux_arm::WatchpointIsEnabled(uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + if ((m_hwp_regs[wp_index].control & 0x1) == 0x1) + return true; + else + return false; +} + +Error +NativeRegisterContextLinux_arm::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + uint32_t watch_size; + lldb::addr_t watch_addr; + + for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) + { + watch_size = GetWatchpointSize (wp_index); + watch_addr = m_hwp_regs[wp_index].address; + + if (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index) + && trap_addr >= watch_addr && trap_addr < watch_addr + watch_size) + { + return Error(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +lldb::addr_t +NativeRegisterContextLinux_arm::GetWatchpointAddress (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].address; + else + return LLDB_INVALID_ADDRESS; +} + +Error +NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() +{ + Error error; + + if (!m_refresh_hwdebug_info) + { + return Error(); + } + + unsigned int cap_val; + + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETHBPREGS, m_thread.GetID(), nullptr, &cap_val, sizeof(unsigned int)); + + if (error.Fail()) + return error; + + m_max_hwp_supported = (cap_val >> 8) & 0xff; + m_max_hbp_supported = cap_val & 0xff; + m_refresh_hwdebug_info = false; + + return error; +} + +Error +NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType, int hwb_index) +{ + Error error; + + lldb::addr_t *addr_buf; + uint32_t *ctrl_buf; + + if (hwbType == eDREGTypeWATCH) + { + addr_buf = &m_hwp_regs[hwb_index].address; + ctrl_buf = &m_hwp_regs[hwb_index].control; + + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS, + m_thread.GetID(), (PTRACE_TYPE_ARG3) -((hwb_index << 1) + 1), + addr_buf, sizeof(unsigned int)); + + if (error.Fail()) + return error; + + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS, + m_thread.GetID(), (PTRACE_TYPE_ARG3) -((hwb_index << 1) + 2), + ctrl_buf, sizeof(unsigned int)); + } + else + { + addr_buf = &m_hwp_regs[hwb_index].address; + ctrl_buf = &m_hwp_regs[hwb_index].control; + + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS, + m_thread.GetID(), (PTRACE_TYPE_ARG3) ((hwb_index << 1) + 1), + addr_buf, sizeof(unsigned int)); + + if (error.Fail()) + return error; + + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS, + m_thread.GetID(), (PTRACE_TYPE_ARG3) ((hwb_index << 1) + 2), + ctrl_buf, sizeof(unsigned int)); + + } + + return error; +} + +uint32_t +NativeRegisterContextLinux_arm::CalculateFprOffset(const RegisterInfo* reg_info) const +{ + return reg_info->byte_offset - GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset; +} + +Error +NativeRegisterContextLinux_arm::DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) +{ + // PTRACE_POKEUSER don't work in the aarch64 liux kernel used on android devices (always return + // "Bad address"). To avoid using PTRACE_POKEUSER we read out the full GPR register set, modify + // the requested register and write it back. This approach is about 4 times slower but the + // performance overhead is negligible in comparision to processing time in lldb-server. + assert(offset % 4 == 0 && "Try to write a register with unaligned offset"); + if (offset + sizeof(uint32_t) > sizeof(m_gpr_arm)) + return Error("Register isn't fit into the size of the GPR area"); + + Error error = DoReadGPR(m_gpr_arm, sizeof(m_gpr_arm)); + if (error.Fail()) + return error; + + uint32_t reg_value = value.GetAsUInt32(); + // As precaution for an undefined behavior encountered while setting PC we + // will clear thumb bit of new PC if we are already in thumb mode; that is + // CPSR thumb mode bit is set. + if (offset / sizeof(uint32_t) == gpr_pc_arm) + { + // Check if we are already in thumb mode and + // thumb bit of current PC is read out to be zero and + // thumb bit of next PC is read out to be one. + if ((m_gpr_arm[gpr_cpsr_arm] & 0x20) && + !(m_gpr_arm[gpr_pc_arm] & 0x01) && + (value.GetAsUInt32() & 0x01)) + { + reg_value &= (~1ull); + } + } + + m_gpr_arm[offset / sizeof(uint32_t)] = reg_value; + return DoWriteGPR(m_gpr_arm, sizeof(m_gpr_arm)); +} + +Error +NativeRegisterContextLinux_arm::DoReadFPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_GETVFPREGS, + m_thread.GetID(), + nullptr, + buf, + buf_size); +} + +Error +NativeRegisterContextLinux_arm::DoWriteFPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_SETVFPREGS, + m_thread.GetID(), + nullptr, + buf, + buf_size); +} + +#endif // defined(__arm__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h new file mode 100644 index 000000000000..611b36ad4db1 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h @@ -0,0 +1,185 @@ +//===-- NativeRegisterContextLinux_arm.h ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__arm__) // arm register context only needed on arm devices + +#ifndef lldb_NativeRegisterContextLinux_arm_h +#define lldb_NativeRegisterContextLinux_arm_h + +#include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/lldb-arm-register-enums.h" + +namespace lldb_private { +namespace process_linux { + + class NativeProcessLinux; + + class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux + { + public: + NativeRegisterContextLinux_arm (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + + uint32_t + GetRegisterSetCount () const override; + + const RegisterSet * + GetRegisterSet (uint32_t set_index) const override; + + uint32_t + GetUserRegisterCount() const override; + + Error + ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) override; + + Error + WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + Error + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override; + + Error + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; + + //------------------------------------------------------------------ + // Hardware breakpoints/watchpoint mangement functions + //------------------------------------------------------------------ + + uint32_t + SetHardwareBreakpoint (lldb::addr_t addr, size_t size) override; + + bool + ClearHardwareBreakpoint (uint32_t hw_idx) override; + + uint32_t + NumSupportedHardwareWatchpoints () override; + + uint32_t + SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) override; + + bool + ClearHardwareWatchpoint (uint32_t hw_index) override; + + Error + ClearAllHardwareWatchpoints () override; + + Error + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; + + lldb::addr_t + GetWatchpointAddress (uint32_t wp_index) override; + + uint32_t + GetWatchpointSize(uint32_t wp_index); + + bool + WatchpointIsEnabled(uint32_t wp_index); + + // Debug register type select + enum DREGType + { + eDREGTypeWATCH = 0, + eDREGTypeBREAK + }; + + protected: + Error + DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) override; + + Error + DoReadFPR(void *buf, size_t buf_size) override; + + Error + DoWriteFPR(void *buf, size_t buf_size) override; + + void* + GetGPRBuffer() override { return &m_gpr_arm; } + + void* + GetFPRBuffer() override { return &m_fpr; } + + size_t + GetFPRSize() override { return sizeof(m_fpr); } + + private: + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + + uint32_t first_fpr_v; + uint32_t last_fpr_v; + + uint32_t gpr_flags; + }; + + struct QReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + union { + uint32_t s[32]; + uint64_t d[32]; + QReg q[16]; // the 128-bit NEON registers + } floats; + uint32_t fpscr; + }; + + uint32_t m_gpr_arm[k_num_gpr_registers_arm]; + RegInfo m_reg_info; + FPU m_fpr; + + // Debug register info for hardware breakpoints and watchpoints management. + struct DREG + { + lldb::addr_t address; // Breakpoint/watchpoint address value. + uint32_t control; // Breakpoint/watchpoint control value. + uint32_t refcount; // Serves as enable/disable and refernce counter. + }; + + struct DREG m_hbr_regs[16]; // Arm native linux hardware breakpoints + struct DREG m_hwp_regs[16]; // Arm native linux hardware watchpoints + + uint32_t m_max_hwp_supported; + uint32_t m_max_hbp_supported; + bool m_refresh_hwdebug_info; + + bool + IsGPR(unsigned reg) const; + + bool + IsFPR(unsigned reg) const; + + Error + ReadHardwareDebugInfo(); + + Error + WriteHardwareDebugRegs(int hwbType, int hwb_index); + + uint32_t + CalculateFprOffset(const RegisterInfo* reg_info) const; + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_arm_h + +#endif // defined(__arm__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp new file mode 100644 index 000000000000..e4d26fc640f3 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -0,0 +1,1042 @@ +//===-- NativeRegisterContextLinux_arm64.cpp --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined (__arm64__) || defined (__aarch64__) + +#include "NativeRegisterContextLinux_arm64.h" + +// C Includes +// C++ Includes + +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/common/NativeProcessProtocol.h" + +#include "Plugins/Process/Linux/NativeProcessLinux.h" +#include "Plugins/Process/Linux/Procfs.h" +#include "Plugins/Process/Utility/RegisterContextLinux_arm64.h" + +// System includes - They have to be included after framework includes because they define some +// macros which collide with variable names in other modules +#include <sys/socket.h> +// NT_PRSTATUS and NT_FPREGSET definition +#include <elf.h> + +#define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize()) + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_linux; + +// ARM64 general purpose registers. +static const uint32_t g_gpr_regnums_arm64[] = +{ + gpr_x0_arm64, + gpr_x1_arm64, + gpr_x2_arm64, + gpr_x3_arm64, + gpr_x4_arm64, + gpr_x5_arm64, + gpr_x6_arm64, + gpr_x7_arm64, + gpr_x8_arm64, + gpr_x9_arm64, + gpr_x10_arm64, + gpr_x11_arm64, + gpr_x12_arm64, + gpr_x13_arm64, + gpr_x14_arm64, + gpr_x15_arm64, + gpr_x16_arm64, + gpr_x17_arm64, + gpr_x18_arm64, + gpr_x19_arm64, + gpr_x20_arm64, + gpr_x21_arm64, + gpr_x22_arm64, + gpr_x23_arm64, + gpr_x24_arm64, + gpr_x25_arm64, + gpr_x26_arm64, + gpr_x27_arm64, + gpr_x28_arm64, + gpr_fp_arm64, + gpr_lr_arm64, + gpr_sp_arm64, + gpr_pc_arm64, + gpr_cpsr_arm64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert(((sizeof g_gpr_regnums_arm64 / sizeof g_gpr_regnums_arm64[0]) - 1) == k_num_gpr_registers_arm64, \ + "g_gpr_regnums_arm64 has wrong number of register infos"); + +// ARM64 floating point registers. +static const uint32_t g_fpu_regnums_arm64[] = +{ + fpu_v0_arm64, + fpu_v1_arm64, + fpu_v2_arm64, + fpu_v3_arm64, + fpu_v4_arm64, + fpu_v5_arm64, + fpu_v6_arm64, + fpu_v7_arm64, + fpu_v8_arm64, + fpu_v9_arm64, + fpu_v10_arm64, + fpu_v11_arm64, + fpu_v12_arm64, + fpu_v13_arm64, + fpu_v14_arm64, + fpu_v15_arm64, + fpu_v16_arm64, + fpu_v17_arm64, + fpu_v18_arm64, + fpu_v19_arm64, + fpu_v20_arm64, + fpu_v21_arm64, + fpu_v22_arm64, + fpu_v23_arm64, + fpu_v24_arm64, + fpu_v25_arm64, + fpu_v26_arm64, + fpu_v27_arm64, + fpu_v28_arm64, + fpu_v29_arm64, + fpu_v30_arm64, + fpu_v31_arm64, + fpu_fpsr_arm64, + fpu_fpcr_arm64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert(((sizeof g_fpu_regnums_arm64 / sizeof g_fpu_regnums_arm64[0]) - 1) == k_num_fpr_registers_arm64, \ + "g_fpu_regnums_arm64 has wrong number of register infos"); + +namespace { + // Number of register sets provided by this context. + enum + { + k_num_register_sets = 2 + }; +} + +// Register sets for ARM64. +static const RegisterSet +g_reg_sets_arm64[k_num_register_sets] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers_arm64, g_gpr_regnums_arm64 }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_arm64, g_fpu_regnums_arm64 } +}; + +NativeRegisterContextLinux* +NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) +{ + return new NativeRegisterContextLinux_arm64(target_arch, native_thread, concrete_frame_idx); +} + +NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) : + NativeRegisterContextLinux (native_thread, concrete_frame_idx, new RegisterContextLinux_arm64(target_arch)) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::aarch64: + m_reg_info.num_registers = k_num_registers_arm64; + m_reg_info.num_gpr_registers = k_num_gpr_registers_arm64; + m_reg_info.num_fpr_registers = k_num_fpr_registers_arm64; + m_reg_info.last_gpr = k_last_gpr_arm64; + m_reg_info.first_fpr = k_first_fpr_arm64; + m_reg_info.last_fpr = k_last_fpr_arm64; + m_reg_info.first_fpr_v = fpu_v0_arm64; + m_reg_info.last_fpr_v = fpu_v31_arm64; + m_reg_info.gpr_flags = gpr_cpsr_arm64; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } + + ::memset(&m_fpr, 0, sizeof (m_fpr)); + ::memset(&m_gpr_arm64, 0, sizeof (m_gpr_arm64)); + ::memset(&m_hwp_regs, 0, sizeof (m_hwp_regs)); + + // 16 is just a maximum value, query hardware for actual watchpoint count + m_max_hwp_supported = 16; + m_max_hbp_supported = 16; + m_refresh_hwdebug_info = true; +} + +uint32_t +NativeRegisterContextLinux_arm64::GetRegisterSetCount () const +{ + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextLinux_arm64::GetRegisterSet (uint32_t set_index) const +{ + if (set_index < k_num_register_sets) + return &g_reg_sets_arm64[set_index]; + + return nullptr; +} + +uint32_t +NativeRegisterContextLinux_arm64::GetUserRegisterCount() const +{ + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + count += g_reg_sets_arm64[set_index].num_registers; + return count; +} + +Error +NativeRegisterContextLinux_arm64::ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) +{ + Error error; + + if (!reg_info) + { + error.SetErrorString ("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (IsFPR(reg)) + { + error = ReadFPR(); + if (error.Fail()) + return error; + } + else + { + uint32_t full_reg = reg; + bool is_subreg = reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM); + + if (is_subreg) + { + // Read the full aligned 64-bit register. + full_reg = reg_info->invalidate_regs[0]; + } + + error = ReadRegisterRaw(full_reg, reg_value); + + if (error.Success ()) + { + // If our read was not aligned (for ah,bh,ch,dh), shift our returned value one byte to the right. + if (is_subreg && (reg_info->byte_offset & 0x1)) + reg_value.SetUInt64(reg_value.GetAsUInt64() >> 8); + + // If our return byte size was greater than the return value reg size, then + // use the type specified by reg_info rather than the uint64_t default + if (reg_value.GetByteSize() > reg_info->byte_size) + reg_value.SetType(reg_info); + } + return error; + } + + // Get pointer to m_fpr variable and set the data from it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert (fpr_offset < sizeof m_fpr); + uint8_t *src = (uint8_t *)&m_fpr + fpr_offset; + reg_value.SetFromMemoryData(reg_info, src, reg_info->byte_size, eByteOrderLittle, error); + + return error; +} + +Error +NativeRegisterContextLinux_arm64::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + if (!reg_info) + return Error ("reg_info NULL"); + + const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg_index == LLDB_INVALID_REGNUM) + return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : "<unknown register>"); + + if (IsGPR(reg_index)) + return WriteRegisterRaw(reg_index, reg_value); + + if (IsFPR(reg_index)) + { + // Get pointer to m_fpr variable and set the data to it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert (fpr_offset < sizeof m_fpr); + uint8_t *dst = (uint8_t *)&m_fpr + fpr_offset; + switch (reg_info->byte_size) + { + case 2: + *(uint16_t *)dst = reg_value.GetAsUInt16(); + break; + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + default: + assert(false && "Unhandled data size."); + return Error ("unhandled register data size %" PRIu32, reg_info->byte_size); + } + + Error error = WriteFPR(); + if (error.Fail()) + return error; + + return Error (); + } + + return Error ("failed - register wasn't recognized to be a GPR or an FPR, write strategy unknown"); +} + +Error +NativeRegisterContextLinux_arm64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + Error error; + + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (!data_sp) + return Error ("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE); + + error = ReadGPR(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes (); + if (dst == nullptr) + { + error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE); + return error; + } + + ::memcpy (dst, &m_gpr_arm64, GetGPRSize()); + dst += GetGPRSize(); + ::memcpy (dst, &m_fpr, sizeof(m_fpr)); + + return error; +} + +Error +NativeRegisterContextLinux_arm64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + Error error; + + if (!data_sp) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s invalid data_sp provided", __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize () != REG_CONTEXT_SIZE) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize ()); + return error; + } + + + uint8_t *src = data_sp->GetBytes (); + if (src == nullptr) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__); + return error; + } + ::memcpy (&m_gpr_arm64, src, GetRegisterInfoInterface ().GetGPRSize ()); + + error = WriteGPR(); + if (error.Fail()) + return error; + + src += GetRegisterInfoInterface ().GetGPRSize (); + ::memcpy (&m_fpr, src, sizeof(m_fpr)); + + error = WriteFPR(); + if (error.Fail()) + return error; + + return error; +} + +bool +NativeRegisterContextLinux_arm64::IsGPR(unsigned reg) const +{ + return reg <= m_reg_info.last_gpr; // GPR's come first. +} + +bool +NativeRegisterContextLinux_arm64::IsFPR(unsigned reg) const +{ + return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr); +} + +uint32_t +NativeRegisterContextLinux_arm64::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, bp_index = 0; + + // Check if size has a valid hardware breakpoint length. + if (size != 4) + return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware breakpoint + + // Check 4-byte alignment for hardware breakpoint target address. + if (addr & 0x03) + return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned. + + // Setup control value + control_value = 0; + control_value |= ((1 << size) - 1) << 5; + control_value |= (2 << 1) | 1; + + // Iterate over stored hardware breakpoints + // Find a free bp_index or update reference count if duplicate. + bp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hbp_supported; i++) + { + if ((m_hbr_regs[i].control & 1) == 0) + { + bp_index = i; // Mark last free slot + } + else if (m_hbr_regs[i].address == addr && m_hbr_regs[i].control == control_value) + { + bp_index = i; // Mark duplicate index + break; // Stop searching here + } + } + + if (bp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Add new or update existing breakpoint + if ((m_hbr_regs[bp_index].control & 1) == 0) + { + m_hbr_regs[bp_index].address = addr; + m_hbr_regs[bp_index].control = control_value; + m_hbr_regs[bp_index].refcount = 1; + + // PTRACE call to set corresponding hardware breakpoint register. + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) + { + m_hbr_regs[bp_index].address = 0; + m_hbr_regs[bp_index].control &= ~1; + m_hbr_regs[bp_index].refcount = 0; + + return LLDB_INVALID_INDEX32; + } + } + else + m_hbr_regs[bp_index].refcount++; + + return bp_index; +} + +bool +NativeRegisterContextLinux_arm64::ClearHardwareBreakpoint (uint32_t hw_idx) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return false; + + if (hw_idx >= m_max_hbp_supported) + return false; + + // Update reference count if multiple references. + if (m_hbr_regs[hw_idx].refcount > 1) + { + m_hbr_regs[hw_idx].refcount--; + return true; + } + else if (m_hbr_regs[hw_idx].refcount == 1) + { + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address; + uint32_t tempControl = m_hbr_regs[hw_idx].control; + uint32_t tempRefCount = m_hbr_regs[hw_idx].refcount; + + m_hbr_regs[hw_idx].control &= ~1; + m_hbr_regs[hw_idx].address = 0; + m_hbr_regs[hw_idx].refcount = 0; + + // PTRACE call to clear corresponding hardware breakpoint register. + WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) + { + m_hbr_regs[hw_idx].control = tempControl; + m_hbr_regs[hw_idx].address = tempAddr; + m_hbr_regs[hw_idx].refcount = tempRefCount; + + return false; + } + + return true; + } + + return false; +} + +uint32_t +NativeRegisterContextLinux_arm64::NumSupportedHardwareWatchpoints () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + return m_max_hwp_supported; +} + +uint32_t +NativeRegisterContextLinux_arm64::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, wp_index = 0; + + // Check if we are setting watchpoint other than read/write/access + // Also update watchpoint flag to match AArch64 write-read bit configuration. + switch (watch_flags) + { + case 1: + watch_flags = 2; + break; + case 2: + watch_flags = 1; + break; + case 3: + break; + default: + return LLDB_INVALID_INDEX32; + } + + // Check if size has a valid hardware watchpoint length. + if (size != 1 && size != 2 && size != 4 && size != 8) + return LLDB_INVALID_INDEX32; + + // Check 8-byte alignment for hardware watchpoint target address. + // TODO: Add support for watching un-aligned addresses + if (addr & 0x07) + return LLDB_INVALID_INDEX32; + + // Setup control value + control_value = watch_flags << 3; + control_value |= ((1 << size) - 1) << 5; + control_value |= (2 << 1) | 1; + + // Iterate over stored watchpoints + // Find a free wp_index or update reference count if duplicate. + wp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hwp_supported; i++) + { + if ((m_hwp_regs[i].control & 1) == 0) + { + wp_index = i; // Mark last free slot + } + else if (m_hwp_regs[i].address == addr && m_hwp_regs[i].control == control_value) + { + wp_index = i; // Mark duplicate index + break; // Stop searching here + } + } + + if (wp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Add new or update existing watchpoint + if ((m_hwp_regs[wp_index].control & 1) == 0) + { + // Update watchpoint in local cache + m_hwp_regs[wp_index].address = addr; + m_hwp_regs[wp_index].control = control_value; + m_hwp_regs[wp_index].refcount = 1; + + // PTRACE call to set corresponding watchpoint register. + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error.Fail()) + { + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].control &= ~1; + m_hwp_regs[wp_index].refcount = 0; + + return LLDB_INVALID_INDEX32; + } + } + else + m_hwp_regs[wp_index].refcount++; + + return wp_index; +} + +bool +NativeRegisterContextLinux_arm64::ClearHardwareWatchpoint (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return false; + + if (wp_index >= m_max_hwp_supported) + return false; + + // Update reference count if multiple references. + if (m_hwp_regs[wp_index].refcount > 1) + { + m_hwp_regs[wp_index].refcount--; + return true; + } + else if (m_hwp_regs[wp_index].refcount == 1) + { + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; + uint32_t tempControl = m_hwp_regs[wp_index].control; + uint32_t tempRefCount = m_hwp_regs[wp_index].refcount; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].control &= ~1; + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].refcount = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error.Fail()) + { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; + m_hwp_regs[wp_index].refcount = tempRefCount; + + return false; + } + + return true; + } + + return false; +} + +Error +NativeRegisterContextLinux_arm64::ClearAllHardwareWatchpoints () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return error; + + lldb::addr_t tempAddr = 0; + uint32_t tempControl = 0, tempRefCount = 0; + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) + { + if (m_hwp_regs[i].control & 0x01) + { + // Create a backup we can revert to in case of failure. + tempAddr = m_hwp_regs[i].address; + tempControl = m_hwp_regs[i].control; + tempRefCount = m_hwp_regs[i].refcount; + + // Clear watchpoints in local cache + m_hwp_regs[i].control &= ~1; + m_hwp_regs[i].address = 0; + m_hwp_regs[i].refcount = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error.Fail()) + { + m_hwp_regs[i].control = tempControl; + m_hwp_regs[i].address = tempAddr; + m_hwp_regs[i].refcount = tempRefCount; + + return error; + } + } + } + + return Error(); +} + +uint32_t +NativeRegisterContextLinux_arm64::GetWatchpointSize(uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) + { + case 0x01: + return 1; + case 0x03: + return 2; + case 0x0f: + return 4; + case 0xff: + return 8; + default: + return 0; + } +} +bool +NativeRegisterContextLinux_arm64::WatchpointIsEnabled(uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + if ((m_hwp_regs[wp_index].control & 0x1) == 0x1) + return true; + else + return false; +} + +Error +NativeRegisterContextLinux_arm64::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + uint32_t watch_size; + lldb::addr_t watch_addr; + + for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) + { + watch_size = GetWatchpointSize (wp_index); + watch_addr = m_hwp_regs[wp_index].address; + + if (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index) + && trap_addr >= watch_addr && trap_addr < watch_addr + watch_size) + { + return Error(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +lldb::addr_t +NativeRegisterContextLinux_arm64::GetWatchpointAddress (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].address; + else + return LLDB_INVALID_ADDRESS; +} + +Error +NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() +{ + if (!m_refresh_hwdebug_info) + { + return Error(); + } + + ::pid_t tid = m_thread.GetID(); + + int regset = NT_ARM_HW_WATCH; + struct iovec ioVec; + struct user_hwdebug_state dreg_state; + Error error; + + ioVec.iov_base = &dreg_state; + ioVec.iov_len = sizeof (dreg_state); + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, ioVec.iov_len); + + if (error.Fail()) + return error; + + m_max_hwp_supported = dreg_state.dbg_info & 0xff; + + regset = NT_ARM_HW_BREAK; + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, ioVec.iov_len); + + if (error.Fail()) + return error; + + m_max_hbp_supported = dreg_state.dbg_info & 0xff; + m_refresh_hwdebug_info = false; + + return error; +} + +Error +NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(int hwbType) +{ + struct iovec ioVec; + struct user_hwdebug_state dreg_state; + Error error; + + memset (&dreg_state, 0, sizeof (dreg_state)); + ioVec.iov_base = &dreg_state; + + if (hwbType == eDREGTypeWATCH) + { + hwbType = NT_ARM_HW_WATCH; + ioVec.iov_len = sizeof (dreg_state.dbg_info) + sizeof (dreg_state.pad) + + (sizeof (dreg_state.dbg_regs [0]) * m_max_hwp_supported); + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) + { + dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address; + dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control; + } + } + else + { + hwbType = NT_ARM_HW_BREAK; + ioVec.iov_len = sizeof (dreg_state.dbg_info) + sizeof (dreg_state.pad) + + (sizeof (dreg_state.dbg_regs [0]) * m_max_hbp_supported); + + for (uint32_t i = 0; i < m_max_hbp_supported; i++) + { + dreg_state.dbg_regs[i].addr = m_hbr_regs[i].address; + dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control; + } + } + + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), &hwbType, &ioVec, ioVec.iov_len); +} + +Error +NativeRegisterContextLinux_arm64::DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value) +{ + Error error; + if (offset > sizeof(struct user_pt_regs)) + { + uintptr_t offset = offset - sizeof(struct user_pt_regs); + if (offset > sizeof(struct user_fpsimd_state)) + { + error.SetErrorString("invalid offset value"); + return error; + } + elf_fpregset_t regs; + int regset = NT_FPREGSET; + struct iovec ioVec; + + ioVec.iov_base = ®s; + ioVec.iov_len = sizeof regs; + error = NativeProcessLinux::PtraceWrapper( + PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, sizeof regs); + if (error.Success()) + { + ArchSpec arch; + if (m_thread.GetProcess()->GetArchitecture(arch)) + value.SetBytes((void *)(((unsigned char *)(®s)) + offset), 16, arch.GetByteOrder()); + else + error.SetErrorString("failed to get architecture"); + } + } + else + { + elf_gregset_t regs; + int regset = NT_PRSTATUS; + struct iovec ioVec; + + ioVec.iov_base = ®s; + ioVec.iov_len = sizeof regs; + error = NativeProcessLinux::PtraceWrapper( + PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, sizeof regs); + if (error.Success()) + { + ArchSpec arch; + if (m_thread.GetProcess()->GetArchitecture(arch)) + value.SetBytes((void *)(((unsigned char *)(regs)) + offset), 8, arch.GetByteOrder()); + else + error.SetErrorString("failed to get architecture"); + } + } + return error; +} + +Error +NativeRegisterContextLinux_arm64::DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) +{ + Error error; + ::pid_t tid = m_thread.GetID(); + if (offset > sizeof(struct user_pt_regs)) + { + uintptr_t offset = offset - sizeof(struct user_pt_regs); + if (offset > sizeof(struct user_fpsimd_state)) + { + error.SetErrorString("invalid offset value"); + return error; + } + elf_fpregset_t regs; + int regset = NT_FPREGSET; + struct iovec ioVec; + + ioVec.iov_base = ®s; + ioVec.iov_len = sizeof regs; + error = NativeProcessLinux::PtraceWrapper( PTRACE_GETREGSET, tid, ®set, &ioVec, sizeof regs); + + if (error.Success()) + { + ::memcpy((void *)(((unsigned char *)(®s)) + offset), value.GetBytes(), 16); + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, ®set, &ioVec, sizeof regs); + } + } + else + { + elf_gregset_t regs; + int regset = NT_PRSTATUS; + struct iovec ioVec; + + ioVec.iov_base = ®s; + ioVec.iov_len = sizeof regs; + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, sizeof regs); + if (error.Success()) + { + ::memcpy((void *)(((unsigned char *)(®s)) + offset), value.GetBytes(), 8); + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, ®set, &ioVec, sizeof regs); + } + } + return error; +} + +Error +NativeRegisterContextLinux_arm64::DoReadGPR(void *buf, size_t buf_size) +{ + int regset = NT_PRSTATUS; + struct iovec ioVec; + Error error; + + ioVec.iov_base = buf; + ioVec.iov_len = buf_size; + return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); +} + +Error +NativeRegisterContextLinux_arm64::DoWriteGPR(void *buf, size_t buf_size) +{ + int regset = NT_PRSTATUS; + struct iovec ioVec; + Error error; + + ioVec.iov_base = buf; + ioVec.iov_len = buf_size; + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); +} + +Error +NativeRegisterContextLinux_arm64::DoReadFPR(void *buf, size_t buf_size) +{ + int regset = NT_FPREGSET; + struct iovec ioVec; + Error error; + + ioVec.iov_base = buf; + ioVec.iov_len = buf_size; + return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); +} + +Error +NativeRegisterContextLinux_arm64::DoWriteFPR(void *buf, size_t buf_size) +{ + int regset = NT_FPREGSET; + struct iovec ioVec; + Error error; + + ioVec.iov_base = buf; + ioVec.iov_len = buf_size; + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); +} + +uint32_t +NativeRegisterContextLinux_arm64::CalculateFprOffset(const RegisterInfo* reg_info) const +{ + return reg_info->byte_offset - GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset; +} + +#endif // defined (__arm64__) || defined (__aarch64__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h new file mode 100644 index 000000000000..c60baa637b8a --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -0,0 +1,196 @@ +//===-- NativeRegisterContextLinux_arm64.h ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined (__arm64__) || defined (__aarch64__) + +#ifndef lldb_NativeRegisterContextLinux_arm64_h +#define lldb_NativeRegisterContextLinux_arm64_h + +#include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/lldb-arm64-register-enums.h" + +namespace lldb_private { +namespace process_linux { + + class NativeProcessLinux; + + class NativeRegisterContextLinux_arm64 : public NativeRegisterContextLinux + { + public: + NativeRegisterContextLinux_arm64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + + uint32_t + GetRegisterSetCount () const override; + + uint32_t + GetUserRegisterCount() const override; + + const RegisterSet * + GetRegisterSet (uint32_t set_index) const override; + + Error + ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) override; + + Error + WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + Error + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override; + + Error + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; + + //------------------------------------------------------------------ + // Hardware breakpoints/watchpoint mangement functions + //------------------------------------------------------------------ + + uint32_t + SetHardwareBreakpoint (lldb::addr_t addr, size_t size) override; + + bool + ClearHardwareBreakpoint (uint32_t hw_idx) override; + + uint32_t + NumSupportedHardwareWatchpoints () override; + + uint32_t + SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) override; + + bool + ClearHardwareWatchpoint (uint32_t hw_index) override; + + Error + ClearAllHardwareWatchpoints () override; + + Error + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; + + lldb::addr_t + GetWatchpointAddress (uint32_t wp_index) override; + + uint32_t + GetWatchpointSize(uint32_t wp_index); + + bool + WatchpointIsEnabled(uint32_t wp_index); + + // Debug register type select + enum DREGType + { + eDREGTypeWATCH = 0, + eDREGTypeBREAK + }; + + protected: + Error + DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value) override; + + Error + DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) override; + + Error + DoReadGPR(void *buf, size_t buf_size) override; + + Error + DoWriteGPR(void *buf, size_t buf_size) override; + + Error + DoReadFPR(void *buf, size_t buf_size) override; + + Error + DoWriteFPR(void *buf, size_t buf_size) override; + + void* + GetGPRBuffer() override { return &m_gpr_arm64; } + + void* + GetFPRBuffer() override { return &m_fpr; } + + size_t + GetFPRSize() override { return sizeof(m_fpr); } + + private: + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + + uint32_t first_fpr_v; + uint32_t last_fpr_v; + + uint32_t gpr_flags; + }; + + // based on RegisterContextDarwin_arm64.h + struct VReg + { + uint8_t bytes[16]; + }; + + // based on RegisterContextDarwin_arm64.h + struct FPU + { + VReg v[32]; + uint32_t fpsr; + uint32_t fpcr; + }; + + uint64_t m_gpr_arm64[k_num_gpr_registers_arm64]; // 64-bit general purpose registers. + RegInfo m_reg_info; + FPU m_fpr; // floating-point registers including extended register sets. + + // Debug register info for hardware breakpoints and watchpoints management. + struct DREG + { + lldb::addr_t address; // Breakpoint/watchpoint address value. + uint32_t control; // Breakpoint/watchpoint control value. + uint32_t refcount; // Serves as enable/disable and refernce counter. + }; + + struct DREG m_hbr_regs[16]; // Arm native linux hardware breakpoints + struct DREG m_hwp_regs[16]; // Arm native linux hardware watchpoints + + uint32_t m_max_hwp_supported; + uint32_t m_max_hbp_supported; + bool m_refresh_hwdebug_info; + + bool + IsGPR(unsigned reg) const; + + bool + IsFPR(unsigned reg) const; + + Error + ReadHardwareDebugInfo(); + + Error + WriteHardwareDebugRegs(int hwbType); + + uint32_t + CalculateFprOffset(const RegisterInfo* reg_info) const; + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_arm64_h + +#endif // defined (__arm64__) || defined (__aarch64__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp new file mode 100644 index 000000000000..3cfeaf5546bc --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp @@ -0,0 +1,1431 @@ +//===-- NativeRegisterContextLinux_mips64.cpp ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined (__mips__) + +#include "NativeRegisterContextLinux_mips64.h" + +// C Includes +// C++ Includes + +// Other libraries and framework includes +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/Host.h" +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-private-enumerations.h" +#include "Plugins/Process/Linux/NativeProcessLinux.h" +#include "Plugins/Process/Linux/Procfs.h" +#include "Plugins/Process/Utility/RegisterContextLinux_mips64.h" +#include "Plugins/Process/Utility/RegisterContextLinux_mips.h" +#define NT_MIPS_MSA 0x600 +#define CONFIG5_FRE (1 << 8) +#define SR_FR (1 << 26) +#define NUM_REGISTERS 32 + +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#ifndef PTRACE_GET_WATCH_REGS +enum pt_watch_style +{ + pt_watch_style_mips32, + pt_watch_style_mips64 +}; +struct mips32_watch_regs +{ + uint32_t watchlo[8]; + uint16_t watchhi[8]; + uint16_t watch_masks[8]; + uint32_t num_valid; +} __attribute__((aligned(8))); + +struct mips64_watch_regs +{ + uint64_t watchlo[8]; + uint16_t watchhi[8]; + uint16_t watch_masks[8]; + uint32_t num_valid; +} __attribute__((aligned(8))); + +struct pt_watch_regs +{ + enum pt_watch_style style; + union + { + struct mips32_watch_regs mips32; + struct mips64_watch_regs mips64; + }; +}; + +#define PTRACE_GET_WATCH_REGS 0xd0 +#define PTRACE_SET_WATCH_REGS 0xd1 +#endif + +#define W (1 << 0) +#define R (1 << 1) +#define I (1 << 2) + +#define IRW (I | R | W) + +struct pt_watch_regs default_watch_regs; + +using namespace lldb_private; +using namespace lldb_private::process_linux; + +// ---------------------------------------------------------------------------- +// Private namespace. +// ---------------------------------------------------------------------------- + +namespace +{ + // mips general purpose registers. + const uint32_t + g_gp_regnums_mips[] = + { + gpr_zero_mips, + gpr_r1_mips, + gpr_r2_mips, + gpr_r3_mips, + gpr_r4_mips, + gpr_r5_mips, + gpr_r6_mips, + gpr_r7_mips, + gpr_r8_mips, + gpr_r9_mips, + gpr_r10_mips, + gpr_r11_mips, + gpr_r12_mips, + gpr_r13_mips, + gpr_r14_mips, + gpr_r15_mips, + gpr_r16_mips, + gpr_r17_mips, + gpr_r18_mips, + gpr_r19_mips, + gpr_r20_mips, + gpr_r21_mips, + gpr_r22_mips, + gpr_r23_mips, + gpr_r24_mips, + gpr_r25_mips, + gpr_r26_mips, + gpr_r27_mips, + gpr_gp_mips, + gpr_sp_mips, + gpr_r30_mips, + gpr_ra_mips, + gpr_sr_mips, + gpr_mullo_mips, + gpr_mulhi_mips, + gpr_badvaddr_mips, + gpr_cause_mips, + gpr_pc_mips, + gpr_config5_mips, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_gp_regnums_mips) / sizeof(g_gp_regnums_mips[0])) - 1 == k_num_gpr_registers_mips, + "g_gp_regnums_mips has wrong number of register infos"); + + // mips floating point registers. + const uint32_t + g_fp_regnums_mips[] = + { + fpr_f0_mips, + fpr_f1_mips, + fpr_f2_mips, + fpr_f3_mips, + fpr_f4_mips, + fpr_f5_mips, + fpr_f6_mips, + fpr_f7_mips, + fpr_f8_mips, + fpr_f9_mips, + fpr_f10_mips, + fpr_f11_mips, + fpr_f12_mips, + fpr_f13_mips, + fpr_f14_mips, + fpr_f15_mips, + fpr_f16_mips, + fpr_f17_mips, + fpr_f18_mips, + fpr_f19_mips, + fpr_f20_mips, + fpr_f21_mips, + fpr_f22_mips, + fpr_f23_mips, + fpr_f24_mips, + fpr_f25_mips, + fpr_f26_mips, + fpr_f27_mips, + fpr_f28_mips, + fpr_f29_mips, + fpr_f30_mips, + fpr_f31_mips, + fpr_fcsr_mips, + fpr_fir_mips, + fpr_config5_mips, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_fp_regnums_mips) / sizeof(g_fp_regnums_mips[0])) - 1 == k_num_fpr_registers_mips, + "g_fp_regnums_mips has wrong number of register infos"); + + // mips MSA registers. + const uint32_t + g_msa_regnums_mips[] = + { + msa_w0_mips, + msa_w1_mips, + msa_w2_mips, + msa_w3_mips, + msa_w4_mips, + msa_w5_mips, + msa_w6_mips, + msa_w7_mips, + msa_w8_mips, + msa_w9_mips, + msa_w10_mips, + msa_w11_mips, + msa_w12_mips, + msa_w13_mips, + msa_w14_mips, + msa_w15_mips, + msa_w16_mips, + msa_w17_mips, + msa_w18_mips, + msa_w19_mips, + msa_w20_mips, + msa_w21_mips, + msa_w22_mips, + msa_w23_mips, + msa_w24_mips, + msa_w25_mips, + msa_w26_mips, + msa_w27_mips, + msa_w28_mips, + msa_w29_mips, + msa_w30_mips, + msa_w31_mips, + msa_fcsr_mips, + msa_fir_mips, + msa_mcsr_mips, + msa_mir_mips, + msa_config5_mips, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_msa_regnums_mips) / sizeof(g_msa_regnums_mips[0])) - 1 == k_num_msa_registers_mips, + "g_msa_regnums_mips has wrong number of register infos"); + + // mips64 general purpose registers. + const uint32_t + g_gp_regnums_mips64[] = + { + gpr_zero_mips64, + gpr_r1_mips64, + gpr_r2_mips64, + gpr_r3_mips64, + gpr_r4_mips64, + gpr_r5_mips64, + gpr_r6_mips64, + gpr_r7_mips64, + gpr_r8_mips64, + gpr_r9_mips64, + gpr_r10_mips64, + gpr_r11_mips64, + gpr_r12_mips64, + gpr_r13_mips64, + gpr_r14_mips64, + gpr_r15_mips64, + gpr_r16_mips64, + gpr_r17_mips64, + gpr_r18_mips64, + gpr_r19_mips64, + gpr_r20_mips64, + gpr_r21_mips64, + gpr_r22_mips64, + gpr_r23_mips64, + gpr_r24_mips64, + gpr_r25_mips64, + gpr_r26_mips64, + gpr_r27_mips64, + gpr_gp_mips64, + gpr_sp_mips64, + gpr_r30_mips64, + gpr_ra_mips64, + gpr_sr_mips64, + gpr_mullo_mips64, + gpr_mulhi_mips64, + gpr_badvaddr_mips64, + gpr_cause_mips64, + gpr_pc_mips64, + gpr_config5_mips64, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_gp_regnums_mips64) / sizeof(g_gp_regnums_mips64[0])) - 1 == k_num_gpr_registers_mips64, + "g_gp_regnums_mips64 has wrong number of register infos"); + + // mips64 floating point registers. + const uint32_t + g_fp_regnums_mips64[] = + { + fpr_f0_mips64, + fpr_f1_mips64, + fpr_f2_mips64, + fpr_f3_mips64, + fpr_f4_mips64, + fpr_f5_mips64, + fpr_f6_mips64, + fpr_f7_mips64, + fpr_f8_mips64, + fpr_f9_mips64, + fpr_f10_mips64, + fpr_f11_mips64, + fpr_f12_mips64, + fpr_f13_mips64, + fpr_f14_mips64, + fpr_f15_mips64, + fpr_f16_mips64, + fpr_f17_mips64, + fpr_f18_mips64, + fpr_f19_mips64, + fpr_f20_mips64, + fpr_f21_mips64, + fpr_f22_mips64, + fpr_f23_mips64, + fpr_f24_mips64, + fpr_f25_mips64, + fpr_f26_mips64, + fpr_f27_mips64, + fpr_f28_mips64, + fpr_f29_mips64, + fpr_f30_mips64, + fpr_f31_mips64, + fpr_fcsr_mips64, + fpr_fir_mips64, + fpr_config5_mips64, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_fp_regnums_mips64) / sizeof(g_fp_regnums_mips64[0])) - 1 == k_num_fpr_registers_mips64, + "g_fp_regnums_mips64 has wrong number of register infos"); + + // mips64 MSA registers. + const uint32_t + g_msa_regnums_mips64[] = + { + msa_w0_mips64, + msa_w1_mips64, + msa_w2_mips64, + msa_w3_mips64, + msa_w4_mips64, + msa_w5_mips64, + msa_w6_mips64, + msa_w7_mips64, + msa_w8_mips64, + msa_w9_mips64, + msa_w10_mips64, + msa_w11_mips64, + msa_w12_mips64, + msa_w13_mips64, + msa_w14_mips64, + msa_w15_mips64, + msa_w16_mips64, + msa_w17_mips64, + msa_w18_mips64, + msa_w19_mips64, + msa_w20_mips64, + msa_w21_mips64, + msa_w22_mips64, + msa_w23_mips64, + msa_w24_mips64, + msa_w25_mips64, + msa_w26_mips64, + msa_w27_mips64, + msa_w28_mips64, + msa_w29_mips64, + msa_w30_mips64, + msa_w31_mips64, + msa_fcsr_mips64, + msa_fir_mips64, + msa_mcsr_mips64, + msa_mir_mips64, + msa_config5_mips64, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_msa_regnums_mips64) / sizeof(g_msa_regnums_mips64[0])) - 1 == k_num_msa_registers_mips64, + "g_msa_regnums_mips64 has wrong number of register infos"); + + // Number of register sets provided by this context. + enum + { + k_num_register_sets = 3 + }; + + // Register sets for mips. + static const RegisterSet + g_reg_sets_mips[k_num_register_sets] = + { + { "General Purpose Registers", "gpr", k_num_gpr_registers_mips, g_gp_regnums_mips }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_mips, g_fp_regnums_mips }, + { "MSA Registers", "msa", k_num_msa_registers_mips, g_msa_regnums_mips } + }; + + // Register sets for mips64. + static const RegisterSet + g_reg_sets_mips64[k_num_register_sets] = + { + { "General Purpose Registers", "gpr", k_num_gpr_registers_mips64, g_gp_regnums_mips64 }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_mips64, g_fp_regnums_mips64 }, + { "MSA Registers", "msa", k_num_msa_registers_mips64, g_msa_regnums_mips64 }, + }; + +} // end of anonymous namespace + +NativeRegisterContextLinux* +NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) +{ + return new NativeRegisterContextLinux_mips64(target_arch, native_thread, concrete_frame_idx); +} + +#define REG_CONTEXT_SIZE (GetRegisterInfoInterface ().GetGPRSize () + sizeof(FPR_linux_mips) + sizeof(MSA_linux_mips)) + +// ---------------------------------------------------------------------------- +// NativeRegisterContextLinux_mips64 members. +// ---------------------------------------------------------------------------- + +static RegisterInfoInterface* +CreateRegisterInfoInterface(const ArchSpec& target_arch) +{ + if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) + { + // 32-bit hosts run with a RegisterContextLinux_mips context. + return new RegisterContextLinux_mips(target_arch, NativeRegisterContextLinux_mips64::IsMSAAvailable()); + } + else + { + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + // mips64 hosts know how to work with 64-bit and 32-bit EXEs using the mips64 register context. + return new RegisterContextLinux_mips64 (target_arch, NativeRegisterContextLinux_mips64::IsMSAAvailable()); + } +} + +NativeRegisterContextLinux_mips64::NativeRegisterContextLinux_mips64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) : + NativeRegisterContextLinux (native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch)) +{ + switch (target_arch.GetMachine ()) + { + case llvm::Triple::mips: + case llvm::Triple::mipsel: + m_reg_info.num_registers = k_num_registers_mips; + m_reg_info.num_gpr_registers = k_num_gpr_registers_mips; + m_reg_info.num_fpr_registers = k_num_fpr_registers_mips; + m_reg_info.last_gpr = k_last_gpr_mips; + m_reg_info.first_fpr = k_first_fpr_mips; + m_reg_info.last_fpr = k_last_fpr_mips; + m_reg_info.first_msa = k_first_msa_mips; + m_reg_info.last_msa = k_last_msa_mips; + break; + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + m_reg_info.num_registers = k_num_registers_mips64; + m_reg_info.num_gpr_registers = k_num_gpr_registers_mips64; + m_reg_info.num_fpr_registers = k_num_fpr_registers_mips64; + m_reg_info.last_gpr = k_last_gpr_mips64; + m_reg_info.first_fpr = k_first_fpr_mips64; + m_reg_info.last_fpr = k_last_fpr_mips64; + m_reg_info.first_msa = k_first_msa_mips64; + m_reg_info.last_msa = k_last_msa_mips64; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } + + // Initialize m_iovec to point to the buffer and buffer size + // using the conventions of Berkeley style UIO structures, as required + // by PTRACE extensions. + m_iovec.iov_base = &m_msa; + m_iovec.iov_len = sizeof(MSA_linux_mips); + + // init h/w watchpoint addr map + for (int index = 0;index <= MAX_NUM_WP; index++) + hw_addr_map[index] = LLDB_INVALID_ADDRESS; + + ::memset(&m_gpr, 0, sizeof(GPR_linux_mips)); + ::memset(&m_fpr, 0, sizeof(FPR_linux_mips)); + ::memset(&m_msa, 0, sizeof(MSA_linux_mips)); +} + +uint32_t +NativeRegisterContextLinux_mips64::GetRegisterSetCount () const +{ + return k_num_register_sets; +} + +lldb::addr_t +NativeRegisterContextLinux_mips64::GetPCfromBreakpointLocation (lldb::addr_t fail_value) +{ + Error error; + RegisterValue pc_value; + lldb::addr_t pc = fail_value; + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_mips64::%s Reading PC from breakpoint location", __FUNCTION__); + + // PC register is at index 34 of the register array + const RegisterInfo *const pc_info_p = GetRegisterInfoAtIndex (gpr_pc_mips64); + + error = ReadRegister (pc_info_p, pc_value); + if (error.Success ()) + { + pc = pc_value.GetAsUInt64 (); + + // CAUSE register is at index 37 of the register array + const RegisterInfo *const cause_info_p = GetRegisterInfoAtIndex (gpr_cause_mips64); + RegisterValue cause_value; + + ReadRegister (cause_info_p, cause_value); + + uint64_t cause = cause_value.GetAsUInt64 (); + + if (log) + log->Printf ("NativeRegisterContextLinux_mips64::%s PC 0x%" PRIx64 " Cause 0x%" PRIx64, __FUNCTION__, pc, cause); + + /* + * The breakpoint might be in a delay slot. In this case PC points + * to the delayed branch instruction rather then the instruction + * in the delay slot. If the CAUSE.BD flag is set then adjust the + * PC based on the size of the branch instruction. + */ + if ((cause & (1 << 31)) != 0) + { + lldb::addr_t branch_delay = 0; + branch_delay = 4; // FIXME - Adjust according to size of branch instruction at PC + pc = pc + branch_delay; + pc_value.SetUInt64 (pc); + WriteRegister (pc_info_p, pc_value); + + if (log) + log->Printf ("NativeRegisterContextLinux_mips64::%s New PC 0x%" PRIx64, __FUNCTION__, pc); + } + } + + return pc; +} + +const RegisterSet * +NativeRegisterContextLinux_mips64::GetRegisterSet (uint32_t set_index) const +{ + if (set_index >= k_num_register_sets) + return nullptr; + + switch (GetRegisterInfoInterface ().GetTargetArchitecture ().GetMachine ()) + { + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + return &g_reg_sets_mips64[set_index]; + case llvm::Triple::mips: + case llvm::Triple::mipsel: + return &g_reg_sets_mips[set_index]; + default: + assert (false && "Unhandled target architecture."); + return nullptr; + } + + return nullptr; +} + +lldb_private::Error +NativeRegisterContextLinux_mips64::ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) +{ + Error error; + + if (!reg_info) + { + error.SetErrorString ("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + { + // This is likely an internal register for lldb use only and should not be directly queried. + error.SetErrorStringWithFormat ("register \"%s\" is an internal-only lldb register, cannot read directly", reg_info->name); + return error; + } + + if (IsMSA(reg) && !IsMSAAvailable()) + { + error.SetErrorString ("MSA not available on this processor"); + return error; + } + + if (IsMSA(reg) || IsFPR(reg)) + { + uint8_t *src; + + error = ReadCP1(); + + if (!error.Success()) + { + error.SetErrorString ("failed to read co-processor 1 register"); + return error; + } + + if (IsFPR(reg)) + { + assert (reg_info->byte_offset < sizeof(UserArea)); + src = (uint8_t *)&m_fpr + reg_info->byte_offset - (sizeof(m_gpr)); + } + else + { + assert (reg_info->byte_offset < sizeof(UserArea)); + src = (uint8_t *)&m_msa + reg_info->byte_offset - (sizeof(m_gpr) + sizeof(m_fpr)); + } + switch (reg_info->byte_size) + { + case 4: + reg_value.SetUInt32(*(uint32_t *)src); + break; + case 8: + reg_value.SetUInt64(*(uint64_t *)src); + break; + case 16: + reg_value.SetBytes((const void *)src, 16, GetByteOrder()); + break; + default: + assert(false && "Unhandled data size."); + error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size); + break; + } + } + else + { + error = ReadRegisterRaw(reg, reg_value); + } + + return error; +} + +lldb_private::Error +NativeRegisterContextLinux_mips64::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + Error error; + + assert (reg_info && "reg_info is null"); + + const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg_index == LLDB_INVALID_REGNUM) + return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : "<unknown register>"); + + if (IsMSA(reg_index) && !IsMSAAvailable()) + { + error.SetErrorString ("MSA not available on this processor"); + return error; + } + + if (IsFPR(reg_index) || IsMSA(reg_index)) + { + uint8_t *dst; + uint64_t *src; + + // Initialise the FP and MSA buffers by reading all co-processor 1 registers + ReadCP1(); + + if (IsFPR(reg_index)) + { + assert (reg_info->byte_offset < sizeof(UserArea)); + dst = (uint8_t *)&m_fpr + reg_info->byte_offset - (sizeof(m_gpr)); + } + else + { + assert (reg_info->byte_offset < sizeof(UserArea)); + dst = (uint8_t *)&m_msa + reg_info->byte_offset - (sizeof(m_gpr) + sizeof(m_fpr)); + } + switch (reg_info->byte_size) + { + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + case 16: + src = (uint64_t *)reg_value.GetBytes(); + *(uint64_t *)dst = *src; + *(uint64_t *)(dst + 8) = *(src + 1); + break; + default: + assert(false && "Unhandled data size."); + error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size); + break; + } + error = WriteCP1(); + if (!error.Success()) + { + error.SetErrorString ("failed to write co-processor 1 register"); + return error; + } + } + else + { + error = WriteRegisterRaw(reg_index, reg_value); + } + + return error; +} + +Error +NativeRegisterContextLinux_mips64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + Error error; + + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (!data_sp) + { + error.SetErrorStringWithFormat ("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE); + return error; + } + + error = ReadGPR(); + if (!error.Success()) + { + error.SetErrorString ("ReadGPR() failed"); + return error; + } + + error = ReadCP1(); + if (!error.Success()) + { + error.SetErrorString ("ReadCP1() failed"); + return error; + } + + uint8_t *dst = data_sp->GetBytes (); + if (dst == nullptr) + { + error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE); + return error; + } + + ::memcpy (dst, &m_gpr, GetRegisterInfoInterface ().GetGPRSize ()); + dst += GetRegisterInfoInterface ().GetGPRSize (); + + ::memcpy (dst, &m_fpr, GetFPRSize ()); + dst += GetFPRSize (); + + ::memcpy (dst, &m_msa, sizeof(MSA_linux_mips)); + + return error; +} + +Error +NativeRegisterContextLinux_mips64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + Error error; + + if (!data_sp) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s invalid data_sp provided", __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize () != REG_CONTEXT_SIZE) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize ()); + return error; + } + + + uint8_t *src = data_sp->GetBytes (); + if (src == nullptr) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__); + return error; + } + + ::memcpy (&m_gpr, src, GetRegisterInfoInterface ().GetGPRSize ()); + src += GetRegisterInfoInterface ().GetGPRSize (); + + ::memcpy (&m_fpr, src, GetFPRSize ()); + src += GetFPRSize (); + + ::memcpy (&m_msa, src, sizeof(MSA_linux_mips)); + + error = WriteGPR(); + if (!error.Success()) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s WriteGPR() failed", __FUNCTION__); + return error; + } + + error = WriteCP1(); + if (!error.Success()) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s WriteCP1() failed", __FUNCTION__); + return error; + } + + return error; +} + +Error +NativeRegisterContextLinux_mips64::ReadCP1() +{ + Error error; + + uint8_t *src, *dst; + + lldb::ByteOrder byte_order = GetByteOrder(); + + uint32_t IsBigEndian = (byte_order == lldb::eByteOrderBig); + + if (IsMSAAvailable()) + { + error = NativeRegisterContextLinux::ReadRegisterSet(&m_iovec, sizeof(MSA_linux_mips), NT_MIPS_MSA); + src = (uint8_t *)&m_msa + (IsBigEndian * 8); + dst = (uint8_t *)&m_fpr; + for ( int i = 0; i < NUM_REGISTERS; i++) + { + // Copy fp values from msa buffer fetched via ptrace + *(uint64_t *) dst = *(uint64_t *) src; + src = src + 16; + dst = dst + 8; + } + m_fpr.fir = m_msa.fir; + m_fpr.fcsr = m_msa.fcsr; + m_fpr.config5 = m_msa.config5; + } + else + { + error = NativeRegisterContextLinux::ReadFPR(); + } + + if (IsFR0() || IsFRE()) + { + src = (uint8_t *)&m_fpr + 4 + (IsBigEndian * 4); + dst = (uint8_t *)&m_fpr + 8 + (IsBigEndian * 4); + for (int i = 0; i < (NUM_REGISTERS / 2); i++) + { + // copy odd single from top of neighbouring even double + *(uint32_t *) dst = *(uint32_t *) src; + src = src + 16; + dst = dst + 16; + } + } + + return error; +} + +Error +NativeRegisterContextLinux_mips64::WriteCP1() +{ + Error error; + + uint8_t *src, *dst; + + lldb::ByteOrder byte_order = GetByteOrder(); + + uint32_t IsBigEndian = (byte_order == lldb::eByteOrderBig); + + if (IsFR0() || IsFRE()) + { + src = (uint8_t *)&m_fpr + 8 + (IsBigEndian * 4); + dst = (uint8_t *)&m_fpr + 4 + (IsBigEndian * 4); + for (int i = 0; i < (NUM_REGISTERS / 2); i++) + { + // copy odd single to top of neighbouring even double + *(uint32_t *) dst = *(uint32_t *) src; + src = src + 16; + dst = dst + 16; + } + } + + if (IsMSAAvailable()) + { + dst = (uint8_t *)&m_msa + (IsBigEndian * 8); + src = (uint8_t *)&m_fpr; + for (int i = 0; i < NUM_REGISTERS; i++) + { + // Copy fp values to msa buffer for ptrace + *(uint64_t *) dst = *(uint64_t *) src; + dst = dst + 16; + src = src + 8; + } + m_msa.fir = m_fpr.fir; + m_msa.fcsr = m_fpr.fcsr; + m_msa.config5 = m_fpr.config5; + error = NativeRegisterContextLinux::WriteRegisterSet(&m_iovec, sizeof(MSA_linux_mips), NT_MIPS_MSA); + } + else + { + error = NativeRegisterContextLinux::WriteFPR(); + } + + return error; +} + +bool +NativeRegisterContextLinux_mips64::IsFR0() +{ + const RegisterInfo *const reg_info_p = GetRegisterInfoAtIndex (gpr_sr_mips64); + + RegisterValue reg_value; + ReadRegister (reg_info_p, reg_value); + + uint64_t value = reg_value.GetAsUInt64(); + + return (!(value & SR_FR)); +} + +bool +NativeRegisterContextLinux_mips64::IsFRE() +{ + const RegisterInfo *const reg_info_p = GetRegisterInfoAtIndex (gpr_config5_mips64); + + RegisterValue reg_value; + ReadRegister (reg_info_p, reg_value); + + uint64_t config5 = reg_value.GetAsUInt64(); + + return (config5 & CONFIG5_FRE); +} + +bool +NativeRegisterContextLinux_mips64::IsFPR(uint32_t reg_index) const +{ + return (m_reg_info.first_fpr <= reg_index && reg_index <= m_reg_info.last_fpr); +} + +static uint32_t +GetWatchHi (struct pt_watch_regs *regs, uint32_t index) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watchhi[index]; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watchhi[index]; + if(log) + log->Printf("Invalid watch register style"); + return 0; +} + +static void +SetWatchHi (struct pt_watch_regs *regs, uint32_t index, uint16_t value) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + regs->mips32.watchhi[index] = value; + else if (regs->style == pt_watch_style_mips64) + regs->mips64.watchhi[index] = value; + if(log) + log->Printf("Invalid watch register style"); + return; +} + +static lldb::addr_t +GetWatchLo (struct pt_watch_regs *regs, uint32_t index) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watchlo[index]; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watchlo[index]; + if(log) + log->Printf("Invalid watch register style"); + return LLDB_INVALID_ADDRESS; +} + +static void +SetWatchLo (struct pt_watch_regs *regs, uint32_t index, uint64_t value) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + regs->mips32.watchlo[index] = (uint32_t) value; + else if (regs->style == pt_watch_style_mips64) + regs->mips64.watchlo[index] = value; + if(log) + log->Printf("Invalid watch register style"); + return; +} + +static uint32_t +GetIRWMask (struct pt_watch_regs *regs, uint32_t index) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watch_masks[index] & IRW; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watch_masks[index] & IRW; + if(log) + log->Printf("Invalid watch register style"); + return 0; +} + +static uint32_t +GetRegMask (struct pt_watch_regs *regs, uint32_t index) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watch_masks[index] & ~IRW; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watch_masks[index] & ~IRW; + if(log) + log->Printf("Invalid watch register style"); + return 0; +} + +static lldb::addr_t +GetRangeMask (lldb::addr_t mask) +{ + lldb::addr_t mask_bit = 1; + while (mask_bit < mask) + { + mask = mask | mask_bit; + mask_bit <<= 1; + } + return mask; +} + +static int +GetVacantWatchIndex (struct pt_watch_regs *regs, lldb::addr_t addr, uint32_t size, uint32_t irw, uint32_t num_valid) +{ + lldb::addr_t last_byte = addr + size - 1; + lldb::addr_t mask = GetRangeMask (addr ^ last_byte) | IRW; + lldb::addr_t base_addr = addr & ~mask; + + // Check if this address is already watched by previous watch points. + lldb::addr_t lo; + uint16_t hi; + uint32_t vacant_watches = 0; + for (uint32_t index = 0; index < num_valid; index++) + { + lo = GetWatchLo (regs, index); + if (lo != 0 && irw == ((uint32_t) lo & irw)) + { + hi = GetWatchHi (regs, index) | IRW; + lo &= ~(lldb::addr_t) hi; + if (addr >= lo && last_byte <= (lo + hi)) + return index; + } + else + vacant_watches++; + } + + // Now try to find a vacant index + if(vacant_watches > 0) + { + vacant_watches = 0; + for (uint32_t index = 0; index < num_valid; index++) + { + lo = GetWatchLo (regs, index); + if (lo == 0 + && irw == (GetIRWMask (regs, index) & irw)) + { + if (mask <= (GetRegMask (regs, index) | IRW)) + { + // It fits, we can use it. + SetWatchLo (regs, index, base_addr | irw); + SetWatchHi (regs, index, mask & ~IRW); + return index; + } + else + { + // It doesn't fit, but has the proper IRW capabilities + vacant_watches++; + } + } + } + + if (vacant_watches > 1) + { + // Split this watchpoint accross several registers + struct pt_watch_regs regs_copy; + regs_copy = *regs; + lldb::addr_t break_addr; + uint32_t segment_size; + for (uint32_t index = 0; index < num_valid; index++) + { + lo = GetWatchLo (®s_copy, index); + hi = GetRegMask (®s_copy, index) | IRW; + if (lo == 0 && irw == (hi & irw)) + { + lo = addr & ~(lldb::addr_t) hi; + break_addr = lo + hi + 1; + if (break_addr >= addr + size) + segment_size = size; + else + segment_size = break_addr - addr; + mask = GetRangeMask (addr ^ (addr + segment_size - 1)); + SetWatchLo (®s_copy, index, (addr & ~mask) | irw); + SetWatchHi (®s_copy, index, mask & ~IRW); + if (break_addr >= addr + size) + { + *regs = regs_copy; + return index; + } + size = addr + size - break_addr; + addr = break_addr; + } + } + } + } + return LLDB_INVALID_INDEX32; +} + +bool +NativeRegisterContextLinux_mips64::IsMSA(uint32_t reg_index) const +{ + return (m_reg_info.first_msa <= reg_index && reg_index <= m_reg_info.last_msa); +} + +bool +NativeRegisterContextLinux_mips64::IsMSAAvailable() +{ + MSA_linux_mips msa_buf; + unsigned int regset = NT_MIPS_MSA; + + Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, Host::GetCurrentProcessID(), static_cast<void *>(®set), &msa_buf, sizeof(MSA_linux_mips)); + + if (error.Success() && msa_buf.mir) + { + return true; + } + + return false; +} + +Error +NativeRegisterContextLinux_mips64::IsWatchpointHit (uint32_t wp_index, bool &is_hit) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + // reading the current state of watch regs + struct pt_watch_regs watch_readback; + Error error = DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&watch_readback)); + + if (GetWatchHi (&watch_readback, wp_index) & (IRW)) + { + // clear hit flag in watchhi + SetWatchHi (&watch_readback, wp_index, (GetWatchHi (&watch_readback, wp_index) & ~(IRW))); + DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&watch_readback)); + + is_hit = true; + return error; + } + is_hit = false; + return error; +} + +Error +NativeRegisterContextLinux_mips64::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) { + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) + { + bool is_hit; + Error error = IsWatchpointHit(wp_index, is_hit); + if (error.Fail()) { + wp_index = LLDB_INVALID_INDEX32; + } else if (is_hit) { + return error; + } + } + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +Error +NativeRegisterContextLinux_mips64::IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) +{ + is_vacant = false; + return Error("MIPS TODO: NativeRegisterContextLinux_mips64::IsWatchpointVacant not implemented"); +} + +bool +NativeRegisterContextLinux_mips64::ClearHardwareWatchpoint(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + struct pt_watch_regs regs; + // First reading the current state of watch regs + DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void*>(®s)); + + if (regs.style == pt_watch_style_mips32) + { + regs.mips32.watchlo[wp_index] = default_watch_regs.mips32.watchlo[wp_index]; + regs.mips32.watchhi[wp_index] = default_watch_regs.mips32.watchhi[wp_index]; + regs.mips32.watch_masks[wp_index] = default_watch_regs.mips32.watch_masks[wp_index]; + } + else // pt_watch_style_mips64 + { + regs.mips64.watchlo[wp_index] = default_watch_regs.mips64.watchlo[wp_index]; + regs.mips64.watchhi[wp_index] = default_watch_regs.mips64.watchhi[wp_index]; + regs.mips64.watch_masks[wp_index] = default_watch_regs.mips64.watch_masks[wp_index]; + } + + Error error = DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s)); + if(!error.Fail()) + { + hw_addr_map[wp_index] = LLDB_INVALID_ADDRESS; + return true; + } + return false; +} + +Error +NativeRegisterContextLinux_mips64::ClearAllHardwareWatchpoints() +{ + return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&default_watch_regs)); +} + +Error +NativeRegisterContextLinux_mips64::SetHardwareWatchpointWithIndex ( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) +{ + Error error; + error.SetErrorString ("MIPS TODO: NativeRegisterContextLinux_mips64::SetHardwareWatchpointWithIndex not implemented"); + return error; +} + +uint32_t +NativeRegisterContextLinux_mips64::SetHardwareWatchpoint ( + lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + struct pt_watch_regs regs; + + // First reading the current state of watch regs + DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s)); + + // Try if a new watch point fits in this state + int index = GetVacantWatchIndex (®s, addr, size, watch_flags, NumSupportedHardwareWatchpoints()); + + // New watchpoint doesn't fit + if (index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + + // It fits, so we go ahead with updating the state of watch regs + DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s)); + + // Storing exact address + hw_addr_map[index] = addr; + return index; +} + +lldb::addr_t +NativeRegisterContextLinux_mips64::GetWatchpointAddress (uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + + return hw_addr_map[wp_index]; +} + +struct EmulatorBaton +{ + lldb::addr_t m_watch_hit_addr; + NativeProcessLinux* m_process; + NativeRegisterContext* m_reg_context; + + EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) : + m_watch_hit_addr(LLDB_INVALID_ADDRESS), + m_process(process), + m_reg_context(reg_context) + {} +}; + +static size_t +ReadMemoryCallback (EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, lldb::addr_t addr, + void *dst, size_t length) +{ + size_t bytes_read; + EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton); + emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read); + return bytes_read; +} + +static size_t +WriteMemoryCallback (EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, const void *dst, size_t length) +{ + return length; +} + +static bool +ReadRegisterCallback (EmulateInstruction *instruction, void *baton, + const RegisterInfo *reg_info, RegisterValue ®_value) +{ + EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton); + + const RegisterInfo* full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo( + lldb::eRegisterKindDWARF, reg_info->kinds[lldb::eRegisterKindDWARF]); + + Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); + if (error.Success()) + return true; + + return false; +} + +static bool +WriteRegisterCallback (EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + if (reg_info->kinds[lldb::eRegisterKindDWARF] == dwarf_bad_mips64) + { + EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton); + emulator_baton->m_watch_hit_addr = reg_value.GetAsUInt64 (); + } + + return true; +} + +/* + * MIPS Linux kernel returns a masked address (last 3bits are masked) + * when a HW watchpoint is hit. However user may not have set a watchpoint + * on this address. Emulate instruction at PC and find the base address of + * the load/store instruction. This will give the exact address used to + * read/write the variable. Send this exact address to client so that + * it can decide to stop or continue the thread. +*/ +lldb::addr_t +NativeRegisterContextLinux_mips64::GetWatchpointHitAddress (uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + + lldb_private::ArchSpec arch; + arch = GetRegisterInfoInterface().GetTargetArchitecture(); + std::unique_ptr<EmulateInstruction> emulator_ap( + EmulateInstruction::FindPlugin(arch, lldb_private::eInstructionTypeAny, nullptr)); + + if (emulator_ap == nullptr) + return LLDB_INVALID_ADDRESS; + + EmulatorBaton baton(static_cast<NativeProcessLinux*>(m_thread.GetProcess().get()), this); + emulator_ap->SetBaton (&baton); + emulator_ap->SetReadMemCallback (&ReadMemoryCallback); + emulator_ap->SetReadRegCallback (&ReadRegisterCallback); + emulator_ap->SetWriteMemCallback (&WriteMemoryCallback); + emulator_ap->SetWriteRegCallback (&WriteRegisterCallback); + + if (!emulator_ap->ReadInstruction()) + return LLDB_INVALID_ADDRESS; + + if (emulator_ap->EvaluateInstruction(lldb::eEmulateInstructionOptionNone)) + return baton.m_watch_hit_addr; + + return LLDB_INVALID_ADDRESS; +} + +uint32_t +NativeRegisterContextLinux_mips64::NumSupportedHardwareWatchpoints () +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + struct pt_watch_regs regs; + static int num_valid = 0; + if (!num_valid) + { + DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s)); + default_watch_regs = regs; // Keeping default watch regs values for future use + switch (regs.style) + { + case pt_watch_style_mips32: + num_valid = regs.mips32.num_valid; // Using num_valid as cache + return num_valid; + case pt_watch_style_mips64: + num_valid = regs.mips64.num_valid; + return num_valid; + default: + if(log) + log->Printf("NativeRegisterContextLinux_mips64::%s Error: Unrecognized watch register style", __FUNCTION__); + } + return 0; + } + return num_valid; +} +Error +NativeRegisterContextLinux_mips64::DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value) +{ + GPR_linux_mips regs; + ::memset(®s, 0, sizeof(GPR_linux_mips)); + + // Clear all bits in RegisterValue before writing actual value read from ptrace to avoid garbage value in 32-bit MSB + value.SetBytes((void *)(((unsigned char *)®s) + offset), 8, GetByteOrder()); + Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), NULL, ®s, sizeof regs); + if (error.Success()) + { + lldb_private::ArchSpec arch; + if (m_thread.GetProcess()->GetArchitecture(arch)) + value.SetBytes((void *)(((unsigned char *)®s) + offset + 4 * (arch.GetMachine() == llvm::Triple::mips)), arch.GetFlags() & lldb_private::ArchSpec::eMIPSABI_O32 ? 4 : 8, arch.GetByteOrder()); + else + error.SetErrorString("failed to get architecture"); + } + return error; +} + +Error +NativeRegisterContextLinux_mips64::DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) +{ + GPR_linux_mips regs; + Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), NULL, ®s, sizeof regs); + if (error.Success()) + { + lldb_private::ArchSpec arch; + if (m_thread.GetProcess()->GetArchitecture(arch)) + { + ::memcpy((void *)(((unsigned char *)(®s)) + offset), value.GetBytes(), arch.GetFlags() & lldb_private::ArchSpec::eMIPSABI_O32 ? 4 : 8); + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGS, m_thread.GetID(), NULL, ®s, sizeof regs); + } + else + error.SetErrorString("failed to get architecture"); + } + return error; +} + +Error +NativeRegisterContextLinux_mips64::DoReadWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback) +{ + return NativeProcessLinux::PtraceWrapper( PTRACE_GET_WATCH_REGS, m_thread.GetID(), watch_readback); +} + +Error +NativeRegisterContextLinux_mips64::DoWriteWatchPointRegisterValue(lldb::tid_t tid, void* watch_reg_value) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_SET_WATCH_REGS, m_thread.GetID(), watch_reg_value); +} + +#endif // defined (__mips__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h new file mode 100644 index 000000000000..9368645116e9 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h @@ -0,0 +1,167 @@ +//===-- NativeRegisterContextLinux_mips64.h ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined (__mips__) + +#ifndef lldb_NativeRegisterContextLinux_mips64_h +#define lldb_NativeRegisterContextLinux_mips64_h + +#include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/RegisterContext_mips.h" +#include "Plugins/Process/Utility/lldb-mips-linux-register-enums.h" + +#define MAX_NUM_WP 8 + +namespace lldb_private { +namespace process_linux { + + class NativeProcessLinux; + + class NativeRegisterContextLinux_mips64 : public NativeRegisterContextLinux + { + public: + NativeRegisterContextLinux_mips64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + + uint32_t + GetRegisterSetCount () const override; + + lldb::addr_t + GetPCfromBreakpointLocation (lldb::addr_t fail_value = LLDB_INVALID_ADDRESS) override; + + lldb::addr_t + GetWatchpointHitAddress (uint32_t wp_index) override; + + const RegisterSet * + GetRegisterSet (uint32_t set_index) const override; + + Error + ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) override; + + Error + WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + Error + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override; + + Error + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; + + Error + ReadCP1(); + + Error + WriteCP1(); + + Error + IsWatchpointHit (uint32_t wp_index, bool &is_hit) override; + + Error + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; + + Error + IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) override; + + bool + ClearHardwareWatchpoint (uint32_t wp_index) override; + + Error + ClearAllHardwareWatchpoints () override; + + Error + SetHardwareWatchpointWithIndex (lldb::addr_t addr, size_t size, + uint32_t watch_flags, uint32_t wp_index); + + uint32_t + SetHardwareWatchpoint (lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t + GetWatchpointAddress (uint32_t wp_index) override; + + uint32_t + NumSupportedHardwareWatchpoints () override; + + static bool + IsMSAAvailable(); + + protected: + Error + DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value) override; + + Error + DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) override; + + Error + DoReadWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback); + + Error + DoWriteWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback); + + bool + IsFR0(); + + bool + IsFRE(); + + bool + IsFPR(uint32_t reg_index) const; + + bool + IsMSA(uint32_t reg_index) const; + + void* + GetGPRBuffer() override { return &m_gpr; } + + void* + GetFPRBuffer() override { return &m_fpr; } + + size_t + GetFPRSize() override { return sizeof(FPR_linux_mips); } + + private: + // Info about register ranges. + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + uint32_t first_msa; + uint32_t last_msa; + }; + + RegInfo m_reg_info; + + GPR_linux_mips m_gpr; + + FPR_linux_mips m_fpr; + + MSA_linux_mips m_msa; + + lldb::addr_t hw_addr_map[MAX_NUM_WP]; + + IOVEC_mips m_iovec; + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_mips64_h + +#endif // defined (__mips__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp new file mode 100755 index 000000000000..2080d2ede372 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp @@ -0,0 +1,1239 @@ +//===-- NativeRegisterContextLinux_x86_64.cpp ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__i386__) || defined(__x86_64__) + +#include "NativeRegisterContextLinux_x86_64.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/HostInfo.h" + +#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" + +using namespace lldb_private; +using namespace lldb_private::process_linux; + +// ---------------------------------------------------------------------------- +// Private namespace. +// ---------------------------------------------------------------------------- + +namespace +{ + // x86 32-bit general purpose registers. + const uint32_t + g_gpr_regnums_i386[] = + { + lldb_eax_i386, + lldb_ebx_i386, + lldb_ecx_i386, + lldb_edx_i386, + lldb_edi_i386, + lldb_esi_i386, + lldb_ebp_i386, + lldb_esp_i386, + lldb_eip_i386, + lldb_eflags_i386, + lldb_cs_i386, + lldb_fs_i386, + lldb_gs_i386, + lldb_ss_i386, + lldb_ds_i386, + lldb_es_i386, + lldb_ax_i386, + lldb_bx_i386, + lldb_cx_i386, + lldb_dx_i386, + lldb_di_i386, + lldb_si_i386, + lldb_bp_i386, + lldb_sp_i386, + lldb_ah_i386, + lldb_bh_i386, + lldb_ch_i386, + lldb_dh_i386, + lldb_al_i386, + lldb_bl_i386, + lldb_cl_i386, + lldb_dl_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_gpr_regnums_i386) / sizeof(g_gpr_regnums_i386[0])) - 1 == k_num_gpr_registers_i386, + "g_gpr_regnums_i386 has wrong number of register infos"); + + // x86 32-bit floating point registers. + const uint32_t + g_fpu_regnums_i386[] = + { + lldb_fctrl_i386, + lldb_fstat_i386, + lldb_ftag_i386, + lldb_fop_i386, + lldb_fiseg_i386, + lldb_fioff_i386, + lldb_foseg_i386, + lldb_fooff_i386, + lldb_mxcsr_i386, + lldb_mxcsrmask_i386, + lldb_st0_i386, + lldb_st1_i386, + lldb_st2_i386, + lldb_st3_i386, + lldb_st4_i386, + lldb_st5_i386, + lldb_st6_i386, + lldb_st7_i386, + lldb_mm0_i386, + lldb_mm1_i386, + lldb_mm2_i386, + lldb_mm3_i386, + lldb_mm4_i386, + lldb_mm5_i386, + lldb_mm6_i386, + lldb_mm7_i386, + lldb_xmm0_i386, + lldb_xmm1_i386, + lldb_xmm2_i386, + lldb_xmm3_i386, + lldb_xmm4_i386, + lldb_xmm5_i386, + lldb_xmm6_i386, + lldb_xmm7_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_fpu_regnums_i386) / sizeof(g_fpu_regnums_i386[0])) - 1 == k_num_fpr_registers_i386, + "g_fpu_regnums_i386 has wrong number of register infos"); + + // x86 32-bit AVX registers. + const uint32_t + g_avx_regnums_i386[] = + { + lldb_ymm0_i386, + lldb_ymm1_i386, + lldb_ymm2_i386, + lldb_ymm3_i386, + lldb_ymm4_i386, + lldb_ymm5_i386, + lldb_ymm6_i386, + lldb_ymm7_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_avx_regnums_i386) / sizeof(g_avx_regnums_i386[0])) - 1 == k_num_avx_registers_i386, + " g_avx_regnums_i386 has wrong number of register infos"); + + // x86 64-bit general purpose registers. + static const + uint32_t g_gpr_regnums_x86_64[] = + { + lldb_rax_x86_64, + lldb_rbx_x86_64, + lldb_rcx_x86_64, + lldb_rdx_x86_64, + lldb_rdi_x86_64, + lldb_rsi_x86_64, + lldb_rbp_x86_64, + lldb_rsp_x86_64, + lldb_r8_x86_64, + lldb_r9_x86_64, + lldb_r10_x86_64, + lldb_r11_x86_64, + lldb_r12_x86_64, + lldb_r13_x86_64, + lldb_r14_x86_64, + lldb_r15_x86_64, + lldb_rip_x86_64, + lldb_rflags_x86_64, + lldb_cs_x86_64, + lldb_fs_x86_64, + lldb_gs_x86_64, + lldb_ss_x86_64, + lldb_ds_x86_64, + lldb_es_x86_64, + lldb_eax_x86_64, + lldb_ebx_x86_64, + lldb_ecx_x86_64, + lldb_edx_x86_64, + lldb_edi_x86_64, + lldb_esi_x86_64, + lldb_ebp_x86_64, + lldb_esp_x86_64, + lldb_r8d_x86_64, // Low 32 bits or r8 + lldb_r9d_x86_64, // Low 32 bits or r9 + lldb_r10d_x86_64, // Low 32 bits or r10 + lldb_r11d_x86_64, // Low 32 bits or r11 + lldb_r12d_x86_64, // Low 32 bits or r12 + lldb_r13d_x86_64, // Low 32 bits or r13 + lldb_r14d_x86_64, // Low 32 bits or r14 + lldb_r15d_x86_64, // Low 32 bits or r15 + lldb_ax_x86_64, + lldb_bx_x86_64, + lldb_cx_x86_64, + lldb_dx_x86_64, + lldb_di_x86_64, + lldb_si_x86_64, + lldb_bp_x86_64, + lldb_sp_x86_64, + lldb_r8w_x86_64, // Low 16 bits or r8 + lldb_r9w_x86_64, // Low 16 bits or r9 + lldb_r10w_x86_64, // Low 16 bits or r10 + lldb_r11w_x86_64, // Low 16 bits or r11 + lldb_r12w_x86_64, // Low 16 bits or r12 + lldb_r13w_x86_64, // Low 16 bits or r13 + lldb_r14w_x86_64, // Low 16 bits or r14 + lldb_r15w_x86_64, // Low 16 bits or r15 + lldb_ah_x86_64, + lldb_bh_x86_64, + lldb_ch_x86_64, + lldb_dh_x86_64, + lldb_al_x86_64, + lldb_bl_x86_64, + lldb_cl_x86_64, + lldb_dl_x86_64, + lldb_dil_x86_64, + lldb_sil_x86_64, + lldb_bpl_x86_64, + lldb_spl_x86_64, + lldb_r8l_x86_64, // Low 8 bits or r8 + lldb_r9l_x86_64, // Low 8 bits or r9 + lldb_r10l_x86_64, // Low 8 bits or r10 + lldb_r11l_x86_64, // Low 8 bits or r11 + lldb_r12l_x86_64, // Low 8 bits or r12 + lldb_r13l_x86_64, // Low 8 bits or r13 + lldb_r14l_x86_64, // Low 8 bits or r14 + lldb_r15l_x86_64, // Low 8 bits or r15 + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) - 1 == k_num_gpr_registers_x86_64, + "g_gpr_regnums_x86_64 has wrong number of register infos"); + + // x86 64-bit floating point registers. + static const uint32_t + g_fpu_regnums_x86_64[] = + { + lldb_fctrl_x86_64, + lldb_fstat_x86_64, + lldb_ftag_x86_64, + lldb_fop_x86_64, + lldb_fiseg_x86_64, + lldb_fioff_x86_64, + lldb_foseg_x86_64, + lldb_fooff_x86_64, + lldb_mxcsr_x86_64, + lldb_mxcsrmask_x86_64, + lldb_st0_x86_64, + lldb_st1_x86_64, + lldb_st2_x86_64, + lldb_st3_x86_64, + lldb_st4_x86_64, + lldb_st5_x86_64, + lldb_st6_x86_64, + lldb_st7_x86_64, + lldb_mm0_x86_64, + lldb_mm1_x86_64, + lldb_mm2_x86_64, + lldb_mm3_x86_64, + lldb_mm4_x86_64, + lldb_mm5_x86_64, + lldb_mm6_x86_64, + lldb_mm7_x86_64, + lldb_xmm0_x86_64, + lldb_xmm1_x86_64, + lldb_xmm2_x86_64, + lldb_xmm3_x86_64, + lldb_xmm4_x86_64, + lldb_xmm5_x86_64, + lldb_xmm6_x86_64, + lldb_xmm7_x86_64, + lldb_xmm8_x86_64, + lldb_xmm9_x86_64, + lldb_xmm10_x86_64, + lldb_xmm11_x86_64, + lldb_xmm12_x86_64, + lldb_xmm13_x86_64, + lldb_xmm14_x86_64, + lldb_xmm15_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_fpu_regnums_x86_64) / sizeof(g_fpu_regnums_x86_64[0])) - 1 == k_num_fpr_registers_x86_64, + "g_fpu_regnums_x86_64 has wrong number of register infos"); + + // x86 64-bit AVX registers. + static const uint32_t + g_avx_regnums_x86_64[] = + { + lldb_ymm0_x86_64, + lldb_ymm1_x86_64, + lldb_ymm2_x86_64, + lldb_ymm3_x86_64, + lldb_ymm4_x86_64, + lldb_ymm5_x86_64, + lldb_ymm6_x86_64, + lldb_ymm7_x86_64, + lldb_ymm8_x86_64, + lldb_ymm9_x86_64, + lldb_ymm10_x86_64, + lldb_ymm11_x86_64, + lldb_ymm12_x86_64, + lldb_ymm13_x86_64, + lldb_ymm14_x86_64, + lldb_ymm15_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_avx_regnums_x86_64) / sizeof(g_avx_regnums_x86_64[0])) - 1 == k_num_avx_registers_x86_64, + "g_avx_regnums_x86_64 has wrong number of register infos"); + + // Number of register sets provided by this context. + enum + { + k_num_extended_register_sets = 1, + k_num_register_sets = 3 + }; + + // Register sets for x86 32-bit. + static const RegisterSet + g_reg_sets_i386[k_num_register_sets] = + { + { "General Purpose Registers", "gpr", k_num_gpr_registers_i386, g_gpr_regnums_i386 }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_i386, g_fpu_regnums_i386 }, + { "Advanced Vector Extensions", "avx", k_num_avx_registers_i386, g_avx_regnums_i386 } + }; + + // Register sets for x86 64-bit. + static const RegisterSet + g_reg_sets_x86_64[k_num_register_sets] = + { + { "General Purpose Registers", "gpr", k_num_gpr_registers_x86_64, g_gpr_regnums_x86_64 }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_x86_64, g_fpu_regnums_x86_64 }, + { "Advanced Vector Extensions", "avx", k_num_avx_registers_x86_64, g_avx_regnums_x86_64 } + }; +} + +#define REG_CONTEXT_SIZE (GetRegisterInfoInterface ().GetGPRSize () + sizeof(FPR)) + +// ---------------------------------------------------------------------------- +// Required ptrace defines. +// ---------------------------------------------------------------------------- + +// Support ptrace extensions even when compiled without required kernel support +#ifndef NT_X86_XSTATE +#define NT_X86_XSTATE 0x202 +#endif +#ifndef NT_PRXFPREG +#define NT_PRXFPREG 0x46e62b7f +#endif + +NativeRegisterContextLinux* +NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) +{ + return new NativeRegisterContextLinux_x86_64(target_arch, native_thread, concrete_frame_idx); +} + +// ---------------------------------------------------------------------------- +// NativeRegisterContextLinux_x86_64 members. +// ---------------------------------------------------------------------------- + +static RegisterInfoInterface* +CreateRegisterInfoInterface(const ArchSpec& target_arch) +{ + if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) + { + // 32-bit hosts run with a RegisterContextLinux_i386 context. + return new RegisterContextLinux_i386(target_arch); + } + else + { + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + // X86_64 hosts know how to work with 64-bit and 32-bit EXEs using the x86_64 register context. + return new RegisterContextLinux_x86_64 (target_arch); + } +} + +NativeRegisterContextLinux_x86_64::NativeRegisterContextLinux_x86_64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) : + NativeRegisterContextLinux (native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch)), + m_fpr_type (eFPRTypeNotValid), + m_fpr (), + m_iovec (), + m_ymm_set (), + m_reg_info (), + m_gpr_x86_64 () +{ + // Set up data about ranges of valid registers. + switch (target_arch.GetMachine ()) + { + case llvm::Triple::x86: + m_reg_info.num_registers = k_num_registers_i386; + m_reg_info.num_gpr_registers = k_num_gpr_registers_i386; + m_reg_info.num_fpr_registers = k_num_fpr_registers_i386; + m_reg_info.num_avx_registers = k_num_avx_registers_i386; + m_reg_info.last_gpr = k_last_gpr_i386; + m_reg_info.first_fpr = k_first_fpr_i386; + m_reg_info.last_fpr = k_last_fpr_i386; + m_reg_info.first_st = lldb_st0_i386; + m_reg_info.last_st = lldb_st7_i386; + m_reg_info.first_mm = lldb_mm0_i386; + m_reg_info.last_mm = lldb_mm7_i386; + m_reg_info.first_xmm = lldb_xmm0_i386; + m_reg_info.last_xmm = lldb_xmm7_i386; + m_reg_info.first_ymm = lldb_ymm0_i386; + m_reg_info.last_ymm = lldb_ymm7_i386; + m_reg_info.first_dr = lldb_dr0_i386; + m_reg_info.gpr_flags = lldb_eflags_i386; + break; + case llvm::Triple::x86_64: + m_reg_info.num_registers = k_num_registers_x86_64; + m_reg_info.num_gpr_registers = k_num_gpr_registers_x86_64; + m_reg_info.num_fpr_registers = k_num_fpr_registers_x86_64; + m_reg_info.num_avx_registers = k_num_avx_registers_x86_64; + m_reg_info.last_gpr = k_last_gpr_x86_64; + m_reg_info.first_fpr = k_first_fpr_x86_64; + m_reg_info.last_fpr = k_last_fpr_x86_64; + m_reg_info.first_st = lldb_st0_x86_64; + m_reg_info.last_st = lldb_st7_x86_64; + m_reg_info.first_mm = lldb_mm0_x86_64; + m_reg_info.last_mm = lldb_mm7_x86_64; + m_reg_info.first_xmm = lldb_xmm0_x86_64; + m_reg_info.last_xmm = lldb_xmm15_x86_64; + m_reg_info.first_ymm = lldb_ymm0_x86_64; + m_reg_info.last_ymm = lldb_ymm15_x86_64; + m_reg_info.first_dr = lldb_dr0_x86_64; + m_reg_info.gpr_flags = lldb_rflags_x86_64; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } + + // Initialize m_iovec to point to the buffer and buffer size + // using the conventions of Berkeley style UIO structures, as required + // by PTRACE extensions. + m_iovec.iov_base = &m_fpr.xstate.xsave; + m_iovec.iov_len = sizeof(m_fpr.xstate.xsave); + + // Clear out the FPR state. + ::memset(&m_fpr, 0, sizeof(FPR)); + + // Store byte offset of fctrl (i.e. first register of FPR) + const RegisterInfo *reg_info_fctrl = GetRegisterInfoByName("fctrl"); + m_fctrl_offset_in_userarea = reg_info_fctrl->byte_offset; +} + +// CONSIDER after local and llgs debugging are merged, register set support can +// be moved into a base x86-64 class with IsRegisterSetAvailable made virtual. +uint32_t +NativeRegisterContextLinux_x86_64::GetRegisterSetCount () const +{ + uint32_t sets = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + { + if (IsRegisterSetAvailable (set_index)) + ++sets; + } + + return sets; +} + +uint32_t +NativeRegisterContextLinux_x86_64::GetUserRegisterCount() const +{ + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + { + const RegisterSet* set = GetRegisterSet(set_index); + if (set) + count += set->num_registers; + } + return count; +} + +const RegisterSet * +NativeRegisterContextLinux_x86_64::GetRegisterSet (uint32_t set_index) const +{ + if (!IsRegisterSetAvailable (set_index)) + return nullptr; + + switch (GetRegisterInfoInterface ().GetTargetArchitecture ().GetMachine ()) + { + case llvm::Triple::x86: + return &g_reg_sets_i386[set_index]; + case llvm::Triple::x86_64: + return &g_reg_sets_x86_64[set_index]; + default: + assert (false && "Unhandled target architecture."); + return nullptr; + } + + return nullptr; +} + +Error +NativeRegisterContextLinux_x86_64::ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) +{ + Error error; + + if (!reg_info) + { + error.SetErrorString ("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + { + // This is likely an internal register for lldb use only and should not be directly queried. + error.SetErrorStringWithFormat ("register \"%s\" is an internal-only lldb register, cannot read directly", reg_info->name); + return error; + } + + if (IsFPR(reg, GetFPRType())) + { + error = ReadFPR(); + if (error.Fail()) + return error; + } + else + { + uint32_t full_reg = reg; + bool is_subreg = reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM); + + if (is_subreg) + { + // Read the full aligned 64-bit register. + full_reg = reg_info->invalidate_regs[0]; + } + + error = ReadRegisterRaw(full_reg, reg_value); + + if (error.Success ()) + { + // If our read was not aligned (for ah,bh,ch,dh), shift our returned value one byte to the right. + if (is_subreg && (reg_info->byte_offset & 0x1)) + reg_value.SetUInt64(reg_value.GetAsUInt64() >> 8); + + // If our return byte size was greater than the return value reg size, then + // use the type specified by reg_info rather than the uint64_t default + if (reg_value.GetByteSize() > reg_info->byte_size) + reg_value.SetType(reg_info); + } + return error; + } + + if (reg_info->encoding == lldb::eEncodingVector) + { + lldb::ByteOrder byte_order = GetByteOrder(); + + if (byte_order != lldb::eByteOrderInvalid) + { + if (reg >= m_reg_info.first_st && reg <= m_reg_info.last_st) + reg_value.SetBytes(m_fpr.xstate.fxsave.stmm[reg - m_reg_info.first_st].bytes, reg_info->byte_size, byte_order); + if (reg >= m_reg_info.first_mm && reg <= m_reg_info.last_mm) + reg_value.SetBytes(m_fpr.xstate.fxsave.stmm[reg - m_reg_info.first_mm].bytes, reg_info->byte_size, byte_order); + if (reg >= m_reg_info.first_xmm && reg <= m_reg_info.last_xmm) + reg_value.SetBytes(m_fpr.xstate.fxsave.xmm[reg - m_reg_info.first_xmm].bytes, reg_info->byte_size, byte_order); + if (reg >= m_reg_info.first_ymm && reg <= m_reg_info.last_ymm) + { + // Concatenate ymm using the register halves in xmm.bytes and ymmh.bytes + if (GetFPRType() == eFPRTypeXSAVE && CopyXSTATEtoYMM(reg, byte_order)) + reg_value.SetBytes(m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes, reg_info->byte_size, byte_order); + else + { + error.SetErrorString ("failed to copy ymm register value"); + return error; + } + } + + if (reg_value.GetType() != RegisterValue::eTypeBytes) + error.SetErrorString ("write failed - type was expected to be RegisterValue::eTypeBytes"); + + return error; + } + + error.SetErrorString ("byte order is invalid"); + return error; + } + + // Get pointer to m_fpr.xstate.fxsave variable and set the data from it. + + // Byte offsets of all registers are calculated wrt 'UserArea' structure. + // However, ReadFPR() reads fpu registers {using ptrace(PTRACE_GETFPREGS,..)} + // and stores them in 'm_fpr' (of type FPR structure). To extract values of fpu + // registers, m_fpr should be read at byte offsets calculated wrt to FPR structure. + + // Since, FPR structure is also one of the member of UserArea structure. + // byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) - byte_offset(fctrl wrt UserArea) + assert ( (reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(m_fpr)); + uint8_t *src = (uint8_t *)&m_fpr + reg_info->byte_offset - m_fctrl_offset_in_userarea; + switch (reg_info->byte_size) + { + case 1: + reg_value.SetUInt8(*(uint8_t *)src); + break; + case 2: + reg_value.SetUInt16(*(uint16_t *)src); + break; + case 4: + reg_value.SetUInt32(*(uint32_t *)src); + break; + case 8: + reg_value.SetUInt64(*(uint64_t *)src); + break; + default: + assert(false && "Unhandled data size."); + error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size); + break; + } + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + assert (reg_info && "reg_info is null"); + + const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg_index == LLDB_INVALID_REGNUM) + return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : "<unknown register>"); + + if (IsGPR(reg_index)) + return WriteRegisterRaw(reg_index, reg_value); + + if (IsFPR(reg_index, GetFPRType())) + { + if (reg_info->encoding == lldb::eEncodingVector) + { + if (reg_index >= m_reg_info.first_st && reg_index <= m_reg_info.last_st) + ::memcpy (m_fpr.xstate.fxsave.stmm[reg_index - m_reg_info.first_st].bytes, reg_value.GetBytes(), reg_value.GetByteSize()); + + if (reg_index >= m_reg_info.first_mm && reg_index <= m_reg_info.last_mm) + ::memcpy (m_fpr.xstate.fxsave.stmm[reg_index - m_reg_info.first_mm].bytes, reg_value.GetBytes(), reg_value.GetByteSize()); + + if (reg_index >= m_reg_info.first_xmm && reg_index <= m_reg_info.last_xmm) + ::memcpy (m_fpr.xstate.fxsave.xmm[reg_index - m_reg_info.first_xmm].bytes, reg_value.GetBytes(), reg_value.GetByteSize()); + + if (reg_index >= m_reg_info.first_ymm && reg_index <= m_reg_info.last_ymm) + { + if (GetFPRType() != eFPRTypeXSAVE) + return Error ("target processor does not support AVX"); + + // Store ymm register content, and split into the register halves in xmm.bytes and ymmh.bytes + ::memcpy (m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes, reg_value.GetBytes(), reg_value.GetByteSize()); + if (!CopyYMMtoXSTATE(reg_index, GetByteOrder())) + return Error ("CopyYMMtoXSTATE() failed"); + } + } + else + { + // Get pointer to m_fpr.xstate.fxsave variable and set the data to it. + + // Byte offsets of all registers are calculated wrt 'UserArea' structure. + // However, WriteFPR() takes m_fpr (of type FPR structure) and writes only fpu + // registers using ptrace(PTRACE_SETFPREGS,..) API. Hence fpu registers should + // be written in m_fpr at byte offsets calculated wrt FPR structure. + + // Since, FPR structure is also one of the member of UserArea structure. + // byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) - byte_offset(fctrl wrt UserArea) + assert ( (reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(m_fpr)); + uint8_t *dst = (uint8_t *)&m_fpr + reg_info->byte_offset - m_fctrl_offset_in_userarea; + switch (reg_info->byte_size) + { + case 1: + *(uint8_t *)dst = reg_value.GetAsUInt8(); + break; + case 2: + *(uint16_t *)dst = reg_value.GetAsUInt16(); + break; + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + default: + assert(false && "Unhandled data size."); + return Error ("unhandled register data size %" PRIu32, reg_info->byte_size); + } + } + + Error error = WriteFPR(); + if (error.Fail()) + return error; + + if (IsAVX(reg_index)) + { + if (!CopyYMMtoXSTATE(reg_index, GetByteOrder())) + return Error ("CopyYMMtoXSTATE() failed"); + } + return Error (); + } + return Error ("failed - register wasn't recognized to be a GPR or an FPR, write strategy unknown"); +} + +Error +NativeRegisterContextLinux_x86_64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + Error error; + + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (!data_sp) + { + error.SetErrorStringWithFormat ("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE); + return error; + } + + error = ReadGPR(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes (); + if (dst == nullptr) + { + error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE); + return error; + } + + ::memcpy (dst, &m_gpr_x86_64, GetRegisterInfoInterface ().GetGPRSize ()); + dst += GetRegisterInfoInterface ().GetGPRSize (); + if (GetFPRType () == eFPRTypeFXSAVE) + ::memcpy (dst, &m_fpr.xstate.fxsave, sizeof(m_fpr.xstate.fxsave)); + else if (GetFPRType () == eFPRTypeXSAVE) + { + lldb::ByteOrder byte_order = GetByteOrder (); + + // Assemble the YMM register content from the register halves. + for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm; ++reg) + { + if (!CopyXSTATEtoYMM (reg, byte_order)) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s CopyXSTATEtoYMM() failed for reg num %" PRIu32, __FUNCTION__, reg); + return error; + } + } + + // Copy the extended register state including the assembled ymm registers. + ::memcpy (dst, &m_fpr, sizeof (m_fpr)); + } + else + { + assert (false && "how do we save the floating point registers?"); + error.SetErrorString ("unsure how to save the floating point registers"); + } + /** The following code is specific to Linux x86 based architectures, + * where the register orig_eax (32 bit)/orig_rax (64 bit) is set to + * -1 to solve the bug 23659, such a setting prevents the automatic + * decrement of the instruction pointer which was causing the SIGILL + * exception. + * **/ + + RegisterValue value((uint64_t) -1); + const RegisterInfo *reg_info = GetRegisterInfoInterface().GetDynamicRegisterInfo("orig_eax"); + if (reg_info == nullptr) + reg_info = GetRegisterInfoInterface().GetDynamicRegisterInfo("orig_rax"); + + if (reg_info != nullptr) + return DoWriteRegisterValue(reg_info->byte_offset,reg_info->name,value); + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + Error error; + + if (!data_sp) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s invalid data_sp provided", __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize () != REG_CONTEXT_SIZE) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize ()); + return error; + } + + + uint8_t *src = data_sp->GetBytes (); + if (src == nullptr) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__); + return error; + } + ::memcpy (&m_gpr_x86_64, src, GetRegisterInfoInterface ().GetGPRSize ()); + + error = WriteGPR(); + if (error.Fail()) + return error; + + src += GetRegisterInfoInterface ().GetGPRSize (); + if (GetFPRType () == eFPRTypeFXSAVE) + ::memcpy (&m_fpr.xstate.fxsave, src, sizeof(m_fpr.xstate.fxsave)); + else if (GetFPRType () == eFPRTypeXSAVE) + ::memcpy (&m_fpr.xstate.xsave, src, sizeof(m_fpr.xstate.xsave)); + + error = WriteFPR(); + if (error.Fail()) + return error; + + if (GetFPRType() == eFPRTypeXSAVE) + { + lldb::ByteOrder byte_order = GetByteOrder(); + + // Parse the YMM register content from the register halves. + for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm; ++reg) + { + if (!CopyYMMtoXSTATE (reg, byte_order)) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s CopyYMMtoXSTATE() failed for reg num %" PRIu32, __FUNCTION__, reg); + return error; + } + } + } + + return error; +} + +bool +NativeRegisterContextLinux_x86_64::IsRegisterSetAvailable (uint32_t set_index) const +{ + // Note: Extended register sets are assumed to be at the end of g_reg_sets. + uint32_t num_sets = k_num_register_sets - k_num_extended_register_sets; + + if (GetFPRType () == eFPRTypeXSAVE) + { + // AVX is the first extended register set. + ++num_sets; + } + return (set_index < num_sets); +} + +bool +NativeRegisterContextLinux_x86_64::IsGPR(uint32_t reg_index) const +{ + // GPRs come first. + return reg_index <= m_reg_info.last_gpr; +} + +NativeRegisterContextLinux_x86_64::FPRType +NativeRegisterContextLinux_x86_64::GetFPRType () const +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (m_fpr_type == eFPRTypeNotValid) + { + // TODO: Use assembly to call cpuid on the inferior and query ebx or ecx. + + // Try and see if AVX register retrieval works. + m_fpr_type = eFPRTypeXSAVE; + if (const_cast<NativeRegisterContextLinux_x86_64*>(this)->ReadFPR().Fail()) + { + // Fall back to general floating point with no AVX support. + m_fpr_type = eFPRTypeFXSAVE; + + // Check if FXSAVE area can be read. + if (const_cast<NativeRegisterContextLinux_x86_64*>(this)->ReadFPR().Fail()) + { + if (log) + log->Printf("NativeRegisterContextLinux_x86_64::%s ptrace APIs failed to read XSAVE/FXSAVE area", __FUNCTION__); + } + } + } + return m_fpr_type; +} + +bool +NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index) const +{ + return (m_reg_info.first_fpr <= reg_index && reg_index <= m_reg_info.last_fpr); +} + +bool +NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index, FPRType fpr_type) const +{ + bool generic_fpr = IsFPR(reg_index); + + if (fpr_type == eFPRTypeXSAVE) + return generic_fpr || IsAVX(reg_index); + return generic_fpr; +} + +Error +NativeRegisterContextLinux_x86_64::WriteFPR() +{ + const FPRType fpr_type = GetFPRType (); + const lldb_private::ArchSpec& target_arch = GetRegisterInfoInterface().GetTargetArchitecture(); + switch (fpr_type) + { + case FPRType::eFPRTypeFXSAVE: + // For 32-bit inferiors on x86_32/x86_64 architectures, + // FXSAVE area can be written using PTRACE_SETREGSET ptrace api + // For 64-bit inferiors on x86_64 architectures, + // FXSAVE area can be written using PTRACE_SETFPREGS ptrace api + switch (target_arch.GetMachine ()) + { + case llvm::Triple::x86: + return WriteRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_PRXFPREG); + case llvm::Triple::x86_64: + return NativeRegisterContextLinux::WriteFPR(); + default: + assert(false && "Unhandled target architecture."); + break; + } + case FPRType::eFPRTypeXSAVE: + return WriteRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE); + default: + return Error("Unrecognized FPR type"); + } +} + +bool +NativeRegisterContextLinux_x86_64::IsAVX(uint32_t reg_index) const +{ + return (m_reg_info.first_ymm <= reg_index && reg_index <= m_reg_info.last_ymm); +} + +bool +NativeRegisterContextLinux_x86_64::CopyXSTATEtoYMM (uint32_t reg_index, lldb::ByteOrder byte_order) +{ + if (!IsAVX (reg_index)) + return false; + + if (byte_order == lldb::eByteOrderLittle) + { + ::memcpy (m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes, + m_fpr.xstate.fxsave.xmm[reg_index - m_reg_info.first_ymm].bytes, + sizeof (XMMReg)); + ::memcpy (m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes + sizeof (XMMReg), + m_fpr.xstate.xsave.ymmh[reg_index - m_reg_info.first_ymm].bytes, + sizeof (YMMHReg)); + return true; + } + + if (byte_order == lldb::eByteOrderBig) + { + ::memcpy(m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes + sizeof (XMMReg), + m_fpr.xstate.fxsave.xmm[reg_index - m_reg_info.first_ymm].bytes, + sizeof (XMMReg)); + ::memcpy(m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes, + m_fpr.xstate.xsave.ymmh[reg_index - m_reg_info.first_ymm].bytes, + sizeof (YMMHReg)); + return true; + } + return false; // unsupported or invalid byte order + +} + +bool +NativeRegisterContextLinux_x86_64::CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order) +{ + if (!IsAVX(reg)) + return false; + + if (byte_order == lldb::eByteOrderLittle) + { + ::memcpy(m_fpr.xstate.fxsave.xmm[reg - m_reg_info.first_ymm].bytes, + m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes, + sizeof(XMMReg)); + ::memcpy(m_fpr.xstate.xsave.ymmh[reg - m_reg_info.first_ymm].bytes, + m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes + sizeof(XMMReg), + sizeof(YMMHReg)); + return true; + } + + if (byte_order == lldb::eByteOrderBig) + { + ::memcpy(m_fpr.xstate.fxsave.xmm[reg - m_reg_info.first_ymm].bytes, + m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes + sizeof(XMMReg), + sizeof(XMMReg)); + ::memcpy(m_fpr.xstate.xsave.ymmh[reg - m_reg_info.first_ymm].bytes, + m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes, + sizeof(YMMHReg)); + return true; + } + return false; // unsupported or invalid byte order +} + +void* +NativeRegisterContextLinux_x86_64::GetFPRBuffer() +{ + const FPRType fpr_type = GetFPRType (); + switch (fpr_type) + { + case FPRType::eFPRTypeFXSAVE: + return &m_fpr.xstate.fxsave; + case FPRType::eFPRTypeXSAVE: + return &m_iovec; + default: + return nullptr; + } +} + +size_t +NativeRegisterContextLinux_x86_64::GetFPRSize() +{ + const FPRType fpr_type = GetFPRType (); + switch (fpr_type) + { + case FPRType::eFPRTypeFXSAVE: + return sizeof(m_fpr.xstate.fxsave); + case FPRType::eFPRTypeXSAVE: + return sizeof(m_iovec); + default: + return 0; + } +} + +Error +NativeRegisterContextLinux_x86_64::ReadFPR () +{ + const FPRType fpr_type = GetFPRType (); + const lldb_private::ArchSpec& target_arch = GetRegisterInfoInterface().GetTargetArchitecture(); + switch (fpr_type) + { + case FPRType::eFPRTypeFXSAVE: + // For 32-bit inferiors on x86_32/x86_64 architectures, + // FXSAVE area can be read using PTRACE_GETREGSET ptrace api + // For 64-bit inferiors on x86_64 architectures, + // FXSAVE area can be read using PTRACE_GETFPREGS ptrace api + switch (target_arch.GetMachine ()) + { + case llvm::Triple::x86: + return ReadRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_PRXFPREG); + case llvm::Triple::x86_64: + return NativeRegisterContextLinux::ReadFPR(); + default: + assert(false && "Unhandled target architecture."); + break; + } + case FPRType::eFPRTypeXSAVE: + return ReadRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE); + default: + return Error("Unrecognized FPR type"); + } +} + +Error +NativeRegisterContextLinux_x86_64::IsWatchpointHit(uint32_t wp_index, bool &is_hit) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + RegisterValue reg_value; + Error error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value); + if (error.Fail()) + { + is_hit = false; + return error; + } + + uint64_t status_bits = reg_value.GetAsUInt64(); + + is_hit = status_bits & (1 << wp_index); + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) { + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) + { + bool is_hit; + Error error = IsWatchpointHit(wp_index, is_hit); + if (error.Fail()) { + wp_index = LLDB_INVALID_INDEX32; + return error; + } else if (is_hit) { + return error; + } + } + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +Error +NativeRegisterContextLinux_x86_64::IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error ("Watchpoint index out of range"); + + RegisterValue reg_value; + Error error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); + if (error.Fail()) + { + is_vacant = false; + return error; + } + + uint64_t control_bits = reg_value.GetAsUInt64(); + + is_vacant = !(control_bits & (1 << (2 * wp_index))); + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error ("Watchpoint index out of range"); + + // Read only watchpoints aren't supported on x86_64. Fall back to read/write waitchpoints instead. + // TODO: Add logic to detect when a write happens and ignore that watchpoint hit. + if (watch_flags == 0x2) + watch_flags = 0x3; + + if (watch_flags != 0x1 && watch_flags != 0x3) + return Error ("Invalid read/write bits for watchpoint"); + + if (size != 1 && size != 2 && size != 4 && size != 8) + return Error ("Invalid size for watchpoint"); + + bool is_vacant; + Error error = IsWatchpointVacant (wp_index, is_vacant); + if (error.Fail()) return error; + if (!is_vacant) return Error("Watchpoint index not vacant"); + + RegisterValue reg_value; + error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); + if (error.Fail()) return error; + + // for watchpoints 0, 1, 2, or 3, respectively, + // set bits 1, 3, 5, or 7 + uint64_t enable_bit = 1 << (2 * wp_index); + + // set bits 16-17, 20-21, 24-25, or 28-29 + // with 0b01 for write, and 0b11 for read/write + uint64_t rw_bits = watch_flags << (16 + 4 * wp_index); + + // set bits 18-19, 22-23, 26-27, or 30-31 + // with 0b00, 0b01, 0b10, or 0b11 + // for 1, 2, 8 (if supported), or 4 bytes, respectively + uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); + + uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + + control_bits |= enable_bit | rw_bits | size_bits; + + error = WriteRegisterRaw(m_reg_info.first_dr + wp_index, RegisterValue(addr)); + if (error.Fail()) return error; + + error = WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits)); + if (error.Fail()) return error; + + error.Clear(); + return error; +} + +bool +NativeRegisterContextLinux_x86_64::ClearHardwareWatchpoint(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + RegisterValue reg_value; + + // for watchpoints 0, 1, 2, or 3, respectively, + // clear bits 0, 1, 2, or 3 of the debug status register (DR6) + Error error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value); + if (error.Fail()) return false; + uint64_t bit_mask = 1 << wp_index; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegisterRaw(m_reg_info.first_dr + 6, RegisterValue(status_bits)); + if (error.Fail()) return false; + + // for watchpoints 0, 1, 2, or 3, respectively, + // clear bits {0-1,16-19}, {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} + // of the debug control register (DR7) + error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); + if (error.Fail()) return false; + bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits)).Success(); +} + +Error +NativeRegisterContextLinux_x86_64::ClearAllHardwareWatchpoints() +{ + RegisterValue reg_value; + + // clear bits {0-4} of the debug status register (DR6) + Error error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value); + if (error.Fail()) return error; + uint64_t bit_mask = 0xF; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegisterRaw(m_reg_info.first_dr + 6, RegisterValue(status_bits)); + if (error.Fail()) return error; + + // clear bits {0-7,16-31} of the debug control register (DR7) + error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); + if (error.Fail()) return error; + bit_mask = 0xFF | (0xFFFF << 16); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits)); +} + +uint32_t +NativeRegisterContextLinux_x86_64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) + { + bool is_vacant; + Error error = IsWatchpointVacant(wp_index, is_vacant); + if (is_vacant) + { + error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index); + if (error.Success()) + return wp_index; + } + if (error.Fail() && log) + { + log->Printf("NativeRegisterContextLinux_x86_64::%s Error: %s", + __FUNCTION__, error.AsCString()); + } + } + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextLinux_x86_64::GetWatchpointAddress(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + RegisterValue reg_value; + if (ReadRegisterRaw(m_reg_info.first_dr + wp_index, reg_value).Fail()) + return LLDB_INVALID_ADDRESS; + return reg_value.GetAsUInt64(); +} + +uint32_t +NativeRegisterContextLinux_x86_64::NumSupportedHardwareWatchpoints () +{ + // Available debug address registers: dr0, dr1, dr2, dr3 + return 4; +} + +#endif // defined(__i386__) || defined(__x86_64__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h new file mode 100644 index 000000000000..b04be4bb7688 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h @@ -0,0 +1,171 @@ +//===-- NativeRegisterContextLinux_x86_64.h ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__i386__) || defined(__x86_64__) + +#ifndef lldb_NativeRegisterContextLinux_x86_64_h +#define lldb_NativeRegisterContextLinux_x86_64_h + +#include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +namespace lldb_private { +namespace process_linux { + + class NativeProcessLinux; + + class NativeRegisterContextLinux_x86_64 : public NativeRegisterContextLinux + { + public: + NativeRegisterContextLinux_x86_64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + + uint32_t + GetRegisterSetCount () const override; + + const RegisterSet * + GetRegisterSet (uint32_t set_index) const override; + + uint32_t + GetUserRegisterCount() const override; + + Error + ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) override; + + Error + WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + Error + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override; + + Error + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; + + Error + IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Error + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; + + Error + IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool + ClearHardwareWatchpoint(uint32_t wp_index) override; + + Error + ClearAllHardwareWatchpoints () override; + + Error + SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, uint32_t wp_index); + + uint32_t + SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t + GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t + NumSupportedHardwareWatchpoints() override; + + protected: + void* + GetGPRBuffer() override { return &m_gpr_x86_64; } + + void* + GetFPRBuffer() override; + + size_t + GetFPRSize() override; + + Error + ReadFPR() override; + + Error + WriteFPR() override; + + private: + + // Private member types. + enum FPRType + { + eFPRTypeNotValid = 0, + eFPRTypeFXSAVE, + eFPRTypeXSAVE + }; + + // Info about register ranges. + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + uint32_t num_avx_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + + uint32_t first_st; + uint32_t last_st; + uint32_t first_mm; + uint32_t last_mm; + uint32_t first_xmm; + uint32_t last_xmm; + uint32_t first_ymm; + uint32_t last_ymm; + + uint32_t first_dr; + uint32_t gpr_flags; + }; + + // Private member variables. + mutable FPRType m_fpr_type; + FPR m_fpr; + IOVEC m_iovec; + YMM m_ymm_set; + RegInfo m_reg_info; + uint64_t m_gpr_x86_64[k_num_gpr_registers_x86_64]; + uint32_t m_fctrl_offset_in_userarea; + + // Private member methods. + bool IsRegisterSetAvailable (uint32_t set_index) const; + + bool + IsGPR(uint32_t reg_index) const; + + FPRType + GetFPRType () const; + + bool + IsFPR(uint32_t reg_index) const; + + bool + IsFPR(uint32_t reg_index, FPRType fpr_type) const; + + bool + CopyXSTATEtoYMM (uint32_t reg_index, lldb::ByteOrder byte_order); + + bool + CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order); + + bool + IsAVX (uint32_t reg_index) const; + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_x86_64_h + +#endif // defined(__i386__) || defined(__x86_64__) diff --git a/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/source/Plugins/Process/Linux/NativeThreadLinux.cpp new file mode 100644 index 000000000000..cbf82885e23a --- /dev/null +++ b/source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -0,0 +1,440 @@ +//===-- NativeThreadLinux.cpp --------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NativeThreadLinux.h" + +#include <signal.h> +#include <sstream> + +#include "NativeProcessLinux.h" +#include "NativeRegisterContextLinux.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/lldb-enumerations.h" + +#include "llvm/ADT/SmallString.h" + +#include "Plugins/Process/POSIX/CrashReason.h" + +#include <sys/syscall.h> +// Try to define a macro to encapsulate the tgkill syscall +#define tgkill(pid, tid, sig) \ + syscall(SYS_tgkill, static_cast< ::pid_t>(pid), static_cast< ::pid_t>(tid), sig) + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_linux; + +namespace +{ + void LogThreadStopInfo (Log &log, const ThreadStopInfo &stop_info, const char *const header) + { + switch (stop_info.reason) + { + case eStopReasonNone: + log.Printf ("%s: %s no stop reason", __FUNCTION__, header); + return; + case eStopReasonTrace: + log.Printf ("%s: %s trace, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + return; + case eStopReasonBreakpoint: + log.Printf ("%s: %s breakpoint, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + return; + case eStopReasonWatchpoint: + log.Printf ("%s: %s watchpoint, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + return; + case eStopReasonSignal: + log.Printf ("%s: %s signal 0x%02" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + return; + case eStopReasonException: + log.Printf ("%s: %s exception type 0x%02" PRIx64, __FUNCTION__, header, stop_info.details.exception.type); + return; + case eStopReasonExec: + log.Printf ("%s: %s exec, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + return; + case eStopReasonPlanComplete: + log.Printf ("%s: %s plan complete", __FUNCTION__, header); + return; + case eStopReasonThreadExiting: + log.Printf ("%s: %s thread exiting", __FUNCTION__, header); + return; + case eStopReasonInstrumentation: + log.Printf ("%s: %s instrumentation", __FUNCTION__, header); + return; + default: + log.Printf ("%s: %s invalid stop reason %" PRIu32, __FUNCTION__, header, static_cast<uint32_t> (stop_info.reason)); + } + } +} + +NativeThreadLinux::NativeThreadLinux (NativeProcessLinux *process, lldb::tid_t tid) : + NativeThreadProtocol (process, tid), + m_state (StateType::eStateInvalid), + m_stop_info (), + m_reg_context_sp (), + m_stop_description () +{ +} + +std::string +NativeThreadLinux::GetName() +{ + NativeProcessProtocolSP process_sp = m_process_wp.lock (); + if (!process_sp) + return "<unknown: no process>"; + + // const NativeProcessLinux *const process = reinterpret_cast<NativeProcessLinux*> (process_sp->get ()); + llvm::SmallString<32> thread_name; + HostNativeThread::GetName(GetID(), thread_name); + return thread_name.c_str(); +} + +lldb::StateType +NativeThreadLinux::GetState () +{ + return m_state; +} + + +bool +NativeThreadLinux::GetStopReason (ThreadStopInfo &stop_info, std::string& description) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + description.clear(); + + switch (m_state) + { + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + case eStateUnloaded: + if (log) + LogThreadStopInfo (*log, m_stop_info, "m_stop_info in thread:"); + stop_info = m_stop_info; + description = m_stop_description; + if (log) + LogThreadStopInfo (*log, stop_info, "returned stop_info:"); + + return true; + + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + if (log) + { + log->Printf ("NativeThreadLinux::%s tid %" PRIu64 " in state %s cannot answer stop reason", + __FUNCTION__, GetID (), StateAsCString (m_state)); + } + return false; + } + llvm_unreachable("unhandled StateType!"); +} + +NativeRegisterContextSP +NativeThreadLinux::GetRegisterContext () +{ + // Return the register context if we already created it. + if (m_reg_context_sp) + return m_reg_context_sp; + + NativeProcessProtocolSP m_process_sp = m_process_wp.lock (); + if (!m_process_sp) + return NativeRegisterContextSP (); + + ArchSpec target_arch; + if (!m_process_sp->GetArchitecture (target_arch)) + return NativeRegisterContextSP (); + + const uint32_t concrete_frame_idx = 0; + m_reg_context_sp.reset (NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(target_arch, + *this, + concrete_frame_idx)); + + return m_reg_context_sp; +} + +Error +NativeThreadLinux::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) +{ + if (!hardware) + return Error ("not implemented"); + if (m_state == eStateLaunching) + return Error (); + Error error = RemoveWatchpoint(addr); + if (error.Fail()) return error; + NativeRegisterContextSP reg_ctx = GetRegisterContext (); + uint32_t wp_index = + reg_ctx->SetHardwareWatchpoint (addr, size, watch_flags); + if (wp_index == LLDB_INVALID_INDEX32) + return Error ("Setting hardware watchpoint failed."); + m_watchpoint_index_map.insert({addr, wp_index}); + return Error (); +} + +Error +NativeThreadLinux::RemoveWatchpoint (lldb::addr_t addr) +{ + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Error (); + uint32_t wp_index = wp->second; + m_watchpoint_index_map.erase(wp); + if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index)) + return Error (); + return Error ("Clearing hardware watchpoint failed."); +} + +void +NativeThreadLinux::SetRunning () +{ + const StateType new_state = StateType::eStateRunning; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_description.clear(); + + // If watchpoints have been set, but none on this thread, + // then this is a new thread. So set all existing watchpoints. + if (m_watchpoint_index_map.empty()) + { + const auto process_sp = GetProcess(); + if (process_sp) + { + const auto &watchpoint_map = process_sp->GetWatchpointMap(); + if (watchpoint_map.empty()) return; + GetRegisterContext()->ClearAllHardwareWatchpoints(); + for (const auto &pair : watchpoint_map) + { + const auto& wp = pair.second; + SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); + } + } + } +} + +void +NativeThreadLinux::SetStepping () +{ + const StateType new_state = StateType::eStateStepping; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; +} + +void +NativeThreadLinux::SetStoppedBySignal(uint32_t signo, const siginfo_t *info) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("NativeThreadLinux::%s called with signal 0x%02" PRIx32, __FUNCTION__, signo); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonSignal; + m_stop_info.details.signal.signo = signo; + + m_stop_description.clear(); + if (info) + { + switch (signo) + { + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + //In case of MIPS64 target, SI_KERNEL is generated for invalid 64bit address. + const auto reason = (info->si_signo == SIGBUS && info->si_code == SI_KERNEL) ? + CrashReason::eInvalidAddress : GetCrashReason(*info); + m_stop_description = GetCrashReasonString(reason, reinterpret_cast<uintptr_t>(info->si_addr)); + break; + } + } +} + +bool +NativeThreadLinux::IsStopped (int *signo) +{ + if (!StateIsStoppedState (m_state, false)) + return false; + + // If we are stopped by a signal, return the signo. + if (signo && + m_state == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonSignal) + { + *signo = m_stop_info.details.signal.signo; + } + + // Regardless, we are stopped. + return true; +} + + +void +NativeThreadLinux::SetStoppedByExec () +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("NativeThreadLinux::%s()", __FUNCTION__); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonExec; + m_stop_info.details.signal.signo = SIGSTOP; +} + +void +NativeThreadLinux::SetStoppedByBreakpoint () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonBreakpoint; + m_stop_info.details.signal.signo = SIGTRAP; + m_stop_description.clear(); +} + +void +NativeThreadLinux::SetStoppedByWatchpoint (uint32_t wp_index) +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + m_stop_description.clear (); + + lldbassert(wp_index != LLDB_INVALID_INDEX32 && + "wp_index cannot be invalid"); + + std::ostringstream ostr; + ostr << GetRegisterContext()->GetWatchpointAddress(wp_index) << " "; + ostr << wp_index; + + /* + * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For example: + * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at 'm', then + * watch exception is generated even when 'n' is read/written. To handle this case, + * find the base address of the load/store instruction and append it in the stop-info + * packet. + */ + ostr << " " << GetRegisterContext()->GetWatchpointHitAddress(wp_index); + + m_stop_description = ostr.str(); + + m_stop_info.reason = StopReason::eStopReasonWatchpoint; + m_stop_info.details.signal.signo = SIGTRAP; +} + +bool +NativeThreadLinux::IsStoppedAtBreakpoint () +{ + return GetState () == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonBreakpoint; +} + +bool +NativeThreadLinux::IsStoppedAtWatchpoint () +{ + return GetState () == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonWatchpoint; +} + +void +NativeThreadLinux::SetStoppedByTrace () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonTrace; + m_stop_info.details.signal.signo = SIGTRAP; +} + +void +NativeThreadLinux::SetStoppedWithNoReason () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_info.details.signal.signo = 0; +} + +void +NativeThreadLinux::SetExited () +{ + const StateType new_state = StateType::eStateExited; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonThreadExiting; +} + +Error +NativeThreadLinux::RequestStop () +{ + Log* log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + const auto process_sp = GetProcess(); + if (! process_sp) + return Error("Process is null."); + + lldb::pid_t pid = process_sp->GetID(); + lldb::tid_t tid = GetID(); + + if (log) + log->Printf ("NativeThreadLinux::%s requesting thread stop(pid: %" PRIu64 ", tid: %" PRIu64 ")", __FUNCTION__, pid, tid); + + Error err; + errno = 0; + if (::tgkill (pid, tid, SIGSTOP) != 0) + { + err.SetErrorToErrno (); + if (log) + log->Printf ("NativeThreadLinux::%s tgkill(%" PRIu64 ", %" PRIu64 ", SIGSTOP) failed: %s", __FUNCTION__, pid, tid, err.AsCString ()); + } + + return err; +} + +void +NativeThreadLinux::MaybeLogStateChange (lldb::StateType new_state) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + // If we're not logging, we're done. + if (!log) + return; + + // If this is a state change to the same state, we're done. + lldb::StateType old_state = m_state; + if (new_state == old_state) + return; + + NativeProcessProtocolSP m_process_sp = m_process_wp.lock (); + lldb::pid_t pid = m_process_sp ? m_process_sp->GetID () : LLDB_INVALID_PROCESS_ID; + + // Log it. + log->Printf ("NativeThreadLinux: thread (pid=%" PRIu64 ", tid=%" PRIu64 ") changing from state %s to %s", pid, GetID (), StateAsCString (old_state), StateAsCString (new_state)); +} diff --git a/source/Plugins/Process/Linux/NativeThreadLinux.h b/source/Plugins/Process/Linux/NativeThreadLinux.h new file mode 100644 index 000000000000..bf6b00a78cfd --- /dev/null +++ b/source/Plugins/Process/Linux/NativeThreadLinux.h @@ -0,0 +1,120 @@ +//===-- NativeThreadLinux.h ----------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeThreadLinux_H_ +#define liblldb_NativeThreadLinux_H_ + +#include "lldb/lldb-private-forward.h" +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include <map> +#include <memory> +#include <string> + +namespace lldb_private { +namespace process_linux { + + class NativeProcessLinux; + + class NativeThreadLinux : public NativeThreadProtocol + { + friend class NativeProcessLinux; + + public: + NativeThreadLinux (NativeProcessLinux *process, lldb::tid_t tid); + + // --------------------------------------------------------------------- + // NativeThreadProtocol Interface + // --------------------------------------------------------------------- + std::string + GetName() override; + + lldb::StateType + GetState () override; + + bool + GetStopReason (ThreadStopInfo &stop_info, std::string& description) override; + + NativeRegisterContextSP + GetRegisterContext () override; + + Error + SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) override; + + Error + RemoveWatchpoint (lldb::addr_t addr) override; + + private: + // --------------------------------------------------------------------- + // Interface for friend classes + // --------------------------------------------------------------------- + void + SetRunning (); + + void + SetStepping (); + + void + SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr); + + /// Return true if the thread is stopped. + /// If stopped by a signal, indicate the signo in the signo argument. + /// Otherwise, return LLDB_INVALID_SIGNAL_NUMBER. + bool + IsStopped (int *signo); + + void + SetStoppedByExec (); + + void + SetStoppedByBreakpoint (); + + void + SetStoppedByWatchpoint (uint32_t wp_index); + + bool + IsStoppedAtBreakpoint (); + + bool + IsStoppedAtWatchpoint (); + + void + SetStoppedByTrace (); + + void + SetStoppedWithNoReason (); + + void + SetExited (); + + Error + RequestStop (); + + // --------------------------------------------------------------------- + // Private interface + // --------------------------------------------------------------------- + void + MaybeLogStateChange (lldb::StateType new_state); + + // --------------------------------------------------------------------- + // Member Variables + // --------------------------------------------------------------------- + lldb::StateType m_state; + ThreadStopInfo m_stop_info; + NativeRegisterContextSP m_reg_context_sp; + std::string m_stop_description; + using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>; + WatchpointIndexMap m_watchpoint_index_map; + }; + + typedef std::shared_ptr<NativeThreadLinux> NativeThreadLinuxSP; +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeThreadLinux_H_ diff --git a/source/Plugins/Process/Linux/ProcFileReader.cpp b/source/Plugins/Process/Linux/ProcFileReader.cpp new file mode 100644 index 000000000000..4d1f231f4f9b --- /dev/null +++ b/source/Plugins/Process/Linux/ProcFileReader.cpp @@ -0,0 +1,108 @@ +//===-- ProcFileReader.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Plugins/Process/Linux/ProcFileReader.h" + +// C Headers +#include <fcntl.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <sys/stat.h> + +// C++ Headers +#include <fstream> + +// LLDB Headers +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" + +using namespace lldb_private; +using namespace lldb_private::process_linux; + +lldb::DataBufferSP +ProcFileReader::ReadIntoDataBuffer (lldb::pid_t pid, const char *name) +{ + int fd; + char path[PATH_MAX]; + + // Make sure we've got a nil terminated buffer for all the folks calling + // GetBytes() directly off our returned DataBufferSP if we hit an error. + lldb::DataBufferSP buf_sp (new DataBufferHeap(1, 0)); + + // Ideally, we would simply create a FileSpec and call ReadFileContents. + // However, files in procfs have zero size (since they are, in general, + // dynamically generated by the kernel) which is incompatible with the + // current ReadFileContents implementation. Therefore we simply stream the + // data into a DataBuffer ourselves. + if (snprintf (path, PATH_MAX, "/proc/%" PRIu64 "/%s", pid, name) > 0) + { + if ((fd = open (path, O_RDONLY, 0)) >= 0) + { + size_t bytes_read = 0; + std::unique_ptr<DataBufferHeap> buf_ap(new DataBufferHeap(1024, 0)); + + for (;;) + { + size_t avail = buf_ap->GetByteSize() - bytes_read; + ssize_t status = read (fd, buf_ap->GetBytes() + bytes_read, avail); + + if (status < 0) + break; + + if (status == 0) + { + buf_ap->SetByteSize (bytes_read); + buf_sp.reset (buf_ap.release()); + break; + } + + bytes_read += status; + + if (avail - status == 0) + buf_ap->SetByteSize (2 * buf_ap->GetByteSize()); + } + + close (fd); + } + } + + return buf_sp; +} + +Error +ProcFileReader::ProcessLineByLine (lldb::pid_t pid, const char *name, std::function<bool (const std::string &line)> line_parser) +{ + Error error; + + // Try to open the /proc/{pid}/maps entry. + char filename [PATH_MAX]; + snprintf (filename, sizeof(filename), "/proc/%" PRIu64 "/%s", pid, name); + filename[sizeof (filename) - 1] = '\0'; + + std::ifstream proc_file (filename); + if (proc_file.fail ()) + { + error.SetErrorStringWithFormat ("failed to open file '%s'", filename); + return error; + } + + // Read the file line by line, processing until either end of file or when the line_parser returns false. + std::string line; + bool should_continue = true; + + while (should_continue && std::getline (proc_file, line)) + { + // Pass the line over to the line_parser for processing. If the line_parser returns false, we + // stop processing. + should_continue = line_parser (line); + } + + return error; +} diff --git a/source/Plugins/Process/Linux/ProcFileReader.h b/source/Plugins/Process/Linux/ProcFileReader.h new file mode 100644 index 000000000000..7b3812433068 --- /dev/null +++ b/source/Plugins/Process/Linux/ProcFileReader.h @@ -0,0 +1,37 @@ +//===-- ProcFileReader.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcFileReader_h_ +#define liblldb_ProcFileReader_h_ + +#include <functional> + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { +namespace process_linux { + + class ProcFileReader + { + public: + + static lldb::DataBufferSP + ReadIntoDataBuffer (lldb::pid_t pid, const char *name); + + /// Parse the /proc/{@a pid}/{@a name} file line by line, passing each line to line_parser, until + /// either end of file or until line_parser returns false. + static Error + ProcessLineByLine (lldb::pid_t pid, const char *name, std::function<bool (const std::string &line)> line_parser); + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef liblldb_ProcFileReader_h_ diff --git a/source/Plugins/Process/Linux/Procfs.h b/source/Plugins/Process/Linux/Procfs.h new file mode 100644 index 000000000000..cad433fb095d --- /dev/null +++ b/source/Plugins/Process/Linux/Procfs.h @@ -0,0 +1,31 @@ +//===-- Procfs.h ---------------------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// source/Plugins/Process/Linux/Procfs.h defines the symbols we need from +// sys/procfs.h on Android/Linux for all supported architectures. + +#include <sys/ptrace.h> + +#ifdef __ANDROID__ +#if defined (__arm64__) || defined (__aarch64__) +typedef unsigned long elf_greg_t; +typedef elf_greg_t elf_gregset_t[(sizeof (struct user_pt_regs) / sizeof(elf_greg_t))]; +typedef struct user_fpsimd_state elf_fpregset_t; +#ifndef NT_FPREGSET + #define NT_FPREGSET NT_PRFPREG +#endif // NT_FPREGSET +#elif defined (__mips__) +#ifndef NT_FPREGSET + #define NT_FPREGSET NT_PRFPREG +#endif // NT_FPREGSET +#endif +#else // __ANDROID__ +#include <sys/procfs.h> +#endif // __ANDROID__ + diff --git a/source/Plugins/Process/MacOSX-Kernel/CMakeLists.txt b/source/Plugins/Process/MacOSX-Kernel/CMakeLists.txt new file mode 100644 index 000000000000..681b7405e2b8 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/CMakeLists.txt @@ -0,0 +1,10 @@ +add_lldb_library(lldbPluginProcessMacOSXKernel + CommunicationKDP.cpp + ProcessKDP.cpp + ProcessKDPLog.cpp + RegisterContextKDP_arm.cpp + RegisterContextKDP_arm64.cpp + RegisterContextKDP_i386.cpp + RegisterContextKDP_x86_64.cpp + ThreadKDP.cpp + ) diff --git a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp new file mode 100644 index 000000000000..5c1c3284a35c --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp @@ -0,0 +1,1445 @@ +//===-- CommunicationKDP.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "CommunicationKDP.h" + +// C Includes +#include <errno.h> +#include <limits.h> +#include <string.h> + +// C++ Includes + +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Target/Process.h" + +// Project includes +#include "ProcessKDPLog.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// CommunicationKDP constructor +//---------------------------------------------------------------------- +CommunicationKDP::CommunicationKDP (const char *comm_name) : + Communication(comm_name), + m_addr_byte_size (4), + m_byte_order (eByteOrderLittle), + m_packet_timeout (5), + m_sequence_mutex (Mutex::eMutexTypeRecursive), + m_is_running (false), + m_session_key (0u), + m_request_sequence_id (0u), + m_exception_sequence_id (0u), + m_kdp_version_version (0u), + m_kdp_version_feature (0u), + m_kdp_hostinfo_cpu_mask (0u), + m_kdp_hostinfo_cpu_type (0u), + m_kdp_hostinfo_cpu_subtype (0u) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommunicationKDP::~CommunicationKDP() +{ + if (IsConnected()) + { + Disconnect(); + } +} + +bool +CommunicationKDP::SendRequestPacket (const PacketStreamType &request_packet) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendRequestPacketNoLock (request_packet); +} + +#if 0 +typedef struct { + uint8_t request; // Either: CommandType | ePacketTypeRequest, or CommandType | ePacketTypeReply + uint8_t sequence; + uint16_t length; // Length of entire packet including this header + uint32_t key; // Session key +} kdp_hdr_t; +#endif + +void +CommunicationKDP::MakeRequestPacketHeader (CommandType request_type, + PacketStreamType &request_packet, + uint16_t request_length) +{ + request_packet.Clear(); + request_packet.PutHex8 (request_type | ePacketTypeRequest); // Set the request type + request_packet.PutHex8 (m_request_sequence_id++); // Sequence number + request_packet.PutHex16 (request_length); // Length of the packet including this header + request_packet.PutHex32 (m_session_key); // Session key +} + +bool +CommunicationKDP::SendRequestAndGetReply (const CommandType command, + const PacketStreamType &request_packet, + DataExtractor &reply_packet) +{ + if (IsRunning()) + { + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + { + PacketStreamType log_strm; + DumpPacket (log_strm, request_packet.GetData(), request_packet.GetSize()); + log->Printf("error: kdp running, not sending packet: %.*s", (uint32_t)log_strm.GetSize(), log_strm.GetData()); + } + return false; + } + + Mutex::Locker locker(m_sequence_mutex); +#ifdef LLDB_CONFIGURATION_DEBUG + // NOTE: this only works for packets that are in native endian byte order + assert (request_packet.GetSize() == *((uint16_t *)(request_packet.GetData() + 2))); +#endif + lldb::offset_t offset = 1; + const uint32_t num_retries = 3; + for (uint32_t i=0; i<num_retries; ++i) + { + if (SendRequestPacketNoLock(request_packet)) + { + const uint8_t request_sequence_id = (uint8_t)request_packet.GetData()[1]; + while (1) + { + if (WaitForPacketWithTimeoutMicroSecondsNoLock (reply_packet, GetPacketTimeoutInMicroSeconds ())) + { + offset = 0; + const uint8_t reply_command = reply_packet.GetU8 (&offset); + const uint8_t reply_sequence_id = reply_packet.GetU8 (&offset); + if (request_sequence_id == reply_sequence_id) + { + // The sequent ID was correct, now verify we got the response we were looking for + if ((reply_command & eCommandTypeMask) == command) + { + // Success + if (command == KDP_RESUMECPUS) + m_is_running.SetValue(true, eBroadcastAlways); + return true; + } + else + { + // Failed to get the correct response, bail + reply_packet.Clear(); + return false; + } + } + else if (reply_sequence_id > request_sequence_id) + { + // Sequence ID was greater than the sequence ID of the packet we sent, something + // is really wrong... + reply_packet.Clear(); + return false; + } + else + { + // The reply sequence ID was less than our current packet's sequence ID + // so we should keep trying to get a response because this was a response + // for a previous packet that we must have retried. + } + } + else + { + // Break and retry sending the packet as we didn't get a response due to timeout + break; + } + } + } + } + reply_packet.Clear(); + return false; +} + +bool +CommunicationKDP::SendRequestPacketNoLock (const PacketStreamType &request_packet) +{ + if (IsConnected()) + { + const char *packet_data = request_packet.GetData(); + const size_t packet_size = request_packet.GetSize(); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + { + PacketStreamType log_strm; + DumpPacket (log_strm, packet_data, packet_size); + log->Printf("%.*s", (uint32_t)log_strm.GetSize(), log_strm.GetData()); + } + ConnectionStatus status = eConnectionStatusSuccess; + + size_t bytes_written = Write (packet_data, + packet_size, + status, + NULL); + + if (bytes_written == packet_size) + return true; + + if (log) + log->Printf ("error: failed to send packet entire packet %" PRIu64 " of %" PRIu64 " bytes sent", (uint64_t)bytes_written, (uint64_t)packet_size); + } + return false; +} + +bool +CommunicationKDP::GetSequenceMutex (Mutex::Locker& locker) +{ + return locker.TryLock (m_sequence_mutex); +} + + +bool +CommunicationKDP::WaitForNotRunningPrivate (const TimeValue *timeout_ptr) +{ + return m_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL); +} + +size_t +CommunicationKDP::WaitForPacketWithTimeoutMicroSeconds (DataExtractor &packet, uint32_t timeout_usec) +{ + Mutex::Locker locker(m_sequence_mutex); + return WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec); +} + +size_t +CommunicationKDP::WaitForPacketWithTimeoutMicroSecondsNoLock (DataExtractor &packet, uint32_t timeout_usec) +{ + uint8_t buffer[8192]; + Error error; + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS | KDP_LOG_VERBOSE)); + + // Check for a packet from our cache first without trying any reading... + if (CheckForPacket (NULL, 0, packet)) + return packet.GetByteSize(); + + bool timed_out = false; + while (IsConnected() && !timed_out) + { + lldb::ConnectionStatus status = eConnectionStatusNoConnection; + size_t bytes_read = Read (buffer, sizeof(buffer), timeout_usec, status, &error); + + if (log) + log->Printf ("%s: Read (buffer, (sizeof(buffer), timeout_usec = 0x%x, status = %s, error = %s) => bytes_read = %" PRIu64, + __PRETTY_FUNCTION__, + timeout_usec, + Communication::ConnectionStatusAsCString (status), + error.AsCString(), + (uint64_t)bytes_read); + + if (bytes_read > 0) + { + if (CheckForPacket (buffer, bytes_read, packet)) + return packet.GetByteSize(); + } + else + { + switch (status) + { + case eConnectionStatusInterrupted: + case eConnectionStatusTimedOut: + timed_out = true; + break; + case eConnectionStatusSuccess: + //printf ("status = success but error = %s\n", error.AsCString("<invalid>")); + break; + + case eConnectionStatusEndOfFile: + case eConnectionStatusNoConnection: + case eConnectionStatusLostConnection: + case eConnectionStatusError: + Disconnect(); + break; + } + } + } + packet.Clear (); + return 0; +} + +bool +CommunicationKDP::CheckForPacket (const uint8_t *src, size_t src_len, DataExtractor &packet) +{ + // Put the packet data into the buffer in a thread safe fashion + Mutex::Locker locker(m_bytes_mutex); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + + if (src && src_len > 0) + { + if (log && log->GetVerbose()) + { + PacketStreamType log_strm; + DataExtractor::DumpHexBytes (&log_strm, src, src_len, UINT32_MAX, LLDB_INVALID_ADDRESS); + log->Printf ("CommunicationKDP::%s adding %u bytes: %s", + __FUNCTION__, + (uint32_t)src_len, + log_strm.GetData()); + } + m_bytes.append ((const char *)src, src_len); + } + + // Make sure we at least have enough bytes for a packet header + const size_t bytes_available = m_bytes.size(); + if (bytes_available >= 8) + { + packet.SetData (&m_bytes[0], bytes_available, m_byte_order); + lldb::offset_t offset = 0; + uint8_t reply_command = packet.GetU8(&offset); + switch (reply_command) + { + case ePacketTypeRequest | KDP_EXCEPTION: + case ePacketTypeRequest | KDP_TERMINATION: + // We got an exception request, so be sure to send an ACK + { + PacketStreamType request_ack_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + // Set the reply but and make the ACK packet + request_ack_packet.PutHex8 (reply_command | ePacketTypeReply); + request_ack_packet.PutHex8 (packet.GetU8(&offset)); + request_ack_packet.PutHex16 (packet.GetU16(&offset)); + request_ack_packet.PutHex32 (packet.GetU32(&offset)); + m_is_running.SetValue(false, eBroadcastAlways); + // Ack to the exception or termination + SendRequestPacketNoLock (request_ack_packet); + } + // Fall through to case below to get packet contents + case ePacketTypeReply | KDP_CONNECT: + case ePacketTypeReply | KDP_DISCONNECT: + case ePacketTypeReply | KDP_HOSTINFO: + case ePacketTypeReply | KDP_VERSION: + case ePacketTypeReply | KDP_MAXBYTES: + case ePacketTypeReply | KDP_READMEM: + case ePacketTypeReply | KDP_WRITEMEM: + case ePacketTypeReply | KDP_READREGS: + case ePacketTypeReply | KDP_WRITEREGS: + case ePacketTypeReply | KDP_LOAD: + case ePacketTypeReply | KDP_IMAGEPATH: + case ePacketTypeReply | KDP_SUSPEND: + case ePacketTypeReply | KDP_RESUMECPUS: + case ePacketTypeReply | KDP_BREAKPOINT_SET: + case ePacketTypeReply | KDP_BREAKPOINT_REMOVE: + case ePacketTypeReply | KDP_REGIONS: + case ePacketTypeReply | KDP_REATTACH: + case ePacketTypeReply | KDP_HOSTREBOOT: + case ePacketTypeReply | KDP_READMEM64: + case ePacketTypeReply | KDP_WRITEMEM64: + case ePacketTypeReply | KDP_BREAKPOINT_SET64: + case ePacketTypeReply | KDP_BREAKPOINT_REMOVE64: + case ePacketTypeReply | KDP_KERNELVERSION: + case ePacketTypeReply | KDP_READPHYSMEM64: + case ePacketTypeReply | KDP_WRITEPHYSMEM64: + case ePacketTypeReply | KDP_READIOPORT: + case ePacketTypeReply | KDP_WRITEIOPORT: + case ePacketTypeReply | KDP_READMSR64: + case ePacketTypeReply | KDP_WRITEMSR64: + case ePacketTypeReply | KDP_DUMPINFO: + { + offset = 2; + const uint16_t length = packet.GetU16 (&offset); + if (length <= bytes_available) + { + // We have an entire packet ready, we need to copy the data + // bytes into a buffer that will be owned by the packet and + // erase the bytes from our communcation buffer "m_bytes" + packet.SetData (DataBufferSP (new DataBufferHeap (&m_bytes[0], length))); + m_bytes.erase (0, length); + + if (log) + { + PacketStreamType log_strm; + DumpPacket (log_strm, packet); + + log->Printf("%.*s", (uint32_t)log_strm.GetSize(), log_strm.GetData()); + } + return true; + } + } + break; + + default: + // Unrecognized reply command byte, erase this byte and try to get back on track + if (log) + log->Printf ("CommunicationKDP::%s: tossing junk byte: 0x%2.2x", + __FUNCTION__, + (uint8_t)m_bytes[0]); + m_bytes.erase(0, 1); + break; + } + } + packet.Clear(); + return false; +} + + +bool +CommunicationKDP::SendRequestConnect (uint16_t reply_port, + uint16_t exc_port, + const char *greeting) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + if (greeting == NULL) + greeting = ""; + + const CommandType command = KDP_CONNECT; + // Length is 82 uint16_t and the length of the greeting C string with the terminating NULL + const uint32_t command_length = 8 + 2 + 2 + ::strlen(greeting) + 1; + MakeRequestPacketHeader (command, request_packet, command_length); + // Always send connect ports as little endian + request_packet.SetByteOrder (eByteOrderLittle); + request_packet.PutHex16 (htons(reply_port)); + request_packet.PutHex16 (htons(exc_port)); + request_packet.SetByteOrder (m_byte_order); + request_packet.PutCString (greeting); + DataExtractor reply_packet; + return SendRequestAndGetReply (command, request_packet, reply_packet); +} + +void +CommunicationKDP::ClearKDPSettings () +{ + m_request_sequence_id = 0; + m_kdp_version_version = 0; + m_kdp_version_feature = 0; + m_kdp_hostinfo_cpu_mask = 0; + m_kdp_hostinfo_cpu_type = 0; + m_kdp_hostinfo_cpu_subtype = 0; +} + +bool +CommunicationKDP::SendRequestReattach (uint16_t reply_port) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_REATTACH; + // Length is 8 bytes for the header plus 2 bytes for the reply UDP port + const uint32_t command_length = 8 + 2; + MakeRequestPacketHeader (command, request_packet, command_length); + // Always send connect ports as little endian + request_packet.SetByteOrder (eByteOrderLittle); + request_packet.PutHex16(htons(reply_port)); + request_packet.SetByteOrder (m_byte_order); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + // Reset the sequence ID to zero for reattach + ClearKDPSettings (); + lldb::offset_t offset = 4; + m_session_key = reply_packet.GetU32 (&offset); + return true; + } + return false; +} + +uint32_t +CommunicationKDP::GetVersion () +{ + if (!VersionIsValid()) + SendRequestVersion(); + return m_kdp_version_version; +} + +uint32_t +CommunicationKDP::GetFeatureFlags () +{ + if (!VersionIsValid()) + SendRequestVersion(); + return m_kdp_version_feature; +} + +bool +CommunicationKDP::SendRequestVersion () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_VERSION; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + m_kdp_version_version = reply_packet.GetU32 (&offset); + m_kdp_version_feature = reply_packet.GetU32 (&offset); + return true; + } + return false; +} + +#if 0 // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... +const char * +CommunicationKDP::GetImagePath () +{ + if (m_image_path.empty()) + SendRequestImagePath(); + return m_image_path.c_str(); +} + +bool +CommunicationKDP::SendRequestImagePath () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_IMAGEPATH; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + const char *path = reply_packet.PeekCStr(8); + if (path && path[0]) + m_kernel_version.assign (path); + return true; + } + return false; +} +#endif + +uint32_t +CommunicationKDP::GetCPUMask () +{ + if (!HostInfoIsValid()) + SendRequestHostInfo(); + return m_kdp_hostinfo_cpu_mask; +} + +uint32_t +CommunicationKDP::GetCPUType () +{ + if (!HostInfoIsValid()) + SendRequestHostInfo(); + return m_kdp_hostinfo_cpu_type; +} + +uint32_t +CommunicationKDP::GetCPUSubtype () +{ + if (!HostInfoIsValid()) + SendRequestHostInfo(); + return m_kdp_hostinfo_cpu_subtype; +} + +lldb_private::UUID +CommunicationKDP::GetUUID () +{ + UUID uuid; + if (GetKernelVersion() == NULL) + return uuid; + + if (m_kernel_version.find("UUID=") == std::string::npos) + return uuid; + + size_t p = m_kernel_version.find("UUID=") + strlen ("UUID="); + std::string uuid_str = m_kernel_version.substr(p, 36); + if (uuid_str.size() < 32) + return uuid; + + if (uuid.SetFromCString (uuid_str.c_str()) == 0) + { + UUID invalid_uuid; + return invalid_uuid; + } + + return uuid; +} + +bool +CommunicationKDP::RemoteIsEFI () +{ + if (GetKernelVersion() == NULL) + return false; + if (strncmp (m_kernel_version.c_str(), "EFI", 3) == 0) + return true; + else + return false; +} + +bool +CommunicationKDP::RemoteIsDarwinKernel () +{ + if (GetKernelVersion() == NULL) + return false; + if (m_kernel_version.find("Darwin Kernel") != std::string::npos) + return true; + else + return false; +} + +lldb::addr_t +CommunicationKDP::GetLoadAddress () +{ + if (GetKernelVersion() == NULL) + return LLDB_INVALID_ADDRESS; + + if (m_kernel_version.find("stext=") == std::string::npos) + return LLDB_INVALID_ADDRESS; + size_t p = m_kernel_version.find("stext=") + strlen ("stext="); + if (m_kernel_version[p] != '0' || m_kernel_version[p + 1] != 'x') + return LLDB_INVALID_ADDRESS; + + addr_t kernel_load_address; + errno = 0; + kernel_load_address = ::strtoul (m_kernel_version.c_str() + p, NULL, 16); + if (errno != 0 || kernel_load_address == 0) + return LLDB_INVALID_ADDRESS; + + return kernel_load_address; +} + +bool +CommunicationKDP::SendRequestHostInfo () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_HOSTINFO; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + m_kdp_hostinfo_cpu_mask = reply_packet.GetU32 (&offset); + m_kdp_hostinfo_cpu_type = reply_packet.GetU32 (&offset); + m_kdp_hostinfo_cpu_subtype = reply_packet.GetU32 (&offset); + + ArchSpec kernel_arch; + kernel_arch.SetArchitecture (eArchTypeMachO, + m_kdp_hostinfo_cpu_type, + m_kdp_hostinfo_cpu_subtype); + + m_addr_byte_size = kernel_arch.GetAddressByteSize(); + m_byte_order = kernel_arch.GetByteOrder(); + return true; + } + return false; +} + +const char * +CommunicationKDP::GetKernelVersion () +{ + if (m_kernel_version.empty()) + SendRequestKernelVersion (); + return m_kernel_version.c_str(); +} + +bool +CommunicationKDP::SendRequestKernelVersion () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_KERNELVERSION; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + const char *kernel_version_cstr = reply_packet.PeekCStr(8); + if (kernel_version_cstr && kernel_version_cstr[0]) + m_kernel_version.assign (kernel_version_cstr); + return true; + } + return false; +} + +bool +CommunicationKDP::SendRequestDisconnect () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_DISCONNECT; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + // Are we supposed to get a reply for disconnect? + } + ClearKDPSettings (); + return true; +} + +uint32_t +CommunicationKDP::SendRequestReadMemory (lldb::addr_t addr, + void *dst, + uint32_t dst_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + bool use_64 = (GetVersion() >= 11); + uint32_t command_addr_byte_size = use_64 ? 8 : 4; + const CommandType command = use_64 ? KDP_READMEM64 : KDP_READMEM; + // Size is header + address size + uint32_t length + const uint32_t command_length = 8 + command_addr_byte_size + 4; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutMaxHex64 (addr, command_addr_byte_size); + request_packet.PutHex32 (dst_len); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + uint32_t src_len = reply_packet.GetByteSize() - 12; + + if (src_len > 0) + { + const void *src = reply_packet.GetData(&offset, src_len); + if (src) + { + ::memcpy (dst, src, src_len); + error.Clear(); + return src_len; + } + } + if (kdp_error) + error.SetErrorStringWithFormat ("kdp read memory failed (error %u)", kdp_error); + else + error.SetErrorString ("kdp read memory failed"); + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + + +uint32_t +CommunicationKDP::SendRequestWriteMemory (lldb::addr_t addr, + const void *src, + uint32_t src_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + bool use_64 = (GetVersion() >= 11); + uint32_t command_addr_byte_size = use_64 ? 8 : 4; + const CommandType command = use_64 ? KDP_WRITEMEM64 : KDP_WRITEMEM; + // Size is header + address size + uint32_t length + const uint32_t command_length = 8 + command_addr_byte_size + 4 + src_len; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutMaxHex64 (addr, command_addr_byte_size); + request_packet.PutHex32 (src_len); + request_packet.PutRawBytes(src, src_len); + + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error) + error.SetErrorStringWithFormat ("kdp write memory failed (error %u)", kdp_error); + else + { + error.Clear(); + return src_len; + } + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + +bool +CommunicationKDP::SendRawRequest (uint8_t command_byte, + const void *src, // Raw packet payload bytes + uint32_t src_len, // Raw packet payload length + DataExtractor &reply_packet, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + // Size is header + address size + uint32_t length + const uint32_t command_length = 8 + src_len; + const CommandType command = (CommandType)command_byte; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutRawBytes(src, src_len); + + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error && (command_byte != KDP_DUMPINFO)) + error.SetErrorStringWithFormat ("request packet 0x%8.8x failed (error %u)", command_byte, kdp_error); + else + { + error.Clear(); + return true; + } + } + else + { + error.SetErrorString ("failed to send packet"); + } + return false; +} + + +const char * +CommunicationKDP::GetCommandAsCString (uint8_t command) +{ + switch (command) + { + case KDP_CONNECT: return "KDP_CONNECT"; + case KDP_DISCONNECT: return "KDP_DISCONNECT"; + case KDP_HOSTINFO: return "KDP_HOSTINFO"; + case KDP_VERSION: return "KDP_VERSION"; + case KDP_MAXBYTES: return "KDP_MAXBYTES"; + case KDP_READMEM: return "KDP_READMEM"; + case KDP_WRITEMEM: return "KDP_WRITEMEM"; + case KDP_READREGS: return "KDP_READREGS"; + case KDP_WRITEREGS: return "KDP_WRITEREGS"; + case KDP_LOAD: return "KDP_LOAD"; + case KDP_IMAGEPATH: return "KDP_IMAGEPATH"; + case KDP_SUSPEND: return "KDP_SUSPEND"; + case KDP_RESUMECPUS: return "KDP_RESUMECPUS"; + case KDP_EXCEPTION: return "KDP_EXCEPTION"; + case KDP_TERMINATION: return "KDP_TERMINATION"; + case KDP_BREAKPOINT_SET: return "KDP_BREAKPOINT_SET"; + case KDP_BREAKPOINT_REMOVE: return "KDP_BREAKPOINT_REMOVE"; + case KDP_REGIONS: return "KDP_REGIONS"; + case KDP_REATTACH: return "KDP_REATTACH"; + case KDP_HOSTREBOOT: return "KDP_HOSTREBOOT"; + case KDP_READMEM64: return "KDP_READMEM64"; + case KDP_WRITEMEM64: return "KDP_WRITEMEM64"; + case KDP_BREAKPOINT_SET64: return "KDP_BREAKPOINT64_SET"; + case KDP_BREAKPOINT_REMOVE64: return "KDP_BREAKPOINT64_REMOVE"; + case KDP_KERNELVERSION: return "KDP_KERNELVERSION"; + case KDP_READPHYSMEM64: return "KDP_READPHYSMEM64"; + case KDP_WRITEPHYSMEM64: return "KDP_WRITEPHYSMEM64"; + case KDP_READIOPORT: return "KDP_READIOPORT"; + case KDP_WRITEIOPORT: return "KDP_WRITEIOPORT"; + case KDP_READMSR64: return "KDP_READMSR64"; + case KDP_WRITEMSR64: return "KDP_WRITEMSR64"; + case KDP_DUMPINFO: return "KDP_DUMPINFO"; + } + return NULL; +} + +void +CommunicationKDP::DumpPacket (Stream &s, const void *data, uint32_t data_len) +{ + DataExtractor extractor (data, data_len, m_byte_order, m_addr_byte_size); + DumpPacket (s, extractor); +} + +void +CommunicationKDP::DumpPacket (Stream &s, const DataExtractor& packet) +{ + const char *error_desc = NULL; + if (packet.GetByteSize() < 8) + { + error_desc = "error: invalid packet (too short): "; + } + else + { + lldb::offset_t offset = 0; + const uint8_t first_packet_byte = packet.GetU8 (&offset); + const uint8_t sequence_id = packet.GetU8 (&offset); + const uint16_t length = packet.GetU16 (&offset); + const uint32_t key = packet.GetU32 (&offset); + const CommandType command = ExtractCommand (first_packet_byte); + const char *command_name = GetCommandAsCString (command); + if (command_name) + { + const bool is_reply = ExtractIsReply(first_packet_byte); + s.Printf ("(running=%i) %s %24s: 0x%2.2x 0x%2.2x 0x%4.4x 0x%8.8x ", + IsRunning(), + is_reply ? "<--" : "-->", + command_name, + first_packet_byte, + sequence_id, + length, + key); + + if (is_reply) + { + // Dump request reply packets + switch (command) + { + // Commands that return a single 32 bit error + case KDP_CONNECT: + case KDP_WRITEMEM: + case KDP_WRITEMEM64: + case KDP_BREAKPOINT_SET: + case KDP_BREAKPOINT_REMOVE: + case KDP_BREAKPOINT_SET64: + case KDP_BREAKPOINT_REMOVE64: + case KDP_WRITEREGS: + case KDP_LOAD: + case KDP_WRITEIOPORT: + case KDP_WRITEMSR64: + { + const uint32_t error = packet.GetU32 (&offset); + s.Printf(" (error=0x%8.8x)", error); + } + break; + + case KDP_DISCONNECT: + case KDP_REATTACH: + case KDP_HOSTREBOOT: + case KDP_SUSPEND: + case KDP_RESUMECPUS: + case KDP_EXCEPTION: + case KDP_TERMINATION: + // No return value for the reply, just the header to ack + s.PutCString(" ()"); + break; + + case KDP_HOSTINFO: + { + const uint32_t cpu_mask = packet.GetU32 (&offset); + const uint32_t cpu_type = packet.GetU32 (&offset); + const uint32_t cpu_subtype = packet.GetU32 (&offset); + s.Printf(" (cpu_mask=0x%8.8x, cpu_type=0x%8.8x, cpu_subtype=0x%8.8x)", cpu_mask, cpu_type, cpu_subtype); + } + break; + + case KDP_VERSION: + { + const uint32_t version = packet.GetU32 (&offset); + const uint32_t feature = packet.GetU32 (&offset); + s.Printf(" (version=0x%8.8x, feature=0x%8.8x)", version, feature); + } + break; + + case KDP_REGIONS: + { + const uint32_t region_count = packet.GetU32 (&offset); + s.Printf(" (count = %u", region_count); + for (uint32_t i=0; i<region_count; ++i) + { + const addr_t region_addr = packet.GetPointer (&offset); + const uint32_t region_size = packet.GetU32 (&offset); + const uint32_t region_prot = packet.GetU32 (&offset); + s.Printf("\n\tregion[%" PRIu64 "] = { range = [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), size = 0x%8.8x, prot = %s }", region_addr, region_addr, region_addr + region_size, region_size, GetPermissionsAsCString (region_prot)); + } + } + break; + + case KDP_READMEM: + case KDP_READMEM64: + case KDP_READPHYSMEM64: + { + const uint32_t error = packet.GetU32 (&offset); + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (error = 0x%8.8x:\n", error); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatBytesWithASCII, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + m_last_read_memory_addr, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_READREGS: + { + const uint32_t error = packet.GetU32 (&offset); + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (error = 0x%8.8x regs:\n", error); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + m_addr_byte_size, // Size of each item in bytes + count / m_addr_byte_size, // Number of items + 16 / m_addr_byte_size, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_KERNELVERSION: + { + const char *kernel_version = packet.PeekCStr(8); + s.Printf(" (version = \"%s\")", kernel_version); + } + break; + + case KDP_MAXBYTES: + { + const uint32_t max_bytes = packet.GetU32 (&offset); + s.Printf(" (max_bytes = 0x%8.8x (%u))", max_bytes, max_bytes); + } + break; + case KDP_IMAGEPATH: + { + const char *path = packet.GetCStr(&offset); + s.Printf(" (path = \"%s\")", path); + } + break; + + case KDP_READIOPORT: + case KDP_READMSR64: + { + const uint32_t error = packet.GetU32 (&offset); + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (error = 0x%8.8x io:\n", error); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + case KDP_DUMPINFO: + { + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (count = %u, bytes = \n", count); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + + } + break; + + default: + s.Printf(" (add support for dumping this packet reply!!!"); + break; + + } + } + else + { + // Dump request packets + switch (command) + { + case KDP_CONNECT: + { + const uint16_t reply_port = ntohs(packet.GetU16 (&offset)); + const uint16_t exc_port = ntohs(packet.GetU16 (&offset)); + s.Printf(" (reply_port = %u, exc_port = %u, greeting = \"%s\")", reply_port, exc_port, packet.GetCStr(&offset)); + } + break; + + case KDP_DISCONNECT: + case KDP_HOSTREBOOT: + case KDP_HOSTINFO: + case KDP_VERSION: + case KDP_REGIONS: + case KDP_KERNELVERSION: + case KDP_MAXBYTES: + case KDP_IMAGEPATH: + case KDP_SUSPEND: + // No args, just the header in the request... + s.PutCString(" ()"); + break; + + case KDP_RESUMECPUS: + { + const uint32_t cpu_mask = packet.GetU32 (&offset); + s.Printf(" (cpu_mask = 0x%8.8x)", cpu_mask); + } + break; + + case KDP_READMEM: + { + const uint32_t addr = packet.GetU32 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%8.8x, size = %u)", addr, size); + m_last_read_memory_addr = addr; + } + break; + + case KDP_WRITEMEM: + { + const uint32_t addr = packet.GetU32 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%8.8x, size = %u, bytes = \n", addr, size); + if (size > 0) + DataExtractor::DumpHexBytes(&s, packet.GetData(&offset, size), size, 32, addr); + } + break; + + case KDP_READMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%16.16" PRIx64 ", size = %u)", addr, size); + m_last_read_memory_addr = addr; + } + break; + + case KDP_READPHYSMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + const uint32_t lcpu = packet.GetU16 (&offset); + s.Printf(" (addr = 0x%16.16llx, size = %u, lcpu = %u)", addr, size, lcpu); + m_last_read_memory_addr = addr; + } + break; + + case KDP_WRITEMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%16.16" PRIx64 ", size = %u, bytes = \n", addr, size); + if (size > 0) + DataExtractor::DumpHexBytes(&s, packet.GetData(&offset, size), size, 32, addr); + } + break; + + case KDP_WRITEPHYSMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + const uint32_t lcpu = packet.GetU16 (&offset); + s.Printf(" (addr = 0x%16.16llx, size = %u, lcpu = %u, bytes = \n", addr, size, lcpu); + if (size > 0) + DataExtractor::DumpHexBytes(&s, packet.GetData(&offset, size), size, 32, addr); + } + break; + + case KDP_READREGS: + { + const uint32_t cpu = packet.GetU32 (&offset); + const uint32_t flavor = packet.GetU32 (&offset); + s.Printf(" (cpu = %u, flavor = %u)", cpu, flavor); + } + break; + + case KDP_WRITEREGS: + { + const uint32_t cpu = packet.GetU32 (&offset); + const uint32_t flavor = packet.GetU32 (&offset); + const uint32_t nbytes = packet.GetByteSize() - offset; + s.Printf(" (cpu = %u, flavor = %u, regs = \n", cpu, flavor); + if (nbytes > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + m_addr_byte_size, // Size of each item in bytes + nbytes / m_addr_byte_size, // Number of items + 16 / m_addr_byte_size, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + + case KDP_BREAKPOINT_SET: + case KDP_BREAKPOINT_REMOVE: + { + const uint32_t addr = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%8.8x)", addr); + } + break; + + case KDP_BREAKPOINT_SET64: + case KDP_BREAKPOINT_REMOVE64: + { + const uint64_t addr = packet.GetU64 (&offset); + s.Printf(" (addr = 0x%16.16" PRIx64 ")", addr); + } + break; + + + case KDP_LOAD: + { + const char *path = packet.GetCStr(&offset); + s.Printf(" (path = \"%s\")", path); + } + break; + + case KDP_EXCEPTION: + { + const uint32_t count = packet.GetU32 (&offset); + + for (uint32_t i=0; i<count; ++i) + { + const uint32_t cpu = packet.GetU32 (&offset); + const uint32_t exc = packet.GetU32 (&offset); + const uint32_t code = packet.GetU32 (&offset); + const uint32_t subcode = packet.GetU32 (&offset); + const char *exc_cstr = NULL; + switch (exc) + { + case 1: exc_cstr = "EXC_BAD_ACCESS"; break; + case 2: exc_cstr = "EXC_BAD_INSTRUCTION"; break; + case 3: exc_cstr = "EXC_ARITHMETIC"; break; + case 4: exc_cstr = "EXC_EMULATION"; break; + case 5: exc_cstr = "EXC_SOFTWARE"; break; + case 6: exc_cstr = "EXC_BREAKPOINT"; break; + case 7: exc_cstr = "EXC_SYSCALL"; break; + case 8: exc_cstr = "EXC_MACH_SYSCALL"; break; + case 9: exc_cstr = "EXC_RPC_ALERT"; break; + case 10: exc_cstr = "EXC_CRASH"; break; + default: + break; + } + + s.Printf ("{ cpu = 0x%8.8x, exc = %s (%u), code = %u (0x%8.8x), subcode = %u (0x%8.8x)} ", + cpu, exc_cstr, exc, code, code, subcode, subcode); + } + } + break; + + case KDP_TERMINATION: + { + const uint32_t term_code = packet.GetU32 (&offset); + const uint32_t exit_code = packet.GetU32 (&offset); + s.Printf(" (term_code = 0x%8.8x (%u), exit_code = 0x%8.8x (%u))", term_code, term_code, exit_code, exit_code); + } + break; + + case KDP_REATTACH: + { + const uint16_t reply_port = ntohs(packet.GetU16 (&offset)); + s.Printf(" (reply_port = %u)", reply_port); + } + break; + + case KDP_READMSR64: + { + const uint32_t address = packet.GetU32 (&offset); + const uint16_t lcpu = packet.GetU16 (&offset); + s.Printf(" (address=0x%8.8x, lcpu=0x%4.4x)", address, lcpu); + } + break; + + case KDP_WRITEMSR64: + { + const uint32_t address = packet.GetU32 (&offset); + const uint16_t lcpu = packet.GetU16 (&offset); + const uint32_t nbytes = packet.GetByteSize() - offset; + s.Printf(" (address=0x%8.8x, lcpu=0x%4.4x, nbytes=0x%8.8x)", lcpu, address, nbytes); + if (nbytes > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + nbytes, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_READIOPORT: + { + const uint16_t lcpu = packet.GetU16 (&offset); + const uint16_t address = packet.GetU16 (&offset); + const uint16_t nbytes = packet.GetU16 (&offset); + s.Printf(" (lcpu=0x%4.4x, address=0x%4.4x, nbytes=%u)", lcpu, address, nbytes); + } + break; + + case KDP_WRITEIOPORT: + { + const uint16_t lcpu = packet.GetU16 (&offset); + const uint16_t address = packet.GetU16 (&offset); + const uint16_t nbytes = packet.GetU16 (&offset); + s.Printf(" (lcpu = %u, addr = 0x%4.4x, nbytes = %u, bytes = \n", lcpu, address, nbytes); + if (nbytes > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + nbytes, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_DUMPINFO: + { + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (count = %u, bytes = \n", count); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + + } + break; + + } + } + } + else + { + error_desc = "error: invalid packet command: "; + } + } + + if (error_desc) + { + s.PutCString (error_desc); + + packet.Dump (&s, // Stream to dump to + 0, // Offset into "packet" + eFormatBytes, // Dump as hex bytes + 1, // Size of each item is 1 for single bytes + packet.GetByteSize(), // Number of bytes + UINT32_MAX, // Num bytes per line + LLDB_INVALID_ADDRESS, // Base address + 0, 0); // Bitfield info set to not do anything bitfield related + } +} + +uint32_t +CommunicationKDP::SendRequestReadRegisters (uint32_t cpu, + uint32_t flavor, + void *dst, + uint32_t dst_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_READREGS; + // Size is header + 4 byte cpu and 4 byte flavor + const uint32_t command_length = 8 + 4 + 4; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutHex32 (cpu); + request_packet.PutHex32 (flavor); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + uint32_t src_len = reply_packet.GetByteSize() - 12; + + if (src_len > 0) + { + const uint32_t bytes_to_copy = std::min<uint32_t>(src_len, dst_len); + const void *src = reply_packet.GetData(&offset, bytes_to_copy); + if (src) + { + ::memcpy (dst, src, bytes_to_copy); + error.Clear(); + // Return the number of bytes we could have returned regardless if + // we copied them or not, just so we know when things don't match up + return src_len; + } + } + if (kdp_error) + error.SetErrorStringWithFormat("failed to read kdp registers for cpu %u flavor %u (error %u)", cpu, flavor, kdp_error); + else + error.SetErrorStringWithFormat("failed to read kdp registers for cpu %u flavor %u", cpu, flavor); + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + +uint32_t +CommunicationKDP::SendRequestWriteRegisters (uint32_t cpu, + uint32_t flavor, + const void *src, + uint32_t src_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_WRITEREGS; + // Size is header + 4 byte cpu and 4 byte flavor + const uint32_t command_length = 8 + 4 + 4 + src_len; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutHex32 (cpu); + request_packet.PutHex32 (flavor); + request_packet.Write(src, src_len); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error == 0) + return src_len; + error.SetErrorStringWithFormat("failed to read kdp registers for cpu %u flavor %u (error %u)", cpu, flavor, kdp_error); + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + + +bool +CommunicationKDP::SendRequestResume () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_RESUMECPUS; + const uint32_t command_length = 12; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutHex32(GetCPUMask()); + + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + return true; + return false; +} + +bool +CommunicationKDP::SendRequestBreakpoint (bool set, addr_t addr) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + bool use_64 = (GetVersion() >= 11); + uint32_t command_addr_byte_size = use_64 ? 8 : 4; + const CommandType command = set ? (use_64 ? KDP_BREAKPOINT_SET64 : KDP_BREAKPOINT_SET ): + (use_64 ? KDP_BREAKPOINT_REMOVE64 : KDP_BREAKPOINT_REMOVE); + + const uint32_t command_length = 8 + command_addr_byte_size; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutMaxHex64 (addr, command_addr_byte_size); + + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error == 0) + return true; + } + return false; +} + +bool +CommunicationKDP::SendRequestSuspend () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_SUSPEND; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + return true; + return false; +} + diff --git a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h new file mode 100644 index 000000000000..98a146d5a06d --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h @@ -0,0 +1,347 @@ +//===-- CommunicationKDP.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommunicationKDP_h_ +#define liblldb_CommunicationKDP_h_ + +// C Includes +// C++ Includes +#include <list> +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Listener.h" +#include "lldb/Core/StreamBuffer.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/TimeValue.h" + +class CommunicationKDP : public lldb_private::Communication +{ +public: + enum + { + eBroadcastBitRunPacketSent = kLoUserBroadcastBit + }; + + const static uint32_t kMaxPacketSize = 1200; + const static uint32_t kMaxDataSize = 1024; + typedef lldb_private::StreamBuffer<1024> PacketStreamType; + typedef enum + { + KDP_CONNECT = 0u, + KDP_DISCONNECT, + KDP_HOSTINFO, + KDP_VERSION, + KDP_MAXBYTES, + KDP_READMEM, + KDP_WRITEMEM, + KDP_READREGS, + KDP_WRITEREGS, + KDP_LOAD, + KDP_IMAGEPATH, + KDP_SUSPEND, + KDP_RESUMECPUS, + KDP_EXCEPTION, + KDP_TERMINATION, + KDP_BREAKPOINT_SET, + KDP_BREAKPOINT_REMOVE, + KDP_REGIONS, + KDP_REATTACH, + KDP_HOSTREBOOT, + KDP_READMEM64, + KDP_WRITEMEM64, + KDP_BREAKPOINT_SET64, + KDP_BREAKPOINT_REMOVE64, + KDP_KERNELVERSION, + KDP_READPHYSMEM64, + KDP_WRITEPHYSMEM64, + KDP_READIOPORT, + KDP_WRITEIOPORT, + KDP_READMSR64, + KDP_WRITEMSR64, + KDP_DUMPINFO + } CommandType; + + enum + { + KDP_FEATURE_BP = (1u << 0) + }; + + typedef enum + { + KDP_PROTERR_SUCCESS = 0, + KDP_PROTERR_ALREADY_CONNECTED, + KDP_PROTERR_BAD_NBYTES, + KDP_PROTERR_BADFLAVOR + } KDPError; + + typedef enum + { + ePacketTypeRequest = 0x00u, + ePacketTypeReply = 0x80u, + ePacketTypeMask = 0x80u, + eCommandTypeMask = 0x7fu + } PacketType; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommunicationKDP (const char *comm_name); + + virtual + ~CommunicationKDP(); + + bool + SendRequestPacket (const PacketStreamType &request_packet); + + // Wait for a packet within 'nsec' seconds + size_t + WaitForPacketWithTimeoutMicroSeconds (lldb_private::DataExtractor &response, + uint32_t usec); + + bool + GetSequenceMutex(lldb_private::Mutex::Locker& locker); + + bool + CheckForPacket (const uint8_t *src, + size_t src_len, + lldb_private::DataExtractor &packet); + bool + IsRunning() const + { + return m_is_running.GetValue(); + } + + //------------------------------------------------------------------ + // Set the global packet timeout. + // + // For clients, this is the timeout that gets used when sending + // packets and waiting for responses. For servers, this might not + // get used, and if it doesn't this should be moved to the + // CommunicationKDPClient. + //------------------------------------------------------------------ + uint32_t + SetPacketTimeout (uint32_t packet_timeout) + { + const uint32_t old_packet_timeout = m_packet_timeout; + m_packet_timeout = packet_timeout; + return old_packet_timeout; + } + + uint32_t + GetPacketTimeoutInMicroSeconds () const + { + return m_packet_timeout * lldb_private::TimeValue::MicroSecPerSec; + } + + //------------------------------------------------------------------ + // Public Request Packets + //------------------------------------------------------------------ + bool + SendRequestConnect (uint16_t reply_port, + uint16_t exc_port, + const char *greeting); + + bool + SendRequestReattach (uint16_t reply_port); + + bool + SendRequestDisconnect (); + + uint32_t + SendRequestReadMemory (lldb::addr_t addr, + void *dst, + uint32_t dst_size, + lldb_private::Error &error); + + uint32_t + SendRequestWriteMemory (lldb::addr_t addr, + const void *src, + uint32_t src_len, + lldb_private::Error &error); + + bool + SendRawRequest (uint8_t command_byte, + const void *src, + uint32_t src_len, + lldb_private::DataExtractor &reply, + lldb_private::Error &error); + + uint32_t + SendRequestReadRegisters (uint32_t cpu, + uint32_t flavor, + void *dst, + uint32_t dst_size, + lldb_private::Error &error); + + uint32_t + SendRequestWriteRegisters (uint32_t cpu, + uint32_t flavor, + const void *src, + uint32_t src_size, + lldb_private::Error &error); + + const char * + GetKernelVersion (); + + // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... + // const char * + // GetImagePath (); + + uint32_t + GetVersion (); + + uint32_t + GetFeatureFlags (); + + bool + LocalBreakpointsAreSupported () + { + return (GetFeatureFlags() & KDP_FEATURE_BP) != 0; + } + + uint32_t + GetCPUMask (); + + uint32_t + GetCPUType (); + + uint32_t + GetCPUSubtype (); + + lldb_private::UUID + GetUUID (); + + bool + RemoteIsEFI (); + + bool + RemoteIsDarwinKernel (); + + lldb::addr_t + GetLoadAddress (); + + bool + SendRequestResume (); + + bool + SendRequestSuspend (); + + bool + SendRequestBreakpoint (bool set, lldb::addr_t addr); + +protected: + + bool + SendRequestPacketNoLock (const PacketStreamType &request_packet); + + size_t + WaitForPacketWithTimeoutMicroSecondsNoLock (lldb_private::DataExtractor &response, + uint32_t timeout_usec); + + bool + WaitForNotRunningPrivate (const lldb_private::TimeValue *timeout_ptr); + + void + MakeRequestPacketHeader (CommandType request_type, + PacketStreamType &request_packet, + uint16_t request_length); + + //------------------------------------------------------------------ + // Protected Request Packets (use public accessors which will cache + // results. + //------------------------------------------------------------------ + bool + SendRequestVersion (); + + bool + SendRequestHostInfo (); + + bool + SendRequestKernelVersion (); + + // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... + //bool + //SendRequestImagePath (); + + void + DumpPacket (lldb_private::Stream &s, + const void *data, + uint32_t data_len); + + void + DumpPacket (lldb_private::Stream &s, + const lldb_private::DataExtractor& extractor); + + bool + VersionIsValid() const + { + return m_kdp_version_version != 0; + } + + bool + HostInfoIsValid() const + { + return m_kdp_hostinfo_cpu_type != 0; + } + + bool + ExtractIsReply (uint8_t first_packet_byte) const + { + // TODO: handle big endian... + return (first_packet_byte & ePacketTypeMask) != 0; + } + + CommandType + ExtractCommand (uint8_t first_packet_byte) const + { + // TODO: handle big endian... + return (CommandType)(first_packet_byte & eCommandTypeMask); + } + + static const char * + GetCommandAsCString (uint8_t command); + + void + ClearKDPSettings (); + + bool + SendRequestAndGetReply (const CommandType command, + const PacketStreamType &request_packet, + lldb_private::DataExtractor &reply_packet); + //------------------------------------------------------------------ + // Classes that inherit from CommunicationKDP can see and modify these + //------------------------------------------------------------------ + uint32_t m_addr_byte_size; + lldb::ByteOrder m_byte_order; + uint32_t m_packet_timeout; + lldb_private::Mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time + lldb_private::Predicate<bool> m_is_running; + uint32_t m_session_key; + uint8_t m_request_sequence_id; + uint8_t m_exception_sequence_id; + uint32_t m_kdp_version_version; + uint32_t m_kdp_version_feature; + uint32_t m_kdp_hostinfo_cpu_mask; + uint32_t m_kdp_hostinfo_cpu_type; + uint32_t m_kdp_hostinfo_cpu_subtype; + std::string m_kernel_version; + //std::string m_image_path; // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... + lldb::addr_t m_last_read_memory_addr; // Last memory read address for logging +private: + //------------------------------------------------------------------ + // For CommunicationKDP only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommunicationKDP); +}; + +#endif // liblldb_CommunicationKDP_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/Makefile b/source/Plugins/Process/MacOSX-Kernel/Makefile new file mode 100644 index 000000000000..e42f390ffe0d --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Process/MacOSX-Darwin/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 := lldbPluginProcessDarwin +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp new file mode 100644 index 000000000000..628f76d104fe --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp @@ -0,0 +1,1211 @@ +//===-- ProcessKDP.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <errno.h> +#include <stdlib.h> + +// C++ Includes +#include <mutex> + +// Other libraries and framework includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/State.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionGroupString.h" +#include "lldb/Interpreter/OptionGroupUInt64.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/StringExtractor.h" + +#define USEC_PER_SEC 1000000 + +// Project includes +#include "ProcessKDP.h" +#include "ProcessKDPLog.h" +#include "ThreadKDP.h" +#include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" +#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { + + static PropertyDefinition + g_properties[] = + { + { "packet-timeout" , OptionValue::eTypeUInt64 , true , 5, NULL, NULL, "Specify the default packet timeout in seconds." }, + { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } + }; + + enum + { + ePropertyPacketTimeout + }; + + class PluginProperties : public Properties + { + public: + + static ConstString + GetSettingName () + { + return ProcessKDP::GetPluginNameStatic(); + } + + PluginProperties() : + Properties () + { + m_collection_sp.reset (new OptionValueProperties(GetSettingName())); + m_collection_sp->Initialize(g_properties); + } + + virtual + ~PluginProperties() + { + } + + uint64_t + GetPacketTimeout() + { + const uint32_t idx = ePropertyPacketTimeout; + return m_collection_sp->GetPropertyAtIndexAsUInt64(NULL, idx, g_properties[idx].default_uint_value); + } + }; + + typedef std::shared_ptr<PluginProperties> ProcessKDPPropertiesSP; + + static const ProcessKDPPropertiesSP & + GetGlobalPluginProperties() + { + static ProcessKDPPropertiesSP g_settings_sp; + if (!g_settings_sp) + g_settings_sp.reset (new PluginProperties ()); + return g_settings_sp; + } + +} // anonymous namespace end + +static const lldb::tid_t g_kernel_tid = 1; + +ConstString +ProcessKDP::GetPluginNameStatic() +{ + static ConstString g_name("kdp-remote"); + return g_name; +} + +const char * +ProcessKDP::GetPluginDescriptionStatic() +{ + return "KDP Remote protocol based debugging plug-in for darwin kernel debugging."; +} + +void +ProcessKDP::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessKDP::CreateInstance); +} + + +lldb::ProcessSP +ProcessKDP::CreateInstance (TargetSP target_sp, + Listener &listener, + const FileSpec *crash_file_path) +{ + lldb::ProcessSP process_sp; + if (crash_file_path == NULL) + process_sp.reset(new ProcessKDP (target_sp, listener)); + return process_sp; +} + +bool +ProcessKDP::CanDebug(TargetSP target_sp, bool plugin_specified_by_name) +{ + if (plugin_specified_by_name) + return true; + + // For now we are just making sure the file exists for a given module + Module *exe_module = target_sp->GetExecutableModulePointer(); + if (exe_module) + { + const llvm::Triple &triple_ref = target_sp->GetArchitecture().GetTriple(); + switch (triple_ref.getOS()) + { + case llvm::Triple::Darwin: // Should use "macosx" for desktop and "ios" for iOS, but accept darwin just in case + case llvm::Triple::MacOSX: // For desktop targets + case llvm::Triple::IOS: // For arm targets + case llvm::Triple::TvOS: + case llvm::Triple::WatchOS: + if (triple_ref.getVendor() == llvm::Triple::Apple) + { + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + if (exe_objfile->GetType() == ObjectFile::eTypeExecutable && + exe_objfile->GetStrata() == ObjectFile::eStrataKernel) + return true; + } + break; + + default: + break; + } + } + return false; +} + +//---------------------------------------------------------------------- +// ProcessKDP constructor +//---------------------------------------------------------------------- +ProcessKDP::ProcessKDP(TargetSP target_sp, Listener &listener) : + Process (target_sp, listener), + m_comm("lldb.process.kdp-remote.communication"), + m_async_broadcaster (NULL, "lldb.process.kdp-remote.async-broadcaster"), + m_dyld_plugin_name (), + m_kernel_load_addr (LLDB_INVALID_ADDRESS), + m_command_sp(), + m_kernel_thread_wp() +{ + m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); + m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue"); + const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); + if (timeout_seconds > 0) + m_comm.SetPacketTimeout(timeout_seconds); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessKDP::~ProcessKDP() +{ + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +lldb_private::ConstString +ProcessKDP::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessKDP::GetPluginVersion() +{ + return 1; +} + +Error +ProcessKDP::WillLaunch (Module* module) +{ + Error error; + error.SetErrorString ("launching not supported in kdp-remote plug-in"); + return error; +} + +Error +ProcessKDP::WillAttachToProcessWithID (lldb::pid_t pid) +{ + Error error; + error.SetErrorString ("attaching to a by process ID not supported in kdp-remote plug-in"); + return error; +} + +Error +ProcessKDP::WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) +{ + Error error; + error.SetErrorString ("attaching to a by process name not supported in kdp-remote plug-in"); + return error; +} + +bool +ProcessKDP::GetHostArchitecture(ArchSpec &arch) +{ + uint32_t cpu = m_comm.GetCPUType(); + if (cpu) + { + uint32_t sub = m_comm.GetCPUSubtype(); + arch.SetArchitecture(eArchTypeMachO, cpu, sub); + // Leave architecture vendor as unspecified unknown + arch.GetTriple().setVendor(llvm::Triple::UnknownVendor); + arch.GetTriple().setVendorName(llvm::StringRef()); + return true; + } + arch.Clear(); + return false; +} + +Error +ProcessKDP::DoConnectRemote (Stream *strm, const char *remote_url) +{ + Error error; + + // Don't let any JIT happen when doing KDP as we can't allocate + // memory and we don't want to be mucking with threads that might + // already be handling exceptions + SetCanJIT(false); + + if (remote_url == NULL || remote_url[0] == '\0') + { + error.SetErrorStringWithFormat ("invalid connection URL '%s'", remote_url); + return error; + } + + std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor()); + if (conn_ap.get()) + { + // Only try once for now. + // TODO: check if we should be retrying? + const uint32_t max_retry_count = 1; + for (uint32_t retry_count = 0; retry_count < max_retry_count; ++retry_count) + { + if (conn_ap->Connect(remote_url, &error) == eConnectionStatusSuccess) + break; + usleep (100000); + } + } + + if (conn_ap->IsConnected()) + { + const TCPSocket& socket = static_cast<const TCPSocket&>(*conn_ap->GetReadObject()); + const uint16_t reply_port = socket.GetLocalPortNumber(); + + if (reply_port != 0) + { + m_comm.SetConnection(conn_ap.release()); + + if (m_comm.SendRequestReattach(reply_port)) + { + if (m_comm.SendRequestConnect(reply_port, reply_port, "Greetings from LLDB...")) + { + m_comm.GetVersion(); + + Target &target = GetTarget(); + ArchSpec kernel_arch; + // The host architecture + GetHostArchitecture(kernel_arch); + ArchSpec target_arch = target.GetArchitecture(); + // Merge in any unspecified stuff into the target architecture in + // case the target arch isn't set at all or incompletely. + target_arch.MergeFrom(kernel_arch); + target.SetArchitecture(target_arch); + + /* Get the kernel's UUID and load address via KDP_KERNELVERSION packet. */ + /* An EFI kdp session has neither UUID nor load address. */ + + UUID kernel_uuid = m_comm.GetUUID (); + addr_t kernel_load_addr = m_comm.GetLoadAddress (); + + if (m_comm.RemoteIsEFI ()) + { + // Select an invalid plugin name for the dynamic loader so one doesn't get used + // since EFI does its own manual loading via python scripting + static ConstString g_none_dynamic_loader("none"); + m_dyld_plugin_name = g_none_dynamic_loader; + + if (kernel_uuid.IsValid()) { + // If EFI passed in a UUID= try to lookup UUID + // The slide will not be provided. But the UUID + // lookup will be used to launch EFI debug scripts + // from the dSYM, that can load all of the symbols. + ModuleSpec module_spec; + module_spec.GetUUID() = kernel_uuid; + module_spec.GetArchitecture() = target.GetArchitecture(); + + // Lookup UUID locally, before attempting dsymForUUID like action + module_spec.GetSymbolFileSpec() = Symbols::LocateExecutableSymbolFile(module_spec); + if (module_spec.GetSymbolFileSpec()) + { + ModuleSpec executable_module_spec = Symbols::LocateExecutableObjectFile (module_spec); + if (executable_module_spec.GetFileSpec().Exists()) + { + module_spec.GetFileSpec() = executable_module_spec.GetFileSpec(); + } + } + if (!module_spec.GetSymbolFileSpec() || !module_spec.GetSymbolFileSpec()) + Symbols::DownloadObjectAndSymbolFile (module_spec, true); + + if (module_spec.GetFileSpec().Exists()) + { + ModuleSP module_sp(new Module (module_spec)); + if (module_sp.get() && module_sp->GetObjectFile()) + { + // Get the current target executable + ModuleSP exe_module_sp (target.GetExecutableModule ()); + + // Make sure you don't already have the right module loaded and they will be uniqued + if (exe_module_sp.get() != module_sp.get()) + target.SetExecutableModule (module_sp, false); + } + } + } + } + else if (m_comm.RemoteIsDarwinKernel ()) + { + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + if (kernel_load_addr != LLDB_INVALID_ADDRESS) + { + m_kernel_load_addr = kernel_load_addr; + } + } + + // Set the thread ID + UpdateThreadListIfNeeded (); + SetID (1); + GetThreadList (); + SetPrivateState (eStateStopped); + StreamSP async_strm_sp(target.GetDebugger().GetAsyncOutputStream()); + if (async_strm_sp) + { + const char *cstr; + if ((cstr = m_comm.GetKernelVersion ()) != NULL) + { + async_strm_sp->Printf ("Version: %s\n", cstr); + async_strm_sp->Flush(); + } +// if ((cstr = m_comm.GetImagePath ()) != NULL) +// { +// async_strm_sp->Printf ("Image Path: %s\n", cstr); +// async_strm_sp->Flush(); +// } + } + } + else + { + error.SetErrorString("KDP_REATTACH failed"); + } + } + else + { + error.SetErrorString("KDP_REATTACH failed"); + } + } + else + { + error.SetErrorString("invalid reply port from UDP connection"); + } + } + else + { + if (error.Success()) + error.SetErrorStringWithFormat ("failed to connect to '%s'", remote_url); + } + if (error.Fail()) + m_comm.Disconnect(); + + return error; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessKDP::DoLaunch (Module *exe_module, + ProcessLaunchInfo &launch_info) +{ + Error error; + error.SetErrorString ("launching not supported in kdp-remote plug-in"); + return error; +} + +Error +ProcessKDP::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) +{ + Error error; + error.SetErrorString ("attach to process by ID is not suppported in kdp remote debugging"); + return error; +} + +Error +ProcessKDP::DoAttachToProcessWithName (const char *process_name, const ProcessAttachInfo &attach_info) +{ + Error error; + error.SetErrorString ("attach to process by name is not suppported in kdp remote debugging"); + return error; +} + + +void +ProcessKDP::DidAttach (ArchSpec &process_arch) +{ + Process::DidAttach(process_arch); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::DidAttach()"); + if (GetID() != LLDB_INVALID_PROCESS_ID) + { + GetHostArchitecture(process_arch); + } +} + +addr_t +ProcessKDP::GetImageInfoAddress() +{ + return m_kernel_load_addr; +} + +lldb_private::DynamicLoader * +ProcessKDP::GetDynamicLoader () +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset (DynamicLoader::FindPlugin(this, m_dyld_plugin_name.IsEmpty() ? NULL : m_dyld_plugin_name.GetCString())); + return m_dyld_ap.get(); +} + +Error +ProcessKDP::WillResume () +{ + return Error(); +} + +Error +ProcessKDP::DoResume () +{ + Error error; + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS)); + // Only start the async thread if we try to do any process control + if (!m_async_thread.IsJoinable()) + StartAsyncThread(); + + bool resume = false; + + // With KDP there is only one thread we can tell what to do + ThreadSP kernel_thread_sp (m_thread_list.FindThreadByProtocolID(g_kernel_tid)); + + if (kernel_thread_sp) + { + const StateType thread_resume_state = kernel_thread_sp->GetTemporaryResumeState(); + + if (log) + log->Printf ("ProcessKDP::DoResume() thread_resume_state = %s", StateAsCString(thread_resume_state)); + switch (thread_resume_state) + { + case eStateSuspended: + // Nothing to do here when a thread will stay suspended + // we just leave the CPU mask bit set to zero for the thread + if (log) + log->Printf ("ProcessKDP::DoResume() = suspended???"); + break; + + case eStateStepping: + { + lldb::RegisterContextSP reg_ctx_sp (kernel_thread_sp->GetRegisterContext()); + + if (reg_ctx_sp) + { + if (log) + log->Printf ("ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (true);"); + reg_ctx_sp->HardwareSingleStep (true); + resume = true; + } + else + { + error.SetErrorStringWithFormat("KDP thread 0x%llx has no register context", kernel_thread_sp->GetID()); + } + } + break; + + case eStateRunning: + { + lldb::RegisterContextSP reg_ctx_sp (kernel_thread_sp->GetRegisterContext()); + + if (reg_ctx_sp) + { + if (log) + log->Printf ("ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (false);"); + reg_ctx_sp->HardwareSingleStep (false); + resume = true; + } + else + { + error.SetErrorStringWithFormat("KDP thread 0x%llx has no register context", kernel_thread_sp->GetID()); + } + } + break; + + default: + // The only valid thread resume states are listed above + assert (!"invalid thread resume state"); + break; + } + } + + if (resume) + { + if (log) + log->Printf ("ProcessKDP::DoResume () sending resume"); + + if (m_comm.SendRequestResume ()) + { + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue); + SetPrivateState(eStateRunning); + } + else + error.SetErrorString ("KDP resume failed"); + } + else + { + error.SetErrorString ("kernel thread is suspended"); + } + + return error; +} + +lldb::ThreadSP +ProcessKDP::GetKernelThread() +{ + // KDP only tells us about one thread/core. Any other threads will usually + // be the ones that are read from memory by the OS plug-ins. + + ThreadSP thread_sp (m_kernel_thread_wp.lock()); + if (!thread_sp) + { + thread_sp.reset(new ThreadKDP (*this, g_kernel_tid)); + m_kernel_thread_wp = thread_sp; + } + return thread_sp; +} + + + + +bool +ProcessKDP::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + // locker will keep a mutex locked until it goes out of scope + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_THREAD)); + if (log && log->GetMask().Test(KDP_LOG_VERBOSE)) + log->Printf ("ProcessKDP::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID()); + + // Even though there is a CPU mask, it doesn't mean we can see each CPU + // individually, there is really only one. Lets call this thread 1. + ThreadSP thread_sp (old_thread_list.FindThreadByProtocolID(g_kernel_tid, false)); + if (!thread_sp) + thread_sp = GetKernelThread (); + new_thread_list.AddThread(thread_sp); + + return new_thread_list.GetSize(false) > 0; +} + +void +ProcessKDP::RefreshStateAfterStop () +{ + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + m_thread_list.RefreshStateAfterStop(); +} + +Error +ProcessKDP::DoHalt (bool &caused_stop) +{ + Error error; + + if (m_comm.IsRunning()) + { + if (m_destroy_in_process) + { + // If we are attemping to destroy, we need to not return an error to + // Halt or DoDestroy won't get called. + // We are also currently running, so send a process stopped event + SetPrivateState (eStateStopped); + } + else + { + error.SetErrorString ("KDP cannot interrupt a running kernel"); + } + } + return error; +} + +Error +ProcessKDP::DoDetach(bool keep_stopped) +{ + Error error; + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::DoDetach(keep_stopped = %i)", keep_stopped); + + if (m_comm.IsRunning()) + { + // We are running and we can't interrupt a running kernel, so we need + // to just close the connection to the kernel and hope for the best + } + else + { + // If we are going to keep the target stopped, then don't send the disconnect message. + if (!keep_stopped && m_comm.IsConnected()) + { + const bool success = m_comm.SendRequestDisconnect(); + if (log) + { + if (success) + log->PutCString ("ProcessKDP::DoDetach() detach packet sent successfully"); + else + log->PutCString ("ProcessKDP::DoDetach() connection channel shutdown failed"); + } + m_comm.Disconnect (); + } + } + StopAsyncThread (); + m_comm.Clear(); + + SetPrivateState (eStateDetached); + ResumePrivateStateThread(); + + //KillDebugserverProcess (); + return error; +} + +Error +ProcessKDP::DoDestroy () +{ + // For KDP there really is no difference between destroy and detach + bool keep_stopped = false; + return DoDetach(keep_stopped); +} + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessKDP::IsAlive () +{ + return m_comm.IsConnected() && Process::IsAlive(); +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ +size_t +ProcessKDP::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + uint8_t *data_buffer = (uint8_t *) buf; + if (m_comm.IsConnected()) + { + const size_t max_read_size = 512; + size_t total_bytes_read = 0; + + // Read the requested amount of memory in 512 byte chunks + while (total_bytes_read < size) + { + size_t bytes_to_read_this_request = size - total_bytes_read; + if (bytes_to_read_this_request > max_read_size) + { + bytes_to_read_this_request = max_read_size; + } + size_t bytes_read = m_comm.SendRequestReadMemory (addr + total_bytes_read, + data_buffer + total_bytes_read, + bytes_to_read_this_request, error); + total_bytes_read += bytes_read; + if (error.Fail() || bytes_read == 0) + { + return total_bytes_read; + } + } + + return total_bytes_read; + } + error.SetErrorString ("not connected"); + return 0; +} + +size_t +ProcessKDP::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error) +{ + if (m_comm.IsConnected()) + return m_comm.SendRequestWriteMemory (addr, buf, size, error); + error.SetErrorString ("not connected"); + return 0; +} + +lldb::addr_t +ProcessKDP::DoAllocateMemory (size_t size, uint32_t permissions, Error &error) +{ + error.SetErrorString ("memory allocation not suppported in kdp remote debugging"); + return LLDB_INVALID_ADDRESS; +} + +Error +ProcessKDP::DoDeallocateMemory (lldb::addr_t addr) +{ + Error error; + error.SetErrorString ("memory deallocation not suppported in kdp remote debugging"); + return error; +} + +Error +ProcessKDP::EnableBreakpointSite (BreakpointSite *bp_site) +{ + if (m_comm.LocalBreakpointsAreSupported ()) + { + Error error; + if (!bp_site->IsEnabled()) + { + if (m_comm.SendRequestBreakpoint(true, bp_site->GetLoadAddress())) + { + bp_site->SetEnabled(true); + bp_site->SetType (BreakpointSite::eExternal); + } + else + { + error.SetErrorString ("KDP set breakpoint failed"); + } + } + return error; + } + return EnableSoftwareBreakpoint (bp_site); +} + +Error +ProcessKDP::DisableBreakpointSite (BreakpointSite *bp_site) +{ + if (m_comm.LocalBreakpointsAreSupported ()) + { + Error error; + if (bp_site->IsEnabled()) + { + BreakpointSite::Type bp_type = bp_site->GetType(); + if (bp_type == BreakpointSite::eExternal) + { + if (m_destroy_in_process && m_comm.IsRunning()) + { + // We are trying to destroy our connection and we are running + bp_site->SetEnabled(false); + } + else + { + if (m_comm.SendRequestBreakpoint(false, bp_site->GetLoadAddress())) + bp_site->SetEnabled(false); + else + error.SetErrorString ("KDP remove breakpoint failed"); + } + } + else + { + error = DisableSoftwareBreakpoint (bp_site); + } + } + return error; + } + return DisableSoftwareBreakpoint (bp_site); +} + +Error +ProcessKDP::EnableWatchpoint (Watchpoint *wp, bool notify) +{ + Error error; + error.SetErrorString ("watchpoints are not suppported in kdp remote debugging"); + return error; +} + +Error +ProcessKDP::DisableWatchpoint (Watchpoint *wp, bool notify) +{ + Error error; + error.SetErrorString ("watchpoints are not suppported in kdp remote debugging"); + return error; +} + +void +ProcessKDP::Clear() +{ + m_thread_list.Clear(); +} + +Error +ProcessKDP::DoSignal (int signo) +{ + Error error; + error.SetErrorString ("sending signals is not suppported in kdp remote debugging"); + return error; +} + +void +ProcessKDP::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() + { + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + DebuggerInitialize); + + Log::Callbacks log_callbacks = { + ProcessKDPLog::DisableLog, + ProcessKDPLog::EnableLog, + ProcessKDPLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessKDP::GetPluginNameStatic(), log_callbacks); + }); +} + +void +ProcessKDP::DebuggerInitialize (lldb_private::Debugger &debugger) +{ + if (!PluginManager::GetSettingForProcessPlugin(debugger, PluginProperties::GetSettingName())) + { + const bool is_global_setting = true; + PluginManager::CreateSettingForProcessPlugin (debugger, + GetGlobalPluginProperties()->GetValueProperties(), + ConstString ("Properties for the kdp-remote process plug-in."), + is_global_setting); + } +} + +bool +ProcessKDP::StartAsyncThread () +{ + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessKDP::StartAsyncThread ()"); + + if (m_async_thread.IsJoinable()) + return true; + + m_async_thread = ThreadLauncher::LaunchThread("<lldb.process.kdp-remote.async>", ProcessKDP::AsyncThread, this, NULL); + return m_async_thread.IsJoinable(); +} + +void +ProcessKDP::StopAsyncThread () +{ + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessKDP::StopAsyncThread ()"); + + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); + + // Stop the stdio thread + if (m_async_thread.IsJoinable()) + m_async_thread.Join(nullptr); +} + + +void * +ProcessKDP::AsyncThread (void *arg) +{ + ProcessKDP *process = (ProcessKDP*) arg; + + const lldb::pid_t pid = process->GetID(); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::AsyncThread (arg = %p, pid = %" PRIu64 ") thread starting...", arg, pid); + + Listener listener ("ProcessKDP::AsyncThread"); + EventSP event_sp; + const uint32_t desired_event_mask = eBroadcastBitAsyncContinue | + eBroadcastBitAsyncThreadShouldExit; + + + if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) + { + bool done = false; + while (!done) + { + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", + pid); + if (listener.WaitForEvent (NULL, event_sp)) + { + uint32_t event_type = event_sp->GetType(); + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") Got an event of type: %d...", + pid, + event_type); + + // When we are running, poll for 1 second to try and get an exception + // to indicate the process has stopped. If we don't get one, check to + // make sure no one asked us to exit + bool is_running = false; + DataExtractor exc_reply_packet; + do + { + switch (event_type) + { + case eBroadcastBitAsyncContinue: + { + is_running = true; + if (process->m_comm.WaitForPacketWithTimeoutMicroSeconds (exc_reply_packet, 1 * USEC_PER_SEC)) + { + ThreadSP thread_sp (process->GetKernelThread()); + if (thread_sp) + { + lldb::RegisterContextSP reg_ctx_sp (thread_sp->GetRegisterContext()); + if (reg_ctx_sp) + reg_ctx_sp->InvalidateAllRegisters(); + static_cast<ThreadKDP *>(thread_sp.get())->SetStopInfoFrom_KDP_EXCEPTION (exc_reply_packet); + } + + // TODO: parse the stop reply packet + is_running = false; + process->SetPrivateState(eStateStopped); + } + else + { + // Check to see if we are supposed to exit. There is no way to + // interrupt a running kernel, so all we can do is wait for an + // exception or detach... + if (listener.GetNextEvent(event_sp)) + { + // We got an event, go through the loop again + event_type = event_sp->GetType(); + } + } + } + break; + + case eBroadcastBitAsyncThreadShouldExit: + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", + pid); + done = true; + is_running = false; + break; + + default: + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") got unknown event 0x%8.8x", + pid, + event_type); + done = true; + is_running = false; + break; + } + } while (is_running); + } + else + { + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", + pid); + done = true; + } + } + } + + if (log) + log->Printf ("ProcessKDP::AsyncThread (arg = %p, pid = %" PRIu64 ") thread exiting...", + arg, + pid); + + process->m_async_thread.Reset(); + return NULL; +} + + +class CommandObjectProcessKDPPacketSend : public CommandObjectParsed +{ +private: + + OptionGroupOptions m_option_group; + OptionGroupUInt64 m_command_byte; + OptionGroupString m_packet_data; + + virtual Options * + GetOptions () + { + return &m_option_group; + } + + +public: + CommandObjectProcessKDPPacketSend(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process plugin packet send", + "Send a custom packet through the KDP protocol by specifying the command byte and the packet payload data. A packet will be sent with a correct header and payload, and the raw result bytes will be displayed as a string value. ", + NULL), + m_option_group (interpreter), + m_command_byte(LLDB_OPT_SET_1, true , "command", 'c', 0, eArgTypeNone, "Specify the command byte to use when sending the KDP request packet.", 0), + m_packet_data (LLDB_OPT_SET_1, false, "payload", 'p', 0, eArgTypeNone, "Specify packet payload bytes as a hex ASCII string with no spaces or hex prefixes.", NULL) + { + m_option_group.Append (&m_command_byte, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_packet_data , LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + ~CommandObjectProcessKDPPacketSend () + { + } + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) + { + if (!m_command_byte.GetOptionValue().OptionWasSet()) + { + result.AppendError ("the --command option must be set to a valid command byte"); + result.SetStatus (eReturnStatusFailed); + } + else + { + const uint64_t command_byte = m_command_byte.GetOptionValue().GetUInt64Value(0); + if (command_byte > 0 && command_byte <= UINT8_MAX) + { + ProcessKDP *process = (ProcessKDP *)m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) + { + const StateType state = process->GetState(); + + if (StateIsStoppedState (state, true)) + { + std::vector<uint8_t> payload_bytes; + const char *ascii_hex_bytes_cstr = m_packet_data.GetOptionValue().GetCurrentValue(); + if (ascii_hex_bytes_cstr && ascii_hex_bytes_cstr[0]) + { + StringExtractor extractor(ascii_hex_bytes_cstr); + const size_t ascii_hex_bytes_cstr_len = extractor.GetStringRef().size(); + if (ascii_hex_bytes_cstr_len & 1) + { + result.AppendErrorWithFormat ("payload data must contain an even number of ASCII hex characters: '%s'", ascii_hex_bytes_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + payload_bytes.resize(ascii_hex_bytes_cstr_len/2); + if (extractor.GetHexBytes(&payload_bytes[0], payload_bytes.size(), '\xdd') != payload_bytes.size()) + { + result.AppendErrorWithFormat ("payload data must only contain ASCII hex characters (no spaces or hex prefixes): '%s'", ascii_hex_bytes_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + Error error; + DataExtractor reply; + process->GetCommunication().SendRawRequest (command_byte, + payload_bytes.empty() ? NULL : payload_bytes.data(), + payload_bytes.size(), + reply, + error); + + if (error.Success()) + { + // Copy the binary bytes into a hex ASCII string for the result + StreamString packet; + packet.PutBytesAsRawHex8(reply.GetDataStart(), + reply.GetByteSize(), + endian::InlHostByteOrder(), + endian::InlHostByteOrder()); + result.AppendMessage(packet.GetString().c_str()); + result.SetStatus (eReturnStatusSuccessFinishResult); + return true; + } + else + { + const char *error_cstr = error.AsCString(); + if (error_cstr && error_cstr[0]) + result.AppendError (error_cstr); + else + result.AppendErrorWithFormat ("unknown error 0x%8.8x", error.GetError()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + result.AppendErrorWithFormat ("process must be stopped in order to send KDP packets, state is %s", StateAsCString (state)); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("invalid process"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("invalid command byte 0x%" PRIx64 ", valid values are 1 - 255", command_byte); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + { + result.AppendErrorWithFormat ("'%s' takes no arguments, only options.", m_cmd_name.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return false; + } +}; + +class CommandObjectProcessKDPPacket : public CommandObjectMultiword +{ +private: + +public: + CommandObjectProcessKDPPacket(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "process plugin packet", + "Commands that deal with KDP remote packets.", + NULL) + { + LoadSubCommand ("send", CommandObjectSP (new CommandObjectProcessKDPPacketSend (interpreter))); + } + + ~CommandObjectProcessKDPPacket () + { + } +}; + +class CommandObjectMultiwordProcessKDP : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordProcessKDP (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "process plugin", + "A set of commands for operating on a ProcessKDP process.", + "process plugin <subcommand> [<subcommand-options>]") + { + LoadSubCommand ("packet", CommandObjectSP (new CommandObjectProcessKDPPacket (interpreter))); + } + + ~CommandObjectMultiwordProcessKDP () + { + } +}; + +CommandObject * +ProcessKDP::GetPluginCommandObject() +{ + if (!m_command_sp) + m_command_sp.reset (new CommandObjectMultiwordProcessKDP (GetTarget().GetDebugger().GetCommandInterpreter())); + return m_command_sp.get(); +} + diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h new file mode 100644 index 000000000000..fe9a4e2844bf --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h @@ -0,0 +1,274 @@ +//===-- ProcessKDP.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessKDP_h_ +#define liblldb_ProcessKDP_h_ + +// C Includes + +// C++ Includes +#include <list> +#include <vector> + +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +#include "CommunicationKDP.h" + +class ThreadKDP; + +class ProcessKDP : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + static lldb::ProcessSP + CreateInstance (lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec *crash_file_path); + + static void + Initialize(); + + static void + DebuggerInitialize (lldb_private::Debugger &debugger); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessKDP(lldb::TargetSP target_sp, lldb_private::Listener &listener); + + virtual + ~ProcessKDP(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool + CanDebug (lldb::TargetSP target_sp, + bool plugin_specified_by_name); + + virtual lldb_private::CommandObject * + GetPluginCommandObject(); + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + virtual lldb_private::Error + WillLaunch (lldb_private::Module* module); + + virtual lldb_private::Error + DoLaunch (lldb_private::Module *exe_module, + lldb_private::ProcessLaunchInfo &launch_info); + + virtual lldb_private::Error + WillAttachToProcessWithID (lldb::pid_t pid); + + virtual lldb_private::Error + WillAttachToProcessWithName (const char *process_name, bool wait_for_launch); + + virtual lldb_private::Error + DoConnectRemote (lldb_private::Stream *strm, const char *remote_url); + + virtual lldb_private::Error + DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info); + + virtual lldb_private::Error + DoAttachToProcessWithName (const char *process_name, const lldb_private::ProcessAttachInfo &attach_info); + + virtual void + DidAttach (lldb_private::ArchSpec &process_arch); + + lldb::addr_t + GetImageInfoAddress(); + + lldb_private::DynamicLoader * + GetDynamicLoader (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ + virtual lldb_private::Error + WillResume (); + + virtual lldb_private::Error + DoResume (); + + virtual lldb_private::Error + DoHalt (bool &caused_stop); + + virtual lldb_private::Error + DoDetach (bool keep_stopped); + + virtual lldb_private::Error + DoSignal (int signal); + + virtual lldb_private::Error + DoDestroy (); + + virtual void + RefreshStateAfterStop(); + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + virtual bool + IsAlive (); + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + virtual size_t + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + + virtual size_t + DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error); + + virtual lldb::addr_t + DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error); + + virtual lldb_private::Error + DoDeallocateMemory (lldb::addr_t ptr); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableBreakpointSite (lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpointSite (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Watchpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true); + + virtual lldb_private::Error + DisableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true); + + CommunicationKDP & + GetCommunication() + { + return m_comm; + } + +protected: + friend class ThreadKDP; + friend class CommunicationKDP; + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + bool + IsRunning ( lldb::StateType state ) + { + return state == lldb::eStateRunning || IsStepping(state); + } + + bool + IsStepping ( lldb::StateType state) + { + return state == lldb::eStateStepping; + } + + bool + CanResume ( lldb::StateType state) + { + return state == lldb::eStateStopped; + } + + bool + HasExited (lldb::StateType state) + { + return state == lldb::eStateExited; + } + + bool + GetHostArchitecture (lldb_private::ArchSpec &arch); + + bool + ProcessIDIsValid ( ) const; + + void + Clear ( ); + + virtual bool + UpdateThreadList (lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list); + + enum + { + eBroadcastBitAsyncContinue = (1 << 0), + eBroadcastBitAsyncThreadShouldExit = (1 << 1) + }; + + lldb::ThreadSP + GetKernelThread (); + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + CommunicationKDP m_comm; + lldb_private::Broadcaster m_async_broadcaster; + lldb_private::HostThread m_async_thread; + lldb_private::ConstString m_dyld_plugin_name; + lldb::addr_t m_kernel_load_addr; + lldb::CommandObjectSP m_command_sp; + lldb::ThreadWP m_kernel_thread_wp; + + + bool + StartAsyncThread (); + + void + StopAsyncThread (); + + static void * + AsyncThread (void *arg); + +private: + //------------------------------------------------------------------ + // For ProcessKDP only + //------------------------------------------------------------------ + + DISALLOW_COPY_AND_ASSIGN (ProcessKDP); + +}; + +#endif // liblldb_ProcessKDP_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp new file mode 100644 index 000000000000..79cb62aa0066 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp @@ -0,0 +1,186 @@ +//===-- ProcessKDPLog.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessKDPLog.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/StreamFile.h" + +#include "ProcessKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +// We want to avoid global constructors where code needs to be run so here we +// control access to our static g_log_sp by hiding it in a singleton function +// that will construct the static g_log_sp the first time this function is +// called. +static bool g_log_enabled = false; +static Log * g_log = NULL; +static Log * +GetLog () +{ + if (!g_log_enabled) + return NULL; + return g_log; +} + +Log * +ProcessKDPLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log(GetLog ()); + if (log && mask) + { + uint32_t log_mask = log->GetMask().Get(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +ProcessKDPLog::DisableLog (const char **categories, Stream *feedback_strm) +{ + Log *log (GetLog ()); + if (log) + { + uint32_t flag_bits = 0; + + if (categories[0] != NULL) + { + flag_bits = log->GetMask().Get(); + for (size_t i = 0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + + if (::strcasecmp (arg, "all") == 0 ) flag_bits &= ~KDP_LOG_ALL; + else if (::strcasecmp (arg, "async") == 0 ) flag_bits &= ~KDP_LOG_ASYNC; + else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits &= ~KDP_LOG_BREAKPOINTS; + else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits &= ~KDP_LOG_COMM; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits &= ~KDP_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) flag_bits &= ~KDP_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits &= ~KDP_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits &= ~KDP_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits &= ~KDP_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits &= ~KDP_LOG_PROCESS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits &= ~KDP_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits &= ~KDP_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits &= ~KDP_LOG_VERBOSE; + else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits &= ~KDP_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + ListLogCategories (feedback_strm); + } + + } + } + + log->GetMask().Reset (flag_bits); + if (flag_bits == 0) + g_log_enabled = false; + } + + return; +} + +Log * +ProcessKDPLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, const char **categories, Stream *feedback_strm) +{ + // Try see if there already is a log - that way we can reuse its settings. + // We could reuse the log in toto, but we don't know that the stream is the same. + uint32_t flag_bits = 0; + if (g_log) + flag_bits = g_log->GetMask().Get(); + + // Now make a new log with this stream if one was provided + if (log_stream_sp) + { + if (g_log) + g_log->SetStream(log_stream_sp); + else + g_log = new Log(log_stream_sp); + } + + if (g_log) + { + bool got_unknown_category = false; + for (size_t i=0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + if (::strcasecmp (arg, "all") == 0 ) flag_bits |= KDP_LOG_ALL; + else if (::strcasecmp (arg, "async") == 0 ) flag_bits |= KDP_LOG_ASYNC; + else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits |= KDP_LOG_BREAKPOINTS; + else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits |= KDP_LOG_COMM; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits |= KDP_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) flag_bits |= KDP_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits |= KDP_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits |= KDP_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits |= KDP_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits |= KDP_LOG_PROCESS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits |= KDP_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits |= KDP_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits |= KDP_LOG_VERBOSE; + else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits |= KDP_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = KDP_LOG_DEFAULT; + g_log->GetMask().Reset(flag_bits); + g_log->GetOptions().Reset(log_options); + } + g_log_enabled = true; + return g_log; +} + +void +ProcessKDPLog::ListLogCategories (Stream *strm) +{ + strm->Printf ("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " async - log asynchronous activity\n" + " break - log breakpoints\n" + " communication - log communication activity\n" + " default - enable the default set of logging categories for liblldb\n" + " packets - log gdb remote packets\n" + " memory - log memory reads and writes\n" + " data-short - log memory bytes for memory reads and writes for short transactions only\n" + " data-long - log memory bytes for memory reads and writes for all transactions\n" + " process - log process events and activities\n" + " thread - log thread events and activities\n" + " step - log step related activities\n" + " verbose - enable verbose logging\n" + " watch - log watchpoint related activities\n", + ProcessKDP::GetPluginNameStatic().GetCString()); +} + + +void +ProcessKDPLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (mask)); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h new file mode 100644 index 000000000000..0cb32d9b2dcf --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h @@ -0,0 +1,54 @@ +//===-- ProcessKDPLog.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessKDPLog_h_ +#define liblldb_ProcessKDPLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define KDP_LOG_VERBOSE (1u << 0) +#define KDP_LOG_PROCESS (1u << 1) +#define KDP_LOG_THREAD (1u << 2) +#define KDP_LOG_PACKETS (1u << 3) +#define KDP_LOG_MEMORY (1u << 4) // Log memory reads/writes calls +#define KDP_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes +#define KDP_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes +#define KDP_LOG_BREAKPOINTS (1u << 7) +#define KDP_LOG_WATCHPOINTS (1u << 8) +#define KDP_LOG_STEP (1u << 9) +#define KDP_LOG_COMM (1u << 10) +#define KDP_LOG_ASYNC (1u << 11) +#define KDP_LOG_ALL (UINT32_MAX) +#define KDP_LOG_DEFAULT KDP_LOG_PACKETS + +class ProcessKDPLog +{ +public: + static lldb_private::Log * + GetLogIfAllCategoriesSet(uint32_t mask = 0); + + static void + DisableLog (const char **categories, lldb_private::Stream *feedback_strm); + + static lldb_private::Log * + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, const char **categories, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories (lldb_private::Stream *strm); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_ProcessKDPLog_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.cpp new file mode 100644 index 000000000000..449ac646ab3c --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.cpp @@ -0,0 +1,161 @@ +//===-- RegisterContextKDP_arm.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextKDP_arm.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_arm::RegisterContextKDP_arm (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_arm (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_arm::~RegisterContextKDP_arm() +{ +} + +int +RegisterContextKDP_arm::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + + diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.h new file mode 100644 index 000000000000..1e547289d9ce --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.h @@ -0,0 +1,61 @@ +//===-- RegisterContextKDP_arm.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextKDP_arm_h_ +#define liblldb_RegisterContextKDP_arm_h_ + +// C Includes + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_arm.h" + +class ThreadKDP; + +class RegisterContextKDP_arm : public RegisterContextDarwin_arm +{ +public: + + RegisterContextKDP_arm (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_arm(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + int + DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_arm_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.cpp new file mode 100644 index 000000000000..ed62f1982d3c --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.cpp @@ -0,0 +1,161 @@ +//===-- RegisterContextKDP_arm64.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextKDP_arm64.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_arm64::RegisterContextKDP_arm64 (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_arm64 (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_arm64::~RegisterContextKDP_arm64() +{ +} + +int +RegisterContextKDP_arm64::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + + diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.h new file mode 100644 index 000000000000..8780b7be4a9a --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.h @@ -0,0 +1,61 @@ +//===-- RegisterContextKDP_arm64.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextKDP_arm64_h_ +#define liblldb_RegisterContextKDP_arm64_h_ + +// C Includes + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_arm64.h" + +class ThreadKDP; + +class RegisterContextKDP_arm64 : public RegisterContextDarwin_arm64 +{ +public: + + RegisterContextKDP_arm64 (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_arm64(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + int + DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_arm64_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.cpp new file mode 100644 index 000000000000..882b0c2e931d --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.cpp @@ -0,0 +1,129 @@ +//===-- RegisterContextKDP_i386.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "RegisterContextKDP_i386.h" +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_i386::RegisterContextKDP_i386 (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_i386 (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_i386::~RegisterContextKDP_i386() +{ +} + +int +RegisterContextKDP_i386::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + + diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.h new file mode 100644 index 000000000000..4b6bc5b262f7 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.h @@ -0,0 +1,53 @@ +//===-- RegisterContextKDP_i386.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextKDP_i386_h_ +#define liblldb_RegisterContextKDP_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_i386.h" + +class ThreadKDP; + +class RegisterContextKDP_i386 : public RegisterContextDarwin_i386 +{ +public: + RegisterContextKDP_i386 (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_i386(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_i386_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.cpp new file mode 100644 index 000000000000..f4247a5da272 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.cpp @@ -0,0 +1,127 @@ +//===-- RegisterContextKDP_x86_64.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "RegisterContextKDP_x86_64.h" +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_x86_64::RegisterContextKDP_x86_64 (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_x86_64 (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_x86_64::~RegisterContextKDP_x86_64() +{ +} + +int +RegisterContextKDP_x86_64::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.h new file mode 100644 index 000000000000..a426349198ff --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.h @@ -0,0 +1,54 @@ +//===-- RegisterContextKDP_x86_64.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextKDP_x86_64_h_ +#define liblldb_RegisterContextKDP_x86_64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_x86_64.h" + +class ThreadKDP; + +class RegisterContextKDP_x86_64 : public RegisterContextDarwin_x86_64 +{ +public: + + RegisterContextKDP_x86_64 (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_x86_64(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_x86_64_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp new file mode 100644 index 000000000000..3b8bed101a8a --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp @@ -0,0 +1,211 @@ +//===-- ThreadKDP.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "ThreadKDP.h" + +#include "lldb/Utility/SafeMachO.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/State.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Breakpoint/Watchpoint.h" + +#include "ProcessKDP.h" +#include "ProcessKDPLog.h" +#include "RegisterContextKDP_arm.h" +#include "RegisterContextKDP_arm64.h" +#include "RegisterContextKDP_i386.h" +#include "RegisterContextKDP_x86_64.h" +#include "Plugins/Process/Utility/StopInfoMachException.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Thread Registers +//---------------------------------------------------------------------- + +ThreadKDP::ThreadKDP (Process &process, lldb::tid_t tid) : + Thread(process, tid), + m_thread_name (), + m_dispatch_queue_name (), + m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS) +{ + ProcessKDPLog::LogIf(KDP_LOG_THREAD, "%p: ThreadKDP::ThreadKDP (tid = 0x%4.4x)", this, GetID()); +} + +ThreadKDP::~ThreadKDP () +{ + ProcessKDPLog::LogIf(KDP_LOG_THREAD, "%p: ThreadKDP::~ThreadKDP (tid = 0x%4.4x)", this, GetID()); + DestroyThread(); +} + +const char * +ThreadKDP::GetName () +{ + if (m_thread_name.empty()) + return NULL; + return m_thread_name.c_str(); +} + +const char * +ThreadKDP::GetQueueName () +{ + return NULL; +} + +void +ThreadKDP::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context. We don't set "force" to + // true because the stop reply packet might have had some register values + // that were expedited and these will already be copied into the register + // context by the time this function gets called. The KDPRegisterContext + // class has been made smart enough to detect when it needs to invalidate + // which registers are valid by putting hooks in the register read and + // register supply functions where they check the process stop ID and do + // the right thing. + const bool force = false; + lldb::RegisterContextSP reg_ctx_sp (GetRegisterContext()); + if (reg_ctx_sp) + reg_ctx_sp->InvalidateIfNeeded (force); +} + +bool +ThreadKDP::ThreadIDIsValid (lldb::tid_t thread) +{ + return thread != 0; +} + +void +ThreadKDP::Dump(Log *log, uint32_t index) +{ +} + + +bool +ThreadKDP::ShouldStop (bool &step_more) +{ + return true; +} +lldb::RegisterContextSP +ThreadKDP::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp = CreateRegisterContextForFrame (NULL); + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadKDP::CreateRegisterContextForFrame (StackFrame *frame) +{ + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex (); + + if (concrete_frame_idx == 0) + { + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + switch (static_cast<ProcessKDP *>(process_sp.get())->GetCommunication().GetCPUType()) + { + case llvm::MachO::CPU_TYPE_ARM: + reg_ctx_sp.reset (new RegisterContextKDP_arm (*this, concrete_frame_idx)); + break; + case llvm::MachO::CPU_TYPE_ARM64: + reg_ctx_sp.reset (new RegisterContextKDP_arm64 (*this, concrete_frame_idx)); + break; + case llvm::MachO::CPU_TYPE_I386: + reg_ctx_sp.reset (new RegisterContextKDP_i386 (*this, concrete_frame_idx)); + break; + case llvm::MachO::CPU_TYPE_X86_64: + reg_ctx_sp.reset (new RegisterContextKDP_x86_64 (*this, concrete_frame_idx)); + break; + default: + assert (!"Add CPU type support in KDP"); + break; + } + } + } + else + { + Unwind *unwinder = GetUnwinder (); + if (unwinder) + reg_ctx_sp = unwinder->CreateRegisterContextForFrame (frame); + } + return reg_ctx_sp; +} + +bool +ThreadKDP::CalculateStopInfo () +{ + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + if (m_cached_stop_info_sp) + { + SetStopInfo (m_cached_stop_info_sp); + } + else + { + SetStopInfo(StopInfo::CreateStopReasonWithSignal (*this, SIGSTOP)); + } + return true; + } + return false; +} + +void +ThreadKDP::SetStopInfoFrom_KDP_EXCEPTION (const DataExtractor &exc_reply_packet) +{ + lldb::offset_t offset = 0; + uint8_t reply_command = exc_reply_packet.GetU8(&offset); + if (reply_command == CommunicationKDP::KDP_EXCEPTION) + { + offset = 8; + const uint32_t count = exc_reply_packet.GetU32 (&offset); + if (count >= 1) + { + //const uint32_t cpu = exc_reply_packet.GetU32 (&offset); + offset += 4; // Skip the useless CPU field + const uint32_t exc_type = exc_reply_packet.GetU32 (&offset); + const uint32_t exc_code = exc_reply_packet.GetU32 (&offset); + const uint32_t exc_subcode = exc_reply_packet.GetU32 (&offset); + // We have to make a copy of the stop info because the thread list + // will iterate through the threads and clear all stop infos.. + + // Let the StopInfoMachException::CreateStopReasonWithMachException() + // function update the PC if needed as we might hit a software breakpoint + // and need to decrement the PC (i386 and x86_64 need this) and KDP + // doesn't do this for us. + const bool pc_already_adjusted = false; + const bool adjust_pc_if_needed = true; + + m_cached_stop_info_sp = StopInfoMachException::CreateStopReasonWithMachException (*this, + exc_type, + 2, + exc_code, + exc_subcode, + 0, + pc_already_adjusted, + adjust_pc_if_needed); + } + } +} + diff --git a/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.h b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.h new file mode 100644 index 000000000000..7dc373f03550 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.h @@ -0,0 +1,98 @@ +//===-- ThreadKDP.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadKDP_h_ +#define liblldb_ThreadKDP_h_ + +#include <string> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +class ProcessKDP; + +class ThreadKDP : public lldb_private::Thread +{ +public: + ThreadKDP (lldb_private::Process &process, + lldb::tid_t tid); + + virtual + ~ThreadKDP (); + + virtual void + RefreshStateAfterStop(); + + virtual const char * + GetName (); + + virtual const char * + GetQueueName (); + + virtual lldb::RegisterContextSP + GetRegisterContext (); + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + void + Dump (lldb_private::Log *log, uint32_t index); + + static bool + ThreadIDIsValid (lldb::tid_t thread); + + bool + ShouldStop (bool &step_more); + + const char * + GetBasicInfoAsString (); + + void + SetName (const char *name) + { + if (name && name[0]) + m_thread_name.assign (name); + else + m_thread_name.clear(); + } + + lldb::addr_t + GetThreadDispatchQAddr () + { + return m_thread_dispatch_qaddr; + } + + void + SetThreadDispatchQAddr (lldb::addr_t thread_dispatch_qaddr) + { + m_thread_dispatch_qaddr = thread_dispatch_qaddr; + } + + void + SetStopInfoFrom_KDP_EXCEPTION (const lldb_private::DataExtractor &exc_reply_packet); + +protected: + + friend class ProcessKDP; + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + std::string m_thread_name; + std::string m_dispatch_queue_name; + lldb::addr_t m_thread_dispatch_qaddr; + lldb::StopInfoSP m_cached_stop_info_sp; + //------------------------------------------------------------------ + // Protected member functions. + //------------------------------------------------------------------ + virtual bool + CalculateStopInfo (); +}; + +#endif // liblldb_ThreadKDP_h_ diff --git a/source/Plugins/Process/POSIX/CMakeLists.txt b/source/Plugins/Process/POSIX/CMakeLists.txt new file mode 100644 index 000000000000..2ed7326dbb71 --- /dev/null +++ b/source/Plugins/Process/POSIX/CMakeLists.txt @@ -0,0 +1,8 @@ +include_directories(.) +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessPOSIX + CrashReason.cpp + ProcessMessage.cpp + ProcessPOSIXLog.cpp + ) diff --git a/source/Plugins/Process/POSIX/Makefile b/source/Plugins/Process/POSIX/Makefile new file mode 100644 index 000000000000..e8ac3a8ae0ed --- /dev/null +++ b/source/Plugins/Process/POSIX/Makefile @@ -0,0 +1,32 @@ +##===- source/Plugins/Process/POSIX/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 := lldbPluginProcessPOSIX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/../../Makefile.config + +# Extend the include path so we may locate UnwindLLDB.h +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility + +ifeq ($(HOST_OS),Linux) +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/Linux + +# Disable warning for now as offsetof is used with an index into a structure member array +# in defining register info tables. +CPP.Flags += -Wno-extended-offsetof +endif + +ifneq (,$(filter $(HOST_OS), FreeBSD GNU/kFreeBSD)) +# Extend the include path so we may locate ProcessMonitor +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/FreeBSD +endif + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/Utility/CMakeLists.txt b/source/Plugins/Process/Utility/CMakeLists.txt new file mode 100644 index 000000000000..4a847b4f966a --- /dev/null +++ b/source/Plugins/Process/Utility/CMakeLists.txt @@ -0,0 +1,47 @@ +include_directories(../../../Utility/) + +add_lldb_library(lldbPluginProcessUtility + DynamicRegisterInfo.cpp + FreeBSDSignals.cpp + GDBRemoteSignals.cpp + HistoryThread.cpp + HistoryUnwind.cpp + InferiorCallPOSIX.cpp + LinuxSignals.cpp + MipsLinuxSignals.cpp + NetBSDSignals.cpp + RegisterContextDarwin_arm.cpp + RegisterContextDarwin_arm64.cpp + RegisterContextDarwin_i386.cpp + RegisterContextDarwin_x86_64.cpp + RegisterContextDummy.cpp + RegisterContextFreeBSD_arm.cpp + RegisterContextFreeBSD_arm64.cpp + RegisterContextFreeBSD_i386.cpp + RegisterContextFreeBSD_mips64.cpp + RegisterContextFreeBSD_powerpc.cpp + RegisterContextFreeBSD_x86_64.cpp + RegisterContextHistory.cpp + RegisterContextLinux_arm.cpp + RegisterContextLinux_arm64.cpp + RegisterContextLinux_i386.cpp + RegisterContextLinux_x86_64.cpp + RegisterContextLinux_mips64.cpp + RegisterContextLinux_mips.cpp + RegisterContextLLDB.cpp + RegisterContextMacOSXFrameBackchain.cpp + RegisterContextMach_arm.cpp + RegisterContextMach_i386.cpp + RegisterContextMach_x86_64.cpp + RegisterContextMemory.cpp + RegisterContextPOSIX_arm.cpp + RegisterContextPOSIX_arm64.cpp + RegisterContextPOSIX_mips64.cpp + RegisterContextPOSIX_powerpc.cpp + RegisterContextPOSIX_x86.cpp + RegisterContextThreadMemory.cpp + StopInfoMachException.cpp + ThreadMemory.cpp + UnwindLLDB.cpp + UnwindMacOSXFrameBackchain.cpp + ) diff --git a/source/Plugins/Process/Utility/Makefile b/source/Plugins/Process/Utility/Makefile new file mode 100644 index 000000000000..eb0caf3f92fe --- /dev/null +++ b/source/Plugins/Process/Utility/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Utility/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 := lldbPluginProcessUtility +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/Windows/Common/CMakeLists.txt b/source/Plugins/Process/Windows/Common/CMakeLists.txt new file mode 100644 index 000000000000..5a53c3c3300c --- /dev/null +++ b/source/Plugins/Process/Windows/Common/CMakeLists.txt @@ -0,0 +1,23 @@ +include_directories(.) +include_directories(../../Utility) + +set(PROC_WINDOWS_COMMON_SOURCES + RegisterContextWindows.cpp + ProcessWindows.cpp + ProcessWindowsLog.cpp + TargetThreadWindows.cpp + ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(PROC_WINDOWS_COMMON_SOURCES ${PROC_WINDOWS_COMMON_SOURCES} + x86/RegisterContextWindows_x86.cpp + ) +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROC_WINDOWS_COMMON_SOURCES ${PROC_WINDOWS_COMMON_SOURCES} + x64/RegisterContextWindows_x64.cpp + ) +endif() + +add_lldb_library(lldbPluginProcessWindowsCommon + ${PROC_WINDOWS_COMMON_SOURCES} + ) diff --git a/source/Plugins/Process/Windows/Common/ExceptionRecord.h b/source/Plugins/Process/Windows/Common/ExceptionRecord.h new file mode 100644 index 000000000000..79dae3fca4e8 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ExceptionRecord.h @@ -0,0 +1,99 @@ +//===-- ExceptionRecord.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_ExceptionRecord_H_ +#define liblldb_Plugins_Process_Windows_ExceptionRecord_H_ + +#include "lldb/lldb-forward.h" +#include "lldb/Host/windows/windows.h" +#include <DbgHelp.h> + +#include <memory> +#include <vector> + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +// ExceptionRecord +// +// ExceptionRecord defines an interface which allows implementors to receive +// notification of events that happen in a debugged process. +//---------------------------------------------------------------------- +class ExceptionRecord +{ + public: + ExceptionRecord(const EXCEPTION_RECORD &record, lldb::tid_t thread_id) + { + m_code = record.ExceptionCode; + m_continuable = (record.ExceptionFlags == 0); + if (record.ExceptionRecord) + m_next_exception.reset(new ExceptionRecord(*record.ExceptionRecord, thread_id)); + m_exception_addr = reinterpret_cast<lldb::addr_t>(record.ExceptionAddress); + m_thread_id = thread_id; + m_arguments.assign(record.ExceptionInformation, record.ExceptionInformation + record.NumberParameters); + } + + // MINIDUMP_EXCEPTIONs are almost identical to EXCEPTION_RECORDs. + ExceptionRecord(const MINIDUMP_EXCEPTION &record, lldb::tid_t thread_id) : + m_code(record.ExceptionCode), + m_continuable(record.ExceptionFlags == 0), + m_next_exception(nullptr), + m_exception_addr(static_cast<lldb::addr_t>(record.ExceptionAddress)), + m_thread_id(thread_id), + m_arguments(record.ExceptionInformation, record.ExceptionInformation + record.NumberParameters) + { + // Set up link to nested exception. + if (record.ExceptionRecord) + { + m_next_exception.reset(new ExceptionRecord(*reinterpret_cast<const MINIDUMP_EXCEPTION *>(record.ExceptionRecord), + thread_id)); + } + } + + virtual ~ExceptionRecord() {} + + DWORD + GetExceptionCode() const + { + return m_code; + } + bool + IsContinuable() const + { + return m_continuable; + } + const ExceptionRecord * + GetNextException() const + { + return m_next_exception.get(); + } + lldb::addr_t + GetExceptionAddress() const + { + return m_exception_addr; + } + + lldb::tid_t + GetThreadID() const + { + return m_thread_id; + } + + private: + DWORD m_code; + bool m_continuable; + std::shared_ptr<ExceptionRecord> m_next_exception; + lldb::addr_t m_exception_addr; + lldb::tid_t m_thread_id; + std::vector<ULONG_PTR> m_arguments; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/source/Plugins/Process/Windows/Common/ProcessWindows.cpp new file mode 100644 index 000000000000..0e6900d8fb7f --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -0,0 +1,100 @@ +//===-- ProcessWindows.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessWindows.h" + +// Other libraries and framework includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +namespace lldb_private +{ + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessWindows::ProcessWindows(lldb::TargetSP target_sp, Listener &listener) + : lldb_private::Process(target_sp, listener) +{ +} + +ProcessWindows::~ProcessWindows() +{ +} + +size_t +ProcessWindows::GetSTDOUT(char *buf, size_t buf_size, Error &error) +{ + error.SetErrorString("GetSTDOUT unsupported on Windows"); + return 0; +} + +size_t +ProcessWindows::GetSTDERR(char *buf, size_t buf_size, Error &error) +{ + error.SetErrorString("GetSTDERR unsupported on Windows"); + return 0; +} + +size_t +ProcessWindows::PutSTDIN(const char *buf, size_t buf_size, Error &error) +{ + error.SetErrorString("PutSTDIN unsupported on Windows"); + return 0; +} + +//------------------------------------------------------------------------------ +// ProcessInterface protocol. + + +lldb::addr_t +ProcessWindows::GetImageInfoAddress() +{ + Target &target = GetTarget(); + ObjectFile *obj_file = target.GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(&target); + if (addr.IsValid()) + return addr.GetLoadAddress(&target); + else + return LLDB_INVALID_ADDRESS; +} + +// The Windows page protection bits are NOT independent masks that can be bitwise-ORed +// together. For example, PAGE_EXECUTE_READ is not (PAGE_EXECUTE | PAGE_READ). +// To test for an access type, it's necessary to test for any of the bits that provide +// that access type. +bool +ProcessWindows::IsPageReadable(uint32_t protect) +{ + return (protect & PAGE_NOACCESS) == 0; +} + +bool +ProcessWindows::IsPageWritable(uint32_t protect) +{ + return (protect & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READWRITE | PAGE_WRITECOPY)) != 0; +} + +bool +ProcessWindows::IsPageExecutable(uint32_t protect) +{ + return (protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0; +} + +} diff --git a/source/Plugins/Process/Windows/Common/ProcessWindows.h b/source/Plugins/Process/Windows/Common/ProcessWindows.h new file mode 100644 index 000000000000..2a437c0ca909 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ProcessWindows.h @@ -0,0 +1,52 @@ +//===-- ProcessWindows.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_Common_ProcessWindows_H_ +#define liblldb_Plugins_Process_Windows_Common_ProcessWindows_H_ + +// Other libraries and framework includes +#include "lldb/lldb-forward.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/Process.h" + +namespace lldb_private +{ + +class ProcessWindows : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + ProcessWindows(lldb::TargetSP target_sp, + lldb_private::Listener &listener); + + ~ProcessWindows(); + + size_t GetSTDOUT(char *buf, size_t buf_size, lldb_private::Error &error) override; + size_t GetSTDERR(char *buf, size_t buf_size, lldb_private::Error &error) override; + size_t PutSTDIN(const char *buf, size_t buf_size, lldb_private::Error &error) override; + + lldb::addr_t GetImageInfoAddress() override; + +protected: + // These decode the page protection bits. + static bool + IsPageReadable(uint32_t protect); + + static bool + IsPageWritable(uint32_t protect); + + static bool + IsPageExecutable(uint32_t protect); +}; + +} + +#endif // liblldb_Plugins_Process_Windows_Common_ProcessWindows_H_ diff --git a/source/Plugins/Process/Windows/Common/ProcessWindowsLog.cpp b/source/Plugins/Process/Windows/Common/ProcessWindowsLog.cpp new file mode 100644 index 000000000000..47722c5146b2 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ProcessWindowsLog.cpp @@ -0,0 +1,194 @@ +//===-- ProcessWindowsLog.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessWindowsLog.h" + +#include <mutex> + +#include "lldb/Core/StreamFile.h" +#include "lldb/Interpreter/Args.h" +#include "llvm/Support/ManagedStatic.h" + +using namespace lldb; +using namespace lldb_private; + + +// We want to avoid global constructors where code needs to be run so here we +// control access to our static g_log_sp by hiding it in a singleton function +// that will construct the static g_log_sp the first time this function is +// called. +static bool g_log_enabled = false; +static Log * g_log = nullptr; + +static llvm::ManagedStatic<std::once_flag> g_once_flag; + +void +ProcessWindowsLog::Initialize() +{ + static ConstString g_name("windows"); + + std::call_once(*g_once_flag, [](){ + Log::Callbacks log_callbacks = { + DisableLog, + EnableLog, + ListLogCategories + }; + + Log::RegisterLogChannel(g_name, log_callbacks); + RegisterPluginName(g_name); + }); +} + +void +ProcessWindowsLog::Terminate() +{ +} + +Log * +ProcessWindowsLog::GetLog() +{ + return (g_log_enabled) ? g_log : nullptr; +} + +bool +ProcessWindowsLog::TestLogFlags(uint32_t mask, LogMaskReq req) +{ + Log *log = GetLog(); + if (!log) + return false; + + uint32_t log_mask = log->GetMask().Get(); + if (req == LogMaskReq::All) + return ((log_mask & mask) == mask); + else + return (log_mask & mask); +} + +static uint32_t +GetFlagBits(const char *arg) +{ + if (::strcasecmp(arg, "all") == 0 ) return WINDOWS_LOG_ALL; + else if (::strcasecmp(arg, "break") == 0 ) return WINDOWS_LOG_BREAKPOINTS; + else if (::strcasecmp(arg, "event") == 0 ) return WINDOWS_LOG_EVENT; + else if (::strcasecmp(arg, "exception") == 0 ) return WINDOWS_LOG_EXCEPTION; + else if (::strcasecmp(arg, "memory") == 0 ) return WINDOWS_LOG_MEMORY; + else if (::strcasecmp(arg, "process") == 0 ) return WINDOWS_LOG_PROCESS; + else if (::strcasecmp(arg, "registers") == 0 ) return WINDOWS_LOG_REGISTERS; + else if (::strcasecmp(arg, "step") == 0 ) return WINDOWS_LOG_STEP; + else if (::strcasecmp(arg, "thread") == 0 ) return WINDOWS_LOG_THREAD; + else if (::strcasecmp(arg, "verbose") == 0 ) return WINDOWS_LOG_VERBOSE; + return 0; +} + +void +ProcessWindowsLog::DisableLog(const char **args, Stream *feedback_strm) +{ + Log *log (GetLog()); + if (log) + { + uint32_t flag_bits = 0; + + if (args[0] != nullptr) + { + flag_bits = log->GetMask().Get(); + for (; args[0]; args++) + { + const char *arg = args[0]; + uint32_t bits = GetFlagBits(arg); + + if (bits) + { + flag_bits &= ~bits; + } + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + ListLogCategories(feedback_strm); + } + } + } + + log->GetMask().Reset(flag_bits); + if (flag_bits == 0) + { + g_log_enabled = false; + log->SetStream(lldb::StreamSP()); + } + } + + return; +} + +Log * +ProcessWindowsLog::EnableLog(StreamSP &log_stream_sp, uint32_t log_options, const char **args, Stream *feedback_strm) +{ + // Try see if there already is a log - that way we can reuse its settings. + // We could reuse the log in toto, but we don't know that the stream is the same. + uint32_t flag_bits = 0; + if (g_log) + flag_bits = g_log->GetMask().Get(); + + // Now make a new log with this stream if one was provided + if (log_stream_sp) + { + if (g_log) + g_log->SetStream(log_stream_sp); + else + g_log = new Log(log_stream_sp); + } + + if (g_log) + { + bool got_unknown_category = false; + for (; args[0]; args++) + { + const char *arg = args[0]; + uint32_t bits = GetFlagBits(arg); + + if (bits) + { + flag_bits |= bits; + } + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = WINDOWS_LOG_ALL; + g_log->GetMask().Reset(flag_bits); + g_log->GetOptions().Reset(log_options); + g_log_enabled = true; + } + return g_log; +} + +void +ProcessWindowsLog::ListLogCategories(Stream *strm) +{ + strm->Printf("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " break - log breakpoints\n" + " event - log low level debugger events\n" + " exception - log exception information\n" + " memory - log memory reads and writes\n" + " process - log process events and activities\n" + " registers - log register read/writes\n" + " thread - log thread events and activities\n" + " step - log step related activities\n" + " verbose - enable verbose logging\n", + ProcessWindowsLog::m_pluginname); +} + +const char *ProcessWindowsLog::m_pluginname = ""; diff --git a/source/Plugins/Process/Windows/Common/ProcessWindowsLog.h b/source/Plugins/Process/Windows/Common/ProcessWindowsLog.h new file mode 100644 index 000000000000..d798d131faeb --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ProcessWindowsLog.h @@ -0,0 +1,96 @@ +//===-- ProcessWindowsLog.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessWindowsLog_h_ +#define liblldb_ProcessWindowsLog_h_ + +#include "lldb/Core/Log.h" + +#define WINDOWS_LOG_VERBOSE (1u << 0) +#define WINDOWS_LOG_PROCESS (1u << 1) // Log process operations +#define WINDOWS_LOG_EXCEPTION (1u << 1) // Log exceptions +#define WINDOWS_LOG_THREAD (1u << 2) // Log thread operations +#define WINDOWS_LOG_MEMORY (1u << 3) // Log memory reads/writes calls +#define WINDOWS_LOG_BREAKPOINTS (1u << 4) // Log breakpoint operations +#define WINDOWS_LOG_STEP (1u << 5) // Log step operations +#define WINDOWS_LOG_REGISTERS (1u << 6) // Log register operations +#define WINDOWS_LOG_EVENT (1u << 7) // Low level debug events +#define WINDOWS_LOG_ALL (UINT32_MAX) + +enum class LogMaskReq +{ + All, + Any +}; + +class ProcessWindowsLog +{ + static const char *m_pluginname; + +public: + // --------------------------------------------------------------------- + // Public Static Methods + // --------------------------------------------------------------------- + static void + Initialize(); + + static void + Terminate(); + + static void + RegisterPluginName(const char *pluginName) + { + m_pluginname = pluginName; + } + + static void + RegisterPluginName(lldb_private::ConstString pluginName) + { + m_pluginname = pluginName.GetCString(); + } + + static bool + TestLogFlags(uint32_t mask, LogMaskReq req); + + static lldb_private::Log * + GetLog(); + + static void + DisableLog(const char **args, lldb_private::Stream *feedback_strm); + + static lldb_private::Log * + EnableLog(lldb::StreamSP &log_stream_sp, uint32_t log_options, + const char **args, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories(lldb_private::Stream *strm); +}; + +#define WINLOGF_IF(Flags, Req, Method, ...) \ + { \ + if (ProcessWindowsLog::TestLogFlags(Flags, Req)) \ + { \ + Log *log = ProcessWindowsLog::GetLog(); \ + if (log) \ + log->Method(__VA_ARGS__); \ + } \ + } + +#define WINLOG_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Printf, __VA_ARGS__) +#define WINLOG_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Printf, __VA_ARGS__) +#define WINLOGV_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Verbose, __VA_ARGS__) +#define WINLOGV_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Verbose, __VA_ARGS__) +#define WINLOGD_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Debug, __VA_ARGS__) +#define WINLOGD_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Debug, __VA_ARGS__) +#define WINERR_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Error, __VA_ARGS__) +#define WINERR_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Error, __VA_ARGS__) +#define WINWARN_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Warning, __VA_ARGS__) +#define WINWARN_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Warning, __VA_ARGS__) + +#endif // liblldb_ProcessWindowsLog_h_ diff --git a/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp b/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp new file mode 100644 index 000000000000..d61675f09b1b --- /dev/null +++ b/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp @@ -0,0 +1,155 @@ +//===-- RegisterContextWindows.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-types.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "ProcessWindowsLog.h" +#include "RegisterContextWindows.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +const DWORD kWinContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + +//------------------------------------------------------------------ +// Constructors and Destructors +//------------------------------------------------------------------ +RegisterContextWindows::RegisterContextWindows(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContext(thread, concrete_frame_idx) + , m_context() + , m_context_stale(true) +{ +} + +RegisterContextWindows::~RegisterContextWindows() +{ +} + +void +RegisterContextWindows::InvalidateAllRegisters() +{ + m_context_stale = true; +} + +bool +RegisterContextWindows::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) +{ + if (!CacheAllRegisterValues()) + return false; + if (data_sp->GetByteSize() < sizeof(m_context)) + { + data_sp.reset(new DataBufferHeap(sizeof(CONTEXT), 0)); + } + memcpy(data_sp->GetBytes(), &m_context, sizeof(m_context)); + return true; +} + +bool +RegisterContextWindows::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + assert(data_sp->GetByteSize() >= sizeof(m_context)); + memcpy(&m_context, data_sp->GetBytes(), sizeof(m_context)); + + TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread); + if (!::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context)) + return false; + + return true; +} + +uint32_t +RegisterContextWindows::ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) +{ + const uint32_t num_regs = GetRegisterCount(); + + assert(kind < kNumRegisterKinds); + for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx); + + if (reg_info->kinds[kind] == num) + return reg_idx; + } + + return LLDB_INVALID_REGNUM; +} + +//------------------------------------------------------------------ +// Subclasses can these functions if desired +//------------------------------------------------------------------ +uint32_t +RegisterContextWindows::NumSupportedHardwareBreakpoints() +{ + // Support for hardware breakpoints not yet implemented. + return 0; +} + +uint32_t +RegisterContextWindows::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) +{ + return 0; +} + +bool +RegisterContextWindows::ClearHardwareBreakpoint(uint32_t hw_idx) +{ + return false; +} + +uint32_t +RegisterContextWindows::NumSupportedHardwareWatchpoints() +{ + // Support for hardware watchpoints not yet implemented. + return 0; +} + +uint32_t +RegisterContextWindows::SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) +{ + return 0; +} + +bool +RegisterContextWindows::ClearHardwareWatchpoint(uint32_t hw_index) +{ + return false; +} + +bool +RegisterContextWindows::HardwareSingleStep(bool enable) +{ + return false; +} + +bool +RegisterContextWindows::CacheAllRegisterValues() +{ + if (!m_context_stale) + return true; + + TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread); + memset(&m_context, 0, sizeof(m_context)); + m_context.ContextFlags = kWinContextFlags; + if (!::GetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context)) + { + WINERR_IFALL(WINDOWS_LOG_REGISTERS, "GetThreadContext failed with error %u while caching register values.", + ::GetLastError()); + return false; + } + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "GetThreadContext successfully updated the register values.", ::GetLastError()); + m_context_stale = false; + return true; +} diff --git a/source/Plugins/Process/Windows/Common/RegisterContextWindows.h b/source/Plugins/Process/Windows/Common/RegisterContextWindows.h new file mode 100644 index 000000000000..66b7a9004ead --- /dev/null +++ b/source/Plugins/Process/Windows/Common/RegisterContextWindows.h @@ -0,0 +1,67 @@ +//===-- RegisterContextWindows.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindows_H_ +#define liblldb_RegisterContextWindows_H_ + +#include "lldb/lldb-forward.h" +#include "lldb/Target/RegisterContext.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindows : public lldb_private::RegisterContext +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindows(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindows(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + void InvalidateAllRegisters() override; + + bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) override; + + //------------------------------------------------------------------ + // Subclasses can override these functions if desired + //------------------------------------------------------------------ + uint32_t NumSupportedHardwareBreakpoints() override; + + uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + bool ClearHardwareBreakpoint(uint32_t hw_idx) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + bool HardwareSingleStep(bool enable) override; + + protected: + virtual bool CacheAllRegisterValues(); + + CONTEXT m_context; + bool m_context_stale; +}; +} + +#endif // #ifndef liblldb_RegisterContextWindows_H_ diff --git a/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp b/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp new file mode 100644 index 000000000000..dcb6f0c72435 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp @@ -0,0 +1,98 @@ +//===-- TargetThreadWindows.cpp----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Core/Logging.h" +#include "lldb/Core/State.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostNativeThreadBase.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Target/RegisterContext.h" + +#include "TargetThreadWindows.h" +#include "ProcessWindows.h" +#include "ProcessWindowsLog.h" +#include "UnwindLLDB.h" + +using namespace lldb; +using namespace lldb_private; + +TargetThreadWindows::TargetThreadWindows(ProcessWindows &process, const HostThread &thread) + : Thread(process, thread.GetNativeThread().GetThreadId()) + , m_host_thread(thread) +{ +} + +TargetThreadWindows::~TargetThreadWindows() +{ + DestroyThread(); +} + +void +TargetThreadWindows::RefreshStateAfterStop() +{ + ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); + SetState(eStateStopped); + GetRegisterContext()->InvalidateIfNeeded(false); +} + +void +TargetThreadWindows::WillResume(lldb::StateType resume_state) +{ +} + +void +TargetThreadWindows::DidStop() +{ +} + +bool +TargetThreadWindows::CalculateStopInfo() +{ + SetStopInfo(m_stop_info_sp); + return true; +} + +Unwind * +TargetThreadWindows::GetUnwinder() +{ + // FIXME: Implement an unwinder based on the Windows unwinder exposed through DIA SDK. + if (m_unwinder_ap.get() == NULL) + m_unwinder_ap.reset(new UnwindLLDB(*this)); + return m_unwinder_ap.get(); +} + +bool +TargetThreadWindows::DoResume() +{ + StateType resume_state = GetTemporaryResumeState(); + StateType current_state = GetState(); + if (resume_state == current_state) + return true; + + if (resume_state == eStateStepping) + { + uint32_t flags_index = GetRegisterContext()->ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + uint64_t flags_value = GetRegisterContext()->ReadRegisterAsUnsigned(flags_index, 0); + flags_value |= 0x100; // Set the trap flag on the CPU + GetRegisterContext()->WriteRegisterFromUnsigned(flags_index, flags_value); + } + + if (resume_state == eStateStepping || resume_state == eStateRunning) + { + DWORD previous_suspend_count = 0; + HANDLE thread_handle = m_host_thread.GetNativeThread().GetSystemHandle(); + do + { + previous_suspend_count = ::ResumeThread(thread_handle); + } while (previous_suspend_count > 0); + } + return true; +} diff --git a/source/Plugins/Process/Windows/Common/TargetThreadWindows.h b/source/Plugins/Process/Windows/Common/TargetThreadWindows.h new file mode 100644 index 000000000000..701b56b6d26a --- /dev/null +++ b/source/Plugins/Process/Windows/Common/TargetThreadWindows.h @@ -0,0 +1,50 @@ +//===-- TargetThreadWindows.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_TargetThreadWindows_H_ +#define liblldb_Plugins_Process_Windows_TargetThreadWindows_H_ + +//#include "ForwardDecl.h" +#include "lldb/lldb-forward.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Thread.h" + +namespace lldb_private +{ +class ProcessWindows; +class HostThread; +class StackFrame; + +class TargetThreadWindows : public lldb_private::Thread +{ + public: + TargetThreadWindows(ProcessWindows &process, const HostThread &thread); + virtual ~TargetThreadWindows(); + + // lldb_private::Thread overrides + void RefreshStateAfterStop() override; + void WillResume(lldb::StateType resume_state) override; + void DidStop() override; + bool CalculateStopInfo() override; + Unwind *GetUnwinder() override; + + bool DoResume(); + + HostThread + GetHostThread() const + { + return m_host_thread; + } + + private: + HostThread m_host_thread; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp new file mode 100644 index 000000000000..103cff4a2a56 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp @@ -0,0 +1,323 @@ +//===-- RegisterContextWindows_x64.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "RegisterContext_x86.h" +#include "RegisterContextWindows_x64.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +#define DEFINE_GPR(reg, alt) #reg, alt, 8, 0, eEncodingUint, eFormatHexUppercase +#define DEFINE_GPR_BIN(reg, alt) #reg, alt, 8, 0, eEncodingUint, eFormatBinary + +namespace +{ + +// This enum defines the layout of the global RegisterInfo array. This is necessary because +// lldb register sets are defined in terms of indices into the register array. As such, the +// order of RegisterInfos defined in global registers array must match the order defined here. +// When defining the register set layouts, these values can appear in an arbitrary order, and that +// determines the order that register values are displayed in a dump. +enum RegisterIndex +{ + eRegisterIndexRax, + eRegisterIndexRbx, + eRegisterIndexRcx, + eRegisterIndexRdx, + eRegisterIndexRdi, + eRegisterIndexRsi, + eRegisterIndexR8, + eRegisterIndexR9, + eRegisterIndexR10, + eRegisterIndexR11, + eRegisterIndexR12, + eRegisterIndexR13, + eRegisterIndexR14, + eRegisterIndexR15, + eRegisterIndexRbp, + eRegisterIndexRsp, + eRegisterIndexRip, + eRegisterIndexRflags +}; + +// Array of all register information supported by Windows x86 +RegisterInfo g_register_infos[] = { + // Macro auto defines most stuff eh_frame DWARF GENERIC + // GDB LLDB VALUE REGS INVALIDATE REGS + // ================================ ========================= ====================== ========================= + // =================== ================= ========== =============== + {DEFINE_GPR(rax, nullptr), + {dwarf_rax_x86_64, dwarf_rax_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rax_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rbx, nullptr), + {dwarf_rbx_x86_64, dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rbx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rcx, nullptr), + {dwarf_rcx_x86_64, dwarf_rcx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rcx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rdx, nullptr), + {dwarf_rdx_x86_64, dwarf_rdx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rdx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rdi, nullptr), + {dwarf_rdi_x86_64, dwarf_rdi_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rdi_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rsi, nullptr), + {dwarf_rsi_x86_64, dwarf_rsi_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rsi_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r8, nullptr), + {dwarf_r8_x86_64, dwarf_r8_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r8_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r9, nullptr), + {dwarf_r9_x86_64, dwarf_r9_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r9_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r10, nullptr), + {dwarf_r10_x86_64, dwarf_r10_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r10_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r11, nullptr), + {dwarf_r11_x86_64, dwarf_r11_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r11_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r12, nullptr), + {dwarf_r12_x86_64, dwarf_r12_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r12_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r13, nullptr), + {dwarf_r13_x86_64, dwarf_r13_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r13_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r14, nullptr), + {dwarf_r14_x86_64, dwarf_r14_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r14_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r15, nullptr), + {dwarf_r15_x86_64, dwarf_r15_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r15_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rbp, "fp"), + {dwarf_rbp_x86_64, dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, lldb_rbp_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rsp, "sp"), + {dwarf_rsp_x86_64, dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, lldb_rsp_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rip, "pc"), + {dwarf_rip_x86_64, dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, lldb_rip_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR_BIN(eflags, "flags"), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, lldb_rflags_x86_64}, + nullptr, + nullptr}, +}; + +// Array of lldb register numbers used to define the set of all General Purpose Registers +uint32_t g_gpr_reg_indices[] = {eRegisterIndexRax, eRegisterIndexRbx, eRegisterIndexRcx, eRegisterIndexRdx, + eRegisterIndexRdi, eRegisterIndexRsi, eRegisterIndexR8, eRegisterIndexR9, + eRegisterIndexR10, eRegisterIndexR11, eRegisterIndexR12, eRegisterIndexR13, + eRegisterIndexR14, eRegisterIndexR15, eRegisterIndexRbp, eRegisterIndexRsp, + eRegisterIndexRip, eRegisterIndexRflags}; + +RegisterSet g_register_sets[] = { + {"General Purpose Registers", "gpr", llvm::array_lengthof(g_gpr_reg_indices), g_gpr_reg_indices}, +}; +} + +//------------------------------------------------------------------ +// Constructors and Destructors +//------------------------------------------------------------------ +RegisterContextWindows_x64::RegisterContextWindows_x64(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows(thread, concrete_frame_idx) +{ +} + +RegisterContextWindows_x64::~RegisterContextWindows_x64() +{ +} + +size_t +RegisterContextWindows_x64::GetRegisterCount() +{ + return llvm::array_lengthof(g_register_infos); +} + +const RegisterInfo * +RegisterContextWindows_x64::GetRegisterInfoAtIndex(size_t reg) +{ + return &g_register_infos[reg]; +} + +size_t +RegisterContextWindows_x64::GetRegisterSetCount() +{ + return llvm::array_lengthof(g_register_sets); +} + +const RegisterSet * +RegisterContextWindows_x64::GetRegisterSet(size_t reg_set) +{ + return &g_register_sets[reg_set]; +} + +bool +RegisterContextWindows_x64::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ + if (!CacheAllRegisterValues()) + return false; + + switch (reg_info->kinds[eRegisterKindLLDB]) + { + case lldb_rax_x86_64: + reg_value.SetUInt64(m_context.Rax); + break; + case lldb_rbx_x86_64: + reg_value.SetUInt64(m_context.Rbx); + break; + case lldb_rcx_x86_64: + reg_value.SetUInt64(m_context.Rcx); + break; + case lldb_rdx_x86_64: + reg_value.SetUInt64(m_context.Rdx); + break; + case lldb_rdi_x86_64: + reg_value.SetUInt64(m_context.Rdi); + break; + case lldb_rsi_x86_64: + reg_value.SetUInt64(m_context.Rsi); + break; + case lldb_r8_x86_64: + reg_value.SetUInt64(m_context.R8); + break; + case lldb_r9_x86_64: + reg_value.SetUInt64(m_context.R9); + break; + case lldb_r10_x86_64: + reg_value.SetUInt64(m_context.R10); + break; + case lldb_r11_x86_64: + reg_value.SetUInt64(m_context.R11); + break; + case lldb_r12_x86_64: + reg_value.SetUInt64(m_context.R12); + break; + case lldb_r13_x86_64: + reg_value.SetUInt64(m_context.R13); + break; + case lldb_r14_x86_64: + reg_value.SetUInt64(m_context.R14); + break; + case lldb_r15_x86_64: + reg_value.SetUInt64(m_context.R15); + break; + case lldb_rbp_x86_64: + reg_value.SetUInt64(m_context.Rbp); + break; + case lldb_rsp_x86_64: + reg_value.SetUInt64(m_context.Rsp); + break; + case lldb_rip_x86_64: + reg_value.SetUInt64(m_context.Rip); + break; + case lldb_rflags_x86_64: + reg_value.SetUInt64(m_context.EFlags); + break; + } + return true; +} + +bool +RegisterContextWindows_x64::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + // Since we cannot only write a single register value to the inferior, we need to make sure + // our cached copy of the register values are fresh. Otherwise when writing EAX, for example, + // we may also overwrite some other register with a stale value. + if (!CacheAllRegisterValues()) + return false; + + switch (reg_info->kinds[eRegisterKindLLDB]) + { + case lldb_rax_x86_64: + m_context.Rax = reg_value.GetAsUInt64(); + break; + case lldb_rbx_x86_64: + m_context.Rbx = reg_value.GetAsUInt64(); + break; + case lldb_rcx_x86_64: + m_context.Rcx = reg_value.GetAsUInt64(); + break; + case lldb_rdx_x86_64: + m_context.Rdx = reg_value.GetAsUInt64(); + break; + case lldb_rdi_x86_64: + m_context.Rdi = reg_value.GetAsUInt64(); + break; + case lldb_rsi_x86_64: + m_context.Rsi = reg_value.GetAsUInt64(); + break; + case lldb_r8_x86_64: + m_context.R8 = reg_value.GetAsUInt64(); + break; + case lldb_r9_x86_64: + m_context.R9 = reg_value.GetAsUInt64(); + break; + case lldb_r10_x86_64: + m_context.R10 = reg_value.GetAsUInt64(); + break; + case lldb_r11_x86_64: + m_context.R11 = reg_value.GetAsUInt64(); + break; + case lldb_r12_x86_64: + m_context.R12 = reg_value.GetAsUInt64(); + break; + case lldb_r13_x86_64: + m_context.R13 = reg_value.GetAsUInt64(); + break; + case lldb_r14_x86_64: + m_context.R14 = reg_value.GetAsUInt64(); + break; + case lldb_r15_x86_64: + m_context.R15 = reg_value.GetAsUInt64(); + break; + case lldb_rbp_x86_64: + m_context.Rbp = reg_value.GetAsUInt64(); + break; + case lldb_rsp_x86_64: + m_context.Rsp = reg_value.GetAsUInt64(); + break; + case lldb_rip_x86_64: + m_context.Rip = reg_value.GetAsUInt64(); + break; + case lldb_rflags_x86_64: + m_context.EFlags = reg_value.GetAsUInt64(); + break; + } + + // Physically update the registers in the target process. + TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread); + return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} diff --git a/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h new file mode 100644 index 000000000000..e69179d99c6c --- /dev/null +++ b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h @@ -0,0 +1,48 @@ +//===-- RegisterContextWindows_x64.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindows_x64_H_ +#define liblldb_RegisterContextWindows_x64_H_ + +#include "lldb/lldb-forward.h" +#include "RegisterContextWindows.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindows_x64 : public RegisterContextWindows +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindows_x64(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindows_x64(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + size_t GetRegisterCount() override; + + const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; +} + +#endif // #ifndef liblldb_RegisterContextWindows_x64_H_ diff --git a/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp new file mode 100644 index 000000000000..e57e1effec9c --- /dev/null +++ b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp @@ -0,0 +1,178 @@ +//===-- RegisterContextWindows_x86.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "ProcessWindowsLog.h" +#include "RegisterContext_x86.h" +#include "RegisterContextWindows_x86.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +#define DEFINE_GPR(reg, alt) #reg, alt, 4, 0, eEncodingUint, eFormatHexUppercase +#define DEFINE_GPR_BIN(reg, alt) #reg, alt, 4, 0, eEncodingUint, eFormatBinary + +namespace +{ + +// This enum defines the layout of the global RegisterInfo array. This is necessary because +// lldb register sets are defined in terms of indices into the register array. As such, the +// order of RegisterInfos defined in global registers array must match the order defined here. +// When defining the register set layouts, these values can appear in an arbitrary order, and that +// determines the order that register values are displayed in a dump. +enum RegisterIndex +{ + eRegisterIndexEax, + eRegisterIndexEbx, + eRegisterIndexEcx, + eRegisterIndexEdx, + eRegisterIndexEdi, + eRegisterIndexEsi, + eRegisterIndexEbp, + eRegisterIndexEsp, + eRegisterIndexEip, + eRegisterIndexEflags +}; + +// Array of all register information supported by Windows x86 +RegisterInfo g_register_infos[] = +{ +// Macro auto defines most stuff eh_frame DWARF GENERIC GDB LLDB VALUE REGS INVALIDATE REGS +// ============================== ======================= =================== ========================= =================== ================= ========== =============== + { DEFINE_GPR(eax, nullptr), { ehframe_eax_i386, dwarf_eax_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_eax_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ebx, nullptr), { ehframe_ebx_i386, dwarf_ebx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_ebx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ecx, nullptr), { ehframe_ecx_i386, dwarf_ecx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_ecx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(edx, nullptr), { ehframe_edx_i386, dwarf_edx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_edx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(edi, nullptr), { ehframe_edi_i386, dwarf_edi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_edi_i386 }, nullptr, nullptr}, + { DEFINE_GPR(esi, nullptr), { ehframe_esi_i386, dwarf_esi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_esi_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ebp, "fp"), { ehframe_ebp_i386, dwarf_ebp_i386, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, lldb_ebp_i386 }, nullptr, nullptr}, + { DEFINE_GPR(esp, "sp"), { ehframe_esp_i386, dwarf_esp_i386, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, lldb_esp_i386 }, nullptr, nullptr}, + { DEFINE_GPR(eip, "pc"), { ehframe_eip_i386, dwarf_eip_i386, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, lldb_eip_i386 }, nullptr, nullptr}, + { DEFINE_GPR_BIN(eflags, "flags"), { ehframe_eflags_i386, dwarf_eflags_i386, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, lldb_eflags_i386}, nullptr, nullptr}, +}; + +// Array of lldb register numbers used to define the set of all General Purpose Registers +uint32_t g_gpr_reg_indices[] = +{ + eRegisterIndexEax, + eRegisterIndexEbx, + eRegisterIndexEcx, + eRegisterIndexEdx, + eRegisterIndexEdi, + eRegisterIndexEsi, + eRegisterIndexEbp, + eRegisterIndexEsp, + eRegisterIndexEip, + eRegisterIndexEflags +}; + +RegisterSet g_register_sets[] = { + {"General Purpose Registers", "gpr", llvm::array_lengthof(g_gpr_reg_indices), g_gpr_reg_indices}, +}; +} + +//------------------------------------------------------------------ +// Constructors and Destructors +//------------------------------------------------------------------ +RegisterContextWindows_x86::RegisterContextWindows_x86(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows(thread, concrete_frame_idx) +{ +} + +RegisterContextWindows_x86::~RegisterContextWindows_x86() +{ +} + +size_t +RegisterContextWindows_x86::GetRegisterCount() +{ + return llvm::array_lengthof(g_register_infos); +} + +const RegisterInfo * +RegisterContextWindows_x86::GetRegisterInfoAtIndex(size_t reg) +{ + return &g_register_infos[reg]; +} + +size_t +RegisterContextWindows_x86::GetRegisterSetCount() +{ + return llvm::array_lengthof(g_register_sets); +} + +const RegisterSet * +RegisterContextWindows_x86::GetRegisterSet(size_t reg_set) +{ + return &g_register_sets[reg_set]; +} + +bool +RegisterContextWindows_x86::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ + if (!CacheAllRegisterValues()) + return false; + + uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + switch (reg) + { + case lldb_eax_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EAX", m_context.Eax); + reg_value.SetUInt32(m_context.Eax); + break; + case lldb_ebx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBX", m_context.Ebx); + reg_value.SetUInt32(m_context.Ebx); + break; + case lldb_ecx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ECX", m_context.Ecx); + reg_value.SetUInt32(m_context.Ecx); + break; + case lldb_edx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDX", m_context.Edx); + reg_value.SetUInt32(m_context.Edx); + break; + case lldb_edi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDI", m_context.Edi); + reg_value.SetUInt32(m_context.Edi); + break; + case lldb_esi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESI", m_context.Esi); + reg_value.SetUInt32(m_context.Esi); + break; + case lldb_ebp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBP", m_context.Ebp); + reg_value.SetUInt32(m_context.Ebp); + break; + case lldb_esp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESP", m_context.Esp); + reg_value.SetUInt32(m_context.Esp); + break; + case lldb_eip_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EIP", m_context.Eip); + reg_value.SetUInt32(m_context.Eip); + break; + case lldb_eflags_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EFLAGS", m_context.EFlags); + reg_value.SetUInt32(m_context.EFlags); + break; + default: + WINWARN_IFALL(WINDOWS_LOG_REGISTERS, "Requested unknown register %u", reg); + break; + } + return true; +} diff --git a/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h new file mode 100644 index 000000000000..7d854ef64a5c --- /dev/null +++ b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h @@ -0,0 +1,48 @@ +//===-- RegisterContextWindows_x86.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindows_x86_H_ +#define liblldb_RegisterContextWindows_x86_H_ + +#include "lldb/lldb-forward.h" +#include "RegisterContextWindows.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindows_x86 : public RegisterContextWindows +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindows_x86(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindows_x86(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + size_t GetRegisterCount() override; + + const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindows_x86_H_ diff --git a/source/Plugins/Process/Windows/Live/CMakeLists.txt b/source/Plugins/Process/Windows/Live/CMakeLists.txt new file mode 100644 index 000000000000..bdb680ad0bee --- /dev/null +++ b/source/Plugins/Process/Windows/Live/CMakeLists.txt @@ -0,0 +1,24 @@ +include_directories(.) +include_directories(../../Utility) +include_directories(../Common) + +set(PROC_WINDOWS_SOURCES + DebuggerThread.cpp + LocalDebugDelegate.cpp + ProcessWindowsLive.cpp + TargetThreadWindowsLive.cpp + ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} + x86/RegisterContextWindowsLive_x86.cpp + ) +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} + x64/RegisterContextWindowsLive_x64.cpp + ) +endif() + +add_lldb_library(lldbPluginProcessWindows + ${PROC_WINDOWS_SOURCES} + ) diff --git a/source/Plugins/Process/Windows/Live/DebuggerThread.cpp b/source/Plugins/Process/Windows/Live/DebuggerThread.cpp new file mode 100644 index 000000000000..d058a412c896 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/DebuggerThread.cpp @@ -0,0 +1,546 @@ +//===-- DebuggerThread.DebuggerThread --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "IDebugDelegate.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/ThisThread.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/windows/HostProcessWindows.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Target/Process.h" + +#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb; +using namespace lldb_private; + +namespace +{ +struct DebugLaunchContext +{ + DebugLaunchContext(DebuggerThread *thread, const ProcessLaunchInfo &launch_info) + : m_thread(thread) + , m_launch_info(launch_info) + { + } + DebuggerThread *m_thread; + ProcessLaunchInfo m_launch_info; +}; + +struct DebugAttachContext +{ + DebugAttachContext(DebuggerThread *thread, lldb::pid_t pid, const ProcessAttachInfo &attach_info) + : m_thread(thread) + , m_pid(pid) + , m_attach_info(attach_info) + { + } + DebuggerThread *m_thread; + lldb::pid_t m_pid; + ProcessAttachInfo m_attach_info; +}; +} + +DebuggerThread::DebuggerThread(DebugDelegateSP debug_delegate) + : m_debug_delegate(debug_delegate) + , m_image_file(nullptr) + , m_debugging_ended_event(nullptr) + , m_is_shutting_down(false) + , m_pid_to_detach(0) + , m_detached(false) +{ + m_debugging_ended_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); +} + +DebuggerThread::~DebuggerThread() +{ + ::CloseHandle(m_debugging_ended_event); +} + +Error +DebuggerThread::DebugLaunch(const ProcessLaunchInfo &launch_info) +{ + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DebuggerThread::DebugLaunch launching '%s'", launch_info.GetExecutableFile().GetPath().c_str()); + + Error error; + DebugLaunchContext *context = new DebugLaunchContext(this, launch_info); + HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", + DebuggerThreadLaunchRoutine, context, &error)); + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "DebugLaunch couldn't launch debugger thread. %s", error.AsCString()); + } + + return error; +} + +Error +DebuggerThread::DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread::DebugAttach attaching to '%u'", (DWORD)pid); + + Error error; + DebugAttachContext *context = new DebugAttachContext(this, pid, attach_info); + HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", + DebuggerThreadAttachRoutine, context, &error)); + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DebugAttach couldn't attach to process '%u'. %s", (DWORD)pid, + error.AsCString()); + } + + return error; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadLaunchRoutine(void *data) +{ + DebugLaunchContext *context = static_cast<DebugLaunchContext *>(data); + lldb::thread_result_t result = context->m_thread->DebuggerThreadLaunchRoutine(context->m_launch_info); + delete context; + return result; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadAttachRoutine(void *data) +{ + DebugAttachContext *context = static_cast<DebugAttachContext *>(data); + lldb::thread_result_t result = + context->m_thread->DebuggerThreadAttachRoutine(context->m_pid, context->m_attach_info); + delete context; + return result; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info) +{ + // Grab a shared_ptr reference to this so that we know it won't get deleted until after the + // thread routine has exited. + std::shared_ptr<DebuggerThread> this_ref(shared_from_this()); + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread preparing to launch '%s' on background thread.", + launch_info.GetExecutableFile().GetPath().c_str()); + + Error error; + ProcessLauncherWindows launcher; + HostProcess process(launcher.LaunchProcess(launch_info, error)); + // If we couldn't create the process, notify waiters immediately. Otherwise enter the debug + // loop and wait until we get the create process debug notification. Note that if the process + // was created successfully, we can throw away the process handle we got from CreateProcess + // because Windows will give us another (potentially more useful?) handle when it sends us the + // CREATE_PROCESS_DEBUG_EVENT. + if (error.Success()) + DebugLoop(); + else + m_debug_delegate->OnDebuggerError(error, 0); + + return 0; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadAttachRoutine(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + // Grab a shared_ptr reference to this so that we know it won't get deleted until after the + // thread routine has exited. + std::shared_ptr<DebuggerThread> this_ref(shared_from_this()); + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread preparing to attach to process '%u' on background thread.", + (DWORD)pid); + + if (!DebugActiveProcess((DWORD)pid)) + { + Error error(::GetLastError(), eErrorTypeWin32); + m_debug_delegate->OnDebuggerError(error, 0); + return 0; + } + + // The attach was successful, enter the debug loop. From here on out, this is no different than + // a create process operation, so all the same comments in DebugLaunch should apply from this + // point out. + DebugLoop(); + + return 0; +} + +Error +DebuggerThread::StopDebugging(bool terminate) +{ + Error error; + + lldb::pid_t pid = m_process.GetProcessId(); + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "StopDebugging('%s') called (inferior=%I64u).", + (terminate ? "true" : "false"), pid); + + // Set m_is_shutting_down to true if it was false. Return if it was already true. + bool expected = false; + if (!m_is_shutting_down.compare_exchange_strong(expected, true)) + return error; + + // Make a copy of the process, since the termination sequence will reset + // DebuggerThread's internal copy and it needs to remain open for the Wait operation. + HostProcess process_copy = m_process; + lldb::process_t handle = m_process.GetNativeProcess().GetSystemHandle(); + + if (terminate) + { + // Initiate the termination before continuing the exception, so that the next debug + // event we get is the exit process event, and not some other event. + BOOL terminate_suceeded = TerminateProcess(handle, 0); + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "StopDebugging called TerminateProcess(0x%p, 0) (inferior=%I64u), success='%s'", + handle, pid, (terminate_suceeded ? "true" : "false")); + } + + // If we're stuck waiting for an exception to continue (e.g. the user is at a breakpoint + // messing around in the debugger), continue it now. But only AFTER calling TerminateProcess + // to make sure that the very next call to WaitForDebugEvent is an exit process event. + if (m_active_exception.get()) + { + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION, + "StopDebugging masking active exception"); + + ContinueAsyncException(ExceptionResult::MaskException); + } + + if (!terminate) + { + // Indicate that we want to detach. + m_pid_to_detach = GetProcess().GetProcessId(); + + // Force a fresh break so that the detach can happen from the debugger thread. + if (!::DebugBreakProcess(GetProcess().GetNativeProcess().GetSystemHandle())) + { + error.SetError(::GetLastError(), eErrorTypeWin32); + } + } + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging waiting for detach from process %u to complete.", pid); + + DWORD wait_result = WaitForSingleObject(m_debugging_ended_event, 5000); + if (wait_result != WAIT_OBJECT_0) + { + error.SetError(GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging WaitForSingleObject(0x%p, 5000) returned %u", + m_debugging_ended_event, wait_result); + } + else + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging detach from process %u completed successfully.", pid); + } + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "StopDebugging encountered an error while trying to stop process %u. %s", + pid, error.AsCString()); + } + return error; +} + +void +DebuggerThread::ContinueAsyncException(ExceptionResult result) +{ + if (!m_active_exception.get()) + return; + + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION, + "ContinueAsyncException called for inferior process %I64u, broadcasting.", + m_process.GetProcessId()); + + m_active_exception.reset(); + m_exception_pred.SetValue(result, eBroadcastAlways); +} + +void +DebuggerThread::FreeProcessHandles() +{ + m_process = HostProcess(); + m_main_thread = HostThread(); + if (m_image_file) + { + ::CloseHandle(m_image_file); + m_image_file = nullptr; + } +} + +void +DebuggerThread::DebugLoop() +{ + DEBUG_EVENT dbe = {0}; + bool should_debug = true; + WINLOG_IFALL(WINDOWS_LOG_EVENT, "Entering WaitForDebugEvent loop"); + while (should_debug) + { + WINLOGD_IFALL(WINDOWS_LOG_EVENT, "Calling WaitForDebugEvent"); + BOOL wait_result = WaitForDebugEvent(&dbe, INFINITE); + if (wait_result) + { + DWORD continue_status = DBG_CONTINUE; + switch (dbe.dwDebugEventCode) + { + case EXCEPTION_DEBUG_EVENT: + { + ExceptionResult status = HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId); + + if (status == ExceptionResult::MaskException) + continue_status = DBG_CONTINUE; + else if (status == ExceptionResult::SendToApplication) + continue_status = DBG_EXCEPTION_NOT_HANDLED; + + break; + } + case CREATE_THREAD_DEBUG_EVENT: + continue_status = HandleCreateThreadEvent(dbe.u.CreateThread, dbe.dwThreadId); + break; + case CREATE_PROCESS_DEBUG_EVENT: + continue_status = HandleCreateProcessEvent(dbe.u.CreateProcessInfo, dbe.dwThreadId); + break; + case EXIT_THREAD_DEBUG_EVENT: + continue_status = HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId); + break; + case EXIT_PROCESS_DEBUG_EVENT: + continue_status = HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId); + should_debug = false; + break; + case LOAD_DLL_DEBUG_EVENT: + continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId); + break; + case UNLOAD_DLL_DEBUG_EVENT: + continue_status = HandleUnloadDllEvent(dbe.u.UnloadDll, dbe.dwThreadId); + break; + case OUTPUT_DEBUG_STRING_EVENT: + continue_status = HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId); + break; + case RIP_EVENT: + continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId); + if (dbe.u.RipInfo.dwType == SLE_ERROR) + should_debug = false; + break; + } + + WINLOGD_IFALL(WINDOWS_LOG_EVENT, "DebugLoop calling ContinueDebugEvent(%u, %u, %u) on thread %u.", + dbe.dwProcessId, dbe.dwThreadId, continue_status, ::GetCurrentThreadId()); + + ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status); + + if (m_detached) + { + should_debug = false; + } + } + else + { + WINERR_IFALL(WINDOWS_LOG_EVENT, + "DebugLoop returned FALSE from WaitForDebugEvent. Error = %u", + ::GetCurrentThreadId(), ::GetLastError()); + + should_debug = false; + } + } + FreeProcessHandles(); + + WINLOG_IFALL(WINDOWS_LOG_EVENT, "WaitForDebugEvent loop completed, exiting."); + SetEvent(m_debugging_ended_event); +} + +ExceptionResult +DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id) +{ + if (m_is_shutting_down) + { + // A breakpoint that occurs while `m_pid_to_detach` is non-zero is a magic exception that + // we use simply to wake up the DebuggerThread so that we can close out the debug loop. + if (m_pid_to_detach != 0 && info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) + { + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_PROCESS, + "Breakpoint exception is cue to detach from process 0x%x", + m_pid_to_detach); + ::DebugActiveProcessStop(m_pid_to_detach); + m_detached = true; + } + + // Don't perform any blocking operations while we're shutting down. That will + // cause TerminateProcess -> WaitForSingleObject to time out. + return ExceptionResult::SendToApplication; + } + + bool first_chance = (info.dwFirstChance != 0); + + m_active_exception.reset(new ExceptionRecord(info.ExceptionRecord, thread_id)); + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION, + "HandleExceptionEvent encountered %s chance exception 0x%x on thread 0x%x", + first_chance ? "first" : "second", info.ExceptionRecord.ExceptionCode, thread_id); + + ExceptionResult result = m_debug_delegate->OnDebugException(first_chance, + *m_active_exception); + m_exception_pred.SetValue(result, eBroadcastNever); + + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION, + "DebuggerThread::HandleExceptionEvent waiting for ExceptionPred != BreakInDebugger"); + + m_exception_pred.WaitForValueNotEqualTo(ExceptionResult::BreakInDebugger, result); + + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION, + "DebuggerThread::HandleExceptionEvent got ExceptionPred = %u", + m_exception_pred.GetValue()); + + return result; +} + +DWORD +DebuggerThread::HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, + "HandleCreateThreadEvent Thread 0x%x spawned in process %I64u", + thread_id, m_process.GetProcessId()); + HostThread thread(info.hThread); + thread.GetNativeThread().SetOwnsHandle(false); + m_debug_delegate->OnCreateThread(thread); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, DWORD thread_id) +{ + uint32_t process_id = ::GetProcessId(info.hProcess); + + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_PROCESS, "HandleCreateProcessEvent process %u spawned", process_id); + + std::string thread_name; + llvm::raw_string_ostream name_stream(thread_name); + name_stream << "lldb.plugin.process-windows.slave[" << process_id << "]"; + name_stream.flush(); + ThisThread::SetName(thread_name.c_str()); + + // info.hProcess and info.hThread are closed automatically by Windows when + // EXIT_PROCESS_DEBUG_EVENT is received. + m_process = HostProcess(info.hProcess); + ((HostProcessWindows &)m_process.GetNativeProcess()).SetOwnsHandle(false); + m_main_thread = HostThread(info.hThread); + m_main_thread.GetNativeThread().SetOwnsHandle(false); + m_image_file = info.hFile; + + lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfImage); + m_debug_delegate->OnDebuggerConnected(load_addr); + + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, + "HandleExitThreadEvent Thread %u exited with code %u in process %I64u", + thread_id, info.dwExitCode, m_process.GetProcessId()); + m_debug_delegate->OnExitThread(thread_id, info.dwExitCode); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, + "HandleExitProcessEvent process %I64u exited with code %u", + m_process.GetProcessId(), info.dwExitCode); + + m_debug_delegate->OnExitProcess(info.dwExitCode); + + FreeProcessHandles(); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread_id) +{ + if (info.hFile == nullptr) + { + // Not sure what this is, so just ignore it. + WINWARN_IFALL(WINDOWS_LOG_EVENT, "Inferior %I64u - HandleLoadDllEvent has a NULL file handle, returning...", + m_process.GetProcessId()); + return DBG_CONTINUE; + } + + std::vector<char> buffer(1); + DWORD required_size = GetFinalPathNameByHandle(info.hFile, &buffer[0], 0, VOLUME_NAME_DOS); + if (required_size > 0) + { + buffer.resize(required_size + 1); + required_size = GetFinalPathNameByHandle(info.hFile, &buffer[0], required_size + 1, VOLUME_NAME_DOS); + llvm::StringRef path_str(&buffer[0]); + const char *path = path_str.data(); + if (path_str.startswith("\\\\?\\")) + path += 4; + + FileSpec file_spec(path, false); + ModuleSpec module_spec(file_spec); + lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll); + + WINLOG_IFALL(WINDOWS_LOG_EVENT, "Inferior %I64u - HandleLoadDllEvent DLL '%s' loaded at address 0x%p...", + m_process.GetProcessId(), path, info.lpBaseOfDll); + + m_debug_delegate->OnLoadDll(module_spec, load_addr); + } + else + { + WINERR_IFALL(WINDOWS_LOG_EVENT, + "Inferior %I64u - HandleLoadDllEvent Error %u occurred calling GetFinalPathNameByHandle", + m_process.GetProcessId(), ::GetLastError()); + } + // Windows does not automatically close info.hFile, so we need to do it. + ::CloseHandle(info.hFile); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFALL(WINDOWS_LOG_EVENT, + "HandleUnloadDllEvent process %I64u unloading DLL at addr 0x%p.", + m_process.GetProcessId(), info.lpBaseOfDll); + + m_debug_delegate->OnUnloadDll(reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll)); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info, DWORD thread_id) +{ + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleRipEvent(const RIP_INFO &info, DWORD thread_id) +{ + WINERR_IFALL(WINDOWS_LOG_EVENT, + "HandleRipEvent encountered error %u (type=%u) in process %I64u thread %u", + info.dwError, info.dwType, m_process.GetProcessId(), thread_id); + + Error error(info.dwError, eErrorTypeWin32); + m_debug_delegate->OnDebuggerError(error, info.dwType); + + return DBG_CONTINUE; +} diff --git a/source/Plugins/Process/Windows/Live/DebuggerThread.h b/source/Plugins/Process/Windows/Live/DebuggerThread.h new file mode 100644 index 000000000000..6a2619413950 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/DebuggerThread.h @@ -0,0 +1,100 @@ +//===-- DebuggerThread.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_DebuggerThread_H_ +#define liblldb_Plugins_Process_Windows_DebuggerThread_H_ + +#include <atomic> +#include <memory> + +#include "ForwardDecl.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/windows/windows.h" + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +// DebuggerThread +// +// Debugs a single process, notifying listeners as appropriate when interesting +// things occur. +//---------------------------------------------------------------------- +class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> +{ + public: + DebuggerThread(DebugDelegateSP debug_delegate); + virtual ~DebuggerThread(); + + Error DebugLaunch(const ProcessLaunchInfo &launch_info); + Error DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info); + + HostProcess + GetProcess() const + { + return m_process; + } + HostThread + GetMainThread() const + { + return m_main_thread; + } + std::weak_ptr<ExceptionRecord> + GetActiveException() + { + return m_active_exception; + } + + Error StopDebugging(bool terminate); + + void ContinueAsyncException(ExceptionResult result); + + private: + void FreeProcessHandles(); + void DebugLoop(); + ExceptionResult HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info, DWORD thread_id); + DWORD HandleRipEvent(const RIP_INFO &info, DWORD thread_id); + + DebugDelegateSP m_debug_delegate; + + HostProcess m_process; // The process being debugged. + HostThread m_main_thread; // The main thread of the inferior. + HANDLE m_image_file; // The image file of the process being debugged. + + ExceptionRecordSP m_active_exception; // The current exception waiting to be handled + + Predicate<ExceptionResult> m_exception_pred; // A predicate which gets signalled when an exception + // is finished processing and the debug loop can be + // continued. + + HANDLE m_debugging_ended_event; // An event which gets signalled by the debugger thread when it + // exits the debugger loop and is detached from the inferior. + + std::atomic<DWORD> m_pid_to_detach; // Signals the loop to detach from the process (specified by pid). + std::atomic<bool> m_is_shutting_down; // Signals the debug loop to stop processing certain types of + // events that block shutdown. + bool m_detached; // Indicates we've detached from the inferior process and the debug loop can exit. + + static lldb::thread_result_t DebuggerThreadLaunchRoutine(void *data); + lldb::thread_result_t DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info); + static lldb::thread_result_t DebuggerThreadAttachRoutine(void *data); + lldb::thread_result_t DebuggerThreadAttachRoutine(lldb::pid_t pid, const ProcessAttachInfo &launch_info); +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/ForwardDecl.h b/source/Plugins/Process/Windows/Live/ForwardDecl.h new file mode 100644 index 000000000000..a782597555e1 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ForwardDecl.h @@ -0,0 +1,41 @@ +//===-- ForwardDecl.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_ForwardDecl_H_ +#define liblldb_Plugins_Process_Windows_ForwardDecl_H_ + +#include <memory> + +// ExceptionResult is returned by the debug delegate to specify how it processed +// the exception. +enum class ExceptionResult +{ + BreakInDebugger, // Break in the debugger and give the user a chance to interact with + // the program before continuing. + MaskException, // Eat the exception and don't let the application know it occurred. + SendToApplication // Send the exception to the application to be handled as if there were + // no debugger attached. +}; + +namespace lldb_private +{ + +class ProcessWindows; + +class IDebugDelegate; +class DebuggerThread; +class ExceptionRecord; + +typedef std::shared_ptr<IDebugDelegate> DebugDelegateSP; +typedef std::shared_ptr<DebuggerThread> DebuggerThreadSP; +typedef std::shared_ptr<ExceptionRecord> ExceptionRecordSP; +typedef std::unique_ptr<ExceptionRecord> ExceptionRecordUP; +} + +#endif
\ No newline at end of file diff --git a/source/Plugins/Process/Windows/Live/IDebugDelegate.h b/source/Plugins/Process/Windows/Live/IDebugDelegate.h new file mode 100644 index 000000000000..0e7849bb8ffe --- /dev/null +++ b/source/Plugins/Process/Windows/Live/IDebugDelegate.h @@ -0,0 +1,46 @@ +//===-- IDebugDelegate.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_Plugins_Process_Windows_IDebugDelegate_H_
+#define liblldb_Plugins_Process_Windows_IDebugDelegate_H_
+
+#include "ForwardDecl.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-types.h"
+#include <string>
+
+namespace lldb_private
+{
+class Error;
+class HostThread;
+
+//----------------------------------------------------------------------
+// IDebugDelegate
+//
+// IDebugDelegate defines an interface which allows implementors to receive
+// notification of events that happen in a debugged process.
+//----------------------------------------------------------------------
+class IDebugDelegate
+{
+ public:
+ virtual ~IDebugDelegate() {}
+
+ virtual void OnExitProcess(uint32_t exit_code) = 0;
+ virtual void OnDebuggerConnected(lldb::addr_t image_base) = 0;
+ virtual ExceptionResult OnDebugException(bool first_chance, const ExceptionRecord &record) = 0;
+ virtual void OnCreateThread(const HostThread &thread) = 0;
+ virtual void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) = 0;
+ virtual void OnLoadDll(const ModuleSpec &module_spec, lldb::addr_t module_addr) = 0;
+ virtual void OnUnloadDll(lldb::addr_t module_addr) = 0;
+ virtual void OnDebugString(const std::string &string) = 0;
+ virtual void OnDebuggerError(const Error &error, uint32_t type) = 0;
+};
+}
+
+#endif
diff --git a/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp new file mode 100644 index 000000000000..a0ac9725c756 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp @@ -0,0 +1,91 @@ +//===-- LocalDebugDelegate.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LocalDebugDelegate.h" +#include "ProcessWindowsLive.h" + +using namespace lldb; +using namespace lldb_private; + +LocalDebugDelegate::LocalDebugDelegate(ProcessWP process) + : m_process(process) +{ +} + +void +LocalDebugDelegate::OnExitProcess(uint32_t exit_code) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnExitProcess(exit_code); +} + +void +LocalDebugDelegate::OnDebuggerConnected(lldb::addr_t image_base) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnDebuggerConnected(image_base); +} + +ExceptionResult +LocalDebugDelegate::OnDebugException(bool first_chance, const ExceptionRecord &record) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + return process->OnDebugException(first_chance, record); + else + return ExceptionResult::MaskException; +} + +void +LocalDebugDelegate::OnCreateThread(const HostThread &thread) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnCreateThread(thread); +} + +void +LocalDebugDelegate::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnExitThread(thread_id, exit_code); +} + +void +LocalDebugDelegate::OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnLoadDll(module_spec, module_addr); +} + +void +LocalDebugDelegate::OnUnloadDll(lldb::addr_t module_addr) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnUnloadDll(module_addr); +} + +void +LocalDebugDelegate::OnDebugString(const std::string &string) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnDebugString(string); +} + +void +LocalDebugDelegate::OnDebuggerError(const Error &error, uint32_t type) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnDebuggerError(error, type); +} + +ProcessWindowsLiveSP +LocalDebugDelegate::GetProcessPointer() +{ + ProcessSP process = m_process.lock(); + return std::static_pointer_cast<ProcessWindowsLive>(process); +} diff --git a/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h new file mode 100644 index 000000000000..ec2f9418142f --- /dev/null +++ b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h @@ -0,0 +1,68 @@ +//===-- LocalDebugDelegate.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_LocalDebugDelegate_H_ +#define liblldb_Plugins_Process_Windows_LocalDebugDelegate_H_ + +#include <memory> + +#include "IDebugDelegate.h" + +#include "lldb/lldb-forward.h" + +namespace lldb_private +{ + +class ProcessWindowsLive; +typedef std::shared_ptr<ProcessWindowsLive> ProcessWindowsLiveSP; + +//---------------------------------------------------------------------- +// LocalDebugDelegate +// +// LocalDebugDelegate creates a connection between a ProcessWindowsLive and the +// debug driver. This serves to decouple ProcessWindowsLive from the debug driver. +// It would be possible to get a similar decoupling by just having +// ProcessWindowsLive implement this interface directly. There are two reasons why +// we don't do this: +// +// 1) In the future when we add support for local debugging through LLGS, and we +// go through the Native*Protocol interface, it is likely we will need the +// additional flexibility provided by this sort of adapter pattern. +// 2) LLDB holds a shared_ptr to the ProcessWindows, and our driver thread +// needs access to it as well. To avoid a race condition, we want to make +// sure that we're also holding onto a shared_ptr. +// lldb_private::Process supports enable_shared_from_this, but that gives us +// a ProcessSP (which is exactly what we are trying to decouple from the +// driver), so this adapter serves as a way to transparently hold the +// ProcessSP while still keeping it decoupled from the driver. +//---------------------------------------------------------------------- +class LocalDebugDelegate : public IDebugDelegate +{ + public: + explicit LocalDebugDelegate(lldb::ProcessWP process); + + void OnExitProcess(uint32_t exit_code) override; + void OnDebuggerConnected(lldb::addr_t image_base) override; + ExceptionResult OnDebugException(bool first_chance, const ExceptionRecord &record) override; + void OnCreateThread(const HostThread &thread) override; + void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override; + void OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) override; + void OnUnloadDll(lldb::addr_t module_addr) override; + void OnDebugString(const std::string &message) override; + void OnDebuggerError(const Error &error, uint32_t type) override; + + private: + ProcessWindowsLiveSP + GetProcessPointer(); + + lldb::ProcessWP m_process; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp new file mode 100644 index 000000000000..55102cc12ebb --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp @@ -0,0 +1,989 @@ +//===-- ProcessWindowsLive.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Windows includes +#include "lldb/Host/windows/windows.h" +#include <psapi.h> + +// C++ Includes +#include <list> +#include <mutex> +#include <set> +#include <vector> + +// Other libraries and framework includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/HostNativeProcessBase.h" +#include "lldb/Host/HostNativeThreadBase.h" +#include "lldb/Host/MonitoringProcessLauncher.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/FileAction.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" + +#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h" + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "LocalDebugDelegate.h" +#include "ProcessWindowsLive.h" +#include "TargetThreadWindowsLive.h" + +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb; +using namespace lldb_private; + +#define BOOL_STR(b) ((b) ? "true" : "false") + +namespace +{ + +std::string +GetProcessExecutableName(HANDLE process_handle) +{ + std::vector<char> file_name; + DWORD file_name_size = MAX_PATH; // first guess, not an absolute limit + DWORD copied = 0; + do + { + file_name_size *= 2; + file_name.resize(file_name_size); + copied = ::GetModuleFileNameEx(process_handle, NULL, file_name.data(), file_name_size); + } while (copied >= file_name_size); + file_name.resize(copied); + return std::string(file_name.begin(), file_name.end()); +} + +std::string +GetProcessExecutableName(DWORD pid) +{ + std::string file_name; + HANDLE process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (process_handle != NULL) + { + file_name = GetProcessExecutableName(process_handle); + ::CloseHandle(process_handle); + } + return file_name; +} + +} // anonymous namespace + +namespace lldb_private +{ + +// We store a pointer to this class in the ProcessWindows, so that we don't expose Windows +// OS specific types and implementation details from a public header file. +class ProcessWindowsData +{ + public: + ProcessWindowsData(bool stop_at_entry) + : m_stop_at_entry(stop_at_entry) + , m_initial_stop_event(nullptr) + , m_initial_stop_received(false) + { + m_initial_stop_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); + } + + ~ProcessWindowsData() { ::CloseHandle(m_initial_stop_event); } + + lldb_private::Error m_launch_error; + lldb_private::DebuggerThreadSP m_debugger; + StopInfoSP m_pending_stop_info; + HANDLE m_initial_stop_event; + bool m_stop_at_entry; + bool m_initial_stop_received; + std::map<lldb::tid_t, HostThread> m_new_threads; + std::set<lldb::tid_t> m_exited_threads; +}; +} +//------------------------------------------------------------------------------ +// Static functions. + +ProcessSP +ProcessWindowsLive::CreateInstance(lldb::TargetSP target_sp, Listener &listener, const FileSpec *) +{ + return ProcessSP(new ProcessWindowsLive(target_sp, listener)); +} + +void +ProcessWindowsLive::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() + { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + }); +} + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessWindowsLive::ProcessWindowsLive(lldb::TargetSP target_sp, Listener &listener) + : lldb_private::ProcessWindows(target_sp, listener) +{ +} + +ProcessWindowsLive::~ProcessWindowsLive() +{ +} + +void +ProcessWindowsLive::Terminate() +{ +} + +lldb_private::ConstString +ProcessWindowsLive::GetPluginNameStatic() +{ + static ConstString g_name("windows"); + return g_name; +} + +const char * +ProcessWindowsLive::GetPluginDescriptionStatic() +{ + return "Process plugin for Windows"; +} + +Error +ProcessWindowsLive::EnableBreakpointSite(BreakpointSite *bp_site) +{ + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS, "EnableBreakpointSite called with bp_site 0x%p " + "(id=%d, addr=0x%x)", + bp_site->GetID(), bp_site->GetLoadAddress()); + + Error error = EnableSoftwareBreakpoint(bp_site); + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_BREAKPOINTS, "EnableBreakpointSite failed. %s", error.AsCString()); + } + return error; +} + +Error +ProcessWindowsLive::DisableBreakpointSite(BreakpointSite *bp_site) +{ + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS, "DisableBreakpointSite called with bp_site 0x%p " + "(id=%d, addr=0x%x)", + bp_site->GetID(), bp_site->GetLoadAddress()); + + Error error = DisableSoftwareBreakpoint(bp_site); + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_BREAKPOINTS, "DisableBreakpointSite failed. %s", error.AsCString()); + } + return error; +} + +bool +ProcessWindowsLive::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + // Add all the threads that were previously running and for which we did not detect a thread + // exited event. + int new_size = 0; + int continued_threads = 0; + int exited_threads = 0; + int new_threads = 0; + + for (ThreadSP old_thread : old_thread_list.Threads()) + { + lldb::tid_t old_thread_id = old_thread->GetID(); + auto exited_thread_iter = m_session_data->m_exited_threads.find(old_thread_id); + if (exited_thread_iter == m_session_data->m_exited_threads.end()) + { + new_thread_list.AddThread(old_thread); + ++new_size; + ++continued_threads; + WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u was running and is still running.", + old_thread_id); + } + else + { + WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u was running and has exited.", + old_thread_id); + ++exited_threads; + } + } + + // Also add all the threads that are new since the last time we broke into the debugger. + for (const auto &thread_info : m_session_data->m_new_threads) + { + ThreadSP thread(new TargetThreadWindowsLive(*this, thread_info.second)); + thread->SetID(thread_info.first); + new_thread_list.AddThread(thread); + ++new_size; + ++new_threads; + WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u is new since last update.", thread_info.first); + } + + WINLOG_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - %d new threads, %d old threads, %d exited threads.", + new_threads, continued_threads, exited_threads); + + m_session_data->m_new_threads.clear(); + m_session_data->m_exited_threads.clear(); + + return new_size > 0; +} + +Error +ProcessWindowsLive::DoLaunch(Module *exe_module, + ProcessLaunchInfo &launch_info) +{ + // Even though m_session_data is accessed here, it is before a debugger thread has been + // kicked off. So there's no race conditions, and it shouldn't be necessary to acquire + // the mutex. + + Error result; + if (!launch_info.GetFlags().Test(eLaunchFlagDebug)) + { + StreamString stream; + stream.Printf("ProcessWindows unable to launch '%s'. ProcessWindows can only be used for debug launches.", + launch_info.GetExecutableFile().GetPath().c_str()); + std::string message = stream.GetString(); + result.SetErrorString(message.c_str()); + + WINERR_IFALL(WINDOWS_LOG_PROCESS, message.c_str()); + return result; + } + + bool stop_at_entry = launch_info.GetFlags().Test(eLaunchFlagStopAtEntry); + m_session_data.reset(new ProcessWindowsData(stop_at_entry)); + + SetPrivateState(eStateLaunching); + DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); + m_session_data->m_debugger.reset(new DebuggerThread(delegate)); + DebuggerThreadSP debugger = m_session_data->m_debugger; + + // Kick off the DebugLaunch asynchronously and wait for it to complete. + result = debugger->DebugLaunch(launch_info); + if (result.Fail()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s", + launch_info.GetExecutableFile().GetPath().c_str(), result.AsCString()); + return result; + } + + HostProcess process; + Error error = WaitForDebuggerConnection(debugger, process); + if (error.Fail()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s", + launch_info.GetExecutableFile().GetPath().c_str(), error.AsCString()); + return error; + } + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch successfully launched '%s'", + launch_info.GetExecutableFile().GetPath().c_str()); + + // We've hit the initial stop. If eLaunchFlagsStopAtEntry was specified, the private state + // should already be set to eStateStopped as a result of hitting the initial breakpoint. If + // it was not set, the breakpoint should have already been resumed from and the private state + // should already be eStateRunning. + launch_info.SetProcessID(process.GetProcessId()); + SetID(process.GetProcessId()); + + return result; +} + +Error +ProcessWindowsLive::DoAttachToProcessWithID(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + m_session_data.reset(new ProcessWindowsData(!attach_info.GetContinueOnceAttached())); + + DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); + DebuggerThreadSP debugger(new DebuggerThread(delegate)); + + m_session_data->m_debugger = debugger; + + DWORD process_id = static_cast<DWORD>(pid); + Error error = debugger->DebugAttach(process_id, attach_info); + if (error.Fail()) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DoAttachToProcessWithID encountered an error occurred initiating the asynchronous attach. %s", + error.AsCString()); + return error; + } + + HostProcess process; + error = WaitForDebuggerConnection(debugger, process); + if (error.Fail()) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DoAttachToProcessWithID encountered an error waiting for the debugger to connect. %s", + error.AsCString()); + return error; + } + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoAttachToProcessWithID successfully attached to process with pid=%u", + process_id); + + // We've hit the initial stop. If eLaunchFlagsStopAtEntry was specified, the private state + // should already be set to eStateStopped as a result of hitting the initial breakpoint. If + // it was not set, the breakpoint should have already been resumed from and the private state + // should already be eStateRunning. + SetID(process.GetProcessId()); + return error; +} + +Error +ProcessWindowsLive::WaitForDebuggerConnection(DebuggerThreadSP debugger, HostProcess &process) +{ + Error result; + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_BREAKPOINTS, "WaitForDebuggerConnection Waiting for loader breakpoint."); + + // Block this function until we receive the initial stop from the process. + if (::WaitForSingleObject(m_session_data->m_initial_stop_event, INFINITE) == WAIT_OBJECT_0) + { + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_BREAKPOINTS, "WaitForDebuggerConnection hit loader breakpoint, returning."); + + process = debugger->GetProcess(); + return m_session_data->m_launch_error; + } + else + return Error(::GetLastError(), eErrorTypeWin32); +} + +Error +ProcessWindowsLive::DoResume() +{ + llvm::sys::ScopedLock lock(m_mutex); + Error error; + + StateType private_state = GetPrivateState(); + if (private_state == eStateStopped || private_state == eStateCrashed) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoResume called for process %I64u while state is %u. Resuming...", + m_session_data->m_debugger->GetProcess().GetProcessId(), GetPrivateState()); + + ExceptionRecordSP active_exception = + m_session_data->m_debugger->GetActiveException().lock(); + if (active_exception) + { + // Resume the process and continue processing debug events. Mask + // the exception so that from the process's view, there is no + // indication that anything happened. + m_session_data->m_debugger->ContinueAsyncException( + ExceptionResult::MaskException); + } + + WINLOG_IFANY(WINDOWS_LOG_PROCESS | WINDOWS_LOG_THREAD, "DoResume resuming %u threads.", + m_thread_list.GetSize()); + + for (int i = 0; i < m_thread_list.GetSize(); ++i) + { + auto thread = std::static_pointer_cast<TargetThreadWindowsLive>( + m_thread_list.GetThreadAtIndex(i)); + thread->DoResume(); + } + + SetPrivateState(eStateRunning); + } + else + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoResume called for process %I64u but state is %u. Returning...", + m_session_data->m_debugger->GetProcess().GetProcessId(), GetPrivateState()); + } + return error; +} + + +//------------------------------------------------------------------------------ +// ProcessInterface protocol. + +lldb_private::ConstString +ProcessWindowsLive::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessWindowsLive::GetPluginVersion() +{ + return 1; +} + +Error +ProcessWindowsLive::DoDetach(bool keep_stopped) +{ + DebuggerThreadSP debugger_thread; + StateType private_state; + { + // Acquire the lock only long enough to get the DebuggerThread. + // StopDebugging() will trigger a call back into ProcessWindows which + // will also acquire the lock. Thus we have to release the lock before + // calling StopDebugging(). + llvm::sys::ScopedLock lock(m_mutex); + + private_state = GetPrivateState(); + + if (!m_session_data) + { + WINWARN_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called while state = %u, but there is no active session.", + private_state); + return Error(); + } + + debugger_thread = m_session_data->m_debugger; + } + + Error error; + if (private_state != eStateExited && private_state != eStateDetached) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called for process %I64u while state = %u. Detaching...", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + error = debugger_thread->StopDebugging(false); + if (error.Success()) + { + SetPrivateState(eStateDetached); + } + + // By the time StopDebugging returns, there is no more debugger thread, so + // we can be assured that no other thread will race for the session data. + m_session_data.reset(); + } + else + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "DoDetach called for process %I64u while state = %u, but cannot destroy in this state.", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + } + + return error; +} + +Error +ProcessWindowsLive::DoDestroy() +{ + DebuggerThreadSP debugger_thread; + StateType private_state; + { + // Acquire this lock inside an inner scope, only long enough to get the DebuggerThread. + // StopDebugging() will trigger a call back into ProcessWindows which will acquire the lock + // again, so we need to not deadlock. + llvm::sys::ScopedLock lock(m_mutex); + + private_state = GetPrivateState(); + + if (!m_session_data) + { + WINWARN_IFALL(WINDOWS_LOG_PROCESS, "DoDestroy called while state = %u, but there is no active session.", + private_state); + return Error(); + } + + debugger_thread = m_session_data->m_debugger; + } + + Error error; + if (private_state != eStateExited && private_state != eStateDetached) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoDestroy called for process %I64u while state = %u. Shutting down...", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + error = debugger_thread->StopDebugging(true); + + // By the time StopDebugging returns, there is no more debugger thread, so + // we can be assured that no other thread will race for the session data. + m_session_data.reset(); + } + else + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "DoDestroy called for process %I64u while state = %u, but cannot destroy in this state.", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + } + + return error; +} + +void +ProcessWindowsLive::RefreshStateAfterStop() +{ + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) + { + WINWARN_IFALL(WINDOWS_LOG_PROCESS, "RefreshStateAfterStop called with no active session. Returning..."); + return; + } + + m_thread_list.RefreshStateAfterStop(); + + std::weak_ptr<ExceptionRecord> exception_record = m_session_data->m_debugger->GetActiveException(); + ExceptionRecordSP active_exception = exception_record.lock(); + if (!active_exception) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "RefreshStateAfterStop called for process %I64u but there is no " + "active exception. Why is the process stopped?", + m_session_data->m_debugger->GetProcess().GetProcessId()); + return; + } + + StopInfoSP stop_info; + m_thread_list.SetSelectedThreadByID(active_exception->GetThreadID()); + ThreadSP stop_thread = m_thread_list.GetSelectedThread(); + if (!stop_thread) + return; + + RegisterContextSP register_context = stop_thread->GetRegisterContext(); + + // The current EIP is AFTER the BP opcode, which is one byte. + uint64_t pc = register_context->GetPC() - 1; + if (active_exception->GetExceptionCode() == EXCEPTION_BREAKPOINT) + { + BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc)); + + if (site) + { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, + "RefreshStateAfterStop detected breakpoint in process %I64u at " + "address 0x%I64x with breakpoint site %d", + m_session_data->m_debugger->GetProcess().GetProcessId(), pc, site->GetID()); + + if (site->ValidForThisThread(stop_thread.get())) + { + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, + "Breakpoint site %d is valid for this thread (0x%I64x), creating stop info.", + site->GetID(), stop_thread->GetID()); + + stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID( + *stop_thread, site->GetID()); + register_context->SetPC(pc); + } + else + { + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, + "Breakpoint site %d is not valid for this thread, creating empty stop info.", + site->GetID()); + } + } + stop_thread->SetStopInfo(stop_info); + } + else if (active_exception->GetExceptionCode() == EXCEPTION_SINGLE_STEP) + { + stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread); + stop_thread->SetStopInfo(stop_info); + WINLOG_IFANY(WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_STEP, "RefreshStateAfterStop single stepping thread %u", + stop_thread->GetID()); + } + else + { + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " << llvm::format_hex(active_exception->GetExceptionCode(), 8) + << " encountered at address " << llvm::format_hex(pc, 8); + stop_info = StopInfo::CreateStopReasonWithException(*stop_thread, desc_stream.str().c_str()); + stop_thread->SetStopInfo(stop_info); + WINLOG_IFALL(WINDOWS_LOG_EXCEPTION, desc_stream.str().c_str()); + } +} + +bool +ProcessWindowsLive::IsAlive() +{ + StateType state = GetPrivateState(); + switch (state) + { + case eStateCrashed: + case eStateDetached: + case eStateUnloaded: + case eStateExited: + case eStateInvalid: + return false; + default: + return true; + } +} + +Error +ProcessWindowsLive::DoHalt(bool &caused_stop) +{ + Error error; + StateType state = GetPrivateState(); + if (state == eStateStopped) + caused_stop = false; + else + { + llvm::sys::ScopedLock lock(m_mutex); + caused_stop = ::DebugBreakProcess(m_session_data->m_debugger->GetProcess().GetNativeProcess().GetSystemHandle()); + if (!caused_stop) + { + error.SetError(::GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoHalt called DebugBreakProcess, but it failed with error %u", + error.GetError()); + } + } + return error; +} + +void +ProcessWindowsLive::DidLaunch() +{ + ArchSpec arch_spec; + DidAttach(arch_spec); +} + +void +ProcessWindowsLive::DidAttach(ArchSpec &arch_spec) +{ + llvm::sys::ScopedLock lock(m_mutex); + + // The initial stop won't broadcast the state change event, so account for that here. + if (m_session_data && GetPrivateState() == eStateStopped && m_session_data->m_stop_at_entry) + RefreshStateAfterStop(); +} + +size_t +ProcessWindowsLive::DoReadMemory(lldb::addr_t vm_addr, + void *buf, + size_t size, + Error &error) +{ + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) + return 0; + + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory attempting to read %u bytes from address 0x%I64x", size, vm_addr); + + HostProcess process = m_session_data->m_debugger->GetProcess(); + void *addr = reinterpret_cast<void *>(vm_addr); + SIZE_T bytes_read = 0; + if (!ReadProcessMemory(process.GetNativeProcess().GetSystemHandle(), addr, buf, size, &bytes_read)) + { + error.SetError(GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory failed with error code %u", error.GetError()); + } + return bytes_read; +} + +size_t +ProcessWindowsLive::DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Error &error) +{ + llvm::sys::ScopedLock lock(m_mutex); + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory attempting to write %u bytes into address 0x%I64x", size, vm_addr); + + if (!m_session_data) + { + WINERR_IFANY(WINDOWS_LOG_MEMORY, "DoWriteMemory cannot write, there is no active debugger connection."); + return 0; + } + + HostProcess process = m_session_data->m_debugger->GetProcess(); + void *addr = reinterpret_cast<void *>(vm_addr); + SIZE_T bytes_written = 0; + lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); + if (WriteProcessMemory(handle, addr, buf, size, &bytes_written)) + FlushInstructionCache(handle, addr, bytes_written); + else + { + error.SetError(GetLastError(), eErrorTypeWin32); + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory failed with error code %u", error.GetError()); + } + return bytes_written; +} + +Error +ProcessWindowsLive::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo &info) +{ + Error error; + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) + { + error.SetErrorString("GetMemoryRegionInfo called with no debugging session."); + WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); + return error; + } + + HostProcess process = m_session_data->m_debugger->GetProcess(); + lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); + if (handle == nullptr || handle == LLDB_INVALID_PROCESS) + { + error.SetErrorString("GetMemoryRegionInfo called with an invalid target process."); + WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); + return error; + } + + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "GetMemoryRegionInfo getting info for address 0x%I64x", vm_addr); + + void *addr = reinterpret_cast<void *>(vm_addr); + MEMORY_BASIC_INFORMATION mem_info = {0}; + SIZE_T result = ::VirtualQueryEx(handle, addr, &mem_info, sizeof(mem_info)); + if (result == 0) + { + error.SetError(::GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_MEMORY, + "VirtualQueryEx returned error %u while getting memory region info for address 0x%I64x", + error.GetError(), vm_addr); + return error; + } + const bool readable = IsPageReadable(mem_info.Protect); + const bool executable = IsPageExecutable(mem_info.Protect); + const bool writable = IsPageWritable(mem_info.Protect); + info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetExecutable(executable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + + error.SetError(::GetLastError(), eErrorTypeWin32); + WINLOGV_IFALL(WINDOWS_LOG_MEMORY, "Memory region info for address 0x%I64u: readable=%s, executable=%s, writable=%s", + BOOL_STR(readable), BOOL_STR(executable), BOOL_STR(writable)); + return error; +} + +bool +ProcessWindowsLive::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) +{ + if (plugin_specified_by_name) + return true; + + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target_sp->GetExecutableModule()); + if (exe_module_sp.get()) + return exe_module_sp->GetFileSpec().Exists(); + // However, if there is no executable module, we return true since we might be preparing to attach. + return true; +} + +void +ProcessWindowsLive::OnExitProcess(uint32_t exit_code) +{ + // No need to acquire the lock since m_session_data isn't accessed. + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Process %u exited with code %u", GetID(), exit_code); + + TargetSP target = m_target_sp.lock(); + if (target) + { + ModuleSP executable_module = target->GetExecutableModule(); + ModuleList unloaded_modules; + unloaded_modules.Append(executable_module); + target->ModulesDidUnload(unloaded_modules, true); + } + + SetProcessExitStatus(nullptr, GetID(), true, 0, exit_code); + SetPrivateState(eStateExited); +} + +void +ProcessWindowsLive::OnDebuggerConnected(lldb::addr_t image_base) +{ + DebuggerThreadSP debugger = m_session_data->m_debugger; + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Debugger connected to process %I64u. Image base = 0x%I64x", + debugger->GetProcess().GetProcessId(), image_base); + + ModuleSP module = GetTarget().GetExecutableModule(); + if (!module) + { + // During attach, we won't have the executable module, so find it now. + const DWORD pid = debugger->GetProcess().GetProcessId(); + const std::string file_name = GetProcessExecutableName(pid); + if (file_name.empty()) + { + return; + } + + FileSpec executable_file(file_name, true); + ModuleSpec module_spec(executable_file); + Error error; + module = GetTarget().GetSharedModule(module_spec, &error); + if (!module) + { + return; + } + + GetTarget().SetExecutableModule(module, false); + } + + bool load_addr_changed; + module->SetLoadAddress(GetTarget(), image_base, false, load_addr_changed); + + ModuleList loaded_modules; + loaded_modules.Append(module); + GetTarget().ModulesDidLoad(loaded_modules); + + // Add the main executable module to the list of pending module loads. We can't call + // GetTarget().ModulesDidLoad() here because we still haven't returned from DoLaunch() / DoAttach() yet + // so the target may not have set the process instance to `this` yet. + llvm::sys::ScopedLock lock(m_mutex); + const HostThreadWindows &wmain_thread = debugger->GetMainThread().GetNativeThread(); + m_session_data->m_new_threads[wmain_thread.GetThreadId()] = debugger->GetMainThread(); +} + +ExceptionResult +ProcessWindowsLive::OnDebugException(bool first_chance, const ExceptionRecord &record) +{ + llvm::sys::ScopedLock lock(m_mutex); + + // FIXME: Without this check, occasionally when running the test suite there is + // an issue where m_session_data can be null. It's not clear how this could happen + // but it only surfaces while running the test suite. In order to properly diagnose + // this, we probably need to first figure allow the test suite to print out full + // lldb logs, and then add logging to the process plugin. + if (!m_session_data) + { + WINERR_IFANY(WINDOWS_LOG_EXCEPTION, + "Debugger thread reported exception 0x%x at address 0x%I64x, but there is no session.", + record.GetExceptionCode(), record.GetExceptionAddress()); + return ExceptionResult::SendToApplication; + } + + if (!first_chance) + { + // Any second chance exception is an application crash by definition. + SetPrivateState(eStateCrashed); + } + + ExceptionResult result = ExceptionResult::SendToApplication; + switch (record.GetExceptionCode()) + { + case EXCEPTION_BREAKPOINT: + // Handle breakpoints at the first chance. + result = ExceptionResult::BreakInDebugger; + + if (!m_session_data->m_initial_stop_received) + { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS, + "Hit loader breakpoint at address 0x%I64x, setting initial stop event.", + record.GetExceptionAddress()); + m_session_data->m_initial_stop_received = true; + ::SetEvent(m_session_data->m_initial_stop_event); + } + else + { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS, + "Hit non-loader breakpoint at address 0x%I64x.", + record.GetExceptionAddress()); + } + SetPrivateState(eStateStopped); + break; + case EXCEPTION_SINGLE_STEP: + result = ExceptionResult::BreakInDebugger; + SetPrivateState(eStateStopped); + break; + default: + WINLOG_IFANY(WINDOWS_LOG_EXCEPTION, + "Debugger thread reported exception 0x%x at address 0x%I64x (first_chance=%s)", + record.GetExceptionCode(), record.GetExceptionAddress(), BOOL_STR(first_chance)); + // For non-breakpoints, give the application a chance to handle the exception first. + if (first_chance) + result = ExceptionResult::SendToApplication; + else + result = ExceptionResult::BreakInDebugger; + } + + return result; +} + +void +ProcessWindowsLive::OnCreateThread(const HostThread &new_thread) +{ + llvm::sys::ScopedLock lock(m_mutex); + const HostThreadWindows &wnew_thread = new_thread.GetNativeThread(); + m_session_data->m_new_threads[wnew_thread.GetThreadId()] = new_thread; +} + +void +ProcessWindowsLive::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) +{ + llvm::sys::ScopedLock lock(m_mutex); + + // On a forced termination, we may get exit thread events after the session + // data has been cleaned up. + if (!m_session_data) + return; + + // A thread may have started and exited before the debugger stopped allowing a refresh. + // Just remove it from the new threads list in that case. + auto iter = m_session_data->m_new_threads.find(thread_id); + if (iter != m_session_data->m_new_threads.end()) + m_session_data->m_new_threads.erase(iter); + else + m_session_data->m_exited_threads.insert(thread_id); +} + +void +ProcessWindowsLive::OnLoadDll(const ModuleSpec &module_spec, lldb::addr_t module_addr) +{ + // Confusingly, there is no Target::AddSharedModule. Instead, calling GetSharedModule() with + // a new module will add it to the module list and return a corresponding ModuleSP. + Error error; + ModuleSP module = GetTarget().GetSharedModule(module_spec, &error); + bool load_addr_changed = false; + module->SetLoadAddress(GetTarget(), module_addr, false, load_addr_changed); + + ModuleList loaded_modules; + loaded_modules.Append(module); + GetTarget().ModulesDidLoad(loaded_modules); +} + +void +ProcessWindowsLive::OnUnloadDll(lldb::addr_t module_addr) +{ + Address resolved_addr; + if (GetTarget().ResolveLoadAddress(module_addr, resolved_addr)) + { + ModuleSP module = resolved_addr.GetModule(); + if (module) + { + ModuleList unloaded_modules; + unloaded_modules.Append(module); + GetTarget().ModulesDidUnload(unloaded_modules, false); + } + } +} + +void +ProcessWindowsLive::OnDebugString(const std::string &string) +{ +} + +void +ProcessWindowsLive::OnDebuggerError(const Error &error, uint32_t type) +{ + llvm::sys::ScopedLock lock(m_mutex); + + if (m_session_data->m_initial_stop_received) + { + // This happened while debugging. Do we shutdown the debugging session, try to continue, + // or do something else? + WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred during debugging. Unexpected behavior may result. %s", + error.GetError(), error.AsCString()); + } + else + { + // If we haven't actually launched the process yet, this was an error launching the + // process. Set the internal error and signal the initial stop event so that the DoLaunch + // method wakes up and returns a failure. + m_session_data->m_launch_error = error; + ::SetEvent(m_session_data->m_initial_stop_event); + WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred launching the process before the initial stop. %s", + error.GetError(), error.AsCString()); + return; + } +} diff --git a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h new file mode 100644 index 000000000000..2429f873c823 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h @@ -0,0 +1,125 @@ +//===-- ProcessWindowsLive.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ +#define liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ + +// C Includes + +// C++ Includes +#include <memory> +#include <queue> + +// Other libraries and framework includes +#include "ForwardDecl.h" +#include "IDebugDelegate.h" +#include "lldb/lldb-forward.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Process.h" + +#include "llvm/Support/Mutex.h" + +#include "plugins/Process/Windows/Common/ProcessWindows.h" + +class ProcessMonitor; + +namespace lldb_private +{ +class HostProcess; +class ProcessWindowsData; + +class ProcessWindowsLive : public lldb_private::ProcessWindows, public lldb_private::IDebugDelegate +{ +public: + //------------------------------------------------------------------ + // Static functions. + //------------------------------------------------------------------ + static lldb::ProcessSP + CreateInstance(lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec *); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + ProcessWindowsLive(lldb::TargetSP target_sp, + lldb_private::Listener &listener); + + ~ProcessWindowsLive(); + + // lldb_private::Process overrides + lldb_private::ConstString GetPluginName() override; + uint32_t GetPluginVersion() override; + + lldb_private::Error EnableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; + lldb_private::Error DisableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; + + lldb_private::Error DoDetach(bool keep_stopped) override; + lldb_private::Error DoLaunch(lldb_private::Module *exe_module, lldb_private::ProcessLaunchInfo &launch_info) override; + lldb_private::Error DoAttachToProcessWithID(lldb::pid_t pid, + const lldb_private::ProcessAttachInfo &attach_info) override; + lldb_private::Error DoResume() override; + lldb_private::Error DoDestroy() override; + lldb_private::Error DoHalt(bool &caused_stop) override; + + void DidLaunch() override; + void DidAttach(lldb_private::ArchSpec &arch_spec) override; + + void RefreshStateAfterStop() override; + + bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override; + bool + DestroyRequiresHalt() override + { + return false; + } + bool UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override; + bool IsAlive() override; + + size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, lldb_private::Error &error) override; + size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Error &error) override; + lldb_private::Error GetMemoryRegionInfo(lldb::addr_t vm_addr, lldb_private::MemoryRegionInfo &info) override; + + // IDebugDelegate overrides. + void OnExitProcess(uint32_t exit_code) override; + void OnDebuggerConnected(lldb::addr_t image_base) override; + ExceptionResult OnDebugException(bool first_chance, const lldb_private::ExceptionRecord &record) override; + void OnCreateThread(const lldb_private::HostThread &thread) override; + void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override; + void OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) override; + void OnUnloadDll(lldb::addr_t module_addr) override; + void OnDebugString(const std::string &string) override; + void OnDebuggerError(const lldb_private::Error &error, uint32_t type) override; + + private: + lldb_private::Error WaitForDebuggerConnection(lldb_private::DebuggerThreadSP debugger, + lldb_private::HostProcess &process); + + llvm::sys::Mutex m_mutex; + + // Data for the active debugging session. + std::unique_ptr<lldb_private::ProcessWindowsData> m_session_data; +}; + +} + +#endif // liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ diff --git a/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp new file mode 100644 index 000000000000..52b32716e674 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp @@ -0,0 +1,147 @@ +//===-- TargetThreadWindowsLive.cpp------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Core/Logging.h" +#include "lldb/Core/State.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostNativeThreadBase.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Target/RegisterContext.h" + +#include "TargetThreadWindowsLive.h" +#include "ProcessWindows.h" +#include "ProcessWindowsLog.h" +#include "UnwindLLDB.h" + +#if defined(_WIN64) +#include "x64/RegisterContextWindowsLive_x64.h" +#else +#include "x86/RegisterContextWindowsLive_x86.h" +#endif + +using namespace lldb; +using namespace lldb_private; + +TargetThreadWindowsLive::TargetThreadWindowsLive(ProcessWindows &process, const HostThread &thread) + : TargetThreadWindows(process, thread) + , m_host_thread(thread) +{ +} + +TargetThreadWindowsLive::~TargetThreadWindowsLive() +{ + DestroyThread(); +} + +void +TargetThreadWindowsLive::RefreshStateAfterStop() +{ + ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); + SetState(eStateStopped); + GetRegisterContext()->InvalidateIfNeeded(false); +} + +void +TargetThreadWindowsLive::WillResume(lldb::StateType resume_state) +{ +} + +void +TargetThreadWindowsLive::DidStop() +{ +} + +RegisterContextSP +TargetThreadWindowsLive::GetRegisterContext() +{ + if (!m_reg_context_sp) + m_reg_context_sp = CreateRegisterContextForFrameIndex(0); + + return m_reg_context_sp; +} + +RegisterContextSP +TargetThreadWindowsLive::CreateRegisterContextForFrame(StackFrame *frame) +{ + return CreateRegisterContextForFrameIndex(frame->GetConcreteFrameIndex()); +} + +RegisterContextSP +TargetThreadWindowsLive::CreateRegisterContextForFrameIndex(uint32_t idx) +{ + if (!m_reg_context_sp) + { + ArchSpec arch = HostInfo::GetArchitecture(); + switch (arch.GetMachine()) + { + case llvm::Triple::x86: +#if defined(_WIN64) + // FIXME: This is a Wow64 process, create a RegisterContextWindows_Wow64 +#else + m_reg_context_sp.reset(new RegisterContextWindowsLive_x86(*this, idx)); +#endif + break; + case llvm::Triple::x86_64: +#if defined(_WIN64) + m_reg_context_sp.reset(new RegisterContextWindowsLive_x64(*this, idx)); +#else + // LLDB is 32-bit, but the target process is 64-bit. We probably can't debug this. +#endif + default: + break; + } + } + return m_reg_context_sp; +} + +bool +TargetThreadWindowsLive::CalculateStopInfo() +{ + SetStopInfo(m_stop_info_sp); + return true; +} + +Unwind * +TargetThreadWindowsLive::GetUnwinder() +{ + // FIXME: Implement an unwinder based on the Windows unwinder exposed through DIA SDK. + if (m_unwinder_ap.get() == NULL) + m_unwinder_ap.reset(new UnwindLLDB(*this)); + return m_unwinder_ap.get(); +} + +bool +TargetThreadWindowsLive::DoResume() +{ + StateType resume_state = GetTemporaryResumeState(); + StateType current_state = GetState(); + if (resume_state == current_state) + return true; + + if (resume_state == eStateStepping) + { + uint32_t flags_index = GetRegisterContext()->ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + uint64_t flags_value = GetRegisterContext()->ReadRegisterAsUnsigned(flags_index, 0); + flags_value |= 0x100; // Set the trap flag on the CPU + GetRegisterContext()->WriteRegisterFromUnsigned(flags_index, flags_value); + } + + if (resume_state == eStateStepping || resume_state == eStateRunning) + { + DWORD previous_suspend_count = 0; + HANDLE thread_handle = m_host_thread.GetNativeThread().GetSystemHandle(); + do + { + previous_suspend_count = ::ResumeThread(thread_handle); + } while (previous_suspend_count > 0); + } + return true; +} diff --git a/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h new file mode 100644 index 000000000000..15262b9e9423 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h @@ -0,0 +1,55 @@ +//===-- TargetThreadWindowsLive.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_TargetThreadWindowsLive_H_ +#define liblldb_Plugins_Process_Windows_TargetThreadWindowsLive_H_ + +#include "lldb/lldb-forward.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Thread.h" + +#include "Plugins/Process/Windows/Common/TargetThreadWindows.h" + +namespace lldb_private +{ +class ProcessWindows; +class HostThread; +class StackFrame; + +class TargetThreadWindowsLive : public lldb_private::TargetThreadWindows +{ + public: + TargetThreadWindowsLive(ProcessWindows &process, const HostThread &thread); + virtual ~TargetThreadWindowsLive(); + + // lldb_private::Thread overrides + void RefreshStateAfterStop() override; + void WillResume(lldb::StateType resume_state) override; + void DidStop() override; + lldb::RegisterContextSP GetRegisterContext() override; + lldb::RegisterContextSP CreateRegisterContextForFrame(StackFrame *frame) override; + bool CalculateStopInfo() override; + Unwind *GetUnwinder() override; + + bool DoResume(); + + HostThread + GetHostThread() const + { + return m_host_thread; + } + + private: + lldb::RegisterContextSP CreateRegisterContextForFrameIndex(uint32_t idx); + + HostThread m_host_thread; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp new file mode 100644 index 000000000000..e74647ab68fb --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp @@ -0,0 +1,171 @@ +//===-- RegisterContextWindowsLive_x64.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "RegisterContextWindowsLive_x64.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextWindowsLive_x64::RegisterContextWindowsLive_x64(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows_x64(thread, concrete_frame_idx) +{ +} + +RegisterContextWindowsLive_x64::~RegisterContextWindowsLive_x64() +{ +} + + +bool +RegisterContextWindowsLive_x64::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ + if (!CacheAllRegisterValues()) + return false; + + switch (reg_info->kinds[eRegisterKindLLDB]) + { + case lldb_rax_x86_64: + reg_value.SetUInt64(m_context.Rax); + break; + case lldb_rbx_x86_64: + reg_value.SetUInt64(m_context.Rbx); + break; + case lldb_rcx_x86_64: + reg_value.SetUInt64(m_context.Rcx); + break; + case lldb_rdx_x86_64: + reg_value.SetUInt64(m_context.Rdx); + break; + case lldb_rdi_x86_64: + reg_value.SetUInt64(m_context.Rdi); + break; + case lldb_rsi_x86_64: + reg_value.SetUInt64(m_context.Rsi); + break; + case lldb_r8_x86_64: + reg_value.SetUInt64(m_context.R8); + break; + case lldb_r9_x86_64: + reg_value.SetUInt64(m_context.R9); + break; + case lldb_r10_x86_64: + reg_value.SetUInt64(m_context.R10); + break; + case lldb_r11_x86_64: + reg_value.SetUInt64(m_context.R11); + break; + case lldb_r12_x86_64: + reg_value.SetUInt64(m_context.R12); + break; + case lldb_r13_x86_64: + reg_value.SetUInt64(m_context.R13); + break; + case lldb_r14_x86_64: + reg_value.SetUInt64(m_context.R14); + break; + case lldb_r15_x86_64: + reg_value.SetUInt64(m_context.R15); + break; + case lldb_rbp_x86_64: + reg_value.SetUInt64(m_context.Rbp); + break; + case lldb_rsp_x86_64: + reg_value.SetUInt64(m_context.Rsp); + break; + case lldb_rip_x86_64: + reg_value.SetUInt64(m_context.Rip); + break; + case lldb_rflags_x86_64: + reg_value.SetUInt64(m_context.EFlags); + break; + } + return true; +} + +bool +RegisterContextWindowsLive_x64::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + // Since we cannot only write a single register value to the inferior, we need to make sure + // our cached copy of the register values are fresh. Otherwise when writing EAX, for example, + // we may also overwrite some other register with a stale value. + if (!CacheAllRegisterValues()) + return false; + + switch (reg_info->kinds[eRegisterKindLLDB]) + { + case lldb_rax_x86_64: + m_context.Rax = reg_value.GetAsUInt64(); + break; + case lldb_rbx_x86_64: + m_context.Rbx = reg_value.GetAsUInt64(); + break; + case lldb_rcx_x86_64: + m_context.Rcx = reg_value.GetAsUInt64(); + break; + case lldb_rdx_x86_64: + m_context.Rdx = reg_value.GetAsUInt64(); + break; + case lldb_rdi_x86_64: + m_context.Rdi = reg_value.GetAsUInt64(); + break; + case lldb_rsi_x86_64: + m_context.Rsi = reg_value.GetAsUInt64(); + break; + case lldb_r8_x86_64: + m_context.R8 = reg_value.GetAsUInt64(); + break; + case lldb_r9_x86_64: + m_context.R9 = reg_value.GetAsUInt64(); + break; + case lldb_r10_x86_64: + m_context.R10 = reg_value.GetAsUInt64(); + break; + case lldb_r11_x86_64: + m_context.R11 = reg_value.GetAsUInt64(); + break; + case lldb_r12_x86_64: + m_context.R12 = reg_value.GetAsUInt64(); + break; + case lldb_r13_x86_64: + m_context.R13 = reg_value.GetAsUInt64(); + break; + case lldb_r14_x86_64: + m_context.R14 = reg_value.GetAsUInt64(); + break; + case lldb_r15_x86_64: + m_context.R15 = reg_value.GetAsUInt64(); + break; + case lldb_rbp_x86_64: + m_context.Rbp = reg_value.GetAsUInt64(); + break; + case lldb_rsp_x86_64: + m_context.Rsp = reg_value.GetAsUInt64(); + break; + case lldb_rip_x86_64: + m_context.Rip = reg_value.GetAsUInt64(); + break; + case lldb_rflags_x86_64: + m_context.EFlags = reg_value.GetAsUInt64(); + break; + } + + // Physically update the registers in the target process. + TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread); + return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} diff --git a/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h new file mode 100644 index 000000000000..bd250a994d3a --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h @@ -0,0 +1,40 @@ +//===-- RegisterContextWindowsLive_x64.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsLive_x64_H_ +#define liblldb_RegisterContextWindowsLive_x64_H_ + +#include "lldb/lldb-forward.h" +#include "../../Common/x64/RegisterContextWindows_x64.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsLive_x64 : public RegisterContextWindows_x64 +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindowsLive_x64(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindowsLive_x64(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; +} + +#endif // #ifndef liblldb_RegisterContextWindowsLive_x64_H_ diff --git a/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp new file mode 100644 index 000000000000..f2decc72d16d --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp @@ -0,0 +1,100 @@ +//===-- RegisterContextWindowsLive_x86.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "ProcessWindowsLog.h" +#include "RegisterContextWindowsLive_x86.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; + +namespace lldb_private +{ + +RegisterContextWindowsLive_x86::RegisterContextWindowsLive_x86(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows_x86(thread, concrete_frame_idx) +{ +} + +RegisterContextWindowsLive_x86::~RegisterContextWindowsLive_x86() +{ +} + + +bool +RegisterContextWindowsLive_x86::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + // Since we cannot only write a single register value to the inferior, we need to make sure + // our cached copy of the register values are fresh. Otherwise when writing EAX, for example, + // we may also overwrite some other register with a stale value. + if (!CacheAllRegisterValues()) + return false; + + uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + switch (reg) + { + case lldb_eax_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EAX", reg_value.GetAsUInt32()); + m_context.Eax = reg_value.GetAsUInt32(); + break; + case lldb_ebx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EBX", reg_value.GetAsUInt32()); + m_context.Ebx = reg_value.GetAsUInt32(); + break; + case lldb_ecx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ECX", reg_value.GetAsUInt32()); + m_context.Ecx = reg_value.GetAsUInt32(); + break; + case lldb_edx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EDX", reg_value.GetAsUInt32()); + m_context.Edx = reg_value.GetAsUInt32(); + break; + case lldb_edi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EDI", reg_value.GetAsUInt32()); + m_context.Edi = reg_value.GetAsUInt32(); + break; + case lldb_esi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ESI", reg_value.GetAsUInt32()); + m_context.Esi = reg_value.GetAsUInt32(); + break; + case lldb_ebp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EBP", reg_value.GetAsUInt32()); + m_context.Ebp = reg_value.GetAsUInt32(); + break; + case lldb_esp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ESP", reg_value.GetAsUInt32()); + m_context.Esp = reg_value.GetAsUInt32(); + break; + case lldb_eip_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EIP", reg_value.GetAsUInt32()); + m_context.Eip = reg_value.GetAsUInt32(); + break; + case lldb_eflags_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EFLAGS", reg_value.GetAsUInt32()); + m_context.EFlags = reg_value.GetAsUInt32(); + break; + default: + WINWARN_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to unknown register %u", reg_value.GetAsUInt32(), + reg); + } + + // Physically update the registers in the target process. + TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread); + return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} + +} // namespace lldb_private diff --git a/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h new file mode 100644 index 000000000000..9554f017408a --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h @@ -0,0 +1,36 @@ +//===-- RegisterContextWindowsLive_x86.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsLive_x86_H_ +#define liblldb_RegisterContextWindowsLive_x86_H_ + +#include "lldb/lldb-forward.h" +#include "../../Common/x86/RegisterContextWindows_x86.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsLive_x86 : public RegisterContextWindows_x86 +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindowsLive_x86(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindowsLive_x86(); + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindowsLive_x86_H_ diff --git a/source/Plugins/Process/Windows/MiniDump/CMakeLists.txt b/source/Plugins/Process/Windows/MiniDump/CMakeLists.txt new file mode 100644 index 000000000000..b43246b00351 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories(../../Utility) +include_directories(../Common) + +set(PROC_WINDOWS_MINIDUMP_SOURCES + ProcessWinMiniDump.cpp + ThreadWinMiniDump.cpp + ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(PROC_WINDOWS_MINIDUMP_SOURCES ${PROC_WINDOWS_MINIDUMP_SOURCES} + x86/RegisterContextWindowsMiniDump_x86.cpp + ) +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROC_WINDOWS_MINIDUMP_SOURCES ${PROC_WINDOWS_MINIDUMP_SOURCES} + x64/RegisterContextWindowsMiniDump_x64.cpp + ) +endif() + +add_lldb_library(lldbPluginProcessWinMiniDump + ${PROC_WINDOWS_MINIDUMP_SOURCES} + ) diff --git a/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp new file mode 100644 index 000000000000..fbc96f085ed4 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp @@ -0,0 +1,550 @@ +//===-- ProcessWinMiniDump.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessWinMiniDump.h" + +#include "lldb/Host/windows/windows.h" +#include <DbgHelp.h> + +#include <assert.h> +#include <stdlib.h> +#include <memory> +#include <mutex> + +#include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/LLDBAssert.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +#include "ExceptionRecord.h" +#include "ThreadWinMiniDump.h" + +using namespace lldb_private; + +namespace +{ + +// Getting a string out of a mini dump is a chore. You're usually given a +// relative virtual address (RVA), which points to a counted string that's in +// Windows Unicode (UTF-16). This wrapper handles all the redirection and +// returns a UTF-8 copy of the string. +std::string +GetMiniDumpString(const void *base_addr, const RVA rva) +{ + std::string result; + if (!base_addr) + { + return result; + } + auto md_string = reinterpret_cast<const MINIDUMP_STRING *>(static_cast<const char *>(base_addr) + rva); + auto source_start = reinterpret_cast<const UTF16 *>(md_string->Buffer); + const auto source_length = ::wcslen(md_string->Buffer); + const auto source_end = source_start + source_length; + result.resize(4*source_length); // worst case length + auto result_start = reinterpret_cast<UTF8 *>(&result[0]); + const auto result_end = result_start + result.size(); + ConvertUTF16toUTF8(&source_start, source_end, &result_start, result_end, strictConversion); + const auto result_size = std::distance(reinterpret_cast<UTF8 *>(&result[0]), result_start); + result.resize(result_size); // shrink to actual length + return result; +} + +} // anonymous namespace + +// Encapsulates the private data for ProcessWinMiniDump. +// TODO(amccarth): Determine if we need a mutex for access. +class ProcessWinMiniDump::Data +{ +public: + Data(); + ~Data(); + + FileSpec m_core_file; + HANDLE m_dump_file; // handle to the open minidump file + HANDLE m_mapping; // handle to the file mapping for the minidump file + void * m_base_addr; // base memory address of the minidump + std::shared_ptr<ExceptionRecord> m_exception_sp; +}; + +ConstString +ProcessWinMiniDump::GetPluginNameStatic() +{ + static ConstString g_name("win-minidump"); + return g_name; +} + +const char * +ProcessWinMiniDump::GetPluginDescriptionStatic() +{ + return "Windows minidump plug-in."; +} + +void +ProcessWinMiniDump::Terminate() +{ + PluginManager::UnregisterPlugin(ProcessWinMiniDump::CreateInstance); +} + + +lldb::ProcessSP +ProcessWinMiniDump::CreateInstance(lldb::TargetSP target_sp, Listener &listener, const FileSpec *crash_file) +{ + lldb::ProcessSP process_sp; + if (crash_file) + { + process_sp.reset(new ProcessWinMiniDump(target_sp, listener, *crash_file)); + } + return process_sp; +} + +bool +ProcessWinMiniDump::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) +{ + // TODO(amccarth): Eventually, this needs some actual logic. + return true; +} + +ProcessWinMiniDump::ProcessWinMiniDump(lldb::TargetSP target_sp, Listener &listener, + const FileSpec &core_file) : + ProcessWindows(target_sp, listener), + m_data_up(new Data) +{ + m_data_up->m_core_file = core_file; +} + +ProcessWinMiniDump::~ProcessWinMiniDump() +{ + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +ConstString +ProcessWinMiniDump::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessWinMiniDump::GetPluginVersion() +{ + return 1; +} + + +Error +ProcessWinMiniDump::DoLoadCore() +{ + Error error; + + error = MapMiniDumpIntoMemory(m_data_up->m_core_file.GetCString()); + if (error.Fail()) + { + return error; + } + + GetTarget().SetArchitecture(DetermineArchitecture()); + ReadMiscInfo(); // notably for process ID + ReadModuleList(); + ReadExceptionRecord(); + + return error; + +} + +DynamicLoader * +ProcessWinMiniDump::GetDynamicLoader() +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset (DynamicLoader::FindPlugin(this, DynamicLoaderWindowsDYLD::GetPluginNameStatic().GetCString())); + return m_dyld_ap.get(); +} + +bool +ProcessWinMiniDump::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + size_t size = 0; + auto thread_list_ptr = static_cast<const MINIDUMP_THREAD_LIST *>(FindDumpStream(ThreadListStream, &size)); + if (thread_list_ptr) + { + const ULONG32 thread_count = thread_list_ptr->NumberOfThreads; + for (ULONG32 i = 0; i < thread_count; ++i) { + const auto &mini_dump_thread = thread_list_ptr->Threads[i]; + auto thread_sp = std::make_shared<ThreadWinMiniDump>(*this, mini_dump_thread.ThreadId); + if (mini_dump_thread.ThreadContext.DataSize >= sizeof(CONTEXT)) + { + const CONTEXT *context = reinterpret_cast<const CONTEXT *>(static_cast<const char *>(m_data_up->m_base_addr) + mini_dump_thread.ThreadContext.Rva); + thread_sp->SetContext(context); + } + new_thread_list.AddThread(thread_sp); + } + } + + return new_thread_list.GetSize(false) > 0; +} + +void +ProcessWinMiniDump::RefreshStateAfterStop() +{ + if (!m_data_up) return; + if (!m_data_up->m_exception_sp) return; + + auto active_exception = m_data_up->m_exception_sp; + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " + << llvm::format_hex(active_exception->GetExceptionCode(), 8) + << " encountered at address " + << llvm::format_hex(active_exception->GetExceptionAddress(), 8); + m_thread_list.SetSelectedThreadByID(active_exception->GetThreadID()); + auto stop_thread = m_thread_list.GetSelectedThread(); + auto stop_info = StopInfo::CreateStopReasonWithException(*stop_thread, desc_stream.str().c_str()); + stop_thread->SetStopInfo(stop_info); +} + +Error +ProcessWinMiniDump::DoDestroy() +{ + return Error(); +} + +bool +ProcessWinMiniDump::IsAlive() +{ + return true; +} + +bool +ProcessWinMiniDump::WarnBeforeDetach () const +{ + // Since this is post-mortem debugging, there's no need to warn the user + // that quitting the debugger will terminate the process. + return false; +} + +size_t +ProcessWinMiniDump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) +{ + // Don't allow the caching that lldb_private::Process::ReadMemory does + // since we have it all cached our our dump file anyway. + return DoReadMemory(addr, buf, size, error); +} + +size_t +ProcessWinMiniDump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) +{ + // I don't have a sense of how frequently this is called or how many memory + // ranges a mini dump typically has, so I'm not sure if searching for the + // appropriate range linearly each time is stupid. Perhaps we should build + // an index for faster lookups. + Range range = {0}; + if (!FindMemoryRange(addr, &range)) + { + return 0; + } + + // There's at least some overlap between the beginning of the desired range + // (addr) and the current range. Figure out where the overlap begins and + // how much overlap there is, then copy it to the destination buffer. + lldbassert(range.start <= addr); + const size_t offset = addr - range.start; + lldbassert(offset < range.size); + const size_t overlap = std::min(size, range.size - offset); + std::memcpy(buf, range.ptr + offset, overlap); + return overlap; +} + +Error +ProcessWinMiniDump::GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &info) +{ + Error error; + size_t size; + const auto list = reinterpret_cast<const MINIDUMP_MEMORY_INFO_LIST *>(FindDumpStream(MemoryInfoListStream, &size)); + if (list == nullptr || size < sizeof(MINIDUMP_MEMORY_INFO_LIST)) + { + error.SetErrorString("the mini dump contains no memory range information"); + return error; + } + + if (list->SizeOfEntry < sizeof(MINIDUMP_MEMORY_INFO)) + { + error.SetErrorString("the entries in the mini dump memory info list are smaller than expected"); + return error; + } + + if (size < list->SizeOfHeader + list->SizeOfEntry * list->NumberOfEntries) + { + error.SetErrorString("the mini dump memory info list is incomplete"); + return error; + } + + for (int i = 0; i < list->NumberOfEntries; ++i) + { + const auto entry = reinterpret_cast<const MINIDUMP_MEMORY_INFO *>(reinterpret_cast<const char *>(list) + + list->SizeOfHeader + i * list->SizeOfEntry); + const auto head = entry->BaseAddress; + const auto tail = head + entry->RegionSize; + if (head <= load_addr && load_addr < tail) + { + info.SetReadable(IsPageReadable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetWritable(IsPageWritable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetExecutable(IsPageExecutable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + return error; + } + } + // Note that the memory info list doesn't seem to contain ranges in kernel space, + // so if you're walking a stack that has kernel frames, the stack may appear + // truncated. + error.SetErrorString("address is not in a known range"); + return error; +} + +void +ProcessWinMiniDump::Clear() +{ + m_thread_list.Clear(); +} + +void +ProcessWinMiniDump::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() + { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + }); +} + +ArchSpec +ProcessWinMiniDump::GetArchitecture() +{ + // TODO + return ArchSpec(); +} + + +ProcessWinMiniDump::Data::Data() : + m_dump_file(INVALID_HANDLE_VALUE), + m_mapping(NULL), + m_base_addr(nullptr) +{ +} + +ProcessWinMiniDump::Data::~Data() +{ + if (m_base_addr) + { + ::UnmapViewOfFile(m_base_addr); + m_base_addr = nullptr; + } + if (m_mapping) + { + ::CloseHandle(m_mapping); + m_mapping = NULL; + } + if (m_dump_file != INVALID_HANDLE_VALUE) + { + ::CloseHandle(m_dump_file); + m_dump_file = INVALID_HANDLE_VALUE; + } +} + +bool +ProcessWinMiniDump::FindMemoryRange(lldb::addr_t addr, Range *range_out) const +{ + size_t stream_size = 0; + auto mem_list_stream = static_cast<const MINIDUMP_MEMORY_LIST *>(FindDumpStream(MemoryListStream, &stream_size)); + if (mem_list_stream) + { + for (ULONG32 i = 0; i < mem_list_stream->NumberOfMemoryRanges; ++i) { + const MINIDUMP_MEMORY_DESCRIPTOR &mem_desc = mem_list_stream->MemoryRanges[i]; + const MINIDUMP_LOCATION_DESCRIPTOR &loc_desc = mem_desc.Memory; + const lldb::addr_t range_start = mem_desc.StartOfMemoryRange; + const size_t range_size = loc_desc.DataSize; + if (range_start <= addr && addr < range_start + range_size) + { + range_out->start = range_start; + range_out->size = range_size; + range_out->ptr = reinterpret_cast<const uint8_t *>(m_data_up->m_base_addr) + loc_desc.Rva; + return true; + } + } + } + + // Some mini dumps have a Memory64ListStream that captures all the heap + // memory. We can't exactly use the same loop as above, because the mini + // dump uses slightly different data structures to describe those. + auto mem_list64_stream = static_cast<const MINIDUMP_MEMORY64_LIST *>(FindDumpStream(Memory64ListStream, &stream_size)); + if (mem_list64_stream) + { + size_t base_rva = mem_list64_stream->BaseRva; + for (ULONG32 i = 0; i < mem_list64_stream->NumberOfMemoryRanges; ++i) { + const MINIDUMP_MEMORY_DESCRIPTOR64 &mem_desc = mem_list64_stream->MemoryRanges[i]; + const lldb::addr_t range_start = mem_desc.StartOfMemoryRange; + const size_t range_size = mem_desc.DataSize; + if (range_start <= addr && addr < range_start + range_size) + { + range_out->start = range_start; + range_out->size = range_size; + range_out->ptr = reinterpret_cast<const uint8_t *>(m_data_up->m_base_addr) + base_rva; + return true; + } + base_rva += range_size; + } + } + + return false; +} + + +Error +ProcessWinMiniDump::MapMiniDumpIntoMemory(const char *file) +{ + Error error; + + m_data_up->m_dump_file = ::CreateFile(file, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (m_data_up->m_dump_file == INVALID_HANDLE_VALUE) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + + m_data_up->m_mapping = ::CreateFileMapping(m_data_up->m_dump_file, NULL, + PAGE_READONLY, 0, 0, NULL); + if (m_data_up->m_mapping == NULL) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + + m_data_up->m_base_addr = ::MapViewOfFile(m_data_up->m_mapping, FILE_MAP_READ, 0, 0, 0); + if (m_data_up->m_base_addr == NULL) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + + return error; +} + + +ArchSpec +ProcessWinMiniDump::DetermineArchitecture() +{ + size_t size = 0; + auto system_info_ptr = static_cast<const MINIDUMP_SYSTEM_INFO *>(FindDumpStream(SystemInfoStream, &size)); + if (system_info_ptr) + { + switch (system_info_ptr->ProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_INTEL: + return ArchSpec(eArchTypeCOFF, IMAGE_FILE_MACHINE_I386, LLDB_INVALID_CPUTYPE); + case PROCESSOR_ARCHITECTURE_AMD64: + return ArchSpec(eArchTypeCOFF, IMAGE_FILE_MACHINE_AMD64, LLDB_INVALID_CPUTYPE); + default: + break; + } + } + + return ArchSpec(); // invalid or unknown +} + +void +ProcessWinMiniDump::ReadExceptionRecord() +{ + size_t size = 0; + auto exception_stream_ptr = static_cast<MINIDUMP_EXCEPTION_STREAM*>(FindDumpStream(ExceptionStream, &size)); + if (exception_stream_ptr) + { + m_data_up->m_exception_sp.reset(new ExceptionRecord(exception_stream_ptr->ExceptionRecord, exception_stream_ptr->ThreadId)); + } +} + +void +ProcessWinMiniDump::ReadMiscInfo() +{ + size_t size = 0; + const auto misc_info_ptr = static_cast<MINIDUMP_MISC_INFO*>(FindDumpStream(MiscInfoStream, &size)); + if (!misc_info_ptr || size < sizeof(MINIDUMP_MISC_INFO)) { + return; + } + + if ((misc_info_ptr->Flags1 & MINIDUMP_MISC1_PROCESS_ID) != 0) { + // This misc info record has the process ID. + SetID(misc_info_ptr->ProcessId); + } +} + +void +ProcessWinMiniDump::ReadModuleList() +{ + size_t size = 0; + auto module_list_ptr = static_cast<MINIDUMP_MODULE_LIST*>(FindDumpStream(ModuleListStream, &size)); + if (!module_list_ptr || module_list_ptr->NumberOfModules == 0) + { + return; + } + + for (ULONG32 i = 0; i < module_list_ptr->NumberOfModules; ++i) + { + const auto &module = module_list_ptr->Modules[i]; + const auto file_name = GetMiniDumpString(m_data_up->m_base_addr, module.ModuleNameRva); + ModuleSpec module_spec = FileSpec(file_name, true); + + lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec); + if (!module_sp) + { + continue; + } + bool load_addr_changed = false; + module_sp->SetLoadAddress(GetTarget(), module.BaseOfImage, false, load_addr_changed); + } +} + +void * +ProcessWinMiniDump::FindDumpStream(unsigned stream_number, size_t *size_out) const +{ + void *stream = nullptr; + *size_out = 0; + + assert(m_data_up != nullptr); + assert(m_data_up->m_base_addr != 0); + + MINIDUMP_DIRECTORY *dir = nullptr; + if (::MiniDumpReadDumpStream(m_data_up->m_base_addr, stream_number, &dir, nullptr, nullptr) && + dir != nullptr && dir->Location.DataSize > 0) + { + assert(dir->StreamType == stream_number); + *size_out = dir->Location.DataSize; + stream = static_cast<void*>(static_cast<char*>(m_data_up->m_base_addr) + dir->Location.Rva); + } + + return stream; +} diff --git a/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h new file mode 100644 index 000000000000..12864be37127 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h @@ -0,0 +1,140 @@ +//===-- ProcessWinMiniDump.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessWinMiniDump_h_ +#define liblldb_ProcessWinMiniDump_h_ + +#include <list> +#include <vector> + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/Process.h" + +#include "Plugins/Process/Windows/Common/ProcessWindows.h" + +struct ThreadData; + +class ProcessWinMiniDump : public lldb_private::ProcessWindows +{ + public: + static lldb::ProcessSP + CreateInstance (lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec *crash_file_path); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + ProcessWinMiniDump(lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec &core_file); + + virtual + ~ProcessWinMiniDump(); + + bool + CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override; + + lldb_private::Error + DoLoadCore() override; + + lldb_private::DynamicLoader * + GetDynamicLoader() override; + + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + + lldb_private::Error + DoDestroy() override; + + void + RefreshStateAfterStop() override; + + bool + IsAlive() override; + + bool + WarnBeforeDetach () const override; + + size_t + ReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + + size_t + DoReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + + lldb_private::ArchSpec + GetArchitecture(); + + lldb_private::Error + GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &range_info) override; + + protected: + void + Clear(); + + bool + UpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; + + private: + // Describes a range of memory captured in the mini dump. + struct Range { + lldb::addr_t start; // virtual address of the beginning of the range + size_t size; // size of the range in bytes + const uint8_t *ptr; // absolute pointer to the first byte of the range + }; + + // If the mini dump has a memory range that contains the desired address, it + // returns true with the details of the range in *range_out. Otherwise, it + // returns false. + bool + FindMemoryRange(lldb::addr_t addr, Range *range_out) const; + + lldb_private::Error + MapMiniDumpIntoMemory(const char *file); + + lldb_private::ArchSpec + DetermineArchitecture(); + + void + ReadExceptionRecord(); + + void + ReadMiscInfo(); + + void + ReadModuleList(); + + // A thin wrapper around WinAPI's MiniDumpReadDumpStream to avoid redundant + // checks. If there's a failure (e.g., if the requested stream doesn't exist), + // the function returns nullptr and sets *size_out to 0. + void * + FindDumpStream(unsigned stream_number, size_t *size_out) const; + + // Isolate the data to keep Windows-specific types out of this header. Can't + // use the typical pimpl idiom because the implementation of this class also + // needs access to public and protected members of the base class. + class Data; + std::unique_ptr<Data> m_data_up; +}; + +#endif // liblldb_ProcessWinMiniDump_h_ diff --git a/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp new file mode 100644 index 000000000000..ddcd15b1ae12 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp @@ -0,0 +1,104 @@ +//===-- ThreadWinMiniDump.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ThreadWinMiniDump.h" + +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/windows/windows.h" +#include <DbgHelp.h> + +#include "ProcessWinMiniDump.h" +#if defined(_WIN64) +#include "x64/RegisterContextWindowsMiniDump_x64.h" +#else +#include "x86/RegisterContextWindowsMiniDump_x86.h" +#endif + +using namespace lldb; +using namespace lldb_private; + +// This is a minimal implementation in order to get something running. It will +// be fleshed out as more mini-dump functionality is added. + +class ThreadWinMiniDump::Data { + public: + Data() : m_context(nullptr) {} + const CONTEXT *m_context; +}; + +ThreadWinMiniDump::ThreadWinMiniDump(lldb_private::Process &process, lldb::tid_t tid) : + Thread(process, tid), + m_data(new Data) +{ +} + +ThreadWinMiniDump::~ThreadWinMiniDump() +{ +} + +void +ThreadWinMiniDump::RefreshStateAfterStop() +{ +} + +lldb::RegisterContextSP +ThreadWinMiniDump::GetRegisterContext() +{ + if (m_reg_context_sp.get() == NULL) { + m_reg_context_sp = CreateRegisterContextForFrame (NULL); + } + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadWinMiniDump::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) +{ + const uint32_t concrete_frame_idx = (frame) ? frame->GetConcreteFrameIndex() : 0; + RegisterContextSP reg_ctx_sp; + ArchSpec arch = HostInfo::GetArchitecture(); + switch (arch.GetMachine()) + { + case llvm::Triple::x86: +#if defined(_WIN64) + // FIXME: This is a Wow64 process, create a RegisterContextWindows_Wow64 +#else + reg_ctx_sp.reset(new RegisterContextWindowsMiniDump_x86(*this, concrete_frame_idx, m_data->m_context)); +#endif + break; + case llvm::Triple::x86_64: +#if defined(_WIN64) + reg_ctx_sp.reset(new RegisterContextWindowsMiniDump_x64(*this, concrete_frame_idx, m_data->m_context)); +#else + // LLDB is 32-bit, but the target process is 64-bit. We probably can't debug this. +#endif + default: + break; + } + return reg_ctx_sp; +} + +void +ThreadWinMiniDump::ClearStackFrames() +{ +} + +void +ThreadWinMiniDump::SetContext(const void *context) +{ + if (m_data) + { + m_data->m_context = static_cast<const CONTEXT *>(context); + } +} + +bool +ThreadWinMiniDump::CalculateStopInfo() +{ + return false; +} diff --git a/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h new file mode 100644 index 000000000000..c78925422102 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h @@ -0,0 +1,49 @@ +//===-- ThreadWinMiniDump.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadWinMiniDump_h_ +#define liblldb_ThreadWinMiniDump_h_ + +#include <string> + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/Thread.h" + +class ThreadWinMiniDump : public lldb_private::Thread +{ +public: + ThreadWinMiniDump(lldb_private::Process &process, lldb::tid_t tid); + + virtual + ~ThreadWinMiniDump(); + + void + RefreshStateAfterStop() override; + + lldb::RegisterContextSP + GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + void + ClearStackFrames() override; + + void + SetContext(const void *context); + +protected: + lldb::RegisterContextSP m_reg_context_sp; + class Data; + std::unique_ptr<Data> m_data; // for WinAPI-specific data + + bool CalculateStopInfo() override; +}; + +#endif diff --git a/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.cpp b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.cpp new file mode 100644 index 000000000000..41d9195f1eed --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.cpp @@ -0,0 +1,47 @@ +//===-- RegisterContextWindowsMiniDump_x64.cpp ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-types.h" +#include "lldb/Host/windows/windows.h" + +#include "RegisterContextWindowsMiniDump_x64.h" + +using namespace lldb; + +namespace lldb_private +{ + +RegisterContextWindowsMiniDump_x64::RegisterContextWindowsMiniDump_x64(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context) + : RegisterContextWindows_x64(thread, concrete_frame_idx) +{ + if (context) + { + m_context = *context; + m_context_stale = false; + } +} + +RegisterContextWindowsMiniDump_x64::~RegisterContextWindowsMiniDump_x64() +{ +} + +bool +RegisterContextWindowsMiniDump_x64::WriteRegister(const RegisterInfo * /* reg_info */, const RegisterValue & /* reg_value */) +{ + return false; +} + +bool +RegisterContextWindowsMiniDump_x64::CacheAllRegisterValues() +{ + // Since this is post-mortem debugging, we either have the context or we don't. + return !m_context_stale; +} + +} // namespace lldb_private diff --git a/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.h b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.h new file mode 100644 index 000000000000..86d58046113f --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.h @@ -0,0 +1,36 @@ +//===-- RegisterContextWindowsMiniDump_x64.h --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsMiniDump_x64_H_ +#define liblldb_RegisterContextWindowsMiniDump_x64_H_ + +#include "lldb/lldb-forward.h" +#include "Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsMiniDump_x64 : public RegisterContextWindows_x64 +{ + public: + RegisterContextWindowsMiniDump_x64(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context); + + virtual ~RegisterContextWindowsMiniDump_x64(); + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + protected: + bool CacheAllRegisterValues() override; +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindowsMiniDump_x64_H_ diff --git a/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.cpp b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.cpp new file mode 100644 index 000000000000..2c8a069c5376 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.cpp @@ -0,0 +1,47 @@ +//===-- RegisterContextWindowsMiniDump_x86.cpp ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-types.h" +#include "lldb/Host/windows/windows.h" + +#include "RegisterContextWindowsMiniDump_x86.h" + +using namespace lldb; + +namespace lldb_private +{ + +RegisterContextWindowsMiniDump_x86::RegisterContextWindowsMiniDump_x86(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context) + : RegisterContextWindows_x86(thread, concrete_frame_idx) +{ + if (context) + { + m_context = *context; + m_context_stale = false; + } +} + +RegisterContextWindowsMiniDump_x86::~RegisterContextWindowsMiniDump_x86() +{ +} + +bool +RegisterContextWindowsMiniDump_x86::WriteRegister(const RegisterInfo * /* reg_info */, const RegisterValue & /* reg_value */) +{ + return false; +} + +bool +RegisterContextWindowsMiniDump_x86::CacheAllRegisterValues() +{ + // Since this is post-mortem debugging, we either have the context or we don't. + return !m_context_stale; +} + +} // namespace lldb_private diff --git a/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.h b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.h new file mode 100644 index 000000000000..d36e0cfd9e7b --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.h @@ -0,0 +1,36 @@ +//===-- RegisterContextWindowsMiniDump_x86.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsMiniDump_x86_H_ +#define liblldb_RegisterContextWindowsMiniDump_x86_H_ + +#include "lldb/lldb-forward.h" +#include "Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsMiniDump_x86 : public RegisterContextWindows_x86 +{ + public: + RegisterContextWindowsMiniDump_x86(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context); + + virtual ~RegisterContextWindowsMiniDump_x86(); + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + protected: + bool CacheAllRegisterValues() override; +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindowsMiniDump_x86_H_ diff --git a/source/Plugins/Process/elf-core/CMakeLists.txt b/source/Plugins/Process/elf-core/CMakeLists.txt new file mode 100644 index 000000000000..1a4dd7e9d333 --- /dev/null +++ b/source/Plugins/Process/elf-core/CMakeLists.txt @@ -0,0 +1,11 @@ +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessElfCore + ProcessElfCore.cpp + ThreadElfCore.cpp + RegisterContextPOSIXCore_arm.cpp + RegisterContextPOSIXCore_arm64.cpp + RegisterContextPOSIXCore_mips64.cpp + RegisterContextPOSIXCore_powerpc.cpp + RegisterContextPOSIXCore_x86_64.cpp + ) diff --git a/source/Plugins/Process/elf-core/Makefile b/source/Plugins/Process/elf-core/Makefile new file mode 100644 index 000000000000..8c5b3b800f5a --- /dev/null +++ b/source/Plugins/Process/elf-core/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Process/elf-core/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 := lldbPluginProcessElfCore +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/gdb-remote/CMakeLists.txt b/source/Plugins/Process/gdb-remote/CMakeLists.txt new file mode 100644 index 000000000000..8dbfa453f2c2 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/CMakeLists.txt @@ -0,0 +1,16 @@ +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + include_directories(${LIBXML2_INCLUDE_DIR}) +endif() + +add_lldb_library(lldbPluginProcessGDBRemote + GDBRemoteCommunication.cpp + GDBRemoteCommunicationClient.cpp + GDBRemoteCommunicationServer.cpp + GDBRemoteCommunicationServerCommon.cpp + GDBRemoteCommunicationServerLLGS.cpp + GDBRemoteCommunicationServerPlatform.cpp + GDBRemoteRegisterContext.cpp + ProcessGDBRemote.cpp + ProcessGDBRemoteLog.cpp + ThreadGDBRemote.cpp + ) diff --git a/source/Plugins/Process/gdb-remote/Makefile b/source/Plugins/Process/gdb-remote/Makefile new file mode 100644 index 000000000000..8a9b61077875 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Process/gdb-remote/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 := lldbPluginProcessGDBRemote +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 2e7a5b5384f4..be380a442e3c 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -109,35 +109,35 @@ namespace { { "target-definition-file" , OptionValue::eTypeFileSpec , true, 0 , NULL, NULL, "The file that provides the description for remote target registers." }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; - + enum { ePropertyPacketTimeout, ePropertyTargetDefinitionFile }; - + class PluginProperties : public Properties { public: - + static ConstString GetSettingName () { return ProcessGDBRemote::GetPluginNameStatic(); } - + PluginProperties() : Properties () { m_collection_sp.reset (new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); } - + virtual ~PluginProperties() { } - + uint64_t GetPacketTimeout() { @@ -159,9 +159,9 @@ namespace { return m_collection_sp->GetPropertyAtIndexAsFileSpec (NULL, idx); } }; - + typedef std::shared_ptr<PluginProperties> ProcessKDPPropertiesSP; - + static const ProcessKDPPropertiesSP & GetGlobalPluginProperties() { @@ -170,7 +170,7 @@ namespace { g_settings_sp.reset (new PluginProperties ()); return g_settings_sp; } - + } // anonymous namespace end class ProcessGDBRemote::GDBLoadedModuleInfoList @@ -446,7 +446,7 @@ ProcessGDBRemote::~ProcessGDBRemote() // destruct this class, then Process::~Process() might have problems // trying to fully destroy the broadcaster. Finalize(); - + // The general Finalize is going to try to destroy the process and that SHOULD // shut down the async thread. However, if we don't kill it it will get stranded and // its connection will go away so when it wakes up it will crash. So kill it for sure here. @@ -587,7 +587,7 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout); } - // Register info search order: + // Register info search order: // 1 - Use the target definition python file if one is specified. // 2 - If the target definition doesn't have any of the info from the target.xml (registers) then proceed to read the target.xml. // 3 - Fall back on the qRegisterInfo packets. @@ -614,12 +614,12 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) if (GetGDBServerRegisterInfo ()) return; - + char packet[128]; uint32_t reg_offset = 0; uint32_t reg_num = 0; for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse; - response_type == StringExtractorGDBRemote::eResponse; + response_type == StringExtractorGDBRemote::eResponse; ++reg_num) { const int packet_len = ::snprintf (packet, sizeof(packet), "qRegisterInfo%x", reg_num); @@ -831,7 +831,7 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Error error (WillLaunchOrAttach ()); - + if (error.Fail()) return error; @@ -845,7 +845,7 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) if (pid == LLDB_INVALID_PROCESS_ID) { // We don't have a valid process ID, so note that we are connected - // and could now request to launch or attach, or get remote process + // and could now request to launch or attach, or get remote process // listings... SetPrivateState (eStateConnected); } @@ -864,7 +864,7 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) HandleStopReplySequence(); Target &target = GetTarget(); - if (!target.GetArchitecture().IsValid()) + if (!target.GetArchitecture().IsValid()) { if (m_gdb_comm.GetProcessArchitecture().IsValid()) { @@ -1058,11 +1058,11 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) m_gdb_comm.SetDetachOnError (launch_flags & eLaunchFlagDetachOnError); m_gdb_comm.SendLaunchArchPacket (GetTarget().GetArchitecture().GetArchitectureName()); - + const char * launch_event_data = launch_info.GetLaunchEventData(); if (launch_event_data != NULL && *launch_event_data != '\0') m_gdb_comm.SendLaunchEventDataPacket (launch_event_data); - + if (working_dir) { m_gdb_comm.SetWorkingDir (working_dir); @@ -1134,7 +1134,7 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) } SetPrivateState (SetThreadStopInfo (response)); - + if (!disable_stdio) { if (pty.GetMasterFileDescriptor() != lldb_utility::PseudoTerminal::invalid_fd) @@ -1152,8 +1152,8 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) { // Set our user ID to an invalid process ID. SetID(LLDB_INVALID_PROCESS_ID); - error.SetErrorStringWithFormat ("failed to get object file from '%s' for arch %s", - exe_module->GetFileSpec().GetFilename().AsCString(), + error.SetErrorStringWithFormat ("failed to get object file from '%s' for arch %s", + exe_module->GetFileSpec().GetFilename().AsCString(), exe_module->GetArchitecture().GetArchitectureName()); } return error; @@ -1167,7 +1167,7 @@ ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) Error error; // Only connect if we have a valid connect URL Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); - + if (connect_url && connect_url[0]) { if (log) @@ -1189,9 +1189,9 @@ ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) // If we were interrupted, don't keep retrying. break; } - + retry_count++; - + if (retry_count >= max_retry_count) break; @@ -1216,7 +1216,7 @@ ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) // We always seem to be able to open a connection to a local port // so we need to make sure we can then send data to it. If we can't // then we aren't actually connected to anything, so try and do the - // handshake with the remote GDB server and make sure that goes + // handshake with the remote GDB server and make sure that goes // alright. if (!m_gdb_comm.HandshakeWithServer (&error)) { @@ -1382,7 +1382,7 @@ ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const Process char packet[64]; const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); - SetID (attach_pid); + SetID (attach_pid); m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet, packet_len)); } else @@ -1405,9 +1405,9 @@ ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, const Pro if (error.Success()) { StreamString packet; - + m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); - + if (attach_info.GetWaitForLaunch()) { if (!m_gdb_comm.GetVAttachOrWaitSupported()) @@ -1426,7 +1426,7 @@ ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, const Pro packet.PutCString("vAttachName"); packet.PutChar(';'); packet.PutBytesAsRawHex8(process_name, strlen(process_name), endian::InlHostByteOrder(), endian::InlHostByteOrder()); - + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet.GetData(), packet.GetSize())); } @@ -1471,12 +1471,12 @@ ProcessGDBRemote::DoResume () Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::Resume()"); - + Listener listener ("gdb-remote.resume-packet-sent"); if (listener.StartListeningForEvents (&m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) { listener.StartListeningForEvents (&m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); - + const size_t num_threads = GetThreadList().GetSize(); StreamString continue_packet; @@ -1496,7 +1496,7 @@ ProcessGDBRemote::DoResume () else { continue_packet.PutCString ("vCont"); - + if (!m_continue_c_tids.empty()) { if (m_gdb_comm.GetVContSupported ('c')) @@ -1504,10 +1504,10 @@ ProcessGDBRemote::DoResume () for (tid_collection::const_iterator t_pos = m_continue_c_tids.begin(), t_end = m_continue_c_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";c:%4.4" PRIx64, *t_pos); } - else + else continue_packet_error = true; } - + if (!continue_packet_error && !m_continue_C_tids.empty()) { if (m_gdb_comm.GetVContSupported ('C')) @@ -1515,7 +1515,7 @@ ProcessGDBRemote::DoResume () for (tid_sig_collection::const_iterator s_pos = m_continue_C_tids.begin(), s_end = m_continue_C_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } - else + else continue_packet_error = true; } @@ -1526,10 +1526,10 @@ ProcessGDBRemote::DoResume () for (tid_collection::const_iterator t_pos = m_continue_s_tids.begin(), t_end = m_continue_s_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";s:%4.4" PRIx64, *t_pos); } - else + else continue_packet_error = true; } - + if (!continue_packet_error && !m_continue_S_tids.empty()) { if (m_gdb_comm.GetVContSupported ('S')) @@ -1540,14 +1540,14 @@ ProcessGDBRemote::DoResume () else continue_packet_error = true; } - + if (continue_packet_error) continue_packet.GetString().clear(); } } else continue_packet_error = true; - + if (continue_packet_error) { // Either no vCont support, or we tried to use part of the vCont @@ -1563,33 +1563,33 @@ ProcessGDBRemote::DoResume () { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun (-1); - continue_packet.PutChar ('c'); + continue_packet.PutChar ('c'); continue_packet_error = false; } else if (num_continue_c_tids == 1 && - num_continue_C_tids == 0 && - num_continue_s_tids == 0 && + num_continue_C_tids == 0 && + num_continue_s_tids == 0 && num_continue_S_tids == 0 ) { // Only one thread is continuing m_gdb_comm.SetCurrentThreadForRun (m_continue_c_tids.front()); - continue_packet.PutChar ('c'); + continue_packet.PutChar ('c'); continue_packet_error = false; } } if (continue_packet_error && num_continue_C_tids > 0) { - if ((num_continue_C_tids + num_continue_c_tids) == num_threads && - num_continue_C_tids > 0 && - num_continue_s_tids == 0 && + if ((num_continue_C_tids + num_continue_c_tids) == num_threads && + num_continue_C_tids > 0 && + num_continue_s_tids == 0 && num_continue_S_tids == 0 ) { const int continue_signo = m_continue_C_tids.front().second; // Only one thread is continuing if (num_continue_C_tids > 1) { - // More that one thread with a signal, yet we don't have + // More that one thread with a signal, yet we don't have // vCont support and we are being asked to resume each // thread with a signal, we need to make sure they are // all the same signal, or we can't issue the continue @@ -1641,13 +1641,13 @@ ProcessGDBRemote::DoResume () continue_packet_error = false; } else if (num_continue_c_tids == 0 && - num_continue_C_tids == 0 && - num_continue_s_tids == 1 && + num_continue_C_tids == 0 && + num_continue_s_tids == 1 && num_continue_S_tids == 0 ) { // Only one thread is stepping m_gdb_comm.SetCurrentThreadForRun (m_continue_s_tids.front()); - continue_packet.PutChar ('s'); + continue_packet.PutChar ('s'); continue_packet_error = false; } } @@ -1675,8 +1675,8 @@ ProcessGDBRemote::DoResume () } } else if (num_continue_c_tids == 0 && - num_continue_C_tids == 0 && - num_continue_s_tids == 0 && + num_continue_C_tids == 0 && + num_continue_s_tids == 0 && num_continue_S_tids == 1 ) { // Only one thread is stepping with signal @@ -1704,7 +1704,7 @@ ProcessGDBRemote::DoResume () log->Printf ("ProcessGDBRemote::DoResume: Trying to resume but the async thread is dead."); return error; } - + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (continue_packet.GetData(), continue_packet.GetSize())); if (listener.WaitForEvent (&timeout, event_sp) == false) @@ -1890,7 +1890,7 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD)); if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) log->Printf ("ProcessGDBRemote::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID()); - + size_t num_thread_ids = m_thread_ids.size(); // The "m_thread_ids" thread ID list should always be updated after each stop // reply packet, but in case it isn't, update it here. @@ -1926,7 +1926,7 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new thread_sp->GetID()); } // The m_thread_pcs vector has pc values in big-endian order, not target-endian, unlike most - // of the register read/write packets in gdb-remote protocol. + // of the register read/write packets in gdb-remote protocol. // Early in the process startup, we may not yet have set the process ByteOrder so we ignore these; // they are a performance improvement over fetching thread register values individually, the // method we will fall back to if needed. @@ -1936,7 +1936,7 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new RegisterContextSP reg_ctx_sp (thread_sp->GetRegisterContext()); if (reg_ctx_sp) { - uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber + uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); if (pc_regnum != LLDB_INVALID_REGNUM) { @@ -1947,7 +1947,7 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new new_thread_list.AddThread(thread_sp); } } - + // Whatever that is left in old_thread_list_copy are not // present in new_thread_list. Remove non-existent threads from internal id table. size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); @@ -1960,7 +1960,7 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new m_thread_id_to_index_id_map.erase(old_thread_id); } } - + return true; } @@ -2658,10 +2658,10 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) if (tid == LLDB_INVALID_THREAD_ID) { - // A thread id may be invalid if the response is old style 'S' packet which does not provide the + // A thread id may be invalid if the response is old style 'S' packet which does not provide the // thread information. So update the thread list and choose the first one. UpdateThreadIDList (); - + if (!m_thread_ids.empty ()) { tid = m_thread_ids.front (); @@ -2742,7 +2742,7 @@ ProcessGDBRemote::RefreshStateAfterStop () // Let all threads recover from stopping and do any clean up based // on the previous thread state (if any). m_thread_list_real.RefreshStateAfterStop(); - + } Error @@ -2752,7 +2752,7 @@ ProcessGDBRemote::DoHalt (bool &caused_stop) bool timed_out = false; Mutex::Locker locker; - + if (m_public_state.GetValue() == eStateAttaching) { // We are being asked to halt during an attach. We need to just close @@ -2768,7 +2768,7 @@ ProcessGDBRemote::DoHalt (bool &caused_stop) else error.SetErrorString("unknown error sending interrupt packet"); } - + caused_stop = m_gdb_comm.GetInterruptWasSent (); } return error; @@ -2781,7 +2781,7 @@ ProcessGDBRemote::DoDetach(bool keep_stopped) Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped); - + error = m_gdb_comm.Detach (keep_stopped); if (log) { @@ -2790,7 +2790,7 @@ ProcessGDBRemote::DoDetach(bool keep_stopped) else log->Printf ("ProcessGDBRemote::DoDetach() detach packet send failed: %s", error.AsCString() ? error.AsCString() : "<unknown error>"); } - + if (!error.Success()) return error; @@ -2833,7 +2833,7 @@ ProcessGDBRemote::DoDestroy () if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning) { PlatformSP platform_sp = GetTarget().GetPlatform(); - + // FIXME: These should be ConstStrings so we aren't doing strcmp'ing. if (platform_sp && platform_sp->GetName() @@ -2845,18 +2845,18 @@ ProcessGDBRemote::DoDestroy () log->PutCString ("ProcessGDBRemote::DoDestroy() - Tried resuming to destroy once already, not doing it again."); } else - { + { // At present, the plans are discarded and the breakpoints disabled Process::Destroy, // but we really need it to happen here and it doesn't matter if we do it twice. m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); - + bool stop_looks_like_crash = false; ThreadList &threads = GetThreadList(); - + { Mutex::Locker locker(threads.GetMutex()); - + size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { @@ -2877,21 +2877,21 @@ ProcessGDBRemote::DoDestroy () } } } - + if (stop_looks_like_crash) { if (log) log->PutCString ("ProcessGDBRemote::DoDestroy() - Stopped at a breakpoint, continue and then kill."); m_destroy_tried_resuming = true; - - // If we are going to run again before killing, it would be good to suspend all the threads + + // If we are going to run again before killing, it would be good to suspend all the threads // before resuming so they won't get into more trouble. Sadly, for the threads stopped with // the breakpoint or exception, the exception doesn't get cleared if it is suspended, so we do // have to run the risk of letting those threads proceed a bit. - + { Mutex::Locker locker(threads.GetMutex()); - + size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { @@ -2916,7 +2916,7 @@ ProcessGDBRemote::DoDestroy () } } } - + // Interrupt if our inferior is running... int exit_status = SIGABRT; std::string exit_string; @@ -3098,7 +3098,7 @@ ProcessGDBRemote::DoReadMemory (addr_t addr, void *buf, size_t size, Error &erro if (size > m_max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be - // called multiple times in order to complete the task by + // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = m_max_memory_size; } @@ -3156,7 +3156,7 @@ ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Erro if (size > m_max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be - // called multiple times in order to complete the task by + // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = m_max_memory_size; } @@ -3191,7 +3191,7 @@ ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &er { Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_EXPRESSIONS)); addr_t allocated_addr = LLDB_INVALID_ADDRESS; - + LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { @@ -3222,7 +3222,7 @@ ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &er } break; } - + if (allocated_addr == LLDB_INVALID_ADDRESS) error.SetErrorStringWithFormat("unable to allocate %" PRIu64 " bytes of memory with permissions %s", (uint64_t)size, GetPermissionsAsCString (permissions)); else @@ -3231,10 +3231,10 @@ ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &er } Error -ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr, +ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr, MemoryRegionInfo ®ion_info) { - + Error error (m_gdb_comm.GetMemoryRegionInfo (load_addr, region_info)); return error; } @@ -3242,7 +3242,7 @@ ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr, Error ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num) { - + Error error (m_gdb_comm.GetWatchpointSupportInfo (num)); return error; } @@ -3257,13 +3257,13 @@ ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num, bool& after) Error ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr) { - Error error; + Error error; LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { case eLazyBoolCalculate: - // We should never be deallocating memory without allocating memory + // We should never be deallocating memory without allocating memory // first so we should never get eLazyBoolCalculate error.SetErrorString ("tried to deallocate memory without ever allocating memory"); break; @@ -3272,7 +3272,7 @@ ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr) if (!m_gdb_comm.DeallocateMemory (addr)) error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); break; - + case eLazyBoolNo: // Call munmap() to deallocate memory in the inferior.. { @@ -3448,7 +3448,7 @@ ProcessGDBRemote::DisableBreakpointSite (BreakpointSite *bp_site) stoppoint_type = eBreakpointHardware; else stoppoint_type = eBreakpointSoftware; - + if (m_gdb_comm.SendGDBStoppointTypePacket(stoppoint_type, false, addr, bp_op_size)) error.SetErrorToGenericError(); } @@ -3554,7 +3554,7 @@ ProcessGDBRemote::DisableWatchpoint (Watchpoint *wp, bool notify) wp->SetEnabled(false, notify); return error; } - + if (wp->IsHardware()) { GDBStoppointType type = GetGDBStoppointType(wp); @@ -3565,7 +3565,7 @@ ProcessGDBRemote::DisableWatchpoint (Watchpoint *wp, bool notify) return error; } else - error.SetErrorString("sending gdb watchpoint packet failed"); + error.SetErrorString("sending gdb watchpoint packet failed"); } // TODO: clear software watchpoints if we implement them } @@ -3669,7 +3669,7 @@ ProcessGDBRemote::LaunchAndConnectToDebugserver (const ProcessInfo &process_info if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) StartAsyncThread (); - + if (error.Fail()) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); @@ -3678,7 +3678,7 @@ ProcessGDBRemote::LaunchAndConnectToDebugserver (const ProcessInfo &process_info log->Printf("failed to start debugserver process: %s", error.AsCString()); return error; } - + if (m_gdb_comm.IsConnected()) { // Finish the connection process by doing the handshake without connecting (send NULL URL) @@ -3708,7 +3708,7 @@ ProcessGDBRemote::MonitorDebugserverProcess // The baton is a "ProcessGDBRemote *". Now this class might be gone // and might not exist anymore, so we need to carefully try to get the // target for this process first since we have a race condition when - // we are done running between getting the notice that the inferior + // we are done running between getting the notice that the inferior // process has died and the debugserver that was debugging this process. // In our test suite, we are also continually running process after // process, so we must be very careful to make sure: @@ -3738,7 +3738,7 @@ ProcessGDBRemote::MonitorDebugserverProcess ProcessSP process_sp (target_sp->GetProcessSP()); // Now we have a shared pointer to the process that can't go away on us // so we now make sure it was the same as the one passed in, and also make - // sure that our previous "process *" didn't get deleted and have a new + // sure that our previous "process *" didn't get deleted and have a new // "process *" created in its place with the same pointer. To verify this // we make sure the process has our debugserver process ID. If we pass all // of these tests, then we are sure that this process is the one we were @@ -3752,7 +3752,7 @@ ProcessGDBRemote::MonitorDebugserverProcess // If our process hasn't yet exited, debugserver might have died. // If the process did exit, the we are reaping it. const StateType state = process->GetState(); - + if (process->m_debugserver_pid != LLDB_INVALID_PROCESS_ID && state != eStateInvalid && state != eStateUnloaded && @@ -3828,7 +3828,7 @@ ProcessGDBRemote::StartAsyncThread () if (log) log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); - + Mutex::Locker start_locker(m_async_thread_state_mutex); if (!m_async_thread.IsJoinable()) { @@ -3855,7 +3855,7 @@ ProcessGDBRemote::StopAsyncThread () if (m_async_thread.IsJoinable()) { m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); - + // This will shut down the async thread. m_gdb_comm.Disconnect(); // Disconnect from the debug server. @@ -3972,7 +3972,7 @@ ProcessGDBRemote::AsyncThread (void *arg) process->SetLastStopPacket (response); process->ClearThreadIDList(); response.SetFilePos(1); - + int exit_status = response.GetHexU8(); const char *desc_cstr = NULL; StringExtractor extractor; @@ -4091,7 +4091,7 @@ ProcessGDBRemote::AsyncThread (void *arg) // { // return Host::ListProcessesMatchingName (name, matches, pids); // } -// else +// else // { // // FIXME: Implement talking to the remote debugserver. // return 0; @@ -4105,7 +4105,7 @@ ProcessGDBRemote::NewThreadNotifyBreakpointHit (void *baton, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { - // I don't think I have to do anything here, just make sure I notice the new thread when it starts to + // I don't think I have to do anything here, just make sure I notice the new thread when it starts to // run so I can stop it if that's what I want to do. Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log) @@ -4148,7 +4148,7 @@ ProcessGDBRemote::StartNoticingNewThreads() bool ProcessGDBRemote::StopNoticingNewThreads() -{ +{ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf ("Disabling new thread notification breakpoint."); @@ -4158,7 +4158,7 @@ ProcessGDBRemote::StopNoticingNewThreads() return true; } - + DynamicLoader * ProcessGDBRemote::GetDynamicLoader () { @@ -4172,9 +4172,9 @@ ProcessGDBRemote::SendEventData(const char *data) { int return_value; bool was_supported; - + Error error; - + return_value = m_gdb_comm.SendLaunchEventDataPacket (data, &was_supported); if (return_value != 0) { @@ -4284,8 +4284,8 @@ ProcessGDBRemote::GetLoadedDynamicLibrariesInfos (lldb::addr_t image_list_addres // Establish the largest memory read/write payloads we should use. // If the remote stub has a max packet size, stay under that size. -// -// If the remote stub's max packet size is crazy large, use a +// +// If the remote stub's max packet size is crazy large, use a // reasonable largeish default. // // If the remote stub doesn't advertise a max packet size, use a @@ -4398,7 +4398,7 @@ struct RegisterSetInfo }; typedef std::map<uint32_t, RegisterSetInfo> RegisterSetMap; - + struct GdbServerTargetInfo { std::string arch; @@ -4407,13 +4407,13 @@ struct GdbServerTargetInfo RegisterSetMap reg_set_map; XMLNode feature_node; }; - + bool ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp) { if (!feature_node) return false; - + uint32_t cur_reg_num = 0; uint32_t reg_offset = 0; @@ -4443,7 +4443,7 @@ ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemot NULL, NULL }; - + reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type, ®_name, &alt_name, &set_name, &value_regs, &invalidate_regs, &encoding_set, &format_set, ®_info, &cur_reg_num, ®_offset](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") { @@ -4538,7 +4538,7 @@ ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemot } return true; // Keep iterating through all attributes }); - + if (!gdb_type.empty() && !(encoding_set || format_set)) { if (gdb_type.find("int") == 0) @@ -4557,12 +4557,12 @@ ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemot reg_info.encoding = eEncodingIEEE754; } } - + // Only update the register set name if we didn't get a "reg_set" attribute. // "set_name" will be empty if we didn't have a "reg_set" attribute. if (!set_name && !gdb_group.empty()) set_name.SetCString(gdb_group.c_str()); - + reg_info.byte_offset = reg_offset; assert (reg_info.byte_size != 0); reg_offset += reg_info.byte_size; @@ -4576,16 +4576,16 @@ ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemot invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } - + ++cur_reg_num; AugmentRegisterInfoViaABI (reg_info, reg_name, abi_sp); dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name); - + return true; // Keep iterating through all "reg" elements }); return true; } - + } // namespace {} @@ -4617,14 +4617,14 @@ ProcessGDBRemote::GetGDBServerRegisterInfo () { return false; } - + XMLDocument xml_document; if (xml_document.ParseMemory(raw.c_str(), raw.size(), "target.xml")) { GdbServerTargetInfo target_info; - + XMLNode target_node = xml_document.GetRootElement("target"); if (target_node) { @@ -4655,7 +4655,7 @@ ProcessGDBRemote::GetGDBServerRegisterInfo () node.ForEachChildElementWithName("group", [&target_info](const XMLNode &node) -> bool { uint32_t set_id = UINT32_MAX; RegisterSetInfo set_info; - + node.ForEachAttribute([&set_id, &set_info](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "id") set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); @@ -4663,7 +4663,7 @@ ProcessGDBRemote::GetGDBServerRegisterInfo () set_info.name = ConstString(value); return true; // Keep iterating through all attributes }); - + if (set_id != UINT32_MAX) target_info.reg_set_map[set_id] = set_info; return true; // Keep iterating through all "group" elements @@ -4671,12 +4671,12 @@ ProcessGDBRemote::GetGDBServerRegisterInfo () } return true; // Keep iterating through all children of the target_node }); - + if (feature_node) { ParseRegisters(feature_node, target_info, this->m_register_info, GetABI()); } - + for (const auto &include : target_info.includes) { // request register file @@ -4730,7 +4730,7 @@ ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list) if (log) log->Printf ("parsing: %s", raw.c_str()); XMLDocument doc; - + if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return Error (0, ErrorType::eErrorTypeGeneric); @@ -4750,7 +4750,7 @@ ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list) GDBLoadedModuleInfoList::LoadedModuleInfo module; library.ForEachAttribute([log, &module](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { - + if (name == "name") module.set_name (value.str()); else if (name == "lm") @@ -4770,7 +4770,7 @@ ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list) // the memory address of the libraries PT_DYAMIC section. module.set_dynamic(StringConvert::ToUInt64(value.data(), LLDB_INVALID_ADDRESS, 0)); } - + return true; // Keep iterating over all properties of "library" }); @@ -5075,7 +5075,7 @@ protected: class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { private: - + public: CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, @@ -5084,11 +5084,11 @@ public: NULL) { } - + ~CommandObjectProcessGDBRemotePacketHistory () { } - + bool DoExecute (Args& command, CommandReturnObject &result) override { @@ -5115,7 +5115,7 @@ public: class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed { private: - + public: CommandObjectProcessGDBRemotePacketXferSize(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, @@ -5124,11 +5124,11 @@ public: NULL) { } - + ~CommandObjectProcessGDBRemotePacketXferSize () { } - + bool DoExecute (Args& command, CommandReturnObject &result) override { @@ -5162,7 +5162,7 @@ public: class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed { private: - + public: CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, @@ -5172,11 +5172,11 @@ public: NULL) { } - + ~CommandObjectProcessGDBRemotePacketSend () { } - + bool DoExecute (Args& command, CommandReturnObject &result) override { @@ -5187,7 +5187,7 @@ public: result.SetStatus (eReturnStatusFailed); return false; } - + ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { @@ -5201,7 +5201,7 @@ public: Stream &output_strm = result.GetOutputStream(); output_strm.Printf (" packet: %s\n", packet_cstr); std::string &response_str = response.GetStringRef(); - + if (strstr(packet_cstr, "qGetProfileData") != NULL) { response_str = process->GetGDBRemote().HarmonizeThreadIdsForProfileData(process, response); @@ -5220,7 +5220,7 @@ public: class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw { private: - + public: CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : CommandObjectRaw (interpreter, @@ -5230,11 +5230,11 @@ public: NULL) { } - + ~CommandObjectProcessGDBRemotePacketMonitor () { } - + bool DoExecute (const char *command, CommandReturnObject &result) override { @@ -5244,7 +5244,7 @@ public: result.SetStatus (eReturnStatusFailed); return false; } - + ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { @@ -5252,7 +5252,7 @@ public: packet.PutCString("qRcmd,"); packet.PutBytesAsRawHex8(command, strlen(command)); const char *packet_cstr = packet.GetString().c_str(); - + bool send_async = true; StringExtractorGDBRemote response; process->GetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async); @@ -5260,7 +5260,7 @@ public: Stream &output_strm = result.GetOutputStream(); output_strm.Printf (" packet: %s\n", packet_cstr); const std::string &response_str = response.GetStringRef(); - + if (response_str.empty()) output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n"); else @@ -5273,7 +5273,7 @@ public: class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword { private: - + public: CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, @@ -5287,10 +5287,10 @@ public: LoadSubCommand ("xfer-size", CommandObjectSP (new CommandObjectProcessGDBRemotePacketXferSize (interpreter))); LoadSubCommand ("speed-test", CommandObjectSP (new CommandObjectProcessGDBRemoteSpeedTest (interpreter))); } - + ~CommandObjectProcessGDBRemotePacket () { - } + } }; class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword diff --git a/source/Plugins/Process/mach-core/CMakeLists.txt b/source/Plugins/Process/mach-core/CMakeLists.txt new file mode 100644 index 000000000000..ac54658cf4dc --- /dev/null +++ b/source/Plugins/Process/mach-core/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessMachCore + ProcessMachCore.cpp + ThreadMachCore.cpp + ) diff --git a/source/Plugins/Process/mach-core/Makefile b/source/Plugins/Process/mach-core/Makefile new file mode 100644 index 000000000000..6db849872267 --- /dev/null +++ b/source/Plugins/Process/mach-core/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Process/mach-core/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 := lldbPluginProcessMachCore +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/source/Plugins/Process/mach-core/ProcessMachCore.cpp new file mode 100644 index 000000000000..b199ec606367 --- /dev/null +++ b/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -0,0 +1,520 @@ +//===-- ProcessMachCore.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <errno.h> +#include <stdlib.h> + +// C++ Includes +#include "llvm/Support/MathExtras.h" +#include <mutex> + +// Other libraries and framework includes +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +// Project includes +#include "ProcessMachCore.h" +#include "ThreadMachCore.h" +#include "StopInfoMachException.h" + +// Needed for the plug-in names for the dynamic loaders. +#include "lldb/Utility/SafeMachO.h" + +#include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h" +#include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" +#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" + +using namespace lldb; +using namespace lldb_private; + +ConstString +ProcessMachCore::GetPluginNameStatic() +{ + static ConstString g_name("mach-o-core"); + return g_name; +} + +const char * +ProcessMachCore::GetPluginDescriptionStatic() +{ + return "Mach-O core file debugging plug-in."; +} + +void +ProcessMachCore::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessMachCore::CreateInstance); +} + + +lldb::ProcessSP +ProcessMachCore::CreateInstance (lldb::TargetSP target_sp, Listener &listener, const FileSpec *crash_file) +{ + lldb::ProcessSP process_sp; + if (crash_file) + { + const size_t header_size = sizeof(llvm::MachO::mach_header); + lldb::DataBufferSP data_sp (crash_file->ReadFileContents(0, header_size)); + if (data_sp && data_sp->GetByteSize() == header_size) + { + DataExtractor data(data_sp, lldb::eByteOrderLittle, 4); + + lldb::offset_t data_offset = 0; + llvm::MachO::mach_header mach_header; + if (ObjectFileMachO::ParseHeader(data, &data_offset, mach_header)) + { + if (mach_header.filetype == llvm::MachO::MH_CORE) + process_sp.reset(new ProcessMachCore (target_sp, listener, *crash_file)); + } + } + + } + return process_sp; +} + +bool +ProcessMachCore::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) +{ + if (plugin_specified_by_name) + return true; + + // For now we are just making sure the file exists for a given module + if (!m_core_module_sp && m_core_file.Exists()) + { + // Don't add the Target's architecture to the ModuleSpec - we may be working + // with a core file that doesn't have the correct cpusubtype in the header + // but we should still try to use it - ModuleSpecList::FindMatchingModuleSpec + // enforces a strict arch mach. + ModuleSpec core_module_spec(m_core_file); + Error error (ModuleList::GetSharedModule (core_module_spec, + m_core_module_sp, + NULL, + NULL, + NULL)); + + if (m_core_module_sp) + { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile) + return true; + } + } + return false; +} + +//---------------------------------------------------------------------- +// ProcessMachCore constructor +//---------------------------------------------------------------------- +ProcessMachCore::ProcessMachCore(lldb::TargetSP target_sp, Listener &listener, const FileSpec &core_file) : + Process (target_sp, listener), + m_core_aranges (), + m_core_module_sp (), + m_core_file (core_file), + m_dyld_addr (LLDB_INVALID_ADDRESS), + m_mach_kernel_addr (LLDB_INVALID_ADDRESS), + m_dyld_plugin_name () +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessMachCore::~ProcessMachCore() +{ + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +ConstString +ProcessMachCore::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessMachCore::GetPluginVersion() +{ + return 1; +} + +bool +ProcessMachCore::GetDynamicLoaderAddress (lldb::addr_t addr) +{ + llvm::MachO::mach_header header; + Error error; + if (DoReadMemory (addr, &header, sizeof(header), error) != sizeof(header)) + return false; + if (header.magic == llvm::MachO::MH_CIGAM || + header.magic == llvm::MachO::MH_CIGAM_64) + { + header.magic = llvm::ByteSwap_32(header.magic); + header.cputype = llvm::ByteSwap_32(header.cputype); + header.cpusubtype = llvm::ByteSwap_32(header.cpusubtype); + header.filetype = llvm::ByteSwap_32(header.filetype); + header.ncmds = llvm::ByteSwap_32(header.ncmds); + header.sizeofcmds = llvm::ByteSwap_32(header.sizeofcmds); + header.flags = llvm::ByteSwap_32(header.flags); + } + + // TODO: swap header if needed... + //printf("0x%16.16" PRIx64 ": magic = 0x%8.8x, file_type= %u\n", vaddr, header.magic, header.filetype); + if (header.magic == llvm::MachO::MH_MAGIC || + header.magic == llvm::MachO::MH_MAGIC_64) + { + // Check MH_EXECUTABLE to see if we can find the mach image + // that contains the shared library list. The dynamic loader + // (dyld) is what contains the list for user applications, + // and the mach kernel contains a global that has the list + // of kexts to load + switch (header.filetype) + { + case llvm::MachO::MH_DYLINKER: + //printf("0x%16.16" PRIx64 ": file_type = MH_DYLINKER\n", vaddr); + // Address of dyld "struct mach_header" in the core file + m_dyld_addr = addr; + return true; + + case llvm::MachO::MH_EXECUTE: + //printf("0x%16.16" PRIx64 ": file_type = MH_EXECUTE\n", vaddr); + // Check MH_EXECUTABLE file types to see if the dynamic link object flag + // is NOT set. If it isn't, then we have a mach_kernel. + if ((header.flags & llvm::MachO::MH_DYLDLINK) == 0) + { + // Address of the mach kernel "struct mach_header" in the core file. + m_mach_kernel_addr = addr; + return true; + } + break; + } + } + return false; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessMachCore::DoLoadCore () +{ + Error error; + if (!m_core_module_sp) + { + error.SetErrorString ("invalid core module"); + return error; + } + + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile == NULL) + { + error.SetErrorString ("invalid core object file"); + return error; + } + + if (core_objfile->GetNumThreadContexts() == 0) + { + error.SetErrorString ("core file doesn't contain any LC_THREAD load commands, or the LC_THREAD architecture is not supported in this lldb"); + return error; + } + + SectionList *section_list = core_objfile->GetSectionList(); + if (section_list == NULL) + { + error.SetErrorString ("core file has no sections"); + return error; + } + + const uint32_t num_sections = section_list->GetNumSections(0); + if (num_sections == 0) + { + error.SetErrorString ("core file has no sections"); + return error; + } + + SetCanJIT(false); + + llvm::MachO::mach_header header; + DataExtractor data (&header, + sizeof(header), + m_core_module_sp->GetArchitecture().GetByteOrder(), + m_core_module_sp->GetArchitecture().GetAddressByteSize()); + + bool ranges_are_sorted = true; + addr_t vm_addr = 0; + for (uint32_t i=0; i<num_sections; ++i) + { + Section *section = section_list->GetSectionAtIndex (i).get(); + if (section) + { + lldb::addr_t section_vm_addr = section->GetFileAddress(); + FileRange file_range (section->GetFileOffset(), section->GetFileSize()); + VMRangeToFileOffset::Entry range_entry (section_vm_addr, + section->GetByteSize(), + file_range); + + if (vm_addr > section_vm_addr) + ranges_are_sorted = false; + vm_addr = section->GetFileAddress(); + VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back(); +// printf ("LC_SEGMENT[%u] arange=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), frange=[0x%8.8x - 0x%8.8x)\n", +// i, +// range_entry.GetRangeBase(), +// range_entry.GetRangeEnd(), +// range_entry.data.GetRangeBase(), +// range_entry.data.GetRangeEnd()); + + if (last_entry && + last_entry->GetRangeEnd() == range_entry.GetRangeBase() && + last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase()) + { + last_entry->SetRangeEnd (range_entry.GetRangeEnd()); + last_entry->data.SetRangeEnd (range_entry.data.GetRangeEnd()); + //puts("combine"); + } + else + { + m_core_aranges.Append(range_entry); + } + } + } + if (!ranges_are_sorted) + { + m_core_aranges.Sort(); + } + + if (m_dyld_addr == LLDB_INVALID_ADDRESS || m_mach_kernel_addr == LLDB_INVALID_ADDRESS) + { + // We need to locate the main executable in the memory ranges + // we have in the core file. We need to search for both a user-process dyld binary + // and a kernel binary in memory; we must look at all the pages in the binary so + // we don't miss one or the other. Step through all memory segments searching for + // a kernel binary and for a user process dyld -- we'll decide which to prefer + // later if both are present. + + const size_t num_core_aranges = m_core_aranges.GetSize(); + for (size_t i = 0; + i < num_core_aranges && (m_dyld_addr == LLDB_INVALID_ADDRESS || m_mach_kernel_addr == LLDB_INVALID_ADDRESS); + ++i) + { + const VMRangeToFileOffset::Entry *entry = m_core_aranges.GetEntryAtIndex(i); + lldb::addr_t section_vm_addr_start = entry->GetRangeBase(); + lldb::addr_t section_vm_addr_end = entry->GetRangeEnd(); + for (lldb::addr_t section_vm_addr = section_vm_addr_start; + section_vm_addr < section_vm_addr_end; + section_vm_addr += 0x1000) + { + GetDynamicLoaderAddress (section_vm_addr); + } + } + } + + // If we found both a user-process dyld and a kernel binary, we need to decide + // which to prefer. + if (GetCorefilePreference() == eKernelCorefile) + { + if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) + { + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + } + else if (m_dyld_addr != LLDB_INVALID_ADDRESS) + { + m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); + } + } + else + { + if (m_dyld_addr != LLDB_INVALID_ADDRESS) + { + m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); + } + else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) + { + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + } + } + + // Even if the architecture is set in the target, we need to override + // it to match the core file which is always single arch. + ArchSpec arch (m_core_module_sp->GetArchitecture()); + if (arch.GetCore() == ArchSpec::eCore_x86_32_i486) + { + arch.SetTriple ("i386", GetTarget().GetPlatform().get()); + } + if (arch.IsValid()) + GetTarget().SetArchitecture(arch); + + return error; +} + +lldb_private::DynamicLoader * +ProcessMachCore::GetDynamicLoader () +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset (DynamicLoader::FindPlugin(this, m_dyld_plugin_name.IsEmpty() ? NULL : m_dyld_plugin_name.GetCString())); + return m_dyld_ap.get(); +} + +bool +ProcessMachCore::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + if (old_thread_list.GetSize(false) == 0) + { + // Make up the thread the first time this is called so we can setup our one and only + // core thread state. + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + + if (core_objfile) + { + const uint32_t num_threads = core_objfile->GetNumThreadContexts (); + for (lldb::tid_t tid = 0; tid < num_threads; ++tid) + { + ThreadSP thread_sp(new ThreadMachCore (*this, tid)); + new_thread_list.AddThread (thread_sp); + } + } + } + else + { + const uint32_t num_threads = old_thread_list.GetSize(false); + for (uint32_t i=0; i<num_threads; ++i) + new_thread_list.AddThread (old_thread_list.GetThreadAtIndex (i, false)); + } + return new_thread_list.GetSize(false) > 0; +} + +void +ProcessMachCore::RefreshStateAfterStop () +{ + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + m_thread_list.RefreshStateAfterStop(); + //SetThreadStopInfo (m_last_stop_packet); +} + +Error +ProcessMachCore::DoDestroy () +{ + return Error(); +} + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessMachCore::IsAlive () +{ + return true; +} + +bool +ProcessMachCore::WarnBeforeDetach () const +{ + return false; +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ +size_t +ProcessMachCore::ReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + // Don't allow the caching that lldb_private::Process::ReadMemory does + // since in core files we have it all cached our our core file anyway. + return DoReadMemory (addr, buf, size, error); +} + +size_t +ProcessMachCore::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + + if (core_objfile) + { + const VMRangeToFileOffset::Entry *core_memory_entry = m_core_aranges.FindEntryThatContains (addr); + if (core_memory_entry) + { + const addr_t offset = addr - core_memory_entry->GetRangeBase(); + const addr_t bytes_left = core_memory_entry->GetRangeEnd() - addr; + size_t bytes_to_read = size; + if (bytes_to_read > bytes_left) + bytes_to_read = bytes_left; + return core_objfile->CopyData (core_memory_entry->data.GetRangeBase() + offset, bytes_to_read, buf); + } + else + { + error.SetErrorStringWithFormat ("core file does not contain 0x%" PRIx64, addr); + } + } + return 0; +} + +void +ProcessMachCore::Clear() +{ + m_thread_list.Clear(); +} + +void +ProcessMachCore::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + }); +} + +addr_t +ProcessMachCore::GetImageInfoAddress() +{ + // If we found both a user-process dyld and a kernel binary, we need to decide + // which to prefer. + if (GetCorefilePreference() == eKernelCorefile) + { + if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) + { + return m_mach_kernel_addr; + } + return m_dyld_addr; + } + else + { + if (m_dyld_addr != LLDB_INVALID_ADDRESS) + { + return m_dyld_addr; + } + return m_mach_kernel_addr; + } +} + + +lldb_private::ObjectFile * +ProcessMachCore::GetCoreObjectFile () +{ + return m_core_module_sp->GetObjectFile(); +} diff --git a/source/Plugins/Process/mach-core/ProcessMachCore.h b/source/Plugins/Process/mach-core/ProcessMachCore.h new file mode 100644 index 000000000000..2de0b772370c --- /dev/null +++ b/source/Plugins/Process/mach-core/ProcessMachCore.h @@ -0,0 +1,164 @@ +//===-- ProcessMachCore.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessMachCore_h_ +#define liblldb_ProcessMachCore_h_ + +// C Includes +// C++ Includes +#include <list> +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/Process.h" + +class ThreadKDP; + +class ProcessMachCore : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessMachCore(lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec &core_file); + + ~ProcessMachCore() override; + + static lldb::ProcessSP + CreateInstance (lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec *crash_file_path); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + bool + CanDebug (lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + lldb_private::Error + DoLoadCore () override; + + lldb_private::DynamicLoader * + GetDynamicLoader () override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ + lldb_private::Error + DoDestroy () override; + + void + RefreshStateAfterStop() override; + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + bool + IsAlive () override; + + bool + WarnBeforeDetach () const override; + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + size_t + ReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + + size_t + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + + lldb::addr_t + GetImageInfoAddress () override; + +protected: + friend class ThreadMachCore; + + void + Clear ( ); + + bool + UpdateThreadList (lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; + + lldb_private::ObjectFile * + GetCoreObjectFile (); +private: + bool + GetDynamicLoaderAddress (lldb::addr_t addr); + + typedef enum CorefilePreference { eUserProcessCorefile, eKernelCorefile } CorefilePreferences; + + //------------------------------------------------------------------ + /// If a core file can be interpreted multiple ways, this establishes + /// which style wins. + /// + /// If a core file contains both a kernel binary and a user-process + /// dynamic loader, lldb needs to pick one over the other. This could + /// be a kernel corefile that happens to have a coyp of dyld in its + /// memory. Or it could be a user process coredump of lldb while doing + /// kernel debugging - so a copy of the kernel is in its heap. This + /// should become a setting so it can be over-ridden when necessary. + //------------------------------------------------------------------ + CorefilePreference + GetCorefilePreference () + { + // For now, if both user process and kernel binaries a present, + // assume this is a kernel coredump which has a copy of a user + // process dyld in one of its pages. + return eKernelCorefile; + } + + //------------------------------------------------------------------ + // For ProcessMachCore only + //------------------------------------------------------------------ + typedef lldb_private::Range<lldb::addr_t, lldb::addr_t> FileRange; + typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, FileRange> VMRangeToFileOffset; + + VMRangeToFileOffset m_core_aranges; + lldb::ModuleSP m_core_module_sp; + lldb_private::FileSpec m_core_file; + lldb::addr_t m_dyld_addr; + lldb::addr_t m_mach_kernel_addr; + lldb_private::ConstString m_dyld_plugin_name; + + DISALLOW_COPY_AND_ASSIGN (ProcessMachCore); +}; + +#endif // liblldb_ProcessMachCore_h_ diff --git a/source/Plugins/Process/mach-core/ThreadMachCore.cpp b/source/Plugins/Process/mach-core/ThreadMachCore.cpp new file mode 100644 index 000000000000..2720c910e4d1 --- /dev/null +++ b/source/Plugins/Process/mach-core/ThreadMachCore.cpp @@ -0,0 +1,132 @@ +//===-- ThreadMachCore.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "ThreadMachCore.h" + +#include "lldb/Utility/SafeMachO.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/State.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Breakpoint/Watchpoint.h" + +#include "ProcessMachCore.h" +//#include "RegisterContextKDP_arm.h" +//#include "RegisterContextKDP_i386.h" +//#include "RegisterContextKDP_x86_64.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Thread Registers +//---------------------------------------------------------------------- + +ThreadMachCore::ThreadMachCore (Process &process, lldb::tid_t tid) : + Thread(process, tid), + m_thread_name (), + m_dispatch_queue_name (), + m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS), + m_thread_reg_ctx_sp () +{ +} + +ThreadMachCore::~ThreadMachCore () +{ + DestroyThread(); +} + +const char * +ThreadMachCore::GetName () +{ + if (m_thread_name.empty()) + return NULL; + return m_thread_name.c_str(); +} + +void +ThreadMachCore::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context. We don't set "force" to + // true because the stop reply packet might have had some register values + // that were expedited and these will already be copied into the register + // context by the time this function gets called. The KDPRegisterContext + // class has been made smart enough to detect when it needs to invalidate + // which registers are valid by putting hooks in the register read and + // register supply functions where they check the process stop ID and do + // the right thing. + const bool force = false; + GetRegisterContext()->InvalidateIfNeeded (force); +} + +bool +ThreadMachCore::ThreadIDIsValid (lldb::tid_t thread) +{ + return thread != 0; +} + +lldb::RegisterContextSP +ThreadMachCore::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp = CreateRegisterContextForFrame (NULL); + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadMachCore::CreateRegisterContextForFrame (StackFrame *frame) +{ + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex (); + + if (concrete_frame_idx == 0) + { + if (!m_thread_reg_ctx_sp) + { + ProcessSP process_sp (GetProcess()); + + ObjectFile *core_objfile = static_cast<ProcessMachCore *>(process_sp.get())->GetCoreObjectFile (); + if (core_objfile) + m_thread_reg_ctx_sp = core_objfile->GetThreadContextAtIndex (GetID(), *this); + } + reg_ctx_sp = m_thread_reg_ctx_sp; + } + else + { + Unwind *unwinder = GetUnwinder (); + if (unwinder) + reg_ctx_sp = unwinder->CreateRegisterContextForFrame (frame); + } + return reg_ctx_sp; +} + +bool +ThreadMachCore::CalculateStopInfo () +{ + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + SetStopInfo(StopInfo::CreateStopReasonWithSignal (*this, SIGSTOP)); + return true; + } + return false; +} + + diff --git a/source/Plugins/Process/mach-core/ThreadMachCore.h b/source/Plugins/Process/mach-core/ThreadMachCore.h new file mode 100644 index 000000000000..25973540db16 --- /dev/null +++ b/source/Plugins/Process/mach-core/ThreadMachCore.h @@ -0,0 +1,91 @@ +//===-- ThreadMachCore.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadMachCore_h_ +#define liblldb_ThreadMachCore_h_ + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" + +class ProcessMachCore; + +class ThreadMachCore : public lldb_private::Thread +{ +public: + ThreadMachCore (lldb_private::Process &process, + lldb::tid_t tid); + + ~ThreadMachCore() override; + + void + RefreshStateAfterStop() override; + + const char * + GetName() override; + + lldb::RegisterContextSP + GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + static bool + ThreadIDIsValid (lldb::tid_t thread); + + bool + ShouldStop (bool &step_more); + + const char * + GetBasicInfoAsString (); + + void + SetName(const char *name) override + { + if (name && name[0]) + m_thread_name.assign (name); + else + m_thread_name.clear(); + } + + lldb::addr_t + GetThreadDispatchQAddr () + { + return m_thread_dispatch_qaddr; + } + + void + SetThreadDispatchQAddr (lldb::addr_t thread_dispatch_qaddr) + { + m_thread_dispatch_qaddr = thread_dispatch_qaddr; + } + +protected: + friend class ProcessMachCore; + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + std::string m_thread_name; + std::string m_dispatch_queue_name; + lldb::addr_t m_thread_dispatch_qaddr; + lldb::RegisterContextSP m_thread_reg_ctx_sp; + + //------------------------------------------------------------------ + // Protected member functions. + //------------------------------------------------------------------ + bool + CalculateStopInfo() override; +}; + +#endif // liblldb_ThreadMachCore_h_ diff --git a/source/Plugins/ScriptInterpreter/CMakeLists.txt b/source/Plugins/ScriptInterpreter/CMakeLists.txt new file mode 100644 index 000000000000..dc2a27d95a31 --- /dev/null +++ b/source/Plugins/ScriptInterpreter/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(None) +add_subdirectory(Python) diff --git a/source/Plugins/ScriptInterpreter/None/CMakeLists.txt b/source/Plugins/ScriptInterpreter/None/CMakeLists.txt new file mode 100644 index 000000000000..5692d2f90710 --- /dev/null +++ b/source/Plugins/ScriptInterpreter/None/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginScriptInterpreterNone + ScriptInterpreterNone.cpp + )
\ No newline at end of file diff --git a/source/Plugins/ScriptInterpreter/None/Makefile b/source/Plugins/ScriptInterpreter/None/Makefile new file mode 100644 index 000000000000..1e2523198520 --- /dev/null +++ b/source/Plugins/ScriptInterpreter/None/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ScriptInterpreter/None/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 := lldbPluginScriptInterpreterNone +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt b/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt new file mode 100644 index 000000000000..71f5807b6f62 --- /dev/null +++ b/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_library(lldbPluginScriptInterpreterPython + PythonDataObjects.cpp + PythonExceptionState.cpp + ScriptInterpreterPython.cpp + ) diff --git a/source/Plugins/ScriptInterpreter/Python/Makefile b/source/Plugins/ScriptInterpreter/Python/Makefile new file mode 100644 index 000000000000..cf605014d458 --- /dev/null +++ b/source/Plugins/ScriptInterpreter/Python/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ScriptInterpreter/Python/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 := lldbPluginScriptInterpreterPython +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SymbolFile/CMakeLists.txt b/source/Plugins/SymbolFile/CMakeLists.txt new file mode 100644 index 000000000000..add6697389f9 --- /dev/null +++ b/source/Plugins/SymbolFile/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(DWARF) +add_subdirectory(Symtab) diff --git a/source/Plugins/SymbolFile/DWARF/CMakeLists.txt b/source/Plugins/SymbolFile/DWARF/CMakeLists.txt new file mode 100644 index 000000000000..b4658115dfeb --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/CMakeLists.txt @@ -0,0 +1,33 @@ +add_lldb_library(lldbPluginSymbolFileDWARF + DIERef.cpp + DWARFAbbreviationDeclaration.cpp + DWARFASTParserClang.cpp + DWARFASTParserGo.cpp + DWARFAttribute.cpp + DWARFCompileUnit.cpp + DWARFDataExtractor.cpp + DWARFDebugAbbrev.cpp + DWARFDebugAranges.cpp + DWARFDebugArangeSet.cpp + DWARFDebugInfo.cpp + DWARFDebugInfoEntry.cpp + DWARFDebugLine.cpp + DWARFDebugMacro.cpp + DWARFDebugMacinfo.cpp + DWARFDebugMacinfoEntry.cpp + DWARFDebugPubnames.cpp + DWARFDebugPubnamesSet.cpp + DWARFDebugRanges.cpp + DWARFDeclContext.cpp + DWARFDefines.cpp + DWARFDIE.cpp + DWARFDIECollection.cpp + DWARFFormValue.cpp + HashedNameToDIE.cpp + LogChannelDWARF.cpp + NameToDIE.cpp + SymbolFileDWARF.cpp + SymbolFileDWARFDwo.cpp + SymbolFileDWARFDebugMap.cpp + UniqueDWARFASTType.cpp + ) diff --git a/source/Plugins/SymbolFile/DWARF/Makefile b/source/Plugins/SymbolFile/DWARF/Makefile new file mode 100644 index 000000000000..509065650ab9 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/SymbolFile/DWARF/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 := lldbPluginSymbolFileDWARF +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp b/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp index 775bb6718b8a..fe02adbb6c87 100644 --- a/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp +++ b/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp @@ -69,7 +69,7 @@ NameToDIE::Dump (Stream *s) { const char *cstr = m_map.GetCStringAtIndex(i); const DIERef& die_ref = m_map.GetValueAtIndexUnchecked(i); - s->Printf("%p: {0x%8.8x/0x%8.8x} \"%s\"\n", cstr, die_ref.cu_offset, die_ref.die_offset, cstr); + s->Printf("%p: {0x%8.8x/0x%8.8x} \"%s\"\n", (const void*) cstr, die_ref.cu_offset, die_ref.die_offset, cstr); } } diff --git a/source/Plugins/SymbolFile/Symtab/CMakeLists.txt b/source/Plugins/SymbolFile/Symtab/CMakeLists.txt new file mode 100644 index 000000000000..20e406b08ab2 --- /dev/null +++ b/source/Plugins/SymbolFile/Symtab/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginSymbolFileSymtab + SymbolFileSymtab.cpp + ) diff --git a/source/Plugins/SymbolFile/Symtab/Makefile b/source/Plugins/SymbolFile/Symtab/Makefile new file mode 100644 index 000000000000..2c3dbb6d86ab --- /dev/null +++ b/source/Plugins/SymbolFile/Symtab/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/SymbolFile/Symtab/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 := lldbPluginSymbolFileSymtab +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SymbolVendor/CMakeLists.txt b/source/Plugins/SymbolVendor/CMakeLists.txt new file mode 100644 index 000000000000..94862d588727 --- /dev/null +++ b/source/Plugins/SymbolVendor/CMakeLists.txt @@ -0,0 +1,5 @@ +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_subdirectory(MacOSX) +endif() + +add_subdirectory(ELF) diff --git a/source/Plugins/SymbolVendor/ELF/CMakeLists.txt b/source/Plugins/SymbolVendor/ELF/CMakeLists.txt new file mode 100644 index 000000000000..cffc2ef74597 --- /dev/null +++ b/source/Plugins/SymbolVendor/ELF/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginSymbolVendorELF + SymbolVendorELF.cpp + ) diff --git a/source/Plugins/SymbolVendor/ELF/Makefile b/source/Plugins/SymbolVendor/ELF/Makefile new file mode 100644 index 000000000000..47c24a2bda34 --- /dev/null +++ b/source/Plugins/SymbolVendor/ELF/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/SymbolVendor/ELF/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 := lldbPluginSymbolVendorELF +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SymbolVendor/MacOSX/CMakeLists.txt b/source/Plugins/SymbolVendor/MacOSX/CMakeLists.txt new file mode 100644 index 000000000000..093766ac07d5 --- /dev/null +++ b/source/Plugins/SymbolVendor/MacOSX/CMakeLists.txt @@ -0,0 +1,5 @@ +include_directories(${LIBXML2_INCLUDE_DIR}) + +add_lldb_library(lldbPluginSymbolVendorMacOSX + SymbolVendorMacOSX.cpp + ) diff --git a/source/Plugins/SymbolVendor/MacOSX/Makefile b/source/Plugins/SymbolVendor/MacOSX/Makefile new file mode 100644 index 000000000000..9f71ad669aa0 --- /dev/null +++ b/source/Plugins/SymbolVendor/MacOSX/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/SymbolVendor/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 := ../../../.. +LIBRARYNAME := lldbPluginSymbolVendorMacOSX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp b/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp new file mode 100644 index 000000000000..7d21fbc0ede4 --- /dev/null +++ b/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp @@ -0,0 +1,251 @@ +//===-- SymbolVendorMacOSX.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolVendorMacOSX.h" + +#include <string.h> + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Host/XML.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// SymbolVendorMacOSX constructor +//---------------------------------------------------------------------- +SymbolVendorMacOSX::SymbolVendorMacOSX(const lldb::ModuleSP &module_sp) : + SymbolVendor (module_sp) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SymbolVendorMacOSX::~SymbolVendorMacOSX() +{ +} + + +static bool +UUIDsMatch(Module *module, ObjectFile *ofile, lldb_private::Stream *feedback_strm) +{ + if (module && ofile) + { + // Make sure the UUIDs match + lldb_private::UUID dsym_uuid; + + if (!ofile->GetUUID(&dsym_uuid)) + { + if (feedback_strm) + { + feedback_strm->PutCString("warning: failed to get the uuid for object file: '"); + ofile->GetFileSpec().Dump(feedback_strm); + feedback_strm->PutCString("\n"); + } + return false; + } + + if (dsym_uuid == module->GetUUID()) + return true; + + // Emit some warning messages since the UUIDs do not match! + if (feedback_strm) + { + feedback_strm->PutCString("warning: UUID mismatch detected between modules:\n "); + module->GetUUID().Dump(feedback_strm); + feedback_strm->PutChar(' '); + module->GetFileSpec().Dump(feedback_strm); + feedback_strm->PutCString("\n "); + dsym_uuid.Dump(feedback_strm); + feedback_strm->PutChar(' '); + ofile->GetFileSpec().Dump(feedback_strm); + feedback_strm->EOL(); + } + } + return false; +} + +void +SymbolVendorMacOSX::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolVendorMacOSX::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +SymbolVendorMacOSX::GetPluginNameStatic() +{ + static ConstString g_name("macosx"); + return g_name; +} + +const char * +SymbolVendorMacOSX::GetPluginDescriptionStatic() +{ + return "Symbol vendor for MacOSX that looks for dSYM files that match executables."; +} + + + +//---------------------------------------------------------------------- +// CreateInstance +// +// Platforms can register a callback to use when creating symbol +// vendors to allow for complex debug information file setups, and to +// also allow for finding separate debug information files. +//---------------------------------------------------------------------- +SymbolVendor* +SymbolVendorMacOSX::CreateInstance (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm) +{ + if (!module_sp) + return NULL; + + ObjectFile * obj_file = module_sp->GetObjectFile(); + if (!obj_file) + return NULL; + + static ConstString obj_file_macho("mach-o"); + ConstString obj_name = obj_file->GetPluginName(); + if (obj_name != obj_file_macho) + return NULL; + + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolVendorMacOSX::CreateInstance (module = %s)", + module_sp->GetFileSpec().GetPath().c_str()); + SymbolVendorMacOSX* symbol_vendor = new SymbolVendorMacOSX(module_sp); + if (symbol_vendor) + { + char path[PATH_MAX]; + path[0] = '\0'; + + // Try and locate the dSYM file on Mac OS X + Timer scoped_timer2 ("SymbolVendorMacOSX::CreateInstance () locate dSYM", + "SymbolVendorMacOSX::CreateInstance (module = %s) locate dSYM", + module_sp->GetFileSpec().GetPath().c_str()); + + // First check to see if the module has a symbol file in mind already. + // If it does, then we MUST use that. + FileSpec dsym_fspec (module_sp->GetSymbolFileFileSpec()); + + ObjectFileSP dsym_objfile_sp; + if (!dsym_fspec) + { + // No symbol file was specified in the module, lets try and find + // one ourselves. + FileSpec file_spec = obj_file->GetFileSpec(); + if (!file_spec) + file_spec = module_sp->GetFileSpec(); + + ModuleSpec module_spec(file_spec, module_sp->GetArchitecture()); + module_spec.GetUUID() = module_sp->GetUUID(); + dsym_fspec = Symbols::LocateExecutableSymbolFile (module_spec); + if (module_spec.GetSourceMappingList().GetSize()) + module_sp->GetSourceMappingList().Append (module_spec.GetSourceMappingList (), true); + } + + if (dsym_fspec) + { + DataBufferSP dsym_file_data_sp; + lldb::offset_t dsym_file_data_offset = 0; + dsym_objfile_sp = ObjectFile::FindPlugin(module_sp, &dsym_fspec, 0, dsym_fspec.GetByteSize(), dsym_file_data_sp, dsym_file_data_offset); + if (UUIDsMatch(module_sp.get(), dsym_objfile_sp.get(), feedback_strm)) + { + // We need a XML parser if we hope to parse a plist... + if (XMLDocument::XMLEnabled()) + { + char dsym_path[PATH_MAX]; + if (module_sp->GetSourceMappingList().IsEmpty() && dsym_fspec.GetPath(dsym_path, sizeof(dsym_path))) + { + lldb_private::UUID dsym_uuid; + if (dsym_objfile_sp->GetUUID(&dsym_uuid)) + { + std::string uuid_str = dsym_uuid.GetAsString (); + if (!uuid_str.empty()) + { + char *resources = strstr (dsym_path, "/Contents/Resources/"); + if (resources) + { + char dsym_uuid_plist_path[PATH_MAX]; + resources[strlen("/Contents/Resources/")] = '\0'; + snprintf(dsym_uuid_plist_path, sizeof(dsym_uuid_plist_path), "%s%s.plist", dsym_path, uuid_str.c_str()); + FileSpec dsym_uuid_plist_spec(dsym_uuid_plist_path, false); + if (dsym_uuid_plist_spec.Exists()) + { + ApplePropertyList plist(dsym_uuid_plist_path); + if (plist) + { + std::string DBGBuildSourcePath; + std::string DBGSourcePath; + + plist.GetValueAsString("DBGBuildSourcePath", DBGBuildSourcePath); + plist.GetValueAsString("DBGSourcePath", DBGSourcePath); + if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) + { + if (DBGSourcePath[0] == '~') + { + FileSpec resolved_source_path(DBGSourcePath.c_str(), true); + DBGSourcePath = resolved_source_path.GetPath(); + } + module_sp->GetSourceMappingList().Append (ConstString(DBGBuildSourcePath), ConstString(DBGSourcePath), true); + } + } + } + } + } + } + } + } + + symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp); + return symbol_vendor; + } + } + + // Just create our symbol vendor using the current objfile as this is either + // an executable with no dSYM (that we could locate), an executable with + // a dSYM that has a UUID that doesn't match. + symbol_vendor->AddSymbolFileRepresentation(obj_file->shared_from_this()); + } + return symbol_vendor; +} + + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString +SymbolVendorMacOSX::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolVendorMacOSX::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h b/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h new file mode 100644 index 000000000000..31a842ade86f --- /dev/null +++ b/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h @@ -0,0 +1,58 @@ +//===-- SymbolVendorMacOSX.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolVendorMacOSX_h_ +#define liblldb_SymbolVendorMacOSX_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Symbol/SymbolVendor.h" + +class SymbolVendorMacOSX : public lldb_private::SymbolVendor +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolVendor* + CreateInstance (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolVendorMacOSX (const lldb::ModuleSP &module_sp); + + virtual + ~SymbolVendorMacOSX(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +private: + DISALLOW_COPY_AND_ASSIGN (SymbolVendorMacOSX); +}; + +#endif // liblldb_SymbolVendorMacOSX_h_ diff --git a/source/Plugins/SystemRuntime/CMakeLists.txt b/source/Plugins/SystemRuntime/CMakeLists.txt new file mode 100644 index 000000000000..0955a9eb74c2 --- /dev/null +++ b/source/Plugins/SystemRuntime/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(MacOSX) diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.cpp b/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.cpp new file mode 100644 index 000000000000..2ca367c0cce8 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.cpp @@ -0,0 +1,383 @@ +//===-- AppleGetItemInfoHandler.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleGetItemInfoHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +const char *AppleGetItemInfoHandler::g_get_item_info_function_name = "__lldb_backtrace_recording_get_item_info"; +const char *AppleGetItemInfoHandler::g_get_item_info_function_code = " \n\ +extern \"C\" \n\ +{ \n\ + /* \n\ + * mach defines \n\ + */ \n\ + \n\ + typedef unsigned int uint32_t; \n\ + typedef unsigned long long uint64_t; \n\ + typedef uint32_t mach_port_t; \n\ + typedef mach_port_t vm_map_t; \n\ + typedef int kern_return_t; \n\ + typedef uint64_t mach_vm_address_t; \n\ + typedef uint64_t mach_vm_size_t; \n\ + \n\ + mach_port_t mach_task_self (); \n\ + kern_return_t mach_vm_deallocate (vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); \n\ + \n\ + /* \n\ + * libBacktraceRecording defines \n\ + */ \n\ + \n\ + typedef uint32_t queue_list_scope_t; \n\ + typedef void *dispatch_queue_t; \n\ + typedef void *introspection_dispatch_queue_info_t; \n\ + typedef void *introspection_dispatch_item_info_ref; \n\ + \n\ + extern uint64_t __introspection_dispatch_queue_item_get_info (introspection_dispatch_item_info_ref item_info_ref, \n\ + introspection_dispatch_item_info_ref *returned_queues_buffer, \n\ + uint64_t *returned_queues_buffer_size); \n\ + extern int printf(const char *format, ...); \n\ + \n\ + /* \n\ + * return type define \n\ + */ \n\ + \n\ + struct get_item_info_return_values \n\ + { \n\ + uint64_t item_info_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ \n\ + uint64_t item_info_buffer_size; /* the size of the items buffer from libBacktraceRecording */ \n\ + }; \n\ + \n\ + void __lldb_backtrace_recording_get_item_info \n\ + (struct get_item_info_return_values *return_buffer, \n\ + int debug, \n\ + uint64_t /* introspection_dispatch_item_info_ref item_info_ref */ item, \n\ + void *page_to_free, \n\ + uint64_t page_to_free_size) \n\ +{ \n\ + if (debug) \n\ + printf (\"entering get_item_info with args return_buffer == %p, debug == %d, item == 0x%llx, page_to_free == %p, page_to_free_size == 0x%llx\\n\", return_buffer, debug, item, page_to_free, page_to_free_size); \n\ + if (page_to_free != 0) \n\ + { \n\ + mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\ + } \n\ + \n\ + __introspection_dispatch_queue_item_get_info ((void*) item, \n\ + (void**)&return_buffer->item_info_buffer_ptr, \n\ + &return_buffer->item_info_buffer_size); \n\ +} \n\ +} \n\ +"; + +AppleGetItemInfoHandler::AppleGetItemInfoHandler (Process *process) : + m_process (process), + m_get_item_info_impl_code (), + m_get_item_info_function_mutex(), + m_get_item_info_return_buffer_addr (LLDB_INVALID_ADDRESS), + m_get_item_info_retbuffer_mutex() +{ +} + +AppleGetItemInfoHandler::~AppleGetItemInfoHandler () +{ +} + +void +AppleGetItemInfoHandler::Detach () +{ + + if (m_process && m_process->IsAlive() && m_get_item_info_return_buffer_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker locker; + locker.TryLock (m_get_item_info_retbuffer_mutex); // Even if we don't get the lock, deallocate the buffer + m_process->DeallocateMemory (m_get_item_info_return_buffer_addr); + } +} + +// Compile our __lldb_backtrace_recording_get_item_info() function (from the +// source above in g_get_item_info_function_code) if we don't find that function in the inferior +// already with USE_BUILTIN_FUNCTION defined. (e.g. this would be the case for testing.) +// +// Insert the __lldb_backtrace_recording_get_item_info into the inferior process if needed. +// +// Write the get_item_info_arglist into the inferior's memory space to prepare for the call. +// +// Returns the address of the arguments written down in the inferior process, which can be used to +// make the function call. + +lldb::addr_t +AppleGetItemInfoHandler::SetupGetItemInfoFunction (Thread &thread, ValueList &get_item_info_arglist) +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + StreamString errors; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + FunctionCaller *get_item_info_caller = nullptr; + + // Scope for mutex locker: + { + Mutex::Locker locker(m_get_item_info_function_mutex); + + // First stage is to make the UtilityFunction to hold our injected function: + + if (!m_get_item_info_impl_code.get()) + { + if (g_get_item_info_function_code != NULL) + { + Error error; + m_get_item_info_impl_code.reset(exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage (g_get_item_info_function_code, + eLanguageTypeObjC, + g_get_item_info_function_name, + error)); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to get utility function: %s.", error.AsCString()); + return args_addr; + } + + if (!m_get_item_info_impl_code->Install(errors, exe_ctx)) + { + if (log) + log->Printf ("Failed to install get-item-info introspection: %s.", errors.GetData()); + m_get_item_info_impl_code.reset(); + return args_addr; + } + } + else + { + if (log) + log->Printf("No get-item-info introspection code found."); + errors.Printf ("No get-item-info introspection code found."); + return LLDB_INVALID_ADDRESS; + } + + // Next make the runner function for our implementation utility function. + Error error; + + TypeSystem *type_system = thread.GetProcess()->GetTarget().GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC); + CompilerType get_item_info_return_type = type_system->GetBasicTypeFromAST(eBasicTypeVoid).GetPointerType(); + + get_item_info_caller = m_get_item_info_impl_code->MakeFunctionCaller(get_item_info_return_type, + get_item_info_arglist, + error); + if (error.Fail()) + { + if (log) + log->Printf ("Error Inserting get-item-info function: \"%s\".", error.AsCString()); + return args_addr; + } + } + else + { + // If it's already made, then we can just retrieve the caller: + get_item_info_caller = m_get_item_info_impl_code->GetFunctionCaller(); + if (!get_item_info_caller) + { + if (log) + log->Printf ("Failed to get get-item-info introspection caller."); + m_get_item_info_impl_code.reset(); + return args_addr; + } + } + } + + errors.Clear(); + + // Now write down the argument values for this particular call. This looks like it might be a race condition + // if other threads were calling into here, but actually it isn't because we allocate a new args structure for + // this call by passing args_addr = LLDB_INVALID_ADDRESS... + + if (!get_item_info_caller->WriteFunctionArguments (exe_ctx, args_addr, get_item_info_arglist, errors)) + { + if (log) + log->Printf ("Error writing get-item-info function arguments: \"%s\".", errors.GetData()); + return args_addr; + } + + return args_addr; +} + +AppleGetItemInfoHandler::GetItemInfoReturnInfo +AppleGetItemInfoHandler::GetItemInfo (Thread &thread, uint64_t item, addr_t page_to_free, uint64_t page_to_free_size, Error &error) +{ + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + ProcessSP process_sp (thread.CalculateProcess()); + TargetSP target_sp (thread.CalculateTarget()); + ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + + GetItemInfoReturnInfo return_value; + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return_value.item_buffer_size = 0; + + error.Clear(); + + if (thread.SafeToCallFunctions() == false) + { + if (log) + log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID()); + error.SetErrorString ("Not safe to call functions on this thread."); + return return_value; + } + + // Set up the arguments for a call to + + // struct get_item_info_return_values + // { + // uint64_t item_info_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ + // uint64_t item_info_buffer_size; /* the size of the items buffer from libBacktraceRecording */ + // }; + // + // void __lldb_backtrace_recording_get_item_info + // (struct get_item_info_return_values *return_buffer, + // int debug, + // uint64_t item, + // void *page_to_free, + // uint64_t page_to_free_size) + + // Where the return_buffer argument points to a 24 byte region of memory already allocated by lldb in + // the inferior process. + + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + Value return_buffer_ptr_value; + return_buffer_ptr_value.SetValueType (Value::eValueTypeScalar); + return_buffer_ptr_value.SetCompilerType (clang_void_ptr_type); + + CompilerType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt); + Value debug_value; + debug_value.SetValueType (Value::eValueTypeScalar); + debug_value.SetCompilerType (clang_int_type); + + CompilerType clang_uint64_type = clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong); + Value item_value; + item_value.SetValueType (Value::eValueTypeScalar); + item_value.SetCompilerType (clang_uint64_type); + + Value page_to_free_value; + page_to_free_value.SetValueType (Value::eValueTypeScalar); + page_to_free_value.SetCompilerType (clang_void_ptr_type); + + Value page_to_free_size_value; + page_to_free_size_value.SetValueType (Value::eValueTypeScalar); + page_to_free_size_value.SetCompilerType (clang_uint64_type); + + + Mutex::Locker locker(m_get_item_info_retbuffer_mutex); + if (m_get_item_info_return_buffer_addr == LLDB_INVALID_ADDRESS) + { + addr_t bufaddr = process_sp->AllocateMemory (32, ePermissionsReadable | ePermissionsWritable, error); + if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("Failed to allocate memory for return buffer for get current queues func call"); + return return_value; + } + m_get_item_info_return_buffer_addr = bufaddr; + } + + ValueList argument_values; + + return_buffer_ptr_value.GetScalar() = m_get_item_info_return_buffer_addr; + argument_values.PushValue (return_buffer_ptr_value); + + debug_value.GetScalar() = 0; + argument_values.PushValue (debug_value); + + item_value.GetScalar() = item; + argument_values.PushValue (item_value); + + if (page_to_free != LLDB_INVALID_ADDRESS) + page_to_free_value.GetScalar() = page_to_free; + else + page_to_free_value.GetScalar() = 0; + argument_values.PushValue (page_to_free_value); + + page_to_free_size_value.GetScalar() = page_to_free_size; + argument_values.PushValue (page_to_free_size_value); + + addr_t args_addr = SetupGetItemInfoFunction (thread, argument_values); + + StreamString errors; + ExecutionContext exe_ctx; + EvaluateExpressionOptions options; + options.SetUnwindOnError (true); + options.SetIgnoreBreakpoints (true); + options.SetStopOthers (true); + options.SetTimeoutUsec(500000); + options.SetTryAllThreads (false); + thread.CalculateExecutionContext (exe_ctx); + + if (!m_get_item_info_impl_code) + { + error.SetErrorString ("Unable to compile function to call __introspection_dispatch_queue_item_get_info"); + return return_value; + } + + + ExpressionResults func_call_ret; + Value results; + FunctionCaller *func_caller = m_get_item_info_impl_code->GetFunctionCaller(); + if (!func_caller) + { + if (log) + log->Printf ("Could not retrieve function caller for __introspection_dispatch_queue_item_get_info."); + error.SetErrorString("Could not retrieve function caller for __introspection_dispatch_queue_item_get_info."); + return return_value; + } + + func_call_ret = func_caller->ExecuteFunction (exe_ctx, &args_addr, options, errors, results); + if (func_call_ret != eExpressionCompleted || !error.Success()) + { + if (log) + log->Printf ("Unable to call __introspection_dispatch_queue_item_get_info(), got ExpressionResults %d, error contains %s", func_call_ret, error.AsCString("")); + error.SetErrorString ("Unable to call __introspection_dispatch_queue_get_item_info() for list of queues"); + return return_value; + } + + return_value.item_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory (m_get_item_info_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error); + if (!error.Success() || return_value.item_buffer_ptr == LLDB_INVALID_ADDRESS) + { + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.item_buffer_size = m_process->ReadUnsignedIntegerFromMemory (m_get_item_info_return_buffer_addr + 8, 8, 0, error); + + if (!error.Success()) + { + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + if (log) + log->Printf ("AppleGetItemInfoHandler called __introspection_dispatch_queue_item_get_info (page_to_free == 0x%" PRIx64 ", size = %" PRId64 "), returned page is at 0x%" PRIx64 ", size %" PRId64, page_to_free, page_to_free_size, return_value.item_buffer_ptr, return_value.item_buffer_size); + + + return return_value; +} diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.h b/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.h new file mode 100644 index 000000000000..51182a624939 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.h @@ -0,0 +1,117 @@ +//===-- AppleGetItemInfoHandler.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_AppleGetItemInfoHandler_h_ +#define lldb_AppleGetItemInfoHandler_h_ + +// C Includes +// C++ Includes +#include <map> +#include <vector> +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/Error.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/CompilerType.h" + +// This class will insert a UtilityFunction into the inferior process for +// calling libBacktraceRecording's __introspection_dispatch_queue_item_get_info() +// function. The function in the inferior will return a struct by value +// with these members: +// +// struct get_item_info_return_values +// { +// introspection_dispatch_item_info_ref *item_buffer; +// uint64_t item_buffer_size; +// }; +// +// The item_buffer pointer is an address in the inferior program's address +// space (item_buffer_size in size) which must be mach_vm_deallocate'd by +// lldb. +// +// The AppleGetItemInfoHandler object should persist so that the UtilityFunction +// can be reused multiple times. + +namespace lldb_private +{ + +class AppleGetItemInfoHandler { +public: + + AppleGetItemInfoHandler (lldb_private::Process *process); + + ~AppleGetItemInfoHandler(); + + struct GetItemInfoReturnInfo + { + lldb::addr_t item_buffer_ptr; /* the address of the item buffer from libBacktraceRecording */ + lldb::addr_t item_buffer_size; /* the size of the item buffer from libBacktraceRecording */ + + GetItemInfoReturnInfo() : + item_buffer_ptr(LLDB_INVALID_ADDRESS), + item_buffer_size(0) + {} + }; + + //---------------------------------------------------------- + /// Get the information about a work item by calling + /// __introspection_dispatch_queue_item_get_info. If there's a page of + /// memory that needs to be freed, pass in the address and size and it will + /// be freed before getting the list of queues. + /// + /// @param [in] thread + /// The thread to run this plan on. + /// + /// @param [in] item + /// The introspection_dispatch_item_info_ref value for the item of interest. + /// + /// @param [in] page_to_free + /// An address of an inferior process vm page that needs to be deallocated, + /// LLDB_INVALID_ADDRESS if this is not needed. + /// + /// @param [in] page_to_free_size + /// The size of the vm page that needs to be deallocated if an address was + /// passed in to page_to_free. + /// + /// @param [out] error + /// This object will be updated with the error status / error string from any failures encountered. + /// + /// @returns + /// The result of the inferior function call execution. If there was a failure of any kind while getting + /// the information, the item_buffer_ptr value will be LLDB_INVALID_ADDRESS. + //---------------------------------------------------------- + GetItemInfoReturnInfo + GetItemInfo (Thread &thread, lldb::addr_t item, lldb::addr_t page_to_free, uint64_t page_to_free_size, lldb_private::Error &error); + + + void + Detach (); + +private: + + lldb::addr_t + SetupGetItemInfoFunction (Thread &thread, ValueList &get_item_info_arglist); + + static const char *g_get_item_info_function_name; + static const char *g_get_item_info_function_code; + + lldb_private::Process *m_process; + std::unique_ptr<UtilityFunction> m_get_item_info_impl_code; + Mutex m_get_item_info_function_mutex; + + lldb::addr_t m_get_item_info_return_buffer_addr; + Mutex m_get_item_info_retbuffer_mutex; + +}; + +} // using namespace lldb_private + +#endif // lldb_AppleGetItemInfoHandler_h_ diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.cpp b/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.cpp new file mode 100644 index 000000000000..97699878f5ee --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.cpp @@ -0,0 +1,383 @@ +//===-- AppleGetPendingItemsHandler.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleGetPendingItemsHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +const char *AppleGetPendingItemsHandler::g_get_pending_items_function_name = "__lldb_backtrace_recording_get_pending_items"; +const char *AppleGetPendingItemsHandler::g_get_pending_items_function_code = " \n\ +extern \"C\" \n\ +{ \n\ + /* \n\ + * mach defines \n\ + */ \n\ + \n\ + typedef unsigned int uint32_t; \n\ + typedef unsigned long long uint64_t; \n\ + typedef uint32_t mach_port_t; \n\ + typedef mach_port_t vm_map_t; \n\ + typedef int kern_return_t; \n\ + typedef uint64_t mach_vm_address_t; \n\ + typedef uint64_t mach_vm_size_t; \n\ + \n\ + mach_port_t mach_task_self (); \n\ + kern_return_t mach_vm_deallocate (vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); \n\ + \n\ + /* \n\ + * libBacktraceRecording defines \n\ + */ \n\ + \n\ + typedef uint32_t queue_list_scope_t; \n\ + typedef void *dispatch_queue_t; \n\ + typedef void *introspection_dispatch_queue_info_t; \n\ + typedef void *introspection_dispatch_item_info_ref; \n\ + \n\ + extern uint64_t __introspection_dispatch_queue_get_pending_items (dispatch_queue_t queue, \n\ + introspection_dispatch_item_info_ref *returned_queues_buffer, \n\ + uint64_t *returned_queues_buffer_size); \n\ + extern int printf(const char *format, ...); \n\ + \n\ + /* \n\ + * return type define \n\ + */ \n\ + \n\ + struct get_pending_items_return_values \n\ + { \n\ + uint64_t pending_items_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ \n\ + uint64_t pending_items_buffer_size; /* the size of the items buffer from libBacktraceRecording */ \n\ + uint64_t count; /* the number of items included in the queues buffer */ \n\ + }; \n\ + \n\ + void __lldb_backtrace_recording_get_pending_items \n\ + (struct get_pending_items_return_values *return_buffer, \n\ + int debug, \n\ + uint64_t /* dispatch_queue_t */ queue, \n\ + void *page_to_free, \n\ + uint64_t page_to_free_size) \n\ +{ \n\ + if (debug) \n\ + printf (\"entering get_pending_items with args return_buffer == %p, debug == %d, queue == 0x%llx, page_to_free == %p, page_to_free_size == 0x%llx\\n\", return_buffer, debug, queue, page_to_free, page_to_free_size); \n\ + if (page_to_free != 0) \n\ + { \n\ + mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\ + } \n\ + \n\ + return_buffer->count = __introspection_dispatch_queue_get_pending_items ( \n\ + (void*) queue, \n\ + (void**)&return_buffer->pending_items_buffer_ptr, \n\ + &return_buffer->pending_items_buffer_size); \n\ + if (debug) \n\ + printf(\"result was count %lld\\n\", return_buffer->count); \n\ +} \n\ +} \n\ +"; + +AppleGetPendingItemsHandler::AppleGetPendingItemsHandler (Process *process) : + m_process (process), + m_get_pending_items_impl_code (), + m_get_pending_items_function_mutex(), + m_get_pending_items_return_buffer_addr (LLDB_INVALID_ADDRESS), + m_get_pending_items_retbuffer_mutex() +{ +} + +AppleGetPendingItemsHandler::~AppleGetPendingItemsHandler () +{ +} + +void +AppleGetPendingItemsHandler::Detach () +{ + + if (m_process && m_process->IsAlive() && m_get_pending_items_return_buffer_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker locker; + locker.TryLock (m_get_pending_items_retbuffer_mutex); // Even if we don't get the lock, deallocate the buffer + m_process->DeallocateMemory (m_get_pending_items_return_buffer_addr); + } +} + +// Compile our __lldb_backtrace_recording_get_pending_items() function (from the +// source above in g_get_pending_items_function_code) if we don't find that function in the inferior +// already with USE_BUILTIN_FUNCTION defined. (e.g. this would be the case for testing.) +// +// Insert the __lldb_backtrace_recording_get_pending_items into the inferior process if needed. +// +// Write the get_pending_items_arglist into the inferior's memory space to prepare for the call. +// +// Returns the address of the arguments written down in the inferior process, which can be used to +// make the function call. + +lldb::addr_t +AppleGetPendingItemsHandler::SetupGetPendingItemsFunction (Thread &thread, ValueList &get_pending_items_arglist) +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + StreamString errors; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + FunctionCaller *get_pending_items_caller = nullptr; + + // Scope for mutex locker: + { + Mutex::Locker locker(m_get_pending_items_function_mutex); + + // First stage is to make the ClangUtility to hold our injected function: + + if (!m_get_pending_items_impl_code.get()) + { + if (g_get_pending_items_function_code != NULL) + { + Error error; + m_get_pending_items_impl_code.reset (exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(g_get_pending_items_function_code, + eLanguageTypeObjC, + g_get_pending_items_function_name, + error)); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to get UtilityFunction for pending-items introspection: %s.", error.AsCString()); + return args_addr; + } + + if (!m_get_pending_items_impl_code->Install(errors, exe_ctx)) + { + if (log) + log->Printf ("Failed to install pending-items introspection: %s.", errors.GetData()); + m_get_pending_items_impl_code.reset(); + return args_addr; + } + } + else + { + if (log) + log->Printf("No pending-items introspection code found."); + return LLDB_INVALID_ADDRESS; + } + + // Next make the runner function for our implementation utility function. + Error error; + ClangASTContext *clang_ast_context = thread.GetProcess()->GetTarget().GetScratchClangASTContext(); + CompilerType get_pending_items_return_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + get_pending_items_caller = m_get_pending_items_impl_code->MakeFunctionCaller (get_pending_items_return_type, + get_pending_items_arglist, + error); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to install pending-items introspection function caller: %s.", error.AsCString()); + m_get_pending_items_impl_code.reset(); + return args_addr; + } + } + + } + + errors.Clear(); + + if (get_pending_items_caller == nullptr) + { + if (log) + log->Printf ("Failed to get get_pending_items_caller."); + return LLDB_INVALID_ADDRESS; + } + + // Now write down the argument values for this particular call. This looks like it might be a race condition + // if other threads were calling into here, but actually it isn't because we allocate a new args structure for + // this call by passing args_addr = LLDB_INVALID_ADDRESS... + + if (!get_pending_items_caller->WriteFunctionArguments (exe_ctx, args_addr, get_pending_items_arglist, errors)) + { + if (log) + log->Printf ("Error writing pending-items function arguments: \"%s\".", errors.GetData()); + return args_addr; + } + + return args_addr; +} + +AppleGetPendingItemsHandler::GetPendingItemsReturnInfo +AppleGetPendingItemsHandler::GetPendingItems (Thread &thread, addr_t queue, addr_t page_to_free, uint64_t page_to_free_size, Error &error) +{ + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + ProcessSP process_sp (thread.CalculateProcess()); + TargetSP target_sp (thread.CalculateTarget()); + ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + + GetPendingItemsReturnInfo return_value; + return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS; + return_value.items_buffer_size = 0; + return_value.count = 0; + + error.Clear(); + + if (thread.SafeToCallFunctions() == false) + { + if (log) + log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID()); + error.SetErrorString ("Not safe to call functions on this thread."); + return return_value; + } + + // Set up the arguments for a call to + + // struct get_pending_items_return_values + // { + // uint64_t pending_items_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ + // uint64_t pending_items_buffer_size; /* the size of the items buffer from libBacktraceRecording */ + // uint64_t count; /* the number of items included in the queues buffer */ + // }; + // + // void __lldb_backtrace_recording_get_pending_items + // (struct get_pending_items_return_values *return_buffer, + // int debug, + // uint64_t /* dispatch_queue_t */ queue + // void *page_to_free, + // uint64_t page_to_free_size) + + // Where the return_buffer argument points to a 24 byte region of memory already allocated by lldb in + // the inferior process. + + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + Value return_buffer_ptr_value; + return_buffer_ptr_value.SetValueType (Value::eValueTypeScalar); + return_buffer_ptr_value.SetCompilerType (clang_void_ptr_type); + + CompilerType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt); + Value debug_value; + debug_value.SetValueType (Value::eValueTypeScalar); + debug_value.SetCompilerType (clang_int_type); + + CompilerType clang_uint64_type = clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong); + Value queue_value; + queue_value.SetValueType (Value::eValueTypeScalar); + queue_value.SetCompilerType (clang_uint64_type); + + Value page_to_free_value; + page_to_free_value.SetValueType (Value::eValueTypeScalar); + page_to_free_value.SetCompilerType (clang_void_ptr_type); + + Value page_to_free_size_value; + page_to_free_size_value.SetValueType (Value::eValueTypeScalar); + page_to_free_size_value.SetCompilerType (clang_uint64_type); + + + Mutex::Locker locker(m_get_pending_items_retbuffer_mutex); + if (m_get_pending_items_return_buffer_addr == LLDB_INVALID_ADDRESS) + { + addr_t bufaddr = process_sp->AllocateMemory (32, ePermissionsReadable | ePermissionsWritable, error); + if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("Failed to allocate memory for return buffer for get current queues func call"); + return return_value; + } + m_get_pending_items_return_buffer_addr = bufaddr; + } + + ValueList argument_values; + + return_buffer_ptr_value.GetScalar() = m_get_pending_items_return_buffer_addr; + argument_values.PushValue (return_buffer_ptr_value); + + debug_value.GetScalar() = 0; + argument_values.PushValue (debug_value); + + queue_value.GetScalar() = queue; + argument_values.PushValue (queue_value); + + if (page_to_free != LLDB_INVALID_ADDRESS) + page_to_free_value.GetScalar() = page_to_free; + else + page_to_free_value.GetScalar() = 0; + argument_values.PushValue (page_to_free_value); + + page_to_free_size_value.GetScalar() = page_to_free_size; + argument_values.PushValue (page_to_free_size_value); + + addr_t args_addr = SetupGetPendingItemsFunction (thread, argument_values); + + StreamString errors; + ExecutionContext exe_ctx; + FunctionCaller *get_pending_items_caller = m_get_pending_items_impl_code->GetFunctionCaller(); + + EvaluateExpressionOptions options; + options.SetUnwindOnError (true); + options.SetIgnoreBreakpoints (true); + options.SetStopOthers (true); + options.SetTimeoutUsec(500000); + options.SetTryAllThreads (false); + thread.CalculateExecutionContext (exe_ctx); + + if (get_pending_items_caller == NULL) + { + error.SetErrorString ("Unable to compile function to call __introspection_dispatch_queue_get_pending_items"); + return return_value; + } + + + ExpressionResults func_call_ret; + Value results; + func_call_ret = get_pending_items_caller->ExecuteFunction (exe_ctx, &args_addr, options, errors, results); + if (func_call_ret != eExpressionCompleted || !error.Success()) + { + if (log) + log->Printf ("Unable to call __introspection_dispatch_queue_get_pending_items(), got ExpressionResults %d, error contains %s", func_call_ret, error.AsCString("")); + error.SetErrorString ("Unable to call __introspection_dispatch_queue_get_pending_items() for list of queues"); + return return_value; + } + + return_value.items_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory (m_get_pending_items_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error); + if (!error.Success() || return_value.items_buffer_ptr == LLDB_INVALID_ADDRESS) + { + return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.items_buffer_size = m_process->ReadUnsignedIntegerFromMemory (m_get_pending_items_return_buffer_addr + 8, 8, 0, error); + + if (!error.Success()) + { + return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.count = m_process->ReadUnsignedIntegerFromMemory (m_get_pending_items_return_buffer_addr + 16, 8, 0, error); + if (!error.Success()) + { + return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + if (log) + log->Printf ("AppleGetPendingItemsHandler called __introspection_dispatch_queue_get_pending_items (page_to_free == 0x%" PRIx64 ", size = %" PRId64 "), returned page is at 0x%" PRIx64 ", size %" PRId64 ", count = %" PRId64, page_to_free, page_to_free_size, return_value.items_buffer_ptr, return_value.items_buffer_size, return_value.count); + + return return_value; +} diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.h b/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.h new file mode 100644 index 000000000000..445c4a0fb82b --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.h @@ -0,0 +1,119 @@ +//===-- AppleGetPendingItemsHandler.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_AppleGetPendingItemsHandler_h_ +#define lldb_AppleGetPendingItemsHandler_h_ + +// C Includes +// C++ Includes +#include <map> +#include <vector> +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/CompilerType.h" + +// This class will insert a UtilityFunction into the inferior process for +// calling libBacktraceRecording's __introspection_dispatch_queue_get_pending_items() +// function. The function in the inferior will return a struct by value +// with these members: +// +// struct get_pending_items_return_values +// { +// introspection_dispatch_item_info_ref *items_buffer; +// uint64_t items_buffer_size; +// uint64_t count; +// }; +// +// The items_buffer pointer is an address in the inferior program's address +// space (items_buffer_size in size) which must be mach_vm_deallocate'd by +// lldb. count is the number of items that were stored in the buffer. +// +// The AppleGetPendingItemsHandler object should persist so that the UtilityFunction +// can be reused multiple times. + +namespace lldb_private +{ + +class AppleGetPendingItemsHandler { +public: + + AppleGetPendingItemsHandler (lldb_private::Process *process); + + ~AppleGetPendingItemsHandler(); + + struct GetPendingItemsReturnInfo + { + lldb::addr_t items_buffer_ptr; /* the address of the pending items buffer from libBacktraceRecording */ + lldb::addr_t items_buffer_size; /* the size of the pending items buffer from libBacktraceRecording */ + uint64_t count; /* the number of pending items included in the buffer */ + + GetPendingItemsReturnInfo () : + items_buffer_ptr(LLDB_INVALID_ADDRESS), + items_buffer_size(0), + count(0) + {} + }; + + //---------------------------------------------------------- + /// Get the list of pending items for a given queue via a call to + /// __introspection_dispatch_queue_get_pending_items. If there's a page of + /// memory that needs to be freed, pass in the address and size and it will + /// be freed before getting the list of queues. + /// + /// @param [in] thread + /// The thread to run this plan on. + /// + /// @param [in] queue + /// The dispatch_queue_t value for the queue of interest. + /// + /// @param [in] page_to_free + /// An address of an inferior process vm page that needs to be deallocated, + /// LLDB_INVALID_ADDRESS if this is not needed. + /// + /// @param [in] page_to_free_size + /// The size of the vm page that needs to be deallocated if an address was + /// passed in to page_to_free. + /// + /// @param [out] error + /// This object will be updated with the error status / error string from any failures encountered. + /// + /// @returns + /// The result of the inferior function call execution. If there was a failure of any kind while getting + /// the information, the items_buffer_ptr value will be LLDB_INVALID_ADDRESS. + //---------------------------------------------------------- + GetPendingItemsReturnInfo + GetPendingItems (Thread &thread, lldb::addr_t queue, lldb::addr_t page_to_free, uint64_t page_to_free_size, lldb_private::Error &error); + + + void + Detach (); + +private: + + lldb::addr_t + SetupGetPendingItemsFunction (Thread &thread, ValueList &get_pending_items_arglist); + + static const char *g_get_pending_items_function_name; + static const char *g_get_pending_items_function_code; + + lldb_private::Process *m_process; + std::unique_ptr<UtilityFunction> m_get_pending_items_impl_code; + Mutex m_get_pending_items_function_mutex; + + lldb::addr_t m_get_pending_items_return_buffer_addr; + Mutex m_get_pending_items_retbuffer_mutex; + +}; + +} // using namespace lldb_private + +#endif // lldb_AppleGetPendingItemsHandler_h_ diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.cpp b/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.cpp new file mode 100644 index 000000000000..3370b3257a25 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.cpp @@ -0,0 +1,382 @@ +//===-- AppleGetQueuesHandler.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleGetQueuesHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +const char *AppleGetQueuesHandler::g_get_current_queues_function_name = "__lldb_backtrace_recording_get_current_queues"; +const char *AppleGetQueuesHandler::g_get_current_queues_function_code = " \n\ +extern \"C\" \n\ +{ \n\ + /* \n\ + * mach defines \n\ + */ \n\ + \n\ + typedef unsigned int uint32_t; \n\ + typedef unsigned long long uint64_t; \n\ + typedef uint32_t mach_port_t; \n\ + typedef mach_port_t vm_map_t; \n\ + typedef int kern_return_t; \n\ + typedef uint64_t mach_vm_address_t; \n\ + typedef uint64_t mach_vm_size_t; \n\ + \n\ + mach_port_t mach_task_self (); \n\ + kern_return_t mach_vm_deallocate (vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); \n\ + \n\ + /* \n\ + * libBacktraceRecording defines \n\ + */ \n\ + \n\ + typedef uint32_t queue_list_scope_t; \n\ + typedef void *introspection_dispatch_queue_info_t; \n\ + \n\ + extern uint64_t __introspection_dispatch_get_queues (queue_list_scope_t scope, \n\ + introspection_dispatch_queue_info_t *returned_queues_buffer, \n\ + uint64_t *returned_queues_buffer_size); \n\ + extern int printf(const char *format, ...); \n\ + \n\ + /* \n\ + * return type define \n\ + */ \n\ + \n\ + struct get_current_queues_return_values \n\ + { \n\ + uint64_t queues_buffer_ptr; /* the address of the queues buffer from libBacktraceRecording */ \n\ + uint64_t queues_buffer_size; /* the size of the queues buffer from libBacktraceRecording */ \n\ + uint64_t count; /* the number of queues included in the queues buffer */ \n\ + }; \n\ + \n\ + void __lldb_backtrace_recording_get_current_queues \n\ + (struct get_current_queues_return_values *return_buffer, \n\ + int debug, \n\ + void *page_to_free, \n\ + uint64_t page_to_free_size) \n\ +{ \n\ + if (debug) \n\ + printf (\"entering get_current_queues with args %p, %d, 0x%p, 0x%llx\\n\", return_buffer, debug, page_to_free, page_to_free_size); \n\ + if (page_to_free != 0) \n\ + { \n\ + mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\ + } \n\ + \n\ + return_buffer->count = __introspection_dispatch_get_queues ( \n\ + /* QUEUES_WITH_ANY_ITEMS */ 2, \n\ + (void**)&return_buffer->queues_buffer_ptr, \n\ + &return_buffer->queues_buffer_size); \n\ + if (debug) \n\ + printf(\"result was count %lld\\n\", return_buffer->count); \n\ +} \n\ +} \n\ +"; + +AppleGetQueuesHandler::AppleGetQueuesHandler (Process *process) : + m_process (process), + m_get_queues_impl_code_up (), + m_get_queues_function_mutex(), + m_get_queues_return_buffer_addr (LLDB_INVALID_ADDRESS), + m_get_queues_retbuffer_mutex() +{ +} + +AppleGetQueuesHandler::~AppleGetQueuesHandler () +{ +} + +void +AppleGetQueuesHandler::Detach () +{ + + if (m_process && m_process->IsAlive() && m_get_queues_return_buffer_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker locker; + locker.TryLock (m_get_queues_retbuffer_mutex); // Even if we don't get the lock, deallocate the buffer + m_process->DeallocateMemory (m_get_queues_return_buffer_addr); + } +} + +// Construct a CompilerType for the structure that g_get_current_queues_function_code will return by value +// so we can extract the fields after performing the function call. +// i.e. we are getting this struct returned to us: +// +// struct get_current_queues_return_values +// { +// introspection_dispatch_queue_info_t *queues_buffer; +// uint64_t queues_buffer_size; +// uint64_t count; +// }; + + +// Compile our __lldb_backtrace_recording_get_current_queues() function (from the +// source above in g_get_current_queues_function_code) if we don't find that function in the inferior +// already with USE_BUILTIN_FUNCTION defined. (e.g. this would be the case for testing.) +// +// Insert the __lldb_backtrace_recording_get_current_queues into the inferior process if needed. +// +// Write the get_queues_arglist into the inferior's memory space to prepare for the call. +// +// Returns the address of the arguments written down in the inferior process, which can be used to +// make the function call. + +lldb::addr_t +AppleGetQueuesHandler::SetupGetQueuesFunction (Thread &thread, ValueList &get_queues_arglist) +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + Address impl_code_address; + StreamString errors; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + + FunctionCaller *get_queues_caller = nullptr; + + // Scope for mutex locker: + { + Mutex::Locker locker(m_get_queues_function_mutex); + + // First stage is to make the ClangUtility to hold our injected function: + + if (!m_get_queues_impl_code_up.get()) + { + if (g_get_current_queues_function_code != NULL) + { + Error error; + m_get_queues_impl_code_up.reset (exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(g_get_current_queues_function_code, + eLanguageTypeC, + g_get_current_queues_function_name, + error)); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to get UtilityFunction for queues introspection: %s.", error.AsCString()); + return args_addr; + } + + if (!m_get_queues_impl_code_up->Install(errors, exe_ctx)) + { + if (log) + log->Printf ("Failed to install queues introspection: %s.", errors.GetData()); + m_get_queues_impl_code_up.reset(); + return args_addr; + } + } + else + { + if (log) + log->Printf("No queues introspection code found."); + errors.Printf ("No queues introspection code found."); + return LLDB_INVALID_ADDRESS; + } + } + + // Next make the runner function for our implementation utility function. + ClangASTContext *clang_ast_context = thread.GetProcess()->GetTarget().GetScratchClangASTContext(); + CompilerType get_queues_return_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + Error error; + get_queues_caller = m_get_queues_impl_code_up->MakeFunctionCaller (get_queues_return_type, + get_queues_arglist, + error); + if (error.Fail()) + { + if (log) + log->Printf ("Could not get function caller for get-queues function: %s.", error.AsCString()); + return args_addr; + } + } + + errors.Clear(); + + // Now write down the argument values for this particular call. This looks like it might be a race condition + // if other threads were calling into here, but actually it isn't because we allocate a new args structure for + // this call by passing args_addr = LLDB_INVALID_ADDRESS... + + if (!get_queues_caller->WriteFunctionArguments (exe_ctx, args_addr, get_queues_arglist, errors)) + { + if (log) + log->Printf ("Error writing get-queues function arguments: \"%s\".", errors.GetData()); + return args_addr; + } + + return args_addr; +} + +AppleGetQueuesHandler::GetQueuesReturnInfo +AppleGetQueuesHandler::GetCurrentQueues (Thread &thread, addr_t page_to_free, uint64_t page_to_free_size, Error &error) +{ + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + ProcessSP process_sp (thread.CalculateProcess()); + TargetSP target_sp (thread.CalculateTarget()); + ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + + GetQueuesReturnInfo return_value; + return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS; + return_value.queues_buffer_size = 0; + return_value.count = 0; + + error.Clear(); + + if (thread.SafeToCallFunctions() == false) + { + if (log) + log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID()); + error.SetErrorString ("Not safe to call functions on this thread."); + return return_value; + } + + // Set up the arguments for a call to + + // struct get_current_queues_return_values + // { + // uint64_t queues_buffer_ptr; /* the address of the queues buffer from libBacktraceRecording */ + // uint64_t queues_buffer_size; /* the size of the queues buffer from libBacktraceRecording */ + // uint64_t count; /* the number of queues included in the queues buffer */ + // }; + // + // void + // __lldb_backtrace_recording_get_current_queues + // (struct get_current_queues_return_values *return_buffer, + // void *page_to_free, + // uint64_t page_to_free_size); + + // Where the return_buffer argument points to a 24 byte region of memory already allocated by lldb in + // the inferior process. + + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + Value return_buffer_ptr_value; + return_buffer_ptr_value.SetValueType (Value::eValueTypeScalar); + return_buffer_ptr_value.SetCompilerType (clang_void_ptr_type); + + CompilerType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt); + Value debug_value; + debug_value.SetValueType (Value::eValueTypeScalar); + debug_value.SetCompilerType (clang_int_type); + + Value page_to_free_value; + page_to_free_value.SetValueType (Value::eValueTypeScalar); + page_to_free_value.SetCompilerType (clang_void_ptr_type); + + CompilerType clang_uint64_type = clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong); + Value page_to_free_size_value; + page_to_free_size_value.SetValueType (Value::eValueTypeScalar); + page_to_free_size_value.SetCompilerType (clang_uint64_type); + + + Mutex::Locker locker(m_get_queues_retbuffer_mutex); + if (m_get_queues_return_buffer_addr == LLDB_INVALID_ADDRESS) + { + addr_t bufaddr = process_sp->AllocateMemory (32, ePermissionsReadable | ePermissionsWritable, error); + if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("Failed to allocate memory for return buffer for get current queues func call"); + return return_value; + } + m_get_queues_return_buffer_addr = bufaddr; + } + + ValueList argument_values; + + return_buffer_ptr_value.GetScalar() = m_get_queues_return_buffer_addr; + argument_values.PushValue (return_buffer_ptr_value); + + debug_value.GetScalar() = 0; + argument_values.PushValue (debug_value); + + if (page_to_free != LLDB_INVALID_ADDRESS) + page_to_free_value.GetScalar() = page_to_free; + else + page_to_free_value.GetScalar() = 0; + argument_values.PushValue (page_to_free_value); + + page_to_free_size_value.GetScalar() = page_to_free_size; + argument_values.PushValue (page_to_free_size_value); + + addr_t args_addr = SetupGetQueuesFunction (thread, argument_values); + + if (!m_get_queues_impl_code_up) + { + error.SetErrorString ("Unable to compile __introspection_dispatch_get_queues."); + return return_value; + } + + FunctionCaller *get_queues_caller = m_get_queues_impl_code_up->GetFunctionCaller(); + + if (get_queues_caller == NULL) + { + error.SetErrorString ("Unable to get caller for call __introspection_dispatch_get_queues"); + return return_value; + } + + StreamString errors; + ExecutionContext exe_ctx; + EvaluateExpressionOptions options; + options.SetUnwindOnError (true); + options.SetIgnoreBreakpoints (true); + options.SetStopOthers (true); + options.SetTimeoutUsec(500000); + options.SetTryAllThreads (false); + thread.CalculateExecutionContext (exe_ctx); + + ExpressionResults func_call_ret; + Value results; + func_call_ret = get_queues_caller->ExecuteFunction (exe_ctx, &args_addr, options, errors, results); + if (func_call_ret != eExpressionCompleted || !error.Success()) + { + if (log) + log->Printf ("Unable to call introspection_get_dispatch_queues(), got ExpressionResults %d, error contains %s", func_call_ret, error.AsCString("")); + error.SetErrorString ("Unable to call introspection_get_dispatch_queues() for list of queues"); + return return_value; + } + + return_value.queues_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error); + if (!error.Success() || return_value.queues_buffer_ptr == LLDB_INVALID_ADDRESS) + { + return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.queues_buffer_size = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr + 8, 8, 0, error); + + if (!error.Success()) + { + return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.count = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr + 16, 8, 0, error); + if (!error.Success()) + { + return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + if (log) + log->Printf ("AppleGetQueuesHandler called __introspection_dispatch_get_queues (page_to_free == 0x%" PRIx64 ", size = %" PRId64 "), returned page is at 0x%" PRIx64 ", size %" PRId64 ", count = %" PRId64, page_to_free, page_to_free_size, return_value.queues_buffer_ptr, return_value.queues_buffer_size, return_value.count); + + return return_value; +} diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.h b/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.h new file mode 100644 index 000000000000..6f3df5f62807 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.h @@ -0,0 +1,116 @@ +//===-- AppleGetQueuesHandler.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_AppleGetQueuesHandler_h_ +#define lldb_AppleGetQueuesHandler_h_ + +// C Includes +// C++ Includes +#include <map> +#include <vector> +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/CompilerType.h" + +// This class will insert a UtilityFunction into the inferior process for +// calling libBacktraceRecording's introspection_get_dispatch_queues() +// function. The function in the inferior will return a struct by value +// with these members: +// +// struct get_current_queues_return_values +// { +// introspection_dispatch_queue_info_t *queues_buffer; +// uint64_t queues_buffer_size; +// uint64_t count; +// }; +// +// The queues_buffer pointer is an address in the inferior program's address +// space (queues_buffer_size in size) which must be mach_vm_deallocate'd by +// lldb. count is the number of queues that were stored in the buffer. +// +// The AppleGetQueuesHandler object should persist so that the UtilityFunction +// can be reused multiple times. + +namespace lldb_private +{ + +class AppleGetQueuesHandler { +public: + + AppleGetQueuesHandler (lldb_private::Process *process); + + ~AppleGetQueuesHandler(); + + struct GetQueuesReturnInfo + { + lldb::addr_t queues_buffer_ptr; /* the address of the queues buffer from libBacktraceRecording */ + lldb::addr_t queues_buffer_size; /* the size of the queues buffer from libBacktraceRecording */ + uint64_t count; /* the number of queues included in the queues buffer */ + + GetQueuesReturnInfo() : + queues_buffer_ptr(LLDB_INVALID_ADDRESS), + queues_buffer_size(0), + count(0) + {} + }; + + //---------------------------------------------------------- + /// Get the list of queues that exist (with any active or pending items) via + /// a call to introspection_get_dispatch_queues(). If there's a page of + /// memory that needs to be freed, pass in the address and size and it will + /// be freed before getting the list of queues. + /// + /// @param [in] thread + /// The thread to run this plan on. + /// + /// @param [in] page_to_free + /// An address of an inferior process vm page that needs to be deallocated, + /// LLDB_INVALID_ADDRESS if this is not needed. + /// + /// @param [in] page_to_free_size + /// The size of the vm page that needs to be deallocated if an address was + /// passed in to page_to_free. + /// + /// @param [out] error + /// This object will be updated with the error status / error string from any failures encountered. + /// + /// @returns + /// The result of the inferior function call execution. If there was a failure of any kind while getting + /// the information, the queues_buffer_ptr value will be LLDB_INVALID_ADDRESS. + //---------------------------------------------------------- + GetQueuesReturnInfo + GetCurrentQueues (Thread &thread, lldb::addr_t page_to_free, uint64_t page_to_free_size, lldb_private::Error &error); + + + void + Detach (); + +private: + + lldb::addr_t + SetupGetQueuesFunction (Thread &thread, ValueList &get_queues_arglist); + + static const char *g_get_current_queues_function_name; + static const char *g_get_current_queues_function_code; + + lldb_private::Process *m_process; + std::unique_ptr<UtilityFunction> m_get_queues_impl_code_up; + Mutex m_get_queues_function_mutex; + + lldb::addr_t m_get_queues_return_buffer_addr; + Mutex m_get_queues_retbuffer_mutex; + +}; + +} // using namespace lldb_private + +#endif // lldb_AppleGetQueuesHandler_h_ diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp b/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp new file mode 100644 index 000000000000..ba03f51152c0 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp @@ -0,0 +1,385 @@ +//===-- AppleGetThreadItemInfoHandler.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleGetThreadItemInfoHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/Expression.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +const char *AppleGetThreadItemInfoHandler::g_get_thread_item_info_function_name = "__lldb_backtrace_recording_get_thread_item_info"; +const char *AppleGetThreadItemInfoHandler::g_get_thread_item_info_function_code = " \n\ +extern \"C\" \n\ +{ \n\ + /* \n\ + * mach defines \n\ + */ \n\ + \n\ + typedef unsigned int uint32_t; \n\ + typedef unsigned long long uint64_t; \n\ + typedef uint32_t mach_port_t; \n\ + typedef mach_port_t vm_map_t; \n\ + typedef int kern_return_t; \n\ + typedef uint64_t mach_vm_address_t; \n\ + typedef uint64_t mach_vm_size_t; \n\ + \n\ + mach_port_t mach_task_self (); \n\ + kern_return_t mach_vm_deallocate (vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); \n\ + \n\ + typedef void *pthread_t; \n\ + extern int printf(const char *format, ...); \n\ + extern pthread_t pthread_self(void); \n\ + \n\ + /* \n\ + * libBacktraceRecording defines \n\ + */ \n\ + \n\ + typedef uint32_t queue_list_scope_t; \n\ + typedef void *dispatch_queue_t; \n\ + typedef void *introspection_dispatch_queue_info_t; \n\ + typedef void *introspection_dispatch_item_info_ref; \n\ + \n\ + extern void __introspection_dispatch_thread_get_item_info (uint64_t thread_id, \n\ + introspection_dispatch_item_info_ref *returned_queues_buffer, \n\ + uint64_t *returned_queues_buffer_size); \n\ + \n\ + /* \n\ + * return type define \n\ + */ \n\ + \n\ + struct get_thread_item_info_return_values \n\ + { \n\ + uint64_t item_info_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ \n\ + uint64_t item_info_buffer_size; /* the size of the items buffer from libBacktraceRecording */ \n\ + }; \n\ + \n\ + void __lldb_backtrace_recording_get_thread_item_info \n\ + (struct get_thread_item_info_return_values *return_buffer, \n\ + int debug, \n\ + uint64_t thread_id, \n\ + void *page_to_free, \n\ + uint64_t page_to_free_size) \n\ +{ \n\ + void *pthread_id = pthread_self (); \n\ + if (debug) \n\ + printf (\"entering get_thread_item_info with args return_buffer == %p, debug == %d, thread id == 0x%llx, page_to_free == %p, page_to_free_size == 0x%llx\\n\", return_buffer, debug, (uint64_t) thread_id, page_to_free, page_to_free_size); \n\ + if (page_to_free != 0) \n\ + { \n\ + mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\ + } \n\ + \n\ + __introspection_dispatch_thread_get_item_info (thread_id, \n\ + (void**)&return_buffer->item_info_buffer_ptr, \n\ + &return_buffer->item_info_buffer_size); \n\ +} \n\ +} \n\ +"; + +AppleGetThreadItemInfoHandler::AppleGetThreadItemInfoHandler (Process *process) : + m_process (process), + m_get_thread_item_info_impl_code (), + m_get_thread_item_info_function_mutex(), + m_get_thread_item_info_return_buffer_addr (LLDB_INVALID_ADDRESS), + m_get_thread_item_info_retbuffer_mutex() +{ +} + +AppleGetThreadItemInfoHandler::~AppleGetThreadItemInfoHandler () +{ +} + +void +AppleGetThreadItemInfoHandler::Detach () +{ + + if (m_process && m_process->IsAlive() && m_get_thread_item_info_return_buffer_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker locker; + locker.TryLock (m_get_thread_item_info_retbuffer_mutex); // Even if we don't get the lock, deallocate the buffer + m_process->DeallocateMemory (m_get_thread_item_info_return_buffer_addr); + } +} + +// Compile our __lldb_backtrace_recording_get_thread_item_info() function (from the +// source above in g_get_thread_item_info_function_code) if we don't find that function in the inferior +// already with USE_BUILTIN_FUNCTION defined. (e.g. this would be the case for testing.) +// +// Insert the __lldb_backtrace_recording_get_thread_item_info into the inferior process if needed. +// +// Write the get_thread_item_info_arglist into the inferior's memory space to prepare for the call. +// +// Returns the address of the arguments written down in the inferior process, which can be used to +// make the function call. + +lldb::addr_t +AppleGetThreadItemInfoHandler::SetupGetThreadItemInfoFunction (Thread &thread, ValueList &get_thread_item_info_arglist) +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + Address impl_code_address; + StreamString errors; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + FunctionCaller *get_thread_item_info_caller = nullptr; + + // Scope for mutex locker: + { + Mutex::Locker locker(m_get_thread_item_info_function_mutex); + + // First stage is to make the ClangUtility to hold our injected function: + + if (!m_get_thread_item_info_impl_code.get()) + { + Error error; + if (g_get_thread_item_info_function_code != NULL) + { + m_get_thread_item_info_impl_code.reset (exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage (g_get_thread_item_info_function_code, + eLanguageTypeC, + g_get_thread_item_info_function_name, + error)); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to get UtilityFunction for get-thread-item-info introspection: %s.", + error.AsCString()); + m_get_thread_item_info_impl_code.reset(); + return args_addr; + } + + if (!m_get_thread_item_info_impl_code->Install(errors, exe_ctx)) + { + if (log) + log->Printf ("Failed to install get-thread-item-info introspection: %s.", errors.GetData()); + m_get_thread_item_info_impl_code.reset(); + return args_addr; + } + } + else + { + if (log) + log->Printf("No get-thread-item-info introspection code found."); + errors.Printf ("No get-thread-item-info introspection code found."); + return LLDB_INVALID_ADDRESS; + } + + // Also make the FunctionCaller for this UtilityFunction: + + ClangASTContext *clang_ast_context = thread.GetProcess()->GetTarget().GetScratchClangASTContext(); + CompilerType get_thread_item_info_return_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + + get_thread_item_info_caller = m_get_thread_item_info_impl_code->MakeFunctionCaller (get_thread_item_info_return_type, + get_thread_item_info_arglist, + error); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to install get-thread-item-info introspection caller: %s.", error.AsCString()); + m_get_thread_item_info_impl_code.reset(); + return args_addr; + } + + } + else + { + get_thread_item_info_caller = m_get_thread_item_info_impl_code->GetFunctionCaller(); + } + } + + errors.Clear(); + + // Now write down the argument values for this particular call. This looks like it might be a race condition + // if other threads were calling into here, but actually it isn't because we allocate a new args structure for + // this call by passing args_addr = LLDB_INVALID_ADDRESS... + + if (!get_thread_item_info_caller->WriteFunctionArguments (exe_ctx, args_addr, get_thread_item_info_arglist, errors)) + { + if (log) + log->Printf ("Error writing get-thread-item-info function arguments: \"%s\".", errors.GetData()); + return args_addr; + } + + return args_addr; +} + +AppleGetThreadItemInfoHandler::GetThreadItemInfoReturnInfo +AppleGetThreadItemInfoHandler::GetThreadItemInfo (Thread &thread, tid_t thread_id, addr_t page_to_free, uint64_t page_to_free_size, Error &error) +{ + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + ProcessSP process_sp (thread.CalculateProcess()); + TargetSP target_sp (thread.CalculateTarget()); + ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + + GetThreadItemInfoReturnInfo return_value; + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return_value.item_buffer_size = 0; + + error.Clear(); + + if (thread.SafeToCallFunctions() == false) + { + if (log) + log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID()); + error.SetErrorString ("Not safe to call functions on this thread."); + return return_value; + } + + // Set up the arguments for a call to + + // struct get_thread_item_info_return_values + // { + // uint64_t item_info_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ + // uint64_t item_info_buffer_size; /* the size of the items buffer from libBacktraceRecording */ + // }; + // + // void __lldb_backtrace_recording_get_thread_item_info + // (struct get_thread_item_info_return_values *return_buffer, + // int debug, + // void *page_to_free, + // uint64_t page_to_free_size) + + // Where the return_buffer argument points to a 24 byte region of memory already allocated by lldb in + // the inferior process. + + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + Value return_buffer_ptr_value; + return_buffer_ptr_value.SetValueType (Value::eValueTypeScalar); + return_buffer_ptr_value.SetCompilerType (clang_void_ptr_type); + + CompilerType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt); + Value debug_value; + debug_value.SetValueType (Value::eValueTypeScalar); + debug_value.SetCompilerType (clang_int_type); + + CompilerType clang_uint64_type = clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong); + Value thread_id_value; + thread_id_value.SetValueType (Value::eValueTypeScalar); + thread_id_value.SetCompilerType (clang_uint64_type); + + Value page_to_free_value; + page_to_free_value.SetValueType (Value::eValueTypeScalar); + page_to_free_value.SetCompilerType (clang_void_ptr_type); + + Value page_to_free_size_value; + page_to_free_size_value.SetValueType (Value::eValueTypeScalar); + page_to_free_size_value.SetCompilerType (clang_uint64_type); + + + Mutex::Locker locker(m_get_thread_item_info_retbuffer_mutex); + if (m_get_thread_item_info_return_buffer_addr == LLDB_INVALID_ADDRESS) + { + addr_t bufaddr = process_sp->AllocateMemory (32, ePermissionsReadable | ePermissionsWritable, error); + if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("Failed to allocate memory for return buffer for get current queues func call"); + return return_value; + } + m_get_thread_item_info_return_buffer_addr = bufaddr; + } + + ValueList argument_values; + + return_buffer_ptr_value.GetScalar() = m_get_thread_item_info_return_buffer_addr; + argument_values.PushValue (return_buffer_ptr_value); + + debug_value.GetScalar() = 0; + argument_values.PushValue (debug_value); + + thread_id_value.GetScalar() = thread_id; + argument_values.PushValue (thread_id_value); + + if (page_to_free != LLDB_INVALID_ADDRESS) + page_to_free_value.GetScalar() = page_to_free; + else + page_to_free_value.GetScalar() = 0; + argument_values.PushValue (page_to_free_value); + + page_to_free_size_value.GetScalar() = page_to_free_size; + argument_values.PushValue (page_to_free_size_value); + + addr_t args_addr = SetupGetThreadItemInfoFunction (thread, argument_values); + + StreamString errors; + ExecutionContext exe_ctx; + EvaluateExpressionOptions options; + FunctionCaller *get_thread_item_info_caller = nullptr; + + options.SetUnwindOnError (true); + options.SetIgnoreBreakpoints (true); + options.SetStopOthers (true); + options.SetTimeoutUsec(500000); + options.SetTryAllThreads (false); + thread.CalculateExecutionContext (exe_ctx); + + if (!m_get_thread_item_info_impl_code) + { + error.SetErrorString ("Unable to compile function to call __introspection_dispatch_thread_get_item_info"); + return return_value; + } + + get_thread_item_info_caller = m_get_thread_item_info_impl_code->GetFunctionCaller(); + + if (!get_thread_item_info_caller) + { + error.SetErrorString ("Unable to compile function caller for __introspection_dispatch_thread_get_item_info"); + return return_value; + } + + ExpressionResults func_call_ret; + Value results; + func_call_ret = get_thread_item_info_caller->ExecuteFunction (exe_ctx, &args_addr, options, errors, results); + if (func_call_ret != eExpressionCompleted || !error.Success()) + { + if (log) + log->Printf ("Unable to call __introspection_dispatch_thread_get_item_info(), got ExpressionResults %d, error contains %s", func_call_ret, error.AsCString("")); + error.SetErrorString ("Unable to call __introspection_dispatch_thread_get_item_info() for list of queues"); + return return_value; + } + + return_value.item_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory (m_get_thread_item_info_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error); + if (!error.Success() || return_value.item_buffer_ptr == LLDB_INVALID_ADDRESS) + { + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.item_buffer_size = m_process->ReadUnsignedIntegerFromMemory (m_get_thread_item_info_return_buffer_addr + 8, 8, 0, error); + + if (!error.Success()) + { + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + if (log) + log->Printf ("AppleGetThreadItemInfoHandler called __introspection_dispatch_thread_get_item_info (page_to_free == 0x%" PRIx64 ", size = %" PRId64 "), returned page is at 0x%" PRIx64 ", size %" PRId64, page_to_free, page_to_free_size, return_value.item_buffer_ptr, return_value.item_buffer_size); + + return return_value; +} diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.h b/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.h new file mode 100644 index 000000000000..c1798fb515b4 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.h @@ -0,0 +1,113 @@ +//===-- AppleGetThreadItemInfoHandler.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_AppleGetThreadItemInfoHandler_h_ +#define lldb_AppleGetThreadItemInfoHandler_h_ + +// C Includes +// C++ Includes +#include <map> +#include <vector> +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/CompilerType.h" + +// This class will insert a UtilityFunction into the inferior process for +// calling libBacktraceRecording's __introspection_dispatch_thread_get_item_info() +// function. The function in the inferior will return a struct by value +// with these members: +// +// struct get_thread_item_info_return_values +// { +// introspection_dispatch_item_info_ref *item_buffer; +// uint64_t item_buffer_size; +// }; +// +// The item_buffer pointer is an address in the inferior program's address +// space (item_buffer_size in size) which must be mach_vm_deallocate'd by +// lldb. +// +// The AppleGetThreadItemInfoHandler object should persist so that the UtilityFunction +// can be reused multiple times. + +namespace lldb_private +{ + +class AppleGetThreadItemInfoHandler { +public: + + AppleGetThreadItemInfoHandler (lldb_private::Process *process); + + ~AppleGetThreadItemInfoHandler(); + + struct GetThreadItemInfoReturnInfo + { + lldb::addr_t item_buffer_ptr; /* the address of the item buffer from libBacktraceRecording */ + lldb::addr_t item_buffer_size; /* the size of the item buffer from libBacktraceRecording */ + + GetThreadItemInfoReturnInfo() : + item_buffer_ptr(LLDB_INVALID_ADDRESS), + item_buffer_size(0) + {} + }; + + //---------------------------------------------------------- + /// Get the information about a work item by calling + /// __introspection_dispatch_thread_get_item_info. If there's a page of + /// memory that needs to be freed, pass in the address and size and it will + /// be freed before getting the list of queues. + /// + /// @param [in] thread_id + /// The thread to get the extended backtrace for. + /// + /// @param [in] page_to_free + /// An address of an inferior process vm page that needs to be deallocated, + /// LLDB_INVALID_ADDRESS if this is not needed. + /// + /// @param [in] page_to_free_size + /// The size of the vm page that needs to be deallocated if an address was + /// passed in to page_to_free. + /// + /// @param [out] error + /// This object will be updated with the error status / error string from any failures encountered. + /// + /// @returns + /// The result of the inferior function call execution. If there was a failure of any kind while getting + /// the information, the item_buffer_ptr value will be LLDB_INVALID_ADDRESS. + //---------------------------------------------------------- + GetThreadItemInfoReturnInfo + GetThreadItemInfo (Thread &thread, lldb::tid_t thread_id, lldb::addr_t page_to_free, uint64_t page_to_free_size, lldb_private::Error &error); + + + void + Detach (); + +private: + + lldb::addr_t + SetupGetThreadItemInfoFunction (Thread &thread, ValueList &get_thread_item_info_arglist); + + static const char *g_get_thread_item_info_function_name; + static const char *g_get_thread_item_info_function_code; + + lldb_private::Process *m_process; + std::unique_ptr<UtilityFunction> m_get_thread_item_info_impl_code; + Mutex m_get_thread_item_info_function_mutex; + + lldb::addr_t m_get_thread_item_info_return_buffer_addr; + Mutex m_get_thread_item_info_retbuffer_mutex; + +}; + +} // using namespace lldb_private + +#endif // lldb_AppleGetThreadItemInfoHandler_h_ diff --git a/source/Plugins/SystemRuntime/MacOSX/CMakeLists.txt b/source/Plugins/SystemRuntime/MacOSX/CMakeLists.txt new file mode 100644 index 000000000000..d1580cce20d6 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/CMakeLists.txt @@ -0,0 +1,7 @@ +add_lldb_library(lldbPluginSystemRuntimeMacOSX + AppleGetItemInfoHandler.cpp + AppleGetPendingItemsHandler.cpp + AppleGetQueuesHandler.cpp + AppleGetThreadItemInfoHandler.cpp + SystemRuntimeMacOSX.cpp + ) diff --git a/source/Plugins/SystemRuntime/MacOSX/Makefile b/source/Plugins/SystemRuntime/MacOSX/Makefile new file mode 100644 index 000000000000..eebfb52073d5 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/SystemRuntime/MacOSX ----*- 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 := lldbPluginSystemRuntimeMacOSX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp b/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp new file mode 100644 index 000000000000..b11a06760325 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp @@ -0,0 +1,1010 @@ +//===-- SystemRuntimeMacOSX.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "Plugins/Process/Utility/HistoryThread.h" +#include "lldb/Target/Queue.h" +#include "lldb/Target/QueueList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/ProcessStructReader.h" + +#include "SystemRuntimeMacOSX.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Create an instance of this class. This function is filled into +// the plugin info class that gets handed out by the plugin factory and +// allows the lldb to instantiate an instance of this class. +//---------------------------------------------------------------------- +SystemRuntime * +SystemRuntimeMacOSX::CreateInstance (Process* process) +{ + bool create = false; + if (!create) + { + create = true; + Module* exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) + { + ObjectFile *object_file = exe_module->GetObjectFile(); + if (object_file) + { + create = (object_file->GetStrata() == ObjectFile::eStrataUser); + } + } + + if (create) + { + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + switch (triple_ref.getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + case llvm::Triple::TvOS: + case llvm::Triple::WatchOS: + create = triple_ref.getVendor() == llvm::Triple::Apple; + break; + default: + create = false; + break; + } + } + } + + if (create) + return new SystemRuntimeMacOSX (process); + return NULL; +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +SystemRuntimeMacOSX::SystemRuntimeMacOSX (Process* process) : + SystemRuntime(process), + m_break_id(LLDB_INVALID_BREAK_ID), + m_mutex(Mutex::eMutexTypeRecursive), + m_get_queues_handler(process), + m_get_pending_items_handler(process), + m_get_item_info_handler(process), + m_get_thread_item_info_handler(process), + m_page_to_free(LLDB_INVALID_ADDRESS), + m_page_to_free_size(0), + m_lib_backtrace_recording_info(), + m_dispatch_queue_offsets_addr (LLDB_INVALID_ADDRESS), + m_libdispatch_offsets(), + m_libpthread_layout_offsets_addr (LLDB_INVALID_ADDRESS), + m_libpthread_offsets(), + m_dispatch_tsd_indexes_addr (LLDB_INVALID_ADDRESS), + m_libdispatch_tsd_indexes(), + m_dispatch_voucher_offsets_addr (LLDB_INVALID_ADDRESS), + m_libdispatch_voucher_offsets() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SystemRuntimeMacOSX::~SystemRuntimeMacOSX() +{ + Clear (true); +} + +void +SystemRuntimeMacOSX::Detach () +{ + m_get_queues_handler.Detach(); + m_get_pending_items_handler.Detach(); + m_get_item_info_handler.Detach(); + m_get_thread_item_info_handler.Detach(); +} + +//---------------------------------------------------------------------- +// Clear out the state of this class. +//---------------------------------------------------------------------- +void +SystemRuntimeMacOSX::Clear (bool clear_process) +{ + Mutex::Locker locker(m_mutex); + + if (m_process->IsAlive() && LLDB_BREAK_ID_IS_VALID(m_break_id)) + m_process->ClearBreakpointSiteByID(m_break_id); + + if (clear_process) + m_process = NULL; + m_break_id = LLDB_INVALID_BREAK_ID; +} + + +std::string +SystemRuntimeMacOSX::GetQueueNameFromThreadQAddress (addr_t dispatch_qaddr) +{ + std::string dispatch_queue_name; + if (dispatch_qaddr == LLDB_INVALID_ADDRESS || dispatch_qaddr == 0) + return ""; + + ReadLibdispatchOffsets (); + if (m_libdispatch_offsets.IsValid ()) + { + // dispatch_qaddr is from a thread_info(THREAD_IDENTIFIER_INFO) call for a thread - + // deref it to get the address of the dispatch_queue_t structure for this thread's + // queue. + Error error; + addr_t dispatch_queue_addr = m_process->ReadPointerFromMemory (dispatch_qaddr, error); + if (error.Success()) + { + if (m_libdispatch_offsets.dqo_version >= 4) + { + // libdispatch versions 4+, pointer to dispatch name is in the + // queue structure. + addr_t pointer_to_label_address = dispatch_queue_addr + m_libdispatch_offsets.dqo_label; + addr_t label_addr = m_process->ReadPointerFromMemory (pointer_to_label_address, error); + if (error.Success()) + { + m_process->ReadCStringFromMemory (label_addr, dispatch_queue_name, error); + } + } + else + { + // libdispatch versions 1-3, dispatch name is a fixed width char array + // in the queue structure. + addr_t label_addr = dispatch_queue_addr + m_libdispatch_offsets.dqo_label; + dispatch_queue_name.resize (m_libdispatch_offsets.dqo_label_size, '\0'); + size_t bytes_read = m_process->ReadMemory (label_addr, &dispatch_queue_name[0], m_libdispatch_offsets.dqo_label_size, error); + if (bytes_read < m_libdispatch_offsets.dqo_label_size) + dispatch_queue_name.erase (bytes_read); + } + } + } + return dispatch_queue_name; +} + +lldb::addr_t +SystemRuntimeMacOSX::GetLibdispatchQueueAddressFromThreadQAddress (addr_t dispatch_qaddr) +{ + addr_t libdispatch_queue_t_address = LLDB_INVALID_ADDRESS; + Error error; + libdispatch_queue_t_address = m_process->ReadPointerFromMemory (dispatch_qaddr, error); + if (!error.Success()) + { + libdispatch_queue_t_address = LLDB_INVALID_ADDRESS; + } + return libdispatch_queue_t_address; +} + +lldb::QueueKind +SystemRuntimeMacOSX::GetQueueKind (addr_t dispatch_queue_addr) +{ + if (dispatch_queue_addr == LLDB_INVALID_ADDRESS || dispatch_queue_addr == 0) + return eQueueKindUnknown; + + QueueKind kind = eQueueKindUnknown; + ReadLibdispatchOffsets (); + if (m_libdispatch_offsets.IsValid () && m_libdispatch_offsets.dqo_version >= 4) + { + Error error; + uint64_t width = m_process->ReadUnsignedIntegerFromMemory (dispatch_queue_addr + m_libdispatch_offsets.dqo_width, m_libdispatch_offsets.dqo_width_size, 0, error); + if (error.Success()) + { + if (width == 1) + { + kind = eQueueKindSerial; + } + if (width > 1) + { + kind = eQueueKindConcurrent; + } + } + } + return kind; +} + +void +SystemRuntimeMacOSX::AddThreadExtendedInfoPacketHints (lldb_private::StructuredData::ObjectSP dict_sp) +{ + StructuredData::Dictionary *dict = dict_sp->GetAsDictionary(); + if (dict) + { + ReadLibpthreadOffsets(); + if (m_libpthread_offsets.IsValid()) + { + dict->AddIntegerItem ("plo_pthread_tsd_base_offset", m_libpthread_offsets.plo_pthread_tsd_base_offset); + dict->AddIntegerItem ("plo_pthread_tsd_base_address_offset", m_libpthread_offsets.plo_pthread_tsd_base_address_offset); + dict->AddIntegerItem ("plo_pthread_tsd_entry_size", m_libpthread_offsets.plo_pthread_tsd_entry_size); + } + + ReadLibdispatchTSDIndexes (); + if (m_libdispatch_tsd_indexes.IsValid()) + { + dict->AddIntegerItem ("dti_queue_index", m_libdispatch_tsd_indexes.dti_queue_index); + dict->AddIntegerItem ("dti_voucher_index", m_libdispatch_tsd_indexes.dti_voucher_index); + dict->AddIntegerItem ("dti_qos_class_index", m_libdispatch_tsd_indexes.dti_qos_class_index); + } + } +} + +bool +SystemRuntimeMacOSX::SafeToCallFunctionsOnThisThread (ThreadSP thread_sp) +{ + if (thread_sp && thread_sp->GetStackFrameCount() > 0 && thread_sp->GetFrameWithConcreteFrameIndex(0)) + { + const SymbolContext sym_ctx (thread_sp->GetFrameWithConcreteFrameIndex(0)->GetSymbolContext (eSymbolContextSymbol)); + static ConstString g_select_symbol ("__select"); + if (sym_ctx.GetFunctionName() == g_select_symbol) + { + return false; + } + } + return true; +} + +lldb::queue_id_t +SystemRuntimeMacOSX::GetQueueIDFromThreadQAddress (lldb::addr_t dispatch_qaddr) +{ + queue_id_t queue_id = LLDB_INVALID_QUEUE_ID; + + if (dispatch_qaddr == LLDB_INVALID_ADDRESS || dispatch_qaddr == 0) + return queue_id; + + ReadLibdispatchOffsets (); + if (m_libdispatch_offsets.IsValid ()) + { + // dispatch_qaddr is from a thread_info(THREAD_IDENTIFIER_INFO) call for a thread - + // deref it to get the address of the dispatch_queue_t structure for this thread's + // queue. + Error error; + uint64_t dispatch_queue_addr = m_process->ReadPointerFromMemory (dispatch_qaddr, error); + if (error.Success()) + { + addr_t serialnum_address = dispatch_queue_addr + m_libdispatch_offsets.dqo_serialnum; + queue_id_t serialnum = m_process->ReadUnsignedIntegerFromMemory (serialnum_address, m_libdispatch_offsets.dqo_serialnum_size, LLDB_INVALID_QUEUE_ID, error); + if (error.Success()) + { + queue_id = serialnum; + } + } + } + + return queue_id; +} + + +void +SystemRuntimeMacOSX::ReadLibdispatchOffsetsAddress () +{ + if (m_dispatch_queue_offsets_addr != LLDB_INVALID_ADDRESS) + return; + + static ConstString g_dispatch_queue_offsets_symbol_name ("dispatch_queue_offsets"); + const Symbol *dispatch_queue_offsets_symbol = NULL; + + // libdispatch symbols were in libSystem.B.dylib up through Mac OS X 10.6 ("Snow Leopard") + ModuleSpec libSystem_module_spec (FileSpec("libSystem.B.dylib", false)); + ModuleSP module_sp(m_process->GetTarget().GetImages().FindFirstModule (libSystem_module_spec)); + if (module_sp) + dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (g_dispatch_queue_offsets_symbol_name, eSymbolTypeData); + + // libdispatch symbols are in their own dylib as of Mac OS X 10.7 ("Lion") and later + if (dispatch_queue_offsets_symbol == NULL) + { + ModuleSpec libdispatch_module_spec (FileSpec("libdispatch.dylib", false)); + module_sp = m_process->GetTarget().GetImages().FindFirstModule (libdispatch_module_spec); + if (module_sp) + dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (g_dispatch_queue_offsets_symbol_name, eSymbolTypeData); + } + if (dispatch_queue_offsets_symbol) + m_dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetLoadAddress(&m_process->GetTarget()); +} + +void +SystemRuntimeMacOSX::ReadLibdispatchOffsets () +{ + if (m_libdispatch_offsets.IsValid()) + return; + + ReadLibdispatchOffsetsAddress (); + + uint8_t memory_buffer[sizeof (struct LibdispatchOffsets)]; + DataExtractor data (memory_buffer, + sizeof(memory_buffer), + m_process->GetByteOrder(), + m_process->GetAddressByteSize()); + + Error error; + if (m_process->ReadMemory (m_dispatch_queue_offsets_addr, memory_buffer, sizeof(memory_buffer), error) == sizeof(memory_buffer)) + { + lldb::offset_t data_offset = 0; + + // The struct LibdispatchOffsets is a series of uint16_t's - extract them all + // in one big go. + data.GetU16 (&data_offset, &m_libdispatch_offsets.dqo_version, sizeof (struct LibdispatchOffsets) / sizeof (uint16_t)); + } +} + +void +SystemRuntimeMacOSX::ReadLibpthreadOffsetsAddress () +{ + if (m_libpthread_layout_offsets_addr != LLDB_INVALID_ADDRESS) + return; + + static ConstString g_libpthread_layout_offsets_symbol_name ("pthread_layout_offsets"); + const Symbol *libpthread_layout_offsets_symbol = NULL; + + ModuleSpec libpthread_module_spec (FileSpec("libsystem_pthread.dylib", false)); + ModuleSP module_sp (m_process->GetTarget().GetImages().FindFirstModule (libpthread_module_spec)); + if (module_sp) + { + libpthread_layout_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType + (g_libpthread_layout_offsets_symbol_name, eSymbolTypeData); + if (libpthread_layout_offsets_symbol) + { + m_libpthread_layout_offsets_addr = libpthread_layout_offsets_symbol->GetLoadAddress(&m_process->GetTarget()); + } + } +} + +void +SystemRuntimeMacOSX::ReadLibpthreadOffsets () +{ + if (m_libpthread_offsets.IsValid()) + return; + + ReadLibpthreadOffsetsAddress (); + + if (m_libpthread_layout_offsets_addr != LLDB_INVALID_ADDRESS) + { + uint8_t memory_buffer[sizeof (struct LibpthreadOffsets)]; + DataExtractor data (memory_buffer, + sizeof(memory_buffer), + m_process->GetByteOrder(), + m_process->GetAddressByteSize()); + Error error; + if (m_process->ReadMemory (m_libpthread_layout_offsets_addr, memory_buffer, sizeof(memory_buffer), error) == sizeof(memory_buffer)) + { + lldb::offset_t data_offset = 0; + + // The struct LibpthreadOffsets is a series of uint16_t's - extract them all + // in one big go. + data.GetU16 (&data_offset, &m_libpthread_offsets.plo_version, sizeof (struct LibpthreadOffsets) / sizeof (uint16_t)); + } + } +} + +void +SystemRuntimeMacOSX::ReadLibdispatchTSDIndexesAddress () +{ + if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) + return; + + static ConstString g_libdispatch_tsd_indexes_symbol_name ("dispatch_tsd_indexes"); + const Symbol *libdispatch_tsd_indexes_symbol = NULL; + + ModuleSpec libpthread_module_spec (FileSpec("libdispatch.dylib", false)); + ModuleSP module_sp (m_process->GetTarget().GetImages().FindFirstModule (libpthread_module_spec)); + if (module_sp) + { + libdispatch_tsd_indexes_symbol = module_sp->FindFirstSymbolWithNameAndType + (g_libdispatch_tsd_indexes_symbol_name, eSymbolTypeData); + if (libdispatch_tsd_indexes_symbol) + { + m_dispatch_tsd_indexes_addr = libdispatch_tsd_indexes_symbol->GetLoadAddress(&m_process->GetTarget()); + } + } +} + +void +SystemRuntimeMacOSX::ReadLibdispatchTSDIndexes () +{ + if (m_libdispatch_tsd_indexes.IsValid()) + return; + + ReadLibdispatchTSDIndexesAddress (); + + if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) + { + + // We don't need to check the version number right now, it will be at least 2, but + // keep this code around to fetch just the version # for the future where we need + // to fetch alternate versions of the struct. +# if 0 + uint16_t dti_version = 2; + Address dti_struct_addr; + if (m_process->GetTarget().ResolveLoadAddress (m_dispatch_tsd_indexes_addr, dti_struct_addr)) + { + Error error; + uint16_t version = m_process->GetTarget().ReadUnsignedIntegerFromMemory (dti_struct_addr, false, 2, UINT16_MAX, error); + if (error.Success() && dti_version != UINT16_MAX) + { + dti_version = version; + } + } +#endif + + ClangASTContext *ast_ctx = m_process->GetTarget().GetScratchClangASTContext(); + if (ast_ctx->getASTContext() && m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) + { + CompilerType uint16 = ast_ctx->GetBuiltinTypeForEncodingAndBitSize (eEncodingUint, 16); + CompilerType dispatch_tsd_indexes_s = ast_ctx->CreateRecordType(nullptr, lldb::eAccessPublic, "__lldb_dispatch_tsd_indexes_s", clang::TTK_Struct, lldb::eLanguageTypeC); + + ClangASTContext::StartTagDeclarationDefinition(dispatch_tsd_indexes_s); + ClangASTContext::AddFieldToRecordType (dispatch_tsd_indexes_s, "dti_version", uint16, lldb::eAccessPublic, 0); + ClangASTContext::AddFieldToRecordType (dispatch_tsd_indexes_s, "dti_queue_index", uint16, lldb::eAccessPublic, 0); + ClangASTContext::AddFieldToRecordType (dispatch_tsd_indexes_s, "dti_voucher_index", uint16, lldb::eAccessPublic, 0); + ClangASTContext::AddFieldToRecordType (dispatch_tsd_indexes_s, "dti_qos_class_index", uint16, lldb::eAccessPublic, 0); + ClangASTContext::CompleteTagDeclarationDefinition(dispatch_tsd_indexes_s); + + ProcessStructReader struct_reader (m_process, m_dispatch_tsd_indexes_addr, dispatch_tsd_indexes_s); + + m_libdispatch_tsd_indexes.dti_version = struct_reader.GetField<uint16_t>(ConstString("dti_version")); + m_libdispatch_tsd_indexes.dti_queue_index = struct_reader.GetField<uint16_t>(ConstString("dti_queue_index")); + m_libdispatch_tsd_indexes.dti_voucher_index = struct_reader.GetField<uint16_t>(ConstString("dti_voucher_index")); + m_libdispatch_tsd_indexes.dti_qos_class_index = struct_reader.GetField<uint16_t>(ConstString("dti_qos_class_index")); + } + } +} + + +ThreadSP +SystemRuntimeMacOSX::GetExtendedBacktraceThread (ThreadSP real_thread, ConstString type) +{ + ThreadSP originating_thread_sp; + if (BacktraceRecordingHeadersInitialized() && type == ConstString ("libdispatch")) + { + Error error; + + // real_thread is either an actual, live thread (in which case we need to call into + // libBacktraceRecording to find its originator) or it is an extended backtrace itself, + // in which case we get the token from it and call into libBacktraceRecording to find + // the originator of that token. + + if (real_thread->GetExtendedBacktraceToken() != LLDB_INVALID_ADDRESS) + { + originating_thread_sp = GetExtendedBacktraceFromItemRef (real_thread->GetExtendedBacktraceToken()); + } + else + { + ThreadSP cur_thread_sp (m_process->GetThreadList().GetSelectedThread()); + AppleGetThreadItemInfoHandler::GetThreadItemInfoReturnInfo ret = m_get_thread_item_info_handler.GetThreadItemInfo (*cur_thread_sp.get(), real_thread->GetID(), m_page_to_free, m_page_to_free_size, error); + m_page_to_free = LLDB_INVALID_ADDRESS; + m_page_to_free_size = 0; + if (ret.item_buffer_ptr != 0 && ret.item_buffer_ptr != LLDB_INVALID_ADDRESS && ret.item_buffer_size > 0) + { + DataBufferHeap data (ret.item_buffer_size, 0); + if (m_process->ReadMemory (ret.item_buffer_ptr, data.GetBytes(), ret.item_buffer_size, error) && error.Success()) + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); + ItemInfo item = ExtractItemInfoFromBuffer (extractor); + bool stop_id_is_valid = true; + if (item.stop_id == 0) + stop_id_is_valid = false; + originating_thread_sp.reset (new HistoryThread (*m_process, + item.enqueuing_thread_id, + item.enqueuing_callstack, + item.stop_id, + stop_id_is_valid)); + originating_thread_sp->SetExtendedBacktraceToken (item.item_that_enqueued_this); + originating_thread_sp->SetQueueName (item.enqueuing_queue_label.c_str()); + originating_thread_sp->SetQueueID (item.enqueuing_queue_serialnum); +// originating_thread_sp->SetThreadName (item.enqueuing_thread_label.c_str()); + } + m_page_to_free = ret.item_buffer_ptr; + m_page_to_free_size = ret.item_buffer_size; + } + } + } + return originating_thread_sp; +} + +ThreadSP +SystemRuntimeMacOSX::GetExtendedBacktraceFromItemRef (lldb::addr_t item_ref) +{ + ThreadSP return_thread_sp; + + AppleGetItemInfoHandler::GetItemInfoReturnInfo ret; + ThreadSP cur_thread_sp (m_process->GetThreadList().GetSelectedThread()); + Error error; + ret = m_get_item_info_handler.GetItemInfo (*cur_thread_sp.get(), item_ref, m_page_to_free, m_page_to_free_size, error); + m_page_to_free = LLDB_INVALID_ADDRESS; + m_page_to_free_size = 0; + if (ret.item_buffer_ptr != 0 && ret.item_buffer_ptr != LLDB_INVALID_ADDRESS && ret.item_buffer_size > 0) + { + DataBufferHeap data (ret.item_buffer_size, 0); + if (m_process->ReadMemory (ret.item_buffer_ptr, data.GetBytes(), ret.item_buffer_size, error) && error.Success()) + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); + ItemInfo item = ExtractItemInfoFromBuffer (extractor); + bool stop_id_is_valid = true; + if (item.stop_id == 0) + stop_id_is_valid = false; + return_thread_sp.reset (new HistoryThread (*m_process, + item.enqueuing_thread_id, + item.enqueuing_callstack, + item.stop_id, + stop_id_is_valid)); + return_thread_sp->SetExtendedBacktraceToken (item.item_that_enqueued_this); + return_thread_sp->SetQueueName (item.enqueuing_queue_label.c_str()); + return_thread_sp->SetQueueID (item.enqueuing_queue_serialnum); +// return_thread_sp->SetThreadName (item.enqueuing_thread_label.c_str()); + + m_page_to_free = ret.item_buffer_ptr; + m_page_to_free_size = ret.item_buffer_size; + } + } + return return_thread_sp; +} + +ThreadSP +SystemRuntimeMacOSX::GetExtendedBacktraceForQueueItem (QueueItemSP queue_item_sp, ConstString type) +{ + ThreadSP extended_thread_sp; + if (type != ConstString("libdispatch")) + return extended_thread_sp; + + bool stop_id_is_valid = true; + if (queue_item_sp->GetStopID() == 0) + stop_id_is_valid = false; + + extended_thread_sp.reset (new HistoryThread (*m_process, + queue_item_sp->GetEnqueueingThreadID(), + queue_item_sp->GetEnqueueingBacktrace(), + queue_item_sp->GetStopID(), + stop_id_is_valid)); + extended_thread_sp->SetExtendedBacktraceToken (queue_item_sp->GetItemThatEnqueuedThis()); + extended_thread_sp->SetQueueName (queue_item_sp->GetQueueLabel().c_str()); + extended_thread_sp->SetQueueID (queue_item_sp->GetEnqueueingQueueID()); +// extended_thread_sp->SetThreadName (queue_item_sp->GetThreadLabel().c_str()); + + return extended_thread_sp; +} + +/* Returns true if we were able to get the version / offset information + * out of libBacktraceRecording. false means we were unable to retrieve + * this; the queue_info_version field will be 0. + */ + +bool +SystemRuntimeMacOSX::BacktraceRecordingHeadersInitialized () +{ + if (m_lib_backtrace_recording_info.queue_info_version != 0) + return true; + + addr_t queue_info_version_address = LLDB_INVALID_ADDRESS; + addr_t queue_info_data_offset_address = LLDB_INVALID_ADDRESS; + addr_t item_info_version_address = LLDB_INVALID_ADDRESS; + addr_t item_info_data_offset_address = LLDB_INVALID_ADDRESS; + Target &target = m_process->GetTarget(); + + + static ConstString introspection_dispatch_queue_info_version ("__introspection_dispatch_queue_info_version"); + SymbolContextList sc_list; + if (m_process->GetTarget().GetImages().FindSymbolsWithNameAndType (introspection_dispatch_queue_info_version, eSymbolTypeData, sc_list) > 0) + { + SymbolContext sc; + sc_list.GetContextAtIndex (0, sc); + AddressRange addr_range; + sc.GetAddressRange (eSymbolContextSymbol, 0, false, addr_range); + queue_info_version_address = addr_range.GetBaseAddress().GetLoadAddress(&target); + } + sc_list.Clear(); + + static ConstString introspection_dispatch_queue_info_data_offset ("__introspection_dispatch_queue_info_data_offset"); + if (m_process->GetTarget().GetImages().FindSymbolsWithNameAndType (introspection_dispatch_queue_info_data_offset, eSymbolTypeData, sc_list) > 0) + { + SymbolContext sc; + sc_list.GetContextAtIndex (0, sc); + AddressRange addr_range; + sc.GetAddressRange (eSymbolContextSymbol, 0, false, addr_range); + queue_info_data_offset_address = addr_range.GetBaseAddress().GetLoadAddress(&target); + } + sc_list.Clear(); + + static ConstString introspection_dispatch_item_info_version ("__introspection_dispatch_item_info_version"); + if (m_process->GetTarget().GetImages().FindSymbolsWithNameAndType (introspection_dispatch_item_info_version, eSymbolTypeData, sc_list) > 0) + { + SymbolContext sc; + sc_list.GetContextAtIndex (0, sc); + AddressRange addr_range; + sc.GetAddressRange (eSymbolContextSymbol, 0, false, addr_range); + item_info_version_address = addr_range.GetBaseAddress().GetLoadAddress(&target); + } + sc_list.Clear(); + + static ConstString introspection_dispatch_item_info_data_offset ("__introspection_dispatch_item_info_data_offset"); + if (m_process->GetTarget().GetImages().FindSymbolsWithNameAndType (introspection_dispatch_item_info_data_offset, eSymbolTypeData, sc_list) > 0) + { + SymbolContext sc; + sc_list.GetContextAtIndex (0, sc); + AddressRange addr_range; + sc.GetAddressRange (eSymbolContextSymbol, 0, false, addr_range); + item_info_data_offset_address = addr_range.GetBaseAddress().GetLoadAddress(&target); + } + + if (queue_info_version_address != LLDB_INVALID_ADDRESS + && queue_info_data_offset_address != LLDB_INVALID_ADDRESS + && item_info_version_address != LLDB_INVALID_ADDRESS + && item_info_data_offset_address != LLDB_INVALID_ADDRESS) + { + Error error; + m_lib_backtrace_recording_info.queue_info_version = m_process->ReadUnsignedIntegerFromMemory (queue_info_version_address, 2, 0, error); + if (error.Success()) + { + m_lib_backtrace_recording_info.queue_info_data_offset = m_process->ReadUnsignedIntegerFromMemory (queue_info_data_offset_address, 2, 0, error); + if (error.Success()) + { + m_lib_backtrace_recording_info.item_info_version = m_process->ReadUnsignedIntegerFromMemory (item_info_version_address, 2, 0, error); + if (error.Success()) + { + m_lib_backtrace_recording_info.item_info_data_offset = m_process->ReadUnsignedIntegerFromMemory (item_info_data_offset_address, 2, 0, error); + if (!error.Success()) + { + m_lib_backtrace_recording_info.queue_info_version = 0; + } + } + else + { + m_lib_backtrace_recording_info.queue_info_version = 0; + } + } + else + { + m_lib_backtrace_recording_info.queue_info_version = 0; + } + } + } + + return m_lib_backtrace_recording_info.queue_info_version != 0; +} + +const std::vector<ConstString> & +SystemRuntimeMacOSX::GetExtendedBacktraceTypes () +{ + if (m_types.size () == 0) + { + m_types.push_back(ConstString("libdispatch")); + // We could have pthread as another type in the future if we have a way of + // gathering that information & it's useful to distinguish between them. + } + return m_types; +} + +void +SystemRuntimeMacOSX::PopulateQueueList (lldb_private::QueueList &queue_list) +{ + if (BacktraceRecordingHeadersInitialized()) + { + AppleGetQueuesHandler::GetQueuesReturnInfo queue_info_pointer; + ThreadSP cur_thread_sp (m_process->GetThreadList().GetSelectedThread()); + if (cur_thread_sp) + { + Error error; + queue_info_pointer = m_get_queues_handler.GetCurrentQueues (*cur_thread_sp.get(), m_page_to_free, m_page_to_free_size, error); + m_page_to_free = LLDB_INVALID_ADDRESS; + m_page_to_free_size = 0; + if (error.Success()) + { + + if (queue_info_pointer.count > 0 + && queue_info_pointer.queues_buffer_size > 0 + && queue_info_pointer.queues_buffer_ptr != 0 + && queue_info_pointer.queues_buffer_ptr != LLDB_INVALID_ADDRESS) + { + PopulateQueuesUsingLibBTR (queue_info_pointer.queues_buffer_ptr, queue_info_pointer.queues_buffer_size, queue_info_pointer.count, queue_list); + } + } + } + } + + // We either didn't have libBacktraceRecording (and need to create the queues list based on threads) + // or we did get the queues list from libBacktraceRecording but some special queues may not be + // included in its information. This is needed because libBacktraceRecording + // will only list queues with pending or running items by default - but the magic com.apple.main-thread + // queue on thread 1 is always around. + + for (ThreadSP thread_sp : m_process->Threads()) + { + if (thread_sp->GetQueueID() != LLDB_INVALID_QUEUE_ID) + { + if (queue_list.FindQueueByID (thread_sp->GetQueueID()).get() == NULL) + { + QueueSP queue_sp (new Queue(m_process->shared_from_this(), thread_sp->GetQueueID(), thread_sp->GetQueueName())); + queue_sp->SetKind (GetQueueKind (thread_sp->GetQueueLibdispatchQueueAddress())); + queue_sp->SetLibdispatchQueueAddress (thread_sp->GetQueueLibdispatchQueueAddress()); + queue_list.AddQueue (queue_sp); + } + } + } +} + +// Returns either an array of introspection_dispatch_item_info_ref's for the pending items on +// a queue or an array introspection_dispatch_item_info_ref's and code addresses for the +// pending items on a queue. The information about each of these pending items then needs to +// be fetched individually by passing the ref to libBacktraceRecording. + +SystemRuntimeMacOSX::PendingItemsForQueue +SystemRuntimeMacOSX::GetPendingItemRefsForQueue (lldb::addr_t queue) +{ + PendingItemsForQueue pending_item_refs; + AppleGetPendingItemsHandler::GetPendingItemsReturnInfo pending_items_pointer; + ThreadSP cur_thread_sp (m_process->GetThreadList().GetSelectedThread()); + if (cur_thread_sp) + { + Error error; + pending_items_pointer = m_get_pending_items_handler.GetPendingItems (*cur_thread_sp.get(), queue, m_page_to_free, m_page_to_free_size, error); + m_page_to_free = LLDB_INVALID_ADDRESS; + m_page_to_free_size = 0; + if (error.Success()) + { + if (pending_items_pointer.count > 0 + && pending_items_pointer.items_buffer_size > 0 + && pending_items_pointer.items_buffer_ptr != 0 + && pending_items_pointer.items_buffer_ptr != LLDB_INVALID_ADDRESS) + { + DataBufferHeap data (pending_items_pointer.items_buffer_size, 0); + if (m_process->ReadMemory (pending_items_pointer.items_buffer_ptr, data.GetBytes(), pending_items_pointer.items_buffer_size, error)) + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); + + // We either have an array of + // void* item_ref + // (old style) or we have a structure returned which looks like + // + // struct introspection_dispatch_pending_item_info_s { + // void *item_ref; + // void *function_or_block; + // }; + // + // struct introspection_dispatch_pending_items_array_s { + // uint32_t version; + // uint32_t size_of_item_info; + // introspection_dispatch_pending_item_info_s items[]; + // } + + offset_t offset = 0; + int i = 0; + uint32_t version = extractor.GetU32(&offset); + if (version == 1) + { + pending_item_refs.new_style = true; + uint32_t item_size = extractor.GetU32(&offset); + uint32_t start_of_array_offset = offset; + while (offset < pending_items_pointer.items_buffer_size && + static_cast<size_t>(i) < pending_items_pointer.count) + { + offset = start_of_array_offset + (i * item_size); + ItemRefAndCodeAddress item; + item.item_ref = extractor.GetPointer (&offset); + item.code_address = extractor.GetPointer (&offset); + pending_item_refs.item_refs_and_code_addresses.push_back (item); + i++; + } + } + else + { + offset = 0; + pending_item_refs.new_style = false; + while (offset < pending_items_pointer.items_buffer_size && + static_cast<size_t>(i) < pending_items_pointer.count) + { + ItemRefAndCodeAddress item; + item.item_ref = extractor.GetPointer (&offset); + item.code_address = LLDB_INVALID_ADDRESS; + pending_item_refs.item_refs_and_code_addresses.push_back (item); + i++; + } + } + } + m_page_to_free = pending_items_pointer.items_buffer_ptr; + m_page_to_free_size = pending_items_pointer.items_buffer_size; + } + } + } + return pending_item_refs; +} + + + +void +SystemRuntimeMacOSX::PopulatePendingItemsForQueue (Queue *queue) +{ + if (BacktraceRecordingHeadersInitialized()) + { + PendingItemsForQueue pending_item_refs = GetPendingItemRefsForQueue (queue->GetLibdispatchQueueAddress()); + for (ItemRefAndCodeAddress pending_item : pending_item_refs.item_refs_and_code_addresses) + { + Address addr; + m_process->GetTarget().ResolveLoadAddress (pending_item.code_address, addr); + QueueItemSP queue_item_sp (new QueueItem (queue->shared_from_this(), m_process->shared_from_this(), pending_item.item_ref, addr)); + queue->PushPendingQueueItem (queue_item_sp); + } + } +} + +void +SystemRuntimeMacOSX::CompleteQueueItem (QueueItem *queue_item, addr_t item_ref) +{ + AppleGetItemInfoHandler::GetItemInfoReturnInfo ret; + + ThreadSP cur_thread_sp (m_process->GetThreadList().GetSelectedThread()); + Error error; + ret = m_get_item_info_handler.GetItemInfo (*cur_thread_sp.get(), item_ref, m_page_to_free, m_page_to_free_size, error); + m_page_to_free = LLDB_INVALID_ADDRESS; + m_page_to_free_size = 0; + if (ret.item_buffer_ptr != 0 && ret.item_buffer_ptr != LLDB_INVALID_ADDRESS && ret.item_buffer_size > 0) + { + DataBufferHeap data (ret.item_buffer_size, 0); + if (m_process->ReadMemory (ret.item_buffer_ptr, data.GetBytes(), ret.item_buffer_size, error) && error.Success()) + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); + ItemInfo item = ExtractItemInfoFromBuffer (extractor); + queue_item->SetItemThatEnqueuedThis (item.item_that_enqueued_this); + queue_item->SetEnqueueingThreadID (item.enqueuing_thread_id); + queue_item->SetEnqueueingQueueID (item.enqueuing_queue_serialnum); + queue_item->SetStopID (item.stop_id); + queue_item->SetEnqueueingBacktrace (item.enqueuing_callstack); + queue_item->SetThreadLabel (item.enqueuing_thread_label); + queue_item->SetQueueLabel (item.enqueuing_queue_label); + queue_item->SetTargetQueueLabel (item.target_queue_label); + } + m_page_to_free = ret.item_buffer_ptr; + m_page_to_free_size = ret.item_buffer_size; + } +} + +void +SystemRuntimeMacOSX::PopulateQueuesUsingLibBTR (lldb::addr_t queues_buffer, uint64_t queues_buffer_size, + uint64_t count, lldb_private::QueueList &queue_list) +{ + Error error; + DataBufferHeap data (queues_buffer_size, 0); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + if (m_process->ReadMemory (queues_buffer, data.GetBytes(), queues_buffer_size, error) == queues_buffer_size && error.Success()) + { + // We've read the information out of inferior memory; free it on the next call we make + m_page_to_free = queues_buffer; + m_page_to_free_size = queues_buffer_size; + + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); + offset_t offset = 0; + uint64_t queues_read = 0; + + // The information about the queues is stored in this format (v1): + // typedef struct introspection_dispatch_queue_info_s { + // uint32_t offset_to_next; + // dispatch_queue_t queue; + // uint64_t serialnum; // queue's serialnum in the process, as provided by libdispatch + // uint32_t running_work_items_count; + // uint32_t pending_work_items_count; + // + // char data[]; // Starting here, we have variable-length data: + // // char queue_label[]; + // } introspection_dispatch_queue_info_s; + + while (queues_read < count && offset < queues_buffer_size) + { + offset_t start_of_this_item = offset; + + uint32_t offset_to_next = extractor.GetU32 (&offset); + + offset += 4; // Skip over the 4 bytes of reserved space + addr_t queue = extractor.GetPointer (&offset); + uint64_t serialnum = extractor.GetU64 (&offset); + uint32_t running_work_items_count = extractor.GetU32 (&offset); + uint32_t pending_work_items_count = extractor.GetU32 (&offset); + + // Read the first field of the variable length data + offset = start_of_this_item + m_lib_backtrace_recording_info.queue_info_data_offset; + const char *queue_label = extractor.GetCStr (&offset); + if (queue_label == NULL) + queue_label = ""; + + offset_t start_of_next_item = start_of_this_item + offset_to_next; + offset = start_of_next_item; + + if (log) + log->Printf ("SystemRuntimeMacOSX::PopulateQueuesUsingLibBTR added queue with dispatch_queue_t 0x%" PRIx64 ", serial number 0x%" PRIx64 ", running items %d, pending items %d, name '%s'", queue, serialnum, running_work_items_count, pending_work_items_count, queue_label); + + QueueSP queue_sp (new Queue (m_process->shared_from_this(), serialnum, queue_label)); + queue_sp->SetNumRunningWorkItems (running_work_items_count); + queue_sp->SetNumPendingWorkItems (pending_work_items_count); + queue_sp->SetLibdispatchQueueAddress (queue); + queue_sp->SetKind (GetQueueKind (queue)); + queue_list.AddQueue (queue_sp); + queues_read++; + } + } +} + +SystemRuntimeMacOSX::ItemInfo +SystemRuntimeMacOSX::ExtractItemInfoFromBuffer (lldb_private::DataExtractor &extractor) +{ + ItemInfo item; + + offset_t offset = 0; + + item.item_that_enqueued_this = extractor.GetPointer (&offset); + item.function_or_block = extractor.GetPointer (&offset); + item.enqueuing_thread_id = extractor.GetU64 (&offset); + item.enqueuing_queue_serialnum = extractor.GetU64 (&offset); + item.target_queue_serialnum = extractor.GetU64 (&offset); + item.enqueuing_callstack_frame_count = extractor.GetU32 (&offset); + item.stop_id = extractor.GetU32 (&offset); + + offset = m_lib_backtrace_recording_info.item_info_data_offset; + + for (uint32_t i = 0; i < item.enqueuing_callstack_frame_count; i++) + { + item.enqueuing_callstack.push_back (extractor.GetPointer (&offset)); + } + item.enqueuing_thread_label = extractor.GetCStr (&offset); + item.enqueuing_queue_label = extractor.GetCStr (&offset); + item.target_queue_label = extractor.GetCStr (&offset); + + return item; +} + +void +SystemRuntimeMacOSX::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SystemRuntimeMacOSX::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +SystemRuntimeMacOSX::GetPluginNameStatic() +{ + static ConstString g_name("systemruntime-macosx"); + return g_name; +} + +const char * +SystemRuntimeMacOSX::GetPluginDescriptionStatic() +{ + return "System runtime plugin for Mac OS X native libraries."; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +SystemRuntimeMacOSX::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SystemRuntimeMacOSX::GetPluginVersion() +{ + return 1; +} diff --git a/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h b/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h new file mode 100644 index 000000000000..5976bed57cf6 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h @@ -0,0 +1,336 @@ +//===-- SystemRuntimeMacOSX.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SystemRuntimeMacOSX_h_ +#define liblldb_SystemRuntimeMacOSX_h_ + +// C Includes +// C++ Includes +#include <vector> +#include <string> + +// Other libraries and framework include +// Project includes +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/StructuredData.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/QueueItem.h" + +#include "AppleGetItemInfoHandler.h" +#include "AppleGetQueuesHandler.h" +#include "AppleGetPendingItemsHandler.h" +#include "AppleGetThreadItemInfoHandler.h" + +class SystemRuntimeMacOSX : public lldb_private::SystemRuntime +{ +public: + SystemRuntimeMacOSX(lldb_private::Process *process); + + ~SystemRuntimeMacOSX() override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SystemRuntime * + CreateInstance (lldb_private::Process *process); + + //------------------------------------------------------------------ + // instance methods + //------------------------------------------------------------------ + + void + Clear(bool clear_process); + + void + Detach() override; + + const std::vector<lldb_private::ConstString> & + GetExtendedBacktraceTypes() override; + + lldb::ThreadSP + GetExtendedBacktraceThread(lldb::ThreadSP thread, lldb_private::ConstString type) override; + + lldb::ThreadSP + GetExtendedBacktraceForQueueItem(lldb::QueueItemSP queue_item_sp, + lldb_private::ConstString type) override; + + lldb::ThreadSP + GetExtendedBacktraceFromItemRef (lldb::addr_t item_ref); + + void + PopulateQueueList(lldb_private::QueueList &queue_list) override; + + void + PopulateQueuesUsingLibBTR (lldb::addr_t queues_buffer, uint64_t queues_buffer_size, uint64_t count, lldb_private::QueueList &queue_list); + + void + PopulatePendingQueuesUsingLibBTR (lldb::addr_t items_buffer, uint64_t items_buffer_size, uint64_t count, lldb_private::Queue *queue); + + std::string + GetQueueNameFromThreadQAddress(lldb::addr_t dispatch_qaddr) override; + + lldb::queue_id_t + GetQueueIDFromThreadQAddress(lldb::addr_t dispatch_qaddr) override; + + lldb::addr_t + GetLibdispatchQueueAddressFromThreadQAddress(lldb::addr_t dispatch_qaddr) override; + + void + PopulatePendingItemsForQueue(lldb_private::Queue *queue) override; + + void + CompleteQueueItem(lldb_private::QueueItem *queue_item, lldb::addr_t item_ref) override; + + virtual lldb::QueueKind + GetQueueKind (lldb::addr_t dispatch_queue_addr); + + void + AddThreadExtendedInfoPacketHints(lldb_private::StructuredData::ObjectSP dict) override; + + bool + SafeToCallFunctionsOnThisThread(lldb::ThreadSP thread_sp) override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + +protected: + lldb::user_id_t m_break_id; + mutable lldb_private::Mutex m_mutex; + +private: + struct libBacktraceRecording_info { + uint16_t queue_info_version; + uint16_t queue_info_data_offset; + uint16_t item_info_version; + uint16_t item_info_data_offset; + + libBacktraceRecording_info () : + queue_info_version(0), + queue_info_data_offset(0), + item_info_version(0), + item_info_data_offset(0) {} + }; + + // A structure which reflects the data recorded in the + // libBacktraceRecording introspection_dispatch_item_info_s. + struct ItemInfo { + lldb::addr_t item_that_enqueued_this; + lldb::addr_t function_or_block; + uint64_t enqueuing_thread_id; + uint64_t enqueuing_queue_serialnum; + uint64_t target_queue_serialnum; + uint32_t enqueuing_callstack_frame_count; + uint32_t stop_id; + std::vector<lldb::addr_t> enqueuing_callstack; + std::string enqueuing_thread_label; + std::string enqueuing_queue_label; + std::string target_queue_label; + }; + + // The offsets of different fields of the dispatch_queue_t structure in + // a thread/queue process. + // Based on libdispatch src/queue_private.h, struct dispatch_queue_offsets_s + // With dqo_version 1-3, the dqo_label field is a per-queue value and cannot be cached. + // With dqo_version 4 (Mac OS X 10.9 / iOS 7), dqo_label is a constant value that can be cached. + struct LibdispatchOffsets + { + uint16_t dqo_version; + uint16_t dqo_label; + uint16_t dqo_label_size; + uint16_t dqo_flags; + uint16_t dqo_flags_size; + uint16_t dqo_serialnum; + uint16_t dqo_serialnum_size; + uint16_t dqo_width; + uint16_t dqo_width_size; + uint16_t dqo_running; + uint16_t dqo_running_size; + + uint16_t dqo_suspend_cnt; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_suspend_cnt_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_target_queue; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_target_queue_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_priority; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_priority_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + + LibdispatchOffsets () + { + dqo_version = UINT16_MAX; + dqo_flags = UINT16_MAX; + dqo_serialnum = UINT16_MAX; + dqo_label = UINT16_MAX; + dqo_width = UINT16_MAX; + dqo_running = UINT16_MAX; + dqo_suspend_cnt = UINT16_MAX; + dqo_target_queue = UINT16_MAX; + dqo_target_queue = UINT16_MAX; + dqo_priority = UINT16_MAX; + } + + bool + IsValid () + { + return dqo_version != UINT16_MAX; + } + + bool + LabelIsValid () + { + return dqo_label != UINT16_MAX; + } + }; + + struct LibdispatchVoucherOffsets + { + uint16_t vo_version; + uint16_t vo_activity_ids_count; + uint16_t vo_activity_ids_count_size; + uint16_t vo_activity_ids_array; + uint16_t vo_activity_ids_array_entry_size; + + LibdispatchVoucherOffsets () : + vo_version (UINT16_MAX), + vo_activity_ids_count (UINT16_MAX), + vo_activity_ids_count_size (UINT16_MAX), + vo_activity_ids_array (UINT16_MAX), + vo_activity_ids_array_entry_size (UINT16_MAX) + { } + + bool IsValid () { return vo_version != UINT16_MAX; } + }; + + struct LibdispatchTSDIndexes + { + uint16_t dti_version; + uint64_t dti_queue_index; + uint64_t dti_voucher_index; + uint64_t dti_qos_class_index; + + LibdispatchTSDIndexes () : + dti_version (UINT16_MAX), + dti_queue_index (UINT64_MAX), + dti_voucher_index (UINT64_MAX), + dti_qos_class_index (UINT64_MAX) + { } + + bool IsValid () { return dti_version != UINT16_MAX; } + }; + + struct LibpthreadOffsets + { + uint16_t plo_version; + uint16_t plo_pthread_tsd_base_offset; + uint16_t plo_pthread_tsd_base_address_offset; + uint16_t plo_pthread_tsd_entry_size; + + LibpthreadOffsets () : + plo_version (UINT16_MAX), + plo_pthread_tsd_base_offset (UINT16_MAX), + plo_pthread_tsd_base_address_offset (UINT16_MAX), + plo_pthread_tsd_entry_size (UINT16_MAX) + { + } + + bool IsValid () + { + return plo_version != UINT16_MAX; + } + }; + + // The libBacktraceRecording function __introspection_dispatch_queue_get_pending_items has + // two forms. It can either return a simple array of item_refs (void *) size or it can return + // a header with uint32_t version, a uint32_t size of item, and then an array of item_refs (void*) + // and code addresses (void*) for all the pending blocks. + + struct ItemRefAndCodeAddress { + lldb::addr_t item_ref; + lldb::addr_t code_address; + }; + + struct PendingItemsForQueue { + bool new_style; // new-style means both item_refs and code_addresses avail + // old-style means only item_refs is filled in + std::vector<ItemRefAndCodeAddress> item_refs_and_code_addresses; + }; + + bool + BacktraceRecordingHeadersInitialized (); + + void + ReadLibdispatchOffsetsAddress(); + + void + ReadLibdispatchOffsets (); + + void + ReadLibpthreadOffsetsAddress(); + + void + ReadLibpthreadOffsets (); + + void + ReadLibdispatchTSDIndexesAddress (); + + void + ReadLibdispatchTSDIndexes (); + + PendingItemsForQueue + GetPendingItemRefsForQueue (lldb::addr_t queue); + + ItemInfo + ExtractItemInfoFromBuffer (lldb_private::DataExtractor &extractor); + + lldb_private::AppleGetQueuesHandler m_get_queues_handler; + lldb_private::AppleGetPendingItemsHandler m_get_pending_items_handler; + lldb_private::AppleGetItemInfoHandler m_get_item_info_handler; + lldb_private::AppleGetThreadItemInfoHandler m_get_thread_item_info_handler; + + lldb::addr_t m_page_to_free; + uint64_t m_page_to_free_size; + libBacktraceRecording_info m_lib_backtrace_recording_info; + + lldb::addr_t m_dispatch_queue_offsets_addr; + struct LibdispatchOffsets m_libdispatch_offsets; + + lldb::addr_t m_libpthread_layout_offsets_addr; + struct LibpthreadOffsets m_libpthread_offsets; + + lldb::addr_t m_dispatch_tsd_indexes_addr; + struct LibdispatchTSDIndexes m_libdispatch_tsd_indexes; + + lldb::addr_t m_dispatch_voucher_offsets_addr; + struct LibdispatchVoucherOffsets m_libdispatch_voucher_offsets; + + DISALLOW_COPY_AND_ASSIGN (SystemRuntimeMacOSX); +}; + +#endif // liblldb_SystemRuntimeMacOSX_h_ diff --git a/source/Plugins/UnwindAssembly/CMakeLists.txt b/source/Plugins/UnwindAssembly/CMakeLists.txt new file mode 100644 index 000000000000..1723a0604580 --- /dev/null +++ b/source/Plugins/UnwindAssembly/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(InstEmulation) +add_subdirectory(x86) diff --git a/source/Plugins/UnwindAssembly/InstEmulation/CMakeLists.txt b/source/Plugins/UnwindAssembly/InstEmulation/CMakeLists.txt new file mode 100644 index 000000000000..21673160bf40 --- /dev/null +++ b/source/Plugins/UnwindAssembly/InstEmulation/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginUnwindAssemblyInstEmulation + UnwindAssemblyInstEmulation.cpp + ) diff --git a/source/Plugins/UnwindAssembly/InstEmulation/Makefile b/source/Plugins/UnwindAssembly/InstEmulation/Makefile new file mode 100644 index 000000000000..e006235864f1 --- /dev/null +++ b/source/Plugins/UnwindAssembly/InstEmulation/Makefile @@ -0,0 +1,14 @@ +##==- source/Plugins/UnwindAssembly/InstEmulation/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 := lldbPluginUnwindAssemblyInstEmulation +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/UnwindAssembly/x86/CMakeLists.txt b/source/Plugins/UnwindAssembly/x86/CMakeLists.txt new file mode 100644 index 000000000000..6ae63891bcc3 --- /dev/null +++ b/source/Plugins/UnwindAssembly/x86/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginUnwindAssemblyX86 + UnwindAssembly-x86.cpp + ) diff --git a/source/Plugins/UnwindAssembly/x86/Makefile b/source/Plugins/UnwindAssembly/x86/Makefile new file mode 100644 index 000000000000..24419c044f04 --- /dev/null +++ b/source/Plugins/UnwindAssembly/x86/Makefile @@ -0,0 +1,14 @@ +##==-- source/Plugins/UnwindAssembly/x86/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 := lldbPluginUnwindAssemblyX86 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Symbol/CMakeLists.txt b/source/Symbol/CMakeLists.txt new file mode 100644 index 000000000000..6372fffde1f1 --- /dev/null +++ b/source/Symbol/CMakeLists.txt @@ -0,0 +1,36 @@ +add_lldb_library(lldbSymbol + ArmUnwindInfo.cpp + Block.cpp + ClangASTContext.cpp + ClangASTImporter.cpp + ClangExternalASTSourceCallbacks.cpp + ClangExternalASTSourceCommon.cpp + CompilerDecl.cpp + CompilerDeclContext.cpp + CompilerType.cpp + CompileUnit.cpp + CompactUnwindInfo.cpp + DebugMacros.cpp + Declaration.cpp + DWARFCallFrameInfo.cpp + Function.cpp + FuncUnwinders.cpp + GoASTContext.cpp + LineEntry.cpp + LineTable.cpp + ObjectFile.cpp + Symbol.cpp + SymbolContext.cpp + SymbolFile.cpp + SymbolVendor.cpp + Symtab.cpp + Type.cpp + TypeList.cpp + TypeMap.cpp + TypeSystem.cpp + UnwindPlan.cpp + UnwindTable.cpp + Variable.cpp + VariableList.cpp + VerifyDecl.cpp + ) diff --git a/source/Symbol/Makefile b/source/Symbol/Makefile new file mode 100644 index 000000000000..ae0cef0e242a --- /dev/null +++ b/source/Symbol/Makefile @@ -0,0 +1,14 @@ +##===- source/Symbol/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 := lldbSymbol +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Target/CMakeLists.txt b/source/Target/CMakeLists.txt new file mode 100644 index 000000000000..f1c2a7e98007 --- /dev/null +++ b/source/Target/CMakeLists.txt @@ -0,0 +1,59 @@ +include_directories(../Plugins/Process/Utility) + +add_lldb_library(lldbTarget + ABI.cpp + CPPLanguageRuntime.cpp + ExecutionContext.cpp + FileAction.cpp + JITLoader.cpp + JITLoaderList.cpp + InstrumentationRuntime.cpp + InstrumentationRuntimeStopInfo.cpp + Language.cpp + LanguageRuntime.cpp + Memory.cpp + MemoryHistory.cpp + ObjCLanguageRuntime.cpp + OperatingSystem.cpp + PathMappingList.cpp + Platform.cpp + Process.cpp + ProcessInfo.cpp + ProcessLaunchInfo.cpp + Queue.cpp + QueueItem.cpp + QueueList.cpp + RegisterContext.cpp + SectionLoadHistory.cpp + SectionLoadList.cpp + StackFrame.cpp + StackFrameList.cpp + StackID.cpp + StopInfo.cpp + SystemRuntime.cpp + Target.cpp + TargetList.cpp + Thread.cpp + ThreadCollection.cpp + ThreadList.cpp + ThreadPlan.cpp + ThreadPlanBase.cpp + ThreadPlanCallFunction.cpp + ThreadPlanCallFunctionUsingABI.cpp + ThreadPlanCallUserExpression.cpp + ThreadPlanPython.cpp + ThreadPlanRunToAddress.cpp + ThreadPlanShouldStopHere.cpp + ThreadPlanStepInRange.cpp + ThreadPlanStepInstruction.cpp + ThreadPlanStepOut.cpp + ThreadPlanStepOverBreakpoint.cpp + ThreadPlanStepOverRange.cpp + ThreadPlanStepRange.cpp + ThreadPlanStepThrough.cpp + ThreadPlanStepUntil.cpp + ThreadPlanTracer.cpp + ThreadSpec.cpp + UnixSignals.cpp + UnwindAssembly.cpp + ) diff --git a/source/Target/Makefile b/source/Target/Makefile new file mode 100644 index 000000000000..0d4be5449ad3 --- /dev/null +++ b/source/Target/Makefile @@ -0,0 +1,14 @@ +##===- source/Target/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 := lldbTarget +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp index 6bc9f2b0c9d8..311c695860fe 100644 --- a/source/Target/Process.cpp +++ b/source/Target/Process.cpp @@ -3980,7 +3980,7 @@ Process::ShouldBroadcastEvent (Event *event_ptr) { Vote stop_vote = m_thread_list.ShouldReportStop (event_ptr); if (log) - log->Printf ("Process::ShouldBroadcastEvent: should_stop: %i state: %s was_restarted: %i stop_vote: %d.", + log->Printf ("Process::ShouldBroadcastEvent: should_resume: %i state: %s was_restarted: %i stop_vote: %d.", should_resume, StateAsCString(state), was_restarted, stop_vote); diff --git a/source/Utility/CMakeLists.txt b/source/Utility/CMakeLists.txt new file mode 100644 index 000000000000..aa4ad0301352 --- /dev/null +++ b/source/Utility/CMakeLists.txt @@ -0,0 +1,20 @@ +add_lldb_library(lldbUtility + ARM_DWARF_Registers.cpp + ARM64_DWARF_Registers.cpp + ConvertEnum.cpp + JSON.cpp + KQueue.cpp + LLDBAssert.cpp + ModuleCache.cpp + NameMatches.cpp + PseudoTerminal.cpp + Range.cpp + RegisterNumber.cpp + SharingPtr.cpp + StringExtractor.cpp + StringExtractorGDBRemote.cpp + StringLexer.cpp + TaskPool.cpp + TimeSpecTimeout.cpp + UriParser.cpp + ) diff --git a/source/Utility/Makefile b/source/Utility/Makefile new file mode 100644 index 000000000000..13bcd8376c2d --- /dev/null +++ b/source/Utility/Makefile @@ -0,0 +1,15 @@ +##===- source/Utility/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 := lldbUtility +BUILD_ARCHIVE = 1 +NO_PEDANTIC = 1 + +include $(LLDB_LEVEL)/Makefile |